Skip to content

Commit

Permalink
Add neighbor state
Browse files Browse the repository at this point in the history
Signed-off-by: Emin Umut Gercek <[email protected]>
  • Loading branch information
eugercek committed Nov 23, 2024
1 parent 49d177b commit b1004d8
Showing 1 changed file with 41 additions and 7 deletions.
48 changes: 41 additions & 7 deletions collector/arp_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,25 @@ type arpCollector struct {
fs procfs.FS
deviceFilter deviceFilter
entries *prometheus.Desc
states *prometheus.Desc
logger *slog.Logger
}

var neighborStatesMap = map[uint16]string{
unix.NUD_INCOMPLETE: "incomplete",
unix.NUD_REACHABLE: "reachable",
unix.NUD_STALE: "stale",
unix.NUD_DELAY: "delay",
unix.NUD_PROBE: "probe",
unix.NUD_FAILED: "failed",
unix.NUD_PERMANENT: "permanent",
}

type neighborState struct {
ip string
state string
}

func init() {
registerCollector("arp", defaultEnabled, NewARPCollector)
}
Expand All @@ -61,6 +77,11 @@ func NewARPCollector(logger *slog.Logger) (Collector, error) {
"ARP entries by device",
[]string{"device"}, nil,
),
states: prometheus.NewDesc(
prometheus.BuildFQName(namespace, "arp", "states"),
"ARP states by device",
[]string{"device", "ip", "state"}, nil,
),
logger: logger,
}, nil
}
Expand All @@ -75,30 +96,33 @@ func getTotalArpEntries(deviceEntries []procfs.ARPEntry) map[string]uint32 {
return entries
}

func getTotalArpEntriesRTNL() (map[string]uint32, error) {
func getArpEntriesNTRL() (map[string]uint32, map[string]neighborState, error) {
conn, err := rtnetlink.Dial(nil)
if err != nil {
return nil, err
return nil, nil, err
}
defer conn.Close()

neighbors, err := conn.Neigh.List()
if err != nil {
return nil, err
return nil, nil, err
}

ifIndexEntries := make(map[uint32]uint32)
ifIndexStates := make(map[uint32]neighborState)

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]++
ifIndexStates[n.Index] = neighborState{ip: n.Attributes.Address.String(), state: neighborStatesMap[n.State]}
}
}

enumEntries := make(map[string]uint32)
enumStates := make(map[string]neighborState)

// Convert interface indexes to names.
for ifIndex, entryCount := range ifIndexEntries {
Expand All @@ -107,22 +131,26 @@ func getTotalArpEntriesRTNL() (map[string]uint32, error) {
if errors.Unwrap(err).Error() == "no such network interface" {
continue
}
return nil, err
return nil, nil, err
}

enumEntries[iface.Name] = entryCount
enumStates[iface.Name] = ifIndexStates[ifIndex]
}

return enumEntries, nil
return enumEntries, enumStates, nil
}

func (c *arpCollector) Update(ch chan<- prometheus.Metric) error {
var enumeratedEntry map[string]uint32
var (
enumeratedEntry map[string]uint32
enumStates map[string]neighborState
)

if *arpNetlink {
var err error

enumeratedEntry, err = getTotalArpEntriesRTNL()
enumeratedEntry, enumStates, err = getArpEntriesNTRL()
if err != nil {
return fmt.Errorf("could not get ARP entries: %w", err)
}
Expand All @@ -141,6 +169,12 @@ func (c *arpCollector) Update(ch chan<- prometheus.Metric) error {
}
ch <- prometheus.MustNewConstMetric(
c.entries, prometheus.GaugeValue, float64(entryCount), device)

if *arpNetlink {
s := enumStates[device]
ch <- prometheus.MustNewConstMetric(
c.states, prometheus.GaugeValue, 1, device, s.ip, s.state)
}
}

return nil
Expand Down

0 comments on commit b1004d8

Please sign in to comment.