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 a668813114..3b61e28f60 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 5ea33bbcd5..e1f247a738 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 85a1d9ad2d..e641b500eb 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 23a5977279..13ed0c5e72 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 0fbcf108c1..9d3b2146e3 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 ff5044cf0d..1bec39c017 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 96b1abe52d..be076814ee 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 008d14fb7d..80737a783e 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);