Optionally fetch ARP stats via rtnetlink instead of procfs (#2777)
* Optionally fetch ARP stats via rtnetlink instead of procfs Implement collection of ARP stats via rtnetlink to work around shortcomings in the output of /proc/net/arp, which truncates InfiniBand link-layer addresses. Fixes: #2776 --------- Signed-off-by: Daniel Swarbrick <daniel.swarbrick@gmail.com> Co-authored-by: Ben Kochie <superq@gmail.com>
This commit is contained in:
parent
cda1d820bb
commit
685b98ec7f
|
@ -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 (c *arpCollector) Update(ch chan<- prometheus.Metric) error {
|
||||
entries, err := c.fs.GatherARPEntries()
|
||||
func getTotalArpEntriesRTNL() (map[string]uint32, error) {
|
||||
conn, err := rtnetlink.Dial(nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get ARP entries: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
neighbors, err := conn.Neigh.List()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
enumeratedEntry := getTotalArpEntries(entries)
|
||||
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)
|
||||
}
|
||||
|
||||
for device, entryCount := range enumeratedEntry {
|
||||
if c.deviceFilter.ignored(device) {
|
||||
|
|
|
@ -132,6 +132,7 @@ fi
|
|||
--collector.qdisc.fixtures="collector/fixtures/qdisc/" \
|
||||
--collector.qdisc.device-include="(wlan0|eth0)" \
|
||||
--collector.arp.device-exclude="nope" \
|
||||
--no-collector.arp.netlink \
|
||||
--collector.hwmon.chip-include="(applesmc|coretemp|hwmon4|nct6779)" \
|
||||
--collector.netclass.ignored-devices="(dmz|int)" \
|
||||
--collector.netclass.ignore-invalid-speed \
|
||||
|
|
4
go.mod
4
go.mod
|
@ -14,7 +14,7 @@ require (
|
|||
github.com/hodgesds/perf-utils v0.7.0
|
||||
github.com/illumos/go-kstat v0.0.0-20210513183136-173c9b0a9973
|
||||
github.com/josharian/native v1.1.0
|
||||
github.com/jsimonetti/rtnetlink v1.3.4
|
||||
github.com/jsimonetti/rtnetlink v1.3.5
|
||||
github.com/lufia/iostat v1.2.1
|
||||
github.com/mattn/go-xmlrpc v0.0.3
|
||||
github.com/mdlayher/ethtool v0.1.0
|
||||
|
@ -29,7 +29,7 @@ require (
|
|||
github.com/prometheus/procfs v0.11.1
|
||||
github.com/safchain/ethtool v0.3.0
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||
golang.org/x/sys v0.10.0
|
||||
golang.org/x/sys v0.11.0
|
||||
howett.net/plist v1.0.0
|
||||
)
|
||||
|
||||
|
|
9
go.sum
9
go.sum
|
@ -8,7 +8,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ=
|
||||
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
|
@ -47,8 +47,8 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
|
|||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/jsimonetti/rtnetlink v1.3.4 h1:uUcd9SE8sQe/enLBEVaWDkgGYWNsX3EU4eIxbEZYmF0=
|
||||
github.com/jsimonetti/rtnetlink v1.3.4/go.mod h1:jGCNm5lJdGplEXKCVwqhQ5tRIGV0dNREhLyNWVzBxHc=
|
||||
github.com/jsimonetti/rtnetlink v1.3.5 h1:hVlNQNRlLDGZz31gBPicsG7Q53rnlsz1l1Ix/9XlpVA=
|
||||
github.com/jsimonetti/rtnetlink v1.3.5/go.mod h1:0LFedyiTkebnd43tE4YAkWGIq9jQphow4CcwxaT2Y00=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
|
@ -141,8 +141,9 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
|
|
Loading…
Reference in New Issue