Linux: What causes static ARP entries to flush on link down

This is a question about a difference in networking behavior between Debian Buster (kernel 4.19.0-18) and Debian Bullseye (kernel 5.10.0-9). On Buster, proxy arp entries survive if the link on the interface goes down. On Bullseye they don't.

Situation: create a static proxy ARP entry on a specific interface:

ip neigh add proxy 1.2.3.5 dev eth0

I can verify that the entry exists:

ip neigh show proxy

Now, if the link on that interface goes down and comes back up again, on Buster the proxy arp entries are still there, on Bullseye they are gone.

I would like to know the reason behind this difference. Is it a kernel feature? Is it udev that does this? Is there a sysctl setting governing this?

Could someone point me in the right direction? If you know how to create static ARP entries on Bullseye / kernel 5.10 that would be even better.

Note that I'm using sysvinit, not systemd. Thanks!


Here's the history of the changes related to this issue:

  • 2018-10-12 09:47:39 -0700

    net: Evict neighbor entries on carrier down

    is committed on netdev -next tree: this is responsible of removing neighbor entries when a link gets a carrier down, with a rationale in the commit message.

  • 2018-10-22 07:47:45 +0100

    kernel v4.19 is released.

    This still doesn't include the commit from the netdev -next branch.

  • 2018-10-24 06:47:44 +0100

    Merge of Davem's pull request including the previous neighbor change is merged in linux master thus to be included in linux v4.20 once released.

fast forward...

  • 2021-11-01 19:57:14 -0700 + 2021-11-01 19:57:17 -0700

    net: arp: introduce arp_evict_nocarrier sysctl parameter

    This change introduces a new sysctl parameter, arp_evict_nocarrier. When set (default) the ARP cache will be cleared on a NOCARRIER event. This new option has been defaulted to '1' which maintains existing behavior.
    [...]

So you likely will have to wait for kernel >= 5.16 in bullseye-backports to get the old behavior back by doing then for example:

sysctl -w net.ipv4.conf.eth0.arp_evict_nocarrier=0

or wait that some later adjustment makes proxy entries behave like permanent entries (which get specific code to not be affected on a carrier down event).


About the second question "how to create static ARP entries", here's the method to add a static entry for 192.0.2.2 with MAC address 12:34:56:78:9a:bc on interface eth0, using the replace keyword instead of the add keyword which could randomly return an RTNETLINK answers: File exists error (or of change which would suffer from the opposite):

ip neigh replace 192.0.2.2 dev eth0 lladdr 12:34:56:78:9a:bc nud permanent