diff --git a/arp.go b/arp.go index b6a42b3..9c705c7 100644 --- a/arp.go +++ b/arp.go @@ -15,8 +15,6 @@ func (t *Tendrils) pollARP(ctx context.Context) { ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() - t.readARPTable() - for { select { case <-ctx.Done(): @@ -35,7 +33,6 @@ type arpEntry struct { func (t *Tendrils) readARPTable() { entries := t.parseARPTable() - localNode := t.getLocalNode() for _, entry := range entries { @@ -65,7 +62,7 @@ func (t *Tendrils) parseARPTable() []arpEntry { } func (t *Tendrils) parseARPDarwin() []arpEntry { - cmd := exec.Command("arp", "-a") + cmd := exec.Command("arp", "-an") output, err := cmd.Output() if err != nil { return nil diff --git a/nodes.go b/nodes.go index a3d2a02..af12c26 100644 --- a/nodes.go +++ b/nodes.go @@ -1,11 +1,13 @@ package tendrils import ( + "context" "fmt" "log" "net" "sort" "sync" + "time" "github.com/fvbommel/sortorder" ) @@ -100,10 +102,11 @@ type PoEBudget struct { } type Node struct { - Name string - Interfaces map[string]*Interface - MACTable map[string]string // peer MAC -> local interface name - PoEBudget *PoEBudget + Name string + Interfaces map[string]*Interface + MACTable map[string]string // peer MAC -> local interface name + PoEBudget *PoEBudget + pollTrigger chan struct{} } func (n *Node) String() string { @@ -131,24 +134,35 @@ func (n *Node) String() string { } type Nodes struct { - mu sync.RWMutex - nodes map[int]*Node - ipIndex map[string]int - macIndex map[string]int - nextID int - t *Tendrils + mu sync.RWMutex + nodes map[int]*Node + ipIndex map[string]int + macIndex map[string]int + nodeCancel map[int]context.CancelFunc + nextID int + t *Tendrils + ctx context.Context + cancelAll context.CancelFunc } func NewNodes(t *Tendrils) *Nodes { + ctx, cancel := context.WithCancel(context.Background()) return &Nodes{ - nodes: map[int]*Node{}, - ipIndex: map[string]int{}, - macIndex: map[string]int{}, - nextID: 1, - t: t, + nodes: map[int]*Node{}, + ipIndex: map[string]int{}, + macIndex: map[string]int{}, + nodeCancel: map[int]context.CancelFunc{}, + nextID: 1, + t: t, + ctx: ctx, + cancelAll: cancel, } } +func (n *Nodes) Shutdown() { + n.cancelAll() +} + func (n *Nodes) Update(target *Node, mac net.HardwareAddr, ips []net.IP, ifaceName, nodeName, source string) { n.mu.Lock() defer n.mu.Unlock() @@ -184,18 +198,22 @@ func (n *Nodes) Update(target *Node, mac net.HardwareAddr, ips []net.IP, ifaceNa } } + var node *Node if targetID == -1 { targetID = n.nextID n.nextID++ - n.nodes[targetID] = &Node{ - Interfaces: map[string]*Interface{}, - MACTable: map[string]string{}, + node = &Node{ + Interfaces: map[string]*Interface{}, + MACTable: map[string]string{}, + pollTrigger: make(chan struct{}, 1), } + n.nodes[targetID] = node isNew = true + n.startNodePoller(targetID, node) + } else { + node = n.nodes[targetID] } - node := n.nodes[targetID] - var added []string if mac != nil { added = n.updateNodeInterface(node, targetID, mac, ips, ifaceName) @@ -206,6 +224,14 @@ func (n *Nodes) Update(target *Node, mac net.HardwareAddr, ips []net.IP, ifaceNa added = append(added, "name="+nodeName) } + hasNewIP := false + for _, a := range added { + if len(a) > 3 && a[:3] == "ip=" { + hasNewIP = true + break + } + } + if len(added) > 0 { if n.t.LogEvents { if isNew { @@ -218,6 +244,38 @@ func (n *Nodes) Update(target *Node, mac net.HardwareAddr, ips []net.IP, ifaceNa n.logNode(node) } } + + if hasNewIP { + n.triggerPoll(node) + } +} + +func (n *Nodes) startNodePoller(nodeID int, node *Node) { + ctx, cancel := context.WithCancel(n.ctx) + n.nodeCancel[nodeID] = cancel + + go func() { + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-node.pollTrigger: + n.t.pollNode(node) + case <-ticker.C: + n.t.pollNode(node) + } + } + }() +} + +func (n *Nodes) triggerPoll(node *Node) { + select { + case node.pollTrigger <- struct{}{}: + default: + } } func (n *Nodes) updateNodeInterface(node *Node, nodeID int, mac net.HardwareAddr, ips []net.IP, ifaceName string) []string { diff --git a/snmp.go b/snmp.go index 45006ea..6382255 100644 --- a/snmp.go +++ b/snmp.go @@ -1,7 +1,6 @@ package tendrils import ( - "context" "fmt" "log" "net" @@ -80,36 +79,25 @@ func (t *Tendrils) connectSNMP(ip net.IP) (*gosnmp.GoSNMP, error) { return snmp, nil } -func (t *Tendrils) pollSNMP(ctx context.Context) { - ticker := time.NewTicker(10 * time.Second) - defer ticker.Stop() - - t.querySwitches() - - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - t.querySwitches() - } +func (t *Tendrils) pollNode(node *Node) { + if t.DisableSNMP { + return } -} -func (t *Tendrils) querySwitches() { - nodes := t.nodes.All() - - for _, node := range nodes { - for _, iface := range node.Interfaces { - for _, ip := range iface.IPs { - if ip.To4() == nil { - continue - } - - go t.querySNMPDevice(node, ip) + t.nodes.mu.RLock() + var ips []net.IP + for _, iface := range node.Interfaces { + for _, ip := range iface.IPs { + if ip.To4() != nil { + ips = append(ips, ip) } } } + t.nodes.mu.RUnlock() + + for _, ip := range ips { + t.querySNMPDevice(node, ip) + } } func (t *Tendrils) querySNMPDevice(node *Node, ip net.IP) { diff --git a/tendrils.go b/tendrils.go index ac520a3..3576c16 100644 --- a/tendrils.go +++ b/tendrils.go @@ -48,11 +48,9 @@ func (t *Tendrils) Run() { t.populateLocalAddresses() if !t.DisableARP { + t.readARPTable() go t.pollARP(ctx) } - if !t.DisableSNMP { - go t.pollSNMP(ctx) - } ticker := time.NewTicker(1 * time.Second) defer ticker.Stop()