netd: bandwidth: tethering global alert support

Now, when nat is enabled/disabled it will let the bandwidthcontroller
know that it might need to add/remove the matching global alert into
the tethering rules in the FORWARD chain of iptables.

Bug: 5336638
Change-Id: I1843f3f6601f371537f754a31db792e054b36a1d
diff --git a/BandwidthController.cpp b/BandwidthController.cpp
index 1761145..f0a856e 100644
--- a/BandwidthController.cpp
+++ b/BandwidthController.cpp
@@ -48,6 +48,7 @@
 /* Alphabetical */
 const char BandwidthController::ALERT_IPT_TEMPLATE[] = "%s %s %s -m quota2 ! --quota %lld --name %s";
 const int  BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4;
+const char BandwidthController::ALERT_GLOBAL_NAME[] = "globalAlert";
 const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
 const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
 const int  BandwidthController::MAX_CMD_ARGS = 32;
@@ -217,6 +218,7 @@
     quotaIfaces.clear();
     naughtyAppUids.clear();
     globalAlertBytes = 0;
+    globalAlertTetherCount = 0;
     sharedQuotaBytes = sharedAlertBytes = 0;
 
 
@@ -698,9 +700,35 @@
     return res;
 }
 
-int BandwidthController::setGlobalAlert(int64_t bytes) {
+int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const char *alertName, int64_t bytes) {
+    int res = 0;
+    const char *opFlag;
+    const char *ifaceLimiting;
     char *alertQuotaCmd;
-    const char *alertName = "globalAlert";
+
+    switch (op) {
+    case IptOpInsert:
+        opFlag = "-I";
+        break;
+    case IptOpReplace:
+        opFlag = "-R";
+        break;
+    default:
+    case IptOpDelete:
+        opFlag = "-D";
+        break;
+    }
+
+    ifaceLimiting = "! -i lo+";
+    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "FORWARD",
+        bytes, alertName, alertName);
+    res = runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
+    free(alertQuotaCmd);
+    return res;
+}
+
+int BandwidthController::setGlobalAlert(int64_t bytes) {
+    const char *alertName = ALERT_GLOBAL_NAME;
     int res = 0;
 
     if (!bytes) {
@@ -711,15 +739,39 @@
         res = updateQuota(alertName, bytes);
     } else {
         res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
+        if (globalAlertTetherCount) {
+            LOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
+            res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
+        }
     }
     globalAlertBytes = bytes;
     return res;
 }
 
-int BandwidthController::removeGlobalAlert(void) {
-    char *alertQuotaCmd;
+int BandwidthController::setGlobalAlertInForwardChain(void) {
+    const char *alertName = ALERT_GLOBAL_NAME;
+    int res = 0;
 
-    const char *alertName = "globalAlert";
+    globalAlertTetherCount++;
+    LOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
+
+    /*
+     * If there is no globalAlert active we are done.
+     * If there is an active globalAlert but this is not the 1st
+     * tether, we are also done.
+     */
+    if (!globalAlertBytes || globalAlertTetherCount != 1) {
+        return 0;
+    }
+
+    /* We only add the rule if this was the 1st tether added. */
+    res = runIptablesAlertFwdCmd(IptOpInsert, alertName, globalAlertBytes);
+    return res;
+}
+
+int BandwidthController::removeGlobalAlert(void) {
+
+    const char *alertName = ALERT_GLOBAL_NAME;
     int res = 0;
 
     if (!globalAlertBytes) {
@@ -727,10 +779,37 @@
         return -1;
     }
     res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
+    if (globalAlertTetherCount) {
+        res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
+    }
     globalAlertBytes = 0;
     return res;
 }
 
+int BandwidthController::removeGlobalAlertInForwardChain(void) {
+    int res = 0;
+    const char *alertName = ALERT_GLOBAL_NAME;
+
+    if (!globalAlertTetherCount) {
+        LOGE("No prior alert set");
+        return -1;
+    }
+
+    globalAlertTetherCount--;
+    /*
+     * If there is no globalAlert active we are done.
+     * If there is an active globalAlert but there are more
+     * tethers, we are also done.
+     */
+    if (!globalAlertBytes || globalAlertTetherCount >= 1) {
+        return 0;
+    }
+
+    /* We only detete the rule if this was the last tether removed. */
+    res = runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
+    return res;
+}
+
 int BandwidthController::setSharedAlert(int64_t bytes) {
     if (!sharedQuotaBytes) {
         LOGE("Need to have a prior shared quota set to set an alert");