Merge "Convert NetdClient to use Soong"
diff --git a/server/Android.mk b/server/Android.mk
index e0596b5..0ee65c2 100644
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -140,9 +140,11 @@
LOCAL_CFLAGS += -Wno-varargs
LOCAL_C_INCLUDES := \
+ bionic/libc/dns/include \
system/netd/include \
system/netd/server \
system/netd/server/binder \
+ system/netd/tests \
system/core/logwrapper/include \
LOCAL_SRC_FILES := \
@@ -154,13 +156,24 @@
IdletimerController.cpp \
NatControllerTest.cpp NatController.cpp \
NetlinkCommands.cpp \
+ RouteController.cpp RouteControllerTest.cpp \
SockDiagTest.cpp SockDiag.cpp \
StrictController.cpp StrictControllerTest.cpp \
UidRanges.cpp \
binder/android/net/UidRange.cpp \
binder/android/net/metrics/INetdEventListener.aidl \
+ ../tests/tun_interface.cpp \
LOCAL_MODULE_TAGS := tests
-LOCAL_SHARED_LIBRARIES := libbinder liblog libbase libcutils liblogwrap libsysutils libutils
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libbinder \
+ libcutils \
+ liblog \
+ liblogwrap \
+ libnetutils \
+ libsysutils \
+ libutils \
+
include $(BUILD_NATIVE_TEST)
diff --git a/server/BandwidthControllerTest.cpp b/server/BandwidthControllerTest.cpp
index 959ad9a..c6b21d8 100644
--- a/server/BandwidthControllerTest.cpp
+++ b/server/BandwidthControllerTest.cpp
@@ -19,6 +19,7 @@
#include <string>
#include <vector>
+#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
@@ -31,6 +32,10 @@
#include "BandwidthController.h"
#include "IptablesBaseTest.h"
+#include "tun_interface.h"
+
+using android::base::StringPrintf;
+using android::net::TunInterface;
class BandwidthControllerTest : public IptablesBaseTest {
public:
@@ -40,6 +45,15 @@
BandwidthController::iptablesRestoreFunction = fakeExecIptablesRestoreWithOutput;
}
BandwidthController mBw;
+ TunInterface mTun;
+
+ void SetUp() {
+ ASSERT_EQ(0, mTun.init());
+ }
+
+ void TearDown() {
+ mTun.destroy();
+ }
void addIptablesRestoreOutput(std::string contents) {
sIptablesRestoreOutput.push_back(contents);
@@ -326,3 +340,51 @@
expectNoSocketClientResponse(socketPair[1]);
clearIptablesRestoreOutput();
}
+
+const std::vector<std::string> makeInterfaceQuotaCommands(const char *iface, int ruleIndex,
+ int64_t quota) {
+ std::vector<std::string> cmds = {
+ StringPrintf("-F bw_costly_%s", iface),
+ StringPrintf("-N bw_costly_%s", iface),
+ StringPrintf("-A bw_costly_%s -j bw_penalty_box", iface),
+ StringPrintf("-D bw_INPUT -i %s --jump bw_costly_%s", iface, iface),
+ StringPrintf("-I bw_INPUT %d -i %s --jump bw_costly_%s", ruleIndex, iface, iface),
+ StringPrintf("-D bw_OUTPUT -o %s --jump bw_costly_%s", iface, iface),
+ StringPrintf("-I bw_OUTPUT %d -o %s --jump bw_costly_%s", ruleIndex, iface, iface),
+ StringPrintf("-D bw_FORWARD -o %s --jump bw_costly_%s", iface, iface),
+ StringPrintf("-A bw_FORWARD -o %s --jump bw_costly_%s", iface, iface),
+ StringPrintf("-A bw_costly_%s -m quota2 ! --quota %" PRIu64 " --name %s --jump REJECT",
+ iface, quota, iface),
+ };
+ return cmds;
+}
+
+const std::vector<std::string> removeInterfaceQuotaCommands(const char *iface) {
+ std::vector<std::string> cmds = {
+ StringPrintf("-D bw_INPUT -i %s --jump bw_costly_%s", iface, iface),
+ StringPrintf("-D bw_OUTPUT -o %s --jump bw_costly_%s", iface, iface),
+ StringPrintf("-D bw_FORWARD -o %s --jump bw_costly_%s", iface, iface),
+ StringPrintf("-F bw_costly_%s", iface),
+ StringPrintf("-X bw_costly_%s", iface),
+ };
+ return cmds;
+}
+
+TEST_F(BandwidthControllerTest, TestSetInterfaceQuota) {
+ const char *iface = mTun.name().c_str();
+ std::vector<std::string> expected = makeInterfaceQuotaCommands(iface, 1, 123456);
+
+ // prepCostlyInterface assumes that exactly one of the "-F chain" and "-N chain" commands fails.
+ // So pretend that the first two commands (the IPv4 -F and the IPv6 -F) fail.
+ std::deque<int> returnValues(expected.size() * 2, 0);
+ returnValues[0] = 1;
+ returnValues[1] = 1;
+ setReturnValues(returnValues);
+
+ EXPECT_EQ(0, mBw.setInterfaceQuota(iface, 123456));
+ expectIptablesCommands(expected);
+
+ expected = removeInterfaceQuotaCommands(iface);
+ EXPECT_EQ(0, mBw.removeInterfaceQuota(iface));
+ expectIptablesCommands(expected);
+}
diff --git a/server/DnsProxyListener.cpp b/server/DnsProxyListener.cpp
index ac7cdbe..59a85f0 100644
--- a/server/DnsProxyListener.cpp
+++ b/server/DnsProxyListener.cpp
@@ -54,6 +54,66 @@
namespace android {
namespace net {
+namespace {
+
+template<typename T>
+void* threadMain(void* obj) {
+ std::unique_ptr<T> handler(reinterpret_cast<T*>(obj));
+ handler->run();
+ return nullptr;
+}
+
+struct scoped_pthread_attr {
+ scoped_pthread_attr() { pthread_attr_init(&attr); }
+ ~scoped_pthread_attr() { pthread_attr_destroy(&attr); }
+
+ int detach() {
+ return pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ }
+
+ pthread_attr_t attr;
+};
+
+template<typename T>
+int threadLaunch(T* self) {
+ if (self == nullptr) { return -EINVAL;}
+
+ scoped_pthread_attr scoped_attr;
+
+ int rval = scoped_attr.detach();
+ if (rval != 0) { return -errno; }
+
+ pthread_t thread;
+ rval = pthread_create(&thread, &scoped_attr.attr, &threadMain<T>, self);
+ if (rval != 0) {
+ ALOGW("pthread_create failed: %d", errno);
+ return -errno;
+ }
+
+ return rval;
+}
+
+template<typename T>
+void tryThreadOrError(SocketClient* cli, T* handler) {
+ cli->incRef();
+
+ const int rval = threadLaunch(handler);
+ if (rval == 0) {
+ // SocketClient decRef() happens in the handler's run() method.
+ return;
+ }
+
+ char* msg = NULL;
+ asprintf(&msg, "%s (%d)", strerror(-rval), -rval);
+ cli->sendMsg(ResponseCode::OperationFailed, msg, false);
+ free(msg);
+
+ delete handler;
+ cli->decRef();
+}
+
+} // namespace
+
DnsProxyListener::DnsProxyListener(const NetworkController* netCtrl, EventReporter* eventReporter) :
FrameworkListener("dnsproxyd"), mNetCtrl(netCtrl), mEventReporter(eventReporter) {
registerCmd(new GetAddrInfoCmd(this));
@@ -80,21 +140,6 @@
free(mHints);
}
-void DnsProxyListener::GetAddrInfoHandler::start() {
- pthread_t thread;
- pthread_create(&thread, NULL,
- DnsProxyListener::GetAddrInfoHandler::threadStart, this);
- pthread_detach(thread);
-}
-
-void* DnsProxyListener::GetAddrInfoHandler::threadStart(void* obj) {
- GetAddrInfoHandler* handler = reinterpret_cast<GetAddrInfoHandler*>(obj);
- handler->run();
- delete handler;
- pthread_exit(NULL);
- return NULL;
-}
-
static bool sendBE32(SocketClient* c, uint32_t data) {
uint32_t be_data = htonl(data);
return c->sendData(&be_data, sizeof(be_data)) == 0;
@@ -317,12 +362,10 @@
const int metricsLevel = mDnsProxyListener->mEventReporter->getMetricsReportingLevel();
- cli->incRef();
DnsProxyListener::GetAddrInfoHandler* handler =
new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netcontext,
metricsLevel, mDnsProxyListener->mEventReporter->getNetdEventListener());
- handler->start();
-
+ tryThreadOrError(cli, handler);
return 0;
}
@@ -364,12 +407,10 @@
uint32_t mark = mDnsProxyListener->mNetCtrl->getNetworkForDns(&netId, uid);
const int metricsLevel = mDnsProxyListener->mEventReporter->getMetricsReportingLevel();
- cli->incRef();
DnsProxyListener::GetHostByNameHandler* handler =
new DnsProxyListener::GetHostByNameHandler(cli, name, af, netId, mark, metricsLevel,
mDnsProxyListener->mEventReporter->getNetdEventListener());
- handler->start();
-
+ tryThreadOrError(cli, handler);
return 0;
}
@@ -389,21 +430,6 @@
free(mName);
}
-void DnsProxyListener::GetHostByNameHandler::start() {
- pthread_t thread;
- pthread_create(&thread, NULL,
- DnsProxyListener::GetHostByNameHandler::threadStart, this);
- pthread_detach(thread);
-}
-
-void* DnsProxyListener::GetHostByNameHandler::threadStart(void* obj) {
- GetHostByNameHandler* handler = reinterpret_cast<GetHostByNameHandler*>(obj);
- handler->run();
- delete handler;
- pthread_exit(NULL);
- return NULL;
-}
-
void DnsProxyListener::GetHostByNameHandler::run() {
if (DBG) {
ALOGD("DnsProxyListener::GetHostByNameHandler::run\n");
@@ -519,11 +545,9 @@
uint32_t mark = mDnsProxyListener->mNetCtrl->getNetworkForDns(&netId, uid);
- cli->incRef();
DnsProxyListener::GetHostByAddrHandler* handler =
new DnsProxyListener::GetHostByAddrHandler(cli, addr, addrLen, addrFamily, netId, mark);
- handler->start();
-
+ tryThreadOrError(cli, handler);
return 0;
}
@@ -545,21 +569,6 @@
free(mAddress);
}
-void DnsProxyListener::GetHostByAddrHandler::start() {
- pthread_t thread;
- pthread_create(&thread, NULL,
- DnsProxyListener::GetHostByAddrHandler::threadStart, this);
- pthread_detach(thread);
-}
-
-void* DnsProxyListener::GetHostByAddrHandler::threadStart(void* obj) {
- GetHostByAddrHandler* handler = reinterpret_cast<GetHostByAddrHandler*>(obj);
- handler->run();
- delete handler;
- pthread_exit(NULL);
- return NULL;
-}
-
void DnsProxyListener::GetHostByAddrHandler::run() {
if (DBG) {
ALOGD("DnsProxyListener::GetHostByAddrHandler::run\n");
diff --git a/server/DnsProxyListener.h b/server/DnsProxyListener.h
index 50cd73d..2c24bda 100644
--- a/server/DnsProxyListener.h
+++ b/server/DnsProxyListener.h
@@ -62,11 +62,9 @@
const android::sp<android::net::metrics::INetdEventListener>& listener);
~GetAddrInfoHandler();
- static void* threadStart(void* handler);
- void start();
+ void run();
private:
- void run();
SocketClient* mClient; // ref counted
char* mHost; // owned
char* mService; // owned
@@ -96,10 +94,10 @@
int reportingLevel,
const android::sp<android::net::metrics::INetdEventListener>& listener);
~GetHostByNameHandler();
- static void* threadStart(void* handler);
- void start();
- private:
+
void run();
+
+ private:
SocketClient* mClient; //ref counted
char* mName; // owned
int mAf;
@@ -129,11 +127,9 @@
uint32_t mark);
~GetHostByAddrHandler();
- static void* threadStart(void* handler);
- void start();
+ void run();
private:
- void run();
SocketClient* mClient; // ref counted
void* mAddress; // address to lookup; owned
int mAddressLen; // length of address to look up
diff --git a/server/IptablesBaseTest.cpp b/server/IptablesBaseTest.cpp
index faa7433..f879fe6 100644
--- a/server/IptablesBaseTest.cpp
+++ b/server/IptablesBaseTest.cpp
@@ -33,6 +33,7 @@
IptablesBaseTest::IptablesBaseTest() {
sCmds.clear();
sRestoreCmds.clear();
+ sReturnValues.clear();
}
int IptablesBaseTest::fake_android_fork_exec(int argc, char* argv[], int *status, bool, bool) {
@@ -43,10 +44,19 @@
cmd += argv[i];
}
sCmds.push_back(cmd);
- if (status) {
- *status = 0;
+
+ int ret;
+ if (sReturnValues.size()) {
+ ret = sReturnValues.front();
+ sReturnValues.pop_front();
+ } else {
+ ret = 0;
}
- return 0;
+
+ if (status) {
+ *status = ret;
+ }
+ return ret;
}
int IptablesBaseTest::fakeExecIptables(IptablesTarget target, ...) {
@@ -169,7 +179,12 @@
sRestoreCmds.clear();
}
+void IptablesBaseTest::setReturnValues(const std::deque<int>& returnValues) {
+ sReturnValues = returnValues;
+}
+
std::vector<std::string> IptablesBaseTest::sCmds = {};
IptablesBaseTest::ExpectedIptablesCommands IptablesBaseTest::sRestoreCmds = {};
std::deque<std::string> IptablesBaseTest::sPopenContents = {};
std::deque<std::string> IptablesBaseTest::sIptablesRestoreOutput = {};
+std::deque<int> IptablesBaseTest::sReturnValues = {};
diff --git a/server/IptablesBaseTest.h b/server/IptablesBaseTest.h
index 5843361..b8ce1e2 100644
--- a/server/IptablesBaseTest.h
+++ b/server/IptablesBaseTest.h
@@ -38,10 +38,12 @@
void expectIptablesCommands(const std::vector<ExpectedIptablesCommands>& snippets);
void expectIptablesRestoreCommands(const std::vector<std::string>& expectedCmds);
void expectIptablesRestoreCommands(const ExpectedIptablesCommands& expectedCmds);
+ void setReturnValues(const std::deque<int>& returnValues);
protected:
static std::vector<std::string> sCmds;
static ExpectedIptablesCommands sRestoreCmds;
+ static std::deque<int> sReturnValues;
static std::deque<std::string> sPopenContents;
static std::deque<std::string> sIptablesRestoreOutput;
int expectIptablesCommand(IptablesTarget target, int pos, const std::string& cmd);
diff --git a/server/IptablesRestoreController.cpp b/server/IptablesRestoreController.cpp
index 9eb023b..406983a 100644
--- a/server/IptablesRestoreController.cpp
+++ b/server/IptablesRestoreController.cpp
@@ -34,10 +34,9 @@
constexpr size_t PING_SIZE = sizeof(PING) - 1;
-// TODO: This mirrors &gCtls.iptablesRestoreCtrl in production and is duplicated
-// here to aid testing. It allows us to unit-test IptablesRestoreController without
-// needing to construct a fully fledged Controllers object.
-/* static */ IptablesRestoreController* sInstance = nullptr;
+// Not compile-time constants because they are changed by the unit tests.
+int IptablesRestoreController::MAX_RETRIES = 50;
+int IptablesRestoreController::POLL_TIMEOUT_MS = 100;
class IptablesProcess {
public:
@@ -266,14 +265,6 @@
process->errBuf.clear();
}
-// The maximum number of times we poll(2) for a response on our set of polled
-// fds. Chosen so that the overall timeout is 1s.
-static constexpr int MAX_RETRIES = 10;
-
-// The timeout (in millis) for each call to poll. The maximum wait is
-// |POLL_TIMEOUT_MS * MAX_RETRIES|. Chosen so that the overall timeout is 1s.
-static constexpr int POLL_TIMEOUT_MS = 100;
-
/* static */
bool IptablesRestoreController::drainAndWaitForAck(const std::unique_ptr<IptablesProcess> &process,
const std::string& command,
@@ -341,6 +332,9 @@
if (!receivedAck && !process->processTerminated) {
ALOGE("Timed out waiting for response from iptables process %d", process->pid);
+ // Kill the process so that if it eventually recovers, we don't misinterpret the ping
+ // response (or any output) of the command we just sent as coming from future commands.
+ process->stop();
}
maybeLogStderr(process, command);
@@ -355,6 +349,8 @@
std::string buffer;
if (output == nullptr) {
output = &buffer;
+ } else {
+ output->clear();
}
int res = 0;
diff --git a/server/IptablesRestoreController.h b/server/IptablesRestoreController.h
index a9b6fdc..4f58461 100644
--- a/server/IptablesRestoreController.h
+++ b/server/IptablesRestoreController.h
@@ -53,6 +53,15 @@
friend class IptablesRestoreControllerTest;
pid_t getIpRestorePid(const IptablesProcessType type);
+ // The maximum number of times we poll(2) for a response on our set of polled
+ // fds. Chosen so that the overall timeout is 5s. The timeout is so high because
+ // our version of iptables still polls every second in xtables_lock.
+ static int MAX_RETRIES;
+
+ // The timeout (in millis) for each call to poll. The maximum wait is
+ // |POLL_TIMEOUT_MS * MAX_RETRIES|. Chosen so that the overall timeout is 1s.
+ static int POLL_TIMEOUT_MS;
+
private:
static IptablesProcess* forkAndExec(const IptablesProcessType type);
diff --git a/server/IptablesRestoreControllerTest.cpp b/server/IptablesRestoreControllerTest.cpp
index 813028d..a5d8a7b 100644
--- a/server/IptablesRestoreControllerTest.cpp
+++ b/server/IptablesRestoreControllerTest.cpp
@@ -16,7 +16,8 @@
#include <string>
#include <fcntl.h>
-#include <signal.h>
+#include <sys/socket.h>
+#include <sys/un.h>
#include <gtest/gtest.h>
@@ -28,12 +29,28 @@
#include "IptablesRestoreController.h"
#include "NetdConstants.h"
+#define XTABLES_LOCK "@xtables"
+
using android::base::Join;
using android::base::StringPrintf;
class IptablesRestoreControllerTest : public ::testing::Test {
public:
IptablesRestoreController con;
+ int mDefaultMaxRetries = con.MAX_RETRIES;
+ int mDefaultPollTimeoutMs = con.POLL_TIMEOUT_MS;
+ int mIptablesLock = -1;
+ std::string mChainName;
+
+ void SetUp() {
+ ASSERT_EQ(0, createTestChain());
+ }
+
+ void TearDown() {
+ con.MAX_RETRIES = mDefaultMaxRetries;
+ con.POLL_TIMEOUT_MS = mDefaultPollTimeoutMs;
+ deleteTestChain();
+ }
pid_t getIpRestorePid(const IptablesRestoreController::IptablesProcessType type) {
return con.getIpRestorePid(type);
@@ -62,6 +79,62 @@
EXPECT_FALSE(statString.find("iptables-restor") || statString.find("ip6tables-resto"))
<< "Previous iptables-restore pid " << pid << " still alive: " << statString;
}
+
+ int createTestChain() {
+ mChainName = StringPrintf("netd_unit_test_%u", arc4random_uniform(10000)).c_str();
+
+ // Create a chain to list.
+ std::vector<std::string> createCommands = {
+ "*filter",
+ StringPrintf(":%s -", mChainName.c_str()),
+ StringPrintf("-A %s -j RETURN", mChainName.c_str()),
+ "COMMIT",
+ ""
+ };
+
+ int ret = con.execute(V4V6, Join(createCommands, "\n"), nullptr);
+ if (ret) mChainName = "";
+ return ret;
+ }
+
+ void deleteTestChain() {
+ std::vector<std::string> deleteCommands = {
+ "*filter",
+ StringPrintf(":%s -", mChainName.c_str()), // Flush chain (otherwise we can't delete it).
+ StringPrintf("-X %s", mChainName.c_str()), // Delete it.
+ "COMMIT",
+ ""
+ };
+ con.execute(V4V6, Join(deleteCommands, "\n"), nullptr);
+ mChainName = "";
+ }
+
+ int acquireIptablesLock() {
+ mIptablesLock = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (mIptablesLock == -1) {
+ return -errno;
+ }
+ sockaddr_un sun = { AF_UNIX, XTABLES_LOCK };
+ sun.sun_path[0] = '\0';
+ size_t len = offsetof(struct sockaddr_un, sun_path) + sizeof(XTABLES_LOCK) - 1;
+ if (int ret = bind(mIptablesLock, reinterpret_cast<sockaddr *>(&sun), len) == -1) {
+ ret = -errno;
+ close(mIptablesLock);
+ return ret;
+ }
+ return 0;
+ }
+
+ void releaseIptablesLock() {
+ if (mIptablesLock != -1) {
+ close(mIptablesLock);
+ }
+ }
+
+ void setRetryParameters(int maxRetries, int pollTimeoutMs) {
+ con.MAX_RETRIES = maxRetries;
+ con.POLL_TIMEOUT_MS = pollTimeoutMs;
+ }
};
TEST_F(IptablesRestoreControllerTest, TestBasicCommand) {
@@ -123,42 +196,39 @@
expectNoIptablesRestoreProcess(pid6);
}
-TEST_F(IptablesRestoreControllerTest, TestOutput) {
- const char *chainName = StringPrintf("netd_unit_test_%u", arc4random_uniform(10000)).c_str();
-
- // Create a chain to list.
- std::vector<std::string> createCommands = {
- "*filter",
- StringPrintf(":%s -", chainName),
- StringPrintf("-A %s -j RETURN", chainName),
- "COMMIT",
- ""
- };
- EXPECT_EQ(0, con.execute(V4V6, Join(createCommands, "\n"), nullptr));
+TEST_F(IptablesRestoreControllerTest, TestCommandTimeout) {
+ // Don't wait 10 seconds for this test to fail.
+ setRetryParameters(3, 50);
// Expected contents of the chain.
std::vector<std::string> expectedLines = {
- StringPrintf("Chain %s (0 references)", chainName),
+ StringPrintf("Chain %s (0 references)", mChainName.c_str()),
"target prot opt source destination ",
"RETURN all -- 0.0.0.0/0 0.0.0.0/0 ",
- StringPrintf("Chain %s (0 references)", chainName),
+ StringPrintf("Chain %s (0 references)", mChainName.c_str()),
"target prot opt source destination ",
"RETURN all ::/0 ::/0 ",
""
};
std::string expected = Join(expectedLines, "\n");
- // List and delete the chain.
std::vector<std::string> listCommands = {
"*filter",
- StringPrintf("-n -L %s", chainName), // List chain.
- StringPrintf(":%s -", chainName), // Flush chain (otherwise we can't delete it).
- StringPrintf("-X %s", chainName), // Delete chain.
+ StringPrintf("-n -L %s", mChainName.c_str()), // List chain.
"COMMIT",
""
};
-
+ std::string commandString = Join(listCommands, "\n");
std::string output;
- EXPECT_EQ(0, con.execute(V4V6, Join(listCommands, "\n"), &output));
+
+ EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, commandString, &output));
+ EXPECT_EQ(expected, output);
+
+ ASSERT_EQ(0, acquireIptablesLock());
+ EXPECT_EQ(-1, con.execute(IptablesTarget::V4V6, commandString, &output));
+ EXPECT_EQ(-1, con.execute(IptablesTarget::V4V6, commandString, &output));
+ releaseIptablesLock();
+
+ EXPECT_EQ(0, con.execute(IptablesTarget::V4V6, commandString, &output));
EXPECT_EQ(expected, output);
}
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index e1a0e8d..aba1458 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -45,8 +45,6 @@
namespace android {
namespace net {
-namespace {
-
// BEGIN CONSTANTS --------------------------------------------------------------------------------
const uint32_t RULE_PRIORITY_VPN_OVERRIDE_SYSTEM = 10000;
@@ -927,8 +925,6 @@
return ret;
}
-} // namespace
-
int RouteController::Init(unsigned localNetId) {
if (int ret = flushRules()) {
return ret;
diff --git a/server/RouteController.h b/server/RouteController.h
index 75b3ca3..48239d7 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -21,6 +21,7 @@
#include "Permission.h"
#include <sys/types.h>
+#include <linux/netlink.h>
namespace android {
namespace net {
@@ -92,6 +93,14 @@
Permission permission) WARN_UNUSED_RESULT;
};
+// Public because they are called by by RouteControllerTest.cpp.
+// TODO: come up with a scheme of unit testing this code that does not rely on making all its
+// functions public.
+int modifyIpRoute(uint16_t action, uint32_t table, const char* interface, const char* destination,
+ const char* nexthop) WARN_UNUSED_RESULT;
+int flushRoutes(uint32_t table) WARN_UNUSED_RESULT;
+uint32_t getRulePriority(const nlmsghdr *nlh);
+
} // namespace net
} // namespace android
diff --git a/server/RouteControllerTest.cpp b/server/RouteControllerTest.cpp
new file mode 100644
index 0000000..6bcb231
--- /dev/null
+++ b/server/RouteControllerTest.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * RouteControllerTest.cpp - unit tests for RouteController.cpp
+ */
+
+#include <gtest/gtest.h>
+
+#include "NetlinkCommands.h"
+#include "RouteController.h"
+
+namespace android {
+namespace net {
+
+TEST(RouteControllerTest, TestGetRulePriority) {
+ // Expect a rule dump for these two families to contain at least the following priorities.
+ for (int family : {AF_INET, AF_INET6 }) {
+ std::set<uint32_t> expectedPriorities = {
+ 0,
+ 15000, // RULE_PRIORITY_LEGACY_SYSTEM
+ 16000, // RULE_PRIORITY_LEGACY_NETWORK
+ 32000, // RULE_PRIORITY_UNREACHABLE
+ };
+
+ NetlinkDumpCallback callback = [&expectedPriorities] (const nlmsghdr *nlh) {
+ expectedPriorities.erase(getRulePriority(nlh));
+ };
+
+ rtmsg rtm = { .rtm_family = static_cast<uint8_t>(family) };
+ iovec iov[] = {
+ { nullptr, 0 },
+ { &rtm, sizeof(rtm) },
+ };
+
+ ASSERT_EQ(0, sendNetlinkRequest(RTM_GETRULE, NETLINK_DUMP_FLAGS,
+ iov, ARRAY_SIZE(iov), &callback));
+
+ EXPECT_TRUE(expectedPriorities.empty()) <<
+ "Did not see rule with priority " << *expectedPriorities.begin() <<
+ " in dump for address family " << family;
+ }
+}
+
+TEST(RouteControllerTest, TestRouteFlush) {
+ // Pick a table number that's not used by the system.
+ const uint32_t table1 = 500;
+ const uint32_t table2 = 600;
+ static_assert(table1 < RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX,
+ "Test table1 number too large");
+ static_assert(table2 < RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX,
+ "Test table2 number too large");
+
+ EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, table1, "lo", "192.0.2.2/32", NULL));
+ EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, table1, "lo", "192.0.2.3/32", NULL));
+ EXPECT_EQ(0, modifyIpRoute(RTM_NEWROUTE, table2, "lo", "192.0.2.4/32", NULL));
+
+ EXPECT_EQ(0, flushRoutes(table1));
+
+ EXPECT_EQ(-ESRCH,
+ modifyIpRoute(RTM_DELROUTE, table1, "lo", "192.0.2.2/32", NULL));
+ EXPECT_EQ(-ESRCH,
+ modifyIpRoute(RTM_DELROUTE, table1, "lo", "192.0.2.3/32", NULL));
+ EXPECT_EQ(0,
+ modifyIpRoute(RTM_DELROUTE, table2, "lo", "192.0.2.4/32", NULL));
+}
+
+} // namespace net
+} // namespace android
diff --git a/tests/Android.mk b/tests/Android.mk
index 7452463..7d47d45 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -38,6 +38,7 @@
dns_responder/dns_responder.cpp \
netd_integration_test.cpp \
netd_test.cpp \
+ tun_interface.cpp \
../server/NetdConstants.cpp \
../server/binder/android/net/metrics/INetdEventListener.aidl
LOCAL_MODULE_TAGS := eng tests
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index c88b02a..1481186 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -43,6 +43,7 @@
#include "NetdConstants.h"
#include "Stopwatch.h"
+#include "tun_interface.h"
#include "android/net/INetd.h"
#include "android/net/UidRange.h"
#include "binder/IServiceManager.h"
@@ -53,6 +54,7 @@
using namespace android::base;
using namespace android::binder;
using android::net::INetd;
+using android::net::TunInterface;
using android::net::UidRange;
static const char* IP_RULE_V4 = "-4";
@@ -75,33 +77,23 @@
// 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);
+ ASSERT_EQ(0, sTun.init());
+ ASSERT_LE(sTun.name().size(), static_cast<size_t>(IFNAMSIZ));
}
static void TearDownTestCase() {
// Closing the socket removes the interface and IP addresses.
- close(sTunFd);
+ sTun.destroy();
}
static void fakeRemoteSocketPair(int *clientSocket, int *serverSocket, int *acceptedSocket);
- static int createTunInterface();
protected:
sp<INetd> mNetd;
- static int sTunFd;
- static std::string sTunIfName;
- static in6_addr sSrcAddr, sDstAddr;
- static char sSrcStr[], sDstStr[];
+ static TunInterface sTun;
};
-int BinderTest::sTunFd;
-std::string BinderTest::sTunIfName;
-in6_addr BinderTest::sSrcAddr;
-in6_addr BinderTest::sDstAddr;
-char BinderTest::sSrcStr[INET6_ADDRSTRLEN];
-char BinderTest::sDstStr[INET6_ADDRSTRLEN];
+TunInterface BinderTest::sTun;
class TimedOperation : public Stopwatch {
public:
@@ -332,52 +324,10 @@
EXPECT_EQ(initialRulesV6, listIpRules(IP_RULE_V6));
}
-int BinderTest::createTunInterface() {
- // Generate a random ULA address pair.
- arc4random_buf(&sSrcAddr, sizeof(sSrcAddr));
- sSrcAddr.s6_addr[0] = 0xfd;
- memcpy(&sDstAddr, &sSrcAddr, sizeof(sDstAddr));
- sDstAddr.s6_addr[15] ^= 1;
-
- // Convert the addresses to strings because that's what ifc_add_address takes.
- sockaddr_in6 src6 = { .sin6_family = AF_INET6, .sin6_addr = sSrcAddr, };
- sockaddr_in6 dst6 = { .sin6_family = AF_INET6, .sin6_addr = sDstAddr, };
- int flags = NI_NUMERICHOST;
- if (getnameinfo((sockaddr *) &src6, sizeof(src6), sSrcStr, sizeof(sSrcStr), NULL, 0, flags) ||
- getnameinfo((sockaddr *) &dst6, sizeof(dst6), sDstStr, sizeof(sDstStr), NULL, 0, flags)) {
- return -1;
- }
-
- // 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), "%s", sTunIfName.c_str());
-
- int fd = open(TUN_DEV, O_RDWR | O_NONBLOCK | O_CLOEXEC);
- EXPECT_NE(-1, fd) << TUN_DEV << ": " << strerror(errno);
- if (fd == -1) return fd;
-
- int ret = ioctl(fd, TUNSETIFF, &ifr, sizeof(ifr));
- EXPECT_EQ(0, ret) << "TUNSETIFF: " << strerror(errno);
- if (ret) {
- close(fd);
- return -1;
- }
-
- if (ifc_add_address(ifr.ifr_name, sSrcStr, 64) ||
- ifc_add_address(ifr.ifr_name, sDstStr, 64)) {
- close(fd);
- return -1;
- }
- return fd;
-}
-
// Create a socket pair that isLoopbackSocket won't think is local.
void BinderTest::fakeRemoteSocketPair(int *clientSocket, int *serverSocket, int *acceptedSocket) {
*serverSocket = socket(AF_INET6, SOCK_STREAM, 0);
- struct sockaddr_in6 server6 = { .sin6_family = AF_INET6, .sin6_addr = sDstAddr };
+ struct sockaddr_in6 server6 = { .sin6_family = AF_INET6, .sin6_addr = sTun.dstAddr() };
ASSERT_EQ(0, bind(*serverSocket, (struct sockaddr *) &server6, sizeof(server6)));
socklen_t addrlen = sizeof(server6);
@@ -385,7 +335,7 @@
ASSERT_EQ(0, listen(*serverSocket, 10));
*clientSocket = socket(AF_INET6, SOCK_STREAM, 0);
- struct sockaddr_in6 client6 = { .sin6_family = AF_INET6, .sin6_addr = sSrcAddr };
+ struct sockaddr_in6 client6 = { .sin6_family = AF_INET6, .sin6_addr = sTun.srcAddr() };
ASSERT_EQ(0, bind(*clientSocket, (struct sockaddr *) &client6, sizeof(client6)));
ASSERT_EQ(0, connect(*clientSocket, (struct sockaddr *) &server6, sizeof(server6)));
ASSERT_EQ(0, getsockname(*clientSocket, (struct sockaddr *) &client6, &addrlen));
@@ -590,7 +540,7 @@
// [1.a] Add the address.
binder::Status status = mNetd->interfaceAddAddress(
- sTunIfName, td.addrString, td.prefixLength);
+ sTun.name(), td.addrString, td.prefixLength);
if (td.expectSuccess) {
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
} else {
@@ -600,13 +550,13 @@
// [1.b] Verify the addition meets the expectation.
if (td.expectSuccess) {
- EXPECT_TRUE(interfaceHasAddress(sTunIfName, td.addrString, td.prefixLength));
+ EXPECT_TRUE(interfaceHasAddress(sTun.name(), td.addrString, td.prefixLength));
} else {
- EXPECT_FALSE(interfaceHasAddress(sTunIfName, td.addrString, -1));
+ EXPECT_FALSE(interfaceHasAddress(sTun.name(), 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);
+ status = mNetd->interfaceDelAddress(sTun.name(), td.addrString, td.prefixLength);
if (td.expectSuccess) {
EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
} else {
@@ -615,7 +565,7 @@
}
// [2.b] No matter what, the address should not be present.
- EXPECT_FALSE(interfaceHasAddress(sTunIfName, td.addrString, -1));
+ EXPECT_FALSE(interfaceHasAddress(sTun.name(), td.addrString, -1));
}
}
@@ -628,13 +578,13 @@
const char *value;
const int expectedReturnCode;
} kTestData[] = {
- { INetd::IPV4, INetd::CONF, sTunIfName.c_str(), "arp_ignore", "1", 0 },
- { -1, INetd::CONF, sTunIfName.c_str(), "arp_ignore", "1", EAFNOSUPPORT },
- { INetd::IPV4, -1, sTunIfName.c_str(), "arp_ignore", "1", EINVAL },
+ { INetd::IPV4, INetd::CONF, sTun.name().c_str(), "arp_ignore", "1", 0 },
+ { -1, INetd::CONF, sTun.name().c_str(), "arp_ignore", "1", EAFNOSUPPORT },
+ { INetd::IPV4, -1, sTun.name().c_str(), "arp_ignore", "1", EINVAL },
{ INetd::IPV4, INetd::CONF, "..", "conf/lo/arp_ignore", "1", EINVAL },
{ INetd::IPV4, INetd::CONF, ".", "lo/arp_ignore", "1", EINVAL },
- { INetd::IPV4, INetd::CONF, sTunIfName.c_str(), "../all/arp_ignore", "1", EINVAL },
- { INetd::IPV6, INetd::NEIGH, sTunIfName.c_str(), "ucast_solicit", "7", 0 },
+ { INetd::IPV4, INetd::CONF, sTun.name().c_str(), "../all/arp_ignore", "1", EINVAL },
+ { INetd::IPV6, INetd::NEIGH, sTun.name().c_str(), "ucast_solicit", "7", 0 },
};
for (unsigned int i = 0; i < arraysize(kTestData); i++) {
diff --git a/tests/tun_interface.cpp b/tests/tun_interface.cpp
new file mode 100644
index 0000000..ddbf0c7
--- /dev/null
+++ b/tests/tun_interface.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * tun_interface.cpp - creates tun interfaces for testing purposes
+ */
+
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <netutils/ifc.h>
+
+#include "tun_interface.h"
+
+#define TUN_DEV "/dev/tun"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace net {
+
+int TunInterface::init() {
+ // Generate a random ULA address pair.
+ arc4random_buf(&mSrcAddr, sizeof(mSrcAddr));
+ mSrcAddr.s6_addr[0] = 0xfd;
+ memcpy(&mDstAddr, &mSrcAddr, sizeof(mDstAddr));
+ mDstAddr.s6_addr[15] ^= 1;
+
+ // Convert the addresses to strings because that's what ifc_add_address takes.
+ char srcStr[INET6_ADDRSTRLEN], dstStr[INET6_ADDRSTRLEN];
+ sockaddr_in6 src6 = { .sin6_family = AF_INET6, .sin6_addr = mSrcAddr, };
+ sockaddr_in6 dst6 = { .sin6_family = AF_INET6, .sin6_addr = mDstAddr, };
+ int flags = NI_NUMERICHOST;
+ if (getnameinfo((sockaddr *) &src6, sizeof(src6), srcStr, sizeof(srcStr), NULL, 0, flags) ||
+ getnameinfo((sockaddr *) &dst6, sizeof(dst6), dstStr, sizeof(dstStr), NULL, 0, flags)) {
+ return -EINVAL;
+ }
+
+ // Create a tun interface with a name based on our PID.
+ mIfName = StringPrintf("netdtest%u", getpid());
+ struct ifreq ifr = {
+ .ifr_ifru = { .ifru_flags = IFF_TUN },
+ };
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", mIfName.c_str());
+
+ mFd = open(TUN_DEV, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+ if (mFd == -1) return -errno;
+
+ int ret = ioctl(mFd, TUNSETIFF, &ifr, sizeof(ifr));
+ if (ret == -1) {
+ ret = -errno;
+ close(mFd);
+ return ret;
+ }
+
+ if (ifc_add_address(ifr.ifr_name, srcStr, 64) ||
+ ifc_add_address(ifr.ifr_name, dstStr, 64)) {
+ ret = -errno;
+ close(mFd);
+ return ret;
+ }
+
+ return 0;
+}
+
+void TunInterface::destroy() {
+ if (mFd != -1) {
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+} // namespace net
+} // namespace android
diff --git a/tests/tun_interface.h b/tests/tun_interface.h
new file mode 100644
index 0000000..b2427f8
--- /dev/null
+++ b/tests/tun_interface.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * tun_interface.h - creates tun interfaces for testing purposes
+ */
+
+#ifndef _SYSTEM_NETD_TESTS_TUN_INTERACE_H
+#define _SYSTEM_NETD_TESTS_TUN_INTERACE_H
+
+namespace android {
+namespace net {
+
+class TunInterface {
+public:
+ TunInterface() = default;
+ ~TunInterface() { destroy(); }
+
+ // Creates a tun interface. Returns 0 on success or -errno on failure. Must succeed before it is
+ // legal to call any of the other methods in this class.
+ int init();
+ void destroy();
+
+ const std::string& name() const { return mIfName; }
+ const in6_addr& srcAddr() const { return mSrcAddr; }
+ const in6_addr& dstAddr() const { return mDstAddr; }
+
+private:
+ int mFd = -1;
+ std::string mIfName;
+ in6_addr mSrcAddr, mDstAddr;
+};
+
+} // namespace net
+} // namespace android
+
+#endif