Split LLDP code into separate file and refactor interface tracking
This commit is contained in:
@@ -2,4 +2,6 @@
|
|||||||
- Use e.g. map[string]bool{} instead of make() wherever possible
|
- Use e.g. map[string]bool{} instead of make() wherever possible
|
||||||
- Use all-lowercase log messages
|
- Use all-lowercase log messages
|
||||||
- Prepend log messages with [ERROR] if applicable
|
- Prepend log messages with [ERROR] if applicable
|
||||||
- Don't mention claude in commit messages. Keep them to a single, short, descriptive sentence
|
- Don't mention claude in commit messages. Keep them to a single, short, descriptive sentence
|
||||||
|
- Always push after commiting
|
||||||
|
- Use git add -A so you don't miss files when committing
|
||||||
56
lldp.go
Normal file
56
lldp.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[%s] lldp packet received: ChassisID=%x PortID=%s TTL=%d",
|
||||||
|
ifaceName, lldp.ChassisID.ID, lldp.PortID.ID, lldp.TTL)
|
||||||
|
}
|
||||||
68
tendrils.go
68
tendrils.go
@@ -2,23 +2,18 @@ package tendrils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/gopacket"
|
|
||||||
"github.com/google/gopacket/layers"
|
|
||||||
"github.com/google/gopacket/pcap"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Tendrils struct {
|
type Tendrils struct {
|
||||||
goroutines map[string]context.CancelFunc
|
activeInterfaces map[string]context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Tendrils {
|
func New() *Tendrils {
|
||||||
return &Tendrils{
|
return &Tendrils{
|
||||||
goroutines: map[string]context.CancelFunc{},
|
activeInterfaces: map[string]context.CancelFunc{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +23,7 @@ func (t *Tendrils) Run() {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
interfaces := t.listInterfaces()
|
interfaces := t.listInterfaces()
|
||||||
t.updateGoroutines(interfaces)
|
t.updateInterfaces(interfaces)
|
||||||
<-ticker.C
|
<-ticker.C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,71 +64,30 @@ func (t *Tendrils) listInterfaces() []net.Interface {
|
|||||||
return validInterfaces
|
return validInterfaces
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tendrils) updateGoroutines(interfaces []net.Interface) {
|
func (t *Tendrils) updateInterfaces(interfaces []net.Interface) {
|
||||||
current := map[string]bool{}
|
current := map[string]bool{}
|
||||||
for _, iface := range interfaces {
|
for _, iface := range interfaces {
|
||||||
current[iface.Name] = true
|
current[iface.Name] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, cancel := range t.goroutines {
|
for name, cancel := range t.activeInterfaces {
|
||||||
if !current[name] {
|
if !current[name] {
|
||||||
log.Printf("interface removed: %s", name)
|
log.Printf("interface removed: %s", name)
|
||||||
cancel()
|
cancel()
|
||||||
delete(t.goroutines, name)
|
delete(t.activeInterfaces, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, iface := range interfaces {
|
for _, iface := range interfaces {
|
||||||
if _, exists := t.goroutines[iface.Name]; !exists {
|
if _, exists := t.activeInterfaces[iface.Name]; !exists {
|
||||||
log.Printf("interface added: %s", iface.Name)
|
log.Printf("interface added: %s", iface.Name)
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
t.goroutines[iface.Name] = cancel
|
t.activeInterfaces[iface.Name] = cancel
|
||||||
go t.handleInterface(ctx, iface)
|
t.startInterface(ctx, iface)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tendrils) handleInterface(ctx context.Context, iface net.Interface) {
|
func (t *Tendrils) startInterface(ctx context.Context, iface net.Interface) {
|
||||||
handle, err := pcap.OpenLive(iface.Name, 65536, true, 5*time.Second)
|
go t.listenLLDP(ctx, iface)
|
||||||
if err != nil {
|
|
||||||
log.Printf("[ERROR] failed to open interface %s: %v", iface.Name, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer handle.Close()
|
|
||||||
|
|
||||||
bpfFilter := fmt.Sprintf("ether proto 0x88cc")
|
|
||||||
if err := handle.SetBPFFilter(bpfFilter); 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
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("[%s] lldp packet received: ChassisID=%x PortID=%s TTL=%d",
|
|
||||||
ifaceName, lldp.ChassisID.ID, lldp.PortID.ID, lldp.TTL)
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user