3

I have a Python script running on Linux that needs to repeatedly and reliably read the ARP table, in order to check some IP-MAC associations. I'm evaluating the possible options, and wondering which one of these is leanest/cleanest - primarily in terms of performance and overhead.

The following are currently on my radar:

  1. Run arp -an using subprocess and parse the output
  2. Read /proc/net/arp and parse the output (similar to #1 but different way to get the underlying data; libraries like python_arptable do exactly that)
  3. Open a socket and use the SIOCGARP ioctl system call to poll the ARP table (per http://man7.org/linux/man-pages/man7/arp.7.html). I don't yet have the Python implementation for this, but believe it would be feasible.
  4. Run ip neighbor list using subprocess and parse the output
  5. Use a Python netlink library like pyroute2 to read the table

If I'm not mistaken, options #1 and #2 read the table directly from the kernel cache, and #4 and #5 use netlink calls; I'm not too sure where #3 falls.

For some time I was doing #5 - specifically using pyroute2 - but experienced some reliability issues; e.g. ARP entries that definitely exist not being listed. I was left with the sense that pyroute2 is potentially a bit too powerful for the simple task at hand here. I'm currently doing #2 and it seems to be working fine for now - but wondering if there's a better way.

Although my implementation language is Python, I believe the question is not strongly language-specific. Would love some pointers on the pros and cons of the above. Specifically:

  • Better to run a command and read its output or read a file from /proc? (option #1 vs #2)
  • Old-school kernel vs new-age netlink? (#1/2 vs #4/5)
  • How does the ioctl approach (#3) compare to the others?
Svet
  • 1,572
  • 10
  • 16
  • To follow up, I've stuck with approach #2 for the past year and it has served me perfectly well. – Svet Feb 11 '21 at 17:53

1 Answers1

0

I found this question when looking for a similar solution. This is a MRE of what I settled on:

arp_cmd=["cat", "/proc/net/arp"]
proc = subprocess.Popen(arp_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(arp_cache, arp_err) = proc.communicate()
arp_entries = arp_cache.decode().split("\n")
for entry in arp_entries.copy():
    if not entry.startswith("IP address"):
        ip = entry.split()[0]
        mac = entry.split()[3]
        mac_if = entry.split()[5]
        print("IP:", ip)
        print("mac:" mac)
        print("mac_if:" mac_if)
fileinsert
  • 431
  • 4
  • 22