2025-11-28 15:44:28 -08:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 20:36:45 -08:00
|
|
|
if len(lldp.ChassisID.ID) == 6 {
|
|
|
|
|
mac := net.HardwareAddr(lldp.ChassisID.ID)
|
|
|
|
|
if !isBroadcastOrZero(mac) {
|
2025-11-29 20:53:29 -08:00
|
|
|
childPort := string(lldp.PortID.ID)
|
2025-11-29 22:27:31 -08:00
|
|
|
|
|
|
|
|
var systemName string
|
|
|
|
|
for _, opt := range lldp.Values {
|
|
|
|
|
if opt.Type == layers.LLDPTLVSysName {
|
|
|
|
|
systemName = string(opt.Value)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 21:12:21 -08:00
|
|
|
if t.DebugLLDP {
|
|
|
|
|
log.Printf("[lldp] %s: mac=%s port=%s name=%s", ifaceName, mac, childPort, systemName)
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-29 20:53:29 -08:00
|
|
|
t.nodes.Update(nil, []net.HardwareAddr{mac}, ifaceName, childPort, "lldp")
|
2025-11-29 22:27:31 -08:00
|
|
|
|
|
|
|
|
if systemName != "" {
|
|
|
|
|
t.nodes.SetName(mac, systemName)
|
|
|
|
|
}
|
2025-11-29 20:36:45 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
2025-11-28 15:44:28 -08:00
|
|
|
}
|