mirror of
https://github.com/netbirdio/netbird.git
synced 2026-03-31 06:34:19 -04:00
Extract v6 exit node pairing into shared helpers
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
@@ -239,74 +238,60 @@ func (c *Client) Networks() *NetworkArray {
|
||||
return nil
|
||||
}
|
||||
|
||||
routesMap := routeManager.GetClientRoutesWithNetID()
|
||||
v6Merged := route.V6ExitMergeSet(routesMap)
|
||||
resolvedDomains := c.recorder.GetResolvedDomainsStates()
|
||||
|
||||
networkArray := &NetworkArray{
|
||||
items: make([]Network, 0),
|
||||
}
|
||||
|
||||
resolvedDomains := c.recorder.GetResolvedDomainsStates()
|
||||
routesMap := routeManager.GetClientRoutesWithNetID()
|
||||
|
||||
// Map v6 exit node IDs (<base>-v6 with ::/0) to their v4 base name.
|
||||
// Also build a set of v6 IDs to skip during the main loop.
|
||||
v6ExitByBase := make(map[route.NetID]route.NetID)
|
||||
v6Merged := make(map[route.NetID]struct{})
|
||||
for id, routes := range routesMap {
|
||||
if len(routes) == 0 {
|
||||
continue
|
||||
}
|
||||
name := string(id)
|
||||
if route.IsV6DefaultRoute(routes[0].Network) && strings.HasSuffix(name, "-v6") {
|
||||
baseName := route.NetID(strings.TrimSuffix(name, "-v6"))
|
||||
if baseRt, ok := routesMap[baseName]; ok && len(baseRt) > 0 && route.IsV4DefaultRoute(baseRt[0].Network) {
|
||||
v6ExitByBase[baseName] = id
|
||||
v6Merged[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for id, routes := range routesMap {
|
||||
if len(routes) == 0 {
|
||||
if _, skip := v6Merged[id]; skip {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := v6Merged[id]; ok {
|
||||
network := c.buildNetwork(id, routes, routeSelector.IsSelected(id), resolvedDomains, v6Merged)
|
||||
if network == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
r := routes[0]
|
||||
domains := c.getNetworkDomainsFromRoute(r, resolvedDomains)
|
||||
netStr := r.Network.String()
|
||||
|
||||
if r.IsDynamic() {
|
||||
netStr = r.Domains.SafeString()
|
||||
}
|
||||
|
||||
routePeer, err := c.findBestRoutePeer(routes)
|
||||
if err != nil {
|
||||
log.Errorf("could not get peer info for route %s: %v", id, err)
|
||||
continue
|
||||
}
|
||||
|
||||
network := Network{
|
||||
Name: string(id),
|
||||
Network: netStr,
|
||||
Peer: routePeer.FQDN,
|
||||
Status: routePeer.ConnStatus.String(),
|
||||
IsSelected: routeSelector.IsSelected(id),
|
||||
Domains: domains,
|
||||
}
|
||||
|
||||
if route.IsV4DefaultRoute(r.Network) {
|
||||
if _, ok := v6ExitByBase[id]; ok {
|
||||
network.Network = "0.0.0.0/0, ::/0"
|
||||
}
|
||||
}
|
||||
|
||||
networkArray.Add(network)
|
||||
networkArray.Add(*network)
|
||||
}
|
||||
return networkArray
|
||||
}
|
||||
|
||||
func (c *Client) buildNetwork(id route.NetID, routes []*route.Route, selected bool, resolvedDomains map[domain.Domain]peer.ResolvedDomainInfo, v6Merged map[route.NetID]struct{}) *Network {
|
||||
r := routes[0]
|
||||
netStr := r.Network.String()
|
||||
if r.IsDynamic() {
|
||||
netStr = r.Domains.SafeString()
|
||||
}
|
||||
|
||||
routePeer, err := c.findBestRoutePeer(routes)
|
||||
if err != nil {
|
||||
log.Errorf("could not get peer info for route %s: %v", id, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
network := &Network{
|
||||
Name: string(id),
|
||||
Network: netStr,
|
||||
Peer: routePeer.FQDN,
|
||||
Status: routePeer.ConnStatus.String(),
|
||||
IsSelected: selected,
|
||||
Domains: c.getNetworkDomainsFromRoute(r, resolvedDomains),
|
||||
}
|
||||
|
||||
if route.IsV4DefaultRoute(r.Network) && route.HasV6ExitPair(id, v6Merged) {
|
||||
network.Network = "0.0.0.0/0, ::/0"
|
||||
}
|
||||
|
||||
return network
|
||||
}
|
||||
|
||||
// findBestRoutePeer returns the peer actively routing traffic for the given
|
||||
// HA route group. Falls back to the first connected peer, then the first peer.
|
||||
func (c *Client) findBestRoutePeer(routes []*route.Route) (peer.State, error) {
|
||||
|
||||
@@ -373,27 +373,20 @@ func (c *Client) GetRoutesSelectionDetails() (*RoutesSelectionDetails, error) {
|
||||
return nil, fmt.Errorf("could not get route selector")
|
||||
}
|
||||
|
||||
// Identify v6 exit nodes paired with a v4 counterpart.
|
||||
v6ExitMerged := make(map[route.NetID]struct{})
|
||||
for id, rt := range routesMap {
|
||||
if len(rt) == 0 {
|
||||
continue
|
||||
}
|
||||
name := string(id)
|
||||
if route.IsV6DefaultRoute(rt[0].Network) && strings.HasSuffix(name, "-v6") {
|
||||
baseName := route.NetID(strings.TrimSuffix(name, "-v6"))
|
||||
if baseRt, ok := routesMap[baseName]; ok && len(baseRt) > 0 && route.IsV4DefaultRoute(baseRt[0].Network) {
|
||||
v6ExitMerged[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
v6ExitMerged := route.V6ExitMergeSet(routesMap)
|
||||
routes := buildSelectRoutes(routesMap, routeSelector.IsSelected, v6ExitMerged)
|
||||
resolvedDomains := c.recorder.GetResolvedDomainsStates()
|
||||
|
||||
return prepareRouteSelectionDetails(routes, resolvedDomains), nil
|
||||
}
|
||||
|
||||
func buildSelectRoutes(routesMap map[route.NetID][]*route.Route, isSelected func(route.NetID) bool, v6Merged map[route.NetID]struct{}) []*selectRoute {
|
||||
var routes []*selectRoute
|
||||
for id, rt := range routesMap {
|
||||
if len(rt) == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := v6ExitMerged[id]; ok {
|
||||
if _, ok := v6Merged[id]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -401,38 +394,30 @@ func (c *Client) GetRoutesSelectionDetails() (*RoutesSelectionDetails, error) {
|
||||
NetID: string(id),
|
||||
Network: rt[0].Network,
|
||||
Domains: rt[0].Domains,
|
||||
Selected: routeSelector.IsSelected(id),
|
||||
Selected: isSelected(id),
|
||||
}
|
||||
|
||||
// Merge paired v6 exit node prefix into this entry.
|
||||
v6ID := route.NetID(string(id) + "-v6")
|
||||
if _, ok := v6ExitMerged[v6ID]; ok {
|
||||
v6Prefix := routesMap[v6ID][0].Network
|
||||
r.extraNetworks = []netip.Prefix{v6Prefix}
|
||||
v6ID := route.NetID(string(id) + route.V6ExitSuffix)
|
||||
if _, ok := v6Merged[v6ID]; ok {
|
||||
r.extraNetworks = []netip.Prefix{routesMap[v6ID][0].Network}
|
||||
}
|
||||
|
||||
routes = append(routes, r)
|
||||
}
|
||||
|
||||
sort.Slice(routes, func(i, j int) bool {
|
||||
iPrefix := routes[i].Network.Bits()
|
||||
jPrefix := routes[j].Network.Bits()
|
||||
|
||||
if iPrefix == jPrefix {
|
||||
iAddr := routes[i].Network.Addr()
|
||||
jAddr := routes[j].Network.Addr()
|
||||
if iAddr == jAddr {
|
||||
return routes[i].NetID < routes[j].NetID
|
||||
}
|
||||
return iAddr.String() < jAddr.String()
|
||||
iBits, jBits := routes[i].Network.Bits(), routes[j].Network.Bits()
|
||||
if iBits != jBits {
|
||||
return iBits < jBits
|
||||
}
|
||||
return iPrefix < jPrefix
|
||||
iAddr, jAddr := routes[i].Network.Addr(), routes[j].Network.Addr()
|
||||
if iAddr != jAddr {
|
||||
return iAddr.Less(jAddr)
|
||||
}
|
||||
return routes[i].NetID < routes[j].NetID
|
||||
})
|
||||
|
||||
resolvedDomains := c.recorder.GetResolvedDomainsStates()
|
||||
|
||||
return prepareRouteSelectionDetails(routes, resolvedDomains), nil
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
func prepareRouteSelectionDetails(routes []*selectRoute, resolvedDomains map[domain.Domain]peer.ResolvedDomainInfo) *RoutesSelectionDetails {
|
||||
|
||||
@@ -45,19 +45,7 @@ func (s *Server) ListNetworks(context.Context, *proto.ListNetworksRequest) (*pro
|
||||
routesMap := routeMgr.GetClientRoutesWithNetID()
|
||||
routeSelector := routeMgr.GetRouteSelector()
|
||||
|
||||
v6ExitMerged := make(map[route.NetID]struct{})
|
||||
for id, rt := range routesMap {
|
||||
if len(rt) == 0 {
|
||||
continue
|
||||
}
|
||||
name := string(id)
|
||||
if route.IsV6DefaultRoute(rt[0].Network) && strings.HasSuffix(name, "-v6") {
|
||||
baseName := route.NetID(strings.TrimSuffix(name, "-v6"))
|
||||
if baseRt, ok := routesMap[baseName]; ok && len(baseRt) > 0 && route.IsV4DefaultRoute(baseRt[0].Network) {
|
||||
v6ExitMerged[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
v6ExitMerged := route.V6ExitMergeSet(routesMap)
|
||||
|
||||
var routes []*selectRoute
|
||||
for id, rt := range routesMap {
|
||||
@@ -77,7 +65,7 @@ func (s *Server) ListNetworks(context.Context, *proto.ListNetworksRequest) (*pro
|
||||
}
|
||||
|
||||
// Merge paired v6 exit node prefix into this entry.
|
||||
v6ID := route.NetID(string(id) + "-v6")
|
||||
v6ID := route.NetID(string(id) + route.V6ExitSuffix)
|
||||
if _, ok := v6ExitMerged[v6ID]; ok {
|
||||
v6Prefix := routesMap[v6ID][0].Network
|
||||
r.extraNetworks = []netip.Prefix{v6Prefix}
|
||||
|
||||
@@ -20,6 +20,9 @@ const (
|
||||
MaxMetric = 9999
|
||||
// MaxNetIDChar Max Network Identifier
|
||||
MaxNetIDChar = 40
|
||||
|
||||
// V6ExitSuffix is appended to a v4 exit node NetID to form its v6 counterpart.
|
||||
V6ExitSuffix = "-v6"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -236,7 +239,7 @@ func ExpandV6ExitPairs(ids []NetID, routesMap map[NetID][]*Route) []NetID {
|
||||
if !ok || len(rt) == 0 || !IsV4DefaultRoute(rt[0].Network) {
|
||||
continue
|
||||
}
|
||||
v6ID := NetID(string(id) + "-v6")
|
||||
v6ID := NetID(string(id) + V6ExitSuffix)
|
||||
if v6Rt, ok := routesMap[v6ID]; ok && len(v6Rt) > 0 && IsV6DefaultRoute(v6Rt[0].Network) {
|
||||
if !slices.Contains(ids, v6ID) {
|
||||
ids = append(ids, v6ID)
|
||||
@@ -245,3 +248,31 @@ func ExpandV6ExitPairs(ids []NetID, routesMap map[NetID][]*Route) []NetID {
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
// V6ExitMergeSet scans routesMap and returns the set of v6 exit node NetIDs
|
||||
// that should be hidden from the UI because they are paired with a v4 exit node.
|
||||
// A v6 ID is paired when it has suffix "-v6", its route is ::/0, and the base
|
||||
// name (without "-v6") exists with route 0.0.0.0/0.
|
||||
func V6ExitMergeSet(routesMap map[NetID][]*Route) map[NetID]struct{} {
|
||||
merged := make(map[NetID]struct{})
|
||||
for id, rt := range routesMap {
|
||||
if len(rt) == 0 {
|
||||
continue
|
||||
}
|
||||
name := string(id)
|
||||
if !IsV6DefaultRoute(rt[0].Network) || !strings.HasSuffix(name, V6ExitSuffix) {
|
||||
continue
|
||||
}
|
||||
baseName := NetID(strings.TrimSuffix(name, V6ExitSuffix))
|
||||
if baseRt, ok := routesMap[baseName]; ok && len(baseRt) > 0 && IsV4DefaultRoute(baseRt[0].Network) {
|
||||
merged[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// HasV6ExitPair reports whether id has a paired v6 exit node in the merge set.
|
||||
func HasV6ExitPair(id NetID, v6Merged map[NetID]struct{}) bool {
|
||||
_, ok := v6Merged[NetID(string(id)+"-v6")]
|
||||
return ok
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user