summaryrefslogtreecommitdiff
path: root/tests/tun_interface.cpp
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2019-02-26 00:30:18 +0900
committerLorenzo Colitti <lorenzo@google.com>2019-02-27 17:38:02 +0900
commit8a9f1ada2083463d9ea8b7b2b6fa4183aae8ec27 (patch)
tree8ffbe742c53da8166cf9131861a055c5b25b38c3 /tests/tun_interface.cpp
parentb5d45875c5bf9a6d06f79d3c18258f8b9293d40c (diff)
downloadnetd-8a9f1ada2083463d9ea8b7b2b6fa4183aae8ec27.tar.gz
Ensure that addresses have actually been created in TunInterface.
Currently TunInterface assumes that IP addresses are created immediately as soon as ifc_add_address returns. However, IPv6 addresses, even if created with IFA_F_NODAD or optimistic DAD, are not created immediately and are not actually usable when the netlink ACK returns. Ensure tests aren't flaky by waiting for RTM_NEWADDR for the address after creating it. Test: system/netd/tests/runtests.sh Change-Id: I5db5e25b24329ecbe953b592997b6c1358722c5c
Diffstat (limited to 'tests/tun_interface.cpp')
-rw-r--r--tests/tun_interface.cpp85
1 files changed, 78 insertions, 7 deletions
diff --git a/tests/tun_interface.cpp b/tests/tun_interface.cpp
index 01278ccf..3d62fb02 100644
--- a/tests/tun_interface.cpp
+++ b/tests/tun_interface.cpp
@@ -16,21 +16,26 @@
* tun_interface.cpp - creates tun interfaces for testing purposes
*/
+#include <string>
+
#include <fcntl.h>
-#include <netdb.h>
-#include <stdlib.h>
-#include <unistd.h>
#include <linux/if.h>
#include <linux/if_tun.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
#include <net/if.h>
+#include <netdb.h>
#include <netinet/in.h>
+#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <unistd.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
#include <netutils/ifc.h>
#include "tun_interface.h"
@@ -38,6 +43,7 @@
#define TUN_DEV "/dev/tun"
using android::base::StringPrintf;
+using android::base::unique_fd;
namespace android {
namespace net {
@@ -89,15 +95,14 @@ int TunInterface::init(const std::string& ifName) {
return ret;
}
- if (ifc_add_address(ifr.ifr_name, srcStr, 64) ||
- ifc_add_address(ifr.ifr_name, dstStr, 64)) {
+ mIfIndex = if_nametoindex(ifr.ifr_name);
+
+ if (addAddress(srcStr, 64) || addAddress(dstStr, 64)) {
ret = -errno;
close(mFd);
return ret;
}
- mIfIndex = if_nametoindex(ifr.ifr_name);
-
if (int ret = ifc_enable(ifr.ifr_name)) {
return ret;
}
@@ -112,5 +117,71 @@ void TunInterface::destroy() {
}
}
+int TunInterface::addAddress(const std::string& addr, int prefixlen) {
+ // Wait for an RTM_NEWADDR indicating that the address has been created.
+ // This is because IPv6 addresses, even addresses that are optimistic or created with
+ // IFA_F_NODAD, are not immediately usable when the netlink ACK returns.
+ // This is not generally necessary in device code because the framework hears about IP addresses
+ // asynchronously via netlink, but it is necessary to ensure tests aren't flaky.
+ unique_fd s(socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+ if (s == -1) return -errno;
+
+ sockaddr_nl groups = {.nl_family = AF_NETLINK,
+ .nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR};
+ if (bind(s, reinterpret_cast<sockaddr*>(&groups), sizeof(groups)) == -1) return -errno;
+
+ sockaddr_nl kernel = {.nl_family = AF_NETLINK};
+ if (connect(s, reinterpret_cast<sockaddr*>(&kernel), sizeof(kernel)) == -1) return -errno;
+
+ // Wait up to 200ms for address to arrive.
+ timeval timeout = {.tv_usec = 200 * 1000};
+ if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) return -errno;
+
+ if (ifc_add_address(mIfName.c_str(), addr.c_str(), prefixlen)) return -errno;
+
+ int family;
+ size_t addrlen;
+ union {
+ in_addr ip4;
+ in6_addr ip6;
+ } ip;
+ if (addr.find(":") != std::string::npos) {
+ family = AF_INET6;
+ inet_pton(AF_INET6, addr.c_str(), &ip.ip6);
+ addrlen = sizeof(ip.ip6);
+ } else {
+ family = AF_INET;
+ inet_pton(AF_INET, addr.c_str(), &ip.ip4);
+ addrlen = sizeof(ip.ip4);
+ }
+
+ while (1) {
+ char buf[4096];
+ ssize_t len = recv(s, buf, sizeof(buf), 0);
+
+ if (len == -1) break;
+ if (len < static_cast<ssize_t>(NLMSG_SPACE(sizeof(ifaddrmsg)))) continue;
+
+ nlmsghdr* nlmsg = reinterpret_cast<nlmsghdr*>(buf);
+ if (nlmsg->nlmsg_type != RTM_NEWADDR) continue;
+
+ ifaddrmsg* ifaddr = reinterpret_cast<ifaddrmsg*>(NLMSG_DATA(nlmsg));
+ if (ifaddr->ifa_family != family) continue;
+ if (ifaddr->ifa_prefixlen != prefixlen) continue;
+ if (ifaddr->ifa_index != static_cast<uint32_t>(mIfIndex)) continue;
+
+ int ifalen = IFA_PAYLOAD(nlmsg);
+ for (rtattr* rta = IFA_RTA(ifaddr); RTA_OK(rta, ifalen); rta = RTA_NEXT(rta, ifalen)) {
+ if (rta->rta_type != IFA_LOCAL && rta->rta_type != IFA_ADDRESS) continue;
+ if (RTA_PAYLOAD(rta) != addrlen) continue;
+ if (!memcmp(RTA_DATA(rta), &ip, addrlen)) {
+ return 0;
+ }
+ }
+ }
+
+ return -errno;
+}
+
} // namespace net
} // namespace android