Skip to content

Commit abab1de

Browse files
committed
Add IPv6 support to getifaddrs() on Bsd
Tested on x86_64 MacOs OpenBsd FreeBsd and NetBsd. There is extra ioctl calls on the ipv6 bsd path because SIOCGIFCONF doesn't returns the addr flag or the netmask. Luckily all bsd systems share the same values for SIOCGIFAFLAG_IN6 and SIOCGIFNETMASK_IN6.
1 parent f466d75 commit abab1de

File tree

2 files changed

+76
-25
lines changed

2 files changed

+76
-25
lines changed

libc/calls/ioctl.c

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "libc/runtime/runtime.h"
4444
#include "libc/runtime/stack.h"
4545
#include "libc/serialize.h"
46+
#include "libc/sock/in.h"
4647
#include "libc/sock/internal.h"
4748
#include "libc/sock/struct/ifconf.h"
4849
#include "libc/sock/struct/ifreq.h"
@@ -533,15 +534,34 @@ static int ioctl_siocgifconf_sysv(int fd, struct ifconf *ifc) {
533534
for (p = b, e = p + MIN(bufMax, READ32LE(ifcBsd)); p + 16 + 16 <= e;
534535
p += IsBsd() ? 16 + MAX(16, p[16] & 255) : 40) {
535536
fam = p[IsBsd() ? 17 : 16] & 255;
536-
if (fam != AF_INET)
537-
continue;
538-
ip = READ32BE(p + 20);
539-
bzero(req, sizeof(*req));
540-
memcpy(req->ifr_name, p, 16);
541-
memcpy(&req->ifr_addr, p + 16, 16);
542-
req->ifr_addr.sa_family = fam;
543-
((struct sockaddr_in *)&req->ifr_addr)->sin_addr.s_addr = htonl(ip);
544-
++req;
537+
if (fam == AF_INET) {
538+
ip = READ32BE(p + 20);
539+
bzero(req, sizeof(*req));
540+
memcpy(req->ifr_name, p, 16);
541+
memcpy(&req->ifr_addr, p + 16, 16);
542+
req->ifr_addr.sa_family = fam;
543+
((struct sockaddr_in *)&req->ifr_addr)->sin_addr.s_addr = htonl(ip);
544+
++req;
545+
} else if (fam == AF_INET6) {
546+
// Only BSD systems returns AF_INET6 addresses with SIOCGIFCONF
547+
// BSD don't return flags or prefix length, need to get them later
548+
bzero(req, sizeof(*req));
549+
memcpy(req->ifr_name, p, 16);
550+
void *addr6 = p + 24;
551+
if (IN6_IS_ADDR_LINKLOCAL(addr6)) {
552+
// link-local bsd special https://stackoverflow.com/q/5888359/2838914
553+
req->ifr6_ifindex = ntohs(*((uint16_t *)(p + 26)));
554+
*((uint16_t *)(p + 26)) = 0x0;
555+
req->ifr6_scope = 0x20; // link
556+
} else if (IN6_IS_ADDR_SITELOCAL(addr6)) {
557+
req->ifr6_scope = 0x40; // site
558+
} else if (IN6_IS_ADDR_LOOPBACK(addr6)) {
559+
req->ifr6_scope = 0x10; // host
560+
}
561+
memcpy(&req->ifr6_addr, addr6, 16);
562+
req->ifr_addr.sa_family = fam;
563+
++req;
564+
}
545565
}
546566
ifc->ifc_len = (char *)req - ifc->ifc_buf; /* Adjust len */
547567
}

libc/sock/ifaddrs.c

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "libc/calls/calls.h"
2121
#include "libc/calls/syscall-sysv.internal.h"
2222
#include "libc/dce.h"
23+
#include "libc/intrin/newbie.h"
2324
#include "libc/limits.h"
2425
#include "libc/mem/mem.h"
2526
#include "libc/sock/sock.h"
@@ -34,6 +35,9 @@
3435
#include "libc/sysv/consts/sio.h"
3536
#include "libc/sysv/consts/sock.h"
3637

38+
#define SIOCGIFAFLAG_IN6 3240126793 // bsd
39+
#define SIOCGIFNETMASK_IN6 3240126757 // bsd
40+
3741
struct IfAddr {
3842
struct ifaddrs ifaddrs;
3943
char name[IFNAMSIZ];
@@ -142,27 +146,27 @@ static int getifaddrs_linux_ip6(struct ifconf *conf) {
142146
* @see tool/viz/getifaddrs.c for example code
143147
*/
144148
int getifaddrs(struct ifaddrs **out_ifpp) {
145-
// printf("%d\n", sizeof(struct ifreq));
146-
int rc = -1;
147-
int fd;
149+
int rc = 0;
150+
int fd, fd6 = -1;
148151
if ((fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) != -1) {
149152
char *data;
150153
size_t size;
151154
if ((data = malloc((size = 16384)))) {
152-
struct ifconf conf;
155+
struct ifconf conf, confl6;
153156
conf.ifc_buf = data;
154157
conf.ifc_len = size;
155158
if (!ioctl(fd, SIOCGIFCONF, &conf)) {
159+
confl6.ifc_buf = data + conf.ifc_len;
160+
confl6.ifc_len = size - conf.ifc_len;
156161
if (IsLinux()) {
157-
struct ifconf confl6;
158-
confl6.ifc_buf = data + conf.ifc_len;
159-
confl6.ifc_len = size - conf.ifc_len;
160-
if ((rc = getifaddrs_linux_ip6(&confl6)))
161-
return rc;
162-
conf.ifc_len += confl6.ifc_len;
162+
rc = getifaddrs_linux_ip6(&confl6);
163163
}
164+
if (rc)
165+
return rc;
166+
conf.ifc_len += confl6.ifc_len;
164167

165168
struct ifaddrs *res = 0;
169+
rc = -1;
166170
for (struct ifreq *ifr = (struct ifreq *)data;
167171
(char *)ifr < data + conf.ifc_len; ++ifr) {
168172
uint16_t family = ifr->ifr_addr.sa_family;
@@ -207,9 +211,10 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
207211
addr6->ifaddrs.ifa_broadaddr = (struct sockaddr *)&addr6->bstaddr;
208212
addr6->ifaddrs.ifa_data = (void *)&addr6->info;
209213

210-
memcpy(&addr6->name, &ifr->ifr_name, IFNAMSIZ);
211-
addr6->info.addr_flags = ifr->ifr6_flags;
212214
addr6->info.addr_scope = ifr->ifr6_scope;
215+
addr6->info.addr_flags = ifr->ifr6_flags;
216+
217+
memcpy(&addr6->name, &ifr->ifr_name, IFNAMSIZ);
213218

214219
addr6->addr.sin6_family = AF_INET6;
215220
addr6->addr.sin6_port = 0;
@@ -222,10 +227,33 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
222227
addr6->netmask.sin6_port = 0;
223228
addr6->netmask.sin6_flowinfo = 0;
224229
addr6->addr.sin6_scope_id = ifr->ifr6_ifindex;
225-
memcpy(&addr6->netmask.sin6_addr, &ifr->ifr6_addr,
226-
sizeof(struct in6_addr));
227-
*((uint128_t *)&(addr6->netmask.sin6_addr)) &=
228-
(UINT128_MAX >> ifr->ifr6_prefixlen);
230+
231+
if (IsBsd()) { // on bsd we miss prefixlen and addr flags
232+
if (fd6 == -1) {
233+
fd6 = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
234+
}
235+
uint8_t in6req[288]; // BSD struct in6_ifreq
236+
bzero(&in6req, sizeof(in6req));
237+
memcpy(&in6req, &ifr->ifr_name, IFNAMSIZ);
238+
in6req[16] = 28; // sin6_len sizeof(struct sockaddr_in6_bsd)
239+
in6req[17] = AF_INET6; // sin6_family
240+
memcpy(&in6req[24], &addr6->addr.sin6_addr,
241+
sizeof(struct in6_addr)); // sin6_addr
242+
if (!ioctl(fd6, SIOCGIFAFLAG_IN6, &in6req)) {
243+
addr6->info.addr_flags =
244+
*(int *)(&in6req[16]); // ifru_flags6
245+
}
246+
in6req[16] = 28; // sin6_len
247+
in6req[17] = AF_INET6; // sin6_family
248+
if (!ioctl(fd6, SIOCGIFNETMASK_IN6, &in6req)) {
249+
memcpy(&(addr6->netmask.sin6_addr), &in6req[24],
250+
sizeof(struct in6_addr));
251+
}
252+
} else {
253+
int prefixlen = ifr->ifr6_prefixlen;
254+
*((uint128_t *)&(addr6->netmask.sin6_addr)) = htobe128(
255+
prefixlen == 0 ? 0 : (UINT128_MAX << (128 - prefixlen)));
256+
}
229257

230258
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
231259
addr6->ifaddrs.ifa_flags = ifr->ifr_flags;
@@ -243,6 +271,9 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
243271
free(data);
244272
}
245273
close(fd);
274+
if (fd6 != -1) {
275+
close(fd6);
276+
}
246277
}
247278
return rc;
248279
}

0 commit comments

Comments
 (0)