OSDN Git Service

Add more tests for getifaddrs(3)
authorYi Kong <yikong@google.com>
Wed, 13 Jan 2016 11:28:14 +0000 (11:28 +0000)
committerYi Kong <yikong@google.com>
Thu, 14 Jan 2016 15:39:26 +0000 (15:39 +0000)
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

tests/ifaddrs_test.cpp

index 8159710..0c32332 100644 (file)
 
 #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);