diff --git a/management/server/networkmap.go b/management/server/networkmap.go index 2a0627643..b4fb95a7f 100644 --- a/management/server/networkmap.go +++ b/management/server/networkmap.go @@ -2,12 +2,18 @@ package server import ( "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "time" log "github.com/sirupsen/logrus" "golang.org/x/exp/maps" nbdns "github.com/netbirdio/netbird/dns" nbpeer "github.com/netbirdio/netbird/management/server/peer" + routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types" "github.com/netbirdio/netbird/management/server/telemetry" "github.com/netbirdio/netbird/management/server/types" ) @@ -24,6 +30,8 @@ func (am *DefaultAccountManager) 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 := am.getAccountFromHolderOrInit(accountId) if account == nil { @@ -32,7 +40,53 @@ func (am *DefaultAccountManager) getPeerNetworkMapExp( Network: &types.Network{}, } } - return account.GetPeerNetworkMapExp(ctx, peerId, customZone, validatedPeers, metrics) + + expMap := account.GetPeerNetworkMapExp(ctx, peerId, customZone, validatedPeers, metrics) + legacyMap := account.GetPeerNetworkMap(ctx, peerId, customZone, validatedPeers, resourcePolicies, routers, nil) + am.compareAndSaveNetworkMaps(ctx, accountId, peerId, expMap, legacyMap) + + return expMap +} + +func (am *DefaultAccountManager) 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 (am *DefaultAccountManager) onPeerAddedUpdNetworkMapCache(account *types.Account, peerId string) error { diff --git a/management/server/peer.go b/management/server/peer.go index 4c605b5eb..ce965e92d 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -431,12 +431,15 @@ func (am *DefaultAccountManager) GetNetworkMap(ctx context.Context, peerID strin return nil, err } + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() + var networkMap *types.NetworkMap if am.experimentalNetworkMap(peer.AccountID) { - networkMap = am.getPeerNetworkMapExp(ctx, peer.AccountID, peerID, validatedPeers, customZone, nil) + networkMap = am.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] @@ -1079,12 +1082,15 @@ func (am *DefaultAccountManager) getValidatedPeerWithMap(ctx context.Context, is return nil, nil, nil, err } + resourcePolicies := account.GetResourcePoliciesMap() + routers := account.GetResourceRoutersMap() + var networkMap *types.NetworkMap if am.experimentalNetworkMap(accountID) { - networkMap = am.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, customZone, am.metrics.AccountManagerMetrics()) + networkMap = am.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, customZone, am.metrics.AccountManagerMetrics(), resourcePolicies, routers) } else { - networkMap = account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, account.GetResourcePoliciesMap(), account.GetResourceRoutersMap(), am.metrics.AccountManagerMetrics()) + networkMap = account.GetPeerNetworkMap(ctx, peer.ID, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics()) } proxyNetworkMap, ok := proxyNetworkMaps[peer.ID] @@ -1303,7 +1309,7 @@ func (am *DefaultAccountManager) UpdateAccountPeers(ctx context.Context, account var remotePeerNetworkMap *types.NetworkMap if am.experimentalNetworkMap(accountID) { - remotePeerNetworkMap = am.getPeerNetworkMapExp(ctx, p.AccountID, p.ID, approvedPeersMap, customZone, am.metrics.AccountManagerMetrics()) + remotePeerNetworkMap = am.getPeerNetworkMapExp(ctx, p.AccountID, p.ID, approvedPeersMap, customZone, am.metrics.AccountManagerMetrics(), resourcePolicies, routers) } else { remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, p.ID, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics()) } @@ -1419,7 +1425,7 @@ func (am *DefaultAccountManager) UpdateAccountPeer(ctx context.Context, accountI var remotePeerNetworkMap *types.NetworkMap if am.experimentalNetworkMap(accountId) { - remotePeerNetworkMap = am.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, customZone, am.metrics.AccountManagerMetrics()) + remotePeerNetworkMap = am.getPeerNetworkMapExp(ctx, peer.AccountID, peer.ID, approvedPeersMap, customZone, am.metrics.AccountManagerMetrics(), resourcePolicies, routers) } else { remotePeerNetworkMap = account.GetPeerNetworkMap(ctx, peerId, customZone, approvedPeersMap, resourcePolicies, routers, am.metrics.AccountManagerMetrics()) }