fix local address population and snmp reverse port discovery

populate root node with local macs/ips at startup, excluding loopback addresses and permanent arp entries. detect when snmp finds parent node mac in child forwarding table and set child localport.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ian Gulliver
2025-11-29 22:13:25 -08:00
parent 5db9e437b4
commit 75bbf4b0a4
4 changed files with 99 additions and 7 deletions

5
arp.go
View File

@@ -63,6 +63,11 @@ func (t *Tendrils) parseARPDarwin() []arpEntry {
scanner := bufio.NewScanner(strings.NewReader(string(output))) scanner := bufio.NewScanner(strings.NewReader(string(output)))
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
if strings.Contains(line, "permanent") {
continue
}
fields := strings.Fields(line) fields := strings.Fields(line)
if len(fields) < 6 { if len(fields) < 6 {
continue continue

View File

@@ -122,6 +122,7 @@ func (n *Nodes) UpdateWithParent(parentIP net.IP, ips []net.IP, macs []net.Hardw
node := n.nodes[targetID] node := n.nodes[targetID]
var added []string var added []string
if targetID != 0 {
if node.LocalPort == "" && childPort != "" { if node.LocalPort == "" && childPort != "" {
node.LocalPort = childPort node.LocalPort = childPort
added = append(added, "localPort="+childPort) added = append(added, "localPort="+childPort)
@@ -131,6 +132,7 @@ func (n *Nodes) UpdateWithParent(parentIP net.IP, ips []net.IP, macs []net.Hardw
node.ParentPort = parentPort node.ParentPort = parentPort
added = append(added, "parentPort="+parentPort) added = append(added, "parentPort="+parentPort)
} }
}
for _, ip := range ips { for _, ip := range ips {
ipKey := ip.String() ipKey := ip.String()

48
snmp.go
View File

@@ -207,6 +207,30 @@ func (t *Tendrils) queryBridgeMIB(snmp *gosnmp.GoSNMP, deviceIP net.IP) {
if addToParent { if addToParent {
t.nodes.Update([]net.IP{deviceIP}, []net.HardwareAddr{mac}, "", "", "snmp") t.nodes.Update([]net.IP{deviceIP}, []net.HardwareAddr{mac}, "", "", "snmp")
} else { } else {
t.nodes.mu.RLock()
deviceNodeID := -1
if id, exists := t.nodes.ipIndex[deviceIP.String()]; exists {
deviceNodeID = id
}
macNodeID := -1
if id, exists := t.nodes.macIndex[mac.String()]; exists {
macNodeID = id
}
if deviceNodeID != -1 && macNodeID != -1 {
deviceNode := t.nodes.nodes[deviceNodeID]
if deviceNode.ParentID == macNodeID {
t.nodes.mu.RUnlock()
t.nodes.mu.Lock()
if deviceNode.LocalPort == "" {
deviceNode.LocalPort = ifName
}
t.nodes.mu.Unlock()
continue
}
}
t.nodes.mu.RUnlock()
t.nodes.UpdateWithParent(deviceIP, nil, []net.HardwareAddr{mac}, ifName, "", "snmp") t.nodes.UpdateWithParent(deviceIP, nil, []net.HardwareAddr{mac}, ifName, "", "snmp")
} }
} }
@@ -280,6 +304,30 @@ func (t *Tendrils) queryARPTable(snmp *gosnmp.GoSNMP, deviceIP net.IP) {
ifName = "??" ifName = "??"
} }
t.nodes.mu.RLock()
deviceNodeID := -1
if id, exists := t.nodes.ipIndex[deviceIP.String()]; exists {
deviceNodeID = id
}
macNodeID := -1
if id, exists := t.nodes.macIndex[mac.String()]; exists {
macNodeID = id
}
if deviceNodeID != -1 && macNodeID != -1 {
deviceNode := t.nodes.nodes[deviceNodeID]
if deviceNode.ParentID == macNodeID {
t.nodes.mu.RUnlock()
t.nodes.mu.Lock()
if deviceNode.LocalPort == "" {
deviceNode.LocalPort = ifName
}
t.nodes.mu.Unlock()
continue
}
}
t.nodes.mu.RUnlock()
t.nodes.UpdateWithParent(deviceIP, ips, []net.HardwareAddr{mac}, ifName, "", "snmp") t.nodes.UpdateWithParent(deviceIP, ips, []net.HardwareAddr{mac}, ifName, "", "snmp")
} }
} }

View File

@@ -23,6 +23,8 @@ func (t *Tendrils) Run() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
t.populateLocalAddresses()
go t.pollARP(ctx) go t.pollARP(ctx)
go t.pollSNMP(ctx) go t.pollSNMP(ctx)
@@ -36,6 +38,41 @@ func (t *Tendrils) Run() {
} }
} }
func (t *Tendrils) populateLocalAddresses() {
interfaces, err := net.Interfaces()
if err != nil {
return
}
t.nodes.mu.Lock()
defer t.nodes.mu.Unlock()
root := t.nodes.nodes[0]
for _, iface := range interfaces {
if len(iface.HardwareAddr) > 0 {
macKey := iface.HardwareAddr.String()
root.MACs[macKey] = iface.HardwareAddr
t.nodes.macIndex[macKey] = 0
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok {
if ipnet.IP.To4() != nil && !ipnet.IP.IsLoopback() {
ipKey := ipnet.IP.String()
root.IPs[ipKey] = ipnet.IP
t.nodes.ipIndex[ipKey] = 0
}
}
}
}
}
func (t *Tendrils) listInterfaces() []net.Interface { func (t *Tendrils) listInterfaces() []net.Interface {
interfaces, err := net.Interfaces() interfaces, err := net.Interfaces()
if err != nil { if err != nil {