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 <pulkomandy@gmail.com>
This commit is contained in:
Leorize 2019-01-11 20:43:11 +07:00 committed by waddlesplash
parent 5f603da01a
commit b6ec8338f9
3 changed files with 100 additions and 43 deletions

View File

@ -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;
}

View File

@ -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>(
"GetAddrInfoTest::EmptyTest", &GetAddrInfoTest::EmptyTest));
suite.addTest(new CppUnit::TestCaller<GetAddrInfoTest>(
"GetAddrInfoTest::AddrConfigTest", &GetAddrInfoTest::AddrConfigTest));
parent.addTest("GetAddrInfoTest", &suite);
}

View File

@ -16,6 +16,7 @@ public:
virtual ~GetAddrInfoTest();
void EmptyTest();
void AddrConfigTest();
static void AddTests(BTestSuite& suite);
};