From d6444e14e433faa57ea61456ad30c41a40e8a2b8 Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Sat, 31 May 2025 12:48:52 +0200 Subject: [PATCH] Move client and server to own packages --- .../routemanager/{ => client}/client.go | 236 ++++++++++-------- .../routemanager/{ => client}/client_test.go | 4 +- client/internal/routemanager/manager.go | 46 ++-- .../routemanager/{ => server}/server.go | 72 +++--- client/ui/debug.go | 4 +- 5 files changed, 192 insertions(+), 170 deletions(-) rename client/internal/routemanager/{ => client}/client.go (62%) rename client/internal/routemanager/{ => client}/client_test.go (99%) rename client/internal/routemanager/{ => server}/server.go (64%) diff --git a/client/internal/routemanager/client.go b/client/internal/routemanager/client/client.go similarity index 62% rename from client/internal/routemanager/client.go rename to client/internal/routemanager/client/client.go index 398824924..c9f0c68d3 100644 --- a/client/internal/routemanager/client.go +++ b/client/internal/routemanager/client/client.go @@ -1,4 +1,4 @@ -package routemanager +package client import ( "context" @@ -43,9 +43,9 @@ type routerPeerStatus struct { latency time.Duration } -type routesUpdate struct { - updateSerial uint64 - routes []*route.Route +type RoutesUpdate struct { + UpdateSerial uint64 + Routes []*route.Route } // RouteHandler defines the interface for handling routes @@ -57,7 +57,7 @@ type RouteHandler interface { RemoveAllowedIPs() error } -type ClientNetworkConfig struct { +type WatcherConfig struct { Context context.Context DNSRouteInterval time.Duration WGInterface iface.WGIface @@ -66,43 +66,45 @@ type ClientNetworkConfig struct { Handler RouteHandler } -type clientNetwork struct { +// Watcher watches route and peer changes and updates allowed IPs accordingly. +// Once stopped, it cannot be reused. +type Watcher struct { ctx context.Context cancel context.CancelFunc statusRecorder *peer.Status wgInterface iface.WGIface routes map[route.ID]*route.Route - routeUpdate chan routesUpdate + routeUpdate chan RoutesUpdate peerStateUpdate chan struct{} - routePeersNotifiers map[string]chan struct{} + routePeersNotifiers map[string]chan struct{} // map of peer key to channel for peer state changes currentChosen *route.Route handler RouteHandler updateSerial uint64 } -func newClientNetworkWatcher(config ClientNetworkConfig) *clientNetwork { +func NewWatcher(config WatcherConfig) *Watcher { ctx, cancel := context.WithCancel(config.Context) - client := &clientNetwork{ + client := &Watcher{ ctx: ctx, cancel: cancel, statusRecorder: config.StatusRecorder, wgInterface: config.WGInterface, routes: make(map[route.ID]*route.Route), routePeersNotifiers: make(map[string]chan struct{}), - routeUpdate: make(chan routesUpdate), + routeUpdate: make(chan RoutesUpdate), peerStateUpdate: make(chan struct{}), handler: config.Handler, } return client } -func (c *clientNetwork) getRouterPeerStatuses() map[route.ID]routerPeerStatus { +func (w *Watcher) getRouterPeerStatuses() map[route.ID]routerPeerStatus { routePeerStatuses := make(map[route.ID]routerPeerStatus) - for _, r := range c.routes { - peerStatus, err := c.statusRecorder.GetPeer(r.Peer) + for _, r := range w.routes { + peerStatus, err := w.statusRecorder.GetPeer(r.Peer) if err != nil { - log.Debugf("couldn't fetch peer state: %v", err) + log.Debugf("couldn't fetch peer state %v: %v", r.Peer, err) continue } routePeerStatuses[r.ID] = routerPeerStatus{ @@ -115,7 +117,7 @@ func (c *clientNetwork) getRouterPeerStatuses() map[route.ID]routerPeerStatus { } // getBestRouteFromStatuses determines the most optimal route from the available routes -// within a clientNetwork, taking into account peer connection status, route metrics, and +// within a Watcher, taking into account peer connection status, route metrics, and // preference for non-relayed and direct connections. // // It follows these prioritization rules: @@ -127,17 +129,17 @@ func (c *clientNetwork) getRouterPeerStatuses() map[route.ID]routerPeerStatus { // * Stability: In case of equal scores, the currently active route (if any) is maintained. // // It returns the ID of the selected optimal route. -func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[route.ID]routerPeerStatus) route.ID { +func (w *Watcher) getBestRouteFromStatuses(routePeerStatuses map[route.ID]routerPeerStatus) route.ID { var chosen route.ID chosenScore := float64(0) currScore := float64(0) var currID route.ID - if c.currentChosen != nil { - currID = c.currentChosen.ID + if w.currentChosen != nil { + currID = w.currentChosen.ID } - for _, r := range c.routes { + for _, r := range w.routes { tempScore := float64(0) peerStatus, found := routePeerStatuses[r.ID] if !found || !peerStatus.connected { @@ -154,7 +156,7 @@ func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[route.ID] if peerStatus.latency != 0 { latency = peerStatus.latency } else { - log.Tracef("peer %s has 0 latency, range %s", r.Peer, c.handler) + log.Tracef("peer %s has 0 latency, range %s", r.Peer, w.handler) } // avoid negative tempScore on the higher latency calculation @@ -184,41 +186,51 @@ func (c *clientNetwork) getBestRouteFromStatuses(routePeerStatuses map[route.ID] } } - log.Debugf("chosen route: %s, chosen score: %f, current route: %s, current score: %f", chosen, chosenScore, currID, currScore) + chosenID := chosen + if chosen == "" { + chosenID = "" + } + currentID := currID + if currID == "" { + currentID = "" + } + + log.Debugf("chosen route: %s, chosen score: %f, current route: %s, current score: %f", chosenID, chosenScore, currentID, currScore) switch { case chosen == "": var peers []string - for _, r := range c.routes { + for _, r := range w.routes { peers = append(peers, r.Peer) } - log.Warnf("The network [%v] has not been assigned a routing peer as no peers from the list %s are currently connected", c.handler, peers) + log.Infof("network [%v] has not been assigned a routing peer as no peers from the list %s are currently connected", w.handler, peers) case chosen != currID: // we compare the current score + 10ms to the chosen score to avoid flapping between routes if currScore != 0 && currScore+0.01 > chosenScore { - log.Debugf("Keeping current routing peer because the score difference with latency is less than 0.01(10ms), current: %f, new: %f", currScore, chosenScore) + log.Debugf("keeping current routing peer %s for [%v]: the score difference with latency is less than 0.01(10ms): current: %f, new: %f", + w.currentChosen.Peer, w.handler, currScore, chosenScore) return currID } var p string - if rt := c.routes[chosen]; rt != nil { + if rt := w.routes[chosen]; rt != nil { p = rt.Peer } - log.Infof("New chosen route is %s with peer %s with score %f for network [%v]", chosen, p, chosenScore, c.handler) + log.Infof("New chosen route is %s with peer %s with score %f for network [%v]", chosen, p, chosenScore, w.handler) } return chosen } -func (c *clientNetwork) watchPeerStatusChanges(ctx context.Context, peerKey string, peerStateUpdate chan struct{}, closer chan struct{}) { +func (w *Watcher) watchPeerStatusChanges(ctx context.Context, peerKey string, peerStateUpdate chan struct{}, closer chan struct{}) { for { select { case <-ctx.Done(): return case <-closer: return - case <-c.statusRecorder.GetPeerStateChangeNotifier(peerKey): - state, err := c.statusRecorder.GetPeer(peerKey) + case <-w.statusRecorder.GetPeerStateChangeNotifier(peerKey): + state, err := w.statusRecorder.GetPeer(peerKey) if err != nil { continue } @@ -228,93 +240,92 @@ func (c *clientNetwork) watchPeerStatusChanges(ctx context.Context, peerKey stri } } -func (c *clientNetwork) startPeersStatusChangeWatcher() { - for _, r := range c.routes { - _, found := c.routePeersNotifiers[r.Peer] - if found { +func (w *Watcher) startNewPeerStatusWatchers() { + for _, r := range w.routes { + if _, found := w.routePeersNotifiers[r.Peer]; found { continue } closerChan := make(chan struct{}) - c.routePeersNotifiers[r.Peer] = closerChan - go c.watchPeerStatusChanges(c.ctx, r.Peer, c.peerStateUpdate, closerChan) + w.routePeersNotifiers[r.Peer] = closerChan + go w.watchPeerStatusChanges(w.ctx, r.Peer, w.peerStateUpdate, closerChan) } } // addAllowedIPs adds the allowed IPs for the current chosen route to the handler. -func (c *clientNetwork) addAllowedIPs(route *route.Route) error { - if err := c.handler.AddAllowedIPs(route.Peer); err != nil { +func (w *Watcher) addAllowedIPs(route *route.Route) error { + if err := w.handler.AddAllowedIPs(route.Peer); err != nil { return fmt.Errorf("add allowed IPs for peer %s: %w", route.Peer, err) } - if err := c.statusRecorder.AddPeerStateRoute(route.Peer, c.handler.String(), route.GetResourceID()); err != nil { + if err := w.statusRecorder.AddPeerStateRoute(route.Peer, w.handler.String(), route.GetResourceID()); err != nil { log.Warnf("Failed to update peer state: %v", err) } - c.connectEvent(route) + w.connectEvent(route) return nil } -func (c *clientNetwork) removeAllowedIPs(route *route.Route, rsn reason) error { - if err := c.statusRecorder.RemovePeerStateRoute(route.Peer, c.handler.String()); err != nil { +func (w *Watcher) removeAllowedIPs(route *route.Route, rsn reason) error { + if err := w.statusRecorder.RemovePeerStateRoute(route.Peer, w.handler.String()); err != nil { log.Warnf("Failed to update peer state: %v", err) } - if err := c.handler.RemoveAllowedIPs(); err != nil { + if err := w.handler.RemoveAllowedIPs(); err != nil { return fmt.Errorf("remove allowed IPs: %w", err) } - c.disconnectEvent(route, rsn) + w.disconnectEvent(route, rsn) return nil } -func (c *clientNetwork) recalculateRoutes(rsn reason) error { - routerPeerStatuses := c.getRouterPeerStatuses() +func (w *Watcher) recalculateRoutes(rsn reason) error { + routerPeerStatuses := w.getRouterPeerStatuses() - newChosenID := c.getBestRouteFromStatuses(routerPeerStatuses) + newChosenID := w.getBestRouteFromStatuses(routerPeerStatuses) // If no route is chosen, remove the route from the peer if newChosenID == "" { - if c.currentChosen == nil { + if w.currentChosen == nil { return nil } - if err := c.removeAllowedIPs(c.currentChosen, rsn); err != nil { + if err := w.removeAllowedIPs(w.currentChosen, rsn); err != nil { return fmt.Errorf("remove obsolete: %w", err) } - c.currentChosen = nil + w.currentChosen = nil return nil } // If the chosen route is the same as the current route, do nothing - if c.currentChosen != nil && c.currentChosen.ID == newChosenID && - c.currentChosen.Equal(c.routes[newChosenID]) { + if w.currentChosen != nil && w.currentChosen.ID == newChosenID && + w.currentChosen.Equal(w.routes[newChosenID]) { return nil } // If the chosen route was assigned to a different peer, remove the allowed IPs first - if isNew := c.currentChosen == nil; !isNew { - if err := c.removeAllowedIPs(c.currentChosen, reasonHA); err != nil { + if isNew := w.currentChosen == nil; !isNew { + if err := w.removeAllowedIPs(w.currentChosen, reasonHA); err != nil { return fmt.Errorf("remove old: %w", err) } } - newChosenRoute := c.routes[newChosenID] - if err := c.addAllowedIPs(newChosenRoute); err != nil { + newChosenRoute := w.routes[newChosenID] + if err := w.addAllowedIPs(newChosenRoute); err != nil { return fmt.Errorf("add new: %w", err) } - c.currentChosen = newChosenRoute + w.currentChosen = newChosenRoute return nil } -func (c *clientNetwork) connectEvent(route *route.Route) { +func (w *Watcher) connectEvent(route *route.Route) { var defaultRoute bool - for _, r := range c.routes { + for _, r := range w.routes { if r.Network.Bits() == 0 { defaultRoute = true break @@ -326,13 +337,13 @@ func (c *clientNetwork) connectEvent(route *route.Route) { } meta := map[string]string{ - "network": c.handler.String(), + "network": w.handler.String(), } if route != nil { meta["id"] = string(route.NetID) meta["peer"] = route.Peer } - c.statusRecorder.PublishEvent( + w.statusRecorder.PublishEvent( proto.SystemEvent_INFO, proto.SystemEvent_NETWORK, "Default route added", @@ -341,9 +352,9 @@ func (c *clientNetwork) connectEvent(route *route.Route) { ) } -func (c *clientNetwork) disconnectEvent(route *route.Route, rsn reason) { +func (w *Watcher) disconnectEvent(route *route.Route, rsn reason) { var defaultRoute bool - for _, r := range c.routes { + for _, r := range w.routes { if r.Network.Bits() == 0 { defaultRoute = true break @@ -363,7 +374,7 @@ func (c *clientNetwork) disconnectEvent(route *route.Route, rsn reason) { meta["id"] = string(route.NetID) meta["peer"] = route.Peer } - meta["network"] = c.handler.String() + meta["network"] = w.handler.String() switch rsn { case reasonShutdown: severity = proto.SystemEvent_INFO @@ -386,7 +397,7 @@ func (c *clientNetwork) disconnectEvent(route *route.Route, rsn reason) { userMessage = "Exit node disconnected for unknown reasons." } - c.statusRecorder.PublishEvent( + w.statusRecorder.PublishEvent( severity, proto.SystemEvent_NETWORK, message, @@ -395,86 +406,95 @@ func (c *clientNetwork) disconnectEvent(route *route.Route, rsn reason) { ) } -func (c *clientNetwork) sendUpdateToClientNetworkWatcher(update routesUpdate) { +func (w *Watcher) SendUpdate(update RoutesUpdate) { go func() { - c.routeUpdate <- update + w.routeUpdate <- update }() } -func (c *clientNetwork) handleUpdate(update routesUpdate) bool { +func (w *Watcher) classifyUpdate(update RoutesUpdate) bool { isUpdateMapDifferent := false updateMap := make(map[route.ID]*route.Route) - for _, r := range update.routes { + for _, r := range update.Routes { updateMap[r.ID] = r } - if len(c.routes) != len(updateMap) { + if len(w.routes) != len(updateMap) { isUpdateMapDifferent = true } - for id, r := range c.routes { + for id, r := range w.routes { _, found := updateMap[id] if !found { - close(c.routePeersNotifiers[r.Peer]) - delete(c.routePeersNotifiers, r.Peer) + close(w.routePeersNotifiers[r.Peer]) + delete(w.routePeersNotifiers, r.Peer) isUpdateMapDifferent = true continue } - if !reflect.DeepEqual(c.routes[id], updateMap[id]) { + if !reflect.DeepEqual(w.routes[id], updateMap[id]) { isUpdateMapDifferent = true } } - c.routes = updateMap + w.routes = updateMap return isUpdateMapDifferent } -// peersStateAndUpdateWatcher is the main point of reacting on client network routing events. +// Start is the main point of reacting on client network routing events. // All the processing related to the client network should be done here. Thread-safe. -func (c *clientNetwork) peersStateAndUpdateWatcher() { +func (w *Watcher) Start() { for { select { - case <-c.ctx.Done(): - log.Debugf("Stopping watcher for network [%v]", c.handler) - if err := c.removeAllowedIPs(c.currentChosen, reasonShutdown); err != nil { - log.Errorf("Failed to remove routes for [%v]: %v", c.handler, err) - } + case <-w.ctx.Done(): return - case <-c.peerStateUpdate: - err := c.recalculateRoutes(reasonPeerUpdate) - if err != nil { - log.Errorf("Failed to recalculate routes for network [%v]: %v", c.handler, err) + case <-w.peerStateUpdate: + if err := w.recalculateRoutes(reasonPeerUpdate); err != nil { + log.Errorf("Failed to recalculate routes for network [%v]: %v", w.handler, err) } - case update := <-c.routeUpdate: - if update.updateSerial < c.updateSerial { - log.Warnf("Received a routes update with smaller serial number (%d -> %d), ignoring it", c.updateSerial, update.updateSerial) + case update := <-w.routeUpdate: + if update.UpdateSerial < w.updateSerial { + log.Warnf("Received a routes update with smaller serial number (%d -> %d), ignoring it", w.updateSerial, update.UpdateSerial) continue } - log.Debugf("Received a new client network route update for [%v]", c.handler) - - // hash update somehow - isTrueRouteUpdate := c.handleUpdate(update) - - c.updateSerial = update.updateSerial - - if isTrueRouteUpdate { - log.Debug("Client network update contains different routes, recalculating routes") - err := c.recalculateRoutes(reasonRouteUpdate) - if err != nil { - log.Errorf("Failed to recalculate routes for network [%v]: %v", c.handler, err) - } - } else { - log.Debug("Route update is not different, skipping route recalculation") - } - - c.startPeersStatusChangeWatcher() + w.handleRouteUpdate(update) } } } -func handlerFromRoute( +func (w *Watcher) handleRouteUpdate(update RoutesUpdate) { + log.Debugf("Received a new client network route update for [%v]", w.handler) + + // hash update somehow + isTrueRouteUpdate := w.classifyUpdate(update) + + w.updateSerial = update.UpdateSerial + + if isTrueRouteUpdate { + log.Debugf("client network update %v for [%v] contains different routes, recalculating routes", update.UpdateSerial, w.handler) + if err := w.recalculateRoutes(reasonRouteUpdate); err != nil { + log.Errorf("failed to recalculate routes for network [%v]: %v", w.handler, err) + } + } else { + log.Debugf("route update %v for [%v] is not different, skipping route recalculation", update.UpdateSerial, w.handler) + } + + w.startNewPeerStatusWatchers() +} + +// Stop stops the watcher and cleans up resources. +func (w *Watcher) Stop() { + log.Debugf("Stopping watcher for network [%v]", w.handler) + + w.cancel() + + if err := w.removeAllowedIPs(w.currentChosen, reasonShutdown); err != nil { + log.Errorf("Failed to remove routes for [%v]: %v", w.handler, err) + } +} + +func HandlerFromRoute( rt *route.Route, routeRefCounter *refcounter.RouteRefCounter, allowedIPsRefCounter *refcounter.AllowedIPsRefCounter, diff --git a/client/internal/routemanager/client_test.go b/client/internal/routemanager/client/client_test.go similarity index 99% rename from client/internal/routemanager/client_test.go rename to client/internal/routemanager/client/client_test.go index 56fcf1613..48a9495bf 100644 --- a/client/internal/routemanager/client_test.go +++ b/client/internal/routemanager/client/client_test.go @@ -1,4 +1,4 @@ -package routemanager +package client import ( "fmt" @@ -395,7 +395,7 @@ func TestGetBestrouteFromStatuses(t *testing.T) { } // create new clientNetwork - client := &clientNetwork{ + client := &Watcher{ handler: static.NewRoute(&route.Route{Network: netip.MustParsePrefix("192.168.0.0/24")}, nil, nil), routes: tc.existingRoutes, currentChosen: currentRoute, diff --git a/client/internal/routemanager/manager.go b/client/internal/routemanager/manager.go index d35227acd..ba04438e2 100644 --- a/client/internal/routemanager/manager.go +++ b/client/internal/routemanager/manager.go @@ -23,9 +23,11 @@ import ( "github.com/netbirdio/netbird/client/internal/listener" "github.com/netbirdio/netbird/client/internal/peer" "github.com/netbirdio/netbird/client/internal/peerstore" + "github.com/netbirdio/netbird/client/internal/routemanager/client" "github.com/netbirdio/netbird/client/internal/routemanager/iface" "github.com/netbirdio/netbird/client/internal/routemanager/notifier" "github.com/netbirdio/netbird/client/internal/routemanager/refcounter" + "github.com/netbirdio/netbird/client/internal/routemanager/server" "github.com/netbirdio/netbird/client/internal/routemanager/systemops" "github.com/netbirdio/netbird/client/internal/routemanager/vars" "github.com/netbirdio/netbird/client/internal/routeselector" @@ -70,9 +72,9 @@ type DefaultManager struct { ctx context.Context stop context.CancelFunc mux sync.Mutex - clientNetworks map[route.HAUniqueID]*clientNetwork + clientNetworks map[route.HAUniqueID]*client.Watcher routeSelector *routeselector.RouteSelector - serverRouter *serverRouter + serverRouter *server.Router sysOps *systemops.SysOps statusRecorder *peer.Status relayMgr *relayClient.Manager @@ -90,7 +92,7 @@ type DefaultManager struct { useNewDNSRoute bool disableClientRoutes bool disableServerRoutes bool - activeRoutes map[route.HAUniqueID]RouteHandler + activeRoutes map[route.HAUniqueID]client.RouteHandler } func NewManager(config ManagerConfig) *DefaultManager { @@ -102,7 +104,7 @@ func NewManager(config ManagerConfig) *DefaultManager { ctx: mCTX, stop: cancel, dnsRouteInterval: config.DNSRouteInterval, - clientNetworks: make(map[route.HAUniqueID]*clientNetwork), + clientNetworks: make(map[route.HAUniqueID]*client.Watcher), relayMgr: config.RelayManager, sysOps: sysOps, statusRecorder: config.StatusRecorder, @@ -114,7 +116,7 @@ func NewManager(config ManagerConfig) *DefaultManager { peerStore: config.PeerStore, disableClientRoutes: config.DisableClientRoutes, disableServerRoutes: config.DisableServerRoutes, - activeRoutes: make(map[route.HAUniqueID]RouteHandler), + activeRoutes: make(map[route.HAUniqueID]client.RouteHandler), } useNoop := netstack.IsEnabled() || config.DisableClientRoutes @@ -230,7 +232,7 @@ func (m *DefaultManager) EnableServerRouter(firewall firewall.Manager) error { } var err error - m.serverRouter, err = newServerRouter(m.ctx, m.wgInterface, firewall, m.statusRecorder) + m.serverRouter, err = server.NewRouter(m.ctx, m.wgInterface, firewall, m.statusRecorder) if err != nil { return err } @@ -241,7 +243,7 @@ func (m *DefaultManager) EnableServerRouter(firewall firewall.Manager) error { func (m *DefaultManager) Stop(stateManager *statemanager.Manager) { m.stop() if m.serverRouter != nil { - m.serverRouter.cleanUp() + m.serverRouter.CleanUp() } if m.routeRefCounter != nil { @@ -271,7 +273,7 @@ func (m *DefaultManager) Stop(stateManager *statemanager.Manager) { // UpdateRoutes compares received routes with existing routes and removes, updates or adds them to the client and server maps func (m *DefaultManager) updateSystemRoutes(newRoutes route.HAMap) error { toAdd := make(map[route.HAUniqueID]*route.Route) - toRemove := make(map[route.HAUniqueID]RouteHandler) + toRemove := make(map[route.HAUniqueID]client.RouteHandler) for id, routes := range newRoutes { if len(routes) > 0 { @@ -296,7 +298,7 @@ func (m *DefaultManager) updateSystemRoutes(newRoutes route.HAMap) error { } for id, route := range toAdd { - handler := handlerFromRoute( + handler := client.HandlerFromRoute( route, m.routeRefCounter, m.allowedIPsRefCounter, @@ -347,7 +349,7 @@ func (m *DefaultManager) UpdateRoutes(updateSerial uint64, newRoutes []*route.Ro return nil } - if err := m.serverRouter.updateRoutes(newServerRoutesMap, useNewDNSRoute); err != nil { + if err := m.serverRouter.UpdateRoutes(newServerRoutesMap, useNewDNSRoute); err != nil { return fmt.Errorf("update routes: %w", err) } @@ -416,7 +418,7 @@ func (m *DefaultManager) TriggerSelection(networks route.HAMap) { continue } - config := ClientNetworkConfig{ + config := client.WatcherConfig{ Context: m.ctx, DNSRouteInterval: m.dnsRouteInterval, WGInterface: m.wgInterface, @@ -424,10 +426,10 @@ func (m *DefaultManager) TriggerSelection(networks route.HAMap) { Route: routes[0], Handler: handler, } - clientNetworkWatcher := newClientNetworkWatcher(config) + clientNetworkWatcher := client.NewWatcher(config) m.clientNetworks[id] = clientNetworkWatcher - go clientNetworkWatcher.peersStateAndUpdateWatcher() - clientNetworkWatcher.sendUpdateToClientNetworkWatcher(routesUpdate{routes: routes}) + go clientNetworkWatcher.Start() + clientNetworkWatcher.SendUpdate(client.RoutesUpdate{Routes: routes}) } if err := m.stateManager.UpdateState((*SelectorState)(m.routeSelector)); err != nil { @@ -440,7 +442,7 @@ func (m *DefaultManager) stopObsoleteClients(networks route.HAMap) { for id, client := range m.clientNetworks { if _, ok := networks[id]; !ok { log.Debugf("Stopping client network watcher, %s", id) - client.cancel() + client.Stop() delete(m.clientNetworks, id) } } @@ -459,7 +461,7 @@ func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks rout continue } - config := ClientNetworkConfig{ + config := client.WatcherConfig{ Context: m.ctx, DNSRouteInterval: m.dnsRouteInterval, WGInterface: m.wgInterface, @@ -467,15 +469,15 @@ func (m *DefaultManager) updateClientNetworks(updateSerial uint64, networks rout Route: routes[0], Handler: handler, } - clientNetworkWatcher = newClientNetworkWatcher(config) + clientNetworkWatcher = client.NewWatcher(config) m.clientNetworks[id] = clientNetworkWatcher - go clientNetworkWatcher.peersStateAndUpdateWatcher() + go clientNetworkWatcher.Start() } - update := routesUpdate{ - updateSerial: updateSerial, - routes: routes, + update := client.RoutesUpdate{ + UpdateSerial: updateSerial, + Routes: routes, } - clientNetworkWatcher.sendUpdateToClientNetworkWatcher(update) + clientNetworkWatcher.SendUpdate(update) } } diff --git a/client/internal/routemanager/server.go b/client/internal/routemanager/server/server.go similarity index 64% rename from client/internal/routemanager/server.go rename to client/internal/routemanager/server/server.go index 5bacb856c..09149feb9 100644 --- a/client/internal/routemanager/server.go +++ b/client/internal/routemanager/server/server.go @@ -1,4 +1,4 @@ -package routemanager +package server import ( "context" @@ -14,7 +14,7 @@ import ( "github.com/netbirdio/netbird/route" ) -type serverRouter struct { +type Router struct { mux sync.Mutex ctx context.Context routes map[route.ID]*route.Route @@ -23,8 +23,8 @@ type serverRouter struct { statusRecorder *peer.Status } -func newServerRouter(ctx context.Context, wgInterface iface.WGIface, firewall firewall.Manager, statusRecorder *peer.Status) (*serverRouter, error) { - return &serverRouter{ +func NewRouter(ctx context.Context, wgInterface iface.WGIface, firewall firewall.Manager, statusRecorder *peer.Status) (*Router, error) { + return &Router{ ctx: ctx, routes: make(map[route.ID]*route.Route), firewall: firewall, @@ -33,104 +33,104 @@ func newServerRouter(ctx context.Context, wgInterface iface.WGIface, firewall fi }, nil } -func (m *serverRouter) updateRoutes(routesMap map[route.ID]*route.Route, useNewDNSRoute bool) error { - m.mux.Lock() - defer m.mux.Unlock() +func (r *Router) UpdateRoutes(routesMap map[route.ID]*route.Route, useNewDNSRoute bool) error { + r.mux.Lock() + defer r.mux.Unlock() serverRoutesToRemove := make([]route.ID, 0) - for routeID := range m.routes { + for routeID := range r.routes { update, found := routesMap[routeID] - if !found || !update.Equal(m.routes[routeID]) { + if !found || !update.Equal(r.routes[routeID]) { serverRoutesToRemove = append(serverRoutesToRemove, routeID) } } for _, routeID := range serverRoutesToRemove { - oldRoute := m.routes[routeID] - err := m.removeFromServerNetwork(oldRoute) + oldRoute := r.routes[routeID] + err := r.removeFromServerNetwork(oldRoute) if err != nil { log.Errorf("Unable to remove route id: %s, network %s, from server, got: %v", oldRoute.ID, oldRoute.Network, err) } - delete(m.routes, routeID) + delete(r.routes, routeID) } // If routing is to be disabled, do it after routes have been removed // If routing is to be enabled, do it before adding new routes; addToServerNetwork needs routing to be enabled if len(routesMap) > 0 { - if err := m.firewall.EnableRouting(); err != nil { + if err := r.firewall.EnableRouting(); err != nil { return fmt.Errorf("enable routing: %w", err) } } else { - if err := m.firewall.DisableRouting(); err != nil { + if err := r.firewall.DisableRouting(); err != nil { return fmt.Errorf("disable routing: %w", err) } } for id, newRoute := range routesMap { - _, found := m.routes[id] + _, found := r.routes[id] if found { continue } - err := m.addToServerNetwork(newRoute, useNewDNSRoute) + err := r.addToServerNetwork(newRoute, useNewDNSRoute) if err != nil { log.Errorf("Unable to add route %s from server, got: %v", newRoute.ID, err) continue } - m.routes[id] = newRoute + r.routes[id] = newRoute } return nil } -func (m *serverRouter) removeFromServerNetwork(route *route.Route) error { - if m.ctx.Err() != nil { +func (r *Router) removeFromServerNetwork(route *route.Route) error { + if r.ctx.Err() != nil { log.Infof("Not removing from server network because context is done") - return m.ctx.Err() + return r.ctx.Err() } routerPair := routeToRouterPair(route, false) - if err := m.firewall.RemoveNatRule(routerPair); err != nil { + if err := r.firewall.RemoveNatRule(routerPair); err != nil { return fmt.Errorf("remove routing rules: %w", err) } - delete(m.routes, route.ID) - m.statusRecorder.RemoveLocalPeerStateRoute(route.NetString()) + delete(r.routes, route.ID) + r.statusRecorder.RemoveLocalPeerStateRoute(route.NetString()) return nil } -func (m *serverRouter) addToServerNetwork(route *route.Route, useNewDNSRoute bool) error { - if m.ctx.Err() != nil { +func (r *Router) addToServerNetwork(route *route.Route, useNewDNSRoute bool) error { + if r.ctx.Err() != nil { log.Infof("Not adding to server network because context is done") - return m.ctx.Err() + return r.ctx.Err() } routerPair := routeToRouterPair(route, useNewDNSRoute) - if err := m.firewall.AddNatRule(routerPair); err != nil { + if err := r.firewall.AddNatRule(routerPair); err != nil { return fmt.Errorf("insert routing rules: %w", err) } - m.routes[route.ID] = route - m.statusRecorder.AddLocalPeerStateRoute(route.NetString(), route.GetResourceID()) + r.routes[route.ID] = route + r.statusRecorder.AddLocalPeerStateRoute(route.NetString(), route.GetResourceID()) return nil } -func (m *serverRouter) cleanUp() { - m.mux.Lock() - defer m.mux.Unlock() +func (r *Router) CleanUp() { + r.mux.Lock() + defer r.mux.Unlock() - for _, r := range m.routes { - routerPair := routeToRouterPair(r, false) - if err := m.firewall.RemoveNatRule(routerPair); err != nil { + for _, route := range r.routes { + routerPair := routeToRouterPair(route, false) + if err := r.firewall.RemoveNatRule(routerPair); err != nil { log.Errorf("Failed to remove cleanup route: %v", err) } } - m.statusRecorder.CleanLocalPeerStateRoutes() + r.statusRecorder.CleanLocalPeerStateRoutes() } func routeToRouterPair(route *route.Route, useNewDNSRoute bool) firewall.RouterPair { diff --git a/client/ui/debug.go b/client/ui/debug.go index ab7dba37a..a534ae2db 100644 --- a/client/ui/debug.go +++ b/client/ui/debug.go @@ -627,7 +627,7 @@ func showUploadFailedDialog(w fyne.Window, localPath, failureReason string) { "A local copy was saved at:\n%s", failureReason, localPath)), ) - customDialog := dialog.NewCustom("Upload Failed", "Cancel", content, w) + customDialog := dialog.NewCustom("Upload Failed", "cancel", content, w) buttonBox := container.NewHBox( createButtonWithAction("Open file", func() { @@ -686,7 +686,7 @@ func showBundleCreatedDialog(w fyne.Window, localPath string) { "Administrator privileges may be required to access the file.", localPath)), ) - customDialog := dialog.NewCustom("Debug Bundle Created", "Cancel", content, w) + customDialog := dialog.NewCustom("Debug Bundle Created", "cancel", content, w) buttonBox := createButtonBox(localPath, w, nil) content.Add(buttonBox)