Fix WiFi-Direct and Tethering.

A LocalNetwork object now always exists in the NetworkController, with a fixed
NetId that's guaranteed not to collide with NetIds created by the framework.

When routes are added on an interface tracked by the LocalNetwork, they are
added to a fixed "local_network" table.

When NAT is enabled, we add a special "iif -> oif" tethering rule.

Bug: 15413694
Bug: 15413741

Change-Id: I36effc438d5ac193a77174493bf196cb68a5b97a
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index f2a8745..1c060f7 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -62,6 +62,14 @@
     return PERMISSION_NONE;
 }
 
+unsigned stringToNetId(const char* arg) {
+    if (!strcmp(arg, "local")) {
+        return NetworkController::LOCAL_NET_ID;
+    }
+    // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId.
+    return strtoul(arg, NULL, 0);
+}
+
 }  // namespace
 
 NetworkController *CommandListener::sNetCtrl = NULL;
@@ -174,9 +182,9 @@
     if (!sSecondaryTableCtrl)
         sSecondaryTableCtrl = new SecondaryTableController(sNetCtrl);
     if (!sTetherCtrl)
-        sTetherCtrl = new TetherController(sNetCtrl);
+        sTetherCtrl = new TetherController();
     if (!sNatCtrl)
-        sNatCtrl = new NatController(sNetCtrl);
+        sNatCtrl = new NatController();
     if (!sPppCtrl)
         sPppCtrl = new PppController();
     if (!sSoftapCtrl)
@@ -237,7 +245,7 @@
 
     sSecondaryTableCtrl->setupIptablesHooks();
 
-    if (int ret = RouteController::Init()) {
+    if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) {
         ALOGE("failed to initialize RouteController (%s)", strerror(-ret));
     }
 }
@@ -813,16 +821,19 @@
         return 0;
     }
 
-    if (!strcmp(argv[1], "enable")) {
-        rc = sNatCtrl->enableNat(argc, argv);
+    //  0     1       2        3
+    // nat  enable intiface extiface
+    // nat disable intiface extiface
+    if (!strcmp(argv[1], "enable") && argc >= 4) {
+        rc = sNatCtrl->enableNat(argv[2], argv[3]);
         if(!rc) {
             /* Ignore ifaces for now. */
             rc = sBandwidthCtrl->setGlobalAlertInForwardChain();
         }
-    } else if (!strcmp(argv[1], "disable")) {
+    } else if (!strcmp(argv[1], "disable") && argc >= 4) {
         /* Ignore ifaces for now. */
         rc = sBandwidthCtrl->removeGlobalAlertInForwardChain();
-        rc |= sNatCtrl->disableNat(argc, argv);
+        rc |= sNatCtrl->disableNat(argv[2], argv[3]);
     } else {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown nat cmd", false);
         return 0;
@@ -1569,8 +1580,7 @@
         }
         ++nextArg;
 
-        // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId.
-        unsigned netId = strtoul(argv[nextArg++], NULL, 0);
+        unsigned netId = stringToNetId(argv[nextArg++]);
         const char* interface = argv[nextArg++];
         const char* destination = argv[nextArg++];
         const char* nexthop = argc > nextArg ? argv[nextArg] : NULL;
@@ -1588,23 +1598,24 @@
         return success(client);
     }
 
-    //    0         1         2         3
-    // network   addiface  <netId> <interface>
-    // network removeiface <netId> <interface>
-    if (!strcmp(argv[1], "addiface") || !strcmp(argv[1], "removeiface")) {
-        if (argc != 4) {
+    //    0        1       2       3         4
+    // network interface  add   <netId> <interface>
+    // network interface remove <netId> <interface>
+    if (!strcmp(argv[1], "interface")) {
+        if (argc != 5) {
             return syntaxError(client, "Missing argument");
         }
-        // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId.
-        unsigned netId = strtoul(argv[2], NULL, 0);
-        if (!strcmp(argv[1], "addiface")) {
-            if (int ret = sNetCtrl->addInterfaceToNetwork(netId, argv[3])) {
+        unsigned netId = stringToNetId(argv[3]);
+        if (!strcmp(argv[2], "add")) {
+            if (int ret = sNetCtrl->addInterfaceToNetwork(netId, argv[4])) {
                 return operationError(client, "addInterfaceToNetwork() failed", ret);
             }
-        } else {
-            if (int ret = sNetCtrl->removeInterfaceFromNetwork(netId, argv[3])) {
+        } else if (!strcmp(argv[2], "remove")) {
+            if (int ret = sNetCtrl->removeInterfaceFromNetwork(netId, argv[4])) {
                 return operationError(client, "removeInterfaceFromNetwork() failed", ret);
             }
+        } else {
+            return syntaxError(client, "Unknown argument");
         }
         return success(client);
     }
@@ -1618,8 +1629,7 @@
         if (argc < 3) {
             return syntaxError(client, "Missing argument");
         }
-        // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId.
-        unsigned netId = strtoul(argv[2], NULL, 0);
+        unsigned netId = stringToNetId(argv[2]);
         if (argc == 5 && !strcmp(argv[3], "vpn")) {
             bool hasDns = atoi(argv[4]);
             if (int ret = sNetCtrl->createVirtualNetwork(netId, hasDns)) {
@@ -1648,8 +1658,7 @@
         if (argc != 3) {
             return syntaxError(client, "Incorrect number of arguments");
         }
-        // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId.
-        unsigned netId = strtoul(argv[2], NULL, 0);
+        unsigned netId = stringToNetId(argv[2]);
         if (int ret = sNetCtrl->destroyNetwork(netId)) {
             return operationError(client, "destroyNetwork() failed", ret);
         }
@@ -1668,8 +1677,7 @@
             if (argc < 4) {
                 return syntaxError(client, "Missing netId");
             }
-            // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId.
-            netId = strtoul(argv[3], NULL, 0);
+            netId = stringToNetId(argv[3]);
         } else if (strcmp(argv[2], "clear")) {
             return syntaxError(client, "Unknown argument");
         }
@@ -1730,8 +1738,7 @@
         if (argc < 4) {
             return syntaxError(client, "Missing argument");
         }
-        // strtoul() returns 0 on errors, which is fine because 0 is an invalid netId.
-        unsigned netId = strtoul(argv[3], NULL, 0);
+        unsigned netId = stringToNetId(argv[3]);
         UidRanges uidRanges;
         if (!uidRanges.parseFrom(argc - 4, argv + 4)) {
             return syntaxError(client, "Invalid UIDs");
@@ -1771,9 +1778,5 @@
         return success(client);
     }
 
-    // TODO:
-    //   o tethering
-    //   o p2p
-
     return syntaxError(client, "Unknown argument");
 }
diff --git a/server/NatController.cpp b/server/NatController.cpp
index 8340b73..3594a5d 100644
--- a/server/NatController.cpp
+++ b/server/NatController.cpp
@@ -32,14 +32,14 @@
 #include <logwrap/logwrap.h>
 
 #include "NatController.h"
-#include "NetworkController.h"
 #include "NetdConstants.h"
+#include "RouteController.h"
 
 const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
 const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
 const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
 
-NatController::NatController(NetworkController* net_ctrl) : mNetCtrl(net_ctrl) {
+NatController::NatController() {
 }
 
 NatController::~NatController() {
@@ -112,7 +112,6 @@
         {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1},
         {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
         {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
-        {{IP_PATH, "route", "flush", "cache"}, 0},
     };
     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
@@ -126,34 +125,7 @@
     return 0;
 }
 
-int NatController::routesOp(bool add, const char *intIface, char **argv, int addrCount) {
-    unsigned netId = mNetCtrl->getNetworkForInterface(intIface);
-    int ret = 0;
-
-    for (int i = 0; i < addrCount; i++) {
-        if (add) {
-            ret |= mNetCtrl->addRoute(netId, intIface, argv[5+i], NULL, false, INVALID_UID);
-        } else {
-            ret |= mNetCtrl->addRoute(netId, intIface, argv[5+i], NULL, false, INVALID_UID);
-        }
-    }
-    const char *cmd[] = {
-            IP_PATH,
-            "route",
-            "flush",
-            "cache"
-    };
-    runCmd(ARRAY_SIZE(cmd), cmd);
-    return ret;
-}
-
-//  0    1       2       3       4            5
-// nat enable intface extface addrcnt nated-ipaddr/prelength
-int NatController::enableNat(const int argc, char **argv) {
-    int addrCount = atoi(argv[4]);
-    const char *intIface = argv[2];
-    const char *extIface = argv[3];
-
+int NatController::enableNat(const char* intIface, const char* extIface) {
     ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
 
     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
@@ -168,18 +140,6 @@
         return -1;
     }
 
-    if (argc < 5 + addrCount) {
-        ALOGE("Missing Argument");
-        errno = EINVAL;
-        return -1;
-    }
-    if (routesOp(true, intIface, argv, addrCount)) {
-        ALOGE("Error setting route rules");
-        routesOp(false, intIface, argv, addrCount);
-        errno = ENODEV;
-        return -1;
-    }
-
     // add this if we are the first added nat
     if (natCount == 0) {
         const char *cmd[] = {
@@ -194,18 +154,15 @@
                 "MASQUERADE"
         };
         if (runCmd(ARRAY_SIZE(cmd), cmd)) {
-            ALOGE("Error seting postroute rule: iface=%s", extIface);
+            ALOGE("Error setting postroute rule: iface=%s", extIface);
             // unwind what's been done, but don't care about success - what more could we do?
-            routesOp(false, intIface, argv, addrCount);
             setDefaults();
             return -1;
         }
     }
 
-
     if (setForwardRules(true, intIface, extIface) != 0) {
         ALOGE("Error setting forward rules");
-        routesOp(false, intIface, argv, addrCount);
         if (natCount == 0) {
             setDefaults();
         }
@@ -231,6 +188,12 @@
     };
     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);
+        errno = -ret;
+        return -1;
+    }
+
     natCount++;
     return 0;
 }
@@ -385,27 +348,19 @@
     return rc;
 }
 
-// nat disable intface extface
-//  0    1       2       3       4            5
-// nat enable intface extface addrcnt nated-ipaddr/prelength
-int NatController::disableNat(const int argc, char **argv) {
-    int addrCount = atoi(argv[4]);
-    const char *intIface = argv[2];
-    const char *extIface = argv[3];
-
+int NatController::disableNat(const char* intIface, const char* extIface) {
     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
         errno = ENODEV;
         return -1;
     }
 
-    if (argc < 5 + addrCount) {
-        ALOGE("Missing Argument");
-        errno = EINVAL;
+    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);
-    routesOp(false, intIface, argv, addrCount);
     if (--natCount <= 0) {
         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
         setDefaults();
diff --git a/server/NatController.h b/server/NatController.h
index e3cc394..9102f46 100644
--- a/server/NatController.h
+++ b/server/NatController.h
@@ -21,16 +21,13 @@
 #include <list>
 #include <string>
 
-class NetworkController;
-
 class NatController {
-
 public:
-    explicit NatController(NetworkController* net_ctrl);
+    NatController();
     virtual ~NatController();
 
-    int enableNat(const int argc, char **argv);
-    int disableNat(const int argc, char **argv);
+    int enableNat(const char* intIface, const char* extIface);
+    int disableNat(const char* intIface, const char* extIface);
     int setupIptablesHooks();
 
     static const char* LOCAL_FORWARD;
@@ -42,7 +39,6 @@
 
 private:
     int natCount;
-    NetworkController *mNetCtrl;
 
     bool checkTetherCountingRuleExist(const char *pair_name);
 
@@ -50,7 +46,6 @@
     int runCmd(int argc, const char **argv);
     int setForwardRules(bool set, const char *intIface, const char *extIface);
     int setTetherCountingRules(bool add, const char *intIface, const char *extIface);
-    int routesOp(bool add, const char *intIface, char **argv, int addrCount);
 };
 
 #endif
diff --git a/server/NetworkController.cpp b/server/NetworkController.cpp
index 44eb2ef..90b7682 100644
--- a/server/NetworkController.cpp
+++ b/server/NetworkController.cpp
@@ -51,6 +51,7 @@
 }  // namespace
 
 NetworkController::NetworkController() : mDefaultNetId(NETID_UNSET) {
+    mNetworks[LOCAL_NET_ID] = new LocalNetwork(LOCAL_NET_ID);
 }
 
 unsigned NetworkController::getDefaultNetwork() const {
@@ -117,29 +118,6 @@
     return network && network->getType() == Network::VIRTUAL;
 }
 
-unsigned NetworkController::getNetIdForLocalNetwork() const {
-    return MIN_NET_ID - 1;
-}
-
-int NetworkController::createLocalNetwork(unsigned netId) {
-    // TODO: Enable this check after removing the getNetIdForLocalNetwork() hack.
-    if (false) {
-        if (netId < MIN_NET_ID || netId > MAX_NET_ID) {
-            ALOGE("invalid netId %u", netId);
-            return -EINVAL;
-        }
-    }
-
-    if (isValidNetwork(netId)) {
-        ALOGE("duplicate netId %u", netId);
-        return -EEXIST;
-    }
-
-    android::RWLock::AutoWLock lock(mRWLock);
-    mNetworks[netId] = new LocalNetwork(netId);
-    return 0;
-}
-
 int NetworkController::createPhysicalNetwork(unsigned netId, Permission permission) {
     if (netId < MIN_NET_ID || netId > MAX_NET_ID) {
         ALOGE("invalid netId %u", netId);
@@ -180,7 +158,7 @@
 }
 
 int NetworkController::destroyNetwork(unsigned netId) {
-    if (!isValidNetwork(netId)) {
+    if (netId == LOCAL_NET_ID || !isValidNetwork(netId)) {
         ALOGE("invalid netId %u", netId);
         return -EINVAL;
     }
@@ -377,7 +355,9 @@
     }
 
     RouteController::TableType tableType;
-    if (legacy) {
+    if (netId == LOCAL_NET_ID) {
+        tableType = RouteController::LOCAL_NETWORK;
+    } else if (legacy) {
         if ((getPermissionForUser(uid) & PERMISSION_SYSTEM) == PERMISSION_SYSTEM) {
             tableType = RouteController::LEGACY_SYSTEM;
         } else {
diff --git a/server/NetworkController.h b/server/NetworkController.h
index 479af5e..f0b42c4 100644
--- a/server/NetworkController.h
+++ b/server/NetworkController.h
@@ -40,6 +40,8 @@
  */
 class NetworkController {
 public:
+    static const int LOCAL_NET_ID = 9;
+
     NetworkController();
 
     unsigned getDefaultNetwork() const;
@@ -53,10 +55,6 @@
     unsigned getNetworkForInterface(const char* interface) const;
     bool isVirtualNetwork(unsigned netId) const;
 
-    // TODO: Remove this hack.
-    unsigned getNetIdForLocalNetwork() const;
-
-    int createLocalNetwork(unsigned netId) WARN_UNUSED_RESULT;
     int createPhysicalNetwork(unsigned netId, Permission permission) WARN_UNUSED_RESULT;
     int createVirtualNetwork(unsigned netId, bool hasDns) WARN_UNUSED_RESULT;
     int destroyNetwork(unsigned netId) WARN_UNUSED_RESULT;
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 8ad10d7..ec51613 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -43,7 +43,7 @@
 const uint32_t RULE_PRIORITY_LEGACY_SYSTEM       = 15000;
 const uint32_t RULE_PRIORITY_LEGACY_NETWORK      = 16000;
 const uint32_t RULE_PRIORITY_LOCAL_NETWORK       = 17000;
-// const uint32_t RULE_PRIORITY_TETHERING           = 18000;
+const uint32_t RULE_PRIORITY_TETHERING           = 18000;
 const uint32_t RULE_PRIORITY_IMPLICIT_NETWORK    = 19000;
 // const uint32_t RULE_PRIORITY_BYPASSABLE_VPN      = 20000;
 // const uint32_t RULE_PRIORITY_VPN_FALLTHROUGH     = 21000;
@@ -51,9 +51,11 @@
 const uint32_t RULE_PRIORITY_DIRECTLY_CONNECTED  = 23000;
 const uint32_t RULE_PRIORITY_UNREACHABLE         = 24000;
 
+const uint32_t ROUTE_TABLE_LOCAL_NETWORK  = 97;
 const uint32_t ROUTE_TABLE_LEGACY_NETWORK = 98;
 const uint32_t ROUTE_TABLE_LEGACY_SYSTEM  = 99;
 
+const char* const ROUTE_TABLE_NAME_LOCAL_NETWORK  = "local_network";
 const char* const ROUTE_TABLE_NAME_LEGACY_NETWORK = "legacy_network";
 const char* const ROUTE_TABLE_NAME_LEGACY_SYSTEM  = "legacy_system";
 
@@ -80,6 +82,7 @@
 const char* const IP_VERSIONS[] = {"-4", "-6"};
 
 const uid_t UID_ROOT = 0;
+const char* const IIF_NONE = NULL;
 const char* const OIF_NONE = NULL;
 const bool ACTION_ADD = true;
 const bool ACTION_DEL = false;
@@ -147,6 +150,7 @@
     addTableName(RT_TABLE_LOCAL, ROUTE_TABLE_NAME_LOCAL, &contents);
     addTableName(RT_TABLE_MAIN,  ROUTE_TABLE_NAME_MAIN,  &contents);
 
+    addTableName(ROUTE_TABLE_LOCAL_NETWORK,  ROUTE_TABLE_NAME_LOCAL_NETWORK,  &contents);
     addTableName(ROUTE_TABLE_LEGACY_NETWORK, ROUTE_TABLE_NAME_LEGACY_NETWORK, &contents);
     addTableName(ROUTE_TABLE_LEGACY_SYSTEM,  ROUTE_TABLE_NAME_LEGACY_SYSTEM,  &contents);
 
@@ -219,36 +223,53 @@
     return ret;
 }
 
+// Returns 0 on success or negative errno on failure.
+int padInterfaceName(const char* input, char* name, size_t* length, uint16_t* padding) {
+    if (!input) {
+        *length = 0;
+        *padding = 0;
+        return 0;
+    }
+    *length = strlcpy(name, input, IFNAMSIZ) + 1;
+    if (*length > IFNAMSIZ) {
+        ALOGE("interface name too long (%zu > %u)", *length, IFNAMSIZ);
+        return -ENAMETOOLONG;
+    }
+    *padding = RTA_SPACE(*length) - RTA_LENGTH(*length);
+    return 0;
+}
+
 // 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 |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is
 //   ignored.
-// + If |interface| is non-NULL, the rule matches the specified outgoing interface.
+// + If |iif| is non-NULL, the rule matches the specified incoming interface.
+// + If |oif| is non-NULL, the rule matches the specified outgoing interface.
+// + If |uidStart| and |uidEnd| are not INVALID_UID, the rule matches packets from UIDs in that
+//   range (inclusive). Otherwise, the rule matches packets from all UIDs.
 //
 // Returns 0 on success or negative errno on failure.
 WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint32_t table,
-                                    uint32_t fwmark, uint32_t mask, const char* interface,
-                                    uid_t uidStart, uid_t uidEnd) {
+                                    uint32_t fwmark, uint32_t mask, const char* iif,
+                                    const char* oif, uid_t uidStart, uid_t uidEnd) {
     // Ensure that if you set a bit in the fwmark, it's not being ignored by the mask.
     if (fwmark & ~mask) {
         ALOGE("mask 0x%x does not select all the bits set in fwmark 0x%x", mask, fwmark);
         return -ERANGE;
     }
 
-    // The interface name must include exactly one terminating NULL and be properly padded, or older
+    // Interface names must include exactly one terminating NULL and be properly padded, or older
     // kernels will refuse to delete rules.
-    uint16_t paddingLength = 0;
-    size_t interfaceLength = 0;
-    char oifname[IFNAMSIZ];
-    if (interface != OIF_NONE) {
-        interfaceLength = strlcpy(oifname, interface, IFNAMSIZ) + 1;
-        if (interfaceLength > IFNAMSIZ) {
-            ALOGE("interface name too long (%zu > %u)", interfaceLength, IFNAMSIZ);
-            return -ENAMETOOLONG;
-        }
-        paddingLength = RTA_SPACE(interfaceLength) - RTA_LENGTH(interfaceLength);
+    char iifName[IFNAMSIZ], oifName[IFNAMSIZ];
+    size_t iifLength, oifLength;
+    uint16_t iifPadding, oifPadding;
+    if (int ret = padInterfaceName(iif, iifName, &iifLength, &iifPadding)) {
+        return ret;
+    }
+    if (int ret = padInterfaceName(oif, oifName, &oifLength, &oifPadding)) {
+        return ret;
     }
 
     // Either both start and end UID must be specified, or neither.
@@ -264,7 +285,8 @@
                                                                   FR_ACT_UNREACHABLE),
     };
 
-    rtattr fraOifname = { U16_RTA_LENGTH(interfaceLength), FRA_OIFNAME };
+    rtattr fraIifName = { U16_RTA_LENGTH(iifLength), FRA_IIFNAME };
+    rtattr fraOifName = { U16_RTA_LENGTH(oifLength), FRA_OIFNAME };
 
     iovec iov[] = {
         { NULL,              0 },
@@ -281,9 +303,12 @@
         { &uidStart,         isUidRule ? sizeof(uidStart) : 0 },
         { &FRATTR_UID_END,   isUidRule ? sizeof(FRATTR_UID_END) : 0 },
         { &uidEnd,           isUidRule ? sizeof(uidEnd) : 0 },
-        { &fraOifname,       interface != OIF_NONE ? sizeof(fraOifname) : 0 },
-        { oifname,           interfaceLength },
-        { PADDING_BUFFER,    paddingLength },
+        { &fraIifName,       iif != IIF_NONE ? sizeof(fraIifName) : 0 },
+        { iifName,           iifLength },
+        { PADDING_BUFFER,    iifPadding },
+        { &fraOifName,       oif != OIF_NONE ? sizeof(fraOifName) : 0 },
+        { oifName,           oifLength },
+        { PADDING_BUFFER,    oifPadding },
     };
 
     uint16_t flags = (action == RTM_NEWRULE) ? NETLINK_CREATE_REQUEST_FLAGS : NETLINK_REQUEST_FLAGS;
@@ -297,6 +322,12 @@
     return 0;
 }
 
+WARN_UNUSED_RESULT int modifyIpRule(uint16_t action, uint32_t priority, uint32_t table,
+                                    uint32_t fwmark, uint32_t mask) {
+    return modifyIpRule(action, priority, table, fwmark, mask, IIF_NONE, OIF_NONE, INVALID_UID,
+                        INVALID_UID);
+}
+
 // Adds or deletes an IPv4 or IPv6 route.
 // Returns 0 on success or negative errno on failure.
 WARN_UNUSED_RESULT int modifyIpRoute(uint16_t action, uint32_t table, const char* interface,
@@ -370,7 +401,7 @@
 }
 
 // Add rules to allow legacy routes added through the requestRouteToHost() API.
-WARN_UNUSED_RESULT int AddLegacyRouteRules() {
+WARN_UNUSED_RESULT int addLegacyRouteRules() {
     Fwmark fwmark;
     Fwmark mask;
 
@@ -379,13 +410,11 @@
 
     // Rules to allow legacy routes to override the default network.
     if (int ret = modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_LEGACY_SYSTEM, ROUTE_TABLE_LEGACY_SYSTEM,
-                               fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID,
-                               INVALID_UID)) {
+                               fwmark.intValue, mask.intValue)) {
         return ret;
     }
     if (int ret = modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_LEGACY_NETWORK,
-                               ROUTE_TABLE_LEGACY_NETWORK, fwmark.intValue,
-                               mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID)) {
+                               ROUTE_TABLE_LEGACY_NETWORK, fwmark.intValue, mask.intValue)) {
         return ret;
     }
 
@@ -394,7 +423,7 @@
 
     // A rule to allow legacy routes from system apps to override VPNs.
     return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_VPN_OVERRIDE_SYSTEM, ROUTE_TABLE_LEGACY_SYSTEM,
-                        fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
+                        fwmark.intValue, mask.intValue);
 }
 
 // Add a new rule to look up the 'main' table, with the same selectors as the "default network"
@@ -402,7 +431,7 @@
 // route, the rule we're adding will never be used for normal routing lookups. However, the kernel
 // may fall-through to it to find directly-connected routes when it validates that a nexthop (in a
 // route being added) is reachable.
-WARN_UNUSED_RESULT int AddDirectlyConnectedRule() {
+WARN_UNUSED_RESULT int addDirectlyConnectedRule() {
     Fwmark fwmark;
     Fwmark mask;
 
@@ -410,14 +439,26 @@
     mask.netId = FWMARK_NET_ID_MASK;
 
     return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_DIRECTLY_CONNECTED, RT_TABLE_MAIN,
-                        fwmark.intValue, mask.intValue, OIF_NONE, UID_ROOT, UID_ROOT);
+                        fwmark.intValue, mask.intValue, IIF_NONE, OIF_NONE, UID_ROOT, UID_ROOT);
 }
 
 // Add a rule to preempt the pre-defined "from all lookup main" rule. Packets that reach this rule
 // will be null-routed, and won't fall-through to the main table.
-WARN_UNUSED_RESULT int AddUnreachableRule() {
+WARN_UNUSED_RESULT int addUnreachableRule() {
     return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_UNREACHABLE, RT_TABLE_UNSPEC, MARK_UNSET,
-                        MARK_UNSET, OIF_NONE, INVALID_UID, INVALID_UID);
+                        MARK_UNSET);
+}
+
+// A rule to lookup the local network before looking up the default network.
+WARN_UNUSED_RESULT int addImplicitLocalNetworkRule() {
+    Fwmark fwmark;
+    Fwmark mask;
+
+    fwmark.explicitlySelected = false;
+    mask.explicitlySelected = true;
+
+    return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_LOCAL_NETWORK, ROUTE_TABLE_LOCAL_NETWORK,
+                        fwmark.intValue, mask.intValue);
 }
 
 // An iptables rule to mark incoming packets on a network with the netId of the network.
@@ -471,7 +512,7 @@
     mask.permission = permission;
 
     return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_EXPLICIT_NETWORK, table,
-                        fwmark.intValue, mask.intValue, OIF_NONE, uidStart, uidEnd);
+                        fwmark.intValue, mask.intValue, IIF_NONE, OIF_NONE, uidStart, uidEnd);
 }
 
 // A rule to route traffic based on a chosen outgoing interface.
@@ -488,7 +529,7 @@
     mask.permission = permission;
 
     return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_OUTPUT_INTERFACE, table,
-                        fwmark.intValue, mask.intValue, interface, uidStart, uidEnd);
+                        fwmark.intValue, mask.intValue, IIF_NONE, interface, uidStart, uidEnd);
 }
 
 // A rule to route traffic based on the chosen network.
@@ -511,7 +552,7 @@
     mask.permission = permission;
 
     return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_IMPLICIT_NETWORK, table,
-                        fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
+                        fwmark.intValue, mask.intValue);
 }
 
 // A rule to route all traffic from a given set of UIDs to go over the VPN.
@@ -528,7 +569,7 @@
     mask.protectedFromVpn = true;
 
     return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_SECURE_VPN, table,
-                        fwmark.intValue, mask.intValue, OIF_NONE, uidStart, uidEnd);
+                        fwmark.intValue, mask.intValue, IIF_NONE, 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
@@ -547,39 +588,26 @@
     mask.permission = PERMISSION_SYSTEM;
 
     return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_SECURE_VPN, table,
-                        fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
+                        fwmark.intValue, mask.intValue);
 }
 
-// A rule to allow local network routes to override the default network.
-WARN_UNUSED_RESULT int modifyLocalOverrideRule(uint32_t table, bool add) {
-    Fwmark fwmark;
-    Fwmark mask;
-
-    fwmark.explicitlySelected = false;
-    mask.explicitlySelected = true;
-
-    return modifyIpRule(add ? RTM_NEWRULE : RTM_DELRULE, RULE_PRIORITY_LOCAL_NETWORK, table,
-                        fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
-}
-
-WARN_UNUSED_RESULT int modifyLocalNetwork(unsigned netId, const char* interface, bool add) {
-    uint32_t table = getRouteTableForInterface(interface);
+WARN_UNUSED_RESULT int modifyTetheringRule(uint16_t action, const char* inputInterface,
+                                           const char* outputInterface) {
+    uint32_t table = getRouteTableForInterface(outputInterface);
     if (table == RT_TABLE_UNSPEC) {
         return -ESRCH;
     }
 
+    return modifyIpRule(action, RULE_PRIORITY_TETHERING, table, MARK_UNSET, MARK_UNSET,
+                        inputInterface, outputInterface, INVALID_UID, INVALID_UID);
+}
+
+WARN_UNUSED_RESULT int modifyLocalNetwork(unsigned netId, const char* interface, bool add) {
     if (int ret = modifyIncomingPacketMark(netId, interface, PERMISSION_NONE, add)) {
         return ret;
     }
-    if (int ret = modifyExplicitNetworkRule(netId, table, PERMISSION_NONE, INVALID_UID, INVALID_UID,
-                                            add)) {
-        return ret;
-    }
-    if (int ret = modifyOutputInterfaceRule(interface, table, PERMISSION_NONE, INVALID_UID,
-                                            INVALID_UID, add)) {
-        return ret;
-    }
-    return modifyLocalOverrideRule(table, add);
+    return modifyOutputInterfaceRule(interface, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE,
+                                     INVALID_UID, INVALID_UID, add);
 }
 
 WARN_UNUSED_RESULT int modifyPhysicalNetwork(unsigned netId, const char* interface,
@@ -656,7 +684,7 @@
     mask.permission = permission;
 
     return modifyIpRule(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark.intValue,
-                        mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
+                        mask.intValue);
 }
 
 // Adds or removes an IPv4 or IPv6 route to the specified table and, if it's a directly-connected
@@ -673,6 +701,10 @@
             }
             break;
         }
+        case RouteController::LOCAL_NETWORK: {
+            table = ROUTE_TABLE_LOCAL_NETWORK;
+            break;
+        }
         case RouteController::LEGACY_NETWORK: {
             table = ROUTE_TABLE_LEGACY_NETWORK;
             break;
@@ -687,7 +719,8 @@
     // We allow apps to call requestRouteToHost() multiple times with the same route, so ignore
     // EEXIST failures when adding routes to legacy tables.
     if (ret && !(action == RTM_NEWROUTE && ret == -EEXIST &&
-                 tableType != RouteController::INTERFACE)) {
+                 (tableType == RouteController::LEGACY_NETWORK ||
+                  tableType == RouteController::LEGACY_SYSTEM))) {
         return ret;
     }
 
@@ -737,40 +770,37 @@
 
 }  // namespace
 
-int RouteController::Init() {
-    if (int ret = AddDirectlyConnectedRule()) {
+int RouteController::Init(unsigned localNetId) {
+    if (int ret = addDirectlyConnectedRule()) {
         return ret;
     }
-    if (int ret = AddLegacyRouteRules()) {
+    if (int ret = addLegacyRouteRules()) {
         return ret;
     }
     // TODO: Enable once we are sure everything works.
     if (false) {
-        if (int ret = AddUnreachableRule()) {
+        if (int ret = addUnreachableRule()) {
             return ret;
         }
     }
+    if (int ret = addImplicitLocalNetworkRule()) {
+        return ret;
+    }
+    // Add a rule to lookup the local network it has been explicitly selected.
+    if (int ret = modifyExplicitNetworkRule(localNetId, ROUTE_TABLE_LOCAL_NETWORK, PERMISSION_NONE,
+                                            INVALID_UID, INVALID_UID, ACTION_ADD)) {
+        return ret;
+    }
     updateTableNamesFile();
     return 0;
 }
 
 int RouteController::addInterfaceToLocalNetwork(unsigned netId, const char* interface) {
-    if (int ret = modifyLocalNetwork(netId, interface, ACTION_ADD)) {
-        return ret;
-    }
-    updateTableNamesFile();
-    return 0;
+    return modifyLocalNetwork(netId, interface, ACTION_ADD);
 }
 
 int RouteController::removeInterfaceFromLocalNetwork(unsigned netId, const char* interface) {
-    if (int ret = modifyLocalNetwork(netId, interface, ACTION_DEL)) {
-        return ret;
-    }
-    if (int ret = flushRoutes(interface)) {
-        return ret;
-    }
-    updateTableNamesFile();
-    return 0;
+    return modifyLocalNetwork(netId, interface, ACTION_DEL);
 }
 
 int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
@@ -857,3 +887,11 @@
                                  const char* nexthop, TableType tableType) {
     return modifyRoute(RTM_DELROUTE, interface, destination, nexthop, tableType);
 }
+
+int RouteController::enableTethering(const char* inputInterface, const char* outputInterface) {
+    return modifyTetheringRule(RTM_NEWRULE, inputInterface, outputInterface);
+}
+
+int RouteController::disableTethering(const char* inputInterface, const char* outputInterface) {
+    return modifyTetheringRule(RTM_DELRULE, inputInterface, outputInterface);
+}
diff --git a/server/RouteController.h b/server/RouteController.h
index a3010de..1d6075d 100644
--- a/server/RouteController.h
+++ b/server/RouteController.h
@@ -29,13 +29,14 @@
     // How the routing table number is determined for route modification requests.
     enum TableType {
         INTERFACE,       // Compute the table number based on the interface index.
+        LOCAL_NETWORK,   // A fixed table used for routes to directly-connected clients/peers.
         LEGACY_NETWORK,  // Use a fixed table that's used to override the default network.
         LEGACY_SYSTEM,   // A fixed table, only modifiable by system apps; overrides VPNs too.
     };
 
     static const int ROUTE_TABLE_OFFSET_FROM_INDEX = 1000;
 
-    static int Init() WARN_UNUSED_RESULT;
+    static int Init(unsigned localNetId) WARN_UNUSED_RESULT;
 
     static int addInterfaceToLocalNetwork(unsigned netId, const char* interface) WARN_UNUSED_RESULT;
     static int removeInterfaceFromLocalNetwork(unsigned netId,
@@ -69,6 +70,11 @@
                         TableType tableType) WARN_UNUSED_RESULT;
     static int removeRoute(const char* interface, const char* destination, const char* nexthop,
                            TableType tableType) WARN_UNUSED_RESULT;
+
+    static int enableTethering(const char* inputInterface,
+                               const char* outputInterface) WARN_UNUSED_RESULT;
+    static int disableTethering(const char* inputInterface,
+                                const char* outputInterface) WARN_UNUSED_RESULT;
 };
 
 #endif  // NETD_SERVER_ROUTE_CONTROLLER_H
diff --git a/server/TetherController.cpp b/server/TetherController.cpp
index 47b620d..fbee5a2 100644
--- a/server/TetherController.cpp
+++ b/server/TetherController.cpp
@@ -33,10 +33,8 @@
 
 #include "NetdConstants.h"
 #include "TetherController.h"
-#include "NetworkController.h"
 
-TetherController::TetherController(NetworkController* networkController) :
-        mNetworkController(networkController) {
+TetherController::TetherController() {
     mInterfaces = new InterfaceCollection();
     mDnsForwarders = new NetAddressCollection();
     mDaemonFd = -1;
@@ -172,17 +170,6 @@
         ALOGD("Tethering services running");
     }
 
-    unsigned netId = mNetworkController->getNetIdForLocalNetwork();
-    if (int ret = mNetworkController->createLocalNetwork(netId)) {
-        return ret;
-    }
-    // If any interfaces have already been configured, add them to the local network now.
-    for (InterfaceCollection::iterator it = mInterfaces->begin(); it != mInterfaces->end(); ++it) {
-        if (int ret = mNetworkController->addInterfaceToNetwork(netId, *it)) {
-            return ret;
-        }
-    }
-
     return 0;
 }
 
@@ -195,9 +182,6 @@
 
     ALOGD("Stopping tethering services");
 
-    // Ignore any error.
-    (void) mNetworkController->destroyNetwork(mNetworkController->getNetIdForLocalNetwork());
-
     kill(mDaemonPid, SIGTERM);
     waitpid(mDaemonPid, NULL, 0);
     mDaemonPid = 0;
@@ -307,10 +291,6 @@
         }
         return -1;
     } else {
-        if (isTetheringStarted()) {
-            unsigned netId = mNetworkController->getNetIdForLocalNetwork();
-            return mNetworkController->addInterfaceToNetwork(netId, interface);
-        }
         return 0;
     }
 }
@@ -320,12 +300,6 @@
 
     ALOGD("untetherInterface(%s)", interface);
 
-    if (isTetheringStarted()) {
-        unsigned netId = mNetworkController->getNetIdForLocalNetwork();
-        // Ignore any error.
-        (void) mNetworkController->removeInterfaceFromNetwork(netId, interface);
-    }
-
     for (it = mInterfaces->begin(); it != mInterfaces->end(); ++it) {
         if (!strcmp(interface, *it)) {
             free(*it);
diff --git a/server/TetherController.h b/server/TetherController.h
index 38f7593..4da10b0 100644
--- a/server/TetherController.h
+++ b/server/TetherController.h
@@ -24,17 +24,14 @@
 typedef android::netd::List<char *> InterfaceCollection;
 typedef android::netd::List<struct in_addr> NetAddressCollection;
 
-class NetworkController;
-
 class TetherController {
     InterfaceCollection  *mInterfaces;
     NetAddressCollection *mDnsForwarders;
     pid_t                 mDaemonPid;
     int                   mDaemonFd;
-    NetworkController* const mNetworkController;
 
 public:
-    explicit TetherController(NetworkController* networkController);
+    TetherController();
     virtual ~TetherController();
 
     int setIpFwdEnabled(bool enable);