Files
tendrils/tendrils.go

204 lines
3.9 KiB
Go
Raw Normal View History

package tendrils
import (
"context"
"log"
"net"
"os"
"os/signal"
"syscall"
"time"
)
type Tendrils struct {
activeInterfaces map[string]context.CancelFunc
nodes *Nodes
artnet *ArtNetNodes
Interface string
DisableARP bool
DisableLLDP bool
DisableSNMP bool
DisableIGMP bool
DisableMDNS bool
DisableArtNet bool
DisableDante bool
LogEvents bool
LogNodes bool
DebugARP bool
DebugLLDP bool
DebugSNMP bool
DebugIGMP bool
DebugMDNS bool
DebugArtNet bool
DebugDante bool
}
func New() *Tendrils {
t := &Tendrils{
activeInterfaces: map[string]context.CancelFunc{},
artnet: NewArtNetNodes(),
}
t.nodes = NewNodes(t)
return t
}
func (t *Tendrils) Run() {
2025-11-29 21:08:32 -08:00
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGUSR1)
go func() {
for range sigCh {
t.nodes.LogAll()
}
}()
t.populateLocalAddresses()
if !t.DisableARP {
t.readARPTable()
go t.pollARP(ctx)
}
2025-11-29 21:08:32 -08:00
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
interfaces := t.listInterfaces()
t.updateInterfaces(interfaces)
<-ticker.C
}
}
func (t *Tendrils) populateLocalAddresses() {
interfaces, err := net.Interfaces()
if err != nil {
return
}
hostname, _ := os.Hostname()
var target *Node
for _, netIface := range interfaces {
if len(netIface.HardwareAddr) == 0 {
continue
}
var ips []net.IP
addrs, err := netIface.Addrs()
if err == nil {
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok {
if ipnet.IP.To4() != nil && !ipnet.IP.IsLoopback() {
ips = append(ips, ipnet.IP)
}
}
}
}
t.nodes.Update(target, netIface.HardwareAddr, ips, netIface.Name, hostname, "local")
if target == nil {
target = t.nodes.GetByMAC(netIface.HardwareAddr)
}
}
}
func (t *Tendrils) getLocalNode() *Node {
interfaces, err := net.Interfaces()
if err != nil {
return nil
}
for _, iface := range interfaces {
if len(iface.HardwareAddr) > 0 {
if node := t.nodes.GetByMAC(iface.HardwareAddr); node != nil {
return node
}
}
}
return nil
}
func (t *Tendrils) listInterfaces() []net.Interface {
interfaces, err := net.Interfaces()
if err != nil {
log.Printf("[ERROR] error getting interfaces: %v", err)
return nil
}
var validInterfaces []net.Interface
for _, iface := range interfaces {
if t.Interface != "" && iface.Name != t.Interface {
continue
}
if iface.Flags&net.FlagUp == 0 {
continue
}
if iface.Flags&net.FlagLoopback != 0 {
continue
}
if iface.Flags&net.FlagPointToPoint != 0 {
continue
}
if iface.Flags&net.FlagBroadcast == 0 {
continue
}
if len(iface.HardwareAddr) == 0 {
continue
}
addrs, err := iface.Addrs()
if err != nil || len(addrs) == 0 {
continue
}
validInterfaces = append(validInterfaces, iface)
}
return validInterfaces
}
func (t *Tendrils) updateInterfaces(interfaces []net.Interface) {
current := map[string]bool{}
for _, iface := range interfaces {
current[iface.Name] = true
}
for name, cancel := range t.activeInterfaces {
if !current[name] {
log.Printf("[iface] remove: %s", name)
cancel()
delete(t.activeInterfaces, name)
}
}
for _, iface := range interfaces {
if _, exists := t.activeInterfaces[iface.Name]; !exists {
log.Printf("[iface] add: %s", iface.Name)
ctx, cancel := context.WithCancel(context.Background())
t.activeInterfaces[iface.Name] = cancel
t.startInterface(ctx, iface)
2025-11-28 15:28:47 -08:00
}
}
}
func (t *Tendrils) startInterface(ctx context.Context, iface net.Interface) {
if !t.DisableLLDP {
go t.listenLLDP(ctx, iface)
}
if !t.DisableIGMP {
go t.listenIGMP(ctx, iface)
}
if !t.DisableMDNS {
go t.listenMDNS(ctx, iface)
}
if !t.DisableArtNet {
go t.listenArtNet(ctx, iface)
}
if !t.DisableDante {
go t.listenDante(ctx, iface)
}
}