diff --git a/client/internal/engine.go b/client/internal/engine.go index 10c4fb970..61e737a4f 100644 --- a/client/internal/engine.go +++ b/client/internal/engine.go @@ -33,6 +33,8 @@ import ( "github.com/netbirdio/netbird/client/internal/acl" "github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/dnsfwd" + "github.com/netbirdio/netbird/client/internal/lazyconn" + lazyConnManager "github.com/netbirdio/netbird/client/internal/lazyconn/manager" "github.com/netbirdio/netbird/client/internal/networkmonitor" "github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peer/guard" @@ -131,6 +133,8 @@ type Engine struct { // peerConns is a map that holds all the peers that are known to this peer peerStore *peerstore.Store + lazyConnMgr *lazyConnManager.Manager + beforePeerHook nbnet.AddHookFunc afterPeerHook nbnet.RemoveHookFunc @@ -285,11 +289,12 @@ func (e *Engine) Stop() error { e.statusRecorder.UpdateDNSStates([]peer.NSGroupState{}) e.statusRecorder.UpdateRelayStates([]relay.ProbeResult{}) - err := e.removeAllPeers() - if err != nil { + if err := e.removeAllPeers(); err != nil { return fmt.Errorf("failed to remove all peers: %s", err) } + e.lazyConnMgr.Close() + if e.cancel != nil { e.cancel() } @@ -401,6 +406,10 @@ func (e *Engine) Start() error { return fmt.Errorf("up wg interface: %w", err) } + e.lazyConnMgr = lazyConnManager.NewManager(e.wgInterface) + go e.lazyConnMgr.Start() + go e.receiveLazyConnEvents() + if e.firewall != nil { e.acl = acl.NewDefaultManager(e.firewall) } @@ -1140,7 +1149,19 @@ func (e *Engine) addNewPeer(peerConfig *mgmProto.RemotePeerConfig) error { log.Warnf("error adding peer %s to status recorder, got error: %v", peerKey, err) } - conn.Open() + peerKeyParsed, err := wgtypes.ParseKey(peerKey) + if err != nil { + return err + } + + lazyPeerCfg := lazyconn.PeerConfig{ + PublicKey: peerKeyParsed, + AllowedIPs: peerIPs, + } + if err := e.lazyConnMgr.AddPeer(lazyPeerCfg); err != nil { + return err + } + return nil } @@ -1221,6 +1242,14 @@ func (e *Engine) receiveSignalEvents() { return fmt.Errorf("wrongly addressed message %s", msg.Key) } + peerKeyParsed, err := wgtypes.ParseKey(msg.Key) + if err != nil { + return err + } + if ok := e.lazyConnMgr.RemovePeer(peerKeyParsed); ok { + conn.Open() + } + switch msg.GetBody().Type { case sProto.Body_OFFER: remoteCred, err := signal.UnMarshalCredential(msg) @@ -1346,6 +1375,12 @@ func (e *Engine) parseNATExternalIPMappings() []string { } func (e *Engine) close() { + log.Debugf("stop lazy connection manager") + if e.lazyConnMgr != nil { + e.lazyConnMgr.Close() + e.lazyConnMgr = nil + } + log.Debugf("removing Netbird interface %s", e.config.WgIfaceName) if e.wgInterface != nil { if err := e.wgInterface.Close(); err != nil { @@ -1770,6 +1805,23 @@ func (e *Engine) Address() (netip.Addr, error) { return ip.Unmap(), nil } +func (e *Engine) receiveLazyConnEvents() { + for { + select { + case peerID := <-e.lazyConnMgr.PeerActivityChan: + e.syncMsgMux.Lock() + peerConn, ok := e.peerStore.PeerConn(peerID.String()) + if !ok { + e.syncMsgMux.Unlock() + continue + } + peerConn.Open() + e.syncMsgMux.Unlock() + case <-e.ctx.Done(): + } + } +} + // isChecksEqual checks if two slices of checks are equal. func isChecksEqual(checks []*mgmProto.Checks, oChecks []*mgmProto.Checks) bool { for _, check := range checks { diff --git a/client/internal/peer/conn.go b/client/internal/peer/conn.go index 9b4d1a554..4bec27964 100644 --- a/client/internal/peer/conn.go +++ b/client/internal/peer/conn.go @@ -166,6 +166,7 @@ func NewConn(engineCtx context.Context, config ConnConfig, statusRecorder *Statu // Open opens connection to the remote peer // It will try to establish a connection using ICE and in parallel with relay. The higher priority connection type will // be used. +// todo: prevent double open func (conn *Conn) Open() { conn.semaphore.Add(conn.ctx) conn.log.Debugf("open connection to peer")