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/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_ */ 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; }