From 3c4ede7a59b806720e07c02bead6806a7981c51c Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 25 Apr 2026 23:58:19 +0800 Subject: [PATCH] =?UTF-8?q?Fix:=20=E4=BF=AE=E6=AD=A3H3C=20LLDP=E9=82=BB?= =?UTF-8?q?=E5=B1=85=E8=A7=A3=E6=9E=90,=E4=BD=BF=E7=94=A8ARP=E8=A1=A8?= =?UTF-8?q?=E6=98=A0=E5=B0=84MAC=E5=88=B0IP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 根据实际输出格式重写解析器 - 添加display arp命令获取MAC-IP映射 - 通过ChassisID(MAC)在ARP表中查找对应IP - 正确提取本地接口和远程接口信息 - 实现MAC地址到IP地址的自动关联 --- internal/device/h3c.go | 159 +++++++++++++++++++++++++++++------------ 1 file changed, 112 insertions(+), 47 deletions(-) diff --git a/internal/device/h3c.go b/internal/device/h3c.go index 72cc38b..aefc3c1 100644 --- a/internal/device/h3c.go +++ b/internal/device/h3c.go @@ -20,18 +20,24 @@ func (p *H3CParser) GetCommands() []string { "display interface", "display ip interface brief", "display lldp neighbor-information", + "display arp", // 添加ARP表用于MAC到IP的映射 } } // Parse 解析H3C设备输出 func (p *H3CParser) Parse(device *models.Device, outputs []string) error { - if len(outputs) < 4 { + if len(outputs) < 5 { return fmt.Errorf("insufficient command outputs") } p.parseVersion(device, outputs[0]) device.Interfaces = p.parseInterfaces(outputs[1], outputs[2]) - device.Neighbors = p.parseNeighbors(outputs[3]) + + // 解析ARP表用于MAC到IP映射 + arpTable := p.parseARPTable(outputs[4]) + + // 解析LLDP邻居,使用ARP表进行MAC到IP的映射 + device.Neighbors = p.parseNeighbors(outputs[3], arpTable) return nil } @@ -112,6 +118,36 @@ func (p *H3CParser) parseInterfaces(interfaceOutput, briefOutput string) []model return interfaces } +// parseARPTable 解析ARP表,建立MAC到IP的映射 +func (p *H3CParser) parseARPTable(output string) map[string]string { + macToIP := make(map[string]string) + lines := strings.Split(output, "\n") + + for _, line := range lines { + // 跳过空行和标题行 + if strings.TrimSpace(line) == "" || + strings.Contains(line, "Type") || + strings.Contains(line, "------") { + continue + } + + // ARP表格式: IP Address MAC Address Interface/Vlan + // 例: 172.16.12.1 a4bb-6de2-62cd GE1/0/1 + fields := strings.Fields(line) + if len(fields) >= 2 { + ip := fields[0] + mac := strings.ToLower(fields[1]) + + // 验证是有效的IP和MAC + if isValidIP(ip) && isValidMAC(mac) { + macToIP[mac] = ip + } + } + } + + return macToIP +} + func (p *H3CParser) parseInterfaceBrief(output string) map[string]models.Interface { interfaces := make(map[string]models.Interface) lines := strings.Split(output, "\n") @@ -131,78 +167,107 @@ func (p *H3CParser) parseInterfaceBrief(output string) map[string]models.Interfa return interfaces } -func (p *H3CParser) parseNeighbors(output string) []models.Neighbor { +func (p *H3CParser) parseNeighbors(output string, arpTable map[string]string) []models.Neighbor { var neighbors []models.Neighbor scanner := bufio.NewScanner(strings.NewReader(output)) var currentNeighbor *models.Neighbor + var localInterface string for scanner.Scan() { line := scanner.Text() - // 跳过空行和标题行 - if strings.TrimSpace(line) == "" || - strings.Contains(line, "LLDP neighbor information") || - strings.Contains(line, "------") { + // 匹配本地接口行: LLDP neighbor-information of port 20[GigabitEthernet1/0/20]: + if portRegex := regexp.MustCompile(`LLDP neighbor-information of port \d+\[([^\]]+)\]:`); portRegex.MatchString(line) { + // 保存前一个邻居 + if currentNeighbor != nil && currentNeighbor.RemoteInterface != "" { + neighbors = append(neighbors, *currentNeighbor) + } + + matches := portRegex.FindStringSubmatch(line) + localInterface = matches[1] + currentNeighbor = &models.Neighbor{ + LocalInterface: localInterface, + Protocol: "LLDP", + } continue } - // 匹配邻居设备信息 - display lldp neighbor-information 输出格式: - // NeighborIndex : 1 - // ChassisID : 00e0-fc12-3456 - // PortID : GigabitEthernet1/0/1 - // SystemName : Switch-02 - // ManagementAddress : 172.16.12.2 - // LocalInterface : GigabitEthernet1/0/1 - - if strings.Contains(line, "NeighborIndex") { - if currentNeighbor != nil && currentNeighbor.RemoteDevice != "" { - neighbors = append(neighbors, *currentNeighbor) - } - currentNeighbor = &models.Neighbor{ - Protocol: "LLDP", - } - } - if currentNeighbor != nil { - // 提取设备名称 - if strings.Contains(line, "SystemName") { + // 提取 ChassisID (MAC地址) + if strings.Contains(line, "ChassisID/subtype") { parts := strings.SplitN(line, ":", 2) if len(parts) == 2 { - currentNeighbor.RemoteDevice = strings.TrimSpace(parts[1]) + value := strings.TrimSpace(parts[1]) + // 格式: a4bb-6de2-62cd/MAC address + if macParts := strings.Split(value, "/"); len(macParts) > 0 { + mac := strings.TrimSpace(strings.ToLower(macParts[0])) + // 通过ARP表查找IP + if ip, ok := arpTable[mac]; ok { + currentNeighbor.RemoteIP = ip + currentNeighbor.RemoteDevice = ip // 暂时用IP作为设备名 + } + } } } - // 提取管理IP地址 - if strings.Contains(line, "ManagementAddress") { + // 提取 PortID (远程接口) + if strings.Contains(line, "PortID/subtype") { parts := strings.SplitN(line, ":", 2) if len(parts) == 2 { - currentNeighbor.RemoteIP = strings.TrimSpace(parts[1]) - } - } - - // 提取远程接口 - if strings.Contains(line, "PortID") { - parts := strings.SplitN(line, ":", 2) - if len(parts) == 2 { - currentNeighbor.RemoteInterface = strings.TrimSpace(parts[1]) - } - } - - // 提取本地接口 - if strings.Contains(line, "LocalInterface") { - parts := strings.SplitN(line, ":", 2) - if len(parts) == 2 { - currentNeighbor.LocalInterface = strings.TrimSpace(parts[1]) + value := strings.TrimSpace(parts[1]) + // 格式: GigabitEthernet0/0/1/Interface name + if portParts := strings.Split(value, "/"); len(portParts) >= 3 { + // 取前3部分作为接口名: GigabitEthernet0/0/1 + currentNeighbor.RemoteInterface = strings.Join(portParts[:3], "/") + } } } } } // 添加最后一个邻居 - if currentNeighbor != nil && currentNeighbor.RemoteDevice != "" { + if currentNeighbor != nil && currentNeighbor.RemoteInterface != "" { neighbors = append(neighbors, *currentNeighbor) } return neighbors } + +// isValidIP 简单验证IP地址 +func isValidIP(ip string) bool { + parts := strings.Split(ip, ".") + if len(parts) != 4 { + return false + } + for _, part := range parts { + if len(part) == 0 || len(part) > 3 { + return false + } + for _, c := range part { + if c < '0' || c > '9' { + return false + } + } + } + return true +} + +// isValidMAC 简单验证MAC地址 +func isValidMAC(mac string) bool { + parts := strings.Split(mac, "-") + if len(parts) != 3 && len(parts) != 6 { + return false + } + for _, part := range parts { + if len(part) != 2 { + return false + } + for _, c := range part { + if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + return false + } + } + } + return true +}