Authors Top

If you have a few years of experience in the Linux ecosystem, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

1. Introduction

The Address Resolution Protocol (ARP) handles the mapping between an Internet Protocol (IP) address and a Media Access Control (MAC) address. Linux stores all such mappings in a local system cache called an ARP table.

In this tutorial, we explore the main Linux command to display and control the ARP table. First, we briefly touch upon ARP caching and the resulting table structure. Next, we install a tool for managing this structure. After that, we talk about ways to display the ARP table. Later, we explain proxy ARP and cache filtering. Finally, we delve into creating and removing ARP cache entries.

We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4 and net-tools 2.10-alpha. It should work in most POSIX-compliant environments.

2. ARP Caching

Since it maps IP to MAC addresses, ARP works with IPv4 between layers 2 and 3 of the Open Systems Interconnection (OSI) Model. Critically, the Network Discovery Protocol (NDP) replaces ARP for IPv6.

In addition to handling the mappings, ARP also allows the discovery of hosts on the network via requests. In fact, we can leverage ARP as a layer 2 ping via the arping command.

Once we see any host by reading a response or broadcast, our local ARP cache stores data in the form of a table:

+--------------+-------------------+
|  IP Address  |    MAC Address    |
+--------------+-------------------+
| 192.168.6.66 | DE-AD-BE-EF-06-66 |
| 192.168.6.10 | FE-ED-1D-EA-F0-10 |
| 192.168.6.1  | 01-00-01-BE-E0-01 |
+--------------+-------------------+

Usually, the Linux ARP table is available via the /proc pseudo-filesystem in the /proc/net/arp file:

$ cat /proc/net/arp
IP address       HW type     Flags       HW address
Mask          Device
192.168.6.66     0x1         0x6         de:ad:be:ef:06:66     *        eth0
192.168.6.10     0x1         0x2         fe:ed:1d:ea:f0:10     *        eth0
192.168.6.1      0x1         0x2         01:00:01:be:e0:01     *        eth0

Notably, there are different separators for the MAC address in the tables above. Both are valid but depend on the tool and context.

Finally, we can display and modify the system ARP cache in a more human-friendly way via the Linux arp command.

3. Installing arp

To use a command, we usually need to install it first. On Debian, arp is available from the net-tools package:

$ apt install net-tools

Here, we use apt to perform the installation.

Critically, even if we install it, the arp tool wouldn’t work under the Windows Subsystem for Linux (WSL).

4. Show ARP Table via arp

To show the current entries in the ARP cache, we just run arp without any arguments or with -e:

$ arp
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.6.10             ether   FE-ED-1D-EA-F0-10   C                     eth0
192.168.6.1              ether   01-00-01-BE-E0-01   C                     eth0

Unlike our example from earlier, the actual table has more fields:

  • Address, IPv4 address of the device
  • HWtype, type of link, where ether means Ethernet
  • HWaddress, MAC address of the device with dash as a separator
  • Flags, one or more of C (complete), M (permanent), and P (published)
  • Mask, optional IPv4 mask (netmask)
  • Iface, local interface

Another way to format the entries is the -a flag, which outputs a BSD-style list that’s arguably easier to parse:

$ arp -a
? (192.168.6.10) at FE-ED-1D-EA-F0-10 [ether] on eth0
? (192.168.6.1) at 01-00-01-BE-E0-01 [ether] on eth0

Like with other tools, we can use -n to avoid resolving any names to numbers. As a side note, IP addresses and hostnames are both valid in most arp commands. As usual, we can map one to the other via the /etc/hosts file.

Finally, when interpreting the flags in the standard output of arp, C (complete) means we got a response to our ARP query, and the host entry is fully configured. Further, permanent (M), i.e., static entries, don’t expire and are usually added manually but without using the temp argument.

On the other hand, published (P) flag entries are related to proxy ARP.

5. Proxy ARP

While not recommended, we can designate a proxy server that handles requests to IP addresses outside the local network. Thus, the proxy server responds with its own MAC address to ARP requests for any such IP address within its scope.

Since the proxy publishes itself, its resulting ARP cache entry in affected systems is called published, as marked by the P flag:

$ arp 192.168.6.68
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.6.68             ether   08-00-08-EB-E0-68   CP    255.255.255.0   eth0

This is where the mask comes into play, as it shows the range of IP addresses behind the proxy. Starting from the Linux kernel version 2.2.0, ARP entries for whole subnets aren’t allowed by default.

6. Filter Entries with arp

Naturally, we can use the HWtype (-H) or Iface (-i) fields to filter the results. To do so, we supply a value to the appropriate arp flag:

  • -H for HWtype
  • -i for Iface

Let’s see how each one works:

$ arp -H ether
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.6.10             ether   FE-ED-1D-EA-F0-10   C                     eth0
192.168.6.1              ether   01-00-01-BE-E0-01   C                     eth0
$ arp -i eth1
arp: in 2 entries no match found.

In the latter case, there are no matches since we supply a missing interface or one with no ARP entries. The argument to the -H flag can be ether (Ethernet), arcnet, pronet, ax25, and netrom. Of course, each one has a different physical address format.

Alternatively, supplying an address to arp filters via the Address field:

$ arp 192.168.6.1
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.6.1              ether   01-00-01-BE-E0-01   C                     eth0

Here, we only see the entry we requested.

7. Handling ARP Entries with arp

Indeed, we can employ arp to modify the ARP table as well.

7.1. Add a Single ARP Entry

To manually include a simple entry in the ARP table, we use -s:

$ arp -s 192.168.6.99 10:99:90:BE:EF:99
$ arp 192.168.6.99
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.6.99             ether   10:99:90:BE:EF:99   CM                    eth0

Since we didn’t use the temp specifier at the end, the Flags field now includes the M permanent flag.

If we want to use our own MAC address, the -D flag, in combination with a local interface, can replace the HWaddr entry. This is useful for proxy ARP.

Of course, we can also specify the options we already discussed: HWtype via -H and Iface via -i.

Let’s see how those work when adding an entry:

$ arp -v -i eth0 -H ether -s 192.168.6.100 10:99:90:BE:E1:00
arp: SIOCSARP()
$ arp -v 192.168.6.100
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.6.100            ether   10:99:90:BE:E1:00   CM                  eth0
Entries: 3      Skipped: 2      Found: 1

Here, we specify the interface and hardware address type for the entry while also enabling verbose mode with -v. Verbosity shows any function calls (SIOCSARP()), as well as additional information when filtering the output.

7.2. Add Multiple ARP Entries

When many ARP entries are involved, a file might be an easier way to add them. First, we create the file in a predefined format:

HOST_OR_IP MAC_ADDRESS [temp,pub] [netmask NETMASK]

Next, we use the -f flag that tells arp to use /etc/ethers or a supplied file path as the source for each mapping.

Let’s see an example ARP table file /arps and how to apply it:

$ cat /arps
192.168.6.66 DE-AD-BE-EF-06-66 temp
192.168.6.10 FE-ED-1D-EA-F0-10
192.168.6.1  01-00-01-BE-E0-01
$ arp -v
Entries: 0      Skipped: 0      Found: 0 
$ arp -f /arps
$ arp -v
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.6.66             ether   DE-AD-BE-EF-06-66   C                      eth0
192.168.6.10             ether   FE-ED-1D-EA-F0-10   CM                     eth0
192.168.6.1              ether   01-00-01-BE-E0-01   CM                    eth0
Entries: 3      Skipped: 0      Found: 3

First, we show the contents of /arps. After that, we confirm that our ARP table is empty. Finally, we use the file to add all entries, one of which lacks the M flag since we added the temp option to it in the file.

7.3. Delete ARP Entry

To remove a mapping from the system ARP table, we use the -d flag with at least a hostname or IP address:

$ arp -d 192.168.6.100
? (192.168.6.100) at 10:99:90:be:e1:00 [ether] PERM on eth0
$ arp -d 192.168.6.100
$ arp 192.168.6.100
arp: in 2 entries no match found.

Here, we verify the ARP entry for host 192.168.6.100 exists before deleting it and confirming the result of our operation.

As with other operations, we can specify the interface for our entry with -i.

8. Summary

In this article, we thoroughly explored the arp command along with its options.

In conclusion, while the Linux kernel provides information from ARP in /proc, we can display and modify the ARP table in a more human-friendly manner using the arp tool.

Authors Bottom

If you have a few years of experience in the Linux ecosystem, and you’re interested in sharing that experience with the community, have a look at our Contribution Guidelines.

Comments are closed on this article!