Add symbolic table names for routing table numbers.

This makes the output from "ip rule" much more readable.

Companion changes are in AOSP.

Change-Id: I69deb1a64d5d6647470823405bf0cc55b24b22de
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index bc50dc4..6604139 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -25,9 +25,11 @@
 #include "resolv_netid.h"
 
 #include <arpa/inet.h>
+#include <fcntl.h>
 #include <linux/fib_rules.h>
 #include <map>
 #include <net/if.h>
+#include <sys/stat.h>
 
 namespace {
 
@@ -49,6 +51,12 @@
 const uint32_t RULE_PRIORITY_DIRECTLY_CONNECTED  = 23000;
 const uint32_t RULE_PRIORITY_UNREACHABLE         = 24000;
 
+const uint32_t ROUTE_TABLE_LEGACY_NETWORK = 98;
+const uint32_t ROUTE_TABLE_LEGACY_SYSTEM  = 99;
+
+const char* const ROUTE_TABLE_NAME_LEGACY_NETWORK = "legacy_network";
+const char* const ROUTE_TABLE_NAME_LEGACY_SYSTEM  = "legacy_system";
+
 // TODO: These values aren't defined by the Linux kernel, because our UID routing changes are not
 // upstream (yet?), so we can't just pick them up from kernel headers. When (if?) the changes make
 // it upstream, we'll remove this and rely on the kernel header values. For now, add a static assert
@@ -74,6 +82,8 @@
 const bool ACTION_DEL = false;
 const bool MODIFY_NON_UID_BASED_RULES = true;
 
+const char* const RT_TABLES_PATH = "/data/misc/net/rt_tables";
+
 // Avoids "non-constant-expression cannot be narrowed from type 'unsigned int' to 'unsigned short'"
 // warnings when using RTA_LENGTH(x) inside static initializers (even when x is already uint16_t).
 constexpr uint16_t U16_RTA_LENGTH(uint16_t x) {
@@ -96,23 +106,60 @@
 
 // END CONSTANTS ----------------------------------------------------------------------------------
 
-std::map<std::string, uint32_t> interfaceToIndex;
+// No locks needed because RouteController is accessed only from one thread (in CommandListener).
+std::map<std::string, uint32_t> interfaceToTable;
 
 uint32_t getRouteTableForInterface(const char* interface) {
     uint32_t index = if_nametoindex(interface);
     if (index) {
-        interfaceToIndex[interface] = index;
-    } else {
-        // If the interface goes away if_nametoindex() will return 0 but we still need to know
-        // the index so we can remove the rules and routes.
-        auto iter = interfaceToIndex.find(interface);
-        if (iter == interfaceToIndex.end()) {
-            ALOGE("cannot find interface %s", interface);
-            return RT_TABLE_UNSPEC;
-        }
-        index = iter->second;
+        index += RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX;
+        interfaceToTable[interface] = index;
+        return index;
     }
-    return index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX;
+    // If the interface goes away if_nametoindex() will return 0 but we still need to know
+    // the index so we can remove the rules and routes.
+    auto iter = interfaceToTable.find(interface);
+    if (iter == interfaceToTable.end()) {
+        ALOGE("cannot find interface %s", interface);
+        return RT_TABLE_UNSPEC;
+    }
+    return iter->second;
+}
+
+void addTableName(uint32_t table, const std::string& name, std::string* contents) {
+    char tableString[UINT32_STRLEN];
+    snprintf(tableString, sizeof(tableString), "%u", table);
+    *contents += tableString;
+    *contents += " ";
+    *contents += name;
+    *contents += "\n";
+}
+
+// Doesn't return success/failure as the file is optional; it's okay if we fail to update it.
+void updateTableNamesFile() {
+    std::string contents;
+    addTableName(ROUTE_TABLE_LEGACY_NETWORK, ROUTE_TABLE_NAME_LEGACY_NETWORK, &contents);
+    addTableName(ROUTE_TABLE_LEGACY_SYSTEM,  ROUTE_TABLE_NAME_LEGACY_SYSTEM,  &contents);
+    for (const auto& entry : interfaceToTable) {
+        addTableName(entry.second, entry.first, &contents);
+    }
+
+    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;  // mode 0644, rw-r--r--
+    int fd = open(RT_TABLES_PATH, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, mode);
+    if (fd == -1) {
+        ALOGE("failed to create %s (%s)", RT_TABLES_PATH, strerror(errno));
+        return;
+    }
+    // File creation is affected by umask, so make sure the right mode bits are set.
+    if (fchmod(fd, mode) == -1) {
+        ALOGE("failed to chmod %s to mode 0%o (%s)", RT_TABLES_PATH, mode, strerror(errno));
+    }
+    ssize_t bytesWritten = write(fd, contents.data(), contents.size());
+    if (bytesWritten != static_cast<ssize_t>(contents.size())) {
+        ALOGE("failed to write to %s (%zd vs %zu bytes) (%s)", RT_TABLES_PATH, bytesWritten,
+              contents.size(), strerror(errno));
+    }
+    close(fd);
 }
 
 // Sends a netlink request and expects an ack.
@@ -322,13 +369,13 @@
     mask.explicitlySelected = true;
 
     // Rules to allow legacy routes to override the default network.
-    if (int ret = modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_LEGACY_SYSTEM,
-                               RouteController::ROUTE_TABLE_LEGACY_SYSTEM, fwmark.intValue,
-                               mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID)) {
+    if (int ret = modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_LEGACY_SYSTEM, ROUTE_TABLE_LEGACY_SYSTEM,
+                               fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID,
+                               INVALID_UID)) {
         return ret;
     }
     if (int ret = modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_LEGACY_NETWORK,
-                               RouteController::ROUTE_TABLE_LEGACY_NETWORK, fwmark.intValue,
+                               ROUTE_TABLE_LEGACY_NETWORK, fwmark.intValue,
                                mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID)) {
         return ret;
     }
@@ -337,9 +384,8 @@
     mask.permission = PERMISSION_SYSTEM;
 
     // A rule to allow legacy routes from system apps to override VPNs.
-    return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_VPN_OVERRIDE_SYSTEM,
-                        RouteController::ROUTE_TABLE_LEGACY_SYSTEM, fwmark.intValue, mask.intValue,
-                        OIF_NONE, INVALID_UID, INVALID_UID);
+    return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_VPN_OVERRIDE_SYSTEM, ROUTE_TABLE_LEGACY_SYSTEM,
+                        fwmark.intValue, mask.intValue, OIF_NONE, INVALID_UID, INVALID_UID);
 }
 
 // Add a new rule to look up the 'main' table, with the same selectors as the "default network"
@@ -587,11 +633,11 @@
             break;
         }
         case RouteController::LEGACY_NETWORK: {
-            table = RouteController::ROUTE_TABLE_LEGACY_NETWORK;
+            table = ROUTE_TABLE_LEGACY_NETWORK;
             break;
         }
         case RouteController::LEGACY_SYSTEM: {
-            table = RouteController::ROUTE_TABLE_LEGACY_SYSTEM;
+            table = ROUTE_TABLE_LEGACY_SYSTEM;
             break;
         }
     }
@@ -644,7 +690,7 @@
         }
     }
 
-    interfaceToIndex.erase(interface);
+    interfaceToTable.erase(interface);
     return 0;
 }
 
@@ -654,20 +700,26 @@
     if (int ret = AddDirectlyConnectedRule()) {
         return ret;
     }
-
     if (int ret = AddLegacyRouteRules()) {
         return ret;
     }
     // TODO: Enable once we are sure everything works.
     if (false) {
-        return AddUnreachableRule();
+        if (int ret = AddUnreachableRule()) {
+            return ret;
+        }
     }
+    updateTableNamesFile();
     return 0;
 }
 
 int RouteController::addInterfaceToPhysicalNetwork(unsigned netId, const char* interface,
                                                    Permission permission) {
-    return modifyPhysicalNetwork(netId, interface, permission, ACTION_ADD);
+    if (int ret = modifyPhysicalNetwork(netId, interface, permission, ACTION_ADD)) {
+        return ret;
+    }
+    updateTableNamesFile();
+    return 0;
 }
 
 int RouteController::removeInterfaceFromPhysicalNetwork(unsigned netId, const char* interface,
@@ -675,13 +727,21 @@
     if (int ret = modifyPhysicalNetwork(netId, interface, permission, ACTION_DEL)) {
         return ret;
     }
-    return flushRoutes(interface);
+    if (int ret = flushRoutes(interface)) {
+        return ret;
+    }
+    updateTableNamesFile();
+    return 0;
 }
 
 int RouteController::addInterfaceToVirtualNetwork(unsigned netId, const char* interface,
                                                   const UidRanges& uidRanges) {
-    return modifyVirtualNetwork(netId, interface, uidRanges, ACTION_ADD,
-                                MODIFY_NON_UID_BASED_RULES);
+    if (int ret = modifyVirtualNetwork(netId, interface, uidRanges, ACTION_ADD,
+                                       MODIFY_NON_UID_BASED_RULES)) {
+        return ret;
+    }
+    updateTableNamesFile();
+    return 0;
 }
 
 int RouteController::removeInterfaceFromVirtualNetwork(unsigned netId, const char* interface,
@@ -690,7 +750,11 @@
                                        MODIFY_NON_UID_BASED_RULES)) {
         return ret;
     }
-    return flushRoutes(interface);
+    if (int ret = flushRoutes(interface)) {
+        return ret;
+    }
+    updateTableNamesFile();
+    return 0;
 }
 
 int RouteController::modifyPhysicalNetworkPermission(unsigned netId, const char* interface,