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.
This commit is contained in:
Sagie D. authored and Simon Kelley committed 2026-05-09 21:44:38 +01:00
1 parent 4bcc9650fa
commit 0637bcf010
1 file changed
+40 -11
+40 -11
View File
@@ -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__) */