Add IPv6 support to getifaddrs() on Windows

We now ask both IPv4 and IPv6 addresses to GetAdaptersAddresses

Ipv6 address flags fill up is left as a todo because
I'm not sure how to get the same information as of Linux

I've tested on a x86_64 Windows machine and I get the same
informations as of ipconfig.exe
This commit is contained in:
Hugues Morisset 2025-08-10 16:52:21 +02:00
parent f1e83d5240
commit 71cb861e74
No known key found for this signature in database

View file

@ -43,6 +43,7 @@
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h" #include "libc/runtime/stack.h"
#include "libc/serialize.h" #include "libc/serialize.h"
#include "libc/sock/in.h"
#include "libc/sock/internal.h" #include "libc/sock/internal.h"
#include "libc/sock/struct/ifconf.h" #include "libc/sock/struct/ifconf.h"
#include "libc/sock/struct/ifreq.h" #include "libc/sock/struct/ifreq.h"
@ -68,6 +69,11 @@ static struct HostAdapterInfoNode {
struct sockaddr unicast; struct sockaddr unicast;
struct sockaddr netmask; struct sockaddr netmask;
struct sockaddr broadcast; struct sockaddr broadcast;
uint16_t ifindex6; /* Interface index */
uint16_t flags6; /* Flags */
uint8_t scope6; /* Addr scope */
uint8_t prefixlen6; /* Prefix length */
struct in6_addr addr6;
short flags; short flags;
} *__hostInfo; } *__hostInfo;
@ -153,7 +159,8 @@ static textwindows struct HostAdapterInfoNode *findAdapterByName(
const char *name) { const char *name) {
struct HostAdapterInfoNode *node = __hostInfo; struct HostAdapterInfoNode *node = __hostInfo;
while (node) { while (node) {
if (!strncmp(name, node->name, IFNAMSIZ)) { if (!strncmp(name, node->name, IFNAMSIZ) &&
node->unicast.sa_family == AF_INET) {
return node; return node;
} }
node = node->next; node = node->next;
@ -178,7 +185,7 @@ static textwindows struct HostAdapterInfoNode *appendHostInfo(
*ptrUA, /* Ptr to ptr to unicast address list node */ *ptrUA, /* Ptr to ptr to unicast address list node */
struct NtIpAdapterPrefix * struct NtIpAdapterPrefix *
*ptrAP, /* Ptr to ptr to Adapter prefix list node */ *ptrAP, /* Ptr to ptr to Adapter prefix list node */
int count) { /* count is used to create a unique name in case of alias */ int *ip4cnt) { /* count is used to create a unique name in case of alias */
struct HostAdapterInfoNode *temp; struct HostAdapterInfoNode *temp;
struct HostAdapterInfoNode *node; struct HostAdapterInfoNode *node;
@ -193,18 +200,17 @@ static textwindows struct HostAdapterInfoNode *appendHostInfo(
memcpy(node->name, baseName, IFNAMSIZ); memcpy(node->name, baseName, IFNAMSIZ);
/* Are there more than a single unicast address ? */ /* Are there more than a single IPv4 unicast address ? */
if (count > 0 || ((*ptrUA)->Next != NULL)) { if (*ip4cnt > 0) {
/* Yes, compose it using <baseName>:<count> */ /* Yes, compose it using <baseName>:<ip4cnt> */
size_t nameLen = strlen(node->name); size_t nameLen = strlen(node->name);
if (nameLen + 2 > IFNAMSIZ - 2) { if (nameLen + 2 > IFNAMSIZ - 2) {
/* Appending the ":x" will exceed the size, need to chop the end */ /* Appending the ":x" will exceed the size, need to chop the end */
nameLen -= 2; nameLen -= 2;
} }
node->name[nameLen - 2] = ':'; node->name[nameLen - 2] = ':';
node->name[nameLen - 1] = '0' + count; node->name[nameLen - 1] = '0' + *ip4cnt;
node->name[nameLen] = '\0'; node->name[nameLen] = '\0';
}
/* Is there a name clash with other interfaces? */ /* Is there a name clash with other interfaces? */
for (attemptNum = 0; attemptNum < MAX_NAME_CLASH; ++attemptNum) { for (attemptNum = 0; attemptNum < MAX_NAME_CLASH; ++attemptNum) {
@ -232,6 +238,7 @@ static textwindows struct HostAdapterInfoNode *appendHostInfo(
errno = EEXIST; errno = EEXIST;
return NULL; return NULL;
} }
}
/* Finally we got a unique short and friendly name */ /* Finally we got a unique short and friendly name */
node->unicast = *((*ptrUA)->Address.lpSockaddr); node->unicast = *((*ptrUA)->Address.lpSockaddr);
@ -267,6 +274,9 @@ static textwindows struct HostAdapterInfoNode *appendHostInfo(
node->flags = 0; node->flags = 0;
} }
int family = ((struct sockaddr_in *)(*ptrUA)->Address.lpSockaddr)->sin_family;
if (family == AF_INET) {
*ip4cnt += 1;
ip = ntohl( ip = ntohl(
((struct sockaddr_in *)(*ptrUA)->Address.lpSockaddr)->sin_addr.s_addr); ((struct sockaddr_in *)(*ptrUA)->Address.lpSockaddr)->sin_addr.s_addr);
netmask = (uint32_t)-1 << (32 - (*ptrUA)->OnLinkPrefixLength); netmask = (uint32_t)-1 << (32 - (*ptrUA)->OnLinkPrefixLength);
@ -320,6 +330,30 @@ static textwindows struct HostAdapterInfoNode *appendHostInfo(
} }
} }
} }
} else if (family == AF_INET6) {
node->addr6 =
((struct sockaddr_in6 *)(*ptrUA)->Address.lpSockaddr)->sin6_addr;
node->ifindex6 =
((struct sockaddr_in6 *)(*ptrUA)->Address.lpSockaddr)->sin6_scope_id;
node->prefixlen6 = (*ptrUA)->OnLinkPrefixLength;
node->scope6 = 0;
if (IN6_IS_ADDR_LINKLOCAL(&node->addr6)) {
node->scope6 = 0x20; // link
} else if (IN6_IS_ADDR_SITELOCAL(&node->addr6)) {
node->scope6 = 0x40; // site
} else if (IN6_IS_ADDR_LOOPBACK(&node->addr6)) {
node->scope6 = 0x10; // host
}
node->flags6 = 0; // TODO ipv6 addr flags
// Move Prefix ptr
if (ptrAP && *ptrAP) {
*ptrAP = (*ptrAP)->Next; /* skip net ip */
if (*ptrAP) {
*ptrAP = (*ptrAP)->Next; /* skip host ip */
}
}
}
*ptrUA = (*ptrUA)->Next; *ptrUA = (*ptrUA)->Next;
@ -341,14 +375,13 @@ static textwindows int createHostInfo(
struct NtIpAdapterPrefix *ap; struct NtIpAdapterPrefix *ap;
struct HostAdapterInfoNode *node = NULL; struct HostAdapterInfoNode *node = NULL;
char baseName[IFNAMSIZ]; char baseName[IFNAMSIZ];
int count, i; int ip4cnt, i;
/* __hostInfo must be empty */ /* __hostInfo must be empty */
unassert(__hostInfo == NULL); unassert(__hostInfo == NULL);
for (aa = firstAdapter; aa; aa = aa->Next) { for (aa = firstAdapter; aa; aa = aa->Next) {
/* Skip all the interfaces with no address and the ones that are not AF_INET /* Skip all the interfaces with no address
*/ */
if (!aa->FirstUnicastAddress || if (!aa->FirstUnicastAddress) {
aa->FirstUnicastAddress->Address.lpSockaddr->sa_family != AF_INET) {
continue; continue;
} }
/* Use max IFNAMSIZ-1 chars, leave the last char for eventual conflicts */ /* Use max IFNAMSIZ-1 chars, leave the last char for eventual conflicts */
@ -361,9 +394,11 @@ static textwindows int createHostInfo(
if (!baseName[i]) if (!baseName[i])
break; break;
} }
for (count = 0, ua = aa->FirstUnicastAddress, ap = aa->FirstPrefix; ua = aa->FirstUnicastAddress;
(ua != NULL) && (count < MAX_UNICAST_ADDR); ++count) { ap = aa->FirstPrefix;
node = appendHostInfo(node, baseName, aa, &ua, &ap, count); ip4cnt = 0;
while (ua != NULL) {
node = appendHostInfo(node, baseName, aa, &ua, &ap, &ip4cnt);
if (!node) if (!node)
goto err; goto err;
if (!__hostInfo) { if (!__hostInfo) {
@ -390,9 +425,9 @@ static textwindows int readAdapterAddresses(void) {
struct NtIpAdapterAddresses *aa = NULL; struct NtIpAdapterAddresses *aa = NULL;
/* /*
* Calculate the required data size * Calculate the required data size
* Note: alternatively you can use AF_UNSPEC to also return IPv6 interfaces * Note: AF_UNSPEC return both IPv4 and IPv6 interfaces
*/ */
rc = GetAdaptersAddresses(AF_INET, rc = GetAdaptersAddresses(AF_UNSPEC,
kNtGaaFlagSkipAnycast | kNtGaaFlagSkipMulticast | kNtGaaFlagSkipAnycast | kNtGaaFlagSkipMulticast |
kNtGaaFlagSkipDnsServer | kNtGaaFlagSkipDnsServer |
kNtGaaFlagIncludePrefix, kNtGaaFlagIncludePrefix,
@ -409,7 +444,7 @@ static textwindows int readAdapterAddresses(void) {
goto err; goto err;
} }
/* Re-run GetAdaptersAddresses this time with a valid buffer */ /* Re-run GetAdaptersAddresses this time with a valid buffer */
rc = GetAdaptersAddresses(AF_INET, rc = GetAdaptersAddresses(AF_UNSPEC,
kNtGaaFlagSkipAnycast | kNtGaaFlagSkipMulticast | kNtGaaFlagSkipAnycast | kNtGaaFlagSkipMulticast |
kNtGaaFlagSkipDnsServer | kNtGaaFlagSkipDnsServer |
kNtGaaFlagIncludePrefix, kNtGaaFlagIncludePrefix,
@ -446,8 +481,19 @@ static textwindows int ioctl_siocgifconf_nt(int fd, struct ifconf *ifc) {
for (ptr = ifc->ifc_req, node = __hostInfo; for (ptr = ifc->ifc_req, node = __hostInfo;
(((char *)(ptr + 1) - ifc->ifc_buf) < ifc->ifc_len) && node; (((char *)(ptr + 1) - ifc->ifc_buf) < ifc->ifc_len) && node;
ptr++, node = node->next) { ptr++, node = node->next) {
bzero(ptr, sizeof(struct ifreq));
memcpy(ptr->ifr_name, node->name, IFNAMSIZ); memcpy(ptr->ifr_name, node->name, IFNAMSIZ);
int family = node->unicast.sa_family;
if (family == AF_INET) {
memcpy(&ptr->ifr_addr, &node->unicast, sizeof(struct sockaddr)); memcpy(&ptr->ifr_addr, &node->unicast, sizeof(struct sockaddr));
} else if (family == AF_INET6) {
ptr->ifr_ifru.in6.sa_family = AF_INET6;
ptr->ifr6_ifindex = node->ifindex6;
ptr->ifr6_flags = node->flags6;
ptr->ifr6_scope = node->scope6;
ptr->ifr6_prefixlen = node->prefixlen6;
memcpy(&ptr->ifr6_addr, &node->addr6, sizeof(struct in6_addr));
}
} }
ifc->ifc_len = (char *)ptr - ifc->ifc_buf; ifc->ifc_len = (char *)ptr - ifc->ifc_buf;
return 0; return 0;