add target node parameter to Update and SIGUSR1 dump handler
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2
arp.go
2
arp.go
@@ -48,7 +48,7 @@ func (t *Tendrils) readARPTable() {
|
||||
log.Printf("[arp] %s: ip=%s mac=%s", entry.iface, entry.ip, entry.mac)
|
||||
}
|
||||
|
||||
t.nodes.Update(entry.mac, []net.IP{entry.ip}, "", "", "arp")
|
||||
t.nodes.Update(nil, entry.mac, []net.IP{entry.ip}, "", "", "arp")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
lldp.go
2
lldp.go
@@ -68,7 +68,7 @@ func (t *Tendrils) handleLLDPPacket(ifaceName string, packet gopacket.Packet) {
|
||||
log.Printf("[lldp] %s: mac=%s port=%s name=%s", ifaceName, mac, childPort, systemName)
|
||||
}
|
||||
|
||||
t.nodes.Update(mac, nil, childPort, systemName, "lldp")
|
||||
t.nodes.Update(nil, mac, nil, childPort, systemName, "lldp")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
nodes.go
41
nodes.go
@@ -69,33 +69,37 @@ type Nodes struct {
|
||||
}
|
||||
|
||||
func NewNodes(t *Tendrils) *Nodes {
|
||||
n := &Nodes{
|
||||
return &Nodes{
|
||||
nodes: map[int]*Node{},
|
||||
ipIndex: map[string]int{},
|
||||
macIndex: map[string]int{},
|
||||
nextID: 1,
|
||||
t: t,
|
||||
}
|
||||
|
||||
n.nodes[0] = &Node{
|
||||
Interfaces: map[string]*Interface{},
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *Nodes) Update(mac net.HardwareAddr, ips []net.IP, ifaceName, nodeName, source string) {
|
||||
func (n *Nodes) Update(target *Node, mac net.HardwareAddr, ips []net.IP, ifaceName, nodeName, source string) {
|
||||
n.mu.Lock()
|
||||
defer n.mu.Unlock()
|
||||
|
||||
if mac == nil {
|
||||
if mac == nil && target == nil {
|
||||
return
|
||||
}
|
||||
|
||||
macKey := mac.String()
|
||||
targetID := -1
|
||||
isNew := false
|
||||
|
||||
if target != nil {
|
||||
for id, node := range n.nodes {
|
||||
if node == target {
|
||||
targetID = id
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if targetID == -1 && mac != nil {
|
||||
macKey := mac.String()
|
||||
if id, exists := n.macIndex[macKey]; exists {
|
||||
if _, nodeExists := n.nodes[id]; nodeExists {
|
||||
targetID = id
|
||||
@@ -103,6 +107,7 @@ func (n *Nodes) Update(mac net.HardwareAddr, ips []net.IP, ifaceName, nodeName,
|
||||
delete(n.macIndex, macKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if targetID == -1 {
|
||||
targetID = n.nextID
|
||||
@@ -115,10 +120,14 @@ func (n *Nodes) Update(mac net.HardwareAddr, ips []net.IP, ifaceName, nodeName,
|
||||
|
||||
node := n.nodes[targetID]
|
||||
|
||||
added := n.updateNodeInterface(node, targetID, mac, ips, ifaceName)
|
||||
var added []string
|
||||
if mac != nil {
|
||||
added = n.updateNodeInterface(node, targetID, mac, ips, ifaceName)
|
||||
}
|
||||
|
||||
if nodeName != "" && node.Name == "" {
|
||||
node.Name = nodeName
|
||||
added = append(added, "name="+nodeName)
|
||||
}
|
||||
|
||||
if len(added) > 0 {
|
||||
@@ -302,3 +311,13 @@ func (n *Nodes) All() []*Node {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (n *Nodes) LogAll() {
|
||||
n.mu.RLock()
|
||||
defer n.mu.RUnlock()
|
||||
|
||||
log.Printf("[sigusr1] ================ %d nodes ================", len(n.nodes))
|
||||
for _, node := range n.nodes {
|
||||
n.logNode(node)
|
||||
}
|
||||
}
|
||||
|
||||
72
snmp.go
72
snmp.go
@@ -106,13 +106,13 @@ func (t *Tendrils) querySwitches() {
|
||||
continue
|
||||
}
|
||||
|
||||
go t.querySNMPDevice(ip)
|
||||
go t.querySNMPDevice(node, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tendrils) querySNMPDevice(ip net.IP) {
|
||||
func (t *Tendrils) querySNMPDevice(node *Node, ip net.IP) {
|
||||
snmp, err := t.connectSNMP(ip)
|
||||
if err != nil {
|
||||
if t.DebugSNMP {
|
||||
@@ -122,12 +122,12 @@ func (t *Tendrils) querySNMPDevice(ip net.IP) {
|
||||
}
|
||||
defer snmp.Conn.Close()
|
||||
|
||||
t.querySysName(snmp, ip)
|
||||
t.queryInterfaceMACs(snmp, ip)
|
||||
t.querySysName(snmp, node)
|
||||
t.queryInterfaceMACs(snmp, node)
|
||||
t.queryBridgeMIB(snmp, ip)
|
||||
}
|
||||
|
||||
func (t *Tendrils) querySysName(snmp *gosnmp.GoSNMP, deviceIP net.IP) {
|
||||
func (t *Tendrils) querySysName(snmp *gosnmp.GoSNMP, node *Node) {
|
||||
oid := "1.3.6.1.2.1.1.5.0"
|
||||
|
||||
result, err := snmp.Get([]string{oid})
|
||||
@@ -135,29 +135,24 @@ func (t *Tendrils) querySysName(snmp *gosnmp.GoSNMP, deviceIP net.IP) {
|
||||
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()
|
||||
if len(result.Variables) == 0 {
|
||||
return
|
||||
}
|
||||
t.nodes.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variable := result.Variables[0]
|
||||
if variable.Type != gosnmp.OctetString {
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Tendrils) queryInterfaceMACs(snmp *gosnmp.GoSNMP, deviceIP net.IP) {
|
||||
sysName := string(variable.Value.([]byte))
|
||||
if sysName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
t.nodes.Update(node, nil, nil, "", sysName, "snmp-sysname")
|
||||
}
|
||||
|
||||
func (t *Tendrils) queryInterfaceMACs(snmp *gosnmp.GoSNMP, node *Node) {
|
||||
oid := "1.3.6.1.2.1.2.2.1.6"
|
||||
|
||||
results, err := snmp.BulkWalkAll(oid)
|
||||
@@ -167,12 +162,6 @@ func (t *Tendrils) queryInterfaceMACs(snmp *gosnmp.GoSNMP, deviceIP net.IP) {
|
||||
|
||||
ifNames := t.getInterfaceNames(snmp)
|
||||
|
||||
type ifaceEntry struct {
|
||||
mac net.HardwareAddr
|
||||
name string
|
||||
}
|
||||
var ifaces []ifaceEntry
|
||||
|
||||
for _, result := range results {
|
||||
if result.Type != gosnmp.OctetString {
|
||||
continue
|
||||
@@ -196,27 +185,10 @@ func (t *Tendrils) queryInterfaceMACs(snmp *gosnmp.GoSNMP, deviceIP net.IP) {
|
||||
|
||||
name := rewritePortName(ifNames[ifIndex])
|
||||
if t.DebugSNMP {
|
||||
log.Printf("[snmp] %s: interface %d mac=%s name=%s", deviceIP, ifIndex, mac, name)
|
||||
log.Printf("[snmp] %s: interface %d mac=%s name=%s", snmp.Target, ifIndex, mac, name)
|
||||
}
|
||||
|
||||
ifaces = append(ifaces, ifaceEntry{mac: mac, name: name})
|
||||
}
|
||||
|
||||
var macs []net.HardwareAddr
|
||||
for _, iface := range ifaces {
|
||||
t.nodes.Update(iface.mac, nil, iface.name, "", "snmp-ifmac")
|
||||
macs = append(macs, iface.mac)
|
||||
}
|
||||
|
||||
existingNode := t.nodes.GetByIP(deviceIP)
|
||||
if existingNode != nil {
|
||||
for _, iface := range existingNode.Interfaces {
|
||||
macs = append(macs, iface.MAC)
|
||||
}
|
||||
}
|
||||
|
||||
if len(macs) > 1 {
|
||||
t.nodes.Merge(macs, "snmp-ifmac")
|
||||
t.nodes.Update(node, mac, nil, name, "", "snmp-ifmac")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,7 +250,7 @@ func (t *Tendrils) queryBridgeMIB(snmp *gosnmp.GoSNMP, deviceIP net.IP) {
|
||||
log.Printf("[snmp] %s: mac=%s port=%s", deviceIP, mac, ifName)
|
||||
}
|
||||
|
||||
t.nodes.Update(mac, nil, "", "", "snmp")
|
||||
t.nodes.Update(nil, mac, nil, "", "", "snmp")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
43
tendrils.go
43
tendrils.go
@@ -5,6 +5,8 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -35,6 +37,14 @@ func (t *Tendrils) Run() {
|
||||
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 {
|
||||
@@ -60,43 +70,30 @@ func (t *Tendrils) populateLocalAddresses() {
|
||||
return
|
||||
}
|
||||
|
||||
t.nodes.mu.Lock()
|
||||
defer t.nodes.mu.Unlock()
|
||||
|
||||
root := t.nodes.nodes[0]
|
||||
|
||||
hostname, err := os.Hostname()
|
||||
if err == nil {
|
||||
root.Name = hostname
|
||||
}
|
||||
hostname, _ := os.Hostname()
|
||||
|
||||
var target *Node
|
||||
for _, netIface := range interfaces {
|
||||
if len(netIface.HardwareAddr) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
macKey := netIface.HardwareAddr.String()
|
||||
iface := &Interface{
|
||||
Name: netIface.Name,
|
||||
MAC: netIface.HardwareAddr,
|
||||
IPs: map[string]net.IP{},
|
||||
}
|
||||
|
||||
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() {
|
||||
ipKey := ipnet.IP.String()
|
||||
iface.IPs[ipKey] = ipnet.IP
|
||||
t.nodes.ipIndex[ipKey] = 0
|
||||
ips = append(ips, ipnet.IP)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
root.Interfaces[macKey] = iface
|
||||
t.nodes.macIndex[macKey] = 0
|
||||
t.nodes.Update(target, netIface.HardwareAddr, ips, netIface.Name, hostname, "local")
|
||||
if target == nil {
|
||||
target = t.nodes.GetByMAC(netIface.HardwareAddr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +144,7 @@ func (t *Tendrils) updateInterfaces(interfaces []net.Interface) {
|
||||
|
||||
for name, cancel := range t.activeInterfaces {
|
||||
if !current[name] {
|
||||
log.Printf("interface removed: %s", name)
|
||||
log.Printf("[iface] remove: %s", name)
|
||||
cancel()
|
||||
delete(t.activeInterfaces, name)
|
||||
}
|
||||
@@ -155,7 +152,7 @@ func (t *Tendrils) updateInterfaces(interfaces []net.Interface) {
|
||||
|
||||
for _, iface := range interfaces {
|
||||
if _, exists := t.activeInterfaces[iface.Name]; !exists {
|
||||
log.Printf("interface added: %s", iface.Name)
|
||||
log.Printf("[iface] add: %s", iface.Name)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
t.activeInterfaces[iface.Name] = cancel
|
||||
t.startInterface(ctx, iface)
|
||||
|
||||
Reference in New Issue
Block a user