master
go 161 lines 4.29 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 collectBridgeLinkRecords(
11 adjacencies []Adjacency,
12 ifIndexByDeviceName map[string]int,
13 strategy topologyInferenceStrategyConfig,
14 ) []bridgeBridgeLinkRecord {
15 records := make([]bridgeBridgeLinkRecord, 0)
16 seen := make(map[string]struct{})
17
18 for _, adj := range adjacencies {
19 protocol := strings.ToLower(strings.TrimSpace(adj.Protocol))
20 if !strategy.acceptsBridgeProtocol(protocol) {
21 continue
22 }
23
24 src := bridgePortFromAdjacencySide(adj.SourceID, adj.SourcePort, ifIndexByDeviceName)
25 dst := bridgePortFromAdjacencySide(adj.TargetID, adj.TargetPort, ifIndexByDeviceName)
26 srcKey := bridgePortRefKey(src, false, false)
27 dstKey := bridgePortRefKey(dst, false, false)
28 if srcKey == "" || dstKey == "" {
29 continue
30 }
31
32 pairKey := bridgePairKey(src, dst)
33 if pairKey == "" {
34 continue
35 }
36 if _, ok := seen[pairKey]; ok {
37 continue
38 }
39 seen[pairKey] = struct{}{}
40
41 designated := src
42 other := dst
43 if protocol == "stp" && strategy.useSTPDesignatedParent {
44 designated = dst
45 other = src
46 if bridgePortRefKey(designated, false, false) == "" {
47 designated = src
48 other = dst
49 }
50 } else {
51 if bridgePortRefSortKey(src) > bridgePortRefSortKey(dst) {
52 designated = dst
53 other = src
54 }
55 }
56 records = append(records, bridgeBridgeLinkRecord{
57 port: other,
58 designatedPort: designated,
59 method: protocol,
60 })
61 }
62
63 sort.SliceStable(records, func(i, j int) bool {
64 li := portSortKey(records[i].designatedPort) + keySep + portSortKey(records[i].port)
65 lj := portSortKey(records[j].designatedPort) + keySep + portSortKey(records[j].port)
66 return li < lj
67 })
68 return records
69 }
70
71 func (s topologyInferenceStrategyConfig) acceptsBridgeProtocol(protocol string) bool {
72 switch strings.ToLower(strings.TrimSpace(protocol)) {
73 case "lldp":
74 return s.includeLLDPBridgeLinks
75 case "cdp":
76 return s.includeCDPBridgeLinks
77 case "stp":
78 return s.includeSTPBridgeLinks
79 default:
80 return false
81 }
82 }
83
84 func mergeBridgeLinkRecordSets(base, extra []bridgeBridgeLinkRecord) []bridgeBridgeLinkRecord {
85 if len(extra) == 0 {
86 return base
87 }
88 out := make([]bridgeBridgeLinkRecord, 0, len(base)+len(extra))
89 out = append(out, base...)
90 seen := make(map[string]struct{}, len(base)+len(extra))
91 for _, link := range out {
92 if key := bridgePairKey(link.designatedPort, link.port); key != "" {
93 seen[key] = struct{}{}
94 }
95 }
96 for _, link := range extra {
97 key := bridgePairKey(link.designatedPort, link.port)
98 if key == "" {
99 continue
100 }
101 if _, ok := seen[key]; ok {
102 continue
103 }
104 seen[key] = struct{}{}
105 out = append(out, link)
106 }
107 sort.SliceStable(out, func(i, j int) bool {
108 li := portSortKey(out[i].designatedPort) + keySep + portSortKey(out[i].port)
109 lj := portSortKey(out[j].designatedPort) + keySep + portSortKey(out[j].port)
110 return li < lj
111 })
112 return out
113 }
114
115 func collectBridgeMacLinkRecords(
116 attachments []Attachment,
117 ifaceByDeviceIndex map[string]Interface,
118 switchFacingPortKeys map[string]struct{},
119 ) []bridgeMacLinkRecord {
120 records := make([]bridgeMacLinkRecord, 0, len(attachments))
121 seen := make(map[string]struct{}, len(attachments))
122
123 attachmentsSorted := append([]Attachment(nil), attachments...)
124 sort.SliceStable(attachmentsSorted, func(i, j int) bool {
125 return bridgeAttachmentSortKey(attachmentsSorted[i]) < bridgeAttachmentSortKey(attachmentsSorted[j])
126 })
127
128 for _, attachment := range attachmentsSorted {
129 port := bridgePortFromAttachment(attachment, ifaceByDeviceIndex)
130 portKey := bridgePortRefKey(port, false, false)
131 endpointID := strings.TrimSpace(attachment.EndpointID)
132 if portKey == "" || endpointID == "" {
133 continue
134 }
135 method := strings.ToLower(strings.TrimSpace(attachment.Method))
136 if method == "" {
137 method = "fdb"
138 }
139 if method == "fdb" {
140 if _, isSwitchFacingPort := switchFacingPortKeys[bridgePortObservationKey(port)]; isSwitchFacingPort {
141 continue
142 }
143 if _, isSwitchFacingPort := switchFacingPortKeys[bridgePortObservationVLANKey(port)]; isSwitchFacingPort {
144 continue
145 }
146 }
147
148 key := portKey + keySep + endpointID + keySep + method
149 if _, ok := seen[key]; ok {
150 continue
151 }
152 seen[key] = struct{}{}
153 records = append(records, bridgeMacLinkRecord{
154 port: port,
155 endpointID: endpointID,
156 method: method,
157 })
158 }
159
160 return records
161 }