From 6613f5394236014058c4a8879c8b25b3ec059e34 Mon Sep 17 00:00:00 2001 From: Daniel Swarbrick Date: Wed, 25 Sep 2024 02:02:07 +0200 Subject: [PATCH] arp: optimize interface name resolution Fixes: #3075 Signed-off-by: Daniel Swarbrick --- collector/arp_linux.go | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/collector/arp_linux.go b/collector/arp_linux.go index 7f417d824e..071320f622 100644 --- a/collector/arp_linux.go +++ b/collector/arp_linux.go @@ -17,7 +17,6 @@ package collector import ( - "errors" "fmt" "log/slog" "net" @@ -87,30 +86,37 @@ func getTotalArpEntriesRTNL() (map[string]uint32, error) { return nil, err } - ifIndexEntries := make(map[uint32]uint32) + // Map of interface index to ARP neighbor count. + ifIndexEntries := make(map[int]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]++ + ifIndexEntries[int(n.Index)]++ } } + // Go's net.InterfaceByIndex(idx) function fetches the _entire_ interface table behind the + // scenes prior to filtering the response. This will result in an exponential amount of + // rtnetlink traffic as the number of interfaces increases. Instead, we fetch the interface + // table _once_, and perform our own filtering / matching. + ifaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + // Map of interface name to ARP neighbor count. 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 + for x := range ifaces { + if ifaces[x].Index == ifIndex { + enumEntries[ifaces[x].Name] = entryCount + break } - return nil, err } - - enumEntries[iface.Name] = entryCount } return enumEntries, nil