From 14aa6153bec48da8f247c099d903566794e2da0a Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Sat, 9 Feb 2013 17:33:46 -0500 Subject: [PATCH] unix, win: add netmask to uv_interface_address Include the netmask when returning information about the OS network interfaces. This commit provides implementations for windows and those unix platforms using getifaddrs(). AIX was not implemented because it requires the use of ioctls and I do not have an AIX development/test environment. The windows code was developed using mingw on winxp as I do not have access to visual studio. Tested on darwin (ipv4/ipv6) and winxp (ipv4 only). Needs testing on newer windows using ipv6 and other unix platforms. --- include/uv.h | 4 ++++ src/unix/aix.c | 2 ++ src/unix/darwin.c | 6 ++++++ src/unix/linux-core.c | 6 ++++++ src/unix/netbsd.c | 6 ++++++ src/unix/sunos.c | 6 ++++++ src/win/util.c | 42 ++++++++++++++++++++++++++++++++++--- test/test-platform-output.c | 8 +++++++ 8 files changed, 77 insertions(+), 3 deletions(-) diff --git a/include/uv.h b/include/uv.h index a6688131..3b61e28f 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1479,6 +1479,10 @@ struct uv_interface_address_s { struct sockaddr_in address4; struct sockaddr_in6 address6; } address; + union { + struct sockaddr_in netmask4; + struct sockaddr_in6 netmask6; + } netmask; }; UV_EXTERN char** uv_setup_args(int argc, char** argv); diff --git a/src/unix/aix.c b/src/unix/aix.c index 5ea33bbc..e1f247a7 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -369,6 +369,8 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, address->address.address4 = *((struct sockaddr_in *)&p->ifr_addr); } + /* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */ + address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; address++; diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 85a1d9ad..e641b500 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -408,6 +408,12 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); } + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6 *)ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in *)ent->ifa_netmask); + } + address->is_internal = ent->ifa_flags & IFF_LOOPBACK ? 1 : 0; address++; diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index 23a59772..13ed0c5e 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -693,6 +693,12 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); } + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6 *)ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in *)ent->ifa_netmask); + } + address->is_internal = ent->ifa_flags & IFF_LOOPBACK ? 1 : 0; address++; diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c index 0fbcf108..9d3b2146 100644 --- a/src/unix/netbsd.c +++ b/src/unix/netbsd.c @@ -331,6 +331,12 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, int* count) address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); } + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6 *)ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in *)ent->ifa_netmask); + } + address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK) ? 1 : 0; address++; diff --git a/src/unix/sunos.c b/src/unix/sunos.c index ff5044cf..1bec39c0 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -622,6 +622,12 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); } + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6 *)ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in *)ent->ifa_netmask); + } + address->is_internal = ent->ifa_flags & IFF_PRIVATE || ent->ifa_flags & IFF_LOOPBACK ? 1 : 0; diff --git a/src/win/util.c b/src/win/util.c index 96b1abe5..be076814 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -31,6 +31,7 @@ #include "uv.h" #include "internal.h" +#include #include #include #include @@ -765,7 +766,7 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses_ptr, /* ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in */ /* win_address_buf_size. */ r = GetAdaptersAddresses(AF_UNSPEC, - 0, + GAA_FLAG_INCLUDE_PREFIX, NULL, win_address_buf, &win_address_buf_size); @@ -882,6 +883,7 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses_ptr, win_address != NULL; win_address = win_address->Next) { IP_ADAPTER_UNICAST_ADDRESS_XP* unicast_address; + IP_ADAPTER_PREFIX* prefix; int name_size; size_t max_name_size; @@ -907,21 +909,55 @@ uv_err_t uv_interface_addresses(uv_interface_address_t** addresses_ptr, return uv__new_sys_error(GetLastError()); } + prefix = win_address->FirstPrefix; + /* Add an uv_interface_address_t element for every unicast address. */ for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS_XP*) win_address->FirstUnicastAddress; unicast_address != NULL; unicast_address = unicast_address->Next) { struct sockaddr* sa; + int prefixlen; uv_address->name = name_buf; + /* Walk the prefix list in sync with the address list and extract + the prefixlen for each address. On Vista and newer, we could + instead just use: unicast_address->OnLinkPrefixLength */ + if (prefix != NULL) { + prefixlen = prefix->PrefixLength; + prefix = prefix->Next; + } else { + prefixlen = 0; + } + sa = unicast_address->Address.lpSockaddr; - if (sa->sa_family == AF_INET6) + if (sa->sa_family == AF_INET6) { + int i; + uv_address->address.address6 = *((struct sockaddr_in6 *) sa); - else + + uv_address->netmask.netmask6.sin6_family = AF_INET6; + prefixlen = prefixlen > 0 ? prefixlen : 128; + for (i = 0; i < 16; ++i) { + int bits; + uint8_t byte_val; + + bits = prefixlen < 8 ? prefixlen : 8; + byte_val = ~(0xff >> bits); + prefixlen -= bits; + + uv_address->netmask.netmask6.sin6_addr.s6_addr[i] = byte_val; + } + } else { uv_address->address.address4 = *((struct sockaddr_in *) sa); + uv_address->netmask.netmask4.sin_family = AF_INET; + prefixlen = prefixlen > 0 ? prefixlen : 32; + uv_address->netmask.netmask4.sin_addr.s_addr = + htonl(0xffffffff << (32 - prefixlen)); + } + uv_address->is_internal = (win_address->IfType == IF_TYPE_SOFTWARE_LOOPBACK); diff --git a/test/test-platform-output.c b/test/test-platform-output.c index 008d14fb..80737a78 100644 --- a/test/test-platform-output.c +++ b/test/test-platform-output.c @@ -80,6 +80,14 @@ TEST_IMPL(platform_output) { } printf(" address: %s\n", buffer); + + if (interfaces[i].netmask.netmask4.sin_family == AF_INET) { + uv_ip4_name(&interfaces[i].netmask.netmask4, buffer, sizeof(buffer)); + } else if (interfaces[i].netmask.netmask4.sin_family == AF_INET6) { + uv_ip6_name(&interfaces[i].netmask.netmask6, buffer, sizeof(buffer)); + } + + printf(" netmask: %s\n", buffer); } uv_free_interface_addresses(interfaces, count);