From 0637bcf010e402e8b70bf9d48dfe53bce17531d8 Mon Sep 17 00:00:00 2001 From: "Sagie D." Date: Sat, 9 May 2026 21:44:38 +0100 Subject: [PATCH] Add support for IPV6 when reading ARP/ND table on *BSD. The arp_enumerate() function is extended into a per-family helper arp_enumerate_family(), called sequentially for AF_INET and AF_INET6, allowing the NDP neighbour cache to be enumerated alongside the ARP table. An empty table for either family is treated as vacuous success rather than an error. Both tables are acquired in *BSD via PF_ROUTE sysctl calls that return raw kernel structures; consequently, for IPv6, the sockaddr_in6 peer address extracted from them has an embedded link-local scope ID. This ID is extracted into sin6_scope_id, and bytes 2-3 of the address are then cleared, per the KAME API contract. Signed-off-by: Sagie D. --- src/bpf.c | 51 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/src/bpf.c b/src/bpf.c index 658a34a..01e4fc9 100644 --- a/src/bpf.c +++ b/src/bpf.c @@ -47,21 +47,20 @@ static union all_addr del_addr; #if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) -static int arp_enumerate(void *parm, callback_t callback) +static int arp_enumerate_family(int family, void *parm, callback_t callback) { int mib[6]; size_t needed; char *next; struct rt_msghdr *rtm; - struct sockaddr_inarp *sin2; struct sockaddr_dl *sdl; - static struct iovec buff; + static struct iovec buff = { NULL, 0 }; int rc; mib[0] = CTL_NET; mib[1] = PF_ROUTE; mib[2] = 0; - mib[3] = AF_INET; + mib[3] = family; mib[4] = NET_RT_FLAGS; #ifdef RTF_LLINFO mib[5] = RTF_LLINFO; @@ -69,8 +68,8 @@ static int arp_enumerate(void *parm, callback_t callback) mib[5] = 0; #endif if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0) - return 0; - + return 1; /* not a failure: unsupported or empty table */ + while (1) { if (!expand_buf(&buff, needed)) @@ -80,20 +79,50 @@ static int arp_enumerate(void *parm, callback_t callback) break; needed += needed / 8; } + if (rc == -1) return 0; for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen) { rtm = (struct rt_msghdr *)next; - sin2 = (struct sockaddr_inarp *)(rtm + 1); - sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); - if (!callback.af_unspec(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm)) - return 0; + if (family == AF_INET) + { + struct sockaddr_inarp *sin2 = (struct sockaddr_inarp *)(rtm + 1); + sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2)); + if (!callback.af_unspec(AF_INET, &sin2->sin_addr, + LLADDR(sdl), sdl->sdl_alen, parm)) + return 0; + } + else + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)(rtm + 1); + sdl = (struct sockaddr_dl *)((char *)sin6 + SA_SIZE(sin6)); + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) + { + /* PF_ROUTE sysctl returns raw kernel structures with the interface + index embedded in bytes 2-3 of link-local addresses. Extract it + into sin6_scope_id per the KAME API contract before clearing. */ + sin6->sin6_scope_id = + ((uint32_t)(sin6->sin6_addr.s6_addr[2]) << 8) | sin6->sin6_addr.s6_addr[3]; + sin6->sin6_addr.s6_addr[2] = 0; + sin6->sin6_addr.s6_addr[3] = 0; + } + if (!callback.af_unspec(AF_INET6, &sin6->sin6_addr, + LLADDR(sdl), sdl->sdl_alen, parm)) + return 0; + } } - + return 1; } + +static int arp_enumerate(void *parm, callback_t callback) +{ + if (!arp_enumerate_family(AF_INET, parm, callback)) + return 0; + return arp_enumerate_family(AF_INET6, parm, callback); +} #endif /* defined(HAVE_BSD_NETWORK) && !defined(__APPLE__) */