master
go 234 lines 5.33 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import (
6 "sort"
7 "strings"
8 )
9
10 func inferFDBEndpointOwners(
11 observations fdbReporterObservation,
12 reporterAliases map[string][]string,
13 switchFacingPortKeys map[string]struct{},
14 ) map[string]fdbEndpointOwner {
15 if len(observations.byEndpoint) == 0 {
16 return nil
17 }
18
19 owners := make(map[string]fdbEndpointOwner, len(observations.byEndpoint))
20 endpointIDs := make([]string, 0, len(observations.byEndpoint))
21 for endpointID := range observations.byEndpoint {
22 endpointIDs = append(endpointIDs, endpointID)
23 }
24 sort.Strings(endpointIDs)
25
26 for _, endpointID := range endpointIDs {
27 reportersMap := observations.byEndpoint[endpointID]
28 if len(reportersMap) < 2 {
29 continue
30 }
31
32 reporterIDs := make([]string, 0, len(reportersMap))
33 for reporterID := range reportersMap {
34 reporterIDs = append(reporterIDs, reporterID)
35 }
36 sort.Strings(reporterIDs)
37
38 validPortsByReporter := make(map[string][]string)
39 for _, reporterID := range reporterIDs {
40 ports := sortedTopologySet(reportersMap[reporterID])
41 if len(ports) == 0 {
42 continue
43 }
44
45 for _, endpointPort := range ports {
46 if _, isSwitchFacingPort := switchFacingPortKeys[endpointPort]; isSwitchFacingPort {
47 continue
48 }
49 if !reporterSatisfiesFDBOwnerRule(endpointPort, reporterID, reporterIDs, observations.byReporter, reporterAliases) {
50 continue
51 }
52 validPortsByReporter[reporterID] = append(validPortsByReporter[reporterID], endpointPort)
53 }
54 }
55
56 if len(validPortsByReporter) != 1 {
57 continue
58 }
59 for _, ports := range validPortsByReporter {
60 ports = uniqueTopologyStrings(ports)
61 if len(ports) == 0 {
62 continue
63 }
64 owners[endpointID] = fdbEndpointOwner{
65 portKey: ports[0],
66 source: "reporter_matrix",
67 }
68 }
69 }
70
71 if len(owners) == 0 {
72 return nil
73 }
74 return owners
75 }
76
77 func reporterSatisfiesFDBOwnerRule(
78 endpointPort string,
79 reporterID string,
80 reporterIDs []string,
81 reporterObservations map[string]map[string]map[string]struct{},
82 reporterAliases map[string][]string,
83 ) bool {
84 reporterEndpoints := reporterObservations[reporterID]
85 if len(reporterEndpoints) == 0 {
86 return false
87 }
88
89 for _, otherReporterID := range reporterIDs {
90 if otherReporterID == reporterID {
91 continue
92 }
93 aliases := reporterAliases[otherReporterID]
94 if len(aliases) == 0 {
95 return false
96 }
97
98 seenOtherOnDifferentPort := false
99 for _, alias := range aliases {
100 ports := reporterEndpoints[alias]
101 if len(ports) == 0 {
102 continue
103 }
104 for observedPort := range ports {
105 if observedPort == endpointPort {
106 return false
107 }
108 seenOtherOnDifferentPort = true
109 }
110 }
111 if !seenOtherOnDifferentPort {
112 return false
113 }
114 }
115
116 return true
117 }
118
119 func inferSinglePortEndpointOwners(
120 macLinks []bridgeMacLinkRecord,
121 switchFacingPortKeys map[string]struct{},
122 ) map[string]fdbEndpointOwner {
123 if len(macLinks) == 0 {
124 return nil
125 }
126
127 type portScope struct {
128 portKey string
129 portVLANKey string
130 port bridgePortRef
131 endpointIDs map[string]struct{}
132 }
133
134 byPortScope := make(map[string]*portScope)
135 for _, link := range macLinks {
136 if strings.ToLower(strings.TrimSpace(link.method)) != "fdb" {
137 continue
138 }
139 endpointID := normalizeFDBEndpointID(link.endpointID)
140 if endpointID == "" {
141 continue
142 }
143
144 portKey := bridgePortObservationKey(link.port)
145 if portKey == "" {
146 continue
147 }
148 if _, isSwitchFacingPort := switchFacingPortKeys[portKey]; isSwitchFacingPort {
149 continue
150 }
151 portVLANKey := bridgePortObservationVLANKey(link.port)
152 if portVLANKey == "" {
153 portVLANKey = portKey
154 }
155 if _, isSwitchFacingPort := switchFacingPortKeys[portVLANKey]; isSwitchFacingPort {
156 continue
157 }
158
159 scope := byPortScope[portVLANKey]
160 if scope == nil {
161 scope = &portScope{
162 portKey: portKey,
163 portVLANKey: portVLANKey,
164 port: link.port,
165 endpointIDs: make(map[string]struct{}),
166 }
167 byPortScope[portVLANKey] = scope
168 }
169 scope.endpointIDs[endpointID] = struct{}{}
170 }
171
172 if len(byPortScope) == 0 {
173 return nil
174 }
175
176 candidatesByEndpoint := make(map[string]map[string]fdbEndpointOwner)
177 scopeKeys := make([]string, 0, len(byPortScope))
178 for key := range byPortScope {
179 scopeKeys = append(scopeKeys, key)
180 }
181 sort.Strings(scopeKeys)
182
183 for _, scopeKey := range scopeKeys {
184 scope := byPortScope[scopeKey]
185 if scope == nil || len(scope.endpointIDs) != 1 {
186 continue
187 }
188 endpointID := ""
189 for id := range scope.endpointIDs {
190 endpointID = id
191 break
192 }
193 if endpointID == "" {
194 continue
195 }
196 candidates := candidatesByEndpoint[endpointID]
197 if candidates == nil {
198 candidates = make(map[string]fdbEndpointOwner)
199 candidatesByEndpoint[endpointID] = candidates
200 }
201 candidates[scope.portVLANKey] = fdbEndpointOwner{
202 portKey: scope.portKey,
203 portVLANKey: scope.portVLANKey,
204 port: scope.port,
205 source: "single_port_mac",
206 }
207 }
208
209 if len(candidatesByEndpoint) == 0 {
210 return nil
211 }
212
213 owners := make(map[string]fdbEndpointOwner)
214 endpointIDs := make([]string, 0, len(candidatesByEndpoint))
215 for endpointID := range candidatesByEndpoint {
216 endpointIDs = append(endpointIDs, endpointID)
217 }
218 sort.Strings(endpointIDs)
219
220 for _, endpointID := range endpointIDs {
221 candidates := candidatesByEndpoint[endpointID]
222 if len(candidates) != 1 {
223 continue
224 }
225 for _, owner := range candidates {
226 owners[endpointID] = owner
227 }
228 }
229
230 if len(owners) == 0 {
231 return nil
232 }
233 return owners
234 }