Files
tendrils/lldp.go

107 lines
2.0 KiB
Go
Raw Normal View History

package tendrils
import (
"context"
"log"
"net"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)
func (t *Tendrils) listenLLDP(ctx context.Context, iface net.Interface) {
handle, err := pcap.OpenLive(iface.Name, 65536, true, 5*time.Second)
if err != nil {
log.Printf("[ERROR] failed to open interface %s: %v", iface.Name, err)
return
}
defer handle.Close()
if err := handle.SetBPFFilter("ether proto 0x88cc"); err != nil {
log.Printf("[ERROR] failed to set BPF filter on %s: %v", iface.Name, err)
return
}
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
packets := packetSource.Packets()
for {
select {
case <-ctx.Done():
return
case packet, ok := <-packets:
if !ok {
return
}
t.handleLLDPPacket(iface.Name, packet)
}
}
}
func (t *Tendrils) handleLLDPPacket(ifaceName string, packet gopacket.Packet) {
lldpLayer := packet.Layer(layers.LayerTypeLinkLayerDiscovery)
if lldpLayer == nil {
return
}
lldp, ok := lldpLayer.(*layers.LinkLayerDiscovery)
if !ok {
return
}
if len(lldp.ChassisID.ID) == 6 {
mac := net.HardwareAddr(lldp.ChassisID.ID)
if !isBroadcastOrZero(mac) {
childPort := string(lldp.PortID.ID)
var systemName string
for _, opt := range lldp.Values {
if opt.Type == layers.LLDPTLVSysName {
systemName = string(opt.Value)
break
}
}
t.nodes.Update(nil, []net.HardwareAddr{mac}, ifaceName, childPort, "lldp")
if systemName != "" {
t.nodes.SetName(mac, systemName)
}
}
}
}
func isBroadcastOrZero(mac net.HardwareAddr) bool {
if len(mac) != 6 {
return true
}
allZero := true
allFF := true
for _, b := range mac {
if b != 0x00 {
allZero = false
}
if b != 0xff {
allFF = false
}
}
2025-11-29 21:08:32 -08:00
if allZero || allFF {
return true
}
if mac[0] == 0x01 && mac[1] == 0x00 && mac[2] == 0x5e {
return true
}
if mac[0] == 0x33 && mac[1] == 0x33 {
return true
}
return false
}