master
go 237 lines 6.33 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import (
6 "sort"
7 "strconv"
8 "strings"
9 )
10
11 func collectTopologyEndpointIDs(
12 endpointMatchByID map[string]Match,
13 endpointLabelsByID map[string]map[string]string,
14 endpointSegmentCandidates map[string][]string,
15 rawFDBObservations fdbReporterObservation,
16 filteredFDBObservations fdbReporterObservation,
17 ) []string {
18 set := make(map[string]struct{})
19 for endpointID := range endpointMatchByID {
20 endpointID = strings.TrimSpace(endpointID)
21 if endpointID != "" {
22 set[endpointID] = struct{}{}
23 }
24 }
25 for endpointID := range endpointLabelsByID {
26 endpointID = strings.TrimSpace(endpointID)
27 if endpointID != "" {
28 set[endpointID] = struct{}{}
29 }
30 }
31 for endpointID := range endpointSegmentCandidates {
32 endpointID = strings.TrimSpace(endpointID)
33 if endpointID != "" {
34 set[endpointID] = struct{}{}
35 }
36 }
37 for endpointID := range rawFDBObservations.byEndpoint {
38 endpointID = strings.TrimSpace(endpointID)
39 if endpointID != "" {
40 set[endpointID] = struct{}{}
41 }
42 }
43 for endpointID := range filteredFDBObservations.byEndpoint {
44 endpointID = strings.TrimSpace(endpointID)
45 if endpointID != "" {
46 set[endpointID] = struct{}{}
47 }
48 }
49 return sortedTopologySet(set)
50 }
51
52 func buildFDBEndpointReporterHints(macLinks []bridgeMacLinkRecord) map[string]map[string][]bridgePortRef {
53 if len(macLinks) == 0 {
54 return nil
55 }
56
57 byEndpointReporterPorts := make(map[string]map[string]map[string]bridgePortRef)
58 for _, link := range macLinks {
59 if strings.ToLower(strings.TrimSpace(link.method)) != "fdb" {
60 continue
61 }
62 endpointID := normalizeFDBEndpointID(link.endpointID)
63 reporterID := strings.TrimSpace(link.port.deviceID)
64 portKey := bridgePortObservationVLANKey(link.port)
65 if endpointID == "" || reporterID == "" || portKey == "" {
66 continue
67 }
68 reporters := byEndpointReporterPorts[endpointID]
69 if reporters == nil {
70 reporters = make(map[string]map[string]bridgePortRef)
71 byEndpointReporterPorts[endpointID] = reporters
72 }
73 ports := reporters[reporterID]
74 if ports == nil {
75 ports = make(map[string]bridgePortRef)
76 reporters[reporterID] = ports
77 }
78 ports[portKey] = link.port
79 }
80
81 out := make(map[string]map[string][]bridgePortRef, len(byEndpointReporterPorts))
82 for endpointID, reporters := range byEndpointReporterPorts {
83 reporterHints := make(map[string][]bridgePortRef, len(reporters))
84 reporterIDs := make([]string, 0, len(reporters))
85 for reporterID := range reporters {
86 reporterIDs = append(reporterIDs, reporterID)
87 }
88 sort.Strings(reporterIDs)
89 for _, reporterID := range reporterIDs {
90 portsMap := reporters[reporterID]
91 if len(portsMap) == 0 {
92 continue
93 }
94 portKeys := make([]string, 0, len(portsMap))
95 for key := range portsMap {
96 portKeys = append(portKeys, key)
97 }
98 sort.Strings(portKeys)
99 ports := make([]bridgePortRef, 0, len(portKeys))
100 for _, key := range portKeys {
101 ports = append(ports, portsMap[key])
102 }
103 reporterHints[reporterID] = ports
104 }
105 if len(reporterHints) > 0 {
106 out[endpointID] = reporterHints
107 }
108 }
109 if len(out) == 0 {
110 return nil
111 }
112 return out
113 }
114
115 func buildSegmentReporterIndex(
116 segmentIDs []string,
117 segmentByID map[string]*bridgeDomainSegment,
118 ) segmentReporterIndex {
119 index := segmentReporterIndex{
120 byDevice: make(map[string]map[string]struct{}),
121 byDeviceIfIndex: make(map[string]map[string]struct{}),
122 byDeviceIfName: make(map[string]map[string]struct{}),
123 }
124 for _, segmentID := range segmentIDs {
125 segment := segmentByID[segmentID]
126 if segment == nil {
127 continue
128 }
129 for _, port := range segment.ports {
130 deviceID := strings.TrimSpace(port.deviceID)
131 if deviceID == "" {
132 continue
133 }
134 addStringSet(index.byDevice, deviceID, segmentID)
135 if port.ifIndex > 0 {
136 addStringSet(index.byDeviceIfIndex, deviceID+keySep+strconv.Itoa(port.ifIndex), segmentID)
137 }
138 if ifName := strings.ToLower(strings.TrimSpace(port.ifName)); ifName != "" {
139 addStringSet(index.byDeviceIfName, deviceID+keySep+ifName, segmentID)
140 }
141 }
142 }
143 return index
144 }
145
146 func addStringSet(out map[string]map[string]struct{}, key string, value string) {
147 key = strings.TrimSpace(key)
148 value = strings.TrimSpace(value)
149 if key == "" || value == "" {
150 return
151 }
152 set := out[key]
153 if set == nil {
154 set = make(map[string]struct{})
155 out[key] = set
156 }
157 set[value] = struct{}{}
158 }
159
160 func probableCandidateSegmentsFromReporterHints(
161 endpointLabels map[string]string,
162 fdbReporters map[string]map[string]struct{},
163 reporterSegmentIndex segmentReporterIndex,
164 aliasOwnerIDs map[string]map[string]struct{},
165 managedDeviceIDs map[string]struct{},
166 ) []string {
167 deviceIDs := resolveTopologyEndpointDeviceHints(
168 topologyEndpointLabelDeviceIDs(endpointLabels),
169 aliasOwnerIDs,
170 )
171 if len(deviceIDs) == 0 {
172 for reporterID := range fdbReporters {
173 reporterID = strings.TrimSpace(reporterID)
174 if reporterID == "" {
175 continue
176 }
177 deviceIDs = append(deviceIDs, reporterID)
178 }
179 deviceIDs = resolveTopologyEndpointDeviceHints(deviceIDs, aliasOwnerIDs)
180 }
181 deviceIDs = filterManagedDeviceHints(deviceIDs, managedDeviceIDs)
182 if len(deviceIDs) == 0 {
183 return nil
184 }
185
186 ifIndexes := labelsCSVToSlice(endpointLabels, "learned_if_indexes")
187 ifNames := labelsCSVToSlice(endpointLabels, "learned_if_names")
188 hasPortHints := false
189 for _, ifIndex := range ifIndexes {
190 if strings.TrimSpace(ifIndex) != "" {
191 hasPortHints = true
192 break
193 }
194 }
195 if !hasPortHints {
196 for _, ifName := range ifNames {
197 if strings.TrimSpace(ifName) != "" {
198 hasPortHints = true
199 break
200 }
201 }
202 }
203 candidateSet := make(map[string]struct{})
204
205 for _, deviceID := range deviceIDs {
206 for _, ifIndex := range ifIndexes {
207 ifIndex = strings.TrimSpace(ifIndex)
208 if ifIndex == "" {
209 continue
210 }
211 for segmentID := range reporterSegmentIndex.byDeviceIfIndex[deviceID+keySep+ifIndex] {
212 candidateSet[segmentID] = struct{}{}
213 }
214 }
215 for _, ifName := range ifNames {
216 ifName = strings.ToLower(strings.TrimSpace(ifName))
217 if ifName == "" {
218 continue
219 }
220 for segmentID := range reporterSegmentIndex.byDeviceIfName[deviceID+keySep+ifName] {
221 candidateSet[segmentID] = struct{}{}
222 }
223 }
224 }
225
226 if len(candidateSet) == 0 && !hasPortHints {
227 for _, deviceID := range deviceIDs {
228 for segmentID := range reporterSegmentIndex.byDevice[deviceID] {
229 candidateSet[segmentID] = struct{}{}
230 }
231 }
232 }
233 if len(candidateSet) == 0 {
234 return nil
235 }
236 return sortedTopologySet(candidateSet)
237 }