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