From 5ad0fdd8a6f41dfd6988be51ee5fb26455ca1772 Mon Sep 17 00:00:00 2001 From: Daniel Swarbrick Date: Wed, 25 Sep 2024 02:02:07 +0200 Subject: [PATCH] arp: optimize netlink interface name resolution github.com/jsimonetti/rtnetlink provides a high level rtnl wrapper around the lower level rtnetlink functions, which essentially does all that we need. The rtnl.Conn.Neighbors uses an internal cache for resolving interface indexes to names, so it makes at most one rtnetlink call per interface to resolve the name. Using this high level wrapper hugely simplifies our code and makes it easier to understand and maintain. Fixes: #3075 Signed-off-by: Daniel Swarbrick --- collector/arp_linux.go | 38 +++++++++++--------------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/collector/arp_linux.go b/collector/arp_linux.go index 7f417d82..e1ba52c8 100644 --- a/collector/arp_linux.go +++ b/collector/arp_linux.go @@ -17,13 +17,11 @@ package collector import ( - "errors" "fmt" "log/slog" - "net" "github.com/alecthomas/kingpin/v2" - "github.com/jsimonetti/rtnetlink/v2" + "github.com/jsimonetti/rtnetlink/v2/rtnl" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/procfs" "golang.org/x/sys/unix" @@ -76,44 +74,30 @@ func getTotalArpEntries(deviceEntries []procfs.ARPEntry) map[string]uint32 { } func getTotalArpEntriesRTNL() (map[string]uint32, error) { - conn, err := rtnetlink.Dial(nil) + conn, err := rtnl.Dial(nil) if err != nil { return nil, err } defer conn.Close() - neighbors, err := conn.Neigh.List() + // Neighbors will also contain IPv6 neighbors, but since this is purely an ARP collector, + // restrict to AF_INET. + neighbors, err := conn.Neighbours(nil, unix.AF_INET) if err != nil { return nil, err } - ifIndexEntries := make(map[uint32]uint32) + // Map of interface name to ARP neighbor count. + entries := make(map[string]uint32) for _, n := range neighbors { - // Neighbors will also contain IPv6 neighbors, but since this is purely an ARP collector, - // restrict to AF_INET. Also skip entries which have state NUD_NOARP to conform to output - // of /proc/net/arp. - if n.Family == unix.AF_INET && n.State&unix.NUD_NOARP == 0 { - ifIndexEntries[n.Index]++ + // Skip entries which have state NUD_NOARP to conform to output of /proc/net/arp. + if n.State&unix.NUD_NOARP == 0 { + entries[n.Interface.Name]++ } } - enumEntries := make(map[string]uint32) - - // Convert interface indexes to names. - for ifIndex, entryCount := range ifIndexEntries { - iface, err := net.InterfaceByIndex(int(ifIndex)) - if err != nil { - if errors.Unwrap(err).Error() == "no such network interface" { - continue - } - return nil, err - } - - enumEntries[iface.Name] = entryCount - } - - return enumEntries, nil + return entries, nil } func (c *arpCollector) Update(ch chan<- prometheus.Metric) error {