Wednesday, April 20, 2022

[Golang] 一個簡單範例使用gopacket去解析LLDP封包內容

因為工作上需要查看主機上所收到任何的LLDP封包,故在網上搜尋了一下資料與範例程式碼,組成下列一個簡單範例。

本程式會找出所有在此本機上的Net Devices ( Interface ),並依序收集封包,一旦發現有LLDP則會列印相關資訊在console上。

package main

import (
    "fmt"
    "log"
    "net"
    "strings"
    "time"

    "github.com/google/gopacket"
    "github.com/google/gopacket/layers"
    "github.com/google/gopacket/pcap"
)

var (
    snapshot_len   int32 = 1024
    promiscuous    bool  = true
    err            error
    timeout        time.Duration = 1 * time.Second
    timerclick     int           = 30
    handle         *pcap.Handle
    nottododevices = []string{"virbr", "docker", "any", "lo", "usbmon"}
)

// contains checks if a string is present in a slice
func contains(s []string, str string) bool {
    for _, v := range s {
        if strings.Contains(str, v) {
            return true
        }
    }

    return false
}

func main() {
    // Get all devices
    devices, err := pcap.FindAllDevs()
    if err != nil {
        log.Fatal(err)
    }
    // Print the device information
    fmt.Println("Devices found:")
    for _, device := range devices {
        fmt.Println("\nName: ", device.Name)
        fmt.Println("Description: ", device.Description)
        fmt.Println("Devices addresses: ", device.Description)
        for _, address := range device.Addresses {
            fmt.Println("- IP address: ", address.IP)
            fmt.Println("- Subnet mask: ", address.Netmask)
        }
        // Get LLDP Packets
        if !contains(nottododevices, device.Name) {
            fmt.Println("Get LLDP...")
            ReadLLDPInfo(device.Name, snapshot_len, promiscuous, timeout)
        }
    }
}

func ReadLLDPInfo(device string, snapshot_len int32, promiscuous bool, timeout time.Duration) {

    // Open device
    handle, err = pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
    if err != nil {
        log.Printf("[%v] Error : %s", device, err)
    }
    defer handle.Close()
    // Use the handle as a packet source to process all packets
    packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
    packets := packetSource.Packets()
    timer := time.NewTimer(time.Duration(timerclick) * time.Second)

    for {
        select {
        case packet := <-packets:
            lldpLayerInfos := packet.Layer(layers.LayerTypeLinkLayerDiscoveryInfo)
            if lldpLayerInfos == nil {
                continue
            }

            lldpInfos := lldpLayerInfos.(*layers.LinkLayerDiscoveryInfo)
            if lldpInfos.PortDescription == device {
                continue
            }
            log.Printf("[%v] ************************************************", device)
            log.Printf("[%v] PortDescription : %s", device, lldpInfos.PortDescription)
            log.Printf("[%v] SysName : %s", device, lldpInfos.SysName)
            log.Printf("[%v] SysDescription : %s", device, lldpInfos.SysDescription)
            var mgmtAddress string
            switch lldpInfos.MgmtAddress.Subtype {
            case layers.IANAAddressFamilyIPV4:
                mgmtAddress = net.IP(lldpInfos.MgmtAddress.Address).String()
            default:
                mgmtAddress = string(lldpInfos.MgmtAddress.Address)
            }
            log.Printf("[%v] MgmtAddress : %s", device, mgmtAddress)

        case <-timer.C:
            log.Printf("[%v] Time's up for %d", device, timerclick)
            timer.Stop()
            return
        }
    }
}

執行結果:

Devices found:

Name:  enp4s0
Description:
Devices addresses:
- IP address:  140.96.27.24
- Subnet mask:  ffffff00
- IP address:  fe80::5ded:b7e0:45dc:b29c
- Subnet mask:  ffffffffffffffff0000000000000000
Get LLDP...
2022/04/19 13:42:11 [enp4s0] ************************************************
2022/04/19 13:42:11 [enp4s0] PortDescription : Gi1/0/14
2022/04/19 13:42:11 [enp4s0] SysName : n2048apm0
2022/04/19 13:42:11 [enp4s0] SysDescription :
2022/04/19 13:42:11 [enp4s0] MgmtAddress :
2022/04/19 13:42:35 [enp4s0] Time's up for 30

Name:  any
Description:  Pseudo-device that captures on all interfaces
Devices addresses:  Pseudo-device that captures on all interfaces

Name:  lo
Description:
Devices addresses:
- IP address:  127.0.0.1
- Subnet mask:  ff000000
- IP address:  ::1
- Subnet mask:  ffffffffffffffffffffffffffffffff

參考資料:

網路流量抓包庫 gopacket

Golang gopacket.NewPacketSource函數代碼示例

How to use decodeLinkLayerDiscovery in lldp.go?

for NIC in $(find /sys/class/net -type l -not -lname "*virtual*" -printf "%f\n" | sort); do
    echo "NIC: ${NIC}"
    echo "NIC MAC: $(ethtool -P ${NIC})"
    timeout 300 tcpdump -nn -v -i ${NIC} -s 1500 -c 1 "ether[20:2] == 0x2000"
done


LLDP: Identify switch port to which the server is connected

lldpLayerInfos := packet.Layer(layers.LayerTypeLinkLayerDiscoveryInfo)
    if lldpLayerInfos == nil {
        continue
    }
    lldpInfos := lldpLayerInfos.(*layers.LinkLayerDiscoveryInfo)
    log.Printf("[%v] ************************************************", iface.Name)
    log.Printf("[%v] PortDescription : %s", iface.Name, lldpInfos.PortDescription)
    log.Printf("[%v] SysName : %s", iface.Name, lldpInfos.SysName)
    log.Printf("[%v] SysDescription : %s", iface.Name, lldpInfos.SysDescription)
    var mgmtAddress string
    switch lldpInfos.MgmtAddress.Subtype {
    case layers.IANAAddressFamilyIPV4:
        mgmtAddress = net.IP(lldpInfos.MgmtAddress.Address).String()
    default:
        mgmtAddress = string(lldpInfos.MgmtAddress.Address)
    }
    log.Printf("[%v] MgmtAddress : %s", iface.Name, mgmtAddress)




No comments: