master
go 203 lines 4.74 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package l2topology
4
5 import (
6 "sort"
7 "strings"
8 "time"
9 )
10
11 func projectSegmentTopology(
12 attachments []Attachment,
13 adjacencies []Adjacency,
14 layer string,
15 source string,
16 collectedAt time.Time,
17 deviceByID map[string]Device,
18 ifaceByDeviceIndex map[string]Interface,
19 ifIndexByDeviceName map[string]int,
20 bridgeLinks []bridgeBridgeLinkRecord,
21 reporterAliases map[string][]string,
22 endpointMatchByID map[string]Match,
23 endpointLabelsByID map[string]map[string]string,
24 actorIndex map[string]struct{},
25 probabilisticConnectivity bool,
26 strategyConfig topologyInferenceStrategyConfig,
27 ) projectedSegments {
28 return newSegmentProjectionBuilder(
29 attachments,
30 adjacencies,
31 layer,
32 source,
33 collectedAt,
34 deviceByID,
35 ifaceByDeviceIndex,
36 ifIndexByDeviceName,
37 bridgeLinks,
38 reporterAliases,
39 endpointMatchByID,
40 endpointLabelsByID,
41 actorIndex,
42 probabilisticConnectivity,
43 strategyConfig,
44 ).build()
45 }
46
47 func pickProbableSegmentAnchorPortID(
48 segment *bridgeDomainSegment,
49 probableEndpoints map[string]struct{},
50 fdbOwners map[string]fdbEndpointOwner,
51 managedDeviceIDs map[string]struct{},
52 ) string {
53 if segment == nil || len(segment.ports) == 0 {
54 return ""
55 }
56
57 portIDs := make([]string, 0, len(segment.ports))
58 portIDByObservation := make(map[string]string, len(segment.ports)*2)
59 designatedPortID := ""
60 managedPortIDs := make(map[string]struct{})
61 for portID, port := range segment.ports {
62 portIDs = append(portIDs, portID)
63 if key := bridgePortObservationKey(port); key != "" {
64 portIDByObservation[key] = portID
65 }
66 if key := bridgePortObservationVLANKey(port); key != "" {
67 portIDByObservation[key] = portID
68 }
69 if segment.portIdentityKey(port) == segment.portIdentityKey(segment.designatedPort) {
70 designatedPortID = portID
71 }
72 if len(managedDeviceIDs) == 0 {
73 managedPortIDs[portID] = struct{}{}
74 continue
75 }
76 if _, ok := managedDeviceIDs[strings.TrimSpace(port.deviceID)]; ok {
77 managedPortIDs[portID] = struct{}{}
78 }
79 }
80 sort.Strings(portIDs)
81 preferManaged := len(managedPortIDs) > 0
82 allowPortID := func(portID string) bool {
83 if !preferManaged {
84 return true
85 }
86 _, ok := managedPortIDs[portID]
87 return ok
88 }
89
90 endpointIDs := sortedTopologySet(probableEndpoints)
91 for _, endpointID := range endpointIDs {
92 owner, ok := fdbOwners[endpointID]
93 if !ok {
94 continue
95 }
96 if portID, ok := portIDByObservation[owner.portVLANKey]; ok {
97 if allowPortID(portID) {
98 return portID
99 }
100 }
101 if portID, ok := portIDByObservation[owner.portKey]; ok {
102 if allowPortID(portID) {
103 return portID
104 }
105 }
106 }
107
108 if designatedPortID != "" && allowPortID(designatedPortID) {
109 return designatedPortID
110 }
111 if preferManaged {
112 managedPortIDList := make([]string, 0, len(managedPortIDs))
113 for portID := range managedPortIDs {
114 managedPortIDList = append(managedPortIDList, portID)
115 }
116 sort.Strings(managedPortIDList)
117 if len(managedPortIDList) > 0 {
118 return managedPortIDList[0]
119 }
120 }
121 if designatedPortID != "" {
122 return designatedPortID
123 }
124 return portIDs[0]
125 }
126
127 func segmentHasManagedPort(segment *bridgeDomainSegment, managedDeviceIDs map[string]struct{}) bool {
128 if segment == nil || len(segment.ports) == 0 {
129 return false
130 }
131 if len(managedDeviceIDs) == 0 {
132 return true
133 }
134 for _, port := range segment.ports {
135 if _, ok := managedDeviceIDs[strings.TrimSpace(port.deviceID)]; ok {
136 return true
137 }
138 }
139 return false
140 }
141
142 func pickMostProbableSegment(
143 candidates []string,
144 endpointLabels map[string]string,
145 segmentIfIndexes map[string]map[string]struct{},
146 segmentIfNames map[string]map[string]struct{},
147 ) string {
148 if len(candidates) == 0 {
149 return ""
150 }
151 if len(candidates) == 1 {
152 return candidates[0]
153 }
154
155 ifIndexes := make(map[string]struct{})
156 for _, ifIndex := range labelsCSVToSlice(endpointLabels, "learned_if_indexes") {
157 ifIndex = strings.TrimSpace(ifIndex)
158 if ifIndex == "" {
159 continue
160 }
161 ifIndexes[ifIndex] = struct{}{}
162 }
163 ifNames := make(map[string]struct{})
164 for _, ifName := range labelsCSVToSlice(endpointLabels, "learned_if_names") {
165 ifName = strings.ToLower(strings.TrimSpace(ifName))
166 if ifName == "" {
167 continue
168 }
169 ifNames[ifName] = struct{}{}
170 }
171
172 bestID := ""
173 bestScore := -1
174 for _, segmentID := range candidates {
175 score := 0
176 for ifIndex := range ifIndexes {
177 if indexes := segmentIfIndexes[segmentID]; indexes != nil {
178 if _, ok := indexes[ifIndex]; ok {
179 score += 2
180 }
181 }
182 }
183 for ifName := range ifNames {
184 if names := segmentIfNames[segmentID]; names != nil {
185 if _, ok := names[ifName]; ok {
186 score++
187 }
188 }
189 }
190 if score > bestScore {
191 bestScore = score
192 bestID = segmentID
193 continue
194 }
195 if score == bestScore && (bestID == "" || segmentID < bestID) {
196 bestID = segmentID
197 }
198 }
199 if bestID != "" {
200 return bestID
201 }
202 return candidates[0]
203 }