From 92299f5efcd026b5606f54195620a99a026a7717 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Sun, 18 Jan 2026 14:00:51 -0800 Subject: [PATCH] key interfaces by name when available and normalize port names Co-Authored-By: Claude Opus 4.5 --- nodes.go | 35 +++++++++++++++++---------- snmp.go | 72 ++++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 77 insertions(+), 30 deletions(-) diff --git a/nodes.go b/nodes.go index d107899..6d7bb50 100644 --- a/nodes.go +++ b/nodes.go @@ -91,12 +91,18 @@ func (n *Nodes) Update(mac net.HardwareAddr, ips []net.IP, ifaceName, nodeName, } macKey := mac.String() - var targetID int + targetID := -1 isNew := false if id, exists := n.macIndex[macKey]; exists { - targetID = id - } else { + if _, nodeExists := n.nodes[id]; nodeExists { + targetID = id + } else { + delete(n.macIndex, macKey) + } + } + + if targetID == -1 { targetID = n.nextID n.nextID++ n.nodes[targetID] = &Node{ @@ -108,15 +114,24 @@ func (n *Nodes) Update(mac net.HardwareAddr, ips []net.IP, ifaceName, nodeName, node := n.nodes[targetID] var added []string - iface, exists := node.Interfaces[macKey] + ifaceKey := macKey + if ifaceName != "" { + ifaceKey = ifaceName + } + + iface, exists := node.Interfaces[ifaceKey] if !exists { iface = &Interface{ - MAC: mac, - IPs: map[string]net.IP{}, + Name: ifaceName, + MAC: mac, + IPs: map[string]net.IP{}, } - node.Interfaces[macKey] = iface + node.Interfaces[ifaceKey] = iface + added = append(added, "iface="+ifaceKey) + } + + if _, exists := n.macIndex[macKey]; !exists { n.macIndex[macKey] = targetID - added = append(added, "mac="+macKey) } for _, ip := range ips { @@ -128,10 +143,6 @@ func (n *Nodes) Update(mac net.HardwareAddr, ips []net.IP, ifaceName, nodeName, n.ipIndex[ipKey] = targetID } - if ifaceName != "" && iface.Name == "" { - iface.Name = ifaceName - } - if nodeName != "" && node.Name == "" { node.Name = nodeName } diff --git a/snmp.go b/snmp.go index e1e0b73..aa01386 100644 --- a/snmp.go +++ b/snmp.go @@ -5,12 +5,29 @@ import ( "fmt" "log" "net" + "regexp" "strings" "time" "github.com/gosnmp/gosnmp" ) +var portNameRewrites = []struct { + re *regexp.Regexp + repl string +}{ + {regexp.MustCompile(`.*Slot: (\d+) Port: (\d+).*`), "$1/$2"}, +} + +func rewritePortName(name string) string { + for _, rw := range portNameRewrites { + if rw.re.MatchString(name) { + return rw.re.ReplaceAllString(name, rw.repl) + } + } + return name +} + type snmpConfig struct { username string authKey string @@ -105,10 +122,6 @@ func (t *Tendrils) querySNMPDevice(ip net.IP) { } defer snmp.Conn.Close() - if t.DebugSNMP { - log.Printf("[snmp] %s: connected", ip) - } - t.querySysName(snmp, ip) t.queryInterfaceMACs(snmp, ip) t.queryBridgeMIB(snmp, ip) @@ -152,24 +165,47 @@ func (t *Tendrils) queryInterfaceMACs(snmp *gosnmp.GoSNMP, deviceIP net.IP) { return } - var macs []net.HardwareAddr + ifNames := t.getInterfaceNames(snmp) + + type ifaceEntry struct { + mac net.HardwareAddr + name string + } + var ifaces []ifaceEntry + for _, result := range results { - if result.Type == gosnmp.OctetString { - macBytes := result.Value.([]byte) - if len(macBytes) == 6 { - mac := net.HardwareAddr(macBytes) - if !isBroadcastOrZero(mac) { - macs = append(macs, mac) - if t.DebugSNMP { - log.Printf("[snmp] %s: interface mac=%s", deviceIP, mac) - } - } - } + if result.Type != gosnmp.OctetString { + continue } + + macBytes := result.Value.([]byte) + if len(macBytes) != 6 { + continue + } + + mac := net.HardwareAddr(macBytes) + if isBroadcastOrZero(mac) { + continue + } + + oidSuffix := strings.TrimPrefix(strings.TrimPrefix(result.Name, "."+oid), ".") + var ifIndex int + if _, err := fmt.Sscanf(oidSuffix, "%d", &ifIndex); err != nil { + continue + } + + name := rewritePortName(ifNames[ifIndex]) + if t.DebugSNMP { + log.Printf("[snmp] %s: interface %d mac=%s name=%s", deviceIP, ifIndex, mac, name) + } + + ifaces = append(ifaces, ifaceEntry{mac: mac, name: name}) } - for _, mac := range macs { - t.nodes.Update(mac, nil, "", "", "snmp-ifmac") + var macs []net.HardwareAddr + for _, iface := range ifaces { + t.nodes.Update(iface.mac, nil, iface.name, "", "snmp-ifmac") + macs = append(macs, iface.mac) } if len(macs) > 1 { t.nodes.Merge(macs, "snmp-ifmac")