From f466d757ec4ad6b06421d1da8ede4049e8abd0c7 Mon Sep 17 00:00:00 2001 From: Hugues Morisset Date: Sun, 10 Aug 2025 17:08:05 +0200 Subject: [PATCH 1/2] Add bswap_128() This greatly helps for IPv6 endianess convertion --- libc/intrin/bswap.c | 5 +++ libc/intrin/bswap.h | 8 +++-- libc/intrin/newbie.h | 84 +++++++++++++++++++++++++------------------- 3 files changed, 58 insertions(+), 39 deletions(-) diff --git a/libc/intrin/bswap.c b/libc/intrin/bswap.c index 1b6816922..66475aa52 100644 --- a/libc/intrin/bswap.c +++ b/libc/intrin/bswap.c @@ -45,3 +45,8 @@ uint64_t(bswap_64)(uint64_t x) { (0x00ff000000000000ull & x) >> 050 | (0xff00000000000000ull & x) >> 070; } + +uint128_t(bswap_128)(uint128_t x) { + return ((uint128_t)bswap_64((uint64_t)x) << 64) | + (uint128_t)bswap_64((uint64_t)(x >> 64)); +} diff --git a/libc/intrin/bswap.h b/libc/intrin/bswap.h index 05f55629d..a16095816 100644 --- a/libc/intrin/bswap.h +++ b/libc/intrin/bswap.h @@ -5,11 +5,13 @@ COSMOPOLITAN_C_START_ libcesque uint16_t bswap_16(uint16_t) pureconst; libcesque uint32_t bswap_32(uint32_t) pureconst; libcesque uint64_t bswap_64(uint64_t) pureconst; +libcesque uint128_t bswap_128(uint128_t) pureconst; #if defined(__GNUC__) && !defined(__STRICT_ANSI__) -#define bswap_16(x) __builtin_bswap16(x) -#define bswap_32(x) __builtin_bswap32(x) -#define bswap_64(x) __builtin_bswap64(x) +#define bswap_16(x) __builtin_bswap16(x) +#define bswap_32(x) __builtin_bswap32(x) +#define bswap_64(x) __builtin_bswap64(x) +#define bswap_128(x) __builtin_bswap128(x) #endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) */ COSMOPOLITAN_C_END_ diff --git a/libc/intrin/newbie.h b/libc/intrin/newbie.h index 46528b0ae..65fcd797d 100644 --- a/libc/intrin/newbie.h +++ b/libc/intrin/newbie.h @@ -8,43 +8,55 @@ #define PDP_ENDIAN __ORDER_PDP_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define htobe16(x) bswap_16(x) -#define be16toh(x) bswap_16(x) -#define betoh16(x) bswap_16(x) -#define htobe32(x) bswap_32(x) -#define be32toh(x) bswap_32(x) -#define betoh32(x) bswap_32(x) -#define htobe64(x) bswap_64(x) -#define be64toh(x) bswap_64(x) -#define betoh64(x) bswap_64(x) -#define htole16(x) (uint16_t)(x) -#define le16toh(x) (uint16_t)(x) -#define letoh16(x) (uint16_t)(x) -#define htole32(x) (uint32_t)(x) -#define le32toh(x) (uint32_t)(x) -#define letoh32(x) (uint32_t)(x) -#define htole64(x) (uint64_t)(x) -#define le64toh(x) (uint64_t)(x) -#define letoh64(x) (uint64_t)(x) +#define htobe16(x) bswap_16(x) +#define be16toh(x) bswap_16(x) +#define betoh16(x) bswap_16(x) +#define htobe32(x) bswap_32(x) +#define be32toh(x) bswap_32(x) +#define betoh32(x) bswap_32(x) +#define htobe64(x) bswap_64(x) +#define be64toh(x) bswap_64(x) +#define betoh64(x) bswap_64(x) +#define htobe128(x) bswap_128(x) +#define be128toh(x) bswap_128(x) +#define betoh128(x) bswap_128(x) +#define htole16(x) (uint16_t)(x) +#define le16toh(x) (uint16_t)(x) +#define letoh16(x) (uint16_t)(x) +#define htole32(x) (uint32_t)(x) +#define le32toh(x) (uint32_t)(x) +#define letoh32(x) (uint32_t)(x) +#define htole64(x) (uint64_t)(x) +#define le64toh(x) (uint64_t)(x) +#define letoh64(x) (uint64_t)(x) +#define htole128(x) (uint128_t)(x) +#define le128toh(x) (uint128_t)(x) +#define letoh128(x) (uint128_t)(x) #else -#define htobe16(x) (uint16_t)(x) -#define be16toh(x) (uint16_t)(x) -#define betoh16(x) (uint16_t)(x) -#define htobe32(x) (uint32_t)(x) -#define be32toh(x) (uint32_t)(x) -#define betoh32(x) (uint32_t)(x) -#define htobe64(x) (uint64_t)(x) -#define be64toh(x) (uint64_t)(x) -#define betoh64(x) (uint64_t)(x) -#define htole16(x) bswap_16(x) -#define le16toh(x) bswap_16(x) -#define letoh16(x) bswap_16(x) -#define htole32(x) bswap_32(x) -#define le32toh(x) bswap_32(x) -#define letoh32(x) bswap_32(x) -#define htole64(x) bswap_64(x) -#define le64toh(x) bswap_64(x) -#define letoh64(x) bswap_64(x) +#define htobe16(x) (uint16_t)(x) +#define be16toh(x) (uint16_t)(x) +#define betoh16(x) (uint16_t)(x) +#define htobe32(x) (uint32_t)(x) +#define be32toh(x) (uint32_t)(x) +#define betoh32(x) (uint32_t)(x) +#define htobe64(x) (uint64_t)(x) +#define be64toh(x) (uint64_t)(x) +#define betoh64(x) (uint64_t)(x) +#define htobe128(x) (uint128_t)(x) +#define be128toh(x) (uint128_t)(x) +#define betoh128(x) (uint128_t)(x) +#define htole16(x) bswap_16(x) +#define le16toh(x) bswap_16(x) +#define letoh16(x) bswap_16(x) +#define htole32(x) bswap_32(x) +#define le32toh(x) bswap_32(x) +#define letoh32(x) bswap_32(x) +#define htole64(x) bswap_64(x) +#define le64toh(x) bswap_64(x) +#define letoh64(x) bswap_64(x) +#define htole128(x) bswap_128(x) +#define le128toh(x) bswap_128(x) +#define letoh128(x) bswap_128(x) #endif #endif /* COSMOPOLITAN_LIBC_BITS_NEWBIE_H_ */ From abab1dec634ca928031369634f927efc8c88e20c Mon Sep 17 00:00:00 2001 From: Hugues Morisset Date: Wed, 13 Aug 2025 11:48:24 +0200 Subject: [PATCH 2/2] 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. --- libc/calls/ioctl.c | 38 ++++++++++++++++++++------- libc/sock/ifaddrs.c | 63 +++++++++++++++++++++++++++++++++------------ 2 files changed, 76 insertions(+), 25 deletions(-) diff --git a/libc/calls/ioctl.c b/libc/calls/ioctl.c index 5151c8524..5fee8669a 100644 --- a/libc/calls/ioctl.c +++ b/libc/calls/ioctl.c @@ -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 */ } diff --git a/libc/sock/ifaddrs.c b/libc/sock/ifaddrs.c index 9ea609e09..da0da5fa6 100644 --- a/libc/sock/ifaddrs.c +++ b/libc/sock/ifaddrs.c @@ -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; }