Skip to content

Commit 533d93e

Browse files
authored
[management,client] Feat/exit node auto apply (#4272)
[management,client] Feat/exit node auto apply (#4272)
1 parent 9685411 commit 533d93e

File tree

19 files changed

+665
-309
lines changed

19 files changed

+665
-309
lines changed

client/internal/engine.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,15 +1111,16 @@ func toRoutes(protoRoutes []*mgmProto.Route) []*route.Route {
11111111
}
11121112

11131113
convertedRoute := &route.Route{
1114-
ID: route.ID(protoRoute.ID),
1115-
Network: prefix.Masked(),
1116-
Domains: domain.FromPunycodeList(protoRoute.Domains),
1117-
NetID: route.NetID(protoRoute.NetID),
1118-
NetworkType: route.NetworkType(protoRoute.NetworkType),
1119-
Peer: protoRoute.Peer,
1120-
Metric: int(protoRoute.Metric),
1121-
Masquerade: protoRoute.Masquerade,
1122-
KeepRoute: protoRoute.KeepRoute,
1114+
ID: route.ID(protoRoute.ID),
1115+
Network: prefix.Masked(),
1116+
Domains: domain.FromPunycodeList(protoRoute.Domains),
1117+
NetID: route.NetID(protoRoute.NetID),
1118+
NetworkType: route.NetworkType(protoRoute.NetworkType),
1119+
Peer: protoRoute.Peer,
1120+
Metric: int(protoRoute.Metric),
1121+
Masquerade: protoRoute.Masquerade,
1122+
KeepRoute: protoRoute.KeepRoute,
1123+
SkipAutoApply: protoRoute.SkipAutoApply,
11231124
}
11241125
routes = append(routes, convertedRoute)
11251126
}

client/internal/routemanager/manager.go

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ import (
3636
"github.com/netbirdio/netbird/client/internal/routemanager/vars"
3737
"github.com/netbirdio/netbird/client/internal/routeselector"
3838
"github.com/netbirdio/netbird/client/internal/statemanager"
39-
relayClient "github.com/netbirdio/netbird/shared/relay/client"
4039
"github.com/netbirdio/netbird/route"
40+
relayClient "github.com/netbirdio/netbird/shared/relay/client"
4141
nbnet "github.com/netbirdio/netbird/util/net"
4242
"github.com/netbirdio/netbird/version"
4343
)
@@ -368,7 +368,11 @@ func (m *DefaultManager) UpdateRoutes(
368368

369369
var merr *multierror.Error
370370
if !m.disableClientRoutes {
371-
filteredClientRoutes := m.routeSelector.FilterSelected(clientRoutes)
371+
372+
// Update route selector based on management server's isSelected status
373+
m.updateRouteSelectorFromManagement(clientRoutes)
374+
375+
filteredClientRoutes := m.routeSelector.FilterSelectedExitNodes(clientRoutes)
372376

373377
if err := m.updateSystemRoutes(filteredClientRoutes); err != nil {
374378
merr = multierror.Append(merr, fmt.Errorf("update system routes: %w", err))
@@ -430,7 +434,7 @@ func (m *DefaultManager) TriggerSelection(networks route.HAMap) {
430434
m.mux.Lock()
431435
defer m.mux.Unlock()
432436

433-
networks = m.routeSelector.FilterSelected(networks)
437+
networks = m.routeSelector.FilterSelectedExitNodes(networks)
434438

435439
m.notifier.OnNewRoutes(networks)
436440

@@ -583,3 +587,106 @@ func resolveURLsToIPs(urls []string) []net.IP {
583587
}
584588
return ips
585589
}
590+
591+
// updateRouteSelectorFromManagement updates the route selector based on the isSelected status from the management server
592+
func (m *DefaultManager) updateRouteSelectorFromManagement(clientRoutes route.HAMap) {
593+
exitNodeInfo := m.collectExitNodeInfo(clientRoutes)
594+
if len(exitNodeInfo.allIDs) == 0 {
595+
return
596+
}
597+
598+
m.updateExitNodeSelections(exitNodeInfo)
599+
m.logExitNodeUpdate(exitNodeInfo)
600+
}
601+
602+
type exitNodeInfo struct {
603+
allIDs []route.NetID
604+
selectedByManagement []route.NetID
605+
userSelected []route.NetID
606+
userDeselected []route.NetID
607+
}
608+
609+
func (m *DefaultManager) collectExitNodeInfo(clientRoutes route.HAMap) exitNodeInfo {
610+
var info exitNodeInfo
611+
612+
for haID, routes := range clientRoutes {
613+
if !m.isExitNodeRoute(routes) {
614+
continue
615+
}
616+
617+
netID := haID.NetID()
618+
info.allIDs = append(info.allIDs, netID)
619+
620+
if m.routeSelector.HasUserSelectionForRoute(netID) {
621+
m.categorizeUserSelection(netID, &info)
622+
} else {
623+
m.checkManagementSelection(routes, netID, &info)
624+
}
625+
}
626+
627+
return info
628+
}
629+
630+
func (m *DefaultManager) isExitNodeRoute(routes []*route.Route) bool {
631+
return len(routes) > 0 && routes[0].Network.String() == vars.ExitNodeCIDR
632+
}
633+
634+
func (m *DefaultManager) categorizeUserSelection(netID route.NetID, info *exitNodeInfo) {
635+
if m.routeSelector.IsSelected(netID) {
636+
info.userSelected = append(info.userSelected, netID)
637+
} else {
638+
info.userDeselected = append(info.userDeselected, netID)
639+
}
640+
}
641+
642+
func (m *DefaultManager) checkManagementSelection(routes []*route.Route, netID route.NetID, info *exitNodeInfo) {
643+
for _, route := range routes {
644+
if !route.SkipAutoApply {
645+
info.selectedByManagement = append(info.selectedByManagement, netID)
646+
break
647+
}
648+
}
649+
}
650+
651+
func (m *DefaultManager) updateExitNodeSelections(info exitNodeInfo) {
652+
routesToDeselect := m.getRoutesToDeselect(info.allIDs)
653+
m.deselectExitNodes(routesToDeselect)
654+
m.selectExitNodesByManagement(info.selectedByManagement, info.allIDs)
655+
}
656+
657+
func (m *DefaultManager) getRoutesToDeselect(allIDs []route.NetID) []route.NetID {
658+
var routesToDeselect []route.NetID
659+
for _, netID := range allIDs {
660+
if !m.routeSelector.HasUserSelectionForRoute(netID) {
661+
routesToDeselect = append(routesToDeselect, netID)
662+
}
663+
}
664+
return routesToDeselect
665+
}
666+
667+
func (m *DefaultManager) deselectExitNodes(routesToDeselect []route.NetID) {
668+
if len(routesToDeselect) == 0 {
669+
return
670+
}
671+
672+
err := m.routeSelector.DeselectRoutes(routesToDeselect, routesToDeselect)
673+
if err != nil {
674+
log.Warnf("Failed to deselect exit nodes: %v", err)
675+
}
676+
}
677+
678+
func (m *DefaultManager) selectExitNodesByManagement(selectedByManagement []route.NetID, allIDs []route.NetID) {
679+
if len(selectedByManagement) == 0 {
680+
return
681+
}
682+
683+
err := m.routeSelector.SelectRoutes(selectedByManagement, true, allIDs)
684+
if err != nil {
685+
log.Warnf("Failed to select exit nodes: %v", err)
686+
}
687+
}
688+
689+
func (m *DefaultManager) logExitNodeUpdate(info exitNodeInfo) {
690+
log.Debugf("Updated route selector: %d exit nodes available, %d selected by management, %d user-selected, %d user-deselected",
691+
len(info.allIDs), len(info.selectedByManagement), len(info.userSelected), len(info.userDeselected))
692+
}

client/internal/routemanager/manager_test.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,14 +190,15 @@ func TestManagerUpdateRoutes(t *testing.T) {
190190
name: "No Small Client Route Should Be Added",
191191
inputRoutes: []*route.Route{
192192
{
193-
ID: "a",
194-
NetID: "routeA",
195-
Peer: remotePeerKey1,
196-
Network: netip.MustParsePrefix("0.0.0.0/0"),
197-
NetworkType: route.IPv4Network,
198-
Metric: 9999,
199-
Masquerade: false,
200-
Enabled: true,
193+
ID: "a",
194+
NetID: "routeA",
195+
Peer: remotePeerKey1,
196+
Network: netip.MustParsePrefix("0.0.0.0/0"),
197+
NetworkType: route.IPv4Network,
198+
Metric: 9999,
199+
Masquerade: false,
200+
Enabled: true,
201+
SkipAutoApply: false,
201202
},
202203
},
203204
inputSerial: 1,

client/internal/routemanager/vars/vars.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ var (
1313

1414
Defaultv4 = netip.PrefixFrom(netip.IPv4Unspecified(), 0)
1515
Defaultv6 = netip.PrefixFrom(netip.IPv6Unspecified(), 0)
16+
17+
ExitNodeCIDR = "0.0.0.0/0"
1618
)

0 commit comments

Comments
 (0)