Start using IP tool for advanced routing.

bug:5495862
bug:5396842
Change-Id: I51f21060947f57e63b18c4d35e9d49fac488d48a
diff --git a/NatController.cpp b/NatController.cpp
index 49984f8..ce5b3f1 100644
--- a/NatController.cpp
+++ b/NatController.cpp
@@ -28,30 +28,33 @@
 #include <cutils/log.h>
 
 #include "NatController.h"
+#include "SecondaryTableController.h"
 
 extern "C" int logwrap(int argc, const char **argv, int background);
 
 static char IPTABLES_PATH[] = "/system/bin/iptables";
+static char IP_PATH[] = "/system/bin/ip";
 
-NatController::NatController() {
-    natCount = 0;
+NatController::NatController(SecondaryTableController *ctrl) {
+    secondaryTableCtrl = ctrl;
+    setDefaults();
 }
 
 NatController::~NatController() {
 }
 
-int NatController::runIptablesCmd(const char *cmd) {
+int NatController::runCmd(const char *path, const char *cmd) {
     char *buffer;
     size_t len = strnlen(cmd, 255);
     int res;
 
     if (len == 255) {
-        LOGE("iptables command too long");
+        LOGE("command too long");
         errno = E2BIG;
         return -1;
     }
 
-    asprintf(&buffer, "%s %s", IPTABLES_PATH, cmd);
+    asprintf(&buffer, "%s %s", path, cmd);
     res = system(buffer);
     free(buffer);
     return res;
@@ -59,57 +62,113 @@
 
 int NatController::setDefaults() {
 
-    if (runIptablesCmd("-P INPUT ACCEPT"))
+    if (runCmd(IPTABLES_PATH, "-P INPUT ACCEPT"))
         return -1;
-    if (runIptablesCmd("-P OUTPUT ACCEPT"))
+    if (runCmd(IPTABLES_PATH, "-P OUTPUT ACCEPT"))
         return -1;
-    if (runIptablesCmd("-P FORWARD DROP"))
+    if (runCmd(IPTABLES_PATH, "-P FORWARD DROP"))
         return -1;
-    if (runIptablesCmd("-F FORWARD"))
+    if (runCmd(IPTABLES_PATH, "-F FORWARD"))
         return -1;
-    if (runIptablesCmd("-t nat -F"))
+    if (runCmd(IPTABLES_PATH, "-t nat -F"))
         return -1;
+
+    runCmd(IP_PATH, "rule flush");
+    runCmd(IP_PATH, "rule add from all lookup default prio 32767");
+    runCmd(IP_PATH, "rule add from all lookup main prio 32766");
+
+    natCount = 0;
     return 0;
 }
 
-bool NatController::interfaceExists(const char *iface) {
-    // XXX: Implement this
+bool NatController::checkInterface(const char *iface) {
+    if (strlen(iface) > MAX_IFACE_LENGTH) return false;
     return true;
 }
 
-// when un-doing NAT, we should report errors, but also try to do as much cleanup
-// as we can - don't short circuit on error.
-int NatController::doNatCommands(const char *intIface, const char *extIface, bool add) {
+//  0    1       2       3       4            5
+// nat enable intface extface addrcnt nated-ipaddr/prelength
+int NatController::enableNat(const int argc, char **argv) {
     char cmd[255];
+    int i;
+    int addrCount = atoi(argv[4]);
+    int ret = 0;
+    const char *intIface = argv[2];
+    const char *extIface = argv[3];
+    int tableNumber;
 
-    char bootmode[PROPERTY_VALUE_MAX] = {0};
-    property_get("ro.bootmode", bootmode, "unknown");
-    if (0 != strcmp("bp-tools", bootmode)) {
-        // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
-        if (add == false) {
-            if (natCount <= 1) {
-                int ret = setDefaults();
-                if (ret == 0) {
-                    natCount=0;
-                }
-                LOGE("setDefaults returned %d", ret);
-                return ret;
-            }
-        }
-    }
-
-    if (!interfaceExists(intIface) || !interfaceExists (extIface)) {
+    if (!checkInterface(intIface) || !checkInterface(extIface)) {
         LOGE("Invalid interface specified");
         errno = ENODEV;
         return -1;
     }
 
+    if (argc < 5 + addrCount) {
+        LOGE("Missing Argument");
+        errno = EINVAL;
+        return -1;
+    }
+
+    tableNumber = secondaryTableCtrl->findTableNumber(extIface);
+    if (tableNumber != -1) {
+        for(i = 0; i < addrCount && ret == 0; i++) {
+            snprintf(cmd, sizeof(cmd), "rule add from %s table %d", argv[5+i],
+                    tableNumber + BASE_TABLE_NUMBER);
+            ret |= runCmd(IP_PATH, cmd);
+            if (ret) LOGE("IP rule %s got %d", cmd, ret);
+
+            snprintf(cmd, sizeof(cmd), "route add %s dev %s table %d", argv[5+i], intIface,
+                    tableNumber + BASE_TABLE_NUMBER);
+            ret |= runCmd(IP_PATH, cmd);
+            if (ret) LOGE("IP route %s got %d", cmd, ret);
+        }
+    }
+
+    if (ret != 0 || setForwardRules(true, intIface, extIface) != 0) {
+        if (tableNumber != -1) {
+            for (i = 0; i < addrCount; i++) {
+                snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
+                        tableNumber + BASE_TABLE_NUMBER);
+                runCmd(IP_PATH, cmd);
+
+                snprintf(cmd, sizeof(cmd), "rule del from %s table %d", argv[5+i],
+                        tableNumber + BASE_TABLE_NUMBER);
+                runCmd(IP_PATH, cmd);
+            }
+        }
+        LOGE("Error setting forward rules");
+        errno = ENODEV;
+        return -1;
+    }
+
+    natCount++;
+    // add this if we are the first added nat
+    if (natCount == 1) {
+        snprintf(cmd, sizeof(cmd), "-t nat -A POSTROUTING -o %s -j MASQUERADE", extIface);
+        if (runCmd(IPTABLES_PATH, cmd)) {
+            LOGE("Error seting postroute rule: %s", cmd);
+            // unwind what's been done, but don't care about success - what more could we do?
+            for (i = 0; i < addrCount; i++) {
+                snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
+                        tableNumber + BASE_TABLE_NUMBER);
+                runCmd(IP_PATH, cmd);
+            }
+            setDefaults();
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+int NatController::setForwardRules(bool add, const char *intIface, const char * extIface) {
+    char cmd[255];
+
     snprintf(cmd, sizeof(cmd),
              "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
              (add ? "A" : "D"),
              extIface, intIface);
-    if (runIptablesCmd(cmd) && add) {
-        // only bail out if we are adding, not removing nat rules
+    if (runCmd(IPTABLES_PATH, cmd) && add) {
         return -1;
     }
 
@@ -117,58 +176,80 @@
             "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP",
             (add ? "A" : "D"),
             intIface, extIface);
-    if (runIptablesCmd(cmd) && add) {
+    if (runCmd(IPTABLES_PATH, cmd) && add) {
         // bail on error, but only if adding
         snprintf(cmd, sizeof(cmd),
                 "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
                 (!add ? "A" : "D"),
                 extIface, intIface);
-        runIptablesCmd(cmd);
+        runCmd(IPTABLES_PATH, cmd);
         return -1;
     }
 
     snprintf(cmd, sizeof(cmd), "-%s FORWARD -i %s -o %s -j ACCEPT", (add ? "A" : "D"),
             intIface, extIface);
-    if (runIptablesCmd(cmd) && add) {
+    if (runCmd(IPTABLES_PATH, cmd) && add) {
         // unwind what's been done, but don't care about success - what more could we do?
         snprintf(cmd, sizeof(cmd),
                 "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP",
                 (!add ? "A" : "D"),
                 intIface, extIface);
-        runIptablesCmd(cmd);
+        runCmd(IPTABLES_PATH, cmd);
 
         snprintf(cmd, sizeof(cmd),
                  "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
                  (!add ? "A" : "D"),
                  extIface, intIface);
-        runIptablesCmd(cmd);
+        runCmd(IPTABLES_PATH, cmd);
         return -1;
     }
-
-    // add this if we are the first added nat
-    if (add && natCount == 0) {
-        snprintf(cmd, sizeof(cmd), "-t nat -A POSTROUTING -o %s -j MASQUERADE", extIface);
-        if (runIptablesCmd(cmd)) {
-            if (0 != strcmp("bp-tools", bootmode)) {
-                // unwind what's been done, but don't care about success - what more could we do?
-                setDefaults();;
-            }
-            return -1;
-        }
-    }
-
-    if (add) {
-        natCount++;
-    } else {
-        natCount--;
-    }
     return 0;
 }
 
-int NatController::enableNat(const char *intIface, const char *extIface) {
-    return doNatCommands(intIface, extIface, true);
-}
+// 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) {
+    char cmd[255];
+    int i;
+    int addrCount = atoi(argv[4]);
+    const char *intIface = argv[2];
+    const char *extIface = argv[3];
+    int tableNumber;
 
-int NatController::disableNat(const char *intIface, const char *extIface) {
-    return doNatCommands(intIface, extIface, false);
+    if (!checkInterface(intIface) || !checkInterface(extIface)) {
+        LOGE("Invalid interface specified");
+        errno = ENODEV;
+        return -1;
+    }
+
+    if (argc < 5 + addrCount) {
+        LOGE("Missing Argument");
+        errno = EINVAL;
+        return -1;
+    }
+
+    setForwardRules(false, intIface, extIface);
+
+    tableNumber = secondaryTableCtrl->findTableNumber(extIface);
+    if (tableNumber != -1) {
+        for (i = 0; i < addrCount; i++) {
+            snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
+                    tableNumber + BASE_TABLE_NUMBER);
+            // if the interface has gone down these will be gone already and give errors
+            // ignore them.
+            runCmd(IP_PATH, cmd);
+        }
+    }
+
+    if (--natCount <= 0) {
+        char bootmode[PROPERTY_VALUE_MAX] = {0};
+        property_get("ro.bootmode", bootmode, "unknown");
+        if (0 != strcmp("bp-tools", bootmode)) {
+            // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
+            setDefaults();
+        }
+        natCount = 0;
+    }
+    return 0;
 }