使用Golang简化现有库以获取LLDP信息

使用Golang简化现有库以获取LLDP信息 我刚开始学习Go语言和编程。我能写一些简单的脚本,但我的知识也就仅限于此了。

我想编写的代码: 我希望有一个“简单”的函数,可以调用它来获取指定接口的LLDP邻居信息。

我已经找到了这个项目,它似乎已经实现了我的需求

我的问题: 它是一个大型软件的插件,并且似乎通过gRPC与其他进程通信。 所以它对我来说功能太多了。我真的只想要一个阻塞函数,调用后返回指定接口的LLDP信息。 于是我尝试了这样做:lldp.go - Pastebin.com(如果你要测试,需要修改第183行的接口名称)。

遗憾的是它似乎没有正常工作:

go build lldptest.go && ./lldptest                                                                                                                                                                                                   2.6.3
2022/10/24 21:06:14 LLDP Plugin Started.
2022/10/24 21:06:14 Started Capture
2022/10/24 21:06:24 Opened interface:  en0
SysName: %s  lol
2022/10/24 21:06:26 Waited for all processes to stop
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
{{0 0   0} <nil>}
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x38 pc=0x10044daac]

goroutine 35 [running]:
github.com/RackHD/inservice/plugins/lldp/neighbors.(*Neighbors).translatePacket(0x1e?, 0xc000100a00)
	/Users/user/code/go/pkg/mod/github.com/!rack!h!d/inservice@v0.0.0-20170929210032-8049898f824e/plugins/lldp/neighbors/decode.go:12 +0x2c
github.com/RackHD/inservice/plugins/lldp/neighbors.(*Neighbors).ProcessPacket(0xc00022e150, 0xc00022e0f0?, {{0x0, 0x0, {0x0, 0x0}, {0x0, 0x0, 0x0}, 0x0}, ...})
	/Users/user/code/go/pkg/mod/github.com/!rack!h!d/inservice@v0.0.0-20170929210032-8049898f824e/plugins/lldp/neighbors/neighbors.go:148 +0xc5
created by main.(*LLDPPlugin).Capture.func1
	/tmp/lldptest/lldptest.go:139 +0x38

更多关于使用Golang简化现有库以获取LLDP信息的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于使用Golang简化现有库以获取LLDP信息的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我看了你的代码和错误信息。问题在于你直接使用了RackHD库的neighbors.Neighbors结构,但该库的设计需要完整的插件初始化流程。这里提供一个简化版的LLDP抓取实现:

package main

import (
    "fmt"
    "log"
    "time"
    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
)

type LLDPNeighbor struct {
    ChassisID   string
    PortID      string
    SystemName  string
    PortDesc    string
    SystemDesc  string
    Capabilities []string
}

func GetLLDPNeighbors(interfaceName string, timeout time.Duration) ([]LLDPNeighbor, error) {
    handle, err := pcap.OpenLive(interfaceName, 65536, true, pcap.BlockForever)
    if err != nil {
        return nil, fmt.Errorf("打开接口失败: %v", err)
    }
    defer handle.Close()

    // 设置BPF过滤器只捕获LLDP包
    err = handle.SetBPFFilter("ether proto 0x88cc")
    if err != nil {
        return nil, fmt.Errorf("设置过滤器失败: %v", err)
    }

    var neighbors []LLDPNeighbor
    endTime := time.Now().Add(timeout)

    for time.Now().Before(endTime) {
        data, _, err := handle.ReadPacketData()
        if err != nil {
            if err == pcap.NextErrorTimeoutExpired {
                continue
            }
            break
        }

        packet := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
        if lldpLayer := packet.Layer(layers.LayerTypeLinkLayerDiscovery); lldpLayer != nil {
            lldp := lldpLayer.(*layers.LinkLayerDiscovery)
            
            neighbor := LLDPNeighbor{}
            
            // 解析Chassis ID
            if lldp.ChassisID != nil {
                neighbor.ChassisID = string(lldp.ChassisID.ID)
            }
            
            // 解析Port ID
            if lldp.PortID != nil {
                neighbor.PortID = string(lldp.PortID.ID)
            }
            
            // 解析TLVs
            for _, tlv := range lldp.OrganizationSpecificTLVs {
                switch tlv.SubType {
                case 5: // System Name
                    neighbor.SystemName = string(tlv.Info)
                case 6: // System Description
                    neighbor.SystemDesc = string(tlv.Info)
                case 7: // System Capabilities
                    neighbor.Capabilities = parseCapabilities(tlv.Info)
                }
            }
            
            neighbors = append(neighbors, neighbor)
        }
    }

    return neighbors, nil
}

func parseCapabilities(data []byte) []string {
    if len(data) < 4 {
        return nil
    }
    
    capabilities := []string{}
    enabled := data[2]<<8 | data[3]
    
    capMap := map[uint16]string{
        0x0001: "Other",
        0x0002: "Repeater",
        0x0004: "Bridge",
        0x0008: "WLAN AP",
        0x0010: "Router",
        0x0020: "Telephone",
        0x0040: "DOCSIS",
        0x0080: "Station",
    }
    
    for mask, name := range capMap {
        if enabled&mask != 0 {
            capabilities = append(capabilities, name)
        }
    }
    
    return capabilities
}

func main() {
    // 使用示例
    neighbors, err := GetLLDPNeighbors("en0", 10*time.Second)
    if err != nil {
        log.Fatal(err)
    }
    
    for i, neighbor := range neighbors {
        fmt.Printf("Neighbor %d:\n", i+1)
        fmt.Printf("  ChassisID: %s\n", neighbor.ChassisID)
        fmt.Printf("  PortID: %s\n", neighbor.PortID)
        fmt.Printf("  SystemName: %s\n", neighbor.SystemName)
        fmt.Printf("  SystemDesc: %s\n", neighbor.SystemDesc)
        fmt.Printf("  Capabilities: %v\n", neighbor.Capabilities)
        fmt.Println()
    }
}

需要安装依赖:

go get github.com/google/gopacket
go get github.com/google/gopacket/layers
go get github.com/google/gopacket/pcap

这个实现直接使用gopacket库捕获和解析LLDP包,避免了复杂的插件系统。GetLLDPNeighbors函数是阻塞的,会在指定超时时间内等待LLDP包。

回到顶部