diff --git a/arp.go b/arp.go index 4e47e6172..d4537821a 100644 --- a/arp.go +++ b/arp.go @@ -17,9 +17,26 @@ import ( "fmt" "io/ioutil" "net" + "strconv" "strings" ) +// Learned from include/uapi/linux/if_arp.h +const ( + // completed entry (ha valid) + ATFComplete = 0x02 + // permanent entry + ATFPermanent = 0x04 + // Publish entry + ATFPublish = 0x08 + // Has requested trailers + ATFUseTrailers = 0x10 + // Obsoleted: Want to use a netmask (only for proxy entries) + ATFNetmask = 0x20 + // Don't answer this addresses + ATFDontPublish = 0x40 +) + // ARPEntry contains a single row of the columnar data represented in // /proc/net/arp. type ARPEntry struct { @@ -29,6 +46,8 @@ type ARPEntry struct { HWAddr net.HardwareAddr // Name of the device Device string + // Flags + Flags byte } // GatherARPEntries retrieves all the ARP entries, parse the relevant columns, @@ -72,14 +91,26 @@ func parseARPEntries(data []byte) ([]ARPEntry, error) { } func parseARPEntry(columns []string) (ARPEntry, error) { + entry := ARPEntry{Device: columns[5]} ip := net.ParseIP(columns[0]) - mac := net.HardwareAddr(columns[3]) + entry.IPAddr = ip + + if mac, err := net.ParseMAC(columns[3]); err == nil { + entry.HWAddr = mac + } else { + return ARPEntry{}, err + } - entry := ARPEntry{ - IPAddr: ip, - HWAddr: mac, - Device: columns[5], + if flags, err := strconv.ParseUint(columns[2], 0, 8); err == nil { + entry.Flags = byte(flags) + } else { + return ARPEntry{}, err } return entry, nil } + +// IsComplete returns true if ARP entry is marked with complete flag +func (entry *ARPEntry) IsComplete() bool { + return entry.Flags&ATFComplete != 0 +} diff --git a/arp_test.go b/arp_test.go index 272567b71..0edaef51b 100644 --- a/arp_test.go +++ b/arp_test.go @@ -33,11 +33,35 @@ func TestARP(t *testing.T) { t.Errorf("want 192.168.224.1, got %s", got) } - if want, got := net.HardwareAddr("00:50:56:c0:00:08").String(), arpFile[0].HWAddr.String(); want != got { - t.Errorf("want 00:50:56:c0:00:08, got %s", got) + if want, got := "00:50:56:c0:00:08", arpFile[0].HWAddr.String(); want != got { + t.Errorf("want %s, got %s", want, got) } if want, got := "ens33", arpFile[0].Device; want != got { t.Errorf("want ens33, got %s", got) } + + if want, got := true, arpFile[0].IsComplete(); want != got { + t.Errorf("want %t, got %t", want, got) + } + + if want, got := "192.168.224.2", arpFile[1].IPAddr.String(); want != got { + t.Errorf("want 192.168.224.2, got %s", got) + } + + if want, got := make(net.HardwareAddr, 6).String(), arpFile[1].HWAddr.String(); want != got { + t.Errorf("expected empty MAC, got %s", got) + } + + if want, got := "ens33", arpFile[1].Device; want != got { + t.Errorf("want %s, got %s", want, got) + } + + if want, got := byte(0x0), arpFile[1].Flags; want != got { + t.Errorf("want %b, got %b", want, got) + } + + if want, got := false, arpFile[1].IsComplete(); want != got { + t.Errorf("want %t, got %t", want, got) + } } diff --git a/fixtures.ttar b/fixtures.ttar index 30df09144..83ab1551d 100644 --- a/fixtures.ttar +++ b/fixtures.ttar @@ -2084,9 +2084,10 @@ Directory: fixtures/proc/net Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/proc/net/arp -Lines: 2 +Lines: 3 IP address HW type Flags HW address Mask Device 192.168.224.1 0x1 0x2 00:50:56:c0:00:08 * ens33 +192.168.224.2 0x1 0x0 00:00:00:00:00:00 * ens33 Mode: 664 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/proc/net/dev