Files
Your Name d0927cbad5 Initial commit: 网络拓扑发现系统
- 支持Cisco、华为、H3C、ASA、Linux、Windows设备
- SSH远程采集设备信息
- 自动发现网络拓扑(LLDP/CDP)
- Web可视化界面
- 支持旧版SSH加密算法兼容
2026-04-25 22:35:51 +08:00

285 regels
7.7 KiB
Go

package device
import (
"bufio"
"fmt"
"net"
"network-topology-discovery/pkg/models"
"regexp"
"strings"
)
// CiscoParser Cisco设备解析器
type CiscoParser struct {
BaseParser
}
// GetCommands 获取Cisco设备命令列表
func (p *CiscoParser) GetCommands() []string {
return []string{
"show version",
"show interface",
"show ip interface brief",
"show cdp neighbors detail",
"show lldp neighbors detail",
}
}
// Parse 解析Cisco设备输出
func (p *CiscoParser) Parse(device *models.Device, outputs []string) error {
if len(outputs) < 5 {
return fmt.Errorf("insufficient command outputs")
}
// 解析设备基本信息
p.parseVersion(device, outputs[0])
// 解析接口信息
interfaces := p.parseInterfaces(outputs[1], outputs[2])
device.Interfaces = interfaces
// 解析邻居信息
neighbors := p.parseNeighbors(outputs[3], outputs[4])
device.Neighbors = neighbors
return nil
}
// parseVersion 解析版本信息
func (p *CiscoParser) parseVersion(device *models.Device, output string) {
// 提取主机名
hostnameRegex := regexp.MustCompile(`^(\S+)\s+#`)
lines := strings.Split(output, "\n")
for _, line := range lines {
if matches := hostnameRegex.FindStringSubmatch(line); len(matches) > 1 {
device.Hostname = matches[1]
break
}
}
// 提取系统版本
versionRegex := regexp.MustCompile(`Cisco IOS Software,?\s+(?:\S+\s+)?(?:\S+\s+)?Version\s+(\S+)`)
if matches := versionRegex.FindStringSubmatch(output); len(matches) > 1 {
device.OSVersion = matches[1]
}
// 提取运行时间
uptimeRegex := regexp.MustCompile(`uptime is\s+(.+)`)
if matches := uptimeRegex.FindStringSubmatch(output); len(matches) > 1 {
device.Uptime = strings.TrimSpace(matches[1])
}
}
// parseInterfaces 解析接口信息
func (p *CiscoParser) parseInterfaces(interfaceOutput, briefOutput string) []models.Interface {
var interfaces []models.Interface
// 从brief输出解析接口状态
briefMap := p.parseInterfaceBrief(briefOutput)
// 从详细输出解析接口详情
scanner := bufio.NewScanner(strings.NewReader(interfaceOutput))
var currentInterface *models.Interface
for scanner.Scan() {
line := scanner.Text()
// 匹配接口名称
if nameRegex := regexp.MustCompile(`^(\S+)\s+is\s+(up|down|administratively down)`); nameRegex.MatchString(line) {
// 保存前一个接口
if currentInterface != nil {
interfaces = append(interfaces, *currentInterface)
}
matches := nameRegex.FindStringSubmatch(line)
currentInterface = &models.Interface{
Name: matches[1],
Status: matches[2],
}
// 填充brief信息
if brief, ok := briefMap[currentInterface.Name]; ok {
currentInterface.IP = brief.IP
currentInterface.Status = brief.Status
}
}
if currentInterface != nil {
// 提取描述
if descRegex := regexp.MustCompile(`Description:\s+(.+)`); descRegex.MatchString(line) {
matches := descRegex.FindStringSubmatch(line)
currentInterface.Description = matches[1]
}
// 提取MAC地址
if macRegex := regexp.MustCompile(`Hardware is\s+\S+,\s+address is\s+(\S+)`); macRegex.MatchString(line) {
matches := macRegex.FindStringSubmatch(line)
currentInterface.MAC = matches[1]
}
// 提取IP地址
if ipRegex := regexp.MustCompile(`Internet address is\s+(\d+\.\d+\.\d+\.\d+/\d+)`); ipRegex.MatchString(line) {
matches := ipRegex.FindStringSubmatch(line)
ipParts := strings.Split(matches[1], "/")
if len(ipParts) == 2 {
currentInterface.IP = ipParts[0]
currentInterface.Mask = ipParts[1]
}
}
// 提取带宽
if speedRegex := regexp.MustCompile(`(\d+)\s+(Kbit|Mbit|Gbit)`); speedRegex.MatchString(line) {
matches := speedRegex.FindStringSubmatch(line)
currentInterface.Speed = matches[1] + " " + matches[2]
}
}
}
// 保存最后一个接口
if currentInterface != nil {
interfaces = append(interfaces, *currentInterface)
}
return interfaces
}
// parseInterfaceBrief 解析接口简要信息
func (p *CiscoParser) parseInterfaceBrief(output string) map[string]models.Interface {
interfaces := make(map[string]models.Interface)
lines := strings.Split(output, "\n")
for _, line := range lines {
// 匹配: Interface IP-Address OK? Method Status Protocol
fields := strings.Fields(line)
if len(fields) >= 4 {
iface := models.Interface{
Name: fields[0],
}
// 检查是否是IP地址
if net.ParseIP(fields[1]) != nil {
iface.IP = fields[1]
}
// 状态
if len(fields) >= 3 {
iface.Status = strings.ToLower(fields[2])
}
interfaces[iface.Name] = iface
}
}
return interfaces
}
// parseNeighbors 解析邻居信息
func (p *CiscoParser) parseNeighbors(cdpOutput, lldpOutput string) []models.Neighbor {
var neighbors []models.Neighbor
// 解析CDP邻居
cdpNeighbors := p.parseCDPNeighbors(cdpOutput)
neighbors = append(neighbors, cdpNeighbors...)
// 解析LLDP邻居
lldpNeighbors := p.parseLLDPNeighbors(lldpOutput)
neighbors = append(neighbors, lldpNeighbors...)
return neighbors
}
// parseCDPNeighbors 解析CDP邻居
func (p *CiscoParser) parseCDPNeighbors(output string) []models.Neighbor {
var neighbors []models.Neighbor
scanner := bufio.NewScanner(strings.NewReader(output))
var currentNeighbor *models.Neighbor
for scanner.Scan() {
line := scanner.Text()
// 设备ID
if deviceRegex := regexp.MustCompile(`Device ID:\s+(\S+)`); deviceRegex.MatchString(line) {
if currentNeighbor != nil {
neighbors = append(neighbors, *currentNeighbor)
}
matches := deviceRegex.FindStringSubmatch(line)
currentNeighbor = &models.Neighbor{
RemoteDevice: matches[1],
Protocol: "CDP",
}
}
if currentNeighbor != nil {
// 本地接口
if localRegex := regexp.MustCompile(`Interface:\s+(\S+),\s+Port ID \(outgoing port\):\s+(\S+)`); localRegex.MatchString(line) {
matches := localRegex.FindStringSubmatch(line)
currentNeighbor.LocalInterface = matches[1]
currentNeighbor.RemoteInterface = matches[2]
}
// IP地址
if ipRegex := regexp.MustCompile(`IP address:\s+(\d+\.\d+\.\d+\.\d+)`); ipRegex.MatchString(line) {
matches := ipRegex.FindStringSubmatch(line)
currentNeighbor.RemoteIP = matches[1]
}
}
}
if currentNeighbor != nil {
neighbors = append(neighbors, *currentNeighbor)
}
return neighbors
}
// parseLLDPNeighbors 解析LLDP邻居
func (p *CiscoParser) parseLLDPNeighbors(output string) []models.Neighbor {
var neighbors []models.Neighbor
scanner := bufio.NewScanner(strings.NewReader(output))
var currentNeighbor *models.Neighbor
for scanner.Scan() {
line := scanner.Text()
// 邻居设备
if deviceRegex := regexp.MustCompile(`Chassis id:\s+(\S+)`); deviceRegex.MatchString(line) {
if currentNeighbor != nil && currentNeighbor.RemoteDevice != "" {
neighbors = append(neighbors, *currentNeighbor)
}
matches := deviceRegex.FindStringSubmatch(line)
currentNeighbor = &models.Neighbor{
RemoteDevice: matches[1],
Protocol: "LLDP",
}
}
if currentNeighbor != nil {
// 本地接口
if localRegex := regexp.MustCompile(`Local Int:\s+(\S+)`); localRegex.MatchString(line) {
matches := localRegex.FindStringSubmatch(line)
currentNeighbor.LocalInterface = matches[1]
}
// 远程接口
if remoteRegex := regexp.MustCompile(`Port ID:\s+(\S+)`); remoteRegex.MatchString(line) {
matches := remoteRegex.FindStringSubmatch(line)
currentNeighbor.RemoteInterface = matches[1]
}
// IP地址
if ipRegex := regexp.MustCompile(`Management address:\s+(\d+\.\d+\.\d+\.\d+)`); ipRegex.MatchString(line) {
matches := ipRegex.FindStringSubmatch(line)
currentNeighbor.RemoteIP = matches[1]
}
}
}
if currentNeighbor != nil && currentNeighbor.RemoteDevice != "" {
neighbors = append(neighbors, *currentNeighbor)
}
return neighbors
}