diff --git a/broadcast.go b/broadcast.go new file mode 100644 index 0000000..3d9e368 --- /dev/null +++ b/broadcast.go @@ -0,0 +1,69 @@ +package tendrils + +import ( + "context" + "log" + "net" + "time" + + "golang.org/x/net/icmp" + "golang.org/x/net/ipv4" +) + +func (t *Tendrils) pingBroadcast(ctx context.Context, iface net.Interface) { + _, broadcast := getInterfaceIPv4(iface) + if broadcast == nil { + return + } + + t.sendBroadcastPing(broadcast, iface.Name) + + ticker := time.NewTicker(30 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + t.sendBroadcastPing(broadcast, iface.Name) + } + } +} + +func (t *Tendrils) sendBroadcastPing(broadcast net.IP, ifaceName string) { + conn, err := icmp.ListenPacket("ip4:icmp", "0.0.0.0") + if err != nil { + if t.DebugARP { + log.Printf("[broadcast] %s: failed to create icmp socket: %v", ifaceName, err) + } + return + } + defer conn.Close() + + msg := icmp.Message{ + Type: ipv4.ICMPTypeEcho, + Code: 0, + Body: &icmp.Echo{ + ID: 1, + Seq: 1, + Data: []byte("tendrils"), + }, + } + msgBytes, err := msg.Marshal(nil) + if err != nil { + return + } + + _, err = conn.WriteTo(msgBytes, &net.IPAddr{IP: broadcast}) + if err != nil { + if t.DebugARP { + log.Printf("[broadcast] %s: failed to send ping to %s: %v", ifaceName, broadcast, err) + } + return + } + + if t.DebugARP { + log.Printf("[broadcast] %s: sent ping to %s", ifaceName, broadcast) + } +} diff --git a/nodes.go b/nodes.go index 8a1b2dc..3f55556 100644 --- a/nodes.go +++ b/nodes.go @@ -189,8 +189,16 @@ func (n *Nodes) updateNodeIPs(node *Node, nodeID int, ips []net.IP) []string { var added []string for _, ip := range ips { ipKey := ip.String() - if _, exists := n.ipIndex[ipKey]; exists { - continue + if existingID, exists := n.ipIndex[ipKey]; exists { + if existingID == nodeID { + continue + } + if existingNode, nodeExists := n.nodes[existingID]; nodeExists { + n.mergeNodes(nodeID, existingID) + if n.t.LogEvents { + log.Printf("[merge] %s into %s (shared ip %s)", existingNode, node, ipKey) + } + } } n.ipIndex[ipKey] = nodeID iface, exists := node.Interfaces[ipKey] @@ -287,6 +295,14 @@ func (n *Nodes) updateNodeInterface(node *Node, nodeID int, mac net.HardwareAddr for _, ip := range ips { ipKey := ip.String() + if existingID, exists := n.ipIndex[ipKey]; exists && existingID != nodeID { + if existingNode, nodeExists := n.nodes[existingID]; nodeExists { + n.mergeNodes(nodeID, existingID) + if n.t.LogEvents { + log.Printf("[merge] %s into %s (shared ip %s)", existingNode, node, ipKey) + } + } + } if !iface.IPs.Has(ipKey) { added = append(added, "ip="+ipKey) } diff --git a/tendrils.go b/tendrils.go index 3b69c14..db8f79b 100644 --- a/tendrils.go +++ b/tendrils.go @@ -223,6 +223,8 @@ func (t *Tendrils) updateInterfaces(interfaces []net.Interface) { } func (t *Tendrils) startInterface(ctx context.Context, iface net.Interface) { + go t.pingBroadcast(ctx, iface) + if !t.DisableLLDP { go t.listenLLDP(ctx, iface) }