1

I've been doing some work with ARP/NDP and discovered that ndisc6 doesn't update the neighbor table (unlike arping).

arping example: 192.168.0.14 is configured on a device connected to a VLAN member so we are able to resolve its address.

root@ubuntu:/# ip neigh show 192.168.0.14
root@ubuntu:/# arping 192.168.0.14 -i Vlan1000
ARPING 192.168.0.14
60 bytes from ca:6c:4c:92:85:1c (192.168.0.14): index=0 time=38.088 msec
60 bytes from ca:6c:4c:92:85:1c (192.168.0.14): index=1 time=84.431 msec
60 bytes from ca:6c:4c:92:85:1c (192.168.0.14): index=2 time=119.154 msec
60 bytes from ca:6c:4c:92:85:1c (192.168.0.14): index=3 time=53.739 msec
^C
--- 192.168.0.14 statistics ---
4 packets transmitted, 4 packets received,   0% unanswered (0 extra)
rtt min/avg/max/std-dev = 38.088/73.853/119.154/31.015 ms
root@ubuntu:/# ip neigh show 192.168.0.14
192.168.0.14 dev Vlan1000 lladdr ca:6c:4c:92:85:1c REACHABLE

As you can see, arping is able to add a new entry into the kernel neighbor table as shown by ip neigh

For the analogous IPv6 example, ndisc6 cannot accomplish this:

root@ubuntu:/# ip neigh show fc02:1000::3
root@ubuntu:/# ndisc6 fc02:1000::3 Vlan1000
Soliciting fc02:1000::3 (fc02:1000::3) on Vlan1000...
Target link-layer address: CA:58:C2:5F:7C:02
 from fc02:1000::3
root@ubuntu:/# ip neigh show fc02:1000::3
root@ubuntu:/#

fc02:1000::3 is a similarly valid IP address configured on a device connected to a VLAN member. Despite receiving a valid neighbor advertisement message, the kernel neighbor table is not update as shown by the empty output from ip neigh

I have also found a reference to someone else seeing the same issue in the comments to this answer but without any explanation as to why.

  • 1
    Doing about the same, I can arping a server without causing it to add an entry to the neighbour table. So it looks on my system, the behavior is the same between arping and ndisc6. Maybe your system has a different setup (eg: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/Documentation/networking/ip-sysctl.rst?h=v5.15#n1608 ) – A.B Aug 04 '22 at 20:49
  • @A.B interesting, perhaps you have the iputils version of arping installed? It seems that version of arping has some limitations (https://en.wikipedia.org/wiki/Arping#:~:text=One%20is%20part%20of%20Linux%20iputils%20suite%2C%5B2%5D%20and%20cannot%20resolve%20MAC%20addresses%20to%20IP%20addresses) – theasianpianist Aug 06 '22 at 06:32
  • Using iputils and tested that the linked kernel toggle toggles the behavior: the emitted packet is not created by the kernel so doesn't count. The received reply is the only one considered by kernel, and depends on the toggle. But it's for IPv4 only. – A.B Aug 06 '22 at 07:44
  • Indeed older (*nix generic) arping has a side effect of creating the entry (as it's a generic multi-OS tool it's quite difficult to understand where it's done). But I don't call this a limitation. – A.B Aug 06 '22 at 08:39

1 Answers1

0

Kernel is the place where neighbor accounting is kept. So if an userspace tool generates an ARP (for IPv4) query or an NDP query (for IPv6) by using raw sockets (AF_PACKET for ARP and AF_INET6, SOCK_RAW for NDP) the reply doesn't matter to the kernel...

... except Linux kernel has an IPv4 ARP toggle to have it trust received ARP traffic even if not solicited. So the ARP reply to the arping command would then make kernel add an ARP entry, but there is no such toggle for IPv6.

I admit there is also a difference in behavior on Linux when using the generic multi-OS arping tool instead of iputils-arping: in the former case the ARP entry is created nevertheless "for some reason".


Anyway, this doesn't happen with IPv6 but it's quite easy to work around: create the entry first, update it later with ndisc6 since the kernel will have an entry and will process the reply. The best state type to use is stale: considered valid but unreliable.

To avoid some unnecessary disruption, ip neigh change is tried first without providing a MAC address so a former probably valid entry is kept, and if it fails because there was no entry or it was possibly in failed state (for these cases the MAC address is mandatory), use ip neigh replace (rather than ip neigh add to avoid a race condition between the two commands) and provide a fake local MAC address.

For OP's case:

ip -6 neigh change fc02:1000::3 nud stale dev Vlan1000 ||
    ip -6 neigh replace fc02:1000::3 lladdr 02:00:00:00:00:00 nud stale dev Vlan1000
ndisc6 fc02:1000::3 Vlan1000

Now this should give a correct entry:

ip -6 neigh show fc02:1000::3
A.B
  • 9,037
  • 2
  • 19
  • 37
  • "... replace ... nud none ..." could have been an other choice but it's not clear if in some cases it could be deleted before ndisc6 is run. – A.B Aug 06 '22 at 09:44