add node names from hostname, lldp, and snmp

populate local node name from hostname. extract system name from lldp packets and snmp sysname oid. call logtree after node merges.

🤖 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:27:31 -08:00
parent ebfe18f84c
commit 0d36c21db2
5 changed files with 74 additions and 3 deletions

View File

@@ -6,4 +6,4 @@
- Always push after commiting - Always push after commiting
- Use git add -A so you don't miss files when committing - Use git add -A so you don't miss files when committing
- Never use go build -- use go run instead - Never use go build -- use go run instead
- Don't commit unless asked to - DO NOT commit unless asked to

13
lldp.go
View File

@@ -55,7 +55,20 @@ func (t *Tendrils) handleLLDPPacket(ifaceName string, packet gopacket.Packet) {
mac := net.HardwareAddr(lldp.ChassisID.ID) mac := net.HardwareAddr(lldp.ChassisID.ID)
if !isBroadcastOrZero(mac) { if !isBroadcastOrZero(mac) {
childPort := string(lldp.PortID.ID) childPort := string(lldp.PortID.ID)
var systemName string
for _, opt := range lldp.Values {
if opt.Type == layers.LLDPTLVSysName {
systemName = string(opt.Value)
break
}
}
t.nodes.Update(nil, []net.HardwareAddr{mac}, ifaceName, childPort, "lldp") t.nodes.Update(nil, []net.HardwareAddr{mac}, ifaceName, childPort, "lldp")
if systemName != "" {
t.nodes.SetName(mac, systemName)
}
} }
} }
} }

View File

@@ -9,6 +9,7 @@ import (
) )
type Node struct { type Node struct {
Name string
IPs map[string]net.IP IPs map[string]net.IP
MACs map[string]net.HardwareAddr MACs map[string]net.HardwareAddr
ParentID int ParentID int
@@ -29,7 +30,12 @@ func (n *Node) String() string {
} }
sort.Strings(ips) sort.Strings(ips)
return fmt.Sprintf("{macs=%v ips=%v}", macs, ips) name := n.Name
if name == "" {
name = "??"
}
return fmt.Sprintf("%s {macs=%v ips=%v}", name, macs, ips)
} }
type Nodes struct { type Nodes struct {
@@ -117,6 +123,9 @@ func (n *Nodes) UpdateWithParent(parentIP net.IP, ips []net.IP, macs []net.Hardw
n.mergeNodes(targetID, ids[i]) n.mergeNodes(targetID, ids[i])
} }
log.Printf("merged nodes %v into %s (via %s)", merging, n.nodes[targetID], source) log.Printf("merged nodes %v into %s (via %s)", merging, n.nodes[targetID], source)
n.mu.Unlock()
n.LogTree()
n.mu.Lock()
} }
node := n.nodes[targetID] node := n.nodes[targetID]
@@ -197,6 +206,18 @@ func (n *Nodes) GetByMAC(mac net.HardwareAddr) *Node {
return nil return nil
} }
func (n *Nodes) SetName(mac net.HardwareAddr, name string) {
n.mu.Lock()
defer n.mu.Unlock()
if id, exists := n.macIndex[mac.String()]; exists {
node := n.nodes[id]
if node.Name == "" {
node.Name = name
}
}
}
func (n *Nodes) All() []*Node { func (n *Nodes) All() []*Node {
n.mu.RLock() n.mu.RLock()
defer n.mu.RUnlock() defer n.mu.RUnlock()
@@ -219,7 +240,7 @@ func (n *Nodes) logNode(id int, prefix string, isLast bool) {
node := n.nodes[id] node := n.nodes[id]
if id == 0 { if id == 0 {
log.Printf("[root] %s", node) log.Printf("%s", node)
n.logChildrenByInterface(id, "") n.logChildrenByInterface(id, "")
} else { } else {
connector := "├──" connector := "├──"

31
snmp.go
View File

@@ -113,9 +113,40 @@ func (t *Tendrils) querySNMPDevice(ip net.IP) {
} }
defer snmp.Conn.Close() defer snmp.Conn.Close()
t.querySysName(snmp, ip)
t.queryBridgeMIB(snmp, ip) t.queryBridgeMIB(snmp, ip)
} }
func (t *Tendrils) querySysName(snmp *gosnmp.GoSNMP, deviceIP net.IP) {
oid := "1.3.6.1.2.1.1.5.0"
result, err := snmp.Get([]string{oid})
if err != nil {
return
}
if len(result.Variables) > 0 {
variable := result.Variables[0]
if variable.Type == gosnmp.OctetString {
sysName := string(variable.Value.([]byte))
if sysName != "" {
t.nodes.mu.RLock()
if id, exists := t.nodes.ipIndex[deviceIP.String()]; exists {
t.nodes.mu.RUnlock()
t.nodes.mu.Lock()
node := t.nodes.nodes[id]
if node.Name == "" {
node.Name = sysName
}
t.nodes.mu.Unlock()
return
}
t.nodes.mu.RUnlock()
}
}
}
}
func (t *Tendrils) queryBridgeMIB(snmp *gosnmp.GoSNMP, deviceIP net.IP) { func (t *Tendrils) queryBridgeMIB(snmp *gosnmp.GoSNMP, deviceIP net.IP) {
portOID := "1.3.6.1.2.1.17.7.1.2.2.1.2" portOID := "1.3.6.1.2.1.17.7.1.2.2.1.2"

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"log" "log"
"net" "net"
"os"
"time" "time"
) )
@@ -49,6 +50,11 @@ func (t *Tendrils) populateLocalAddresses() {
root := t.nodes.nodes[0] root := t.nodes.nodes[0]
hostname, err := os.Hostname()
if err == nil {
root.Name = hostname
}
for _, iface := range interfaces { for _, iface := range interfaces {
if len(iface.HardwareAddr) > 0 { if len(iface.HardwareAddr) > 0 {
macKey := iface.HardwareAddr.String() macKey := iface.HardwareAddr.String()