diff --git a/cmd/yggstack/main.go b/cmd/yggstack/main.go index bc0c5a8c..56e5c7b6 100644 --- a/cmd/yggstack/main.go +++ b/cmd/yggstack/main.go @@ -6,29 +6,18 @@ import ( "encoding/json" "flag" "fmt" - "io" "net" - "net/http" "os" "os/signal" "syscall" - "inet.af/netstack/tcpip" - "inet.af/netstack/tcpip/adapters/gonet" - "inet.af/netstack/tcpip/buffer" - "inet.af/netstack/tcpip/header" - "inet.af/netstack/tcpip/network/ipv6" - "inet.af/netstack/tcpip/stack" - "inet.af/netstack/tcpip/transport/icmp" - "inet.af/netstack/tcpip/transport/tcp" - "github.com/gologme/log" gsyslog "github.com/hashicorp/go-syslog" "github.com/hjson/hjson-go" + "github.com/yggdrasil-network/yggdrasil-go/contrib/netstack" "github.com/yggdrasil-network/yggdrasil-go/src/address" "github.com/yggdrasil-network/yggdrasil-go/src/config" - "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" "github.com/yggdrasil-network/yggdrasil-go/src/setup" "github.com/yggdrasil-network/yggdrasil-go/src/version" @@ -152,64 +141,21 @@ func main() { logger.Infof("Your IPv6 address is %s", address.String()) logger.Infof("Your IPv6 subnet is %s", subnet.String()) - iprwc := ipv6rwc.NewReadWriteCloser(&n.Core) - s := stack.New(stack.Options{ - NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, - TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, icmp.NewProtocol6}, - HandleLocal: true, - IPTables: &stack.IPTables{}, - }) - endpoint := &TCPIPEndpoint{ - stack: s, - ipv6rwc: iprwc, - readBuf: make([]byte, iprwc.MTU()), - writeBuf: make([]byte, iprwc.MTU()), - } - if err := s.CreateNIC(1, endpoint); err != nil { - panic(err) - } - if err := s.AddProtocolAddress( - 1, - tcpip.ProtocolAddress{ - Protocol: ipv6.ProtocolNumber, - AddressWithPrefix: tcpip.Address(address).WithPrefix(), - }, - stack.AddressProperties{}, - ); err != nil { - panic(err) - } - s.AddRoute(tcpip.Route{ - Destination: header.IPv6EmptySubnet, - NIC: 1, - }) - s.AllowICMPMessage() - go func() { - var rx int - var err error - for { - rx, err = iprwc.Read(endpoint.readBuf) - if err != nil { - log.Println(err) - break - } - pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{ - Data: buffer.NewVectorisedView(rx, []buffer.View{ - buffer.NewViewFromBytes(endpoint.readBuf[:rx]), - }), - }) - endpoint.dispatcher.DeliverNetworkPacket("", "", ipv6.ProtocolNumber, pkb) - } - }() - - listener, err := endpoint.ListenTCP(&net.TCPAddr{Port: 80}) + _, err = netstack.CreateYggdrasilNetstack(&n.Core, false) if err != nil { - log.Panicln(err) + logger.Fatalln(err) } - http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { - _, _ = io.WriteString(writer, "Hello from userspace TCP "+request.RemoteAddr) - }) - httpServer := &http.Server{} - go httpServer.Serve(listener) // nolint:errcheck + /* + listener, err := s.ListenTCP(&net.TCPAddr{Port: 80}) + if err != nil { + log.Panicln(err) + } + http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) { + _, _ = io.WriteString(writer, "Hello from userspace TCP "+request.RemoteAddr) + }) + httpServer := &http.Server{} + go httpServer.Serve(listener) // nolint:errcheck + */ term := make(chan os.Signal, 1) signal.Notify(term, os.Interrupt, syscall.SIGTERM) @@ -221,95 +167,3 @@ func main() { n.Close() } - -const IPv6HdrSize = 40 - -type TCPIPEndpoint struct { - stack *stack.Stack - ipv6rwc *ipv6rwc.ReadWriteCloser - dispatcher stack.NetworkDispatcher - readBuf []byte - writeBuf []byte -} - -func (e *TCPIPEndpoint) Attach(dispatcher stack.NetworkDispatcher) { e.dispatcher = dispatcher } - -func (e *TCPIPEndpoint) IsAttached() bool { return e.dispatcher != nil } - -func (e *TCPIPEndpoint) MTU() uint32 { return uint32(e.ipv6rwc.MTU()) } - -func (*TCPIPEndpoint) Capabilities() stack.LinkEndpointCapabilities { return stack.CapabilityNone } - -func (*TCPIPEndpoint) MaxHeaderLength() uint16 { return IPv6HdrSize } - -func (*TCPIPEndpoint) LinkAddress() tcpip.LinkAddress { return "" } - -func (*TCPIPEndpoint) Wait() {} - -func (e *TCPIPEndpoint) WritePacket( - _ stack.RouteInfo, - _ tcpip.NetworkProtocolNumber, - pkt *stack.PacketBuffer, -) tcpip.Error { - vv := buffer.NewVectorisedView(pkt.Size(), pkt.Views()) - n, err := vv.Read(e.writeBuf) - if err != nil { - log.Println(err) - return &tcpip.ErrAborted{} - } - _, err = e.ipv6rwc.Write(e.writeBuf[:n]) - if err != nil { - log.Println(err) - return &tcpip.ErrAborted{} - } - return nil -} - -func (e *TCPIPEndpoint) WritePackets( - stack.RouteInfo, - stack.PacketBufferList, - tcpip.NetworkProtocolNumber, -) (int, tcpip.Error) { - panic("not implemented") -} - -func (e *TCPIPEndpoint) WriteRawPacket(*stack.PacketBuffer) tcpip.Error { - panic("not implemented") -} - -func (*TCPIPEndpoint) ARPHardwareType() header.ARPHardwareType { - return header.ARPHardwareNone -} - -func (e *TCPIPEndpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) { -} - -func (e *TCPIPEndpoint) Close() error { - e.stack.RemoveNIC(1) - e.dispatcher = nil - return nil -} - -func convertToFullAddr(ip net.IP, port int) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) { - return tcpip.FullAddress{ - NIC: 1, - Addr: tcpip.Address(ip), - Port: uint16(port), - }, ipv6.ProtocolNumber -} - -func (e *TCPIPEndpoint) DialTCP(addr *net.TCPAddr) (net.Conn, error) { - if addr == nil { - panic("not implemented") - } - fa, pn := convertToFullAddr(addr.IP, addr.Port) - return gonet.DialTCP(e.stack, fa, pn) -} - -func (e *TCPIPEndpoint) ListenTCP(addr *net.TCPAddr) (net.Listener, error) { - if addr == nil { - panic("not implemented") - } - fa, pn := convertToFullAddr(addr.IP, addr.Port) - return gonet.ListenTCP(e.stack, fa, pn) -} diff --git a/contrib/netstack/netstack.go b/contrib/netstack/netstack.go new file mode 100644 index 00000000..b13c49e1 --- /dev/null +++ b/contrib/netstack/netstack.go @@ -0,0 +1,97 @@ +package netstack + +import ( + "encoding/base64" + "fmt" + "net" + "time" + + "github.com/yggdrasil-network/yggdrasil-go/src/core" + + "inet.af/netstack/tcpip" + "inet.af/netstack/tcpip/adapters/gonet" + "inet.af/netstack/tcpip/network/ipv6" + "inet.af/netstack/tcpip/stack" + "inet.af/netstack/tcpip/transport/icmp" + "inet.af/netstack/tcpip/transport/tcp" + "inet.af/netstack/tcpip/transport/udp" +) + +type YggdrasilNetstack struct { + stack *stack.Stack +} + +func CreateYggdrasilNetstack(ygg *core.Core, handlelocal bool) (*YggdrasilNetstack, error) { + s := &YggdrasilNetstack{ + stack: stack.New(stack.Options{ + NetworkProtocols: []stack.NetworkProtocolFactory{ipv6.NewProtocol}, + TransportProtocols: []stack.TransportProtocolFactory{tcp.NewProtocol, udp.NewProtocol, icmp.NewProtocol6}, + HandleLocal: handlelocal, + }), + } + if s.stack.HandleLocal() { + s.stack.AllowICMPMessage() + } else if err := s.stack.SetForwardingDefaultAndAllNICs(ipv6.ProtocolNumber, true); err != nil { + panic(err) + } + if err := s.NewYggdrasilNIC(ygg); err != nil { + return nil, fmt.Errorf("s.NewYggdrasilNIC: %s", err.String()) + } + pk, err := base64.RawStdEncoding.WithPadding('=').DecodeString("T6wTiFBhFj1evcApUyQDuDNADGePrheqx0A6vW9rFEw=") + if err != nil { + panic(err) + } + if err := s.NewWireguardNIC(ygg, pk[:]); err != nil { + return nil, fmt.Errorf("s.NewWireguardNIC: %s", err.String()) + } + + var f func() + f = func() { + fmt.Printf("\nRouting table: %+v\n", s.stack.GetRouteTable()) + fmt.Printf("Forwarding: %+v\n", s.stack.Stats().IP.Forwarding) + time.AfterFunc(time.Second, f) + } + f() + + return s, nil +} + +func convertToFullAddr(ip net.IP, port int) (tcpip.FullAddress, tcpip.NetworkProtocolNumber) { + return tcpip.FullAddress{ + NIC: 1, + Addr: tcpip.Address(ip), + Port: uint16(port), + }, ipv6.ProtocolNumber +} + +func (s *YggdrasilNetstack) DialTCP(addr *net.TCPAddr) (net.Conn, error) { + if !s.stack.HandleLocal() { + return nil, fmt.Errorf("only available when handling local traffic is enabled") + } + fa, pn := convertToFullAddr(addr.IP, addr.Port) + return gonet.DialTCP(s.stack, fa, pn) +} + +func (s *YggdrasilNetstack) DialUDP(addr *net.UDPAddr) (net.PacketConn, error) { + if !s.stack.HandleLocal() { + return nil, fmt.Errorf("only available when handling local traffic is enabled") + } + fa, pn := convertToFullAddr(addr.IP, addr.Port) + return gonet.DialUDP(s.stack, nil, &fa, pn) +} + +func (s *YggdrasilNetstack) ListenTCP(addr *net.TCPAddr) (net.Listener, error) { + if !s.stack.HandleLocal() { + return nil, fmt.Errorf("only available when handling local traffic is enabled") + } + fa, pn := convertToFullAddr(addr.IP, addr.Port) + return gonet.ListenTCP(s.stack, fa, pn) +} + +func (s *YggdrasilNetstack) ListenUDP(addr *net.UDPAddr) (net.PacketConn, error) { + if !s.stack.HandleLocal() { + return nil, fmt.Errorf("only available when handling local traffic is enabled") + } + fa, pn := convertToFullAddr(addr.IP, addr.Port) + return gonet.DialUDP(s.stack, &fa, nil, pn) +} diff --git a/contrib/netstack/wireguard.go b/contrib/netstack/wireguard.go new file mode 100644 index 00000000..cfdc5740 --- /dev/null +++ b/contrib/netstack/wireguard.go @@ -0,0 +1,218 @@ +package netstack + +import ( + "crypto/ed25519" + "encoding/hex" + "fmt" + "os" + + "github.com/yggdrasil-network/yggdrasil-go/src/core" + "golang.org/x/crypto/curve25519" + + "golang.zx2c4.com/wireguard/conn" + "golang.zx2c4.com/wireguard/device" + "golang.zx2c4.com/wireguard/tun" + + "inet.af/netstack/tcpip" + "inet.af/netstack/tcpip/buffer" + "inet.af/netstack/tcpip/header" + "inet.af/netstack/tcpip/network/ipv4" + "inet.af/netstack/tcpip/network/ipv6" + "inet.af/netstack/tcpip/stack" +) + +type YggdrasilWireguard struct { + stack *YggdrasilNetstack + device *device.Device + dispatcher stack.NetworkDispatcher + events chan tun.Event + incomingPacket chan buffer.VectorisedView +} + +type YggdrasilWireguardEndpoint YggdrasilWireguard + +func (s *YggdrasilNetstack) NewWireguardNIC(ygg *core.Core, public ed25519.PublicKey) tcpip.Error { + wg := &YggdrasilWireguard{ + stack: s, + } + + var nsk device.NoisePrivateKey + var npk device.NoisePublicKey + apk := (*[device.NoisePublicKeySize]byte)(&npk) + ask := (*[device.NoisePrivateKeySize]byte)(&nsk) + + ysk := hex.EncodeToString(ygg.PrivateKey()[:ed25519.PrivateKeySize-ed25519.PublicKeySize]) + if err := nsk.FromMaybeZeroHex(ysk); err != nil { + panic(err) + } + curve25519.ScalarBaseMult(apk, ask) + + wg.device = device.NewDevice(wg, conn.NewDefaultBind(), device.NewLogger(device.LogLevelError, "")) + if err := wg.device.IpcSet(fmt.Sprintf(""+ + "listen_port=12346\n"+ + "private_key=%s\n"+ + "public_key=%s\n"+ + "allowed_ip=%s/128\n"+ + "allowed_ip=%s/64", + hex.EncodeToString(ask[:]), + hex.EncodeToString(public[:]), + ygg.Address().String(), + ygg.Subnet().IP.String(), + )); err != nil { + panic(err) + } + wg.device.Up() + + /* + fmt.Println("WIREGUARD CONFIG:") + fmt.Println() + fmt.Println("[Interface]") + fmt.Println("Address =", ygg.Address().String()) + fmt.Println() + fmt.Println("[Peer]") + fmt.Println("Endpoint = localhost:12346") + fmt.Println("AllowedIPs = 200::/7") + fmt.Println("PublicKey =", base64.RawStdEncoding.WithPadding('=').EncodeToString(apk[:])) + fmt.Println() + */ + + if err := s.stack.CreateNIC(2, (*YggdrasilWireguardEndpoint)(wg)); err != nil { + return err + } + + addr := ygg.Address() + snet := ygg.Subnet() + m := []byte{ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + } + + routeAddr, err := tcpip.NewSubnet( + tcpip.Address(addr), + tcpip.AddressMask(m[:]), + ) + if err != nil { + panic(err) + } + routeSnet, err := tcpip.NewSubnet( + tcpip.Address(string(snet.IP)), + tcpip.AddressMask(string(snet.Mask)), + ) + if err != nil { + panic(err) + } + s.stack.AddRoute(tcpip.Route{ + Destination: routeAddr, + NIC: 2, + }) + s.stack.AddRoute(tcpip.Route{ + Destination: routeSnet, + NIC: 2, + }) + return nil +} + +//////////// BELOW IMPLEMENTS tcpip.Endpoint //////////// + +func (e *YggdrasilWireguardEndpoint) Attach(dispatcher stack.NetworkDispatcher) { + e.dispatcher = dispatcher +} + +func (e *YggdrasilWireguardEndpoint) IsAttached() bool { + return e.dispatcher != nil +} + +func (e *YggdrasilWireguardEndpoint) MTU() uint32 { + return 1420 +} + +func (*YggdrasilWireguardEndpoint) Capabilities() stack.LinkEndpointCapabilities { + return stack.CapabilityNone +} + +func (*YggdrasilWireguardEndpoint) MaxHeaderLength() uint16 { + return 0 +} + +func (*YggdrasilWireguardEndpoint) LinkAddress() tcpip.LinkAddress { + return "" +} + +func (*YggdrasilWireguardEndpoint) Wait() {} + +func (e *YggdrasilWireguardEndpoint) WritePacket(_ stack.RouteInfo, _ tcpip.NetworkProtocolNumber, pkt *stack.PacketBuffer) tcpip.Error { + e.incomingPacket <- buffer.NewVectorisedView(pkt.Size(), pkt.Views()) + return nil +} + +func (e *YggdrasilWireguardEndpoint) WritePackets(stack.RouteInfo, stack.PacketBufferList, tcpip.NetworkProtocolNumber) (int, tcpip.Error) { + panic("not implemented") +} + +func (e *YggdrasilWireguardEndpoint) WriteRawPacket(*stack.PacketBuffer) tcpip.Error { + panic("not implemented") +} + +func (*YggdrasilWireguardEndpoint) ARPHardwareType() header.ARPHardwareType { + return header.ARPHardwareNone +} + +func (e *YggdrasilWireguardEndpoint) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) { +} + +//////////// BELOW IMPLEMENTS tun.Device //////////// + +func (tun *YggdrasilWireguard) Name() (string, error) { + return "go", nil +} + +func (tun *YggdrasilWireguard) File() *os.File { + return nil +} + +func (tun *YggdrasilWireguard) Events() chan tun.Event { + return tun.events +} + +func (tun *YggdrasilWireguard) Read(buf []byte, offset int) (int, error) { + view, ok := <-tun.incomingPacket + if !ok { + return 0, os.ErrClosed + } + return view.Read(buf[offset:]) +} + +func (tun *YggdrasilWireguard) Write(buf []byte, offset int) (int, error) { + packet := buf[offset:] + if len(packet) == 0 { + return 0, nil + } + + pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{Data: buffer.NewVectorisedView(len(packet), []buffer.View{buffer.NewViewFromBytes(packet)})}) + switch packet[0] >> 4 { + case 4: + tun.dispatcher.DeliverNetworkPacket("", "", ipv4.ProtocolNumber, pkb) + case 6: + tun.dispatcher.DeliverNetworkPacket("", "", ipv6.ProtocolNumber, pkb) + } + + return len(buf), nil +} + +func (tun *YggdrasilWireguard) Flush() error { + return nil +} + +func (tun *YggdrasilWireguard) Close() error { + if tun.events != nil { + close(tun.events) + } + if tun.incomingPacket != nil { + close(tun.incomingPacket) + } + return nil +} + +func (tun *YggdrasilWireguard) MTU() (int, error) { + return 1420, nil +} diff --git a/contrib/netstack/yggdrasil.go b/contrib/netstack/yggdrasil.go new file mode 100644 index 00000000..02228bd2 --- /dev/null +++ b/contrib/netstack/yggdrasil.go @@ -0,0 +1,140 @@ +package netstack + +import ( + "log" + "net" + + "github.com/yggdrasil-network/yggdrasil-go/src/core" + "github.com/yggdrasil-network/yggdrasil-go/src/ipv6rwc" + + "inet.af/netstack/tcpip" + "inet.af/netstack/tcpip/buffer" + "inet.af/netstack/tcpip/header" + "inet.af/netstack/tcpip/network/ipv6" + "inet.af/netstack/tcpip/stack" +) + +type YggdrasilNIC struct { + stack *YggdrasilNetstack + ipv6rwc *ipv6rwc.ReadWriteCloser + dispatcher stack.NetworkDispatcher + readBuf []byte + writeBuf []byte +} + +func (s *YggdrasilNetstack) NewYggdrasilNIC(ygg *core.Core) tcpip.Error { + rwc := ipv6rwc.NewReadWriteCloser(ygg) + mtu := rwc.MTU() + nic := &YggdrasilNIC{ + ipv6rwc: rwc, + readBuf: make([]byte, mtu), + writeBuf: make([]byte, mtu), + } + if err := s.stack.CreateNIC(1, nic); err != nil { + return err + } + go func() { + var rx int + var err error + for { + rx, err = nic.ipv6rwc.Read(nic.readBuf) + if err != nil { + log.Println(err) + break + } + pkb := stack.NewPacketBuffer(stack.PacketBufferOptions{ + Data: buffer.NewVectorisedView(rx, []buffer.View{ + buffer.NewViewFromBytes(nic.readBuf[:rx]), + }), + }) + nic.dispatcher.DeliverNetworkPacket("", "", ipv6.ProtocolNumber, pkb) + } + }() + _, snet, err := net.ParseCIDR("0200::/7") + if err != nil { + return &tcpip.ErrBadAddress{} + } + subnet, err := tcpip.NewSubnet( + tcpip.Address(string(snet.IP)), + tcpip.AddressMask(string(snet.Mask)), + ) + if err != nil { + return &tcpip.ErrBadAddress{} + } + s.stack.AddRoute(tcpip.Route{ + Destination: subnet, + NIC: 1, + }) + if s.stack.HandleLocal() { + ip := ygg.Address() + if err := s.stack.AddProtocolAddress( + 1, + tcpip.ProtocolAddress{ + Protocol: ipv6.ProtocolNumber, + AddressWithPrefix: tcpip.Address(ip).WithPrefix(), + }, + stack.AddressProperties{}, + ); err != nil { + return err + } + } + return nil +} + +func (e *YggdrasilNIC) Attach(dispatcher stack.NetworkDispatcher) { e.dispatcher = dispatcher } + +func (e *YggdrasilNIC) IsAttached() bool { return e.dispatcher != nil } + +func (e *YggdrasilNIC) MTU() uint32 { return uint32(e.ipv6rwc.MTU()) } + +func (*YggdrasilNIC) Capabilities() stack.LinkEndpointCapabilities { return stack.CapabilityNone } + +func (*YggdrasilNIC) MaxHeaderLength() uint16 { return 40 } + +func (*YggdrasilNIC) LinkAddress() tcpip.LinkAddress { return "" } + +func (*YggdrasilNIC) Wait() {} + +func (e *YggdrasilNIC) WritePacket( + _ stack.RouteInfo, + _ tcpip.NetworkProtocolNumber, + pkt *stack.PacketBuffer, +) tcpip.Error { + vv := buffer.NewVectorisedView(pkt.Size(), pkt.Views()) + n, err := vv.Read(e.writeBuf) + if err != nil { + log.Println(err) + return &tcpip.ErrAborted{} + } + _, err = e.ipv6rwc.Write(e.writeBuf[:n]) + if err != nil { + log.Println(err) + return &tcpip.ErrAborted{} + } + return nil +} + +func (e *YggdrasilNIC) WritePackets( + stack.RouteInfo, + stack.PacketBufferList, + tcpip.NetworkProtocolNumber, +) (int, tcpip.Error) { + panic("not implemented") +} + +func (e *YggdrasilNIC) WriteRawPacket(*stack.PacketBuffer) tcpip.Error { + panic("not implemented") +} + +func (*YggdrasilNIC) ARPHardwareType() header.ARPHardwareType { + return header.ARPHardwareNone +} + +func (e *YggdrasilNIC) AddHeader(tcpip.LinkAddress, tcpip.LinkAddress, tcpip.NetworkProtocolNumber, *stack.PacketBuffer) { +} + +func (e *YggdrasilNIC) Close() error { + e.stack.stack.RemoveNIC(1) + e.dispatcher = nil + return nil +}