Start using IP tool for advanced routing.

bug:5495862
bug:5396842
Change-Id: I51f21060947f57e63b18c4d35e9d49fac488d48a
diff --git a/Android.mk b/Android.mk
index 2004eba..dbf9b7d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -13,6 +13,7 @@
                   PanController.cpp                    \
                   PppController.cpp                    \
                   ResolverController.cpp               \
+                  SecondaryTableController.cpp         \
                   SoftapController.cpp                 \
                   TetherController.cpp                 \
                   ThrottleController.cpp               \
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 0d14d13..0fed54b 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -37,7 +37,7 @@
 #include "ResponseCode.h"
 #include "ThrottleController.h"
 #include "BandwidthController.h"
-
+#include "SecondaryTableController.h"
 
 
 TetherController *CommandListener::sTetherCtrl = NULL;
@@ -47,6 +47,7 @@
 SoftapController *CommandListener::sSoftapCtrl = NULL;
 BandwidthController * CommandListener::sBandwidthCtrl = NULL;
 ResolverController *CommandListener::sResolverCtrl = NULL;
+SecondaryTableController *CommandListener::sSecondaryTableCtrl = NULL;
 
 CommandListener::CommandListener() :
                  FrameworkListener("netd") {
@@ -61,10 +62,12 @@
     registerCmd(new BandwidthControlCmd());
     registerCmd(new ResolverCmd());
 
+    if (!sSecondaryTableCtrl)
+        sSecondaryTableCtrl = new SecondaryTableController();
     if (!sTetherCtrl)
         sTetherCtrl = new TetherController();
     if (!sNatCtrl)
-        sNatCtrl = new NatController();
+        sNatCtrl = new NatController(sSecondaryTableCtrl);
     if (!sPppCtrl)
         sPppCtrl = new PppController();
     if (!sPanCtrl)
@@ -203,27 +206,51 @@
             return 0;
         }
 
+        //     0       1       2        3          4           5     6      7
+        // interface route add/remove iface default/secondary dest prefix gateway
         if (!strcmp(argv[1], "route")) {
             int prefix_length = 0;
-            if (argc < 7) {
+            if (argc < 8) {
                 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
                 return 0;
             }
-            if (sscanf(argv[5], "%d", &prefix_length) != 1) {
+            if (sscanf(argv[6], "%d", &prefix_length) != 1) {
                 cli->sendMsg(ResponseCode::CommandParameterError, "Invalid route prefix", false);
                 return 0;
             }
             if (!strcmp(argv[2], "add")) {
-                if (ifc_add_route(argv[3], argv[4], prefix_length, argv[6])) {
-                    cli->sendMsg(ResponseCode::OperationFailed, "Failed to add route", true);
+                if (!strcmp(argv[4], "default")) {
+                    if (ifc_add_route(argv[3], argv[5], prefix_length, argv[7])) {
+                        cli->sendMsg(ResponseCode::OperationFailed,
+                                "Failed to add route to default table", true);
+                    } else {
+                        cli->sendMsg(ResponseCode::CommandOkay,
+                                "Route added to default table", false);
+                    }
+                } else if (!strcmp(argv[4], "secondary")) {
+                    return sSecondaryTableCtrl->addRoute(cli, argv[3], argv[5],
+                            prefix_length, argv[7]);
                 } else {
-                    cli->sendMsg(ResponseCode::CommandOkay, "Route added", false);
+                    cli->sendMsg(ResponseCode::CommandParameterError,
+                            "Invalid route type, expecting 'default' or 'secondary'", false);
+                    return 0;
                 }
             } else if (!strcmp(argv[2], "remove")) {
-                if (ifc_remove_route(argv[3], argv[4], prefix_length, argv[6])) {
-                    cli->sendMsg(ResponseCode::OperationFailed, "Failed to remove route", true);
+                if (!strcmp(argv[4], "default")) {
+                    if (ifc_remove_route(argv[3], argv[5], prefix_length, argv[7])) {
+                        cli->sendMsg(ResponseCode::OperationFailed,
+                                "Failed to remove route from default table", true);
+                    } else {
+                        cli->sendMsg(ResponseCode::CommandOkay,
+                                "Route removed from default table", false);
+                    }
+                } else if (!strcmp(argv[4], "secondary")) {
+                    return sSecondaryTableCtrl->removeRoute(cli, argv[3], argv[5],
+                            prefix_length, argv[7]);
                 } else {
-                    cli->sendMsg(ResponseCode::CommandOkay, "Route removed", false);
+                    cli->sendMsg(ResponseCode::CommandParameterError,
+                            "Invalid route type, expecting 'default' or 'secondary'", false);
+                    return 0;
                 }
             } else {
                 cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown interface cmd", false);
@@ -575,21 +602,21 @@
                                                       int argc, char **argv) {
     int rc = 0;
 
-    if (argc < 3) {
+    if (argc < 5) {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
         return 0;
     }
 
     if (!strcmp(argv[1], "enable")) {
-        rc = sNatCtrl->enableNat(argv[2], argv[3]);
+        rc = sNatCtrl->enableNat(argc, argv);
         if(!rc) {
             /* Ignore ifaces for now. */
             rc = sBandwidthCtrl->setGlobalAlertInForwardChain();
         }
     } else if (!strcmp(argv[1], "disable")) {
-        rc = sNatCtrl->disableNat(argv[2], argv[3]);
         /* Ignore ifaces for now. */
-        rc |= sBandwidthCtrl->removeGlobalAlertInForwardChain();
+        rc = sBandwidthCtrl->removeGlobalAlertInForwardChain();
+        rc |= sNatCtrl->disableNat(argc, argv);
     } else {
         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown nat cmd", false);
         return 0;
diff --git a/CommandListener.h b/CommandListener.h
index f0a7db7..0ed600b 100644
--- a/CommandListener.h
+++ b/CommandListener.h
@@ -27,6 +27,7 @@
 #include "SoftapController.h"
 #include "BandwidthController.h"
 #include "ResolverController.h"
+#include "SecondaryTableController.h"
 
 class CommandListener : public FrameworkListener {
     static TetherController *sTetherCtrl;
@@ -36,6 +37,7 @@
     static SoftapController *sSoftapCtrl;
     static BandwidthController *sBandwidthCtrl;
     static ResolverController *sResolverCtrl;
+    static SecondaryTableController *sSecondaryTableCtrl;
 
 public:
     CommandListener();
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;
 }
diff --git a/NatController.h b/NatController.h
index 1ab0202..d10cbd9 100644
--- a/NatController.h
+++ b/NatController.h
@@ -21,22 +21,25 @@
 
 #include <utils/List.h>
 
+#include "SecondaryTableController.h"
+
 class NatController {
 
 public:
-    NatController();
+    NatController(SecondaryTableController *ctrl);
     virtual ~NatController();
 
-    int enableNat(const char *intIface, const char *extIface);
-    int disableNat(const char *intIface, const char *extIface);
+    int enableNat(const int argc, char **argv);
+    int disableNat(const int argc, char **argv);
 
 private:
     int natCount;
+    SecondaryTableController *secondaryTableCtrl;
 
     int setDefaults();
-    int runIptablesCmd(const char *cmd);
-    bool interfaceExists(const char *iface);
-    int doNatCommands(const char *intIface, const char *extIface, bool add);
+    int runCmd(const char *path, const char *cmd);
+    bool checkInterface(const char *iface);
+    int setForwardRules(bool set, const char *intIface, const char *extIface);
 };
 
 #endif
diff --git a/SecondaryTableController.cpp b/SecondaryTableController.cpp
new file mode 100644
index 0000000..d6f6825
--- /dev/null
+++ b/SecondaryTableController.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008 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 <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define LOG_TAG "SecondaryTablController"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "ResponseCode.h"
+#include "SecondaryTableController.h"
+
+static char IP_PATH[] = "/system/bin/ip";
+
+SecondaryTableController::SecondaryTableController() {
+    int i;
+    for (i=0; i < INTERFACES_TRACKED; i++) {
+        mInterfaceTable[i][0] = 0;
+        // TODO - use a hashtable or other prebuilt container class
+        mInterfaceRuleCount[i] = 0;
+    }
+}
+
+SecondaryTableController::~SecondaryTableController() {
+}
+
+int SecondaryTableController::findTableNumber(const char *iface) {
+    int i;
+    for (i = 0; i < INTERFACES_TRACKED; i++) {
+        if (strncmp(iface, mInterfaceTable[i], MAX_IFACE_LENGTH) == 0) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+int SecondaryTableController::addRoute(SocketClient *cli, char *iface, char *dest, int prefix,
+        char *gateway) {
+    char *cmd;
+
+    int tableIndex = findTableNumber(iface);
+    if (tableIndex == -1) {
+        tableIndex = findTableNumber(""); // look for an empty slot
+        if (tableIndex == -1) {
+            LOGE("Max number of NATed interfaces reached");
+            errno = ENODEV;
+            cli->sendMsg(ResponseCode::OperationFailed, "Max number NATed", true);
+            return -1;
+        }
+        strncpy(mInterfaceTable[tableIndex], iface, MAX_IFACE_LENGTH);
+    }
+
+    asprintf(&cmd, "%s route add %s/%d via %s table %d",
+            IP_PATH, dest, prefix, gateway, tableIndex+BASE_TABLE_NUMBER);
+    if (runAndFree(cli, cmd)) {
+        LOGE("ip route add failed: %s", cmd);
+        errno = ENODEV;
+        cli->sendMsg(ResponseCode::OperationFailed, "ip route add failed", true);
+        return -1;
+    }
+    mInterfaceRuleCount[tableIndex]++;
+    cli->sendMsg(ResponseCode::CommandOkay, "Route added", false);
+    return 0;
+}
+
+int SecondaryTableController::removeRoute(SocketClient *cli, char *iface, char *dest, int prefix,
+        char *gateway) {
+    char *cmd;
+    int tableIndex = findTableNumber(iface);
+    if (tableIndex == -1) {
+        LOGE("Interface not found");
+        errno = ENODEV;
+        cli->sendMsg(ResponseCode::OperationFailed, "Interface not found", true);
+        return -1;
+    }
+
+    asprintf(&cmd, "%s route del %s/%d via %s table %d",
+            IP_PATH, dest, prefix, gateway, tableIndex+BASE_TABLE_NUMBER);
+    if (runAndFree(cli, cmd)) {
+        LOGE("ip route del failed");
+        errno = ENODEV;
+        cli->sendMsg(ResponseCode::OperationFailed, "ip route del failed", true);
+        return -1;
+    }
+    if (--mInterfaceRuleCount[tableIndex]<1) {
+        mInterfaceTable[tableIndex][0]=0;
+    }
+    cli->sendMsg(ResponseCode::CommandOkay, "Route removed", false);
+    return 0;
+}
+
+int SecondaryTableController::runAndFree(SocketClient *cli, char *cmd) {
+    int ret = 0;
+    if (strlen(cmd) >= 255) {
+        LOGE("ip command (%s) too long", cmd);
+        errno = E2BIG;
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Too long", true);
+        free(cmd);
+        return -1;
+    }
+    ret = system(cmd);
+    free(cmd);
+    return ret;
+}
diff --git a/SecondaryTableController.h b/SecondaryTableController.h
new file mode 100644
index 0000000..7c77385
--- /dev/null
+++ b/SecondaryTableController.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 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 _SECONDARY_TABLE_CONTROLLER_H
+#define _SECONDARY_TABLE_CONTROLLER_H
+
+#include <sysutils/FrameworkListener.h>
+
+static const unsigned int MAX_IFACE_LENGTH = 10;
+static const int INTERFACES_TRACKED = 10;
+static const int BASE_TABLE_NUMBER = 60;
+static int MAX_TABLE_NUMBER = BASE_TABLE_NUMBER + INTERFACES_TRACKED;
+
+class SecondaryTableController {
+
+public:
+    SecondaryTableController();
+    virtual ~SecondaryTableController();
+
+    int addRoute(SocketClient *cli, char *iface, char *dest, int prefixLen, char *gateway);
+    int removeRoute(SocketClient *cli, char *iface, char *dest, int prefixLen, char *gateway);
+    int findTableNumber(const char *iface);
+
+private:
+    char mInterfaceTable[INTERFACES_TRACKED][MAX_IFACE_LENGTH];
+    int mInterfaceRuleCount[INTERFACES_TRACKED];
+
+    int runAndFree(SocketClient *cli, char *cmd);
+};
+
+#endif