arp: optimize netlink interface name resolution (#3133)
Some checks failed
golangci-lint / lint (push) Has been cancelled

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 <daniel.swarbrick@gmail.com>
This commit is contained in:
Daniel Swarbrick 2025-01-10 10:58:08 +01:00 committed by GitHub
parent 8f9a914bee
commit acdd9b813d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -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 {