From 6771858c4074da0a77063b96bd8e4306f3eccb64 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 26 Apr 2026 01:08:14 +0800 Subject: [PATCH] =?UTF-8?q?Feat:=20=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87MAC?= =?UTF-8?q?=E5=9C=B0=E5=9D=80=E8=BF=9B=E8=A1=8C=E9=82=BB=E5=B1=85=E5=8C=B9?= =?UTF-8?q?=E9=85=8D=E5=92=8C=E6=8B=93=E6=89=91=E8=BF=9E=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Neighbor模型添加RemoteMAC字段 - Device模型添加MACAddresses字段 - H3C解析器保存邻居MAC地址和设备所有接口MAC - 拓扑构建支持三层匹配: IP -> 设备名 -> MAC地址 - 即使ARP表获取失败也能通过MAC地址自动连线 --- internal/device/h3c.go | 19 ++++++++++++++++--- internal/topology/builder.go | 33 +++++++++++++++++++++++++-------- pkg/models/models.go | 3 +++ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/internal/device/h3c.go b/internal/device/h3c.go index 9948778..c2695ac 100644 --- a/internal/device/h3c.go +++ b/internal/device/h3c.go @@ -41,6 +41,17 @@ func (p *H3CParser) Parse(device *models.Device, outputs []string) error { fmt.Printf("Warning: parsed 0 interfaces for device %s (output length: %d)\n", device.IP, len(outputs[1])) } + + // 收集所有接口的MAC地址(用于邻居匹配) + macSet := make(map[string]bool) + for _, iface := range device.Interfaces { + if iface.MAC != "" { + macSet[iface.MAC] = true + } + } + for mac := range macSet { + device.MACAddresses = append(device.MACAddresses, mac) + } } // 解析ARP表用于MAC到IP映射(允许失败) @@ -276,14 +287,16 @@ func (p *H3CParser) parseNeighbors(output string, arpTable map[string]string) [] // 格式: a4bb-6de2-62cd/MAC address if macParts := strings.Split(value, "/"); len(macParts) > 0 { mac := strings.TrimSpace(strings.ToLower(macParts[0])) - // 通过ARP表查找IP + // 保存MAC地址 + currentNeighbor.RemoteMAC = mac + + // 通过ARP表查找IP(如果有) if ip, ok := arpTable[mac]; ok { currentNeighbor.RemoteIP = ip currentNeighbor.RemoteDevice = ip } else { - // 如果ARP表中没有,使用MAC地址作为标识 + // 如果ARP表中没有,使用MAC地址作为标识(但RemoteIP仍为空) currentNeighbor.RemoteDevice = mac - // 注意:RemoteIP为空,拓扑可能无法连线 } } } diff --git a/internal/topology/builder.go b/internal/topology/builder.go index 7b0114b..2dabf09 100644 --- a/internal/topology/builder.go +++ b/internal/topology/builder.go @@ -56,24 +56,41 @@ func (b *Builder) Build() models.TopologyGraph { edgeMap := make(map[string]bool) // 用于去重 for _, device := range b.devices { for _, neighbor := range device.Neighbors { - // 检查邻居是否在设备列表中 - if _, exists := nodeMap[neighbor.RemoteIP]; !exists && neighbor.RemoteDevice != "" { - // 尝试通过设备名匹配 + // 尝试匹配邻居设备 + targetIP := neighbor.RemoteIP + + // 如果没有IP,尝试通过设备名匹配 + if targetIP == "" && neighbor.RemoteDevice != "" { for _, d := range b.devices { if d.Hostname == neighbor.RemoteDevice { - neighbor.RemoteIP = d.IP + targetIP = d.IP + break + } + } + } + + // 如果还是没有,尝试通过MAC地址匹配(新增) + if targetIP == "" && neighbor.RemoteMAC != "" { + for _, d := range b.devices { + for _, mac := range d.MACAddresses { + if mac == neighbor.RemoteMAC { + targetIP = d.IP + break + } + } + if targetIP != "" { break } } } - if neighbor.RemoteIP == "" { + if targetIP == "" { continue } // 创建唯一的边ID - edgeID := fmt.Sprintf("%s-%s-%s", device.IP, neighbor.LocalInterface, neighbor.RemoteIP) - reverseEdgeID := fmt.Sprintf("%s-%s-%s", neighbor.RemoteIP, neighbor.RemoteInterface, device.IP) + edgeID := fmt.Sprintf("%s-%s-%s", device.IP, neighbor.LocalInterface, targetIP) + reverseEdgeID := fmt.Sprintf("%s-%s-%s", targetIP, neighbor.RemoteInterface, device.IP) // 避免重复边 if edgeMap[edgeID] || edgeMap[reverseEdgeID] { @@ -83,7 +100,7 @@ func (b *Builder) Build() models.TopologyGraph { edge := models.TopologyEdge{ ID: edgeID, Source: device.IP, - Target: neighbor.RemoteIP, + Target: targetIP, SourceInterface: neighbor.LocalInterface, TargetInterface: neighbor.RemoteInterface, Protocol: neighbor.Protocol, diff --git a/pkg/models/models.go b/pkg/models/models.go index 55ffec6..ce5a1f9 100644 --- a/pkg/models/models.go +++ b/pkg/models/models.go @@ -24,6 +24,7 @@ type Device struct { Uptime string `json:"uptime"` Interfaces []Interface `json:"interfaces"` Neighbors []Neighbor `json:"neighbors"` + MACAddresses []string `json:"mac_addresses,omitempty"` // 设备的所有MAC地址(用于邻居匹配) LastScanned time.Time `json:"last_scanned"` ScanStatus string `json:"scan_status"` // success, failed, pending ErrorMessage string `json:"error_message,omitempty"` @@ -46,11 +47,13 @@ type Interface struct { OutPackets int64 `json:"out_packets"` } +// Neighbor 邻居设备信息 // Neighbor 邻居设备信息 type Neighbor struct { LocalInterface string `json:"local_interface"` RemoteDevice string `json:"remote_device"` RemoteIP string `json:"remote_ip"` + RemoteMAC string `json:"remote_mac,omitempty"` // 新增:邻居MAC地址 RemoteInterface string `json:"remote_interface"` Protocol string `json:"protocol"` // CDP, LLDP, ARP }