am 8246d45e: Merge "bundle init.rc contents with its service"

* commit '8246d45e94bfb0ce878f41a01602b7b8d78c40ad':
  bundle init.rc contents with its service
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/client/Android.mk b/client/Android.mk
index 0c5e7ea..e202534 100644
--- a/client/Android.mk
+++ b/client/Android.mk
@@ -16,7 +16,7 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_C_INCLUDES := bionic/libc/dns/include external/libcxx/include system/netd/include
+LOCAL_C_INCLUDES := bionic/libc/dns/include system/netd/include
 LOCAL_CLANG := true
 LOCAL_CPPFLAGS := -std=c++11 -Wall -Werror
 LOCAL_MODULE := libnetd_client
diff --git a/client/FwmarkClient.cpp b/client/FwmarkClient.cpp
index 0ac1fbb..a82f4c2 100644
--- a/client/FwmarkClient.cpp
+++ b/client/FwmarkClient.cpp
@@ -16,6 +16,8 @@
 
 #include "FwmarkClient.h"
 
+#include "FwmarkCommand.h"
+
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
@@ -42,7 +44,7 @@
     }
 }
 
-int FwmarkClient::send(void* data, size_t len, int fd) {
+int FwmarkClient::send(FwmarkCommand* data, int fd) {
     mChannel = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
     if (mChannel == -1) {
         return -errno;
@@ -57,7 +59,7 @@
 
     iovec iov;
     iov.iov_base = data;
-    iov.iov_len = len;
+    iov.iov_len = sizeof(*data);
 
     msghdr message;
     memset(&message, 0, sizeof(message));
@@ -69,15 +71,17 @@
         char cmsg[CMSG_SPACE(sizeof(fd))];
     } cmsgu;
 
-    memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
-    message.msg_control = cmsgu.cmsg;
-    message.msg_controllen = sizeof(cmsgu.cmsg);
+    if (data->cmdId != FwmarkCommand::QUERY_USER_ACCESS) {
+        memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
+        message.msg_control = cmsgu.cmsg;
+        message.msg_controllen = sizeof(cmsgu.cmsg);
 
-    cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
-    cmsgh->cmsg_len = CMSG_LEN(sizeof(fd));
-    cmsgh->cmsg_level = SOL_SOCKET;
-    cmsgh->cmsg_type = SCM_RIGHTS;
-    memcpy(CMSG_DATA(cmsgh), &fd, sizeof(fd));
+        cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
+        cmsgh->cmsg_len = CMSG_LEN(sizeof(fd));
+        cmsgh->cmsg_level = SOL_SOCKET;
+        cmsgh->cmsg_type = SCM_RIGHTS;
+        memcpy(CMSG_DATA(cmsgh), &fd, sizeof(fd));
+    }
 
     if (TEMP_FAILURE_RETRY(sendmsg(mChannel, &message, 0)) == -1) {
         return -errno;
diff --git a/client/FwmarkClient.h b/client/FwmarkClient.h
index 620275e..df7686d 100644
--- a/client/FwmarkClient.h
+++ b/client/FwmarkClient.h
@@ -19,6 +19,8 @@
 
 #include <sys/socket.h>
 
+struct FwmarkCommand;
+
 class FwmarkClient {
 public:
     // Returns true if a socket of the given |family| should be sent to the fwmark server to have
@@ -30,7 +32,7 @@
 
     // Sends |data| to the fwmark server, along with |fd| as ancillary data using cmsg(3).
     // Returns 0 on success or a negative errno value on failure.
-    int send(void* data, size_t len, int fd);
+    int send(FwmarkCommand* data, int fd);
 
 private:
     int mChannel;
diff --git a/client/NetdClient.cpp b/client/NetdClient.cpp
index 3157d3a..392b0af 100644
--- a/client/NetdClient.cpp
+++ b/client/NetdClient.cpp
@@ -65,7 +65,7 @@
     }
     if (FwmarkClient::shouldSetFwmark(family)) {
         FwmarkCommand command = {FwmarkCommand::ON_ACCEPT, 0, 0};
-        if (int error = FwmarkClient().send(&command, sizeof(command), acceptedSocket)) {
+        if (int error = FwmarkClient().send(&command, acceptedSocket)) {
             return closeFdAndSetErrno(acceptedSocket, error);
         }
     }
@@ -75,7 +75,7 @@
 int netdClientConnect(int sockfd, const sockaddr* addr, socklen_t addrlen) {
     if (sockfd >= 0 && addr && FwmarkClient::shouldSetFwmark(addr->sa_family)) {
         FwmarkCommand command = {FwmarkCommand::ON_CONNECT, 0, 0};
-        if (int error = FwmarkClient().send(&command, sizeof(command), sockfd)) {
+        if (int error = FwmarkClient().send(&command, sockfd)) {
             errno = -error;
             return -1;
         }
@@ -185,7 +185,7 @@
         return -EBADF;
     }
     FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0};
-    return FwmarkClient().send(&command, sizeof(command), socketFd);
+    return FwmarkClient().send(&command, socketFd);
 }
 
 extern "C" int setNetworkForProcess(unsigned netId) {
@@ -201,7 +201,7 @@
         return -EBADF;
     }
     FwmarkCommand command = {FwmarkCommand::PROTECT_FROM_VPN, 0, 0};
-    return FwmarkClient().send(&command, sizeof(command), socketFd);
+    return FwmarkClient().send(&command, socketFd);
 }
 
 extern "C" int setNetworkForUser(uid_t uid, int socketFd) {
@@ -209,5 +209,10 @@
         return -EBADF;
     }
     FwmarkCommand command = {FwmarkCommand::SELECT_FOR_USER, 0, uid};
-    return FwmarkClient().send(&command, sizeof(command), socketFd);
+    return FwmarkClient().send(&command, socketFd);
+}
+
+extern "C" int queryUserAccess(uid_t uid, unsigned netId) {
+    FwmarkCommand command = {FwmarkCommand::QUERY_USER_ACCESS, netId, uid};
+    return FwmarkClient().send(&command, -1);
 }
diff --git a/include/FwmarkCommand.h b/include/FwmarkCommand.h
index 57464b4..be06899 100644
--- a/include/FwmarkCommand.h
+++ b/include/FwmarkCommand.h
@@ -27,9 +27,11 @@
         SELECT_NETWORK,
         PROTECT_FROM_VPN,
         SELECT_FOR_USER,
+        QUERY_USER_ACCESS,
     } cmdId;
     unsigned netId;  // used only in the SELECT_NETWORK command; ignored otherwise.
-    uid_t uid;  // used only in the SELECT_FOR_USER command; ignored otherwise.
+    uid_t uid;  // used only in the SELECT_FOR_USER and QUERY_USER_ACCESS commands;
+                // ignored otherwise.
 };
 
 #endif  // NETD_INCLUDE_FWMARK_COMMAND_H
diff --git a/include/NetdClient.h b/include/NetdClient.h
index 71529a3..7db0906 100644
--- a/include/NetdClient.h
+++ b/include/NetdClient.h
@@ -37,6 +37,8 @@
 
 int setNetworkForUser(uid_t uid, int socketFd);
 
+int queryUserAccess(uid_t uid, unsigned netId);
+
 __END_DECLS
 
 #endif  // NETD_INCLUDE_NETD_CLIENT_H
diff --git a/server/Android.mk b/server/Android.mk
index 42bafea..d0cd49e 100644
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -50,6 +50,7 @@
         ClatdController.cpp \
         CommandListener.cpp \
         DnsProxyListener.cpp \
+        DummyNetwork.cpp \
         FirewallController.cpp \
         FwmarkServer.cpp \
         IdletimerController.cpp \
diff --git a/server/BandwidthController.cpp b/server/BandwidthController.cpp
index 8795f7c..d71ce3b 100644
--- a/server/BandwidthController.cpp
+++ b/server/BandwidthController.cpp
@@ -199,7 +199,7 @@
         break;
     }
 
-    fullCmd.insert(0, " ");
+    fullCmd.insert(0, " -w ");
     fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
 
     if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index b508d3f..7ecbffc 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -474,7 +474,7 @@
                 cli->sendMsg(ResponseCode::CommandOkay, "MTU changed", false);
             } else {
                 cli->sendMsg(ResponseCode::OperationFailed,
-                        "Failed to get MTU", true);
+                        "Failed to set MTU", true);
             }
             return 0;
         } else {
@@ -507,37 +507,59 @@
                  NetdCommand("ipfwd") {
 }
 
-int CommandListener::IpFwdCmd::runCommand(SocketClient *cli,
-                                                      int argc, char **argv) {
-    int rc = 0;
+int CommandListener::IpFwdCmd::runCommand(SocketClient *cli, int argc, char **argv) {
+    bool matched = false;
+    bool success;
 
-    if (argc < 2) {
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
-        return 0;
+    if (argc == 2) {
+        //   0     1
+        // ipfwd status
+        if (!strcmp(argv[1], "status")) {
+            char *tmp = NULL;
+
+            asprintf(&tmp, "Forwarding %s",
+                     ((sTetherCtrl->forwardingRequestCount() > 0) ? "enabled" : "disabled"));
+            cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);
+            free(tmp);
+            return 0;
+        }
+    } else if (argc == 3) {
+        //  0      1         2
+        // ipfwd enable  <requester>
+        // ipfwd disable <requester>
+        if (!strcmp(argv[1], "enable")) {
+            matched = true;
+            success = sTetherCtrl->enableForwarding(argv[2]);
+        } else if (!strcmp(argv[1], "disable")) {
+            matched = true;
+            success = sTetherCtrl->disableForwarding(argv[2]);
+        }
+    } else if (argc == 4) {
+        //  0      1      2     3
+        // ipfwd  add   wlan0 dummy0
+        // ipfwd remove wlan0 dummy0
+        int ret = 0;
+        if (!strcmp(argv[1], "add")) {
+            matched = true;
+            ret = RouteController::enableTethering(argv[2], argv[3]);
+        } else if (!strcmp(argv[1], "remove")) {
+            matched = true;
+            ret = RouteController::disableTethering(argv[2], argv[3]);
+        }
+        success = (ret == 0);
+        errno = -ret;
     }
 
-    if (!strcmp(argv[1], "status")) {
-        char *tmp = NULL;
-
-        asprintf(&tmp, "Forwarding %s", (sTetherCtrl->getIpFwdEnabled() ? "enabled" : "disabled"));
-        cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);
-        free(tmp);
-        return 0;
-    } else if (!strcmp(argv[1], "enable")) {
-        rc = sTetherCtrl->setIpFwdEnabled(true);
-    } else if (!strcmp(argv[1], "disable")) {
-        rc = sTetherCtrl->setIpFwdEnabled(false);
-    } else {
+    if (!matched) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown ipfwd cmd", false);
         return 0;
     }
 
-    if (!rc) {
+    if (success) {
         cli->sendMsg(ResponseCode::CommandOkay, "ipfwd operation succeeded", false);
     } else {
         cli->sendMsg(ResponseCode::OperationFailed, "ipfwd operation failed", true);
     }
-
     return 0;
 }
 
@@ -797,15 +819,19 @@
     int rc = 0;
     const char **argv = const_cast<const char **>(margv);
 
-    if (argc < 2) {
+    if (argc < 3) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Resolver missing arguments", false);
         return 0;
     }
 
+    unsigned netId = stringToNetId(argv[2]);
+    // TODO: Consider making NetworkController.isValidNetwork() public
+    // and making that check here.
+
     if (!strcmp(argv[1], "setnetdns")) {
         // "resolver setnetdns <netId> <domains> <dns1> <dns2> ..."
         if (argc >= 5) {
-            rc = sResolverCtrl->setDnsServers(strtoul(argv[2], NULL, 0), argv[3], &argv[4], argc - 4);
+            rc = sResolverCtrl->setDnsServers(netId, argv[3], &argv[4], argc - 4);
         } else {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Wrong number of arguments to resolver setnetdns", false);
@@ -813,7 +839,7 @@
         }
     } else if (!strcmp(argv[1], "clearnetdns")) { // "resolver clearnetdns <netId>"
         if (argc == 3) {
-            rc = sResolverCtrl->clearDnsServers(strtoul(argv[2], NULL, 0));
+            rc = sResolverCtrl->clearDnsServers(netId);
         } else {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Wrong number of arguments to resolver clearnetdns", false);
@@ -821,7 +847,7 @@
         }
     } else if (!strcmp(argv[1], "flushnet")) { // "resolver flushnet <netId>"
         if (argc == 3) {
-            rc = sResolverCtrl->flushDnsCache(strtoul(argv[2], NULL, 0));
+            rc = sResolverCtrl->flushDnsCache(netId);
         } else {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Wrong number of arguments to resolver flushnet", false);
@@ -1249,8 +1275,35 @@
 FirewallRule CommandListener::FirewallCmd::parseRule(const char* arg) {
     if (!strcmp(arg, "allow")) {
         return ALLOW;
-    } else {
+    } else if (!strcmp(arg, "deny")) {
         return DENY;
+    } else {
+        ALOGE("failed to parse uid rule (%s)", arg);
+        return ALLOW;
+    }
+}
+
+FirewallType CommandListener::FirewallCmd::parseFirewallType(const char* arg) {
+    if (!strcmp(arg, "whitelist")) {
+        return WHITELIST;
+    } else if (!strcmp(arg, "blacklist")) {
+        return BLACKLIST;
+    } else {
+        ALOGE("failed to parse firewall type (%s)", arg);
+        return BLACKLIST;
+    }
+}
+
+ChildChain CommandListener::FirewallCmd::parseChildChain(const char* arg) {
+    if (!strcmp(arg, "dozable")) {
+        return DOZABLE;
+    } else if (!strcmp(arg, "standby")) {
+        return STANDBY;
+    } else if (!strcmp(arg, "none")) {
+        return NONE;
+    } else {
+        ALOGE("failed to parse child firewall chain (%s)", arg);
+        return INVALID_CHAIN;
     }
 }
 
@@ -1262,7 +1315,14 @@
     }
 
     if (!strcmp(argv[1], "enable")) {
-        int res = sFirewallCtrl->enableFirewall();
+        if (argc != 3) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                        "Usage: firewall enable <whitelist|blacklist>", false);
+            return 0;
+        }
+        FirewallType firewallType = parseFirewallType(argv[2]);
+
+        int res = sFirewallCtrl->enableFirewall(firewallType);
         return sendGenericOkFail(cli, res);
     }
     if (!strcmp(argv[1], "disable")) {
@@ -1322,17 +1382,49 @@
     }
 
     if (!strcmp(argv[1], "set_uid_rule")) {
-        if (argc != 4) {
+        if (argc != 5) {
             cli->sendMsg(ResponseCode::CommandSyntaxError,
-                         "Usage: firewall set_uid_rule <1000> <allow|deny>",
+                         "Usage: firewall set_uid_rule <dozable|standby|none> <1000> <allow|deny>",
                          false);
             return 0;
         }
 
-        int uid = atoi(argv[2]);
-        FirewallRule rule = parseRule(argv[3]);
+        ChildChain childChain = parseChildChain(argv[2]);
+        if (childChain == INVALID_CHAIN) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                         "Invalid chain name. Valid names are: <dozable|standby|none>",
+                         false);
+            return 0;
+        }
+        int uid = atoi(argv[3]);
+        FirewallRule rule = parseRule(argv[4]);
+        int res = sFirewallCtrl->setUidRule(childChain, uid, rule);
+        return sendGenericOkFail(cli, res);
+    }
 
-        int res = sFirewallCtrl->setUidRule(uid, rule);
+    if (!strcmp(argv[1], "enable_chain")) {
+        if (argc != 3) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                         "Usage: firewall enable_chain <dozable|standby>",
+                         false);
+            return 0;
+        }
+
+        ChildChain childChain = parseChildChain(argv[2]);
+        int res = sFirewallCtrl->enableChildChains(childChain, true);
+        return sendGenericOkFail(cli, res);
+    }
+
+    if (!strcmp(argv[1], "disable_chain")) {
+        if (argc != 3) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError,
+                         "Usage: firewall disable_chain <dozable|standby>",
+                         false);
+            return 0;
+        }
+
+        ChildChain childChain = parseChildChain(argv[2]);
+        int res = sFirewallCtrl->enableChildChains(childChain, false);
         return sendGenericOkFail(cli, res);
     }
 
@@ -1633,24 +1725,36 @@
         if (nextArg == argc) {
             return syntaxError(client, "Missing id");
         }
+
+        bool userPermissions = !strcmp(argv[2], "user");
+        bool networkPermissions = !strcmp(argv[2], "network");
+        if (!userPermissions && !networkPermissions) {
+            return syntaxError(client, "Unknown argument");
+        }
+
         std::vector<unsigned> ids;
         for (; nextArg < argc; ++nextArg) {
-            char* endPtr;
-            unsigned id = strtoul(argv[nextArg], &endPtr, 0);
-            if (!*argv[nextArg] || *endPtr) {
-                return syntaxError(client, "Invalid id");
+            if (userPermissions) {
+                char* endPtr;
+                unsigned id = strtoul(argv[nextArg], &endPtr, 0);
+                if (!*argv[nextArg] || *endPtr) {
+                    return syntaxError(client, "Invalid id");
+                }
+                ids.push_back(id);
+            } else {
+                // networkPermissions
+                ids.push_back(stringToNetId(argv[nextArg]));
             }
-            ids.push_back(id);
         }
-        if (!strcmp(argv[2], "user")) {
+        if (userPermissions) {
             sNetCtrl->setPermissionForUsers(permission, ids);
-        } else if (!strcmp(argv[2], "network")) {
+        } else {
+            // networkPermissions
             if (int ret = sNetCtrl->setPermissionForNetworks(permission, ids)) {
                 return operationError(client, "setPermissionForNetworks() failed", ret);
             }
-        } else {
-            return syntaxError(client, "Unknown argument");
         }
+
         return success(client);
     }
 
diff --git a/server/CommandListener.h b/server/CommandListener.h
index b71bc45..72f4da1 100644
--- a/server/CommandListener.h
+++ b/server/CommandListener.h
@@ -136,6 +136,8 @@
     protected:
         int sendGenericOkFail(SocketClient *cli, int cond);
         static FirewallRule parseRule(const char* arg);
+        static FirewallType parseFirewallType(const char* arg);
+        static ChildChain parseChildChain(const char* arg);
     };
 
     class ClatdCmd : public NetdCommand {
diff --git a/server/DnsProxyListener.cpp b/server/DnsProxyListener.cpp
index a0f0a30..6c71c5b 100644
--- a/server/DnsProxyListener.cpp
+++ b/server/DnsProxyListener.cpp
@@ -48,18 +48,14 @@
     registerCmd(new GetHostByNameCmd(this));
 }
 
-DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(SocketClient *c,
-                                                         char* host,
-                                                         char* service,
-                                                         struct addrinfo* hints,
-                                                         unsigned netId,
-                                                         uint32_t mark)
+DnsProxyListener::GetAddrInfoHandler::GetAddrInfoHandler(
+        SocketClient *c, char* host, char* service, struct addrinfo* hints,
+        const struct android_net_context& netcontext)
         : mClient(c),
           mHost(host),
           mService(service),
           mHints(hints),
-          mNetId(netId),
-          mMark(mark) {
+          mNetContext(netcontext) {
 }
 
 DnsProxyListener::GetAddrInfoHandler::~GetAddrInfoHandler() {
@@ -160,11 +156,14 @@
 
 void DnsProxyListener::GetAddrInfoHandler::run() {
     if (DBG) {
-        ALOGD("GetAddrInfoHandler, now for %s / %s / %u / %u", mHost, mService, mNetId, mMark);
+        ALOGD("GetAddrInfoHandler, now for %s / %s / {%u,%u,%u,%u,%u}", mHost, mService,
+                mNetContext.app_netid, mNetContext.app_mark,
+                mNetContext.dns_netid, mNetContext.dns_mark,
+                mNetContext.uid);
     }
 
     struct addrinfo* result = NULL;
-    uint32_t rv = android_getaddrinfofornet(mHost, mService, mHints, mNetId, mMark, &result);
+    uint32_t rv = android_getaddrinfofornetcontext(mHost, mService, mHints, &mNetContext, &result);
     if (rv) {
         // getaddrinfo failed
         mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));
@@ -229,7 +228,8 @@
     unsigned netId = strtoul(argv[7], NULL, 10);
     uid_t uid = cli->getUid();
 
-    uint32_t mark = mDnsProxyListener->mNetCtrl->getNetworkForDns(&netId, uid);
+    struct android_net_context netcontext;
+    mDnsProxyListener->mNetCtrl->getNetworkContext(netId, uid, &netcontext);
 
     if (ai_flags != -1 || ai_family != -1 ||
         ai_socktype != -1 || ai_protocol != -1) {
@@ -242,21 +242,23 @@
         // Only implement AI_ADDRCONFIG if application is using default network since our
         // implementation only works on the default network.
         if ((hints->ai_flags & AI_ADDRCONFIG) &&
-                netId != mDnsProxyListener->mNetCtrl->getDefaultNetwork()) {
+                netcontext.dns_netid != mDnsProxyListener->mNetCtrl->getDefaultNetwork()) {
             hints->ai_flags &= ~AI_ADDRCONFIG;
         }
     }
 
     if (DBG) {
-        ALOGD("GetAddrInfoHandler for %s / %s / %u / %d / %u",
+        ALOGD("GetAddrInfoHandler for %s / %s / {%u,%u,%u,%u,%u}",
              name ? name : "[nullhost]",
              service ? service : "[nullservice]",
-             netId, uid, mark);
+             netcontext.app_netid, netcontext.app_mark,
+             netcontext.dns_netid, netcontext.dns_mark,
+             netcontext.uid);
     }
 
     cli->incRef();
     DnsProxyListener::GetAddrInfoHandler* handler =
-            new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netId, mark);
+            new DnsProxyListener::GetAddrInfoHandler(cli, name, service, hints, netcontext);
     handler->start();
 
     return 0;
diff --git a/server/DnsProxyListener.h b/server/DnsProxyListener.h
index 106961e..bd7251d 100644
--- a/server/DnsProxyListener.h
+++ b/server/DnsProxyListener.h
@@ -17,6 +17,7 @@
 #ifndef _DNSPROXYLISTENER_H__
 #define _DNSPROXYLISTENER_H__
 
+#include <resolv_netid.h>  // struct android_net_context
 #include <sysutils/FrameworkListener.h>
 
 #include "NetdCommand.h"
@@ -46,8 +47,7 @@
                            char* host,
                            char* service,
                            struct addrinfo* hints,
-                           unsigned netId,
-                           uint32_t mark);
+                           const struct android_net_context& netcontext);
         ~GetAddrInfoHandler();
 
         static void* threadStart(void* handler);
@@ -59,8 +59,7 @@
         char* mHost;    // owned
         char* mService; // owned
         struct addrinfo* mHints;  // owned
-        unsigned mNetId;
-        uint32_t mMark;
+        struct android_net_context mNetContext;
     };
 
     /* ------ gethostbyname ------*/
diff --git a/server/DummyNetwork.cpp b/server/DummyNetwork.cpp
new file mode 100644
index 0000000..a3b3435
--- /dev/null
+++ b/server/DummyNetwork.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "DummyNetwork.h"
+
+#include "RouteController.h"
+
+#define LOG_TAG "Netd"
+#include "log/log.h"
+#include "errno.h"
+
+const char* DummyNetwork::INTERFACE_NAME = "dummy0";
+
+DummyNetwork::DummyNetwork(unsigned netId) : Network(netId) {
+    mInterfaces.insert(INTERFACE_NAME);
+}
+
+DummyNetwork::~DummyNetwork() {
+}
+
+Network::Type DummyNetwork::getType() const {
+    return DUMMY;
+}
+
+int DummyNetwork::addInterface(const std::string& /* interface */) {
+    return -EINVAL;
+}
+
+int DummyNetwork::removeInterface(const std::string& /* interface */) {
+    return -EINVAL;
+}
diff --git a/server/DummyNetwork.h b/server/DummyNetwork.h
new file mode 100644
index 0000000..7bc0d3d
--- /dev/null
+++ b/server/DummyNetwork.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef NETD_SERVER_DUMMY_NETWORK_H
+#define NETD_SERVER_DUMMY_NETWORK_H
+
+#include "Network.h"
+
+class DummyNetwork : public Network {
+public:
+    static const char* INTERFACE_NAME;
+    explicit DummyNetwork(unsigned netId);
+    virtual ~DummyNetwork();
+
+private:
+    Type getType() const override;
+    int addInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+    int removeInterface(const std::string& interface) override WARN_UNUSED_RESULT;
+};
+
+#endif  // NETD_SERVER_DUMMY_NETWORK_H
diff --git a/server/FirewallController.cpp b/server/FirewallController.cpp
index 17c6da4..bcf7524 100644
--- a/server/FirewallController.cpp
+++ b/server/FirewallController.cpp
@@ -23,38 +23,61 @@
 #define LOG_NDEBUG 0
 
 #include <cutils/log.h>
+#include <private/android_filesystem_config.h>
 
 #include "NetdConstants.h"
 #include "FirewallController.h"
 
+const char* FirewallController::TABLE = "filter";
+
 const char* FirewallController::LOCAL_INPUT = "fw_INPUT";
 const char* FirewallController::LOCAL_OUTPUT = "fw_OUTPUT";
 const char* FirewallController::LOCAL_FORWARD = "fw_FORWARD";
 
+const char* FirewallController::LOCAL_DOZABLE = "fw_dozable";
+const char* FirewallController::LOCAL_STANDBY = "fw_standby";
+
 FirewallController::FirewallController(void) {
+    // If no rules are set, it's in BLACKLIST mode
+    mFirewallType = BLACKLIST;
 }
 
 int FirewallController::setupIptablesHooks(void) {
-    return 0;
+    int res = 0;
+    // child chains are created but not attached, they will be attached explicitly.
+    FirewallType firewallType = getFirewallType(DOZABLE);
+    res |= createChain(LOCAL_DOZABLE, LOCAL_INPUT, firewallType);
+
+    firewallType = getFirewallType(STANDBY);
+    res |= createChain(LOCAL_STANDBY, LOCAL_INPUT, firewallType);
+
+    return res;
 }
 
-int FirewallController::enableFirewall(void) {
+int FirewallController::enableFirewall(FirewallType ftype) {
     int res = 0;
+    if (mFirewallType != ftype) {
+        // flush any existing rules
+        disableFirewall();
 
-    // flush any existing rules
-    disableFirewall();
+        if (ftype == WHITELIST) {
+            // create default rule to drop all traffic
+            res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
+            res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
+            res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
+        }
 
-    // create default rule to drop all traffic
-    res |= execIptables(V4V6, "-A", LOCAL_INPUT, "-j", "DROP", NULL);
-    res |= execIptables(V4V6, "-A", LOCAL_OUTPUT, "-j", "REJECT", NULL);
-    res |= execIptables(V4V6, "-A", LOCAL_FORWARD, "-j", "REJECT", NULL);
-
+        // Set this after calling disableFirewall(), since it defaults to WHITELIST there
+        mFirewallType = ftype;
+    }
     return res;
 }
 
 int FirewallController::disableFirewall(void) {
     int res = 0;
 
+    mFirewallType = WHITELIST;
+
     // flush any existing rules
     res |= execIptables(V4V6, "-F", LOCAL_INPUT, NULL);
     res |= execIptables(V4V6, "-F", LOCAL_OUTPUT, NULL);
@@ -63,12 +86,41 @@
     return res;
 }
 
+int FirewallController::enableChildChains(ChildChain chain, bool enable) {
+    int res = 0;
+    const char* name;
+    switch(chain) {
+        case DOZABLE:
+            name = LOCAL_DOZABLE;
+            break;
+        case STANDBY:
+            name = LOCAL_STANDBY;
+            break;
+        default:
+            return res;
+    }
+
+    if (enable) {
+        res |= attachChain(name, LOCAL_INPUT);
+        res |= attachChain(name, LOCAL_OUTPUT);
+    } else {
+        res |= detachChain(name, LOCAL_INPUT);
+        res |= detachChain(name, LOCAL_OUTPUT);
+    }
+    return res;
+}
+
 int FirewallController::isFirewallEnabled(void) {
     // TODO: verify that rules are still in place near top
     return -1;
 }
 
 int FirewallController::setInterfaceRule(const char* iface, FirewallRule rule) {
+    if (mFirewallType == BLACKLIST) {
+        // Unsupported in BLACKLIST mode
+        return -1;
+    }
+
     if (!isIfaceName(iface)) {
         errno = ENOENT;
         return -1;
@@ -88,6 +140,11 @@
 }
 
 int FirewallController::setEgressSourceRule(const char* addr, FirewallRule rule) {
+    if (mFirewallType == BLACKLIST) {
+        // Unsupported in BLACKLIST mode
+        return -1;
+    }
+
     IptablesTarget target = V4;
     if (strchr(addr, ':')) {
         target = V6;
@@ -108,6 +165,11 @@
 
 int FirewallController::setEgressDestRule(const char* addr, int protocol, int port,
         FirewallRule rule) {
+    if (mFirewallType == BLACKLIST) {
+        // Unsupported in BLACKLIST mode
+        return -1;
+    }
+
     IptablesTarget target = V4;
     if (strchr(addr, ':')) {
         target = V6;
@@ -134,21 +196,81 @@
     return res;
 }
 
-int FirewallController::setUidRule(int uid, FirewallRule rule) {
+FirewallType FirewallController::getFirewallType(ChildChain chain) {
+    switch(chain) {
+        case DOZABLE:
+            return WHITELIST;
+        case STANDBY:
+            return BLACKLIST;
+        case NONE:
+            return mFirewallType;
+        default:
+            return BLACKLIST;
+    }
+}
+
+int FirewallController::setUidRule(ChildChain chain, int uid, FirewallRule rule) {
     char uidStr[16];
     sprintf(uidStr, "%d", uid);
 
     const char* op;
-    if (rule == ALLOW) {
-        op = "-I";
-    } else {
-        op = "-D";
+    const char* target;
+    FirewallType firewallType = getFirewallType(chain);
+    if (firewallType == WHITELIST) {
+        target = "RETURN";
+        op = (rule == ALLOW)? "-I" : "-D";
+    } else { // BLACKLIST mode
+        target = "DROP";
+        op = (rule == DENY)? "-I" : "-D";
     }
 
     int res = 0;
-    res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
-            "-j", "RETURN", NULL);
-    res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
-            "-j", "RETURN", NULL);
+    switch(chain) {
+        case DOZABLE:
+            res |= execIptables(V4V6, op, LOCAL_DOZABLE, "-m", "owner", "--uid-owner",
+                    uidStr, "-j", target, NULL);
+            break;
+        case STANDBY:
+            res |= execIptables(V4V6, op, LOCAL_STANDBY, "-m", "owner", "--uid-owner",
+                    uidStr, "-j", target, NULL);
+            break;
+        case NONE:
+            res |= execIptables(V4V6, op, LOCAL_INPUT, "-m", "owner", "--uid-owner", uidStr,
+                    "-j", target, NULL);
+            res |= execIptables(V4V6, op, LOCAL_OUTPUT, "-m", "owner", "--uid-owner", uidStr,
+                    "-j", target, NULL);
+            break;
+        default:
+            ALOGW("Unknown child chain: %d", chain);
+            break;
+    }
+    return res;
+}
+
+int FirewallController::attachChain(const char* childChain, const char* parentChain) {
+    return execIptables(V4V6, "-t", TABLE, "-A", parentChain, "-j", childChain, NULL);
+}
+
+int FirewallController::detachChain(const char* childChain, const char* parentChain) {
+    return execIptables(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
+}
+
+int FirewallController::createChain(const char* childChain,
+        const char* parentChain, FirewallType type) {
+    // Order is important, otherwise later steps may fail.
+    execIptablesSilently(V4V6, "-t", TABLE, "-D", parentChain, "-j", childChain, NULL);
+    execIptablesSilently(V4V6, "-t", TABLE, "-F", childChain, NULL);
+    execIptablesSilently(V4V6, "-t", TABLE, "-X", childChain, NULL);
+    int res = 0;
+    res |= execIptables(V4V6, "-t", TABLE, "-N", childChain, NULL);
+    if (type == WHITELIST) {
+        // create default white list for system uid range
+        char uidStr[16];
+        sprintf(uidStr, "0-%d", AID_APP - 1);
+        res |= execIptables(V4V6, "-A", childChain, "-m", "owner", "--uid-owner",
+                uidStr, "-j", "RETURN", NULL);
+        // create default rule to drop all traffic
+        res |= execIptables(V4V6, "-A", childChain, "-j", "DROP", NULL);
+    }
     return res;
 }
diff --git a/server/FirewallController.h b/server/FirewallController.h
index 158e0fa..b32072e 100644
--- a/server/FirewallController.h
+++ b/server/FirewallController.h
@@ -19,7 +19,14 @@
 
 #include <string>
 
-enum FirewallRule { ALLOW, DENY };
+enum FirewallRule { DENY, ALLOW };
+
+// WHITELIST means the firewall denies all by default, uids must be explicitly ALLOWed
+// BLACKLIST means the firewall allows all by default, uids must be explicitly DENYed
+
+enum FirewallType { WHITELIST, BLACKLIST };
+
+enum ChildChain { NONE, DOZABLE, STANDBY, INVALID_CHAIN };
 
 #define PROTOCOL_TCP 6
 #define PROTOCOL_UDP 17
@@ -34,7 +41,7 @@
 
     int setupIptablesHooks(void);
 
-    int enableFirewall(void);
+    int enableFirewall(FirewallType);
     int disableFirewall(void);
     int isFirewallEnabled(void);
 
@@ -44,13 +51,25 @@
     int setEgressSourceRule(const char*, FirewallRule);
     /* Match traffic coming-in-from or going-out-to given address, port, and protocol. */
     int setEgressDestRule(const char*, int, int, FirewallRule);
-    /* Match traffic owned by given UID. */
-    int setUidRule(int, FirewallRule);
+    /* Match traffic owned by given UID. This is specific to a particular chain. */
+    int setUidRule(ChildChain, int, FirewallRule);
+
+    int enableChildChains(ChildChain, bool);
+
+    static const char* TABLE;
 
     static const char* LOCAL_INPUT;
     static const char* LOCAL_OUTPUT;
     static const char* LOCAL_FORWARD;
 
+    static const char* LOCAL_DOZABLE;
+    static const char* LOCAL_STANDBY;
+private:
+    FirewallType mFirewallType;
+    int attachChain(const char*, const char*);
+    int detachChain(const char*, const char*);
+    int createChain(const char*, const char*, FirewallType);
+    FirewallType getFirewallType(ChildChain);
 };
 
 #endif
diff --git a/server/FwmarkServer.cpp b/server/FwmarkServer.cpp
index b11e075..530e96a 100644
--- a/server/FwmarkServer.cpp
+++ b/server/FwmarkServer.cpp
@@ -75,6 +75,15 @@
         return -EBADMSG;
     }
 
+    Permission permission = mNetworkController->getPermissionForUser(client->getUid());
+
+    if (command.cmdId == FwmarkCommand::QUERY_USER_ACCESS) {
+        if ((permission & PERMISSION_SYSTEM) != PERMISSION_SYSTEM) {
+            return -EPERM;
+        }
+        return mNetworkController->checkUserNetworkAccess(command.uid, command.netId);
+    }
+
     cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
     if (cmsgh && cmsgh->cmsg_level == SOL_SOCKET && cmsgh->cmsg_type == SCM_RIGHTS &&
         cmsgh->cmsg_len == CMSG_LEN(sizeof(*socketFd))) {
@@ -91,8 +100,6 @@
         return -errno;
     }
 
-    Permission permission = mNetworkController->getPermissionForUser(client->getUid());
-
     switch (command.cmdId) {
         case FwmarkCommand::ON_ACCEPT: {
             // Called after a socket accept(). The kernel would've marked the NetId and necessary
diff --git a/server/IdletimerController.cpp b/server/IdletimerController.cpp
index ae7ae04..e6306fd 100644
--- a/server/IdletimerController.cpp
+++ b/server/IdletimerController.cpp
@@ -154,6 +154,7 @@
   int res;
   const char *cmd1[] = {
       NULL, // To be filled inside runIpxtablesCmd
+      "-w",
       "-t",
       "raw",
       "-F",
@@ -166,6 +167,7 @@
 
   const char *cmd2[] = {
       NULL, // To be filled inside runIpxtablesCmd
+      "-w",
       "-t",
       "mangle",
       "-F",
@@ -201,6 +203,7 @@
 
   const char *cmd1[] = {
       NULL, // To be filled inside runIpxtablesCmd
+      "-w",
       "-t",
       "raw",
       (op == IptOpAdd) ? "-A" : "-D",
@@ -223,6 +226,7 @@
 
   const char *cmd2[] = {
       NULL, // To be filled inside runIpxtablesCmd
+      "-w",
       "-t",
       "mangle",
       (op == IptOpAdd) ? "-A" : "-D",
diff --git a/server/InterfaceController.cpp b/server/InterfaceController.cpp
index 76f5e05..3882bcc 100644
--- a/server/InterfaceController.cpp
+++ b/server/InterfaceController.cpp
@@ -27,16 +27,61 @@
 #include "InterfaceController.h"
 #include "RouteController.h"
 
-using android::base::ReadFileToString;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
 
+namespace {
+
 const char ipv6_proc_path[] = "/proc/sys/net/ipv6/conf";
 
+const char ipv4_neigh_conf_dir[] = "/proc/sys/net/ipv4/neigh";
+
+const char ipv6_neigh_conf_dir[] = "/proc/sys/net/ipv6/neigh";
+
 const char sys_net_path[] = "/sys/class/net";
 
 const char wl_util_path[] = "/system/xbin/wlutil";
 
+bool isInterfaceName(const char *name) {
+    return strcmp(name, ".") &&
+            strcmp(name, "..") &&
+            strcmp(name, "default") &&
+            strcmp(name, "all");
+}
+
+int writeValueToPath(
+        const char* dirname, const char* subdirname, const char* basename,
+        const char* value) {
+    std::string path(StringPrintf("%s/%s/%s", dirname, subdirname, basename));
+    return WriteStringToFile(value, path) ? 0 : -1;
+}
+
+void setOnAllInterfaces(const char* dirname, const char* basename, const char* value) {
+    // Set the default value, which is used by any interfaces that are created in the future.
+    writeValueToPath(dirname, "default", basename, value);
+
+    // Set the value on all the interfaces that currently exist.
+    DIR* dir = opendir(dirname);
+    if (!dir) {
+        ALOGE("Can't list %s: %s", dirname, strerror(errno));
+        return;
+    }
+    dirent* d;
+    while ((d = readdir(dir))) {
+        if ((d->d_type != DT_DIR) || !isInterfaceName(d->d_name)) {
+            continue;
+        }
+        writeValueToPath(dirname, d->d_name, basename, value);
+    }
+    closedir(dir);
+}
+
+void setIPv6UseOutgoingInterfaceAddrsOnly(const char *value) {
+    setOnAllInterfaces(ipv6_proc_path, "use_oif_addrs_only", value);
+}
+
+}  // namespace
+
 InterfaceController::InterfaceController() {
 	// Initial IPv6 settings.
 	// By default, accept_ra is set to 1 (accept RAs unless forwarding is on) on all interfaces.
@@ -49,32 +94,38 @@
 
 	// Enable optimistic DAD for IPv6 addresses on all interfaces.
 	setIPv6OptimisticMode("1");
+
+	// Reduce the ARP/ND base reachable time from the default (30sec) to 15sec.
+	setBaseReachableTimeMs(15 * 1000);
+
+	// When sending traffic via a given interface use only addresses configured
+        // on that interface as possible source addresses.
+	setIPv6UseOutgoingInterfaceAddrsOnly("1");
 }
 
 InterfaceController::~InterfaceController() {
 }
 
-int InterfaceController::writeIPv6ProcPath(const char *interface, const char *setting, const char *value) {
-	if (!isIfaceName(interface)) {
-		errno = ENOENT;
-		return -1;
-	}
-	std::string path(StringPrintf("%s/%s/%s", ipv6_proc_path, interface, setting));
-	return WriteStringToFile(value, path);
-}
-
 int InterfaceController::setEnableIPv6(const char *interface, const int on) {
-	// When disable_ipv6 changes from 1 to 0, the kernel starts autoconf.
-	// When disable_ipv6 changes from 0 to 1, the kernel clears all autoconf
-	// addresses and routes and disables IPv6 on the interface.
-	const char *disable_ipv6 = on ? "0" : "1";
-	return writeIPv6ProcPath(interface, "disable_ipv6", disable_ipv6);
+    if (!isIfaceName(interface)) {
+        errno = ENOENT;
+        return -1;
+    }
+    // When disable_ipv6 changes from 1 to 0, the kernel starts autoconf.
+    // When disable_ipv6 changes from 0 to 1, the kernel clears all autoconf
+    // addresses and routes and disables IPv6 on the interface.
+    const char *disable_ipv6 = on ? "0" : "1";
+    return writeValueToPath(ipv6_proc_path, interface, "disable_ipv6", disable_ipv6);
 }
 
 int InterfaceController::setIPv6PrivacyExtensions(const char *interface, const int on) {
-	// 0: disable IPv6 privacy addresses
-	// 0: enable IPv6 privacy addresses and prefer them over non-privacy ones.
-	return writeIPv6ProcPath(interface, "use_tempaddr", on ? "2" : "0");
+    if (!isIfaceName(interface)) {
+        errno = ENOENT;
+        return -1;
+    }
+    // 0: disable IPv6 privacy addresses
+    // 0: enable IPv6 privacy addresses and prefer them over non-privacy ones.
+    return writeValueToPath(ipv6_proc_path, interface, "use_tempaddr", on ? "2" : "0");
 }
 
 // Enables or disables IPv6 ND offload. This is useful for 464xlat on wifi, IPv6 tethering, and
@@ -100,34 +151,8 @@
     }
 }
 
-int InterfaceController::isInterfaceName(const char *name) {
-	return strcmp(name, ".") &&
-		strcmp(name, "..") &&
-		strcmp(name, "default") &&
-		strcmp(name, "all");
-}
-
-void InterfaceController::setOnAllInterfaces(const char* filename, const char* value) {
-	// Set the default value, which is used by any interfaces that are created in the future.
-	writeIPv6ProcPath("default", filename, value);
-
-	// Set the value on all the interfaces that currently exist.
-	DIR* dir = opendir(ipv6_proc_path);
-	if (!dir) {
-		ALOGE("Can't list %s: %s", ipv6_proc_path, strerror(errno));
-		return;
-	}
-	dirent* d;
-	while ((d = readdir(dir))) {
-		if (d->d_type == DT_DIR && isInterfaceName(d->d_name)) {
-			writeIPv6ProcPath(d->d_name, filename, value);
-		}
-	}
-	closedir(dir);
-}
-
 void InterfaceController::setAcceptRA(const char *value) {
-	setOnAllInterfaces("accept_ra", value);
+    setOnAllInterfaces(ipv6_proc_path, "accept_ra", value);
 }
 
 // |tableOrOffset| is interpreted as:
@@ -137,21 +162,26 @@
 //             ID to get the table. If it's set to -1000, routes from interface ID 5 will go into
 //             table 1005, etc.
 void InterfaceController::setAcceptRARouteTable(int tableOrOffset) {
-	std::string value(StringPrintf("%d", tableOrOffset));
-	setOnAllInterfaces("accept_ra_rt_table", value.c_str());
+    std::string value(StringPrintf("%d", tableOrOffset));
+    setOnAllInterfaces(ipv6_proc_path, "accept_ra_rt_table", value.c_str());
 }
 
 int InterfaceController::setMtu(const char *interface, const char *mtu)
 {
-	if (!isIfaceName(interface)) {
-		errno = ENOENT;
-		return -1;
-	}
-	std::string path(StringPrintf("%s/%s/mtu", sys_net_path, interface));
-	return WriteStringToFile(mtu, path);
+    if (!isIfaceName(interface)) {
+        errno = ENOENT;
+        return -1;
+    }
+    return writeValueToPath(sys_net_path, interface, "mtu", mtu);
+}
+
+void InterfaceController::setBaseReachableTimeMs(unsigned int millis) {
+    std::string value(StringPrintf("%u", millis));
+    setOnAllInterfaces(ipv4_neigh_conf_dir, "base_reachable_time_ms", value.c_str());
+    setOnAllInterfaces(ipv6_neigh_conf_dir, "base_reachable_time_ms", value.c_str());
 }
 
 void InterfaceController::setIPv6OptimisticMode(const char *value) {
-	setOnAllInterfaces("optimistic_dad", value);
-	setOnAllInterfaces("use_optimistic", value);
+    setOnAllInterfaces(ipv6_proc_path, "optimistic_dad", value);
+    setOnAllInterfaces(ipv6_proc_path, "use_optimistic", value);
 }
diff --git a/server/InterfaceController.h b/server/InterfaceController.h
index 60e2131..89728b1 100644
--- a/server/InterfaceController.h
+++ b/server/InterfaceController.h
@@ -27,11 +27,9 @@
 	int setMtu(const char *interface, const char *mtu);
 
  private:
-	int writeIPv6ProcPath(const char *interface, const char *setting, const char *value);
-	int isInterfaceName(const char *name);
-	void setOnAllInterfaces(const char* filename, const char* value);
 	void setAcceptRA(const char* value);
 	void setAcceptRARouteTable(int tableOrOffset);
+	void setBaseReachableTimeMs(unsigned int millis);
 	void setIPv6OptimisticMode(const char *value);
 };
 
diff --git a/server/NatController.cpp b/server/NatController.cpp
index e66d971..19d19c7 100644
--- a/server/NatController.cpp
+++ b/server/NatController.cpp
@@ -95,10 +95,10 @@
          * Bug 17629786 asks to make the failure more obvious, or even fatal
          * so that all builds eventually gain the performance improvement.
          */
-        {{IPTABLES_PATH, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
-        {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
-        {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
-        {{IPTABLES_PATH, "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags",
+        {{IPTABLES_PATH, "-w", "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
+        {{IPTABLES_PATH, "-w", "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
+        {{IPTABLES_PATH, "-w", "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
+        {{IPTABLES_PATH, "-w", "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags",
                 "SYN", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"}, 0},
     };
     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
@@ -120,9 +120,9 @@
      *  - internally it will be memcopied to an array and terminated with a NULL.
      */
     struct CommandsAndArgs defaultCommands[] = {
-        {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1},
-        {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
-        {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
+        {{IPTABLES_PATH, "-w", "-F", LOCAL_FORWARD,}, 1},
+        {{IPTABLES_PATH, "-w", "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
+        {{IPTABLES_PATH, "-w", "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
     };
     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
@@ -155,6 +155,7 @@
     if (natCount == 0) {
         const char *cmd[] = {
                 IPTABLES_PATH,
+                "-w",
                 "-t",
                 "nat",
                 "-A",
@@ -184,6 +185,7 @@
     /* Always make sure the drop rule is at the end */
     const char *cmd1[] = {
             IPTABLES_PATH,
+            "-w",
             "-D",
             LOCAL_FORWARD,
             "-j",
@@ -192,6 +194,7 @@
     runCmd(ARRAY_SIZE(cmd1), cmd1);
     const char *cmd2[] = {
             IPTABLES_PATH,
+            "-w",
             "-A",
             LOCAL_FORWARD,
             "-j",
@@ -199,15 +202,6 @@
     };
     runCmd(ARRAY_SIZE(cmd2), cmd2);
 
-    if (int ret = RouteController::enableTethering(intIface, extIface)) {
-        ALOGE("failed to add tethering rule for iif=%s oif=%s", intIface, extIface);
-        if (natCount == 0) {
-            setDefaults();
-        }
-        errno = -ret;
-        return -1;
-    }
-
     natCount++;
     return 0;
 }
@@ -239,6 +233,7 @@
     }
     const char *cmd2b[] = {
             IPTABLES_PATH,
+            "-w",
             "-A",
             LOCAL_TETHER_COUNTERS_CHAIN,
             "-i",
@@ -264,6 +259,7 @@
 
     const char *cmd3b[] = {
             IPTABLES_PATH,
+            "-w",
             "-A",
             LOCAL_TETHER_COUNTERS_CHAIN,
             "-i",
@@ -287,6 +283,7 @@
 int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
     const char *cmd1[] = {
             IPTABLES_PATH,
+            "-w",
             add ? "-A" : "-D",
             LOCAL_FORWARD,
             "-i",
@@ -308,6 +305,7 @@
 
     const char *cmd2[] = {
             IPTABLES_PATH,
+            "-w",
             add ? "-A" : "-D",
             LOCAL_FORWARD,
             "-i",
@@ -324,6 +322,7 @@
 
     const char *cmd3[] = {
             IPTABLES_PATH,
+            "-w",
             add ? "-A" : "-D",
             LOCAL_FORWARD,
             "-i",
@@ -354,10 +353,10 @@
     return 0;
 
 err_return:
-    cmd2[1] = "-D";
+    cmd2[2] = "-D";
     runCmd(ARRAY_SIZE(cmd2), cmd2);
 err_invalid_drop:
-    cmd1[1] = "-D";
+    cmd1[2] = "-D";
     runCmd(ARRAY_SIZE(cmd1), cmd1);
     return rc;
 }
@@ -368,12 +367,6 @@
         return -1;
     }
 
-    if (int ret = RouteController::disableTethering(intIface, extIface)) {
-        ALOGE("failed to remove tethering rule for iif=%s oif=%s", intIface, extIface);
-        errno = -ret;
-        return -1;
-    }
-
     setForwardRules(false, intIface, extIface);
     if (--natCount <= 0) {
         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
diff --git a/server/NetdConstants.cpp b/server/NetdConstants.cpp
index d2e0bbe..c86538b 100644
--- a/server/NetdConstants.cpp
+++ b/server/NetdConstants.cpp
@@ -73,6 +73,10 @@
     std::list<const char*> argsList;
     argsList.push_back(NULL);
     const char* arg;
+
+    // Wait to avoid failure due to another process holding the lock
+    argsList.push_back("-w");
+
     do {
         arg = va_arg(args, const char *);
         argsList.push_back(arg);
diff --git a/server/NetlinkHandler.cpp b/server/NetlinkHandler.cpp
index 0a5a3f0..97dc3e0 100644
--- a/server/NetlinkHandler.cpp
+++ b/server/NetlinkHandler.cpp
@@ -58,26 +58,26 @@
     }
 
     if (!strcmp(subsys, "net")) {
-        int action = evt->getAction();
+        NetlinkEvent::Action action = evt->getAction();
         const char *iface = evt->findParam("INTERFACE");
 
-        if (action == evt->NlActionAdd) {
+        if (action == NetlinkEvent::Action::kAdd) {
             notifyInterfaceAdded(iface);
-        } else if (action == evt->NlActionRemove) {
+        } else if (action == NetlinkEvent::Action::kRemove) {
             notifyInterfaceRemoved(iface);
-        } else if (action == evt->NlActionChange) {
+        } else if (action == NetlinkEvent::Action::kChange) {
             evt->dump();
             notifyInterfaceChanged("nana", true);
-        } else if (action == evt->NlActionLinkUp) {
+        } else if (action == NetlinkEvent::Action::kLinkUp) {
             notifyInterfaceLinkChanged(iface, true);
-        } else if (action == evt->NlActionLinkDown) {
+        } else if (action == NetlinkEvent::Action::kLinkDown) {
             notifyInterfaceLinkChanged(iface, false);
-        } else if (action == evt->NlActionAddressUpdated ||
-                   action == evt->NlActionAddressRemoved) {
+        } else if (action == NetlinkEvent::Action::kAddressUpdated ||
+                   action == NetlinkEvent::Action::kAddressRemoved) {
             const char *address = evt->findParam("ADDRESS");
             const char *flags = evt->findParam("FLAGS");
             const char *scope = evt->findParam("SCOPE");
-            if (action == evt->NlActionAddressRemoved && iface && address) {
+            if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {
                 int resetMask = strchr(address, ':') ? RESET_IPV6_ADDRESSES : RESET_IPV4_ADDRESSES;
                 resetMask |= RESET_IGNORE_INTERFACE_ADDRESS;
                 if (int ret = ifc_reset_connections(iface, resetMask)) {
@@ -88,14 +88,14 @@
             if (iface && flags && scope) {
                 notifyAddressChanged(action, address, iface, flags, scope);
             }
-        } else if (action == evt->NlActionRdnss) {
+        } else if (action == NetlinkEvent::Action::kRdnss) {
             const char *lifetime = evt->findParam("LIFETIME");
             const char *servers = evt->findParam("SERVERS");
             if (lifetime && servers) {
                 notifyInterfaceDnsServers(iface, lifetime, servers);
             }
-        } else if (action == evt->NlActionRouteUpdated ||
-                   action == evt->NlActionRouteRemoved) {
+        } else if (action == NetlinkEvent::Action::kRouteUpdated ||
+                   action == NetlinkEvent::Action::kRouteRemoved) {
             const char *route = evt->findParam("ROUTE");
             const char *gateway = evt->findParam("GATEWAY");
             const char *iface = evt->findParam("INTERFACE");
@@ -118,8 +118,10 @@
         const char *label = evt->findParam("INTERFACE");
         const char *state = evt->findParam("STATE");
         const char *timestamp = evt->findParam("TIME_NS");
+        const char *uid = evt->findParam("UID");
         if (state)
-            notifyInterfaceClassActivity(label, !strcmp("active", state), timestamp);
+            notifyInterfaceClassActivity(label, !strcmp("active", state),
+                                         timestamp, uid);
 
 #if !LOG_NDEBUG
     } else if (strcmp(subsys, "platform") && strcmp(subsys, "backlight")) {
@@ -165,21 +167,26 @@
 }
 
 void NetlinkHandler::notifyInterfaceClassActivity(const char *name,
-                                                  bool isActive, const char *timestamp) {
+                                                  bool isActive,
+                                                  const char *timestamp,
+                                                  const char *uid) {
     if (timestamp == NULL)
         notify(ResponseCode::InterfaceClassActivity,
            "IfaceClass %s %s", isActive ? "active" : "idle", name);
+    else if (uid != NULL && isActive)
+        notify(ResponseCode::InterfaceClassActivity,
+           "IfaceClass active %s %s %s", name, timestamp, uid);
     else
         notify(ResponseCode::InterfaceClassActivity,
            "IfaceClass %s %s %s", isActive ? "active" : "idle", name, timestamp);
 }
 
-void NetlinkHandler::notifyAddressChanged(int action, const char *addr,
+void NetlinkHandler::notifyAddressChanged(NetlinkEvent::Action action, const char *addr,
                                           const char *iface, const char *flags,
                                           const char *scope) {
     notify(ResponseCode::InterfaceAddressChange,
            "Address %s %s %s %s %s",
-           (action == NetlinkEvent::NlActionAddressUpdated) ? kUpdated : kRemoved,
+           (action == NetlinkEvent::Action::kAddressUpdated) ? kUpdated : kRemoved,
            addr, iface, flags, scope);
 }
 
@@ -190,11 +197,11 @@
            iface, lifetime, servers);
 }
 
-void NetlinkHandler::notifyRouteChange(int action, const char *route,
+void NetlinkHandler::notifyRouteChange(NetlinkEvent::Action action, const char *route,
                                        const char *gateway, const char *iface) {
     notify(ResponseCode::RouteChange,
            "Route %s %s%s%s%s%s",
-           (action == NetlinkEvent::NlActionRouteUpdated) ? kUpdated : kRemoved,
+           (action == NetlinkEvent::Action::kRouteUpdated) ? kUpdated : kRemoved,
            route,
            *gateway ? " via " : "",
            gateway,
diff --git a/server/NetlinkHandler.h b/server/NetlinkHandler.h
index bee52dc..d97c864 100644
--- a/server/NetlinkHandler.h
+++ b/server/NetlinkHandler.h
@@ -17,6 +17,7 @@
 #ifndef _NETLINKHANDLER_H
 #define _NETLINKHANDLER_H
 
+#include <sysutils/NetlinkEvent.h>
 #include <sysutils/NetlinkListener.h>
 #include "NetlinkManager.h"
 
@@ -40,12 +41,12 @@
     void notifyInterfaceLinkChanged(const char *name, bool isUp);
     void notifyQuotaLimitReached(const char *name, const char *iface);
     void notifyInterfaceClassActivity(const char *name, bool isActive,
-                                      const char *timestamp);
-    void notifyAddressChanged(int action, const char *addr, const char *iface,
+                                      const char *timestamp, const char *uid);
+    void notifyAddressChanged(NetlinkEvent::Action action, const char *addr, const char *iface,
                               const char *flags, const char *scope);
     void notifyInterfaceDnsServers(const char *iface, const char *lifetime,
                                    const char *servers);
-    void notifyRouteChange(int action, const char *route, const char *gateway, const char *iface);
+    void notifyRouteChange(NetlinkEvent::Action action, const char *route, const char *gateway, const char *iface);
     void notifyStrictCleartext(const char* uid, const char* hex);
 };
 #endif
diff --git a/server/Network.h b/server/Network.h
index 115997a..3af53d9 100644
--- a/server/Network.h
+++ b/server/Network.h
@@ -26,6 +26,7 @@
 class Network {
 public:
     enum Type {
+        DUMMY,
         LOCAL,
         PHYSICAL,
         VIRTUAL,
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index 20d8e97..93a0763 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -32,6 +32,7 @@
 
 #include "NetworkController.h"
 
+#include "DummyNetwork.h"
 #include "Fwmark.h"
 #include "LocalNetwork.h"
 #include "PhysicalNetwork.h"
@@ -53,7 +54,8 @@
 
 const unsigned NetworkController::MIN_OEM_ID   =  1;
 const unsigned NetworkController::MAX_OEM_ID   = 50;
-// NetIds 51..98 are reserved for future use.
+const unsigned NetworkController::DUMMY_NET_ID = 51;
+// NetIds 52..98 are reserved for future use.
 const unsigned NetworkController::LOCAL_NET_ID = 99;
 
 // All calls to methods here are made while holding a write lock on mRWLock.
@@ -132,6 +134,7 @@
 NetworkController::NetworkController() :
         mDelegateImpl(new NetworkController::DelegateImpl(this)), mDefaultNetId(NETID_UNSET) {
     mNetworks[LOCAL_NET_ID] = new LocalNetwork(LOCAL_NET_ID);
+    mNetworks[DUMMY_NET_ID] = new DummyNetwork(DUMMY_NET_ID);
 }
 
 unsigned NetworkController::getDefaultNetwork() const {
@@ -234,6 +237,30 @@
     return mDefaultNetId;
 }
 
+void NetworkController::getNetworkContext(
+        unsigned netId, uid_t uid, struct android_net_context* netcontext) const {
+    struct android_net_context nc = {
+            .app_netid = netId,
+            .app_mark = MARK_UNSET,
+            .dns_netid = netId,
+            .dns_mark = MARK_UNSET,
+            .uid = uid,
+    };
+
+    if (nc.app_netid == NETID_UNSET) {
+        nc.app_netid = getNetworkForConnect(uid);
+    }
+    Fwmark fwmark;
+    fwmark.netId = nc.app_netid;
+    nc.app_mark = fwmark.intValue;
+
+    nc.dns_mark = getNetworkForDns(&(nc.dns_netid), uid);
+
+    if (netcontext) {
+        *netcontext = nc;
+    }
+}
+
 unsigned NetworkController::getNetworkForInterface(const char* interface) const {
     android::RWLock::AutoRLock lock(mRWLock);
     for (const auto& entry : mNetworks) {
diff --git a/server/NetworkController.h b/server/NetworkController.h
index 5596f0c..6d72aeb 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -43,6 +43,7 @@
     static const unsigned MIN_OEM_ID;
     static const unsigned MAX_OEM_ID;
     static const unsigned LOCAL_NET_ID;
+    static const unsigned DUMMY_NET_ID;
 
     NetworkController();
 
@@ -55,6 +56,7 @@
     uint32_t getNetworkForDns(unsigned* netId, uid_t uid) const;
     unsigned getNetworkForUser(uid_t uid) const;
     unsigned getNetworkForConnect(uid_t uid) const;
+    void getNetworkContext(unsigned netId, uid_t uid, struct android_net_context* netcontext) const;
     unsigned getNetworkForInterface(const char* interface) const;
     bool isVirtualNetwork(unsigned netId) const;
 
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 736aa74..889779d 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -29,11 +29,13 @@
 
 #include "Fwmark.h"
 #include "UidRanges.h"
+#include "DummyNetwork.h"
 
 #include "base/file.h"
 #define LOG_TAG "Netd"
 #include "log/log.h"
 #include "logwrap/logwrap.h"
+#include "netutils/ifc.h"
 #include "resolv_netid.h"
 
 using android::base::WriteStringToFile;
@@ -43,6 +45,7 @@
 // BEGIN CONSTANTS --------------------------------------------------------------------------------
 
 const uint32_t RULE_PRIORITY_VPN_OVERRIDE_SYSTEM = 10000;
+const uint32_t RULE_PRIORITY_VPN_OVERRIDE_OIF    = 10500;
 const uint32_t RULE_PRIORITY_VPN_OUTPUT_TO_LOCAL = 11000;
 const uint32_t RULE_PRIORITY_SECURE_VPN          = 12000;
 const uint32_t RULE_PRIORITY_EXPLICIT_NETWORK    = 13000;
@@ -89,6 +92,7 @@
 const char* const IP_VERSIONS[] = {"-4", "-6"};
 
 const uid_t UID_ROOT = 0;
+const char* const IIF_LOOPBACK = "lo";
 const char* const IIF_NONE = NULL;
 const char* const OIF_NONE = NULL;
 const bool ACTION_ADD = true;
@@ -238,8 +242,10 @@
 
 // Adds or removes a routing rule for IPv4 and IPv6.
 //
-// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the rule
-//   returns ENETUNREACH.
+// + If |priority| is RULE_PRIORITY_UNREACHABLE, the rule returns ENETUNREACH (i.e., specifies an
+//   action of FR_ACT_UNREACHABLE). Otherwise, the rule specifies an action of FR_ACT_TO_TBL.
+// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the table is
+//   unspecified. An unspecified table is only allowed when deleting a rule.
 // + If |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is
 //   ignored.
 // + If |iif| is non-NULL, the rule matches the specified incoming interface.
@@ -278,10 +284,20 @@
 
     // Assemble a rule request and put it in an array of iovec structures.
     fib_rule_hdr rule = {
-        .action = static_cast<uint8_t>(table != RT_TABLE_UNSPEC ? FR_ACT_TO_TBL :
-                                                                  FR_ACT_UNREACHABLE),
+        .action = static_cast<uint8_t>(priority != RULE_PRIORITY_UNREACHABLE ? FR_ACT_TO_TBL :
+                                                                               FR_ACT_UNREACHABLE),
+        // Note that here we're implicitly setting rule.table to 0. When we want to specify a
+        // non-zero table, we do this via the FRATTR_TABLE attribute.
     };
 
+    // Don't ever create a rule that looks up table 0, because table 0 is the local table.
+    // It's OK to specify a table ID of 0 when deleting a rule, because that doesn't actually select
+    // table 0, it's a wildcard that matches anything.
+    if (table == RT_TABLE_UNSPEC && rule.action == FR_ACT_TO_TBL && action != RTM_DELRULE) {
+        ALOGE("RT_TABLE_UNSPEC only allowed when deleting rules");
+        return -ENOTUNIQ;
+    }
+
     rtattr fraIifName = { U16_RTA_LENGTH(iifLength), FRA_IIFNAME };
     rtattr fraOifName = { U16_RTA_LENGTH(oifLength), FRA_OIFNAME };
 
@@ -476,7 +492,7 @@
     }
 
     return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, priority, table, fwmark.intValue,
-                        mask.intValue, IIF_NONE, OIF_NONE, uidStart, uidEnd);
+                        mask.intValue, IIF_LOOPBACK, OIF_NONE, uidStart, uidEnd);
 }
 
 // A rule to allow system apps to send traffic over this VPN even if they are not part of the target
@@ -531,15 +547,25 @@
 //
 // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already knows
 // the outgoing interface (typically for link-local communications).
-WARN_UNUSED_RESULT int modifyOutputInterfaceRule(const char* interface, uint32_t table,
-                                                 Permission permission, uid_t uidStart,
-                                                 uid_t uidEnd, bool add) {
+WARN_UNUSED_RESULT int modifyOutputInterfaceRules(const char* interface, uint32_t table,
+                                                  Permission permission, uid_t uidStart,
+                                                  uid_t uidEnd, bool add) {
     Fwmark fwmark;
     Fwmark mask;
 
     fwmark.permission = permission;
     mask.permission = permission;
 
+    // If this rule does not specify a UID range, then also add a corresponding high-priority rule
+    // for UID. This covers forwarded packets and system daemons such as the tethering DHCP server.
+    if (uidStart == INVALID_UID && uidEnd == INVALID_UID) {
+        if (int ret = modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_VPN_OVERRIDE_OIF,
+                                   table, fwmark.intValue, mask.intValue, IIF_NONE, interface,
+                                   UID_ROOT, UID_ROOT)) {
+            return ret;
+        }
+    }
+
     return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_OUTPUT_INTERFACE, table,
                         fwmark.intValue, mask.intValue, IIF_NONE, interface, uidStart, uidEnd);
 }
@@ -639,6 +665,41 @@
                         fwmark.intValue, mask.intValue);
 }
 
+int configureDummyNetwork() {
+    const char *interface = DummyNetwork::INTERFACE_NAME;
+    uint32_t table = getRouteTableForInterface(interface);
+    if (table == RT_TABLE_UNSPEC) {
+        // getRouteTableForInterface has already looged an error.
+        return -ESRCH;
+    }
+
+    ifc_init();
+    int ret = ifc_up(interface);
+    ifc_close();
+    if (ret) {
+        ALOGE("Can't bring up %s: %s", interface, strerror(errno));
+        return -errno;
+    }
+
+    if ((ret = modifyOutputInterfaceRules(interface, table, PERMISSION_NONE,
+                                          INVALID_UID, INVALID_UID, ACTION_ADD))) {
+        ALOGE("Can't create oif rules for %s: %s", interface, strerror(-ret));
+        return ret;
+    }
+
+    if ((ret = modifyIpRoute(RTM_NEWROUTE, table, interface, "0.0.0.0/0", NULL))) {
+        ALOGE("Can't add IPv4 default route to %s: %s", interface, strerror(-ret));
+        return ret;
+    }
+
+    if ((ret = modifyIpRoute(RTM_NEWROUTE, table, interface, "::/0", NULL))) {
+        ALOGE("Can't add IPv6 default route to %s: %s", interface, strerror(-ret));
+        return ret;
+    }
+
+    return 0;
+}
+
 // Add a new rule to look up the 'main' table, with the same selectors as the "default network"
 // rule, but with a lower priority. We will never create routes in the main table; it should only be
 // used for directly-connected routes implicitly created by the kernel when adding IP addresses.
@@ -668,8 +729,8 @@
     if (int ret = modifyIncomingPacketMark(netId, interface, PERMISSION_NONE, add)) {
         return ret;
     }
-    return modifyOutputInterfaceRule(interface, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE,
-                                     INVALID_UID, INVALID_UID, add);
+    return modifyOutputInterfaceRules(interface, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE,
+                                      INVALID_UID, INVALID_UID, add);
 }
 
 WARN_UNUSED_RESULT int modifyPhysicalNetwork(unsigned netId, const char* interface,
@@ -686,7 +747,7 @@
                                             add)) {
         return ret;
     }
-    if (int ret = modifyOutputInterfaceRule(interface, table, permission, INVALID_UID, INVALID_UID,
+    if (int ret = modifyOutputInterfaceRules(interface, table, permission, INVALID_UID, INVALID_UID,
                                             add)) {
         return ret;
     }
@@ -709,8 +770,8 @@
                                                 range.second, add)) {
             return ret;
         }
-        if (int ret = modifyOutputInterfaceRule(interface, table, PERMISSION_NONE, range.first,
-                                                range.second, add)) {
+        if (int ret = modifyOutputInterfaceRules(interface, table, PERMISSION_NONE, range.first,
+                                                 range.second, add)) {
             return ret;
         }
     }
@@ -866,6 +927,20 @@
     return ret;
 }
 
+WARN_UNUSED_RESULT int clearTetheringRules(const char* inputInterface) {
+    int ret = 0;
+    while (ret == 0) {
+        ret = modifyIpRule(RTM_DELRULE, RULE_PRIORITY_TETHERING, 0, MARK_UNSET, MARK_UNSET,
+                           inputInterface, OIF_NONE, INVALID_UID, INVALID_UID);
+    }
+
+    if (ret == -ENOENT) {
+        return 0;
+    } else {
+        return ret;
+    }
+}
+
 }  // namespace
 
 int RouteController::Init(unsigned localNetId) {
@@ -884,6 +959,9 @@
     if (int ret = addUnreachableRule()) {
         return ret;
     }
+    // Don't complain if we can't add the dummy network, since not all devices support it.
+    configureDummyNetwork();
+
     updateTableNamesFile();
     return 0;
 }
@@ -913,6 +991,9 @@
     if (int ret = flushRoutes(interface)) {
         return ret;
     }
+    if (int ret = clearTetheringRules(interface)) {
+        return ret;
+    }
     updateTableNamesFile();
     return 0;
 }
diff --git a/server/SoftapController.cpp b/server/SoftapController.cpp
index c1db1fc..d9e40a4 100644
--- a/server/SoftapController.cpp
+++ b/server/SoftapController.cpp
@@ -145,10 +145,10 @@
             "ssid=%s\n"
             "channel=%d\n"
             "ieee80211n=1\n"
-            "hw_mode=g\n"
+            "hw_mode=%c\n"
             "ignore_broadcast_ssid=%d\n"
             "wowlan_triggers=any\n",
-            argv[2], argv[3], channel, hidden));
+            argv[2], argv[3], channel, (channel <= 14) ? 'g' : 'a', hidden));
 
     std::string fbuf;
     if (argc > 7) {
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index 44c7e35..88baa31 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -27,7 +27,6 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#include <base/file.h>
 #define LOG_TAG "TetherController"
 #include <cutils/log.h>
 #include <cutils/properties.h>
@@ -37,8 +36,37 @@
 #include "Permission.h"
 #include "TetherController.h"
 
-using android::base::ReadFileToString;
-using android::base::WriteStringToFile;
+namespace {
+
+static const char BP_TOOLS_MODE[] = "bp-tools";
+static const char IPV4_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv4/ip_forward";
+static const char IPV6_FORWARDING_PROC_FILE[] = "/proc/sys/net/ipv6/conf/all/forwarding";
+
+bool writeToFile(const char* filename, const char* value) {
+    int fd = open(filename, O_WRONLY);
+    if (fd < 0) {
+        ALOGE("Failed to open %s: %s", filename, strerror(errno));
+        return false;
+    }
+
+    const ssize_t len = strlen(value);
+    if (write(fd, value, len) != len) {
+        ALOGE("Failed to write %s to %s: %s", value, filename, strerror(errno));
+        close(fd);
+        return false;
+    }
+    close(fd);
+    return true;
+}
+
+bool inBpToolsMode() {
+    // In BP tools mode, do not disable IP forwarding
+    char bootmode[PROPERTY_VALUE_MAX] = {0};
+    property_get("ro.bootmode", bootmode, "unknown");
+    return !strcmp(BP_TOOLS_MODE, bootmode);
+}
+
+}  // namespace
 
 TetherController::TetherController() {
     mInterfaces = new InterfaceCollection();
@@ -46,6 +74,11 @@
     mDnsForwarders = new NetAddressCollection();
     mDaemonFd = -1;
     mDaemonPid = 0;
+    if (inBpToolsMode()) {
+        enableForwarding(BP_TOOLS_MODE);
+    } else {
+        setIpFwdEnabled();
+    }
 }
 
 TetherController::~TetherController() {
@@ -57,35 +90,33 @@
     mInterfaces->clear();
 
     mDnsForwarders->clear();
+    mForwardingRequests.clear();
 }
 
-int TetherController::setIpFwdEnabled(bool enable) {
-
-    ALOGD("Setting IP forward enable = %d", enable);
-
-    // In BP tools mode, do not disable IP forwarding
-    char bootmode[PROPERTY_VALUE_MAX] = {0};
-    property_get("ro.bootmode", bootmode, "unknown");
-    if ((enable == false) && (0 == strcmp("bp-tools", bootmode))) {
-        return 0;
-    }
-
-    if (!WriteStringToFile(enable ? "1" : "0", "/proc/sys/net/ipv4/ip_forward")) {
-        ALOGE("Failed to write ip_forward (%s)", strerror(errno));
-        return -1;
-    }
-
-    return 0;
+bool TetherController::setIpFwdEnabled() {
+    bool success = true;
+    const char* value = mForwardingRequests.empty() ? "0" : "1";
+    ALOGD("Setting IP forward enable = %s", value);
+    success &= writeToFile(IPV4_FORWARDING_PROC_FILE, value);
+    success &= writeToFile(IPV6_FORWARDING_PROC_FILE, value);
+    return success;
 }
 
-bool TetherController::getIpFwdEnabled() {
-    std::string enabled;
-    if (!ReadFileToString("/proc/sys/net/ipv4/ip_forward", &enabled)) {
-        ALOGE("Failed to read ip_forward (%s)", strerror(errno));
-        return -1;
-    }
+bool TetherController::enableForwarding(const char* requester) {
+    // Don't return an error if this requester already requested forwarding. Only return errors for
+    // things that the caller caller needs to care about, such as "couldn't write to the file to
+    // enable forwarding".
+    mForwardingRequests.insert(requester);
+    return setIpFwdEnabled();
+}
 
-    return (enabled == "1" ? true : false);
+bool TetherController::disableForwarding(const char* requester) {
+    mForwardingRequests.erase(requester);
+    return setIpFwdEnabled();
+}
+
+size_t TetherController::forwardingRequestCount() {
+    return mForwardingRequests.size();
 }
 
 #define TETHER_START_CONST_ARG        8
diff --git a/server/TetherController.h b/server/TetherController.h
index 1c32627..91ffb9c 100644
--- a/server/TetherController.h
+++ b/server/TetherController.h
@@ -18,6 +18,8 @@
 #define _TETHER_CONTROLLER_H
 
 #include <netinet/in.h>
+#include <set>
+#include <string>
 
 #include "List.h"
 
@@ -32,16 +34,17 @@
     NetAddressCollection *mDnsForwarders;
     pid_t                 mDaemonPid;
     int                   mDaemonFd;
+    std::set<std::string> mForwardingRequests;
 
 public:
     TetherController();
     virtual ~TetherController();
 
-    int setIpFwdEnabled(bool enable);
-    bool getIpFwdEnabled();
+    bool enableForwarding(const char* requester);
+    bool disableForwarding(const char* requester);
+    size_t forwardingRequestCount();
 
     int startTethering(int num_addrs, struct in_addr* addrs);
-
     int stopTethering();
     bool isTetheringStarted();
 
@@ -55,6 +58,7 @@
 
 private:
     int applyDnsInterfaces();
+    bool setIpFwdEnabled();
 };
 
 #endif
diff --git a/server/oem_iptables_hook.cpp b/server/oem_iptables_hook.cpp
index 70260f3..7e4b3cb 100644
--- a/server/oem_iptables_hook.cpp
+++ b/server/oem_iptables_hook.cpp
@@ -37,6 +37,7 @@
 static bool oemCleanupHooks() {
     const char *cmd1[] = {
             IPTABLES_PATH,
+            "-w",
             "-F",
             "oem_out"
     };
@@ -44,6 +45,7 @@
 
     const char *cmd2[] = {
             IPTABLES_PATH,
+            "-w",
             "-F",
             "oem_fwd"
     };
@@ -51,6 +53,7 @@
 
     const char *cmd3[] = {
             IPTABLES_PATH,
+            "-w",
             "-t",
             "nat",
             "-F",