diff --git a/internal/topology/builder.go b/internal/topology/builder.go index 328b44a..f9bf910 100644 --- a/internal/topology/builder.go +++ b/internal/topology/builder.go @@ -157,18 +157,59 @@ func (b *Builder) Build() models.TopologyGraph { } } - // 策略3b: MAC前缀匹配已禁用 - 同品牌设备MAC前缀相同,极易产生误匹配 - // 如果需要连接,请使用精确MAC匹配或确保邻居设备在设备列表中 - if false && targetIP == "" && neighbor.RemoteMAC != "" { - neighborMACPrefix := getMACPrefix(neighbor.RemoteMAC) - fmt.Printf(" ⚠ MAC prefix match disabled (too unreliable): %s (prefix: %s)\n", neighbor.RemoteMAC, neighborMACPrefix) + // 策略3b: MAC前缀匹配(使用12字符前缀 - 更精确) + // 同品牌设备虽然4字符前缀相同,但12字符前缀通常不同 + if targetIP == "" && neighbor.RemoteMAC != "" { + // 使用12字符前缀(例如:642f-c7e0-03) + macPrefix := "" + if len(neighbor.RemoteMAC) >= 12 { + macPrefix = neighbor.RemoteMAC[:12] + } else { + macPrefix = neighbor.RemoteMAC + } + + fmt.Printf(" Trying MAC prefix match (12 chars): %s (prefix: %s)\n", neighbor.RemoteMAC, macPrefix) + + var matchedDevices []string + + for _, d := range b.devices { + if d.IP == device.IP { + continue // 跳过自己 + } + + // 检查设备d的MAC地址是否有匹配的前缀 + matchingMACs := 0 + for _, mac := range d.MACAddresses { + if len(mac) >= 12 && mac[:12] == macPrefix { + matchingMACs++ + } + } + + if matchingMACs >= 3 { + matchedDevices = append(matchedDevices, d.IP) + } + } + + // 只有唯一匹配时才建立连接 + if len(matchedDevices) == 1 { + targetIP = matchedDevices[0] + matchMethod = fmt.Sprintf("MAC-prefix-12(%s)", macPrefix) + fmt.Printf(" ✓ Matched by MAC prefix-12 (unique): %s -> %s\n", neighbor.RemoteMAC, targetIP) + } else if len(matchedDevices) > 1 { + fmt.Printf(" ✗ Ambiguous MAC prefix-12 match: %s matched %v\n", macPrefix, matchedDevices) + } else { + fmt.Printf(" ✗ No device matches MAC prefix-12 %s\n", macPrefix) + } } - // 策略3c: LLDP对称性匹配(改进 - 需要双向验证) - // 设备A的接口X连接到设备B的接口Y - // 同时设备B的接口Y也应该连接到设备A的接口X + // 策略3c: LLDP接口匹配(改进 - 单向但要求唯一性) + // 如果设备A的LLDP信息显示它连接到接口Y + // 而设备B有接口Y,则A连接到B + // 但如果有多个设备都有接口Y,则不匹配(避免歧义) if targetIP == "" && neighbor.RemoteMAC != "" && neighbor.RemoteInterface != "" { - fmt.Printf(" Trying LLDP symmetric match: looking for device with interface %s\n", neighbor.RemoteInterface) + fmt.Printf(" Trying LLDP interface match: looking for device with interface %s\n", neighbor.RemoteInterface) + + var matchedDevices []string for _, d := range b.devices { if d.IP == device.IP { @@ -176,38 +217,25 @@ func (b *Builder) Build() models.TopologyGraph { } // 检查设备d是否有这个接口 - hasInterface := false for _, iface := range d.Interfaces { if iface.Name == neighbor.RemoteInterface { - hasInterface = true + matchedDevices = append(matchedDevices, d.IP) break } } + } - if !hasInterface { - continue - } - - // 双向验证:检查设备d的邻居信息中,这个接口是否连接回当前设备 - hasReverseLink := false - for _, dNeighbor := range d.Neighbors { - if dNeighbor.RemoteInterface == neighbor.LocalInterface && dNeighbor.LocalInterface == neighbor.RemoteInterface { - hasReverseLink = true - break - } - } - - 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) - } + // 只有唯一匹配时才建立连接 + if len(matchedDevices) == 1 { + targetIP = matchedDevices[0] + matchMethod = fmt.Sprintf("LLDP-interface(%s->%s)", neighbor.LocalInterface, neighbor.RemoteInterface) + fmt.Printf(" ✓ Matched by LLDP interface (unique): %s %s -> %s %s\n", + device.IP, neighbor.LocalInterface, targetIP, neighbor.RemoteInterface) + } else if len(matchedDevices) > 1 { + fmt.Printf(" ✗ Ambiguous: multiple devices have interface %s: %v\n", + neighbor.RemoteInterface, matchedDevices) + } else { + fmt.Printf(" ✗ No device found with interface %s\n", neighbor.RemoteInterface) } }