Add more tests for getifaddrs(3)
This adds the following two checks: * getifaddrs sees the same list of interfaces as /sys/class/net. * IPv4 addresses we get from netdevice(7) agrees with results from getifaddrs. Change-Id: I2f6d79d0b5cde6d98a0f671d1623b6b2bc75b60f
This commit is contained in:
parent
1307831ba4
commit
64b481c29b
1 changed files with 98 additions and 3 deletions
|
@ -18,24 +18,30 @@
|
|||
|
||||
#include <ifaddrs.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
TEST(ifaddrs, freeifaddrs_null) {
|
||||
freeifaddrs(nullptr);
|
||||
}
|
||||
|
||||
TEST(ifaddrs, getifaddrs_smoke) {
|
||||
// We can't statically say much about what network interfaces are available, but we can be pretty
|
||||
// sure there's a loopback interface, and that it has IPv4, IPv6, and AF_PACKET entries.
|
||||
TEST(ifaddrs, getifaddrs_lo) {
|
||||
ifaddrs* addrs = nullptr;
|
||||
|
||||
ASSERT_EQ(0, getifaddrs(&addrs));
|
||||
ASSERT_TRUE(addrs != nullptr);
|
||||
|
||||
// We can't say much about what network interfaces are available, but we can be pretty
|
||||
// sure there's a loopback interface, and that it has IPv4, IPv6, and AF_PACKET entries.
|
||||
ifaddrs* lo_inet4 = nullptr;
|
||||
ifaddrs* lo_inet6 = nullptr;
|
||||
ifaddrs* lo_packet = nullptr;
|
||||
|
@ -65,6 +71,95 @@ TEST(ifaddrs, getifaddrs_smoke) {
|
|||
freeifaddrs(addrs);
|
||||
}
|
||||
|
||||
// Check that getifaddrs sees the same list of interfaces as /sys/class/net.
|
||||
TEST(ifaddrs, getifaddrs_interfaces) {
|
||||
std::vector<std::string> ifaddrs_socks;
|
||||
{
|
||||
ifaddrs* addrs;
|
||||
ASSERT_EQ(0, getifaddrs(&addrs));
|
||||
|
||||
for (ifaddrs* addr = addrs; addr != nullptr; addr = addr->ifa_next) {
|
||||
int family = addr->ifa_addr ? addr->ifa_addr->sa_family :
|
||||
addr->ifa_broadaddr ? addr->ifa_broadaddr->sa_family :
|
||||
AF_UNSPEC;
|
||||
|
||||
if (family == AF_PACKET || family == AF_UNSPEC) {
|
||||
ifaddrs_socks.push_back(std::string(addr->ifa_name));
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(addrs);
|
||||
}
|
||||
|
||||
std::vector<std::string> sys_class_net;
|
||||
{
|
||||
auto dir_deleter = [](DIR* handle) { if (handle) closedir(handle); };
|
||||
std::unique_ptr<DIR, decltype(dir_deleter)> d(opendir("/sys/class/net"), dir_deleter);
|
||||
ASSERT_TRUE(d != nullptr);
|
||||
dirent* dir;
|
||||
while ((dir = readdir(d.get())) != nullptr) {
|
||||
if (dir->d_type == DT_LNK) {
|
||||
sys_class_net.push_back(std::string(dir->d_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_TRUE(std::is_permutation(ifaddrs_socks.begin(), ifaddrs_socks.end(),
|
||||
sys_class_net.begin()));
|
||||
}
|
||||
|
||||
TEST(ifaddrs, getifaddrs_INET) {
|
||||
std::multimap<std::string,in_addr_t> inetaddrs;
|
||||
std::multimap<std::string,in_addr_t> broadinetaddrs;
|
||||
|
||||
{
|
||||
ifaddrs* addrs;
|
||||
ASSERT_EQ(0, getifaddrs(&addrs));
|
||||
for (ifaddrs* addr = addrs; addr != nullptr; addr = addr->ifa_next) {
|
||||
if (addr->ifa_name && addr->ifa_addr && addr->ifa_addr->sa_family == AF_INET) {
|
||||
auto sock = reinterpret_cast<sockaddr_in*>(addr->ifa_addr);
|
||||
inetaddrs.emplace(std::string(addr->ifa_name), sock->sin_addr.s_addr);
|
||||
}
|
||||
if (addr->ifa_name && addr->ifa_broadaddr && addr->ifa_broadaddr->sa_family == AF_INET) {
|
||||
auto sock = reinterpret_cast<sockaddr_in*>(addr->ifa_broadaddr);
|
||||
broadinetaddrs.emplace(std::string(addr->ifa_name), sock->sin_addr.s_addr);
|
||||
}
|
||||
}
|
||||
freeifaddrs(addrs);
|
||||
}
|
||||
|
||||
{
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
ASSERT_TRUE(fd != -1);
|
||||
|
||||
auto check_inet_agrees = [&](std::multimap<std::string, in_addr_t> addrs, int request)->bool {
|
||||
for (auto it = addrs.begin(); it != addrs.end(); ) {
|
||||
ifreq ifr;
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
it->first.copy(ifr.ifr_name, IFNAMSIZ - 1);
|
||||
ioctl(fd, request, &ifr);
|
||||
|
||||
sockaddr_in* sock = reinterpret_cast<sockaddr_in*>(&ifr.ifr_addr);
|
||||
in_addr_t addr = sock->sin_addr.s_addr;
|
||||
|
||||
bool found = false;
|
||||
for (auto ub = addrs.upper_bound(it->first); it != ub; ++it) {
|
||||
if (it->second == addr) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
ASSERT_TRUE(check_inet_agrees(inetaddrs, SIOCGIFADDR));
|
||||
ASSERT_TRUE(check_inet_agrees(broadinetaddrs, SIOCGIFBRDADDR));
|
||||
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_sockaddr_ll(const char* what, const sockaddr* p) {
|
||||
const sockaddr_ll* s = reinterpret_cast<const sockaddr_ll*>(p);
|
||||
printf("\t%s\t", what);
|
||||
|
|
Loading…
Reference in a new issue