master
go 143 lines 5.18 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 (b *segmentProjectionBuilder) initializeSegments() bool {
12 // Hard deterministic rule: LLDP/CDP-adjacent ports are transit ports.
13 // FDB data learned on those ports belongs to the neighbor domain and must
14 // not create parallel inferred segment paths on top of direct discovery.
15 //
16 // NOTE:
17 // switch-facing/trunk classification must not suppress endpoint ownership.
18 // It is a topology correlation/confidence signal, not a hard endpoint
19 // placement filter.
20 deterministicTransitPortKeys := buildDeterministicTransitPortKeySet(b.adjacencies, b.ifIndexByDeviceName)
21 seedMacLinks := collectBridgeMacLinkRecords(b.attachments, b.ifaceByDeviceIndex, deterministicTransitPortKeys)
22 b.rawFDBObservations = buildFDBReporterObservations(seedMacLinks)
23 model := buildBridgeDomainModel(b.bridgeLinks, seedMacLinks)
24 if len(model.domains) == 0 {
25 return false
26 }
27
28 b.segmentMatchByID = make(map[string]Match)
29 b.segmentByID = make(map[string]*bridgeDomainSegment)
30 for _, domain := range model.domains {
31 if domain == nil {
32 continue
33 }
34 for _, segment := range domain.segments {
35 if segment == nil || len(segment.endpointIDs) == 0 {
36 continue
37 }
38 segmentID := bridgeDomainSegmentID(segment)
39 if _, exists := b.segmentByID[segmentID]; exists {
40 continue
41 }
42 b.segmentByID[segmentID] = segment
43 b.segmentIDs = append(b.segmentIDs, segmentID)
44 }
45 }
46 sort.Strings(b.segmentIDs)
47 if len(b.segmentIDs) == 0 {
48 return false
49 }
50
51 for _, segmentID := range b.segmentIDs {
52 segment := b.segmentByID[segmentID]
53 if segment == nil {
54 continue
55 }
56 match, actor := buildBridgeSegmentActor(segmentID, segment, b.layer, b.source)
57 keys := topologyMatchIdentityKeys(actor.Match)
58 if len(keys) > 0 && !topologyIdentityIndexOverlaps(b.actorIndex, keys) {
59 addTopologyIdentityKeys(b.actorIndex, keys)
60 }
61 b.out.actors = append(b.out.actors, actor)
62 b.segmentMatchByID[segmentID] = match
63 }
64
65 b.deviceSegmentEdgeSeen = make(map[string]struct{})
66 b.endpointSegmentEdgeSeen = make(map[string]struct{})
67 b.endpointSegmentCandidates = make(map[string][]string)
68 b.segmentPortKeys = make(map[string]map[string]struct{}, len(b.segmentIDs))
69 b.segmentIfIndexes = make(map[string]map[string]struct{}, len(b.segmentIDs))
70 b.segmentIfNames = make(map[string]map[string]struct{}, len(b.segmentIDs))
71 for _, segmentID := range b.segmentIDs {
72 segment := b.segmentByID[segmentID]
73 if segment == nil {
74 continue
75 }
76 portKeys := make(map[string]struct{}, len(segment.ports))
77 ifIndexes := make(map[string]struct{}, len(segment.ports))
78 ifNames := make(map[string]struct{}, len(segment.ports))
79 for _, port := range segment.ports {
80 if portKey := bridgePortObservationKey(port); portKey != "" {
81 portKeys[portKey] = struct{}{}
82 }
83 if portVLANKey := bridgePortObservationVLANKey(port); portVLANKey != "" {
84 portKeys[portVLANKey] = struct{}{}
85 }
86 if port.ifIndex > 0 {
87 ifIndexes[strconv.Itoa(port.ifIndex)] = struct{}{}
88 }
89 if ifName := strings.TrimSpace(port.ifName); ifName != "" {
90 ifNames[strings.ToLower(ifName)] = struct{}{}
91 }
92 }
93 b.segmentPortKeys[segmentID] = portKeys
94 b.segmentIfIndexes[segmentID] = ifIndexes
95 b.segmentIfNames[segmentID] = ifNames
96 for endpointID := range segment.endpointIDs {
97 endpointID = strings.TrimSpace(endpointID)
98 if endpointID == "" {
99 continue
100 }
101 b.endpointSegmentCandidates[endpointID] = append(b.endpointSegmentCandidates[endpointID], segmentID)
102 }
103 }
104
105 b.rawFDBReporterHints = buildFDBEndpointReporterHints(seedMacLinks)
106 b.fdbObservations = buildFDBReporterObservations(seedMacLinks)
107 b.fdbOwners = inferFDBEndpointOwners(b.fdbObservations, b.reporterAliases, deterministicTransitPortKeys)
108 for endpointID, owner := range inferSinglePortEndpointOwners(seedMacLinks, deterministicTransitPortKeys) {
109 if strings.TrimSpace(endpointID) == "" {
110 continue
111 }
112 if b.fdbOwners == nil {
113 b.fdbOwners = make(map[string]fdbEndpointOwner)
114 }
115 // Port-centric ownership has precedence: if a port carries a single learned
116 // MAC in the same snapshot/VLAN scope, use it to resolve ambiguous placement.
117 b.fdbOwners[endpointID] = owner
118 if b.out.endpointDirectOwners == nil {
119 b.out.endpointDirectOwners = make(map[string]fdbEndpointOwner)
120 }
121 b.out.endpointDirectOwners[endpointID] = owner
122 }
123
124 b.deviceIdentityByID = buildDeviceIdentityKeySetByID(b.deviceByID, b.adjacencies, b.ifaceByDeviceIndex)
125 b.reporterSegmentIndex = buildSegmentReporterIndex(b.segmentIDs, b.segmentByID)
126 b.aliasOwnerIDs = buildFDBAliasOwnerMap(b.reporterAliases)
127 b.managedDeviceIDs = make(map[string]struct{}, len(b.deviceByID))
128 for deviceID := range b.deviceByID {
129 deviceID = strings.TrimSpace(deviceID)
130 if deviceID == "" {
131 continue
132 }
133 b.managedDeviceIDs[deviceID] = struct{}{}
134 }
135 b.managedDeviceIDList = sortedTopologySet(b.managedDeviceIDs)
136 b.allowedEndpointBySegment = make(map[string]map[string]struct{})
137 b.strictEndpointBySegment = make(map[string]map[string]struct{})
138 b.probableEndpointBySegment = make(map[string]map[string]struct{})
139 b.probableAttachmentModes = make(map[string]map[string]string)
140 b.assignedEndpoints = make(map[string]struct{}, len(b.endpointMatchByID))
141
142 return true
143 }