|
|
@@ -157,49 +157,62 @@ func (b *Builder) Build() models.TopologyGraph {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 策略3b: 通过MAC前缀匹配(改进:排除歧义前缀)
|
|
|
- // 当精确MAC匹配失败时,尝试通过MAC前缀匹配
|
|
|
- // 但如果同一前缀匹配到多台设备,则跳过(避免错误连接)
|
|
|
- if targetIP == "" && neighbor.RemoteMAC != "" {
|
|
|
+ // 策略3b: MAC前缀匹配已禁用 - 同品牌设备MAC前缀相同,极易产生误匹配
|
|
|
+ // 如果需要连接,请使用精确MAC匹配或确保邻居设备在设备列表中
|
|
|
+ if false && targetIP == "" && neighbor.RemoteMAC != "" {
|
|
|
neighborMACPrefix := getMACPrefix(neighbor.RemoteMAC)
|
|
|
- fmt.Printf(" Trying MAC prefix match: %s (prefix: %s)\n", neighbor.RemoteMAC, neighborMACPrefix)
|
|
|
+ fmt.Printf(" ⚠ MAC prefix match disabled (too unreliable): %s (prefix: %s)\n", neighbor.RemoteMAC, neighborMACPrefix)
|
|
|
+ }
|
|
|
|
|
|
- // 先统计有多少台设备匹配此MAC前缀
|
|
|
- type prefixMatch struct {
|
|
|
- ip string
|
|
|
- matchingMACs int
|
|
|
- }
|
|
|
- var matches []prefixMatch
|
|
|
+ // 策略3c: LLDP对称性匹配(改进 - 需要双向验证)
|
|
|
+ // 设备A的接口X连接到设备B的接口Y
|
|
|
+ // 同时设备B的接口Y也应该连接到设备A的接口X
|
|
|
+ if targetIP == "" && neighbor.RemoteMAC != "" && neighbor.RemoteInterface != "" {
|
|
|
+ fmt.Printf(" Trying LLDP symmetric match: looking for device with interface %s\n", neighbor.RemoteInterface)
|
|
|
|
|
|
for _, d := range b.devices {
|
|
|
if d.IP == device.IP {
|
|
|
continue // 跳过自己
|
|
|
}
|
|
|
|
|
|
- matchingMACs := 0
|
|
|
- for _, mac := range d.MACAddresses {
|
|
|
- if getMACPrefix(mac) == neighborMACPrefix {
|
|
|
- matchingMACs++
|
|
|
+ // 检查设备d是否有这个接口
|
|
|
+ hasInterface := false
|
|
|
+ for _, iface := range d.Interfaces {
|
|
|
+ if iface.Name == neighbor.RemoteInterface {
|
|
|
+ hasInterface = true
|
|
|
+ break
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if matchingMACs >= 3 {
|
|
|
- matches = append(matches, prefixMatch{ip: d.IP, matchingMACs: matchingMACs})
|
|
|
+ if !hasInterface {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // 双向验证:检查设备d的邻居信息中,这个接口是否连接回当前设备
|
|
|
+ hasReverseLink := false
|
|
|
+ for _, dNeighbor := range d.Neighbors {
|
|
|
+ if dNeighbor.RemoteInterface == neighbor.LocalInterface && dNeighbor.LocalInterface == neighbor.RemoteInterface {
|
|
|
+ hasReverseLink = true
|
|
|
+ break
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // 只在唯一匹配时使用前缀匹配
|
|
|
- if len(matches) == 1 && getSubnet(matches[0].ip) == getSubnet(device.IP) {
|
|
|
- targetIP = matches[0].ip
|
|
|
- matchMethod = fmt.Sprintf("MAC-prefix(%s)", neighborMACPrefix)
|
|
|
- fmt.Printf(" ✓ Matched by MAC prefix: %s (device has %d MACs with prefix %s, same subnet) -> %s\n",
|
|
|
- neighbor.RemoteMAC, matches[0].matchingMACs, neighborMACPrefix, targetIP)
|
|
|
- } else if len(matches) > 1 {
|
|
|
- fmt.Printf(" ✗ Skipping MAC prefix match: %d devices share prefix %s (ambiguous)\n", len(matches), neighborMACPrefix)
|
|
|
+ if hasReverseLink {
|
|
|
+ // 双向验证通过!
|
|
|
+ targetIP = d.IP
|
|
|
+ matchMethod = fmt.Sprintf("LLDP-symmetric(%s<->%s)", neighbor.LocalInterface, neighbor.RemoteInterface)
|
|
|
+ fmt.Printf(" ✓ Matched by LLDP symmetric (bidirectional): %s %s <-> %s %s -> %s\n",
|
|
|
+ device.IP, neighbor.LocalInterface, d.IP, neighbor.RemoteInterface, targetIP)
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+ fmt.Printf(" ✗ Device %s has interface %s but no reverse link to %s %s\n",
|
|
|
+ d.IP, neighbor.RemoteInterface, device.IP, neighbor.LocalInterface)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 策略4: 通过本地接口IP网段匹配(新增)
|
|
|
+ // 注意:此策略容易产生误匹配,仅在必要时使用
|
|
|
if targetIP == "" {
|
|
|
// 查找本地接口的IP地址
|
|
|
localInterfaceIP := ""
|
|
|
@@ -211,7 +224,7 @@ func (b *Builder) Build() models.TopologyGraph {
|
|
|
}
|
|
|
|
|
|
if localInterfaceIP != "" {
|
|
|
- fmt.Printf(" Trying subnet match: local interface %s has IP %s\n",
|
|
|
+ fmt.Printf(" ⚠ Trying subnet match (risky): local interface %s has IP %s\n",
|
|
|
neighbor.LocalInterface, localInterfaceIP)
|
|
|
|
|
|
// 计算本地接口的网段
|
|
|
@@ -227,7 +240,7 @@ func (b *Builder) Build() models.TopologyGraph {
|
|
|
if getSubnet(d.IP) == localSubnet {
|
|
|
targetIP = d.IP
|
|
|
matchMethod = "subnet(management IP)"
|
|
|
- fmt.Printf(" ✓ Matched by subnet: %s in %s\n", d.IP, localSubnet)
|
|
|
+ fmt.Printf(" ⚠ Matched by subnet (risky): %s in %s\n", d.IP, localSubnet)
|
|
|
break
|
|
|
}
|
|
|
|
|
|
@@ -236,7 +249,7 @@ func (b *Builder) Build() models.TopologyGraph {
|
|
|
if iface.IP != "" && getSubnet(iface.IP) == localSubnet {
|
|
|
targetIP = d.IP
|
|
|
matchMethod = fmt.Sprintf("subnet(interface %s)", iface.Name)
|
|
|
- fmt.Printf(" ✓ Matched by subnet: %s (%s) in %s\n",
|
|
|
+ fmt.Printf(" ⚠ Matched by subnet (risky): %s (%s) in %s\n",
|
|
|
d.IP, iface.Name, localSubnet)
|
|
|
break
|
|
|
}
|
|
|
@@ -246,27 +259,9 @@ func (b *Builder) Build() models.TopologyGraph {
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
- // 策略4b: 本地接口没有IP,尝试使用设备管理IP进行子网匹配(新增)
|
|
|
- // 注意:只有当该网段只有2台设备时才使用此策略(点对点连接)
|
|
|
- fmt.Printf(" Trying subnet match with management IP: %s\n", device.IP)
|
|
|
- localSubnet := getSubnet(device.IP)
|
|
|
-
|
|
|
- // 统计在该网段的设备数量
|
|
|
- var devicesInSubnet []string
|
|
|
- for _, d := range b.devices {
|
|
|
- if d.IP != device.IP && getSubnet(d.IP) == localSubnet {
|
|
|
- devicesInSubnet = append(devicesInSubnet, d.IP)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 只有当网段中恰好有1台其他设备时才匹配(点对点)
|
|
|
- if len(devicesInSubnet) == 1 {
|
|
|
- targetIP = devicesInSubnet[0]
|
|
|
- matchMethod = "subnet(management IP both sides)"
|
|
|
- fmt.Printf(" ✓ Matched by management subnet: %s in %s (only device in subnet)\n", targetIP, localSubnet)
|
|
|
- } else if len(devicesInSubnet) > 1 {
|
|
|
- fmt.Printf(" ✗ Skipping subnet match: %d devices in subnet %s (ambiguous)\n", len(devicesInSubnet)+1, localSubnet)
|
|
|
- }
|
|
|
+ // 策略4b: 本地接口没有IP,尝试使用设备管理IP进行子网匹配(高风险策略 - 已禁用)
|
|
|
+ // 此策略容易产生错误连接,暂时禁用
|
|
|
+ fmt.Printf(" ✗ Skipping subnet match with management IP (disabled - too risky)\n")
|
|
|
}
|
|
|
}
|
|
|
|