diff --git a/client/hhhh.go b/client/hhhh.go new file mode 100644 index 000000000..26f2050f6 --- /dev/null +++ b/client/hhhh.go @@ -0,0 +1,98 @@ +package main + +/* +import ( + "flag" + "github.com/netbirdio/netbird/iface" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + "net" + "net/http" + _ "net/http/pprof" + "time" +) + +var name = flag.String("name", "wg0", "WireGuard interface name") +var addr = flag.String("addr", "100.64.0.1/24", "interface WireGuard IP addr") +var key = flag.String("key", "100.64.0.1/24", "WireGuard private key") +var port = flag.Int("port", 51820, "WireGuard port") + +var remoteKey = flag.String("remote-key", "", "remote WireGuard public key") +var remoteAddr = flag.String("remote-addr", "100.64.0.2/32", "remote WireGuard IP addr") +var remoteEndpoint = flag.String("remote-endpoint", "127.0.0.1:51820", "remote WireGuard endpoint") + +func fff() { + + flag.Parse() + + go func() { + log.Println(http.ListenAndServe("localhost:6060", nil)) + }() + + myKey, err := wgtypes.ParseKey(*key) + if err != nil { + log.Error(err) + return + } + + log.Infof("public key and addr [%s] [%s] ", myKey.PublicKey().String(), *addr) + + wgIFace, err := iface.NewWGIFace(*name, *addr, 1280) + if err != nil { + log.Error(err) + return + } + defer wgIFace.Close() + + // todo wrap into UDPMux + sharedSock, _, err := listenNet("udp4", *port) + if err != nil { + log.Error(err) + return + } + defer sharedSock.Close() + + // err = wgIFace.Create() + err = wgIFace.CreateNew(sharedSock) + if err != nil { + log.Errorf("failed to create interface %s %v", *name, err) + return + } + + err = wgIFace.Configure(*key, *port) + if err != nil { + log.Errorf("failed to configure interface %s %v", *name, err) + return + } + + ip, err := net.ResolveUDPAddr("udp4", *remoteEndpoint) + if err != nil { + // handle error + } + + err = wgIFace.UpdatePeer(*remoteKey, *remoteAddr, 20*time.Second, ip, nil) + if err != nil { + log.Errorf("failed to configure remote peer %s %v", *remoteKey, err) + return + } + + select {} + +} + +func listenNet(network string, port int) (*net.UDPConn, int, error) { + conn, err := net.ListenUDP(network, &net.UDPAddr{Port: port}) + if err != nil { + return nil, 0, err + } + + // Retrieve port. + laddr := conn.LocalAddr() + uaddr, err := net.ResolveUDPAddr( + laddr.Network(), + laddr.String(), + ) + if err != nil { + return nil, 0, err + } + return conn, uaddr.Port, nil +}*/ diff --git a/client/internal/engine.go b/client/internal/engine.go index 0a322c483..546e90fb4 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -5,8 +5,10 @@ import ( "fmt" nbssh "github.com/netbirdio/netbird/client/ssh" nbstatus "github.com/netbirdio/netbird/client/status" + "github.com/pion/stun" "math/rand" "net" + "net/netip" "reflect" "runtime" "strings" @@ -187,6 +189,49 @@ func (e *Engine) Stop() error { return nil } +// Matching rules come from +// https://tools.ietf.org/html/rfc7983 +func isWebRTC(p []byte, n int) bool { + if len(p) == 0 { + return true + } else if p[0] <= 3 { // STUN + return true + } else if p[0] >= 20 && p[0] <= 63 { // DTLS + return true + } else if p[0] >= 64 && p[0] <= 79 { // TURN + return true + } else if p[0] >= 128 && p[0] <= 191 { // RTP and RTCP + return true + } + + return false +} + +type sharedUDPConn struct { + net.PacketConn + bind *iface.UserBind +} + +func (s *sharedUDPConn) ReadFrom(buff []byte) (n int, addr net.Addr, err error) { + if n, addr, err = s.PacketConn.ReadFrom(buff); err == nil { + bytes := make([]byte, n) + copy(bytes, buff) + if !stun.IsMessage(bytes[:n]) { + e, err := netip.ParseAddrPort(addr.String()) + if err != nil { + return 0, nil, err + } + s.bind.OnData(bytes, &net.UDPAddr{ + IP: e.Addr().AsSlice(), + Port: int(e.Port()), + Zone: e.Addr().Zone(), + }) + } + } + + return +} + // Start creates a new Wireguard tunnel interface and listens to events from Signal and Management services // Connections to remote peers are not established here. // However, they will be established once an event with a list of peers to connect to will be received from Management Service @@ -216,17 +261,23 @@ func (e *Engine) Start() error { log.Errorf("failed listening on UDP port %d: [%s]", e.config.UDPMuxSrflxPort, err.Error()) return err } - e.udpMux = ice.NewUDPMuxDefault(ice.UDPMuxParams{UDPConn: e.udpMuxConn}) - e.udpMuxSrflx = ice.NewUniversalUDPMuxDefault(ice.UniversalUDPMuxParams{UDPConn: e.udpMuxConnSrflx}) - - err = e.wgInterface.Create() + s := &sharedUDPConn{PacketConn: e.udpMuxConnSrflx} + bind := iface.NewUserBind(s) + s.bind = bind + e.udpMuxSrflx = ice.NewUniversalUDPMuxDefault(ice.UniversalUDPMuxParams{UDPConn: s}) + err = e.wgInterface.CreateNew(bind) if err != nil { log.Errorf("failed creating tunnel interface %s: [%s]", wgIfaceName, err.Error()) return err } - err = e.wgInterface.Configure(myPrivateKey.String(), e.config.WgPort) + log.Infof("shared sock ------------------> %s", s.LocalAddr().String()) + addrPort, err := netip.ParseAddrPort(s.LocalAddr().String()) + if err != nil { + return err + } + err = e.wgInterface.Configure(myPrivateKey.String(), int(addrPort.Port())) if err != nil { log.Errorf("failed configuring Wireguard interface [%s]: %s", wgIfaceName, err.Error()) return err diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index 1bb6bf823..f11db253c 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -147,7 +147,7 @@ func (conn *Conn) reCreateAgent() error { MulticastDNSMode: ice.MulticastDNSModeDisabled, NetworkTypes: []ice.NetworkType{ice.NetworkTypeUDP4}, Urls: conn.config.StunTurn, - CandidateTypes: []ice.CandidateType{ice.CandidateTypeHost, ice.CandidateTypeServerReflexive, ice.CandidateTypeRelay}, + CandidateTypes: []ice.CandidateType{ice.CandidateTypeServerReflexive}, FailedTimeout: &failedTimeout, InterfaceFilter: interfaceFilter(conn.config.InterfaceBlackList), UDPMux: conn.config.UDPMux, @@ -280,14 +280,7 @@ func (conn *Conn) Open() error { return err } - if conn.proxy.Type() == proxy.TypeNoProxy { - host, _, _ := net.SplitHostPort(remoteConn.LocalAddr().String()) - rhost, _, _ := net.SplitHostPort(remoteConn.RemoteAddr().String()) - // direct Wireguard connection - log.Infof("directly connected to peer %s [laddr <-> raddr] [%s:%d <-> %s:%d]", conn.config.Key, host, iface.DefaultWgPort, rhost, iface.DefaultWgPort) - } else { - log.Infof("connected to peer %s [laddr <-> raddr] [%s <-> %s]", conn.config.Key, remoteConn.LocalAddr().String(), remoteConn.RemoteAddr().String()) - } + log.Infof("connected to peer %s [laddr <-> raddr] [%s <-> %s]", conn.config.Key, remoteConn.LocalAddr().String(), remoteConn.RemoteAddr().String()) // wait until connection disconnected or has been closed externally (upper layer, e.g. engine) select { @@ -351,15 +344,9 @@ func (conn *Conn) startProxy(remoteConn net.Conn, remoteWgPort int) error { } peerState := nbStatus.PeerState{PubKey: conn.config.Key} - useProxy := shouldUseProxy(pair) - var p proxy.Proxy - if useProxy { - p = proxy.NewWireguardProxy(conn.config.ProxyConfig) - peerState.Direct = false - } else { - p = proxy.NewNoProxy(conn.config.ProxyConfig, remoteWgPort) - peerState.Direct = true - } + + p := proxy.NewNoProxy(conn.config.ProxyConfig, remoteWgPort) + peerState.Direct = true conn.proxy = p err = p.Start(remoteConn) if err != nil { diff --git a/client/internal/proxy/noproxy.go b/client/internal/proxy/noproxy.go index 361ab68bb..a33639b0c 100644 --- a/client/internal/proxy/noproxy.go +++ b/client/internal/proxy/noproxy.go @@ -39,7 +39,6 @@ func (p *NoProxy) Start(remoteConn net.Conn) error { if err != nil { return err } - addr.Port = p.RemoteWgListenPort err = p.config.WgInterface.UpdatePeer(p.config.RemoteKey, p.config.AllowedIps, DefaultWgKeepAlive, addr, p.config.PreSharedKey) diff --git a/client/main.go b/client/main.go index ffe201dde..2a70590fe 100644 --- a/client/main.go +++ b/client/main.go @@ -1,99 +1,12 @@ package main import ( - "flag" - "github.com/netbirdio/netbird/iface" - log "github.com/sirupsen/logrus" - "golang.zx2c4.com/wireguard/wgctrl/wgtypes" - "net" - "net/http" - "time" - - _ "net/http/pprof" + "github.com/netbirdio/netbird/client/cmd" + "os" ) -var name = flag.String("name", "wg0", "WireGuard interface name") -var addr = flag.String("addr", "100.64.0.1/24", "interface WireGuard IP addr") -var key = flag.String("key", "100.64.0.1/24", "WireGuard private key") -var port = flag.Int("port", 51820, "WireGuard port") - -var remoteKey = flag.String("remote-key", "", "remote WireGuard public key") -var remoteAddr = flag.String("remote-addr", "100.64.0.2/32", "remote WireGuard IP addr") -var remoteEndpoint = flag.String("remote-endpoint", "127.0.0.1:51820", "remote WireGuard endpoint") - func main() { - - flag.Parse() - - go func() { - log.Println(http.ListenAndServe("localhost:6060", nil)) - }() - - myKey, err := wgtypes.ParseKey(*key) - if err != nil { - log.Error(err) - return + if err := cmd.Execute(); err != nil { + os.Exit(1) } - - log.Infof("public key and addr [%s] [%s] ", myKey.PublicKey().String(), *addr) - - wgIFace, err := iface.NewWGIFace(*name, *addr, 1280) - if err != nil { - log.Error(err) - return - } - defer wgIFace.Close() - - // todo wrap into UDPMux - sharedSock, _, err := listenNet("udp4", *port) - if err != nil { - log.Error(err) - return - } - defer sharedSock.Close() - - // err = wgIFace.Create() - err = wgIFace.CreateNew(sharedSock) - if err != nil { - log.Errorf("failed to create interface %s %v", *name, err) - return - } - - err = wgIFace.Configure(*key, *port) - if err != nil { - log.Errorf("failed to configure interface %s %v", *name, err) - return - } - - ip, err := net.ResolveUDPAddr("udp4", *remoteEndpoint) - if err != nil { - // handle error - } - - err = wgIFace.UpdatePeer(*remoteKey, *remoteAddr, 20*time.Second, ip, nil) - if err != nil { - log.Errorf("failed to configure remote peer %s %v", *remoteKey, err) - return - } - - select {} - -} - -func listenNet(network string, port int) (*net.UDPConn, int, error) { - conn, err := net.ListenUDP(network, &net.UDPAddr{Port: port}) - if err != nil { - return nil, 0, err - } - - // Retrieve port. - laddr := conn.LocalAddr() - uaddr, err := net.ResolveUDPAddr( - laddr.Network(), - laddr.String(), - ) - if err != nil { - return nil, 0, err - } - return conn, uaddr.Port, nil } diff --git a/go.mod b/go.mod index 98ab1ebfc..047818ef1 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require ( github.com/gliderlabs/ssh v0.3.4 github.com/magiconair/properties v1.8.5 github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/pion/stun v0.3.5 github.com/rs/xid v1.3.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/stretchr/testify v1.7.1 @@ -81,7 +82,6 @@ require ( github.com/pion/logging v0.2.2 // indirect github.com/pion/mdns v0.0.5 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/stun v0.3.5 // indirect github.com/pion/transport v0.13.0 // indirect github.com/pion/turn/v2 v2.0.7 // indirect github.com/pion/udp v0.1.1 // indirect diff --git a/iface/bind.go b/iface/bind.go index 9df2a168f..5203c7efa 100644 --- a/iface/bind.go +++ b/iface/bind.go @@ -1,152 +1,110 @@ package iface import ( - log "github.com/sirupsen/logrus" "golang.zx2c4.com/wireguard/conn" "net" "net/netip" "sync" ) -type packets struct { +type UserEndpoint struct { + conn.StdNetEndpoint +} + +type packet struct { buff []byte - addr net.UDPAddr + addr *net.UDPAddr } -type ICEBind struct { - mu sync.Mutex - packets chan packets +type UserBind struct { + endpointsLock sync.RWMutex + endpoints map[netip.AddrPort]*UserEndpoint + sharedConn net.PacketConn + + Packets chan packet closeSignal chan struct{} - conn *net.UDPConn } -func NewICEBind(udpConn *net.UDPConn) *ICEBind { - return &ICEBind{ - conn: udpConn, - mu: sync.Mutex{}, - } +func NewUserBind(sharedConn net.PacketConn) *UserBind { + return &UserBind{sharedConn: sharedConn} } -func (bind *ICEBind) Open(port uint16) ([]conn.ReceiveFunc, uint16, error) { +func (b *UserBind) Open(port uint16) ([]conn.ReceiveFunc, uint16, error) { - bind.mu.Lock() - defer bind.mu.Unlock() - log.Infof("opening Bind on port %d", port) + b.Packets = make(chan packet, 1000) + b.closeSignal = make(chan struct{}) - udpConn, p, err := listenNet("udp4", int(port)) + return []conn.ReceiveFunc{b.receive}, port, nil +} + +func (b *UserBind) receive(buff []byte) (int, conn.Endpoint, error) { + + /*n, endpoint, err := b.sharedConn.ReadFrom(buff) if err != nil { - return nil, 0, err + return 0, nil, err } - bind.conn = udpConn - return []conn.ReceiveFunc{bind.makeReceiveIPv4(udpConn)}, uint16(p), nil - - /*bind.packets = make(chan packets) - bind.closeSignal = make(chan struct{}) - - addrPort, err := netip.ParseAddrPort(bind.conn.LocalAddr().String()) + e, err := netip.ParseAddrPort(endpoint.String()) if err != nil { - return nil, 0, err + return 0, nil, err } - - return []conn.ReceiveFunc{bind.makeReceiveIPv4(bind.conn)}, addrPort.Port(), nil*/ -} - -func (bind *ICEBind) fakeReceiveIPv4(c *net.UDPConn) conn.ReceiveFunc { - return func(buff []byte) (int, conn.Endpoint, error) { - return 0, nil, nil - } -} - -func (bind *ICEBind) makeReceiveIPv4(c *net.UDPConn) conn.ReceiveFunc { - return func(buff []byte) (int, conn.Endpoint, error) { - n, endpoint, err := c.ReadFromUDP(buff) - if endpoint != nil { - endpoint.IP = endpoint.IP.To4() - } - return n, (*conn.StdNetEndpoint)(endpoint), err - } -} - -/*func (bind *ICEBind) receive(buff []byte) (int, conn.Endpoint, error) { - n, endpoint, err := bind.conn.ReadFromUDP(buff) - if endpoint != nil { - endpoint.IP = endpoint.IP.To4() - } - return n, (*conn.StdNetEndpoint)(endpoint), err + return n, (*conn.StdNetEndpoint)(&net.UDPAddr{ + IP: e.addr().AsSlice(), + Port: int(e.Port()), + Zone: e.addr().Zone(), + }), err*/ select { - case <-bind.closeSignal: + case <-b.closeSignal: return 0, nil, net.ErrClosed - case pkt := <-bind.packets: - return copy(buf, pkt.buff), (*conn.StdNetEndpoint)(&pkt.addr), nil + case pkt := <-b.Packets: + /*log.Infof("received packet %d from %s to copy to buffer %d", binary.Size(pkt.buff), pkt.addr.String(), + len(buff))*/ + return copy(buff, pkt.buff), (*conn.StdNetEndpoint)(pkt.addr), nil } -}*/ +} -func (bind *ICEBind) Close() error { - bind.mu.Lock() - defer bind.mu.Unlock() - - err := bind.conn.Close() - if err != nil { - return err - } - - if bind.closeSignal != nil { +func (b *UserBind) Close() error { + if b.closeSignal != nil { select { - case <-bind.closeSignal: + case <-b.closeSignal: default: - close(bind.closeSignal) + close(b.closeSignal) } - bind.packets = nil } return nil } // SetMark sets the mark for each packet sent through this Bind. // This mark is passed to the kernel as the socket option SO_MARK. -func (bind *ICEBind) SetMark(mark uint32) error { +func (b *UserBind) SetMark(mark uint32) error { return nil } -func (bind *ICEBind) Send(buf []byte, endpoint conn.Endpoint) error { - +func (b *UserBind) Send(buff []byte, endpoint conn.Endpoint) error { nend, ok := endpoint.(*conn.StdNetEndpoint) if !ok { return conn.ErrWrongEndpointType } - _, err := bind.conn.WriteToUDP(buf, (*net.UDPAddr)(nend)) + //log.Infof("sending packet %d from %s to %s", binary.Size(buff), b.sharedConn.LocalAddr().String(), (*net.UDPAddr)(nend).String()) + + _, err := b.sharedConn.WriteTo(buff, (*net.UDPAddr)(nend)) return err } // ParseEndpoint creates a new endpoint from a string. -func (bind *ICEBind) ParseEndpoint(s string) (ep conn.Endpoint, err error) { - ap, err := netip.ParseAddrPort(s) - if err != nil { - return nil, err - } - +func (b *UserBind) ParseEndpoint(s string) (ep conn.Endpoint, err error) { + e, err := netip.ParseAddrPort(s) return (*conn.StdNetEndpoint)(&net.UDPAddr{ - IP: ap.Addr().AsSlice(), - Port: int(ap.Port()), - Zone: ap.Addr().Zone(), + IP: e.Addr().AsSlice(), + Port: int(e.Port()), + Zone: e.Addr().Zone(), }), err } -func listenNet(network string, port int) (*net.UDPConn, int, error) { - conn, err := net.ListenUDP(network, &net.UDPAddr{Port: port}) - if err != nil { - return nil, 0, err +func (b *UserBind) OnData(buff []byte, addr *net.UDPAddr) { + b.Packets <- packet{ + buff: buff, + addr: addr, } - - // Retrieve port. - laddr := conn.LocalAddr() - uaddr, err := net.ResolveUDPAddr( - laddr.Network(), - laddr.String(), - ) - if err != nil { - return nil, 0, err - } - return conn, uaddr.Port, nil } diff --git a/iface/iface.go b/iface/iface.go index bdfa78abb..5e4c5c7a6 100644 --- a/iface/iface.go +++ b/iface/iface.go @@ -21,6 +21,7 @@ type WGIface struct { Address WGAddress Interface NetInterface mu sync.Mutex + Bind *UserBind } // WGAddress Wireguard parsed address diff --git a/iface/iface_linux.go b/iface/iface_linux.go index 3023e4a76..d497cb96b 100644 --- a/iface/iface_linux.go +++ b/iface/iface_linux.go @@ -2,8 +2,8 @@ package iface import ( "errors" + "golang.zx2c4.com/wireguard/conn" "math" - "net" "os" "syscall" @@ -33,11 +33,11 @@ func WireguardModExists() bool { return errors.Is(err, syscall.EINVAL) } -func (w *WGIface) CreateNew(sharedSock *net.UDPConn) error { +func (w *WGIface) CreateNew(bind conn.Bind) error { w.mu.Lock() defer w.mu.Unlock() - return w.createWithUserspaceNew(sharedSock) + return w.createWithUserspaceNew(bind) } // Create creates a new Wireguard interface, sets a given IP and brings it up. diff --git a/iface/iface_unix.go b/iface/iface_unix.go index fbed919d2..719114da5 100644 --- a/iface/iface_unix.go +++ b/iface/iface_unix.go @@ -12,16 +12,13 @@ import ( "net" ) -func (w *WGIface) createWithUserspaceNew(sharedSock *net.UDPConn) error { +func (w *WGIface) createWithUserspaceNew(bind conn.Bind) error { tunIface, err := tun.CreateTUN(w.Name, w.MTU) if err != nil { return err } w.Interface = tunIface - bind := &ICEBind{ - conn: sharedSock, - } // We need to create a wireguard-go device and listen to configuration requests tunDevice := device.NewDevice(tunIface, bind, device.NewLogger(device.LogLevelSilent, "[wiretrustee] "))