diff options
author | Lorenzo Colitti <lorenzo@google.com> | 2019-02-26 00:30:18 +0900 |
---|---|---|
committer | Lorenzo Colitti <lorenzo@google.com> | 2019-02-27 17:38:02 +0900 |
commit | 8a9f1ada2083463d9ea8b7b2b6fa4183aae8ec27 (patch) | |
tree | 8ffbe742c53da8166cf9131861a055c5b25b38c3 /tests/tun_interface.cpp | |
parent | b5d45875c5bf9a6d06f79d3c18258f8b9293d40c (diff) | |
download | netd-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.cpp | 85 |
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 |