Search code examples
cwinapinetwork-programmingipv6ipv4

list adapters addresses and mask/prefix, both IPv4 and IPv6 (GetAdaptersAddresses)


I am tasked with listing all unicast interfaces (IP-address & subnet via netmask (IPv4) or prefix length (IPv6)) on the local machine. Being new to win32 API, I am reading GetAdaptersAddress docs. There is an example which is quite clear:

  • call GetAdaptersAddresses with increasing buffer size until the results fit into the allocated memory chunk
  • iterate over returned linked list of PIP_ADAPTER_ADDRESSES (pCurrAddresses):
    • iterate over PIP_ADAPTER_UNICAST_ADDRESS* pCurrAddresses->FirstUnicastAddresses linked list to retrieve all IP addresses (SOCKET_ADDRESS and its LPSOCKADDR Address->lpSockAddr member); it is a sockaddr_in* or sockaddr_in6* depending on lpSockAddr.sa_family (AF_INET or AF_INET6)

At this moment, I have IP addresses.

How about netmask/prefix? This seems to be what PIP_ADAPTER_PREFIX_XP (and its SOCKET_ADDRESS Address field) is about. That is another linked list, accessible from PIP_ADAPTER_ADDRESSES through ->FirstPrefix.

Unhelpfully, the docs state that prefixes may not be ordered the same as addresses:

In addition, the linked IP_ADAPTER_UNICAST_ADDRESS structures pointed to by the FirstUnicastAddress member and the linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix member are maintained as separate internal linked lists by the operating system. As a result, the order of linked IP_ADAPTER_UNICAST_ADDRESS structures pointed to by the FirstUnicastAddress member does not have any relationship with the order of linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix member.

What is the solution? Am I missing something obvious?

Some code (e.g. zeroMQ) only uses, for each adapter, the first unicast address and the first prefix. Is that approach safe? Am I going to miss interfaces?


Solution

  • The IP_ADAPTER_ADDRESSES::FirstPrefix field gives you a list of subnets/prefixes that are assigned to the adapter, but as the documentation states, GetAdaptersAddresses() doesn't tell you which specific IP address on the adapter corresponds to which subnet/prefix. So, to determine each IP address's particular subnet/prefix, you have to look at the individual entries in the IP_ADAPTER_ADDRESSES::FirstUnicastAddresses list.

    On Windows Vista and later, the IP_ADAPTER_UNICAST_ADDRESS::OnLinkPrefixLength field provides the length of the IPv4 subnet mask for an AF_INET address, and the length of the IPv6 prefix for an AF_INET6 address. For IPv4, the length can be passed to the ConvertLengthToIpv4Mask() function if you need the actual subnet mask.

    On Windows XP, the OnLinkPrefixLength field does not exist. Fortunately, an AF_INET address's IPv4 subnet mask can be retrieved by calling the GetIpAddrTable() function and then looking for the IP address in the table (in the MIB_IPADDRROW::dwAddr field - the subnet mask will be in the MIB_IPADDRROW::dwMask field).

    I don't know how to get an AF_INET6 address's IPv6 prefix length on XP, if you need that.