Fix ARP incomplete entries and Art-Net broadcast reception

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ian Gulliver
2026-01-27 22:57:53 -08:00
parent 25f8f410c1
commit ccd6956d6a
3 changed files with 48 additions and 36 deletions

View File

@@ -30,20 +30,15 @@ type ArtNetNode struct {
LastSeen time.Time `json:"last_seen"`
}
func (t *Tendrils) listenArtNet(ctx context.Context, iface net.Interface) {
srcIP, _ := getInterfaceIPv4(iface)
if srcIP == nil {
return
}
conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: srcIP, Port: artNetPort})
func (t *Tendrils) startArtNetListener(ctx context.Context) {
conn, err := net.ListenUDP("udp4", &net.UDPAddr{Port: artNetPort})
if err != nil {
log.Printf("[ERROR] failed to listen artnet on %s: %v", iface.Name, err)
log.Printf("[ERROR] failed to listen artnet: %v", err)
return
}
defer conn.Close()
go t.runArtNetPoller(ctx, iface, conn)
t.artnetConn = conn
buf := make([]byte, 65536)
for {
@@ -62,11 +57,39 @@ func (t *Tendrils) listenArtNet(ctx context.Context, iface net.Interface) {
continue
}
t.handleArtNetPacket(iface.Name, src.IP, buf[:n])
t.handleArtNetPacket(src.IP, buf[:n])
}
}
func (t *Tendrils) handleArtNetPacket(ifaceName string, srcIP net.IP, data []byte) {
func (t *Tendrils) startArtNetPoller(ctx context.Context, iface net.Interface) {
srcIP, broadcast := getInterfaceIPv4(iface)
if srcIP == nil || broadcast == nil {
return
}
sendConn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: srcIP, Port: 0})
if err != nil {
log.Printf("[ERROR] failed to create artnet send socket on %s: %v", iface.Name, err)
return
}
defer sendConn.Close()
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
t.sendArtPoll(sendConn, broadcast, iface.Name)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
t.sendArtPoll(sendConn, broadcast, iface.Name)
}
}
}
func (t *Tendrils) handleArtNetPacket(srcIP net.IP, data []byte) {
if len(data) < 12 {
return
}
@@ -79,11 +102,11 @@ func (t *Tendrils) handleArtNetPacket(ifaceName string, srcIP net.IP, data []byt
switch opcode {
case opPollReply:
t.handleArtPollReply(ifaceName, srcIP, data)
t.handleArtPollReply(srcIP, data)
}
}
func (t *Tendrils) handleArtPollReply(ifaceName string, srcIP net.IP, data []byte) {
func (t *Tendrils) handleArtPollReply(srcIP net.IP, data []byte) {
if len(data) < 207 {
return
}
@@ -123,7 +146,7 @@ func (t *Tendrils) handleArtPollReply(ifaceName string, srcIP net.IP, data []byt
}
if t.DebugArtNet {
log.Printf("[artnet] %s: %s %s short=%q long=%q numPorts=%d portTypes=%v in=%v out=%v", ifaceName, ip, mac, shortName, longName, numPorts, data[174:178], inputs, outputs)
log.Printf("[artnet] %s %s short=%q long=%q numPorts=%d portTypes=%v in=%v out=%v", ip, mac, shortName, longName, numPorts, data[174:178], inputs, outputs)
}
name := longName
@@ -146,27 +169,6 @@ func (t *Tendrils) handleArtPollReply(ifaceName string, srcIP net.IP, data []byt
}
}
func (t *Tendrils) runArtNetPoller(ctx context.Context, iface net.Interface, conn *net.UDPConn) {
_, broadcast := getInterfaceIPv4(iface)
if broadcast == nil {
return
}
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
t.sendArtPoll(conn, broadcast, iface.Name)
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
t.sendArtPoll(conn, broadcast, iface.Name)
}
}
}
func (t *Tendrils) sendArtPoll(conn *net.UDPConn, broadcast net.IP, ifaceName string) {
packet := make([]byte, 14)
copy(packet[0:8], artNetID)