master
go 268 lines 8.14 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import (
6 "strconv"
7 "strings"
8 )
9
10 func (b *segmentProjectionBuilder) allowEndpoint(segmentID, endpointID string, probable bool, probableMode string) {
11 if strings.TrimSpace(segmentID) == "" || strings.TrimSpace(endpointID) == "" {
12 return
13 }
14
15 allowed := b.allowedEndpointBySegment[segmentID]
16 if allowed == nil {
17 allowed = make(map[string]struct{})
18 b.allowedEndpointBySegment[segmentID] = allowed
19 }
20 allowed[endpointID] = struct{}{}
21 b.assignedEndpoints[endpointID] = struct{}{}
22
23 if !probable {
24 strictSet := b.strictEndpointBySegment[segmentID]
25 if strictSet == nil {
26 strictSet = make(map[string]struct{})
27 b.strictEndpointBySegment[segmentID] = strictSet
28 }
29 strictSet[endpointID] = struct{}{}
30 return
31 }
32
33 probableSet := b.probableEndpointBySegment[segmentID]
34 if probableSet == nil {
35 probableSet = make(map[string]struct{})
36 b.probableEndpointBySegment[segmentID] = probableSet
37 }
38 probableSet[endpointID] = struct{}{}
39 if strings.TrimSpace(probableMode) == "" {
40 probableMode = "probable_segment"
41 }
42 modes := b.probableAttachmentModes[segmentID]
43 if modes == nil {
44 modes = make(map[string]string)
45 b.probableAttachmentModes[segmentID] = modes
46 }
47 modes[endpointID] = probableMode
48 }
49
50 func (b *segmentProjectionBuilder) initializeEndpointCandidates() []string {
51 endpointIDs := collectTopologyEndpointIDs(
52 b.endpointMatchByID,
53 b.endpointLabelsByID,
54 b.endpointSegmentCandidates,
55 b.rawFDBObservations,
56 b.fdbObservations,
57 )
58 b.baseCandidatesByEndpoint = make(map[string][]string, len(endpointIDs))
59 b.probableCandidatesByEP = make(map[string][]string, len(endpointIDs))
60 b.strictLinkedEndpoints = make(map[string]struct{}, len(endpointIDs))
61
62 for _, endpointID := range endpointIDs {
63 candidates := b.endpointSegmentCandidates[endpointID]
64 candidateSet := make(map[string]struct{}, len(candidates))
65 for _, candidate := range candidates {
66 candidate = strings.TrimSpace(candidate)
67 if candidate == "" {
68 continue
69 }
70 candidateSet[candidate] = struct{}{}
71 }
72 sortedCandidates := sortedTopologySet(candidateSet)
73 b.out.endpointLinksCandidates += len(sortedCandidates)
74 b.baseCandidatesByEndpoint[endpointID] = sortedCandidates
75 strictSegmentID := ""
76 probableCandidates := sortedCandidates
77 if len(sortedCandidates) == 1 {
78 strictSegmentID = sortedCandidates[0]
79 } else if owner, ok := b.fdbOwners[endpointID]; ok {
80 filtered := make([]string, 0, len(sortedCandidates))
81 for _, segmentID := range sortedCandidates {
82 portKeys := b.segmentPortKeys[segmentID]
83 if len(portKeys) == 0 {
84 continue
85 }
86 if _, matchesOwnerPort := portKeys[owner.portVLANKey]; matchesOwnerPort {
87 filtered = append(filtered, segmentID)
88 continue
89 }
90 if _, matchesOwnerPort := portKeys[owner.portKey]; matchesOwnerPort {
91 filtered = append(filtered, segmentID)
92 }
93 }
94 if len(filtered) == 1 {
95 strictSegmentID = filtered[0]
96 }
97 if len(filtered) > 0 {
98 probableCandidates = filtered
99 }
100 }
101 b.probableCandidatesByEP[endpointID] = probableCandidates
102
103 if strictSegmentID != "" {
104 b.allowEndpoint(strictSegmentID, endpointID, false, "")
105 b.strictLinkedEndpoints[endpointID] = struct{}{}
106 }
107 }
108
109 return endpointIDs
110 }
111
112 func (b *segmentProjectionBuilder) selectManagedProbableHint(endpointID string) probableEndpointReporterHint {
113 hint := selectProbableEndpointReporterHint(
114 b.endpointLabelsByID[endpointID],
115 b.rawFDBReporterHints[normalizeFDBEndpointID(endpointID)],
116 b.fdbOwners[endpointID],
117 b.aliasOwnerIDs,
118 b.managedDeviceIDs,
119 )
120 return ensureManagedProbableReporterHint(
121 hint,
122 b.endpointLabelsByID[endpointID],
123 b.rawFDBReporterHints[normalizeFDBEndpointID(endpointID)],
124 b.aliasOwnerIDs,
125 b.managedDeviceIDs,
126 b.managedDeviceIDList,
127 )
128 }
129
130 func (b *segmentProjectionBuilder) registerProbableSegment(endpointID string, hint probableEndpointReporterHint) string {
131 if strings.TrimSpace(hint.deviceID) == "" {
132 return ""
133 }
134 segmentID, created := ensureProbablePortlessSegment(b.segmentByID, hint)
135 if strings.TrimSpace(segmentID) == "" {
136 return ""
137 }
138 if created {
139 b.segmentIDs = append(b.segmentIDs, segmentID)
140 b.segmentIfIndexes[segmentID] = make(map[string]struct{})
141 b.segmentIfNames[segmentID] = make(map[string]struct{})
142 if hint.ifIndex > 0 {
143 b.segmentIfIndexes[segmentID][strconv.Itoa(hint.ifIndex)] = struct{}{}
144 }
145 if ifName := strings.ToLower(strings.TrimSpace(hint.ifName)); ifName != "" {
146 b.segmentIfNames[segmentID][ifName] = struct{}{}
147 }
148 match, actor := buildBridgeSegmentActor(segmentID, b.segmentByID[segmentID], b.layer, b.source)
149 keys := topologyMatchIdentityKeys(actor.Match)
150 if len(keys) > 0 && !topologyIdentityIndexOverlaps(b.actorIndex, keys) {
151 addTopologyIdentityKeys(b.actorIndex, keys)
152 }
153 b.out.actors = append(b.out.actors, actor)
154 b.segmentMatchByID[segmentID] = match
155 }
156 if seg := b.segmentByID[segmentID]; seg != nil {
157 seg.addEndpoint(endpointID, "probable")
158 }
159 return segmentID
160 }
161
162 func (b *segmentProjectionBuilder) ensureProbableManagedSegment(endpointID string) string {
163 return b.registerProbableSegment(endpointID, b.selectManagedProbableHint(endpointID))
164 }
165
166 func (b *segmentProjectionBuilder) assignProbableEndpoints(endpointIDs []string) {
167 if b.probabilisticConnectivity {
168 for _, endpointID := range endpointIDs {
169 if _, strictLinked := b.strictLinkedEndpoints[endpointID]; strictLinked {
170 continue
171 }
172
173 baseCandidates := b.baseCandidatesByEndpoint[endpointID]
174 probableCandidates := append([]string(nil), b.probableCandidatesByEP[endpointID]...)
175 if len(probableCandidates) == 0 {
176 probableCandidates = probableCandidateSegmentsFromReporterHints(
177 b.endpointLabelsByID[endpointID],
178 b.rawFDBObservations.byEndpoint[normalizeFDBEndpointID(endpointID)],
179 b.reporterSegmentIndex,
180 b.aliasOwnerIDs,
181 b.managedDeviceIDs,
182 )
183 }
184
185 segmentID := pickMostProbableSegment(
186 probableCandidates,
187 b.endpointLabelsByID[endpointID],
188 b.segmentIfIndexes,
189 b.segmentIfNames,
190 )
191 if segmentID == "" && len(probableCandidates) > 0 {
192 segmentID = probableCandidates[0]
193 }
194 if segmentID != "" && !segmentHasManagedPort(b.segmentByID[segmentID], b.managedDeviceIDs) {
195 segmentID = b.ensureProbableManagedSegment(endpointID)
196 }
197 if segmentID == "" {
198 segmentID = b.ensureProbableManagedSegment(endpointID)
199 }
200
201 if segmentID != "" {
202 probableMode := "probable_segment"
203 if strings.HasPrefix(segmentID, "bridge-domain:probable:") {
204 probableMode = "probable_portless"
205 }
206 b.allowEndpoint(segmentID, endpointID, true, probableMode)
207 if len(baseCandidates) > 1 {
208 b.out.endpointLinksSuppressed += len(baseCandidates) - 1
209 }
210 continue
211 }
212
213 if len(baseCandidates) > 1 {
214 b.out.endpointsWithAmbiguousSegment++
215 b.out.endpointLinksSuppressed += len(baseCandidates)
216 }
217 }
218 return
219 }
220
221 for _, endpointID := range endpointIDs {
222 if _, strictLinked := b.strictLinkedEndpoints[endpointID]; strictLinked {
223 continue
224 }
225 baseCandidates := b.baseCandidatesByEndpoint[endpointID]
226 if len(baseCandidates) > 1 {
227 b.out.endpointsWithAmbiguousSegment++
228 b.out.endpointLinksSuppressed += len(baseCandidates)
229 }
230 }
231 }
232
233 func (b *segmentProjectionBuilder) assignRemainingProbableEndpoints(endpointIDs []string) {
234 if !b.probabilisticConnectivity || len(b.managedDeviceIDs) == 0 {
235 return
236 }
237
238 for _, endpointID := range endpointIDs {
239 if _, alreadyAssigned := b.assignedEndpoints[endpointID]; alreadyAssigned {
240 continue
241 }
242 segmentID := b.ensureProbableManagedSegment(endpointID)
243 if strings.TrimSpace(segmentID) == "" {
244 continue
245 }
246 b.allowEndpoint(segmentID, endpointID, true, "probable_portless")
247 }
248 }
249
250 func (b *segmentProjectionBuilder) buildProbableOnlyAnchorPortIDBySegment() map[string]string {
251 out := make(map[string]string)
252 for _, segmentID := range b.segmentIDs {
253 if len(b.probableEndpointBySegment[segmentID]) == 0 {
254 continue
255 }
256 if len(b.strictEndpointBySegment[segmentID]) > 0 {
257 continue
258 }
259 segment := b.segmentByID[segmentID]
260 if segment == nil {
261 continue
262 }
263 if portID := pickProbableSegmentAnchorPortID(segment, b.probableEndpointBySegment[segmentID], b.fdbOwners, b.managedDeviceIDs); portID != "" {
264 out[segmentID] = portID
265 }
266 }
267 return out
268 }