@ -17,16 +17,22 @@
package collector
import (
"errors"
"fmt"
"net"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/jsimonetti/rtnetlink"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/procfs"
"golang.org/x/sys/unix"
)
var (
arpDeviceInclude = kingpin . Flag ( "collector.arp.device-include" , "Regexp of arp devices to include (mutually exclusive to device-exclude)." ) . String ( )
arpDeviceExclude = kingpin . Flag ( "collector.arp.device-exclude" , "Regexp of arp devices to exclude (mutually exclusive to device-include)." ) . String ( )
arpNetlink = kingpin . Flag ( "collector.arp.netlink" , "Use netlink to gather stats instead of /proc/net/arp." ) . Default ( "true" ) . Bool ( )
)
type arpCollector struct {
@ -69,13 +75,65 @@ func getTotalArpEntries(deviceEntries []procfs.ARPEntry) map[string]uint32 {
return entries
}
func getTotalArpEntriesRTNL ( ) ( map [ string ] uint32 , error ) {
conn , err := rtnetlink . Dial ( nil )
if err != nil {
return nil , err
}
defer conn . Close ( )
neighbors , err := conn . Neigh . List ( )
if err != nil {
return nil , err
}
ifIndexEntries := make ( map [ uint32 ] 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 ] ++
}
}
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
}
func ( c * arpCollector ) Update ( ch chan <- prometheus . Metric ) error {
var enumeratedEntry map [ string ] uint32
if * arpNetlink {
var err error
enumeratedEntry , err = getTotalArpEntriesRTNL ( )
if err != nil {
return fmt . Errorf ( "could not get ARP entries: %w" , err )
}
} else {
entries , err := c . fs . GatherARPEntries ( )
if err != nil {
return fmt . Errorf ( "could not get ARP entries: %w" , err )
}
enumeratedEntry := getTotalArpEntries ( entries )
enumeratedEntry = getTotalArpEntries ( entries )
}
for device , entryCount := range enumeratedEntry {
if c . deviceFilter . ignored ( device ) {