From b6ec8338f95126201b53a46982f748da2d662fb4 Mon Sep 17 00:00:00 2001 From: Leorize Date: Fri, 11 Jan 2019 20:43:11 +0700 Subject: [PATCH] libnetwork: getifaddrs() now returns ipv6 and physical addresses If available, getifaddrs() will now return additional entries for different addresses of an interface. This is similiar to how NetBSD does it. This allows software such as libuv to construct detailed information about network interfaces within the system. Adrien have some concerns about getaddrinfo() wouldn't correctly exclude IPv6 addresses after this change so a test was added to cover that. Change-Id: I8c5d3a02b9294d746ca928a285f40344dfd6e3cb Reviewed-on: https://review.haiku-os.org/c/874 Reviewed-by: Adrien Destugues --- src/system/libnetwork/getifaddrs.cpp | 117 +++++++++++------- .../system/network/posixnet/GetAddrInfo.cpp | 25 ++++ .../system/network/posixnet/GetAddrInfo.h | 1 + 3 files changed, 100 insertions(+), 43 deletions(-) diff --git a/src/system/libnetwork/getifaddrs.cpp b/src/system/libnetwork/getifaddrs.cpp index 585a347da0..005bc082f9 100644 --- a/src/system/libnetwork/getifaddrs.cpp +++ b/src/system/libnetwork/getifaddrs.cpp @@ -50,69 +50,34 @@ copy_address(const sockaddr& address) } -/*! Returns a chained list of all interfaces. - - We follow BSD semantics, and only return one entry per interface, - not per address; since this is mainly used by NetBSD's netresolv, it's - probably what it expects. -*/ -int -getifaddrs(struct ifaddrs** _ifaddrs) +static int +_getifaddrs(int domain, char* buffer, size_t len, struct ifaddrs** previous) { - if (_ifaddrs == NULL) { - errno = B_BAD_VALUE; - return -1; - } - - int socket = ::socket(AF_INET, SOCK_DGRAM, 0); + int socket = ::socket(domain, SOCK_DGRAM, 0); if (socket < 0) return -1; - FileDescriptorCloser closer(socket); - // Get interface count - ifconf config; - config.ifc_len = sizeof(config.ifc_value); - if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0) - return -1; - - size_t count = (size_t)config.ifc_value; - if (count == 0) { - // No interfaces found - *_ifaddrs = NULL; - return 0; - } - - // Allocate a buffer for ifreqs for all interfaces - char* buffer = (char*)malloc(count * sizeof(struct ifreq)); - if (buffer == NULL) { - errno = B_NO_MEMORY; - return -1; - } - - MemoryDeleter deleter(buffer); - // Get interfaces configuration - config.ifc_len = count * sizeof(struct ifreq); + ifconf config; config.ifc_buf = buffer; + config.ifc_len = len; if (ioctl(socket, SIOCGIFCONF, &config, sizeof(struct ifconf)) < 0) return -1; ifreq* interfaces = (ifreq*)buffer; ifreq* end = (ifreq*)(buffer + config.ifc_len); - struct ifaddrs* previous = NULL; - for (uint32_t i = 0; interfaces < end; i++) { + while (interfaces < end) { struct ifaddrs* current = new(std::nothrow) ifaddrs(); if (current == NULL) { - freeifaddrs(previous); errno = B_NO_MEMORY; return -1; } // Chain this interface with the next one - current->ifa_next = previous; - previous = current; + current->ifa_next = *previous; + *previous = current; current->ifa_name = strdup(interfaces[0].ifr_name); current->ifa_addr = copy_address(interfaces[0].ifr_addr); @@ -139,6 +104,72 @@ getifaddrs(struct ifaddrs** _ifaddrs) + _SIZEOF_ADDR_IFREQ(interfaces[0])); } + return 0; +} + + +/*! Returns a chained list of all interfaces. */ +int +getifaddrs(struct ifaddrs** _ifaddrs) +{ + if (_ifaddrs == NULL) { + errno = B_BAD_VALUE; + return -1; + } + + int socket = ::socket(AF_INET, SOCK_DGRAM, 0); + if (socket < 0) + return -1; + + FileDescriptorCloser closer(socket); + + // Get interface count + ifconf config; + config.ifc_len = sizeof(config.ifc_value); + if (ioctl(socket, SIOCGIFCOUNT, &config, sizeof(struct ifconf)) < 0) + return -1; + + socket = -1; + closer.Unset(); + + size_t count = (size_t)config.ifc_value; + if (count == 0) { + // No interfaces found + *_ifaddrs = NULL; + return 0; + } + + // Allocate a buffer for ifreqs for all interfaces + size_t buflen = count * sizeof(struct ifreq); + char* buffer = (char*)malloc(buflen); + if (buffer == NULL) { + errno = B_NO_MEMORY; + return -1; + } + + MemoryDeleter deleter(buffer); + + struct ifaddrs* previous = NULL; + int serrno = errno; + if (_getifaddrs(AF_INET, buffer, buflen, &previous) < 0 && + errno != B_UNSUPPORTED) { + freeifaddrs(previous); + return -1; + } + if (_getifaddrs(AF_INET6, buffer, buflen, &previous) < 0 && + errno != B_UNSUPPORTED) { + freeifaddrs(previous); + return -1; + } + if (_getifaddrs(AF_LINK, buffer, buflen, &previous) < 0 && + errno != B_UNSUPPORTED) { + freeifaddrs(previous); + return -1; + } + if (previous == NULL) + return -1; + errno = serrno; + *_ifaddrs = previous; return 0; } diff --git a/src/tests/system/network/posixnet/GetAddrInfo.cpp b/src/tests/system/network/posixnet/GetAddrInfo.cpp index 5ec2d709f9..84905b62c2 100644 --- a/src/tests/system/network/posixnet/GetAddrInfo.cpp +++ b/src/tests/system/network/posixnet/GetAddrInfo.cpp @@ -43,6 +43,29 @@ GetAddrInfoTest::EmptyTest() } +void +GetAddrInfoTest::AddrConfigTest() +{ + struct addrinfo *info = NULL, hints = {0}; + hints.ai_flags = AI_ADDRCONFIG; + + // localhost is guaranteed to have an IPv6 address. + int result = getaddrinfo("localhost", NULL, &hints, &info); + CPPUNIT_ASSERT(result == 0); + CPPUNIT_ASSERT(info != NULL); + + bool check; + for (struct addrinfo* iter = info; iter != NULL; iter = iter->ai_next) { + // only IPv4 addresses should be returned as we don't have IPv6 routing. + check = iter->ai_family == AF_INET; + if (!check) break; + } + + freeaddrinfo(info); + CPPUNIT_ASSERT(check); +} + + /* static */ void GetAddrInfoTest::AddTests(BTestSuite& parent) { @@ -50,6 +73,8 @@ GetAddrInfoTest::AddTests(BTestSuite& parent) suite.addTest(new CppUnit::TestCaller( "GetAddrInfoTest::EmptyTest", &GetAddrInfoTest::EmptyTest)); + suite.addTest(new CppUnit::TestCaller( + "GetAddrInfoTest::AddrConfigTest", &GetAddrInfoTest::AddrConfigTest)); parent.addTest("GetAddrInfoTest", &suite); } diff --git a/src/tests/system/network/posixnet/GetAddrInfo.h b/src/tests/system/network/posixnet/GetAddrInfo.h index ec826c8b65..739be5c80d 100644 --- a/src/tests/system/network/posixnet/GetAddrInfo.h +++ b/src/tests/system/network/posixnet/GetAddrInfo.h @@ -16,6 +16,7 @@ public: virtual ~GetAddrInfoTest(); void EmptyTest(); + void AddrConfigTest(); static void AddTests(BTestSuite& suite); };