diff --git a/client/internal/engine.go b/client/internal/engine.go index d7e78cbf1..e2effecc3 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -18,7 +18,6 @@ import ( "github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/peer" - "github.com/netbirdio/netbird/client/internal/proxy" "github.com/netbirdio/netbird/client/internal/routemanager" nbssh "github.com/netbirdio/netbird/client/ssh" nbdns "github.com/netbirdio/netbird/dns" @@ -262,7 +261,7 @@ func (e *Engine) modifyPeers(peersUpdate []*mgmProto.RemotePeerConfig) error { for _, p := range peersUpdate { peerPubKey := p.GetWgPubKey() if peerConn, ok := e.peerConns[peerPubKey]; ok { - if peerConn.GetConf().ProxyConfig.AllowedIps != strings.Join(p.AllowedIps, ",") { + if peerConn.WgConfig().AllowedIps != strings.Join(p.AllowedIps, ",") { modified = append(modified, p) continue } @@ -776,9 +775,7 @@ func (e *Engine) connWorker(conn *peer.Conn, peerKey string) { // we might have received new STUN and TURN servers meanwhile, so update them e.syncMsgMux.Lock() - conf := conn.GetConf() - conf.StunTurn = append(e.STUNs, e.TURNs...) - conn.UpdateConf(conf) + conn.UpdateStunTurn(append(e.STUNs, e.TURNs...)) e.syncMsgMux.Unlock() err := conn.Open() @@ -807,7 +804,7 @@ func (e Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, er stunTurn = append(stunTurn, e.STUNs...) stunTurn = append(stunTurn, e.TURNs...) - proxyConfig := proxy.Config{ + wgConfig := peer.WgConfig{ RemoteKey: pubKey, WgListenAddr: fmt.Sprintf("127.0.0.1:%d", e.config.WgPort), WgInterface: e.wgInterface, @@ -826,7 +823,7 @@ func (e Engine) createPeerConn(pubKey string, allowedIPs string) (*peer.Conn, er Timeout: timeout, UDPMux: e.udpMux, UDPMuxSrflx: e.udpMuxSrflx, - ProxyConfig: proxyConfig, + WgConfig: wgConfig, LocalWgPort: e.config.WgPort, NATExternalIPs: e.parseNATExternalIPMappings(), UserspaceBind: e.wgInterface.IsUserspaceBind(), diff --git a/client/internal/engine_test.go b/client/internal/engine_test.go index 85413e049..6e02143d8 100644 --- a/client/internal/engine_test.go +++ b/client/internal/engine_test.go @@ -3,8 +3,6 @@ package internal import ( "context" "fmt" - "github.com/netbirdio/netbird/iface/bind" - "github.com/pion/transport/v2/stdnet" "net" "net/netip" "os" @@ -15,6 +13,10 @@ import ( "testing" "time" + "github.com/pion/transport/v2/stdnet" + + "github.com/netbirdio/netbird/iface/bind" + log "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -367,9 +369,9 @@ func TestEngine_UpdateNetworkMap(t *testing.T) { t.Errorf("expecting Engine.peerConns to contain peer %s", p) } expectedAllowedIPs := strings.Join(p.AllowedIps, ",") - if conn.GetConf().ProxyConfig.AllowedIps != expectedAllowedIPs { + if conn.WgConfig().AllowedIps != expectedAllowedIPs { t.Errorf("expecting peer %s to have AllowedIPs= %s, got %s", p.GetWgPubKey(), - expectedAllowedIPs, conn.GetConf().ProxyConfig.AllowedIps) + expectedAllowedIPs, conn.WgConfig().AllowedIps) } } }) diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index 423a626d1..241493be6 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -11,7 +11,6 @@ import ( "github.com/pion/ice/v2" log "github.com/sirupsen/logrus" - "github.com/netbirdio/netbird/client/internal/proxy" "github.com/netbirdio/netbird/client/internal/stdnet" "github.com/netbirdio/netbird/iface" signal "github.com/netbirdio/netbird/signal/client" @@ -37,7 +36,7 @@ type ConnConfig struct { Timeout time.Duration - ProxyConfig proxy.Config + WgConfig WgConfig UDPMux ice.UDPMux UDPMuxSrflx ice.UniversalUDPMux @@ -92,7 +91,8 @@ type Conn struct { statusRecorder *Status - proxy proxy.Proxy + proxy proxy + wgPeerMgr *wgPeerManager remoteModeCh chan ModeMessage meta meta @@ -111,14 +111,14 @@ type ModeMessage struct { Direct bool } -// GetConf returns the connection config -func (conn *Conn) GetConf() ConnConfig { - return conn.config +// WgConfig returns the WireGuard config +func (conn *Conn) WgConfig() WgConfig { + return conn.config.WgConfig } -// UpdateConf updates the connection config -func (conn *Conn) UpdateConf(conf ConnConfig) { - conn.config = conf +// UpdateStunTurn update the turn and stun addresses +func (conn *Conn) UpdateStunTurn(turnStun []*ice.URL) { + conn.config.StunTurn = turnStun } // NewConn creates a new not opened Conn to the remote peer. @@ -198,7 +198,7 @@ func (conn *Conn) Open() error { peerState := State{PubKey: conn.config.Key} - peerState.IP = strings.Split(conn.config.ProxyConfig.AllowedIps, "/")[0] + peerState.IP = strings.Split(conn.config.WgConfig.AllowedIps, "/")[0] peerState.ConnStatusUpdate = time.Now() peerState.ConnStatus = conn.status @@ -290,19 +290,12 @@ func (conn *Conn) Open() error { remoteWgPort = remoteOfferAnswer.WgListenPort } // the ice connection has been established successfully so we are ready to start the proxy - err = conn.startProxy(remoteConn, remoteWgPort) + err = conn.configureConnection(remoteConn, remoteWgPort) if err != nil { return err } - if conn.proxy.Type() == proxy.TypeDirectNoProxy { - 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, conn.config.LocalWgPort, rhost, remoteWgPort) - } 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 with proxy %v, [laddr <-> raddr] [%s <-> %s]", conn.config.Key, conn.proxy != nil, laddr, conn.wgPeerMgr.remoteAddr.String()) // wait until connection disconnected or has been closed externally (upper layer, e.g. engine) select { @@ -315,7 +308,7 @@ func (conn *Conn) Open() error { } } -// useProxy determines whether a direct connection (without a go proxy) is possible +// isPreferredDirectMode determines whether a direct connection (without a go proxy) is not possible // // There are 3 cases: // @@ -326,35 +319,33 @@ func (conn *Conn) Open() error { // * Local peer uses userspace interface with bind.ICEBind and is not relayed // // Please note, that this check happens when peers were already able to ping each other using ICE layer. -func shouldUseProxy(pair *ice.CandidatePair, userspaceBind bool) bool { - +func isPreferredDirectMode(pair *ice.CandidatePair, userspaceBind bool) bool { if !isRelayCandidate(pair.Local) && userspaceBind { log.Debugf("shouldn't use proxy because using Bind and the connection is not relayed") - return false + return true } if !isHardNATCandidate(pair.Local) && isHostCandidateWithPublicIP(pair.Remote) { log.Debugf("shouldn't use proxy because the local peer is not behind a hard NAT and the remote one has a public IP") - return false + return true } if !isHardNATCandidate(pair.Remote) && isHostCandidateWithPublicIP(pair.Local) { log.Debugf("shouldn't use proxy because the remote peer is not behind a hard NAT and the local one has a public IP") - return false + return true } if isHostCandidateWithPrivateIP(pair.Local) && isHostCandidateWithPrivateIP(pair.Remote) && isSameNetworkPrefix(pair) { log.Debugf("shouldn't use proxy because peers are in the same private /16 network") - return false + return true } if (isPeerReflexiveCandidateWithPrivateIP(pair.Local) && isHostCandidateWithPrivateIP(pair.Remote) || isHostCandidateWithPrivateIP(pair.Local) && isPeerReflexiveCandidateWithPrivateIP(pair.Remote)) && isSameNetworkPrefix(pair) { log.Debugf("shouldn't use proxy because peers are in the same private /16 network and one peer is peer reflexive") - return false + return true } - - return true + return false } func isSameNetworkPrefix(pair *ice.CandidatePair) bool { @@ -397,27 +388,38 @@ func isPublicIP(address string) bool { return true } -// startProxy starts proxying traffic from/to local Wireguard and sets connection status to StatusConnected -func (conn *Conn) startProxy(remoteConn net.Conn, remoteWgPort int) error { +// configureConnection starts proxying traffic from/to local Wireguard and sets connection status to StatusConnected +func (conn *Conn) configureConnection(remoteConn net.Conn, remoteWgPort int) error { conn.mu.Lock() defer conn.mu.Unlock() - var pair *ice.CandidatePair pair, err := conn.agent.GetSelectedCandidatePair() if err != nil { return err } - peerState := State{PubKey: conn.config.Key} - p := conn.getProxyWithMessageExchange(pair, remoteWgPort) - conn.proxy = p - err = p.Start(remoteConn) + localDirectMode, remoteDirectMode := conn.getNetworkConditions(pair) + + wgPeerMgr := newWgPeerManager(conn.config.WgConfig) + err = wgPeerMgr.configureWgPeer(localDirectMode, remoteDirectMode, conn.config.UserspaceBind, remoteConn, remoteWgPort) if err != nil { return err } + conn.wgPeerMgr = wgPeerMgr + + if conn.isProxyNeeded(localDirectMode, remoteDirectMode) { + p := NewWireGuardProxy(conn.config.WgConfig.WgListenAddr, conn.config.WgConfig.RemoteKey) + err = p.Start(remoteConn) + if err != nil { + return err + } + conn.proxy = p + } conn.status = StatusConnected + // update Peer's state + peerState := State{PubKey: conn.config.Key} peerState.ConnStatus = conn.status peerState.ConnStatusUpdate = time.Now() peerState.LocalIceCandidateType = pair.Local.Type().String() @@ -425,7 +427,7 @@ func (conn *Conn) startProxy(remoteConn net.Conn, remoteWgPort int) error { if pair.Local.Type() == ice.CandidateTypeRelay || pair.Remote.Type() == ice.CandidateTypeRelay { peerState.Relayed = true } - peerState.Direct = p.Type() == proxy.TypeDirectNoProxy || p.Type() == proxy.TypeNoProxy + peerState.Direct = conn.proxy == nil err = conn.statusRecorder.UpdatePeerState(peerState) if err != nil { @@ -435,27 +437,29 @@ func (conn *Conn) startProxy(remoteConn net.Conn, remoteWgPort int) error { return nil } -func (conn *Conn) getProxyWithMessageExchange(pair *ice.CandidatePair, remoteWgPort int) proxy.Proxy { - useProxy := shouldUseProxy(pair, conn.config.UserspaceBind) - localDirectMode := !useProxy - remoteDirectMode := localDirectMode +func (conn *Conn) getNetworkConditions(pair *ice.CandidatePair) (bool, bool) { + var localDirectMode, remoteDirectMode bool + localDirectMode = isPreferredDirectMode(pair, conn.config.UserspaceBind) if conn.meta.protoSupport.DirectCheck { go conn.sendLocalDirectMode(localDirectMode) // will block until message received or timeout remoteDirectMode = conn.receiveRemoteDirectMode() + } else { + remoteDirectMode = localDirectMode } + return localDirectMode, remoteDirectMode +} +func (conn *Conn) isProxyNeeded(localDirectMode, remoteDirectMode bool) bool { if conn.config.UserspaceBind && localDirectMode { - return proxy.NewNoProxy(conn.config.ProxyConfig) + return false } if localDirectMode && remoteDirectMode { - return proxy.NewDirectNoProxy(conn.config.ProxyConfig, remoteWgPort) + return false } - - log.Debugf("falling back to local proxy mode with peer %s", conn.config.Key) - return proxy.NewWireGuardProxy(conn.config.ProxyConfig) + return true } func (conn *Conn) sendLocalDirectMode(localMode bool) { @@ -500,20 +504,26 @@ func (conn *Conn) cleanup() error { conn.mu.Lock() defer conn.mu.Unlock() + var err1, err2, err3 error if conn.agent != nil { - err := conn.agent.Close() - if err != nil { - return err + err1 = conn.agent.Close() + if err1 == nil { + conn.agent = nil + } + } + + if conn.wgPeerMgr != nil { + err2 = conn.wgPeerMgr.close() + if err2 == nil { + conn.wgPeerMgr = nil } - conn.agent = nil } if conn.proxy != nil { - err := conn.proxy.Close() - if err != nil { - return err + err3 = conn.proxy.Close() + if err3 == nil { + conn.proxy = nil } - conn.proxy = nil } if conn.notifyDisconnected != nil { @@ -535,8 +545,13 @@ func (conn *Conn) cleanup() error { } log.Debugf("cleaned up connection to peer %s", conn.config.Key) - - return nil + if err1 != nil { + return err1 + } + if err2 != nil { + return err2 + } + return err3 } // SetSignalOffer sets a handler function to be triggered by Conn when a new connection offer has to be signalled to the remote peer diff --git a/client/internal/peer/conn_test.go b/client/internal/peer/conn_test.go index c34f6fed5..9bfbd7f69 100644 --- a/client/internal/peer/conn_test.go +++ b/client/internal/peer/conn_test.go @@ -11,7 +11,6 @@ import ( "github.com/pion/ice/v2" "golang.org/x/sync/errgroup" - "github.com/netbirdio/netbird/client/internal/proxy" "github.com/netbirdio/netbird/iface" sproto "github.com/netbirdio/netbird/signal/proto" ) @@ -22,7 +21,7 @@ var connConf = ConnConfig{ StunTurn: []*ice.URL{}, InterfaceBlackList: nil, Timeout: time.Second, - ProxyConfig: proxy.Config{}, + WgConfig: WgConfig{}, LocalWgPort: 51820, } @@ -244,87 +243,87 @@ func TestConn_ShouldUseProxy(t *testing.T) { expected bool }{ { - name: "Use Proxy When Local Candidate Is Relay", + name: "Use proxy When Local Candidate Is Relay", candatePair: &ice.CandidatePair{ Local: relayCandidate, Remote: privateHostCandidate, }, - expected: true, + expected: false, }, { - name: "Use Proxy When Remote Candidate Is Relay", + name: "Use proxy When Remote Candidate Is Relay", candatePair: &ice.CandidatePair{ Local: privateHostCandidate, Remote: relayCandidate, }, - expected: true, + expected: false, }, { - name: "Use Proxy When Local Candidate Is Peer Reflexive", + name: "Use proxy When Local Candidate Is Peer Reflexive", candatePair: &ice.CandidatePair{ Local: prflxCandidate, Remote: privateHostCandidate, }, - expected: true, + expected: false, }, { - name: "Use Proxy When Remote Candidate Is Peer Reflexive", + name: "Use proxy When Remote Candidate Is Peer Reflexive", candatePair: &ice.CandidatePair{ Local: privateHostCandidate, Remote: prflxCandidate, }, - expected: true, + expected: false, }, { - name: "Don't Use Proxy When Local Candidate Is Public And Remote Is Private", + name: "Don't Use proxy When Local Candidate Is Public And Remote Is Private", candatePair: &ice.CandidatePair{ Local: publicHostCandidate, Remote: privateHostCandidate, }, - expected: false, + expected: true, }, { - name: "Don't Use Proxy When Remote Candidate Is Public And Local Is Private", + name: "Don't Use proxy When Remote Candidate Is Public And Local Is Private", candatePair: &ice.CandidatePair{ Local: privateHostCandidate, Remote: publicHostCandidate, }, - expected: false, + expected: true, }, { - name: "Don't Use Proxy When Local Candidate is Public And Remote Is Server Reflexive", + name: "Don't Use proxy When Local Candidate is Public And Remote Is Server Reflexive", candatePair: &ice.CandidatePair{ Local: publicHostCandidate, Remote: srflxCandidate, }, - expected: false, + expected: true, }, { - name: "Don't Use Proxy When Remote Candidate is Public And Local Is Server Reflexive", + name: "Don't Use proxy When Remote Candidate is Public And Local Is Server Reflexive", candatePair: &ice.CandidatePair{ Local: srflxCandidate, Remote: publicHostCandidate, }, - expected: false, + expected: true, }, { - name: "Don't Use Proxy When Both Candidates Are Public", + name: "Don't Use proxy When Both Candidates Are Public", candatePair: &ice.CandidatePair{ Local: publicHostCandidate, Remote: publicHostCandidate, }, - expected: false, + expected: true, }, { - name: "Don't Use Proxy When Both Candidates Are Private", + name: "Don't Use proxy When Both Candidates Are Private", candatePair: &ice.CandidatePair{ Local: privateHostCandidate, Remote: privateHostCandidate, }, - expected: false, + expected: true, }, { - name: "Don't Use Proxy When Both Candidates are in private network and one is peer reflexive", + name: "Don't Use proxy When Both Candidates are in private network and one is peer reflexive", candatePair: &ice.CandidatePair{ Local: &mockICECandidate{AddressFunc: func() string { return "10.16.102.168" @@ -339,10 +338,10 @@ func TestConn_ShouldUseProxy(t *testing.T) { return ice.CandidateTypePeerReflexive }}, }, - expected: false, + expected: true, }, { - name: "Should Use Proxy When Both Candidates are in private network and both are peer reflexive", + name: "Should Use proxy When Both Candidates are in private network and both are peer reflexive", candatePair: &ice.CandidatePair{ Local: &mockICECandidate{AddressFunc: func() string { return "10.16.102.168" @@ -357,13 +356,13 @@ func TestConn_ShouldUseProxy(t *testing.T) { return ice.CandidateTypePeerReflexive }}, }, - expected: true, + expected: false, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - result := shouldUseProxy(testCase.candatePair, false) + result := isPreferredDirectMode(testCase.candatePair, false) if result != testCase.expected { t.Errorf("got a different result. Expected %t Got %t", testCase.expected, result) } @@ -394,57 +393,57 @@ func TestGetProxyWithMessageExchange(t *testing.T) { candatePair *ice.CandidatePair inputDirectModeSupport bool inputRemoteModeMessage bool - expected proxy.Type + expected bool }{ { - name: "Should Result In Using Wireguard Proxy When Local Eval Is Use Proxy", + name: "Should Result In Using Wireguard proxy When Local Eval Is Use proxy", candatePair: &ice.CandidatePair{ Local: relayCandidate, Remote: publicHostCandidate, }, inputDirectModeSupport: true, inputRemoteModeMessage: true, - expected: proxy.TypeWireGuard, + expected: true, }, { - name: "Should Result In Using Wireguard Proxy When Remote Eval Is Use Proxy", + name: "Should Result In Using Wireguard proxy When Remote Eval Is Use proxy", candatePair: &ice.CandidatePair{ Local: publicHostCandidate, Remote: publicHostCandidate, }, inputDirectModeSupport: true, inputRemoteModeMessage: false, - expected: proxy.TypeWireGuard, + expected: true, }, { - name: "Should Result In Using Wireguard Proxy When Remote Direct Mode Support Is False And Local Eval Is Use Proxy", + name: "Should Result In Using Wireguard proxy When Remote Direct Mode Support Is False And Local Eval Is Use proxy", candatePair: &ice.CandidatePair{ Local: relayCandidate, Remote: publicHostCandidate, }, inputDirectModeSupport: false, inputRemoteModeMessage: false, - expected: proxy.TypeWireGuard, + expected: true, }, { - name: "Should Result In Using Direct When Remote Direct Mode Support Is False And Local Eval Is No Use Proxy", + name: "Should Result In Using Direct When Remote Direct Mode Support Is False And Local Eval Is No Use proxy", candatePair: &ice.CandidatePair{ Local: publicHostCandidate, Remote: publicHostCandidate, }, inputDirectModeSupport: false, inputRemoteModeMessage: false, - expected: proxy.TypeDirectNoProxy, + expected: false, }, { - name: "Should Result In Using Direct When Local And Remote Eval Is No Proxy", + name: "Should Result In Using Direct When Local And Remote Eval Is No proxy", candatePair: &ice.CandidatePair{ Local: publicHostCandidate, Remote: publicHostCandidate, }, inputDirectModeSupport: true, inputRemoteModeMessage: true, - expected: proxy.TypeDirectNoProxy, + expected: false, }, } for _, testCase := range testCases { @@ -464,15 +463,15 @@ func TestGetProxyWithMessageExchange(t *testing.T) { Direct: testCase.inputRemoteModeMessage, }) }) - - resultProxy := conn.getProxyWithMessageExchange(testCase.candatePair, 1000) + conn.config.UserspaceBind = false + resultProxy := conn.isProxyNeeded(testCase.inputDirectModeSupport, testCase.inputRemoteModeMessage) err = g.Wait() if err != nil { t.Error(err) } - if resultProxy.Type() != testCase.expected { - t.Errorf("result didn't match expected value: Expected: %s, Got: %s", testCase.expected, resultProxy.Type()) + if resultProxy != testCase.expected { + t.Errorf("result didn't match expected value: Expected: %v, Got: %v", testCase.expected, resultProxy) } }) } diff --git a/client/internal/peer/proxy.go b/client/internal/peer/proxy.go new file mode 100644 index 000000000..1c3ba67e8 --- /dev/null +++ b/client/internal/peer/proxy.go @@ -0,0 +1,12 @@ +package peer + +import ( + "io" + "net" +) + +type proxy interface { + io.Closer + // Start creates a local remoteConn and starts proxying data from/to remoteConn + Start(remoteConn net.Conn) error +} diff --git a/client/internal/proxy/wireguard.go b/client/internal/peer/proxy_wg.go similarity index 62% rename from client/internal/proxy/wireguard.go rename to client/internal/peer/proxy_wg.go index ec3c6a730..c2281055c 100644 --- a/client/internal/proxy/wireguard.go +++ b/client/internal/peer/proxy_wg.go @@ -1,9 +1,10 @@ -package proxy +package peer import ( "context" - log "github.com/sirupsen/logrus" "net" + + log "github.com/sirupsen/logrus" ) // WireGuardProxy proxies @@ -11,49 +12,32 @@ type WireGuardProxy struct { ctx context.Context cancel context.CancelFunc - config Config + wgListenAddr string + remoteKey string remoteConn net.Conn localConn net.Conn } -func NewWireGuardProxy(config Config) *WireGuardProxy { - p := &WireGuardProxy{config: config} +func NewWireGuardProxy(wgListenAddr, remoteKey string) *WireGuardProxy { + p := &WireGuardProxy{ + wgListenAddr: wgListenAddr, + remoteKey: remoteKey, + } p.ctx, p.cancel = context.WithCancel(context.Background()) return p } -func (p *WireGuardProxy) updateEndpoint() error { - udpAddr, err := net.ResolveUDPAddr(p.localConn.LocalAddr().Network(), p.localConn.LocalAddr().String()) - if err != nil { - return err - } - // add local proxy connection as a Wireguard peer - err = p.config.WgInterface.UpdatePeer(p.config.RemoteKey, p.config.AllowedIps, DefaultWgKeepAlive, - udpAddr, p.config.PreSharedKey) - if err != nil { - return err - } - - return nil -} - func (p *WireGuardProxy) Start(remoteConn net.Conn) error { p.remoteConn = remoteConn var err error - p.localConn, err = net.Dial("udp", p.config.WgListenAddr) + p.localConn, err = net.Dial("udp", p.wgListenAddr) if err != nil { log.Errorf("failed dialing to local Wireguard port %s", err) return err } - err = p.updateEndpoint() - if err != nil { - log.Errorf("error while updating Wireguard peer endpoint [%s] %v", p.config.RemoteKey, err) - return err - } - go p.proxyToRemote() go p.proxyToLocal() @@ -68,10 +52,6 @@ func (p *WireGuardProxy) Close() error { return err } } - err := p.config.WgInterface.RemovePeer(p.config.RemoteKey) - if err != nil { - return err - } return nil } @@ -83,7 +63,7 @@ func (p *WireGuardProxy) proxyToRemote() { for { select { case <-p.ctx.Done(): - log.Debugf("stopped proxying to remote peer %s due to closed connection", p.config.RemoteKey) + log.Debugf("stopped proxying to remote peer %s due to closed connection", p.remoteKey) return default: n, err := p.localConn.Read(buf) @@ -107,7 +87,7 @@ func (p *WireGuardProxy) proxyToLocal() { for { select { case <-p.ctx.Done(): - log.Debugf("stopped proxying from remote peer %s due to closed connection", p.config.RemoteKey) + log.Debugf("stopped proxying from remote peer %s due to closed connection", p.remoteKey) return default: n, err := p.remoteConn.Read(buf) @@ -122,7 +102,3 @@ func (p *WireGuardProxy) proxyToLocal() { } } } - -func (p *WireGuardProxy) Type() Type { - return TypeWireGuard -} diff --git a/client/internal/peer/wg_peer_manager.go b/client/internal/peer/wg_peer_manager.go new file mode 100644 index 000000000..090bbd023 --- /dev/null +++ b/client/internal/peer/wg_peer_manager.go @@ -0,0 +1,68 @@ +package peer + +import ( + "net" + "time" + + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + + "github.com/netbirdio/netbird/iface" +) + +const defaultWgKeepAlive = 25 * time.Second + +type WgConfig struct { + WgListenAddr string + RemoteKey string + WgInterface *iface.WGIface + AllowedIps string + PreSharedKey *wgtypes.Key +} + +type wgPeerManager struct { + wgConfig WgConfig + + remoteAddr *net.UDPAddr +} + +func newWgPeerManager(wgConfig WgConfig) *wgPeerManager { + return &wgPeerManager{ + wgConfig: wgConfig, + } +} + +func (mgr *wgPeerManager) configureWgPeer(localDirectMode, remoteDirectMode, userspaceBind bool, remoteConn net.Conn, remoteWgPort int) error { + var err error + mgr.remoteAddr, err = net.ResolveUDPAddr("udp", remoteConn.RemoteAddr().String()) + if err != nil { + return err + } + + if remoteDirectMode { + mgr.remoteAddr.Port = remoteWgPort + } + + if userspaceBind && localDirectMode { + return mgr.updateWgPeer() + } + + if localDirectMode && remoteDirectMode { + return mgr.updateWgPeer() + } + + mgr.remoteAddr, err = net.ResolveUDPAddr("udp", mgr.wgConfig.WgListenAddr) + if err != nil { + return err + } + return mgr.updateWgPeer() +} + +// Close removes peer from the WireGuard interface +func (mgr *wgPeerManager) close() error { + return mgr.wgConfig.WgInterface.RemovePeer(mgr.wgConfig.RemoteKey) +} + +func (mgr *wgPeerManager) updateWgPeer() error { + return mgr.wgConfig.WgInterface.UpdatePeer(mgr.wgConfig.RemoteKey, mgr.wgConfig.AllowedIps, defaultWgKeepAlive, + mgr.remoteAddr, mgr.wgConfig.PreSharedKey) +} diff --git a/client/internal/proxy/direct.go b/client/internal/proxy/direct.go deleted file mode 100644 index 58603a831..000000000 --- a/client/internal/proxy/direct.go +++ /dev/null @@ -1,57 +0,0 @@ -package proxy - -import ( - log "github.com/sirupsen/logrus" - "net" -) - -// DirectNoProxy is used when there is no need for a proxy between ICE and WireGuard. -// This is possible in either of these cases: -// - peers are in the same local network -// - one of the peers has a public static IP (host) -// DirectNoProxy will just update remote peer with a remote host and fixed WireGuard port (r.g. 51820). -// In order DirectNoProxy to work, WireGuard port has to be fixed for the time being. -type DirectNoProxy struct { - config Config - // RemoteWgListenPort is a WireGuard port of a remote peer. - // It is used instead of the hardcoded 51820 port. - RemoteWgListenPort int -} - -// NewDirectNoProxy creates a new DirectNoProxy with a provided config and remote peer's WireGuard listen port -func NewDirectNoProxy(config Config, remoteWgPort int) *DirectNoProxy { - return &DirectNoProxy{config: config, RemoteWgListenPort: remoteWgPort} -} - -// Close removes peer from the WireGuard interface -func (p *DirectNoProxy) Close() error { - err := p.config.WgInterface.RemovePeer(p.config.RemoteKey) - if err != nil { - return err - } - return nil -} - -// Start just updates WireGuard peer with the remote IP and default WireGuard port -func (p *DirectNoProxy) Start(remoteConn net.Conn) error { - - log.Debugf("using DirectNoProxy while connecting to peer %s", p.config.RemoteKey) - addr, err := net.ResolveUDPAddr("udp", remoteConn.RemoteAddr().String()) - 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) - - if err != nil { - return err - } - - return nil -} - -// Type returns the type of this proxy -func (p *DirectNoProxy) Type() Type { - return TypeDirectNoProxy -} diff --git a/client/internal/proxy/dummy.go b/client/internal/proxy/dummy.go deleted file mode 100644 index ebd7cd68f..000000000 --- a/client/internal/proxy/dummy.go +++ /dev/null @@ -1,72 +0,0 @@ -package proxy - -import ( - "context" - log "github.com/sirupsen/logrus" - "net" - "time" -) - -// DummyProxy just sends pings to the RemoteKey peer and reads responses -type DummyProxy struct { - conn net.Conn - remote string - ctx context.Context - cancel context.CancelFunc -} - -func NewDummyProxy(remote string) *DummyProxy { - p := &DummyProxy{remote: remote} - p.ctx, p.cancel = context.WithCancel(context.Background()) - return p -} - -func (p *DummyProxy) Close() error { - p.cancel() - return nil -} - -func (p *DummyProxy) Start(remoteConn net.Conn) error { - p.conn = remoteConn - go func() { - buf := make([]byte, 1500) - for { - select { - case <-p.ctx.Done(): - return - default: - _, err := p.conn.Read(buf) - if err != nil { - log.Errorf("error while reading RemoteKey %s proxy %v", p.remote, err) - return - } - //log.Debugf("received %s from %s", string(buf[:n]), p.remote) - } - - } - }() - - go func() { - for { - select { - case <-p.ctx.Done(): - return - default: - _, err := p.conn.Write([]byte("hello")) - //log.Debugf("sent ping to %s", p.remote) - if err != nil { - log.Errorf("error while writing to RemoteKey %s proxy %v", p.remote, err) - return - } - time.Sleep(5 * time.Second) - } - } - - }() - - return nil -} - -func (p *DummyProxy) Type() Type { - return TypeDummy -} diff --git a/client/internal/proxy/noproxy.go b/client/internal/proxy/noproxy.go deleted file mode 100644 index dcfe182fd..000000000 --- a/client/internal/proxy/noproxy.go +++ /dev/null @@ -1,42 +0,0 @@ -package proxy - -import ( - log "github.com/sirupsen/logrus" - "net" -) - -// NoProxy is used just to configure WireGuard without any local proxy in between. -// Used when the WireGuard interface is userspace and uses bind.ICEBind -type NoProxy struct { - config Config -} - -// NewNoProxy creates a new NoProxy with a provided config -func NewNoProxy(config Config) *NoProxy { - return &NoProxy{config: config} -} - -// Close removes peer from the WireGuard interface -func (p *NoProxy) Close() error { - err := p.config.WgInterface.RemovePeer(p.config.RemoteKey) - if err != nil { - return err - } - return nil -} - -// Start just updates WireGuard peer with the remote address -func (p *NoProxy) Start(remoteConn net.Conn) error { - - log.Debugf("using NoProxy to connect to peer %s at %s", p.config.RemoteKey, remoteConn.RemoteAddr().String()) - addr, err := net.ResolveUDPAddr("udp", remoteConn.RemoteAddr().String()) - if err != nil { - return err - } - return p.config.WgInterface.UpdatePeer(p.config.RemoteKey, p.config.AllowedIps, DefaultWgKeepAlive, - addr, p.config.PreSharedKey) -} - -func (p *NoProxy) Type() Type { - return TypeNoProxy -} diff --git a/client/internal/proxy/proxy.go b/client/internal/proxy/proxy.go deleted file mode 100644 index a0b9e98a1..000000000 --- a/client/internal/proxy/proxy.go +++ /dev/null @@ -1,35 +0,0 @@ -package proxy - -import ( - "github.com/netbirdio/netbird/iface" - "golang.zx2c4.com/wireguard/wgctrl/wgtypes" - "io" - "net" - "time" -) - -const DefaultWgKeepAlive = 25 * time.Second - -type Type string - -const ( - TypeDirectNoProxy Type = "DirectNoProxy" - TypeWireGuard Type = "WireGuard" - TypeDummy Type = "Dummy" - TypeNoProxy Type = "NoProxy" -) - -type Config struct { - WgListenAddr string - RemoteKey string - WgInterface *iface.WGIface - AllowedIps string - PreSharedKey *wgtypes.Key -} - -type Proxy interface { - io.Closer - // Start creates a local remoteConn and starts proxying data from/to remoteConn - Start(remoteConn net.Conn) error - Type() Type -} diff --git a/signal/client/grpc.go b/signal/client/grpc.go index 3c63fbe40..b8514af1f 100644 --- a/signal/client/grpc.go +++ b/signal/client/grpc.go @@ -350,7 +350,7 @@ func (c *GrpcClient) receive(stream proto.SignalExchange_ConnectStreamClient, } else if err != nil { return err } - log.Debugf("received a new message from Peer [fingerprint: %s]", msg.Key) + log.Tracef("received a new message from Peer [fingerprint: %s]", msg.Key) decryptedMessage, err := c.decryptMessage(msg) if err != nil {