master
go 202 lines 4.43 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 const cdpMatchPassDefault = "default"
11
12 type cdpMatchLink struct {
13 index int
14
15 sourceDeviceID string
16 sourceGlobalID string
17
18 localInterfaceName string
19
20 remoteDeviceID string
21 remoteDevicePort string
22 remoteHost string
23 remoteAddressRaw string
24 }
25
26 type cdpMatchedPair struct {
27 sourceIndex int
28 targetIndex int
29 pass string
30 }
31
32 func buildCDPMatchLinks(observations []L2Observation) []cdpMatchLink {
33 links := make([]cdpMatchLink, 0)
34 for _, obs := range observations {
35 sourceID := strings.TrimSpace(obs.DeviceID)
36 if sourceID == "" {
37 continue
38 }
39
40 sourceGlobalID := strings.TrimSpace(obs.Hostname)
41 if sourceGlobalID == "" {
42 sourceGlobalID = sourceID
43 }
44
45 remotes := sortedCDPRemotes(obs.CDPRemotes)
46 for _, remote := range remotes {
47 localInterfaceName := strings.TrimSpace(remote.LocalIfName)
48 if localInterfaceName == "" && remote.LocalIfIndex > 0 {
49 localInterfaceName = strconv.Itoa(remote.LocalIfIndex)
50 }
51
52 remoteDeviceID := strings.TrimSpace(remote.DeviceID)
53 remoteHost := strings.TrimSpace(remote.SysName)
54 if remoteHost == "" {
55 remoteHost = remoteDeviceID
56 }
57 if remoteDeviceID == "" {
58 remoteDeviceID = remoteHost
59 }
60
61 links = append(links, cdpMatchLink{
62 index: len(links),
63 sourceDeviceID: sourceID,
64 sourceGlobalID: sourceGlobalID,
65 localInterfaceName: localInterfaceName,
66 remoteDeviceID: remoteDeviceID,
67 remoteDevicePort: strings.TrimSpace(remote.DevicePort),
68 remoteHost: remoteHost,
69 remoteAddressRaw: strings.TrimSpace(remote.Address),
70 })
71 }
72 }
73 return links
74 }
75
76 func buildCDPLookupMap(links []cdpMatchLink) map[string]int {
77 lookup := make(map[string]int, len(links))
78 for _, link := range links {
79 key := topologyMatchCompositeKey(
80 link.remoteDevicePort,
81 link.localInterfaceName,
82 link.sourceGlobalID,
83 link.remoteDeviceID,
84 )
85 if _, ok := lookup[key]; ok {
86 continue
87 }
88 lookup[key] = link.index
89 }
90 return lookup
91 }
92
93 func matchCDPLinksEnlinkdPassOrder(links []cdpMatchLink) []cdpMatchedPair {
94 if len(links) == 0 {
95 return nil
96 }
97
98 lookup := buildCDPLookupMap(links)
99 parsed := make(map[int]struct{}, len(links))
100 pairs := make([]cdpMatchedPair, 0, len(links)/2)
101
102 for _, source := range links {
103 if _, ok := parsed[source.index]; ok {
104 continue
105 }
106
107 key := topologyMatchCompositeKey(
108 source.localInterfaceName,
109 source.remoteDevicePort,
110 source.remoteDeviceID,
111 source.sourceGlobalID,
112 )
113 targetIndex, ok := lookup[key]
114 if !ok {
115 continue
116 }
117
118 if source.index == targetIndex {
119 continue
120 }
121 if _, targetParsed := parsed[targetIndex]; targetParsed {
122 continue
123 }
124
125 parsed[source.index] = struct{}{}
126 parsed[targetIndex] = struct{}{}
127 pairs = append(pairs, cdpMatchedPair{
128 sourceIndex: source.index,
129 targetIndex: targetIndex,
130 pass: cdpMatchPassDefault,
131 })
132 }
133
134 return pairs
135 }
136
137 func buildCDPTargetOverrides(links []cdpMatchLink, pairs []cdpMatchedPair) map[int]string {
138 if len(pairs) == 0 {
139 return nil
140 }
141
142 indexToLink := make(map[int]cdpMatchLink, len(links))
143 for _, link := range links {
144 indexToLink[link.index] = link
145 }
146
147 overrides := make(map[int]string, len(pairs)*2)
148 for _, pair := range pairs {
149 source, sourceOK := indexToLink[pair.sourceIndex]
150 target, targetOK := indexToLink[pair.targetIndex]
151 if !sourceOK || !targetOK {
152 continue
153 }
154
155 overrides[source.index] = target.sourceDeviceID
156 overrides[target.index] = source.sourceDeviceID
157 }
158
159 return overrides
160 }
161
162 func buildCDPPairMetadata(links []cdpMatchLink, pairs []cdpMatchedPair) map[int]matchedPairMetadata {
163 if len(pairs) == 0 {
164 return nil
165 }
166
167 indexToLink := make(map[int]cdpMatchLink, len(links))
168 for _, link := range links {
169 indexToLink[link.index] = link
170 }
171
172 metadata := make(map[int]matchedPairMetadata, len(pairs)*2)
173 for _, pair := range pairs {
174 sourceLink, sourceOK := indexToLink[pair.sourceIndex]
175 targetLink, targetOK := indexToLink[pair.targetIndex]
176 if !sourceOK || !targetOK {
177 continue
178 }
179
180 pairID := canonicalAdjacencyPairID(
181 "cdp",
182 sourceLink.sourceDeviceID,
183 sourceLink.localInterfaceName,
184 targetLink.sourceDeviceID,
185 targetLink.localInterfaceName,
186 )
187 if pairID == "" {
188 continue
189 }
190
191 metadata[sourceLink.index] = matchedPairMetadata{
192 id: pairID,
193 pass: pair.pass,
194 }
195 metadata[targetLink.index] = matchedPairMetadata{
196 id: pairID,
197 pass: pair.pass,
198 }
199 }
200
201 return metadata
202 }