diff --git a/management/internals/controllers/network_map/controller/controller.go b/management/internals/controllers/network_map/controller/controller.go index ad25494c7..05fca99e3 100644 --- a/management/internals/controllers/network_map/controller/controller.go +++ b/management/internals/controllers/network_map/controller/controller.go @@ -2,9 +2,11 @@ package controller import ( "context" + "encoding/json" "errors" "fmt" "os" + "path/filepath" "slices" "strconv" "strings" @@ -23,6 +25,7 @@ import ( "github.com/netbirdio/netbird/management/server/account" "github.com/netbirdio/netbird/management/server/integrations/integrated_validator" "github.com/netbirdio/netbird/management/server/integrations/port_forwarding" + routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" nbpeer "github.com/netbirdio/netbird/management/server/peer" "github.com/netbirdio/netbird/management/server/posture" "github.com/netbirdio/netbird/management/server/settings" @@ -191,7 +194,7 @@ func (c *Controller) sendUpdateAccountPeers(ctx context.Context, accountID strin var remotePeerNetworkMap *types.NetworkMap if c.experimentalNetworkMap(accountID) { - remotePeerNetworkMap = c.getPeerNetworkMapExp(ctx, p.AccountID, p.ID, approvedPeersMap, customZone, c.accountManagerMetrics) + remotePeerNetworkMap = c.getPeerNetworkMapExp(ctx, p.AccountID, p.ID, approvedPeersMap, customZone, c.accountManagerMetrics, resourcePolicies, routers) } else { remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, p.ID, customZone, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics) } @@ -305,7 +308,7 @@ func (c *Controller) UpdateAccountPeer(ctx context.Context, accountId string, pe var remotePeerNetworkMap *types.NetworkMap if c.experimentalNetworkMap(accountId) { - remotePeerNetworkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, customZone, c.accountManagerMetrics) + remotePeerNetworkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, customZone, c.accountManagerMetrics, resourcePolicies, routers) } else { remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, peerId, customZone, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics) } @@ -434,6 +437,8 @@ func (c *Controller) GetValidatedPeerWithMap(ctx context.Context, isRequiresAppr log.WithContext(ctx).Debugf("getPeerPostureChecks took %s", time.Since(startPosture)) customZone := account.GetPeersCustomZone(ctx, c.GetDNSDomain(account.Settings)) + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() proxyNetworkMaps, err := c.proxyController.GetProxyNetworkMaps(ctx, account.Id, peer.ID, account.Peers) if err != nil { @@ -444,9 +449,9 @@ func (c *Controller) GetValidatedPeerWithMap(ctx context.Context, isRequiresAppr var networkMap *types.NetworkMap if c.experimentalNetworkMap(accountID) { - networkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, customZone, c.accountManagerMetrics) + networkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, customZone, c.accountManagerMetrics, resourcePolicies, routers) } else { - networkMap = account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), c.accountManagerMetrics) + networkMap = account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, resourcePolicies, routers, c.accountManagerMetrics) } proxyNetworkMap, ok := proxyNetworkMaps[peer.ID] @@ -471,6 +476,8 @@ func (c *Controller) getPeerNetworkMapExp( validatedPeers map[string]struct{}, customZone nbdns.CustomZone, metrics *telemetry.AccountManagerMetrics, + resourcePolicies map[string][]*types.Policy, + routers map[string]map[string]*routerTypes.NetworkRouter, ) *types.NetworkMap { account := c.getAccountFromHolderOrInit(accountId) if account == nil { @@ -479,7 +486,56 @@ func (c *Controller) getPeerNetworkMapExp( Network: &types.Network{}, } } - return account.GetPeerNetworkMapExp(ctx, peerId, customZone, validatedPeers, metrics) + + legacyMap := account.GetPeerNetworkMap(ctx, peerId, customZone, validatedPeers, resourcePolicies, routers, nil) + + go func() { + expMap := account.GetPeerNetworkMapExp(ctx, peerId, customZone, validatedPeers, metrics) + c.compareAndSaveNetworkMaps(ctx, accountId, peerId, expMap, legacyMap) + }() + + return legacyMap +} + +func (c *Controller) compareAndSaveNetworkMaps(ctx context.Context, accountId, peerId string, expMap, legacyMap *types.NetworkMap) { + expBytes, err := json.Marshal(expMap) + if err != nil { + log.WithContext(ctx).Warnf("failed to marshal experimental network map: %v", err) + return + } + + legacyBytes, err := json.Marshal(legacyMap) + if err != nil { + log.WithContext(ctx).Warnf("failed to marshal legacy network map: %v", err) + return + } + + if len(expBytes) == len(legacyBytes) { + log.WithContext(ctx).Debugf("network maps are equal for peer %s in account %s (size: %d bytes)", peerId, accountId, len(expBytes)) + return + } + + timestamp := time.Now().UnixMicro() + baseDir := filepath.Join("debug_networkmaps", accountId, peerId) + + if err := os.MkdirAll(baseDir, 0o755); err != nil { + log.WithContext(ctx).Warnf("failed to create debug directory %s: %v", baseDir, err) + return + } + + expFile := filepath.Join(baseDir, fmt.Sprintf("exp_networkmap_%d.json", timestamp)) + if err := os.WriteFile(expFile, expBytes, 0o644); err != nil { + log.WithContext(ctx).Warnf("failed to write experimental network map to %s: %v", expFile, err) + return + } + + legacyFile := filepath.Join(baseDir, fmt.Sprintf("legacy_networkmap_%d.json", timestamp)) + if err := os.WriteFile(legacyFile, legacyBytes, 0o644); err != nil { + log.WithContext(ctx).Warnf("failed to write legacy network map to %s: %v", legacyFile, err) + return + } + + log.WithContext(ctx).Infof("network maps differ for peer %s in account %s - saved to %s (exp: %d bytes, legacy: %d bytes)", peerId, accountId, baseDir, len(expBytes), len(legacyBytes)) } func (c *Controller) onPeerAddedUpdNetworkMapCache(account *types.Account, peerId string) error { @@ -752,6 +808,8 @@ func (c *Controller) GetNetworkMap(ctx context.Context, peerID string) (*types.N return nil, err } customZone := account.GetPeersCustomZone(ctx, c.GetDNSDomain(account.Settings)) + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() proxyNetworkMaps, err := c.proxyController.GetProxyNetworkMaps(ctx, account.Id, peerID, account.Peers) if err != nil { @@ -762,9 +820,9 @@ func (c *Controller) GetNetworkMap(ctx context.Context, peerID string) (*types.N var networkMap *types.NetworkMap if c.experimentalNetworkMap(peer.AccountID) { - networkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peerID, validatedPeers, customZone, nil) + networkMap = c.getPeerNetworkMapExp(ctx, peer.AccountID, peerID, validatedPeers, customZone, nil, resourcePolicies, routers) } else { - networkMap = account.GetPeerNetworkMap(ctx, peer.ID, customZone, validatedPeers, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), nil) + networkMap = account.GetPeerNetworkMap(ctx, peer.ID, customZone, validatedPeers, resourcePolicies, routers, nil) } proxyNetworkMap, ok := proxyNetworkMaps[peer.ID]