master
go 278 lines 6.62 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 pruneSegmentArtifacts(actors []Actor, links []Link) ([]Actor, []Link, int) {
11 if len(actors) == 0 || len(links) == 0 {
12 return actors, links, 0
13 }
14
15 segmentKeys := make(map[string]struct{})
16 segmentOrder := make([]string, 0)
17 for _, actor := range actors {
18 if !strings.EqualFold(strings.TrimSpace(actor.ActorType), "segment") {
19 continue
20 }
21 key := canonicalTopologyMatchKey(actor.Match)
22 if key == "" {
23 continue
24 }
25 if _, seen := segmentKeys[key]; seen {
26 continue
27 }
28 segmentKeys[key] = struct{}{}
29 segmentOrder = append(segmentOrder, key)
30 }
31 if len(segmentKeys) == 0 {
32 return actors, links, 0
33 }
34 sort.Strings(segmentOrder)
35
36 discoveryPairs := make(map[string]struct{})
37 for _, link := range links {
38 protocol := strings.ToLower(strings.TrimSpace(link.Protocol))
39 if protocol != "lldp" && protocol != "cdp" {
40 continue
41 }
42 src := canonicalTopologyMatchKey(link.Src.Match)
43 dst := canonicalTopologyMatchKey(link.Dst.Match)
44 if src == "" || dst == "" {
45 continue
46 }
47 if _, srcSegment := segmentKeys[src]; srcSegment {
48 continue
49 }
50 if _, dstSegment := segmentKeys[dst]; dstSegment {
51 continue
52 }
53 if pair := topologyUndirectedPairKey(src, dst); pair != "" {
54 discoveryPairs[pair] = struct{}{}
55 }
56 }
57
58 suppressed := make(map[string]struct{})
59 for {
60 changed := false
61 neighborsBySegment := make(map[string]map[string]struct{})
62 for _, link := range links {
63 src := canonicalTopologyMatchKey(link.Src.Match)
64 dst := canonicalTopologyMatchKey(link.Dst.Match)
65 if src == "" || dst == "" {
66 continue
67 }
68 if _, srcSuppressed := suppressed[src]; srcSuppressed {
69 continue
70 }
71 if _, dstSuppressed := suppressed[dst]; dstSuppressed {
72 continue
73 }
74
75 _, srcSegment := segmentKeys[src]
76 _, dstSegment := segmentKeys[dst]
77
78 if srcSegment && !dstSegment {
79 neighbors := neighborsBySegment[src]
80 if neighbors == nil {
81 neighbors = make(map[string]struct{})
82 neighborsBySegment[src] = neighbors
83 }
84 neighbors[dst] = struct{}{}
85 }
86 if dstSegment && !srcSegment {
87 neighbors := neighborsBySegment[dst]
88 if neighbors == nil {
89 neighbors = make(map[string]struct{})
90 neighborsBySegment[dst] = neighbors
91 }
92 neighbors[src] = struct{}{}
93 }
94 }
95
96 for _, segmentKey := range segmentOrder {
97 if _, alreadySuppressed := suppressed[segmentKey]; alreadySuppressed {
98 continue
99 }
100
101 neighbors := neighborsBySegment[segmentKey]
102 if len(neighbors) < 2 {
103 suppressed[segmentKey] = struct{}{}
104 changed = true
105 continue
106 }
107
108 if len(neighbors) == 2 {
109 pairValues := make([]string, 0, 2)
110 for neighbor := range neighbors {
111 pairValues = append(pairValues, neighbor)
112 }
113 if len(pairValues) == 2 {
114 if pair := topologyUndirectedPairKey(pairValues[0], pairValues[1]); pair != "" {
115 if _, found := discoveryPairs[pair]; found {
116 suppressed[segmentKey] = struct{}{}
117 changed = true
118 }
119 }
120 }
121 }
122 }
123
124 if !changed {
125 break
126 }
127 }
128
129 if len(suppressed) == 0 {
130 return actors, links, 0
131 }
132
133 filteredActors := make([]Actor, 0, len(actors))
134 for _, actor := range actors {
135 key := canonicalTopologyMatchKey(actor.Match)
136 if key == "" {
137 filteredActors = append(filteredActors, actor)
138 continue
139 }
140 if _, isSuppressed := suppressed[key]; isSuppressed && strings.EqualFold(strings.TrimSpace(actor.ActorType), "segment") {
141 continue
142 }
143 filteredActors = append(filteredActors, actor)
144 }
145
146 filteredLinks := make([]Link, 0, len(links))
147 for _, link := range links {
148 src := canonicalTopologyMatchKey(link.Src.Match)
149 dst := canonicalTopologyMatchKey(link.Dst.Match)
150 if src != "" {
151 if _, srcSuppressed := suppressed[src]; srcSuppressed {
152 continue
153 }
154 }
155 if dst != "" {
156 if _, dstSuppressed := suppressed[dst]; dstSuppressed {
157 continue
158 }
159 }
160 filteredLinks = append(filteredLinks, link)
161 }
162
163 return filteredActors, filteredLinks, len(suppressed)
164 }
165
166 func topologyUndirectedPairKey(left, right string) string {
167 left = strings.TrimSpace(left)
168 right = strings.TrimSpace(right)
169 if left == "" || right == "" {
170 return ""
171 }
172 if left <= right {
173 return left + keySep + right
174 }
175 return right + keySep + left
176 }
177
178 type topologyLinkCounts struct {
179 lldp int
180 cdp int
181 fdb int
182 arp int
183 bidirectional int
184 unidirectional int
185 }
186
187 func summarizeTopologyLinks(links []Link) topologyLinkCounts {
188 var counts topologyLinkCounts
189 for _, link := range links {
190 switch strings.ToLower(strings.TrimSpace(link.Protocol)) {
191 case "lldp":
192 counts.lldp++
193 case "cdp":
194 counts.cdp++
195 case "bridge", "fdb":
196 counts.fdb++
197 case "arp":
198 counts.arp++
199 }
200
201 switch strings.ToLower(strings.TrimSpace(link.Direction)) {
202 case "bidirectional":
203 counts.bidirectional++
204 case "unidirectional":
205 counts.unidirectional++
206 }
207 }
208 return counts
209 }
210
211 func pruneManagedOverlapUnlinkedEndpointActors(
212 actors []Actor,
213 links []Link,
214 suppressedEndpointIDs map[string]struct{},
215 ) ([]Actor, int) {
216 if len(actors) == 0 || len(suppressedEndpointIDs) == 0 {
217 return actors, 0
218 }
219
220 suppressedIdentityKeys := make(map[string]struct{})
221 for endpointID := range suppressedEndpointIDs {
222 endpointID = normalizeFDBEndpointID(endpointID)
223 if endpointID == "" {
224 continue
225 }
226 match := endpointMatchFromID(endpointID)
227 for _, key := range topologyMatchIdentityKeys(match) {
228 key = strings.TrimSpace(key)
229 if key == "" {
230 continue
231 }
232 suppressedIdentityKeys[key] = struct{}{}
233 }
234 }
235 if len(suppressedIdentityKeys) == 0 {
236 return actors, 0
237 }
238
239 linkedIdentityKeys := make(map[string]struct{}, len(links)*2)
240 for _, link := range links {
241 for _, key := range topologyMatchIdentityKeys(link.Src.Match) {
242 key = strings.TrimSpace(key)
243 if key == "" {
244 continue
245 }
246 linkedIdentityKeys[key] = struct{}{}
247 }
248 for _, key := range topologyMatchIdentityKeys(link.Dst.Match) {
249 key = strings.TrimSpace(key)
250 if key == "" {
251 continue
252 }
253 linkedIdentityKeys[key] = struct{}{}
254 }
255 }
256
257 filtered := make([]Actor, 0, len(actors))
258 suppressedCount := 0
259 for _, actor := range actors {
260 if !strings.EqualFold(strings.TrimSpace(actor.ActorType), "endpoint") {
261 filtered = append(filtered, actor)
262 continue
263 }
264
265 actorKeys := topologyMatchIdentityKeys(actor.Match)
266 if !topologyIdentityKeysOverlap(actorKeys, suppressedIdentityKeys) {
267 filtered = append(filtered, actor)
268 continue
269 }
270 // Keep endpoint actors that still participate in at least one emitted link.
271 if topologyIdentityKeysOverlap(actorKeys, linkedIdentityKeys) {
272 filtered = append(filtered, actor)
273 continue
274 }
275 suppressedCount++
276 }
277 return filtered, suppressedCount
278 }