BandwidthController: fix bad flushing for bw_costly_* tables.

Some of the bw_costly_<iface> rules would not get correctly flushed and
cleared on netd re-start, which would cause a failure when trying to
setup the bw_penalty_box as bw_costly_<iface> would reference it.
The resulting symptom would be that bandwidth could not be re-enabled.

Bug: 10183445
Change-Id: I79a8a73ae52e18b3bff8a58e47ac1aea2454ae63
diff --git a/BandwidthController.cpp b/BandwidthController.cpp
index 0b6bcbe..e31a515 100644
--- a/BandwidthController.cpp
+++ b/BandwidthController.cpp
@@ -119,10 +119,6 @@
     "-F bw_penalty_box",
     "-F bw_costly_shared",
 
-    /* Just a couple that are the most common. */
-    "-F bw_costly_rmnet0",
-    "-F bw_costly_wlan0",
-
     "-t raw -F bw_raw_PREROUTING",
     "-t mangle -F bw_mangle_POSTROUTING",
 };
@@ -132,10 +128,6 @@
     "-X bw_happy_box",
     "-X bw_penalty_box",
     "-X bw_costly_shared",
-
-    /* Just a couple that are the most common. */
-    "-X bw_costly_rmnet0",
-    "-X bw_costly_wlan0",
 };
 
 const char *BandwidthController::IPT_SETUP_COMMANDS[] = {
@@ -231,15 +223,24 @@
     return res;
 }
 
-int BandwidthController::setupIptablesHooks(void) {
+void BandwidthController::flushCleanTables(bool doClean) {
+    /* Flush and remove the bw_costly_<iface> tables */
+    flushExistingCostlyTables(doClean);
 
     /* Some of the initialCommands are allowed to fail */
     runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
             IPT_FLUSH_COMMANDS, RunCmdFailureOk);
 
-    runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
-            IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
+    if (doClean) {
+        runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
+                IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
+    }
+}
 
+int BandwidthController::setupIptablesHooks(void) {
+
+    /* flush+clean is allowed to fail */
+    flushCleanTables(true);
     runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*),
             IPT_SETUP_COMMANDS, RunCmdFailureBad);
 
@@ -265,10 +266,8 @@
     globalAlertTetherCount = 0;
     sharedQuotaBytes = sharedAlertBytes = 0;
 
-    res = runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
-            IPT_FLUSH_COMMANDS, RunCmdFailureOk);
-
-    res |= runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
+    flushCleanTables(false);
+    res = runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
             IPT_BASIC_ACCOUNTING_COMMANDS, RunCmdFailureBad);
 
     return res;
@@ -276,8 +275,8 @@
 }
 
 int BandwidthController::disableBandwidthControl(void) {
-    runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
-            IPT_FLUSH_COMMANDS, RunCmdFailureOk);
+
+    flushCleanTables(false);
     return 0;
 }
 
@@ -1221,3 +1220,51 @@
     /* Currently NatController doesn't do ipv6 tethering, so we are done. */
     return res;
 }
+
+void BandwidthController::flushExistingCostlyTables(bool doClean) {
+    int res;
+    std::string fullCmd;
+    FILE *iptOutput;
+    const char *cmd;
+
+    /* Only lookup ip4 table names as ip6 will have the same tables ... */
+    fullCmd = IPTABLES_PATH;
+    fullCmd += " -S";
+    iptOutput = popen(fullCmd.c_str(), "r");
+    if (!iptOutput) {
+            ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
+        return;
+    }
+    /* ... then flush/clean both ip4 and ip6 iptables. */
+    parseAndFlushCostlyTables(iptOutput, doClean);
+    pclose(iptOutput);
+}
+
+void BandwidthController::parseAndFlushCostlyTables(FILE *fp, bool doRemove) {
+    int res;
+    char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
+    char costlyIfaceName[MAX_IPT_OUTPUT_LINE_LEN];
+    char cmd[MAX_CMD_LEN];
+    char *buffPtr;
+
+    while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
+        costlyIfaceName[0] = '\0';   /* So that debugging output always works */
+        res = sscanf(buffPtr, "-N bw_costly_%s", costlyIfaceName);
+        ALOGV("parse res=%d costly=<%s> orig line=<%s>", res,
+            costlyIfaceName, buffPtr);
+        if (res != 1) {
+            continue;
+        }
+        /* Exclusions: "shared" is not an ifacename */
+        if (!strcmp(costlyIfaceName, "shared")) {
+            continue;
+        }
+
+        snprintf(cmd, sizeof(cmd), "-F bw_costly_%s", costlyIfaceName);
+        runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
+        if (doRemove) {
+            snprintf(cmd, sizeof(cmd), "-X bw_costly_%s", costlyIfaceName);
+            runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
+        }
+    }
+}