|
6 | 6 | "fmt"
|
7 | 7 | "math/rand"
|
8 | 8 | "net"
|
| 9 | + "net/netip" |
9 | 10 | "os"
|
10 | 11 | "reflect"
|
11 | 12 | "regexp"
|
@@ -324,6 +325,13 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
|
324 | 325 | return err
|
325 | 326 | }
|
326 | 327 |
|
| 328 | + if oldSettings.NetworkRange != newSettings.NetworkRange { |
| 329 | + if err = am.reallocateAccountPeerIPs(ctx, transaction, accountID, newSettings.NetworkRange); err != nil { |
| 330 | + return err |
| 331 | + } |
| 332 | + updateAccountPeers = true |
| 333 | + } |
| 334 | + |
327 | 335 | if oldSettings.RoutingPeerDNSResolutionEnabled != newSettings.RoutingPeerDNSResolutionEnabled ||
|
328 | 336 | oldSettings.LazyConnectionEnabled != newSettings.LazyConnectionEnabled ||
|
329 | 337 | oldSettings.DNSDomain != newSettings.DNSDomain {
|
@@ -362,7 +370,18 @@ func (am *DefaultAccountManager) UpdateAccountSettings(ctx context.Context, acco
|
362 | 370 | return nil, err
|
363 | 371 | }
|
364 | 372 | if oldSettings.DNSDomain != newSettings.DNSDomain {
|
365 |
| - am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountDNSDomainUpdated, nil) |
| 373 | + eventMeta := map[string]any{ |
| 374 | + "old_dns_domain": oldSettings.DNSDomain, |
| 375 | + "new_dns_domain": newSettings.DNSDomain, |
| 376 | + } |
| 377 | + am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountDNSDomainUpdated, eventMeta) |
| 378 | + } |
| 379 | + if oldSettings.NetworkRange != newSettings.NetworkRange { |
| 380 | + eventMeta := map[string]any{ |
| 381 | + "old_network_range": oldSettings.NetworkRange.String(), |
| 382 | + "new_network_range": newSettings.NetworkRange.String(), |
| 383 | + } |
| 384 | + am.StoreEvent(ctx, userID, accountID, accountID, activity.AccountNetworkRangeUpdated, eventMeta) |
366 | 385 | }
|
367 | 386 |
|
368 | 387 | if updateAccountPeers || extraSettingsChanged || groupChangesAffectPeers {
|
@@ -2018,3 +2037,154 @@ func propagateUserGroupMemberships(ctx context.Context, transaction store.Store,
|
2018 | 2037 |
|
2019 | 2038 | return len(updatedGroups) > 0, peersAffected, nil
|
2020 | 2039 | }
|
| 2040 | + |
| 2041 | +// reallocateAccountPeerIPs re-allocates all peer IPs when the network range changes |
| 2042 | +func (am *DefaultAccountManager) reallocateAccountPeerIPs(ctx context.Context, transaction store.Store, accountID string, newNetworkRange netip.Prefix) error { |
| 2043 | + if !newNetworkRange.IsValid() { |
| 2044 | + return nil |
| 2045 | + } |
| 2046 | + |
| 2047 | + newIPNet := net.IPNet{ |
| 2048 | + IP: newNetworkRange.Masked().Addr().AsSlice(), |
| 2049 | + Mask: net.CIDRMask(newNetworkRange.Bits(), newNetworkRange.Addr().BitLen()), |
| 2050 | + } |
| 2051 | + |
| 2052 | + account, err := transaction.GetAccount(ctx, accountID) |
| 2053 | + if err != nil { |
| 2054 | + return err |
| 2055 | + } |
| 2056 | + |
| 2057 | + account.Network.Net = newIPNet |
| 2058 | + |
| 2059 | + peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "") |
| 2060 | + if err != nil { |
| 2061 | + return err |
| 2062 | + } |
| 2063 | + |
| 2064 | + var takenIPs []net.IP |
| 2065 | + |
| 2066 | + for _, peer := range peers { |
| 2067 | + newIP, err := types.AllocatePeerIP(newIPNet, takenIPs) |
| 2068 | + if err != nil { |
| 2069 | + return status.Errorf(status.Internal, "allocate IP for peer %s: %v", peer.ID, err) |
| 2070 | + } |
| 2071 | + |
| 2072 | + log.WithContext(ctx).Infof("reallocating peer %s IP from %s to %s due to network range change", |
| 2073 | + peer.ID, peer.IP.String(), newIP.String()) |
| 2074 | + |
| 2075 | + peer.IP = newIP |
| 2076 | + takenIPs = append(takenIPs, newIP) |
| 2077 | + } |
| 2078 | + |
| 2079 | + if err = transaction.SaveAccount(ctx, account); err != nil { |
| 2080 | + return err |
| 2081 | + } |
| 2082 | + |
| 2083 | + for _, peer := range peers { |
| 2084 | + if err = transaction.SavePeer(ctx, store.LockingStrengthUpdate, accountID, peer); err != nil { |
| 2085 | + return status.Errorf(status.Internal, "save updated peer %s: %v", peer.ID, err) |
| 2086 | + } |
| 2087 | + } |
| 2088 | + |
| 2089 | + log.WithContext(ctx).Infof("successfully re-allocated IPs for %d peers in account %s to network range %s", |
| 2090 | + len(peers), accountID, newNetworkRange.String()) |
| 2091 | + |
| 2092 | + return nil |
| 2093 | +} |
| 2094 | + |
| 2095 | +func (am *DefaultAccountManager) validateIPForUpdate(account *types.Account, peers []*nbpeer.Peer, peerID string, newIP netip.Addr) error { |
| 2096 | + if !account.Network.Net.Contains(newIP.AsSlice()) { |
| 2097 | + return status.Errorf(status.InvalidArgument, "IP %s is not within the account network range %s", newIP.String(), account.Network.Net.String()) |
| 2098 | + } |
| 2099 | + |
| 2100 | + for _, peer := range peers { |
| 2101 | + if peer.ID != peerID && peer.IP.Equal(newIP.AsSlice()) { |
| 2102 | + return status.Errorf(status.InvalidArgument, "IP %s is already assigned to peer %s", newIP.String(), peer.ID) |
| 2103 | + } |
| 2104 | + } |
| 2105 | + return nil |
| 2106 | +} |
| 2107 | + |
| 2108 | +func (am *DefaultAccountManager) UpdatePeerIP(ctx context.Context, accountID, userID, peerID string, newIP netip.Addr) error { |
| 2109 | + unlock := am.Store.AcquireWriteLockByUID(ctx, accountID) |
| 2110 | + defer unlock() |
| 2111 | + |
| 2112 | + allowed, err := am.permissionsManager.ValidateUserPermissions(ctx, accountID, userID, modules.Peers, operations.Update) |
| 2113 | + if err != nil { |
| 2114 | + return fmt.Errorf("validate user permissions: %w", err) |
| 2115 | + } |
| 2116 | + if !allowed { |
| 2117 | + return status.NewPermissionDeniedError() |
| 2118 | + } |
| 2119 | + |
| 2120 | + updateNetworkMap, err := am.updatePeerIPInTransaction(ctx, accountID, userID, peerID, newIP) |
| 2121 | + if err != nil { |
| 2122 | + return fmt.Errorf("update peer IP transaction: %w", err) |
| 2123 | + } |
| 2124 | + |
| 2125 | + if updateNetworkMap { |
| 2126 | + am.BufferUpdateAccountPeers(ctx, accountID) |
| 2127 | + } |
| 2128 | + return nil |
| 2129 | +} |
| 2130 | + |
| 2131 | +func (am *DefaultAccountManager) updatePeerIPInTransaction(ctx context.Context, accountID, userID, peerID string, newIP netip.Addr) (bool, error) { |
| 2132 | + var updateNetworkMap bool |
| 2133 | + err := am.Store.ExecuteInTransaction(ctx, func(transaction store.Store) error { |
| 2134 | + account, err := transaction.GetAccount(ctx, accountID) |
| 2135 | + if err != nil { |
| 2136 | + return fmt.Errorf("get account: %w", err) |
| 2137 | + } |
| 2138 | + |
| 2139 | + existingPeer, err := transaction.GetPeerByID(ctx, store.LockingStrengthShare, accountID, peerID) |
| 2140 | + if err != nil { |
| 2141 | + return fmt.Errorf("get peer: %w", err) |
| 2142 | + } |
| 2143 | + |
| 2144 | + if existingPeer.IP.Equal(newIP.AsSlice()) { |
| 2145 | + return nil |
| 2146 | + } |
| 2147 | + |
| 2148 | + peers, err := transaction.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "") |
| 2149 | + if err != nil { |
| 2150 | + return fmt.Errorf("get account peers: %w", err) |
| 2151 | + } |
| 2152 | + |
| 2153 | + if err := am.validateIPForUpdate(account, peers, peerID, newIP); err != nil { |
| 2154 | + return err |
| 2155 | + } |
| 2156 | + |
| 2157 | + if err := am.savePeerIPUpdate(ctx, transaction, accountID, userID, existingPeer, newIP); err != nil { |
| 2158 | + return err |
| 2159 | + } |
| 2160 | + |
| 2161 | + updateNetworkMap = true |
| 2162 | + return nil |
| 2163 | + }) |
| 2164 | + return updateNetworkMap, err |
| 2165 | +} |
| 2166 | + |
| 2167 | +func (am *DefaultAccountManager) savePeerIPUpdate(ctx context.Context, transaction store.Store, accountID, userID string, peer *nbpeer.Peer, newIP netip.Addr) error { |
| 2168 | + log.WithContext(ctx).Infof("updating peer %s IP from %s to %s", peer.ID, peer.IP, newIP) |
| 2169 | + |
| 2170 | + settings, err := transaction.GetAccountSettings(ctx, store.LockingStrengthShare, accountID) |
| 2171 | + if err != nil { |
| 2172 | + return fmt.Errorf("get account settings: %w", err) |
| 2173 | + } |
| 2174 | + dnsDomain := am.GetDNSDomain(settings) |
| 2175 | + |
| 2176 | + eventMeta := peer.EventMeta(dnsDomain) |
| 2177 | + oldIP := peer.IP.String() |
| 2178 | + |
| 2179 | + peer.IP = newIP.AsSlice() |
| 2180 | + err = transaction.SavePeer(ctx, store.LockingStrengthUpdate, accountID, peer) |
| 2181 | + if err != nil { |
| 2182 | + return fmt.Errorf("save peer: %w", err) |
| 2183 | + } |
| 2184 | + |
| 2185 | + eventMeta["old_ip"] = oldIP |
| 2186 | + eventMeta["ip"] = newIP.String() |
| 2187 | + am.StoreEvent(ctx, userID, peer.ID, accountID, activity.PeerIPUpdated, eventMeta) |
| 2188 | + |
| 2189 | + return nil |
| 2190 | +} |
0 commit comments