Skip to content

Commit

Permalink
arp: optimize netlink interface name resolution
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
dswarbrick committed Oct 4, 2024
1 parent 1b332ed commit 5ad0fdd
Showing 1 changed file with 11 additions and 27 deletions.
38 changes: 11 additions & 27 deletions collector/arp_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit 5ad0fdd

Please sign in to comment.