master
go 353 lines 7.92 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import (
6 "fmt"
7 "net/netip"
8 "sort"
9 "strconv"
10 )
11
12 var (
13 pointToPointMaskIPv4 = netip.MustParseAddr("255.255.255.252")
14 pointToPointMaskIPv6 = netip.MustParseAddr("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")
15 loopbackMaskIPv4 = netip.MustParseAddr("255.255.255.255")
16 loopbackMaskIPv6 = netip.MustParseAddr("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
17 loopbackAddrIPv4 = netip.MustParseAddr("127.0.0.1")
18 )
19
20 // SubNetwork mirrors Enlinkd SubNetwork behavior for node/IP membership.
21 type SubNetwork struct {
22 network netip.Addr
23 netmask netip.Addr
24 nodeInterfaceMap map[int]map[netip.Addr]struct{}
25 }
26
27 // NewSubNetwork creates a subnet from one managed IP interface.
28 func NewSubNetwork(nodeID int, ip, netmask netip.Addr) (*SubNetwork, error) {
29 if nodeID <= 0 {
30 return nil, fmt.Errorf("node id is required")
31 }
32 if !ip.IsValid() {
33 return nil, fmt.Errorf("ip is required")
34 }
35 if !netmask.IsValid() {
36 return nil, fmt.Errorf("netmask is required")
37 }
38 network, ok := NetworkAddress(ip, netmask)
39 if !ok {
40 return nil, fmt.Errorf("cannot build network from ip %q and netmask %q", ip, netmask)
41 }
42 s := &SubNetwork{
43 network: network,
44 netmask: netmask,
45 nodeInterfaceMap: map[int]map[netip.Addr]struct{}{},
46 }
47 s.nodeInterfaceMap[nodeID] = map[netip.Addr]struct{}{ip.Unmap(): {}}
48 return s, nil
49 }
50
51 // Network returns the network address.
52 func (s *SubNetwork) Network() netip.Addr {
53 if s == nil {
54 return netip.Addr{}
55 }
56 return s.network
57 }
58
59 // Netmask returns the subnet mask.
60 func (s *SubNetwork) Netmask() netip.Addr {
61 if s == nil {
62 return netip.Addr{}
63 }
64 return s.netmask
65 }
66
67 // CIDR returns network/prefix format.
68 func (s *SubNetwork) CIDR() string {
69 if s == nil || !s.network.IsValid() || !s.netmask.IsValid() {
70 return ""
71 }
72 prefix, err := MaskToCIDRPrefix(s.netmask)
73 if err != nil {
74 return ""
75 }
76 return s.network.String() + "/" + strconv.Itoa(prefix)
77 }
78
79 // NetworkPrefix returns the CIDR prefix for the mask.
80 func (s *SubNetwork) NetworkPrefix() int {
81 if s == nil {
82 return 0
83 }
84 prefix, err := MaskToCIDRPrefix(s.netmask)
85 if err != nil {
86 return 0
87 }
88 return prefix
89 }
90
91 // IsIPv4Subnetwork reports if the subnet uses IPv4.
92 func (s *SubNetwork) IsIPv4Subnetwork() bool {
93 return s != nil && s.network.IsValid() && s.network.Is4()
94 }
95
96 // NodeIDs returns sorted node IDs in the subnet.
97 func (s *SubNetwork) NodeIDs() []int {
98 if s == nil || len(s.nodeInterfaceMap) == 0 {
99 return nil
100 }
101 ids := make([]int, 0, len(s.nodeInterfaceMap))
102 for nodeID := range s.nodeInterfaceMap {
103 ids = append(ids, nodeID)
104 }
105 sort.Ints(ids)
106 return ids
107 }
108
109 // Add adds one node/IP membership if the address is in range.
110 func (s *SubNetwork) Add(nodeID int, ip netip.Addr) bool {
111 if s == nil || nodeID <= 0 || !ip.IsValid() || !s.IsInRange(ip) {
112 return false
113 }
114 ip = ip.Unmap()
115 if _, ok := s.nodeInterfaceMap[nodeID]; !ok {
116 s.nodeInterfaceMap[nodeID] = map[netip.Addr]struct{}{}
117 }
118 if _, exists := s.nodeInterfaceMap[nodeID][ip]; exists {
119 return false
120 }
121 s.nodeInterfaceMap[nodeID][ip] = struct{}{}
122 return true
123 }
124
125 // Remove removes one node/IP membership.
126 func (s *SubNetwork) Remove(nodeID int, ip netip.Addr) bool {
127 if s == nil || nodeID <= 0 || !ip.IsValid() {
128 return false
129 }
130 ip = ip.Unmap()
131 ips, ok := s.nodeInterfaceMap[nodeID]
132 if !ok {
133 return false
134 }
135 if _, exists := ips[ip]; !exists {
136 return false
137 }
138 delete(ips, ip)
139 if len(ips) == 0 {
140 delete(s.nodeInterfaceMap, nodeID)
141 }
142 return true
143 }
144
145 // IsInRange reports if ip belongs to this subnet.
146 func (s *SubNetwork) IsInRange(ip netip.Addr) bool {
147 if s == nil || !ip.IsValid() || !s.network.IsValid() || !s.netmask.IsValid() {
148 return false
149 }
150 return InSameNetwork(ip.Unmap(), s.network, s.netmask)
151 }
152
153 // HasDuplicatedAddress reports true when the same address exists under multiple entries.
154 func (s *SubNetwork) HasDuplicatedAddress() bool {
155 if s == nil {
156 return false
157 }
158 seen := make(map[netip.Addr]struct{})
159 for _, addresses := range s.nodeInterfaceMap {
160 for addr := range addresses {
161 if _, ok := seen[addr]; ok {
162 return true
163 }
164 seen[addr] = struct{}{}
165 }
166 }
167 return false
168 }
169
170 func (s *SubNetwork) clone() *SubNetwork {
171 if s == nil {
172 return nil
173 }
174 out := &SubNetwork{
175 network: s.network,
176 netmask: s.netmask,
177 nodeInterfaceMap: make(map[int]map[netip.Addr]struct{}, len(s.nodeInterfaceMap)),
178 }
179 for nodeID, ips := range s.nodeInterfaceMap {
180 copySet := make(map[netip.Addr]struct{}, len(ips))
181 for ip := range ips {
182 copySet[ip] = struct{}{}
183 }
184 out.nodeInterfaceMap[nodeID] = copySet
185 }
186 return out
187 }
188
189 func (s *SubNetwork) key() string {
190 if s == nil {
191 return ""
192 }
193 return subnetKey(s.network, s.netmask)
194 }
195
196 func subnetKey(network, netmask netip.Addr) string {
197 if !network.IsValid() || !netmask.IsValid() {
198 return ""
199 }
200 // Unmap IPv4-mapped IPv6 addresses so that ::ffff:10.0.0.0 and 10.0.0.0
201 // produce the same key.
202 network = network.Unmap()
203 netmask = netmask.Unmap()
204 return network.String() + keySep + netmask.String()
205 }
206
207 func sortedSubnetworkKeys(subnets map[string]*SubNetwork) []string {
208 keys := make([]string, 0, len(subnets))
209 for key := range subnets {
210 keys = append(keys, key)
211 }
212 sort.Strings(keys)
213 return keys
214 }
215
216 func compareAddr(a, b netip.Addr) int {
217 ab := addrBytes(a)
218 bb := addrBytes(b)
219 if len(ab) != len(bb) {
220 if len(ab) < len(bb) {
221 return -1
222 }
223 return 1
224 }
225 for i := range ab {
226 if ab[i] < bb[i] {
227 return -1
228 }
229 if ab[i] > bb[i] {
230 return 1
231 }
232 }
233 return 0
234 }
235
236 // IsPointToPointMask ports InetAddressUtils.isPointToPointMask().
237 func IsPointToPointMask(mask netip.Addr) bool {
238 mask = mask.Unmap()
239 return mask == pointToPointMaskIPv4 || mask == pointToPointMaskIPv6
240 }
241
242 // IsLoopbackMask ports InetAddressUtils.isLoopbackMask().
243 func IsLoopbackMask(mask netip.Addr) bool {
244 mask = mask.Unmap()
245 return mask == loopbackMaskIPv4 || mask == loopbackMaskIPv6
246 }
247
248 // InSameNetwork ports InetAddressUtils.inSameNetwork().
249 func InSameNetwork(addr1, addr2, mask netip.Addr) bool {
250 addr1 = addr1.Unmap()
251 addr2 = addr2.Unmap()
252 mask = mask.Unmap()
253 if !addr1.IsValid() || !addr2.IsValid() || !mask.IsValid() {
254 return false
255 }
256 if addr1.Is4() != addr2.Is4() || addr1.Is4() != mask.Is4() {
257 return false
258 }
259
260 ab := addrBytes(addr1)
261 bb := addrBytes(addr2)
262 mb := addrBytes(mask)
263 if len(ab) != len(bb) || len(ab) != len(mb) {
264 return false
265 }
266 for i := range ab {
267 if (ab[i] & mb[i]) != (bb[i] & mb[i]) {
268 return false
269 }
270 }
271 return true
272 }
273
274 // NetworkAddress returns ip&mask for matching IP families.
275 func NetworkAddress(ip, mask netip.Addr) (netip.Addr, bool) {
276 ip = ip.Unmap()
277 mask = mask.Unmap()
278 if !ip.IsValid() || !mask.IsValid() || ip.Is4() != mask.Is4() {
279 return netip.Addr{}, false
280 }
281 ib := addrBytes(ip)
282 mb := addrBytes(mask)
283 if len(ib) != len(mb) {
284 return netip.Addr{}, false
285 }
286 out := make([]byte, len(ib))
287 for i := range ib {
288 out[i] = ib[i] & mb[i]
289 }
290 addr, ok := netip.AddrFromSlice(out)
291 if !ok {
292 return netip.Addr{}, false
293 }
294 return addr.Unmap(), true
295 }
296
297 // MaskToCIDRPrefix ports InetAddressUtils.convertInetAddressMaskToCidr().
298 func MaskToCIDRPrefix(mask netip.Addr) (int, error) {
299 mask = mask.Unmap()
300 if !mask.IsValid() {
301 return 0, fmt.Errorf("invalid mask")
302 }
303 foundZero := false
304 cidr := 0
305 for _, value := range addrBytes(mask) {
306 k := int(value)
307 if foundZero && k != 0 {
308 return 0, fmt.Errorf("invalid mask %q", mask)
309 }
310 switch k {
311 case 255:
312 cidr += 8
313 case 254:
314 cidr += 7
315 foundZero = true
316 case 252:
317 cidr += 6
318 foundZero = true
319 case 248:
320 cidr += 5
321 foundZero = true
322 case 240:
323 cidr += 4
324 foundZero = true
325 case 224:
326 cidr += 3
327 foundZero = true
328 case 192:
329 cidr += 2
330 foundZero = true
331 case 128:
332 cidr += 1
333 foundZero = true
334 case 0:
335 foundZero = true
336 default:
337 return 0, fmt.Errorf("invalid mask %q", mask)
338 }
339 }
340 return cidr, nil
341 }
342
343 func addrBytes(addr netip.Addr) []byte {
344 if !addr.IsValid() {
345 return nil
346 }
347 if addr.Is4() {
348 a := addr.As4()
349 return a[:]
350 }
351 a := addr.As16()
352 return a[:]
353 }