#include <vector>
#include <fcntl.h>
+#include <ifaddrs.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/if.h>
#include <linux/if_tun.h>
+#include <android-base/macros.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/multiuser.h>
// Static because setting up the tun interface takes about 40ms.
static void SetUpTestCase() {
sTunFd = createTunInterface();
+ ASSERT_LE(sTunIfName.size(), static_cast<size_t>(IFNAMSIZ));
ASSERT_NE(-1, sTunFd);
}
protected:
sp<INetd> mNetd;
static int sTunFd;
+ static std::string sTunIfName;
static in6_addr sSrcAddr, sDstAddr;
static char sSrcStr[], sDstStr[];
};
int BinderTest::sTunFd;
+std::string BinderTest::sTunIfName;
in6_addr BinderTest::sSrcAddr;
in6_addr BinderTest::sDstAddr;
char BinderTest::sSrcStr[INET6_ADDRSTRLEN];
}
// Create a tun interface with a name based on our PID.
+ sTunIfName = StringPrintf("netdtest%u", getpid());
struct ifreq ifr = {
.ifr_ifru = { .ifru_flags = IFF_TUN },
};
- snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "netdtest%u", getpid());
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", sTunIfName.c_str());
int fd = open(TUN_DEV, O_RDWR | O_NONBLOCK | O_CLOEXEC);
EXPECT_NE(-1, fd) << TUN_DEV << ": " << strerror(errno);
close(serverSocket);
close(acceptedSocket);
}
+
+namespace {
+
+int netmaskToPrefixLength(const uint8_t *buf, size_t buflen) {
+ if (buf == nullptr) return -1;
+
+ int prefixLength = 0;
+ bool endOfContiguousBits = false;
+ for (unsigned int i = 0; i < buflen; i++) {
+ const uint8_t value = buf[i];
+
+ // Bad bit sequence: check for a contiguous set of bits from the high
+ // end by verifying that the inverted value + 1 is a power of 2
+ // (power of 2 iff. (v & (v - 1)) == 0).
+ const uint8_t inverse = ~value + 1;
+ if ((inverse & (inverse - 1)) != 0) return -1;
+
+ prefixLength += (value == 0) ? 0 : CHAR_BIT - ffs(value) + 1;
+
+ // Bogus netmask.
+ if (endOfContiguousBits && value != 0) return -1;
+
+ if (value != 0xff) endOfContiguousBits = true;
+ }
+
+ return prefixLength;
+}
+
+template<typename T>
+int netmaskToPrefixLength(const T *p) {
+ return netmaskToPrefixLength(reinterpret_cast<const uint8_t*>(p), sizeof(T));
+}
+
+
+static bool interfaceHasAddress(
+ const std::string &ifname, const char *addrString, int prefixLength) {
+ struct addrinfo *addrinfoList = nullptr;
+ ScopedAddrinfo addrinfoCleanup(addrinfoList);
+
+ const struct addrinfo hints = {
+ .ai_flags = AI_NUMERICHOST,
+ .ai_family = AF_UNSPEC,
+ .ai_socktype = SOCK_DGRAM,
+ };
+ if (getaddrinfo(addrString, nullptr, &hints, &addrinfoList) != 0 ||
+ addrinfoList == nullptr || addrinfoList->ai_addr == nullptr) {
+ return false;
+ }
+
+ struct ifaddrs *ifaddrsList = nullptr;
+ ScopedIfaddrs ifaddrsCleanup(ifaddrsList);
+
+ if (getifaddrs(&ifaddrsList) != 0) {
+ return false;
+ }
+
+ for (struct ifaddrs *addr = ifaddrsList; addr != nullptr; addr = addr->ifa_next) {
+ if (std::string(addr->ifa_name) != ifname ||
+ addr->ifa_addr == nullptr ||
+ addr->ifa_addr->sa_family != addrinfoList->ai_addr->sa_family) {
+ continue;
+ }
+
+ switch (addr->ifa_addr->sa_family) {
+ case AF_INET: {
+ auto *addr4 = reinterpret_cast<const struct sockaddr_in*>(addr->ifa_addr);
+ auto *want = reinterpret_cast<const struct sockaddr_in*>(addrinfoList->ai_addr);
+ if (memcmp(&addr4->sin_addr, &want->sin_addr, sizeof(want->sin_addr)) != 0) {
+ continue;
+ }
+
+ if (prefixLength < 0) return true; // not checking prefix lengths
+
+ if (addr->ifa_netmask == nullptr) return false;
+ auto *nm = reinterpret_cast<const struct sockaddr_in*>(addr->ifa_netmask);
+ EXPECT_EQ(prefixLength, netmaskToPrefixLength(&nm->sin_addr));
+ return (prefixLength == netmaskToPrefixLength(&nm->sin_addr));
+ }
+ case AF_INET6: {
+ auto *addr6 = reinterpret_cast<const struct sockaddr_in6*>(addr->ifa_addr);
+ auto *want = reinterpret_cast<const struct sockaddr_in6*>(addrinfoList->ai_addr);
+ if (memcmp(&addr6->sin6_addr, &want->sin6_addr, sizeof(want->sin6_addr)) != 0) {
+ continue;
+ }
+
+ if (prefixLength < 0) return true; // not checking prefix lengths
+
+ if (addr->ifa_netmask == nullptr) return false;
+ auto *nm = reinterpret_cast<const struct sockaddr_in6*>(addr->ifa_netmask);
+ EXPECT_EQ(prefixLength, netmaskToPrefixLength(&nm->sin6_addr));
+ return (prefixLength == netmaskToPrefixLength(&nm->sin6_addr));
+ }
+ default:
+ // Cannot happen because we have already screened for matching
+ // address families at the top of each iteration.
+ continue;
+ }
+ }
+
+ return false;
+}
+
+} // namespace
+
+TEST_F(BinderTest, TestInterfaceAddRemoveAddress) {
+ static const struct TestData {
+ const char *addrString;
+ const int prefixLength;
+ const bool expectSuccess;
+ } kTestData[] = {
+ { "192.0.2.1", 24, true },
+ { "192.0.2.2", 25, true },
+ { "192.0.2.3", 32, true },
+ { "192.0.2.4", 33, false },
+ { "192.not.an.ip", 24, false },
+ { "2001:db8::1", 64, true },
+ { "2001:db8::2", 65, true },
+ { "2001:db8::3", 128, true },
+ { "2001:db8::4", 129, false },
+ { "foo:bar::bad", 64, false },
+ };
+
+ for (unsigned int i = 0; i < arraysize(kTestData); i++) {
+ const auto &td = kTestData[i];
+
+ // [1.a] Add the address.
+ binder::Status status = mNetd->interfaceAddAddress(
+ sTunIfName, td.addrString, td.prefixLength);
+ if (td.expectSuccess) {
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ } else {
+ ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+ ASSERT_NE(0, status.serviceSpecificErrorCode());
+ }
+
+ // [1.b] Verify the addition meets the expectation.
+ if (td.expectSuccess) {
+ EXPECT_TRUE(interfaceHasAddress(sTunIfName, td.addrString, td.prefixLength));
+ } else {
+ EXPECT_FALSE(interfaceHasAddress(sTunIfName, td.addrString, -1));
+ }
+
+ // [2.a] Try to remove the address. If it was not previously added, removing it fails.
+ status = mNetd->interfaceDelAddress(sTunIfName, td.addrString, td.prefixLength);
+ if (td.expectSuccess) {
+ EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+ } else {
+ ASSERT_EQ(binder::Status::EX_SERVICE_SPECIFIC, status.exceptionCode());
+ ASSERT_NE(0, status.serviceSpecificErrorCode());
+ }
+
+ // [2.b] No matter what, the address should not be present.
+ EXPECT_FALSE(interfaceHasAddress(sTunIfName, td.addrString, -1));
+ }
+}