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:
parent
5f603da01a
commit
b6ec8338f9
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ public:
|
||||
virtual ~GetAddrInfoTest();
|
||||
|
||||
void EmptyTest();
|
||||
void AddrConfigTest();
|
||||
|
||||
static void AddTests(BTestSuite& suite);
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user