mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-21 09:02:17 +00:00
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.
This commit is contained in:
parent
f466d757ec
commit
abab1dec63
2 changed files with 76 additions and 25 deletions
|
@ -43,6 +43,7 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/serialize.h"
|
||||
#include "libc/sock/in.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/struct/ifconf.h"
|
||||
#include "libc/sock/struct/ifreq.h"
|
||||
|
@ -533,15 +534,34 @@ static int ioctl_siocgifconf_sysv(int fd, struct ifconf *ifc) {
|
|||
for (p = b, e = p + MIN(bufMax, READ32LE(ifcBsd)); p + 16 + 16 <= e;
|
||||
p += IsBsd() ? 16 + MAX(16, p[16] & 255) : 40) {
|
||||
fam = p[IsBsd() ? 17 : 16] & 255;
|
||||
if (fam != AF_INET)
|
||||
continue;
|
||||
ip = READ32BE(p + 20);
|
||||
bzero(req, sizeof(*req));
|
||||
memcpy(req->ifr_name, p, 16);
|
||||
memcpy(&req->ifr_addr, p + 16, 16);
|
||||
req->ifr_addr.sa_family = fam;
|
||||
((struct sockaddr_in *)&req->ifr_addr)->sin_addr.s_addr = htonl(ip);
|
||||
++req;
|
||||
if (fam == AF_INET) {
|
||||
ip = READ32BE(p + 20);
|
||||
bzero(req, sizeof(*req));
|
||||
memcpy(req->ifr_name, p, 16);
|
||||
memcpy(&req->ifr_addr, p + 16, 16);
|
||||
req->ifr_addr.sa_family = fam;
|
||||
((struct sockaddr_in *)&req->ifr_addr)->sin_addr.s_addr = htonl(ip);
|
||||
++req;
|
||||
} else if (fam == AF_INET6) {
|
||||
// Only BSD systems returns AF_INET6 addresses with SIOCGIFCONF
|
||||
// BSD don't return flags or prefix length, need to get them later
|
||||
bzero(req, sizeof(*req));
|
||||
memcpy(req->ifr_name, p, 16);
|
||||
void *addr6 = p + 24;
|
||||
if (IN6_IS_ADDR_LINKLOCAL(addr6)) {
|
||||
// link-local bsd special https://stackoverflow.com/q/5888359/2838914
|
||||
req->ifr6_ifindex = ntohs(*((uint16_t *)(p + 26)));
|
||||
*((uint16_t *)(p + 26)) = 0x0;
|
||||
req->ifr6_scope = 0x20; // link
|
||||
} else if (IN6_IS_ADDR_SITELOCAL(addr6)) {
|
||||
req->ifr6_scope = 0x40; // site
|
||||
} else if (IN6_IS_ADDR_LOOPBACK(addr6)) {
|
||||
req->ifr6_scope = 0x10; // host
|
||||
}
|
||||
memcpy(&req->ifr6_addr, addr6, 16);
|
||||
req->ifr_addr.sa_family = fam;
|
||||
++req;
|
||||
}
|
||||
}
|
||||
ifc->ifc_len = (char *)req - ifc->ifc_buf; /* Adjust len */
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/newbie.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/sock/sock.h"
|
||||
|
@ -34,6 +35,9 @@
|
|||
#include "libc/sysv/consts/sio.h"
|
||||
#include "libc/sysv/consts/sock.h"
|
||||
|
||||
#define SIOCGIFAFLAG_IN6 3240126793 // bsd
|
||||
#define SIOCGIFNETMASK_IN6 3240126757 // bsd
|
||||
|
||||
struct IfAddr {
|
||||
struct ifaddrs ifaddrs;
|
||||
char name[IFNAMSIZ];
|
||||
|
@ -142,27 +146,27 @@ static int getifaddrs_linux_ip6(struct ifconf *conf) {
|
|||
* @see tool/viz/getifaddrs.c for example code
|
||||
*/
|
||||
int getifaddrs(struct ifaddrs **out_ifpp) {
|
||||
// printf("%d\n", sizeof(struct ifreq));
|
||||
int rc = -1;
|
||||
int fd;
|
||||
int rc = 0;
|
||||
int fd, fd6 = -1;
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)) != -1) {
|
||||
char *data;
|
||||
size_t size;
|
||||
if ((data = malloc((size = 16384)))) {
|
||||
struct ifconf conf;
|
||||
struct ifconf conf, confl6;
|
||||
conf.ifc_buf = data;
|
||||
conf.ifc_len = size;
|
||||
if (!ioctl(fd, SIOCGIFCONF, &conf)) {
|
||||
confl6.ifc_buf = data + conf.ifc_len;
|
||||
confl6.ifc_len = size - conf.ifc_len;
|
||||
if (IsLinux()) {
|
||||
struct ifconf confl6;
|
||||
confl6.ifc_buf = data + conf.ifc_len;
|
||||
confl6.ifc_len = size - conf.ifc_len;
|
||||
if ((rc = getifaddrs_linux_ip6(&confl6)))
|
||||
return rc;
|
||||
conf.ifc_len += confl6.ifc_len;
|
||||
rc = getifaddrs_linux_ip6(&confl6);
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
conf.ifc_len += confl6.ifc_len;
|
||||
|
||||
struct ifaddrs *res = 0;
|
||||
rc = -1;
|
||||
for (struct ifreq *ifr = (struct ifreq *)data;
|
||||
(char *)ifr < data + conf.ifc_len; ++ifr) {
|
||||
uint16_t family = ifr->ifr_addr.sa_family;
|
||||
|
@ -207,9 +211,10 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
|
|||
addr6->ifaddrs.ifa_broadaddr = (struct sockaddr *)&addr6->bstaddr;
|
||||
addr6->ifaddrs.ifa_data = (void *)&addr6->info;
|
||||
|
||||
memcpy(&addr6->name, &ifr->ifr_name, IFNAMSIZ);
|
||||
addr6->info.addr_flags = ifr->ifr6_flags;
|
||||
addr6->info.addr_scope = ifr->ifr6_scope;
|
||||
addr6->info.addr_flags = ifr->ifr6_flags;
|
||||
|
||||
memcpy(&addr6->name, &ifr->ifr_name, IFNAMSIZ);
|
||||
|
||||
addr6->addr.sin6_family = AF_INET6;
|
||||
addr6->addr.sin6_port = 0;
|
||||
|
@ -222,10 +227,33 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
|
|||
addr6->netmask.sin6_port = 0;
|
||||
addr6->netmask.sin6_flowinfo = 0;
|
||||
addr6->addr.sin6_scope_id = ifr->ifr6_ifindex;
|
||||
memcpy(&addr6->netmask.sin6_addr, &ifr->ifr6_addr,
|
||||
sizeof(struct in6_addr));
|
||||
*((uint128_t *)&(addr6->netmask.sin6_addr)) &=
|
||||
(UINT128_MAX >> ifr->ifr6_prefixlen);
|
||||
|
||||
if (IsBsd()) { // on bsd we miss prefixlen and addr flags
|
||||
if (fd6 == -1) {
|
||||
fd6 = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
||||
}
|
||||
uint8_t in6req[288]; // BSD struct in6_ifreq
|
||||
bzero(&in6req, sizeof(in6req));
|
||||
memcpy(&in6req, &ifr->ifr_name, IFNAMSIZ);
|
||||
in6req[16] = 28; // sin6_len sizeof(struct sockaddr_in6_bsd)
|
||||
in6req[17] = AF_INET6; // sin6_family
|
||||
memcpy(&in6req[24], &addr6->addr.sin6_addr,
|
||||
sizeof(struct in6_addr)); // sin6_addr
|
||||
if (!ioctl(fd6, SIOCGIFAFLAG_IN6, &in6req)) {
|
||||
addr6->info.addr_flags =
|
||||
*(int *)(&in6req[16]); // ifru_flags6
|
||||
}
|
||||
in6req[16] = 28; // sin6_len
|
||||
in6req[17] = AF_INET6; // sin6_family
|
||||
if (!ioctl(fd6, SIOCGIFNETMASK_IN6, &in6req)) {
|
||||
memcpy(&(addr6->netmask.sin6_addr), &in6req[24],
|
||||
sizeof(struct in6_addr));
|
||||
}
|
||||
} else {
|
||||
int prefixlen = ifr->ifr6_prefixlen;
|
||||
*((uint128_t *)&(addr6->netmask.sin6_addr)) = htobe128(
|
||||
prefixlen == 0 ? 0 : (UINT128_MAX << (128 - prefixlen)));
|
||||
}
|
||||
|
||||
if (!ioctl(fd, SIOCGIFFLAGS, ifr)) {
|
||||
addr6->ifaddrs.ifa_flags = ifr->ifr_flags;
|
||||
|
@ -243,6 +271,9 @@ int getifaddrs(struct ifaddrs **out_ifpp) {
|
|||
free(data);
|
||||
}
|
||||
close(fd);
|
||||
if (fd6 != -1) {
|
||||
close(fd6);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue