Skip to content

Commit 4f28f99

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 4f28f99

File tree

3 files changed

+97
-36
lines changed

3 files changed

+97
-36
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: 51 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,13 @@
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+
41+
struct in6_ifreq {
42+
char a[288];
43+
};
44+
3745
struct IfAddr {
3846
struct ifaddrs ifaddrs;
3947
char name[IFNAMSIZ];
@@ -142,27 +150,27 @@ static int getifaddrs_linux_ip6(struct ifconf *conf) {
142150
* @see tool/viz/getifaddrs.c for example code
143151
*/
144152
int getifaddrs(struct ifaddrs **out_ifpp) {
145-
// printf("%d\n", sizeof(struct ifreq));
146-
int rc = -1;
147-
int fd;
153+
int rc = 0;
154+
int fd, fd6 = -1;
148155
if ((fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) != -1) {
149156
char *data;
150157
size_t size;
151158
if ((data = malloc((size = 16384)))) {
152-
struct ifconf conf;
159+
struct ifconf conf, confl6;
153160
conf.ifc_buf = data;
154161
conf.ifc_len = size;
155162
if (!ioctl(fd, SIOCGIFCONF, &conf)) {
163+
confl6.ifc_buf = data + conf.ifc_len;
164+
confl6.ifc_len = size - conf.ifc_len;
156165
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;
166+
rc = getifaddrs_linux_ip6(&confl6);
163167
}
168+
if (rc)
169+
return rc;
170+
conf.ifc_len += confl6.ifc_len;
164171

165172
struct ifaddrs *res = 0;
173+
rc = -1;
166174
for (struct ifreq *ifr = (struct ifreq *)data;
167175
(char *)ifr < data + conf.ifc_len; ++ifr) {
168176
uint16_t family = ifr->ifr_addr.sa_family;
@@ -207,9 +215,10 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
207215
addr6->ifaddrs.ifa_broadaddr = (struct sockaddr *)&addr6->bstaddr;
208216
addr6->ifaddrs.ifa_data = (void *)&addr6->info;
209217

210-
memcpy(&addr6->name, &ifr->ifr_name, IFNAMSIZ);
211-
addr6->info.addr_flags = ifr->ifr6_flags;
212218
addr6->info.addr_scope = ifr->ifr6_scope;
219+
addr6->info.addr_flags = ifr->ifr6_flags;
220+
221+
memcpy(&addr6->name, &ifr->ifr_name, IFNAMSIZ);
213222

214223
addr6->addr.sin6_family = AF_INET6;
215224
addr6->addr.sin6_port = 0;
@@ -222,10 +231,33 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
222231
addr6->netmask.sin6_port = 0;
223232
addr6->netmask.sin6_flowinfo = 0;
224233
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);
234+
235+
if (IsBsd()) { // on bsd we miss prefixlen and addr flags
236+
if (fd6 == -1) {
237+
fd6 = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
238+
}
239+
uint8_t in6req[288]; // BSD struct in6_ifreq
240+
bzero(&in6req, sizeof(in6req));
241+
memcpy(&in6req, &ifr->ifr_name, IFNAMSIZ);
242+
in6req[16] = 28; // sin6_len sizeof(struct sockaddr_in6_bsd)
243+
in6req[17] = AF_INET6; // sin6_family
244+
memcpy(&in6req[24], &addr6->addr.sin6_addr,
245+
sizeof(struct in6_addr)); // sin6_addr
246+
if (!ioctl(fd6, SIOCGIFAFLAG_IN6, &in6req)) {
247+
addr6->info.addr_flags =
248+
*(int *)(&in6req[16]); // ifru_flags6
249+
}
250+
in6req[16] = 28; // sin6_len
251+
in6req[17] = AF_INET6; // sin6_family
252+
if (!ioctl(fd6, SIOCGIFNETMASK_IN6, &in6req)) {
253+
memcpy(&(addr6->netmask.sin6_addr), &in6req[24],
254+
sizeof(struct in6_addr));
255+
}
256+
} else {
257+
int prefixlen = ifr->ifr6_prefixlen;
258+
*((uint128_t *)&(addr6->netmask.sin6_addr)) = htobe128(
259+
prefixlen == 0 ? 0 : (UINT128_MAX << (128 - prefixlen)));
260+
}
229261

230262
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
231263
addr6->ifaddrs.ifa_flags = ifr->ifr_flags;
@@ -243,6 +275,9 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
243275
free(data);
244276
}
245277
close(fd);
278+
if (fd6 != -1) {
279+
close(fd6);
280+
}
246281
}
247282
return rc;
248283
}

tool/viz/getifaddrs.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,23 @@
2929
#include "libc/sysv/consts/iff.h"
3030

3131
/* example output:
32-
33-
eth0
34-
addr: 10.10.10.237
35-
netmask: 255.255.255.0
36-
broadcast: 10.10.10.255
37-
flags: IFF_UP IFF_BROADCAST IFF_MULTICAST IFF_RUNNING
38-
39-
lo
40-
addr: 127.0.0.1
41-
netmask: 255.0.0.0
42-
flags: IFF_UP IFF_LOOPBACK IFF_RUNNING */
32+
vnet0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>
33+
inet6 fe80::fc54:ff:fefe:70d prefixlen 64 flags<128> class 0x20<link> ifidx 38
34+
vnet1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>
35+
inet6 fe80::fc54:ff:fe6a:6545 prefixlen 64 flags<128> class 0x20<link> ifidx 44
36+
enp51s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>
37+
inet6 fe80::ce3f:bd13:34ea:c170 prefixlen 64 flags<128> class 0x20<link> ifidx 43
38+
inet 192.168.1.2 netmask 255.255.255.0 broadcast 192.168.1.255
39+
wlp0s20f3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>
40+
inet6 fe80::9e1f:6462:15e2:2bf7 prefixlen 64 flags<128> class 0x20<link> ifidx 3
41+
inet 192.168.1.95 netmask 255.255.255.0 broadcast 192.168.1.255
42+
lo: flags=73<UP,RUNNING,LOOPBACK>
43+
inet6 ::1 prefixlen 128 flags<128> class 0x10<host>
44+
inet 127.0.0.1 netmask 255.0.0.0
45+
virbr1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>
46+
inet 192.168.121.1 netmask 255.255.255.0 broadcast 192.168.121.255
47+
virbr0: flags=4099<UP,BROADCAST,MULTICAST>
48+
inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255 */
4349

4450
const char *sockaddr2str(const struct sockaddr *sa, char *buf, size_t size) {
4551
if (sa->sa_family == AF_INET) {

0 commit comments

Comments
 (0)