Fix tethering in the case of a regular upstream connection.

Fixes tethering via Ethernet, Bluetooth and WiFi (hotspot).

Tethering when the upstream has a DUN-specific APN is likely still broken
(untested).

For now, assign a fixed NetId (a hack) until we can change the framework to
create a valid NetworkAgent and all that jazz.

Bug: 15968336
Bug: 14988803
Change-Id: Idcf4d492d9329a9c87913e27be6dd835a792bea2
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index d33c47c..8efbda4 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -42,7 +42,7 @@
 const uint32_t RULE_PRIORITY_OUTPUT_INTERFACE    = 14000;
 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_LOCAL_NETWORK       = 17000;
 // const uint32_t RULE_PRIORITY_TETHERING           = 18000;
 const uint32_t RULE_PRIORITY_IMPLICIT_NETWORK    = 19000;
 // const uint32_t RULE_PRIORITY_BYPASSABLE_VPN      = 20000;
@@ -542,6 +542,38 @@
                         fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
 }
 
+// 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);
+    if (table == RT_TABLE_UNSPEC) {
+        return -ESRCH;
+    }
+
+    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);
+}
+
 WARN_UNUSED_RESULT int modifyPhysicalNetwork(unsigned netId, const char* interface,
                                              Permission permission, bool add) {
     uint32_t table = getRouteTableForInterface(interface);
@@ -714,6 +746,25 @@
     return 0;
 }
 
+int RouteController::addInterfaceToLocalNetwork(unsigned netId, const char* interface) {
+    if (int ret = modifyLocalNetwork(netId, interface, ACTION_ADD)) {
+        return ret;
+    }
+    updateTableNamesFile();
+    return 0;
+}
+
+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;
+}
+
 int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
                                                    Permission permission) {
     if (int ret = modifyPhysicalNetwork(netId, interface, permission, ACTION_ADD)) {