master
go 167 lines 4.85 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import (
6 "net/netip"
7 "strings"
8 )
9
10 func (s *l2BuildState) isMACCompatibleWithDevice(deviceID, remoteMAC string) bool {
11 deviceID = strings.TrimSpace(deviceID)
12 remoteMAC = normalizeMAC(remoteMAC)
13 if deviceID == "" || remoteMAC == "" {
14 return true
15 }
16 device, ok := s.devices[deviceID]
17 if !ok {
18 return true
19 }
20 deviceMAC := primaryL2MACIdentity(device.ChassisID, "")
21 if deviceMAC == "" {
22 return true
23 }
24 return deviceMAC == remoteMAC
25 }
26
27 func (s *l2BuildState) shouldEnforceHostnameMACGuard(deviceID, mgmtIP string) bool {
28 deviceID = strings.TrimSpace(deviceID)
29 if deviceID == "" {
30 return false
31 }
32 if canonicalIP(mgmtIP) != "" {
33 return true
34 }
35 return s.managedObservationByDeviceID[deviceID]
36 }
37
38 func (s *l2BuildState) resolveKnownRemote(hostname, chassisID, mgmtIP, remoteMAC string) string {
39 remoteIP := canonicalIP(mgmtIP)
40 enforceMACGuard := remoteMAC != ""
41 candidates := []string{
42 s.hostToID[canonicalHost(hostname)],
43 s.chassisToID[canonicalToken(chassisID)],
44 s.ipToID[remoteIP],
45 }
46 for _, candidateID := range candidates {
47 candidateID = strings.TrimSpace(candidateID)
48 if candidateID == "" {
49 continue
50 }
51 if enforceMACGuard && !s.isMACCompatibleWithDevice(candidateID, remoteMAC) {
52 continue
53 }
54 return candidateID
55 }
56 return ""
57 }
58
59 func (s *l2BuildState) resolveRemote(hostname, chassisID, mgmtIP, fallbackID string) string {
60 return s.resolveRemoteWithHostnameMACGuard(hostname, chassisID, mgmtIP, fallbackID, false)
61 }
62
63 func (s *l2BuildState) resolveRemoteEnforcingHostnameMACGuard(hostname, chassisID, mgmtIP, fallbackID string) string {
64 return s.resolveRemoteWithHostnameMACGuard(hostname, chassisID, mgmtIP, fallbackID, true)
65 }
66
67 func (s *l2BuildState) resolveRemoteWithHostnameMACGuard(hostname, chassisID, mgmtIP, fallbackID string, enforceHostnameMACGuard bool) string {
68 remoteMAC := primaryL2MACIdentity(chassisID, "")
69 if knownID := s.resolveKnownRemote(hostname, chassisID, mgmtIP, remoteMAC); knownID != "" {
70 if remoteMAC != "" {
71 s.macToID[remoteMAC] = knownID
72 s.chassisToID[canonicalToken(remoteMAC)] = knownID
73 if device, ok := s.devices[knownID]; ok && primaryL2MACIdentity(device.ChassisID, "") == "" {
74 device.ChassisID = remoteMAC
75 s.devices[knownID] = device
76 }
77 }
78 return knownID
79 }
80
81 if remoteMAC != "" {
82 if id := s.macToID[remoteMAC]; id != "" {
83 return id
84 }
85
86 generatedID := deriveRemoteDeviceID(hostname, remoteMAC, mgmtIP, fallbackID)
87 if existingID := strings.TrimSpace(s.hostToID[canonicalHost(hostname)]); existingID != "" &&
88 (enforceHostnameMACGuard || s.shouldEnforceHostnameMACGuard(existingID, mgmtIP)) &&
89 !s.isMACCompatibleWithDevice(existingID, remoteMAC) {
90 generatedID = deriveRemoteDeviceID("", remoteMAC, mgmtIP, fallbackID)
91 }
92 if _, ok := s.devices[generatedID]; !ok {
93 device := Device{
94 ID: generatedID,
95 Hostname: strings.TrimSpace(hostname),
96 SysObject: "",
97 ChassisID: remoteMAC,
98 }
99 if device.Hostname == "" {
100 device.Hostname = generatedID
101 }
102 if ip := parseAddr(mgmtIP); ip.IsValid() {
103 device.Addresses = []netip.Addr{ip}
104 }
105 s.devices[generatedID] = device
106 }
107
108 s.macToID[remoteMAC] = generatedID
109 s.chassisToID[canonicalToken(remoteMAC)] = generatedID
110 if host := canonicalHost(hostname); host != "" {
111 if existingID := strings.TrimSpace(s.hostToID[host]); existingID == "" ||
112 (!enforceHostnameMACGuard && !s.shouldEnforceHostnameMACGuard(existingID, mgmtIP)) ||
113 s.isMACCompatibleWithDevice(existingID, remoteMAC) {
114 s.hostToID[host] = generatedID
115 }
116 }
117 if ip := canonicalIP(mgmtIP); ip != "" {
118 if existingID := strings.TrimSpace(s.ipToID[ip]); existingID == "" || s.isMACCompatibleWithDevice(existingID, remoteMAC) {
119 s.ipToID[ip] = generatedID
120 }
121 }
122 return generatedID
123 }
124
125 if id := s.hostToID[canonicalHost(hostname)]; id != "" {
126 return id
127 }
128 if id := s.chassisToID[canonicalToken(chassisID)]; id != "" {
129 return id
130 }
131 if id := s.ipToID[canonicalIP(mgmtIP)]; id != "" {
132 return id
133 }
134
135 generatedID := deriveRemoteDeviceID(hostname, chassisID, mgmtIP, fallbackID)
136 if _, ok := s.devices[generatedID]; !ok {
137 device := Device{
138 ID: generatedID,
139 Hostname: strings.TrimSpace(hostname),
140 SysObject: "",
141 ChassisID: strings.TrimSpace(chassisID),
142 }
143 if device.Hostname == "" {
144 device.Hostname = generatedID
145 }
146 if ip := parseAddr(mgmtIP); ip.IsValid() {
147 device.Addresses = []netip.Addr{ip}
148 }
149 s.devices[generatedID] = device
150 }
151 if host := canonicalHost(hostname); host != "" {
152 if _, exists := s.hostToID[host]; !exists {
153 s.hostToID[host] = generatedID
154 }
155 }
156 if chassis := canonicalToken(chassisID); chassis != "" {
157 if _, exists := s.chassisToID[chassis]; !exists {
158 s.chassisToID[chassis] = generatedID
159 }
160 }
161 if ip := canonicalIP(mgmtIP); ip != "" {
162 if _, exists := s.ipToID[ip]; !exists {
163 s.ipToID[ip] = generatedID
164 }
165 }
166 return generatedID
167 }