Home | History | Annotate | Download | only in netd
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 // #define LOG_NDEBUG 0
     18 
     19 /*
     20  * The CommandListener, FrameworkListener don't allow for
     21  * multiple calls in parallel to reach the BandwidthController.
     22  * If they ever were to allow it, then netd/ would need some tweaking.
     23  */
     24 
     25 #include <errno.h>
     26 #include <fcntl.h>
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 
     31 #include <sys/socket.h>
     32 #include <sys/stat.h>
     33 #include <sys/types.h>
     34 #include <sys/wait.h>
     35 
     36 #include <linux/netlink.h>
     37 #include <linux/rtnetlink.h>
     38 #include <linux/pkt_sched.h>
     39 
     40 #define LOG_TAG "BandwidthController"
     41 #include <cutils/log.h>
     42 #include <cutils/properties.h>
     43 #include <logwrap/logwrap.h>
     44 
     45 #include "NetdConstants.h"
     46 #include "BandwidthController.h"
     47 #include "NatController.h"  /* For LOCAL_TETHER_COUNTERS_CHAIN */
     48 #include "ResponseCode.h"
     49 
     50 /* Alphabetical */
     51 #define ALERT_IPT_TEMPLATE "%s %s -m quota2 ! --quota %lld --name %s"
     52 const char BandwidthController::ALERT_GLOBAL_NAME[] = "globalAlert";
     53 const char* BandwidthController::LOCAL_INPUT = "bw_INPUT";
     54 const char* BandwidthController::LOCAL_FORWARD = "bw_FORWARD";
     55 const char* BandwidthController::LOCAL_OUTPUT = "bw_OUTPUT";
     56 const char* BandwidthController::LOCAL_RAW_PREROUTING = "bw_raw_PREROUTING";
     57 const char* BandwidthController::LOCAL_MANGLE_POSTROUTING = "bw_mangle_POSTROUTING";
     58 const int  BandwidthController::MAX_CMD_ARGS = 32;
     59 const int  BandwidthController::MAX_CMD_LEN = 1024;
     60 const int  BandwidthController::MAX_IFACENAME_LEN = 64;
     61 const int  BandwidthController::MAX_IPT_OUTPUT_LINE_LEN = 256;
     62 
     63 /**
     64  * Some comments about the rules:
     65  *  * Ordering
     66  *    - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
     67  *      E.g. "-I bw_INPUT -i rmnet0 --jump costly"
     68  *    - quota'd rules in the costly chain should be before bw_penalty_box lookups.
     69  *    - bw_happy_box rejects everything by default.
     70  *    - the qtaguid counting is done at the end of the bw_INPUT/bw_OUTPUT user chains.
     71  *
     72  * * global quota vs per interface quota
     73  *   - global quota for all costly interfaces uses a single costly chain:
     74  *    . initial rules
     75  *      iptables -N bw_costly_shared
     76  *      iptables -I bw_INPUT -i iface0 --jump bw_costly_shared
     77  *      iptables -I bw_OUTPUT -o iface0 --jump bw_costly_shared
     78  *      iptables -I bw_costly_shared -m quota \! --quota 500000 \
     79  *          --jump REJECT --reject-with icmp-net-prohibited
     80  *      iptables -A bw_costly_shared --jump bw_penalty_box
     81  *      If the happy box is enabled,
     82  *        iptables -A bw_penalty_box --jump bw_happy_box
     83  *
     84  *    . adding a new iface to this, E.g.:
     85  *      iptables -I bw_INPUT -i iface1 --jump bw_costly_shared
     86  *      iptables -I bw_OUTPUT -o iface1 --jump bw_costly_shared
     87  *
     88  *   - quota per interface. This is achieve by having "costly" chains per quota.
     89  *     E.g. adding a new costly interface iface0 with its own quota:
     90  *      iptables -N bw_costly_iface0
     91  *      iptables -I bw_INPUT -i iface0 --jump bw_costly_iface0
     92  *      iptables -I bw_OUTPUT -o iface0 --jump bw_costly_iface0
     93  *      iptables -A bw_costly_iface0 -m quota \! --quota 500000 \
     94  *          --jump REJECT --reject-with icmp-port-unreachable
     95  *      iptables -A bw_costly_iface0 --jump bw_penalty_box
     96  *
     97  * * bw_penalty_box handling:
     98  *  - only one bw_penalty_box for all interfaces
     99  *   E.g  Adding an app, it has to preserve the appened bw_happy_box, so "-I":
    100  *    iptables -I bw_penalty_box -m owner --uid-owner app_3 \
    101  *        --jump REJECT --reject-with icmp-port-unreachable
    102  *
    103  * * bw_happy_box handling:
    104  *  - The bw_happy_box goes at the end of the penalty box.
    105  *   E.g  Adding a happy app,
    106  *    iptables -I bw_happy_box -m owner --uid-owner app_3 \
    107  *        --jump RETURN
    108  */
    109 const char *BandwidthController::IPT_FLUSH_COMMANDS[] = {
    110     /*
    111      * Cleanup rules.
    112      * Should normally include bw_costly_<iface>, but we rely on the way they are setup
    113      * to allow coexistance.
    114      */
    115     "-F bw_INPUT",
    116     "-F bw_OUTPUT",
    117     "-F bw_FORWARD",
    118     "-F bw_happy_box",
    119     "-F bw_penalty_box",
    120     "-F bw_costly_shared",
    121 
    122     "-t raw -F bw_raw_PREROUTING",
    123     "-t mangle -F bw_mangle_POSTROUTING",
    124 };
    125 
    126 /* The cleanup commands assume flushing has been done. */
    127 const char *BandwidthController::IPT_CLEANUP_COMMANDS[] = {
    128     "-X bw_happy_box",
    129     "-X bw_penalty_box",
    130     "-X bw_costly_shared",
    131 };
    132 
    133 const char *BandwidthController::IPT_SETUP_COMMANDS[] = {
    134     "-N bw_happy_box",
    135     "-N bw_penalty_box",
    136     "-N bw_costly_shared",
    137 };
    138 
    139 const char *BandwidthController::IPT_BASIC_ACCOUNTING_COMMANDS[] = {
    140     "-A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */
    141 
    142     "-A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
    143 
    144     "-A bw_costly_shared --jump bw_penalty_box",
    145 
    146     "-t raw -A bw_raw_PREROUTING -m owner --socket-exists", /* This is a tracking rule. */
    147     "-t mangle -A bw_mangle_POSTROUTING -m owner --socket-exists", /* This is a tracking rule. */
    148 };
    149 
    150 BandwidthController::BandwidthController(void) {
    151 }
    152 
    153 int BandwidthController::runIpxtablesCmd(const char *cmd, IptJumpOp jumpHandling,
    154                                          IptFailureLog failureHandling) {
    155     int res = 0;
    156 
    157     ALOGV("runIpxtablesCmd(cmd=%s)", cmd);
    158     res |= runIptablesCmd(cmd, jumpHandling, IptIpV4, failureHandling);
    159     res |= runIptablesCmd(cmd, jumpHandling, IptIpV6, failureHandling);
    160     return res;
    161 }
    162 
    163 int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
    164 
    165     memset(buffer, '\0', buffSize);  // strncpy() is not filling leftover with '\0'
    166     strncpy(buffer, src, buffSize);
    167     return buffer[buffSize - 1];
    168 }
    169 
    170 int BandwidthController::runIptablesCmd(const char *cmd, IptJumpOp jumpHandling,
    171                                         IptIpVer iptVer, IptFailureLog failureHandling) {
    172     char buffer[MAX_CMD_LEN];
    173     const char *argv[MAX_CMD_ARGS];
    174     int argc = 0;
    175     char *next = buffer;
    176     char *tmp;
    177     int res;
    178     int status = 0;
    179 
    180     std::string fullCmd = cmd;
    181 
    182     switch (jumpHandling) {
    183     case IptJumpReject:
    184         /*
    185          * Must be carefull what one rejects with, as uper layer protocols will just
    186          * keep on hammering the device until the number of retries are done.
    187          * For port-unreachable (default), TCP should consider as an abort (RFC1122).
    188          */
    189         fullCmd += " --jump REJECT";
    190         break;
    191     case IptJumpReturn:
    192         fullCmd += " --jump RETURN";
    193         break;
    194     case IptJumpNoAdd:
    195         break;
    196     }
    197 
    198     fullCmd.insert(0, " ");
    199     fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
    200 
    201     if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
    202         ALOGE("iptables command too long");
    203         return -1;
    204     }
    205 
    206     argc = 0;
    207     while ((tmp = strsep(&next, " "))) {
    208         argv[argc++] = tmp;
    209         if (argc >= MAX_CMD_ARGS) {
    210             ALOGE("iptables argument overflow");
    211             return -1;
    212         }
    213     }
    214 
    215     argv[argc] = NULL;
    216     res = android_fork_execvp(argc, (char **)argv, &status, false,
    217             failureHandling == IptFailShow);
    218     res = res || !WIFEXITED(status) || WEXITSTATUS(status);
    219     if (res && failureHandling == IptFailShow) {
    220       ALOGE("runIptablesCmd(): res=%d status=%d failed %s", res, status,
    221             fullCmd.c_str());
    222     }
    223     return res;
    224 }
    225 
    226 void BandwidthController::flushCleanTables(bool doClean) {
    227     /* Flush and remove the bw_costly_<iface> tables */
    228     flushExistingCostlyTables(doClean);
    229 
    230     /* Some of the initialCommands are allowed to fail */
    231     runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
    232             IPT_FLUSH_COMMANDS, RunCmdFailureOk);
    233 
    234     if (doClean) {
    235         runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
    236                 IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
    237     }
    238 }
    239 
    240 int BandwidthController::setupIptablesHooks(void) {
    241 
    242     /* flush+clean is allowed to fail */
    243     flushCleanTables(true);
    244     runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*),
    245             IPT_SETUP_COMMANDS, RunCmdFailureBad);
    246 
    247     return 0;
    248 }
    249 
    250 int BandwidthController::enableBandwidthControl(bool force) {
    251     int res;
    252     char value[PROPERTY_VALUE_MAX];
    253 
    254     if (!force) {
    255             property_get("persist.bandwidth.enable", value, "1");
    256             if (!strcmp(value, "0"))
    257                     return 0;
    258     }
    259 
    260     /* Let's pretend we started from scratch ... */
    261     sharedQuotaIfaces.clear();
    262     quotaIfaces.clear();
    263     naughtyAppUids.clear();
    264     niceAppUids.clear();
    265     globalAlertBytes = 0;
    266     globalAlertTetherCount = 0;
    267     sharedQuotaBytes = sharedAlertBytes = 0;
    268 
    269     flushCleanTables(false);
    270     res = runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
    271             IPT_BASIC_ACCOUNTING_COMMANDS, RunCmdFailureBad);
    272 
    273     return res;
    274 
    275 }
    276 
    277 int BandwidthController::disableBandwidthControl(void) {
    278 
    279     flushCleanTables(false);
    280     return 0;
    281 }
    282 
    283 int BandwidthController::runCommands(int numCommands, const char *commands[],
    284                                      RunCmdErrHandling cmdErrHandling) {
    285     int res = 0;
    286     IptFailureLog failureLogging = IptFailShow;
    287     if (cmdErrHandling == RunCmdFailureOk) {
    288         failureLogging = IptFailHide;
    289     }
    290     ALOGV("runCommands(): %d commands", numCommands);
    291     for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
    292         res = runIpxtablesCmd(commands[cmdNum], IptJumpNoAdd, failureLogging);
    293         if (res && cmdErrHandling != RunCmdFailureOk)
    294             return res;
    295     }
    296     return 0;
    297 }
    298 
    299 std::string BandwidthController::makeIptablesSpecialAppCmd(IptOp op, int uid, const char *chain) {
    300     std::string res;
    301     char *buff;
    302     const char *opFlag;
    303 
    304     switch (op) {
    305     case IptOpInsert:
    306         opFlag = "-I";
    307         break;
    308     case IptOpAppend:
    309         ALOGE("Append op not supported for %s uids", chain);
    310         res = "";
    311         return res;
    312         break;
    313     case IptOpReplace:
    314         opFlag = "-R";
    315         break;
    316     default:
    317     case IptOpDelete:
    318         opFlag = "-D";
    319         break;
    320     }
    321     asprintf(&buff, "%s %s -m owner --uid-owner %d", opFlag, chain, uid);
    322     res = buff;
    323     free(buff);
    324     return res;
    325 }
    326 
    327 int BandwidthController::enableHappyBox(void) {
    328     char cmd[MAX_CMD_LEN];
    329     int res = 0;
    330 
    331     /*
    332      * We tentatively delete before adding, which helps recovering
    333      * from bad states (e.g. netd died).
    334      */
    335 
    336     /* Should not exist, but ignore result if already there. */
    337     snprintf(cmd, sizeof(cmd), "-N bw_happy_box");
    338     runIpxtablesCmd(cmd, IptJumpNoAdd);
    339 
    340     /* Should be empty, but clear in case something was wrong. */
    341     niceAppUids.clear();
    342     snprintf(cmd, sizeof(cmd), "-F bw_happy_box");
    343     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
    344 
    345     snprintf(cmd, sizeof(cmd), "-D bw_penalty_box -j bw_happy_box");
    346     runIpxtablesCmd(cmd, IptJumpNoAdd);
    347     snprintf(cmd, sizeof(cmd), "-A bw_penalty_box -j bw_happy_box");
    348     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
    349 
    350     /* Reject. Defaulting to prot-unreachable */
    351     snprintf(cmd, sizeof(cmd), "-D bw_happy_box -j REJECT");
    352     runIpxtablesCmd(cmd, IptJumpNoAdd);
    353     snprintf(cmd, sizeof(cmd), "-A bw_happy_box -j REJECT");
    354     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
    355 
    356     return res;
    357 }
    358 
    359 int BandwidthController::disableHappyBox(void) {
    360     char cmd[MAX_CMD_LEN];
    361 
    362     /* Best effort */
    363     snprintf(cmd, sizeof(cmd), "-D bw_penalty_box -j bw_happy_box");
    364     runIpxtablesCmd(cmd, IptJumpNoAdd);
    365     niceAppUids.clear();
    366     snprintf(cmd, sizeof(cmd), "-F bw_happy_box");
    367     runIpxtablesCmd(cmd, IptJumpNoAdd);
    368     snprintf(cmd, sizeof(cmd), "-X bw_happy_box");
    369     runIpxtablesCmd(cmd, IptJumpNoAdd);
    370 
    371     return 0;
    372 }
    373 
    374 int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
    375     return manipulateNaughtyApps(numUids, appUids, SpecialAppOpAdd);
    376 }
    377 
    378 int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
    379     return manipulateNaughtyApps(numUids, appUids, SpecialAppOpRemove);
    380 }
    381 
    382 int BandwidthController::addNiceApps(int numUids, char *appUids[]) {
    383     return manipulateNiceApps(numUids, appUids, SpecialAppOpAdd);
    384 }
    385 
    386 int BandwidthController::removeNiceApps(int numUids, char *appUids[]) {
    387     return manipulateNiceApps(numUids, appUids, SpecialAppOpRemove);
    388 }
    389 
    390 int BandwidthController::manipulateNaughtyApps(int numUids, char *appStrUids[], SpecialAppOp appOp) {
    391     return manipulateSpecialApps(numUids, appStrUids, "bw_penalty_box", naughtyAppUids, IptJumpReject, appOp);
    392 }
    393 
    394 int BandwidthController::manipulateNiceApps(int numUids, char *appStrUids[], SpecialAppOp appOp) {
    395     return manipulateSpecialApps(numUids, appStrUids, "bw_happy_box", niceAppUids, IptJumpReturn, appOp);
    396 }
    397 
    398 
    399 int BandwidthController::manipulateSpecialApps(int numUids, char *appStrUids[],
    400                                                const char *chain,
    401                                                std::list<int /*appUid*/> &specialAppUids,
    402                                                IptJumpOp jumpHandling, SpecialAppOp appOp) {
    403 
    404     char cmd[MAX_CMD_LEN];
    405     int uidNum;
    406     const char *failLogTemplate;
    407     IptOp op;
    408     int appUids[numUids];
    409     std::string iptCmd;
    410     std::list<int /*uid*/>::iterator it;
    411 
    412     switch (appOp) {
    413     case SpecialAppOpAdd:
    414         op = IptOpInsert;
    415         failLogTemplate = "Failed to add app uid %s(%d) to %s.";
    416         break;
    417     case SpecialAppOpRemove:
    418         op = IptOpDelete;
    419         failLogTemplate = "Failed to delete app uid %s(%d) from %s box.";
    420         break;
    421     default:
    422         ALOGE("Unexpected app Op %d", appOp);
    423         return -1;
    424     }
    425 
    426     for (uidNum = 0; uidNum < numUids; uidNum++) {
    427         char *end;
    428         appUids[uidNum] = strtoul(appStrUids[uidNum], &end, 0);
    429         if (*end || !*appStrUids[uidNum]) {
    430             ALOGE(failLogTemplate, appStrUids[uidNum], appUids[uidNum], chain);
    431             goto fail_parse;
    432         }
    433     }
    434 
    435     for (uidNum = 0; uidNum < numUids; uidNum++) {
    436         int uid = appUids[uidNum];
    437         for (it = specialAppUids.begin(); it != specialAppUids.end(); it++) {
    438             if (*it == uid)
    439                 break;
    440         }
    441         bool found = (it != specialAppUids.end());
    442 
    443         if (appOp == SpecialAppOpRemove) {
    444             if (!found) {
    445                 ALOGE("No such appUid %d to remove", uid);
    446                 return -1;
    447             }
    448             specialAppUids.erase(it);
    449         } else {
    450             if (found) {
    451                 ALOGE("appUid %d exists already", uid);
    452                 return -1;
    453             }
    454             specialAppUids.push_front(uid);
    455         }
    456 
    457         iptCmd = makeIptablesSpecialAppCmd(op, uid, chain);
    458         if (runIpxtablesCmd(iptCmd.c_str(), jumpHandling)) {
    459             ALOGE(failLogTemplate, appStrUids[uidNum], uid, chain);
    460             goto fail_with_uidNum;
    461         }
    462     }
    463     return 0;
    464 
    465 fail_with_uidNum:
    466     /* Try to remove the uid that failed in any case*/
    467     iptCmd = makeIptablesSpecialAppCmd(IptOpDelete, appUids[uidNum], chain);
    468     runIpxtablesCmd(iptCmd.c_str(), jumpHandling);
    469 fail_parse:
    470     return -1;
    471 }
    472 
    473 std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
    474     std::string res;
    475     char *buff;
    476     const char *opFlag;
    477 
    478     ALOGV("makeIptablesQuotaCmd(%d, %lld)", op, quota);
    479 
    480     switch (op) {
    481     case IptOpInsert:
    482         opFlag = "-I";
    483         break;
    484     case IptOpAppend:
    485         opFlag = "-A";
    486         break;
    487     case IptOpReplace:
    488         opFlag = "-R";
    489         break;
    490     default:
    491     case IptOpDelete:
    492         opFlag = "-D";
    493         break;
    494     }
    495 
    496     // The requried IP version specific --jump REJECT ... will be added later.
    497     asprintf(&buff, "%s bw_costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota,
    498              costName);
    499     res = buff;
    500     free(buff);
    501     return res;
    502 }
    503 
    504 int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
    505     char cmd[MAX_CMD_LEN];
    506     int res = 0, res1, res2;
    507     int ruleInsertPos = 1;
    508     std::string costString;
    509     const char *costCString;
    510 
    511     /* The "-N costly" is created upfront, no need to handle it here. */
    512     switch (quotaType) {
    513     case QuotaUnique:
    514         costString = "bw_costly_";
    515         costString += ifn;
    516         costCString = costString.c_str();
    517         /*
    518          * Flush the bw_costly_<iface> is allowed to fail in case it didn't exist.
    519          * Creating a new one is allowed to fail in case it existed.
    520          * This helps with netd restarts.
    521          */
    522         snprintf(cmd, sizeof(cmd), "-F %s", costCString);
    523         res1 = runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
    524         snprintf(cmd, sizeof(cmd), "-N %s", costCString);
    525         res2 = runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
    526         res = (res1 && res2) || (!res1 && !res2);
    527 
    528         snprintf(cmd, sizeof(cmd), "-A %s -j bw_penalty_box", costCString);
    529         res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
    530         break;
    531     case QuotaShared:
    532         costCString = "bw_costly_shared";
    533         break;
    534     default:
    535         ALOGE("Unexpected quotatype %d", quotaType);
    536         return -1;
    537     }
    538 
    539     if (globalAlertBytes) {
    540         /* The alert rule comes 1st */
    541         ruleInsertPos = 2;
    542     }
    543 
    544     snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
    545     runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
    546 
    547     snprintf(cmd, sizeof(cmd), "-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, ifn, costCString);
    548     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
    549 
    550     snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
    551     runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
    552 
    553     snprintf(cmd, sizeof(cmd), "-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, ifn, costCString);
    554     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
    555     return res;
    556 }
    557 
    558 int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
    559     char cmd[MAX_CMD_LEN];
    560     int res = 0;
    561     std::string costString;
    562     const char *costCString;
    563 
    564     switch (quotaType) {
    565     case QuotaUnique:
    566         costString = "bw_costly_";
    567         costString += ifn;
    568         costCString = costString.c_str();
    569         break;
    570     case QuotaShared:
    571         costCString = "bw_costly_shared";
    572         break;
    573     default:
    574         ALOGE("Unexpected quotatype %d", quotaType);
    575         return -1;
    576     }
    577 
    578     snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
    579     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
    580     snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
    581     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
    582 
    583     /* The "-N bw_costly_shared" is created upfront, no need to handle it here. */
    584     if (quotaType == QuotaUnique) {
    585         snprintf(cmd, sizeof(cmd), "-F %s", costCString);
    586         res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
    587         snprintf(cmd, sizeof(cmd), "-X %s", costCString);
    588         res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
    589     }
    590     return res;
    591 }
    592 
    593 int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
    594     char cmd[MAX_CMD_LEN];
    595     char ifn[MAX_IFACENAME_LEN];
    596     int res = 0;
    597     std::string quotaCmd;
    598     std::string ifaceName;
    599     ;
    600     const char *costName = "shared";
    601     std::list<std::string>::iterator it;
    602 
    603     if (!maxBytes) {
    604         /* Don't talk about -1, deprecate it. */
    605         ALOGE("Invalid bytes value. 1..max_int64.");
    606         return -1;
    607     }
    608     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
    609         ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
    610         return -1;
    611     }
    612     ifaceName = ifn;
    613 
    614     if (maxBytes == -1) {
    615         return removeInterfaceSharedQuota(ifn);
    616     }
    617 
    618     /* Insert ingress quota. */
    619     for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
    620         if (*it == ifaceName)
    621             break;
    622     }
    623 
    624     if (it == sharedQuotaIfaces.end()) {
    625         res |= prepCostlyIface(ifn, QuotaShared);
    626         if (sharedQuotaIfaces.empty()) {
    627             quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
    628             res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
    629             if (res) {
    630                 ALOGE("Failed set quota rule");
    631                 goto fail;
    632             }
    633             sharedQuotaBytes = maxBytes;
    634         }
    635         sharedQuotaIfaces.push_front(ifaceName);
    636 
    637     }
    638 
    639     if (maxBytes != sharedQuotaBytes) {
    640         res |= updateQuota(costName, maxBytes);
    641         if (res) {
    642             ALOGE("Failed update quota for %s", costName);
    643             goto fail;
    644         }
    645         sharedQuotaBytes = maxBytes;
    646     }
    647     return 0;
    648 
    649     fail:
    650     /*
    651      * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
    652      * rules in the kernel to see which ones need cleaning up.
    653      * For now callers needs to choose if they want to "ndc bandwidth enable"
    654      * which resets everything.
    655      */
    656     removeInterfaceSharedQuota(ifn);
    657     return -1;
    658 }
    659 
    660 /* It will also cleanup any shared alerts */
    661 int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
    662     char ifn[MAX_IFACENAME_LEN];
    663     int res = 0;
    664     std::string ifaceName;
    665     std::list<std::string>::iterator it;
    666     const char *costName = "shared";
    667 
    668     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
    669         ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
    670         return -1;
    671     }
    672     ifaceName = ifn;
    673 
    674     for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
    675         if (*it == ifaceName)
    676             break;
    677     }
    678     if (it == sharedQuotaIfaces.end()) {
    679         ALOGE("No such iface %s to delete", ifn);
    680         return -1;
    681     }
    682 
    683     res |= cleanupCostlyIface(ifn, QuotaShared);
    684     sharedQuotaIfaces.erase(it);
    685 
    686     if (sharedQuotaIfaces.empty()) {
    687         std::string quotaCmd;
    688         quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
    689         res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
    690         sharedQuotaBytes = 0;
    691         if (sharedAlertBytes) {
    692             removeSharedAlert();
    693             sharedAlertBytes = 0;
    694         }
    695     }
    696     return res;
    697 }
    698 
    699 int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
    700     char ifn[MAX_IFACENAME_LEN];
    701     int res = 0;
    702     std::string ifaceName;
    703     const char *costName;
    704     std::list<QuotaInfo>::iterator it;
    705     std::string quotaCmd;
    706 
    707     if (!maxBytes) {
    708         /* Don't talk about -1, deprecate it. */
    709         ALOGE("Invalid bytes value. 1..max_int64.");
    710         return -1;
    711     }
    712     if (maxBytes == -1) {
    713         return removeInterfaceQuota(iface);
    714     }
    715 
    716     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
    717         ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
    718         return -1;
    719     }
    720     ifaceName = ifn;
    721     costName = iface;
    722 
    723     /* Insert ingress quota. */
    724     for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
    725         if (it->ifaceName == ifaceName)
    726             break;
    727     }
    728 
    729     if (it == quotaIfaces.end()) {
    730         /* Preparing the iface adds a penalty/happy box check */
    731         res |= prepCostlyIface(ifn, QuotaUnique);
    732         /*
    733          * The rejecting quota limit should go after the penalty/happy box checks
    734          * or else a naughty app could just eat up the quota.
    735          * So we append here.
    736          */
    737         quotaCmd = makeIptablesQuotaCmd(IptOpAppend, costName, maxBytes);
    738         res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
    739         if (res) {
    740             ALOGE("Failed set quota rule");
    741             goto fail;
    742         }
    743 
    744         quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
    745 
    746     } else {
    747         res |= updateQuota(costName, maxBytes);
    748         if (res) {
    749             ALOGE("Failed update quota for %s", iface);
    750             goto fail;
    751         }
    752         it->quota = maxBytes;
    753     }
    754     return 0;
    755 
    756     fail:
    757     /*
    758      * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
    759      * rules in the kernel to see which ones need cleaning up.
    760      * For now callers needs to choose if they want to "ndc bandwidth enable"
    761      * which resets everything.
    762      */
    763     removeInterfaceSharedQuota(ifn);
    764     return -1;
    765 }
    766 
    767 int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
    768     return getInterfaceQuota("shared", bytes);
    769 }
    770 
    771 int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
    772     FILE *fp;
    773     char *fname;
    774     int scanRes;
    775 
    776     asprintf(&fname, "/proc/net/xt_quota/%s", costName);
    777     fp = fopen(fname, "r");
    778     free(fname);
    779     if (!fp) {
    780         ALOGE("Reading quota %s failed (%s)", costName, strerror(errno));
    781         return -1;
    782     }
    783     scanRes = fscanf(fp, "%lld", bytes);
    784     ALOGV("Read quota res=%d bytes=%lld", scanRes, *bytes);
    785     fclose(fp);
    786     return scanRes == 1 ? 0 : -1;
    787 }
    788 
    789 int BandwidthController::removeInterfaceQuota(const char *iface) {
    790 
    791     char ifn[MAX_IFACENAME_LEN];
    792     int res = 0;
    793     std::string ifaceName;
    794     const char *costName;
    795     std::list<QuotaInfo>::iterator it;
    796 
    797     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
    798         ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
    799         return -1;
    800     }
    801     ifaceName = ifn;
    802     costName = iface;
    803 
    804     for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
    805         if (it->ifaceName == ifaceName)
    806             break;
    807     }
    808 
    809     if (it == quotaIfaces.end()) {
    810         ALOGE("No such iface %s to delete", ifn);
    811         return -1;
    812     }
    813 
    814     /* This also removes the quota command of CostlyIface chain. */
    815     res |= cleanupCostlyIface(ifn, QuotaUnique);
    816 
    817     quotaIfaces.erase(it);
    818 
    819     return res;
    820 }
    821 
    822 int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
    823     FILE *fp;
    824     char *fname;
    825 
    826     asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
    827     fp = fopen(fname, "w");
    828     free(fname);
    829     if (!fp) {
    830         ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
    831         return -1;
    832     }
    833     fprintf(fp, "%lld\n", bytes);
    834     fclose(fp);
    835     return 0;
    836 }
    837 
    838 int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
    839     int res = 0;
    840     const char *opFlag;
    841     char *alertQuotaCmd;
    842 
    843     switch (op) {
    844     case IptOpInsert:
    845         opFlag = "-I";
    846         break;
    847     case IptOpAppend:
    848         opFlag = "-A";
    849         break;
    850     case IptOpReplace:
    851         opFlag = "-R";
    852         break;
    853     default:
    854     case IptOpDelete:
    855         opFlag = "-D";
    856         break;
    857     }
    858 
    859     asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_INPUT",
    860         bytes, alertName);
    861     res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
    862     free(alertQuotaCmd);
    863     asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_OUTPUT",
    864         bytes, alertName);
    865     res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
    866     free(alertQuotaCmd);
    867     return res;
    868 }
    869 
    870 int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const char *alertName, int64_t bytes) {
    871     int res = 0;
    872     const char *opFlag;
    873     char *alertQuotaCmd;
    874 
    875     switch (op) {
    876     case IptOpInsert:
    877         opFlag = "-I";
    878         break;
    879     case IptOpAppend:
    880         opFlag = "-A";
    881         break;
    882     case IptOpReplace:
    883         opFlag = "-R";
    884         break;
    885     default:
    886     case IptOpDelete:
    887         opFlag = "-D";
    888         break;
    889     }
    890 
    891     asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_FORWARD",
    892         bytes, alertName);
    893     res = runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
    894     free(alertQuotaCmd);
    895     return res;
    896 }
    897 
    898 int BandwidthController::setGlobalAlert(int64_t bytes) {
    899     const char *alertName = ALERT_GLOBAL_NAME;
    900     int res = 0;
    901 
    902     if (!bytes) {
    903         ALOGE("Invalid bytes value. 1..max_int64.");
    904         return -1;
    905     }
    906     if (globalAlertBytes) {
    907         res = updateQuota(alertName, bytes);
    908     } else {
    909         res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
    910         if (globalAlertTetherCount) {
    911             ALOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
    912             res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
    913         }
    914     }
    915     globalAlertBytes = bytes;
    916     return res;
    917 }
    918 
    919 int BandwidthController::setGlobalAlertInForwardChain(void) {
    920     const char *alertName = ALERT_GLOBAL_NAME;
    921     int res = 0;
    922 
    923     globalAlertTetherCount++;
    924     ALOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
    925 
    926     /*
    927      * If there is no globalAlert active we are done.
    928      * If there is an active globalAlert but this is not the 1st
    929      * tether, we are also done.
    930      */
    931     if (!globalAlertBytes || globalAlertTetherCount != 1) {
    932         return 0;
    933     }
    934 
    935     /* We only add the rule if this was the 1st tether added. */
    936     res = runIptablesAlertFwdCmd(IptOpInsert, alertName, globalAlertBytes);
    937     return res;
    938 }
    939 
    940 int BandwidthController::removeGlobalAlert(void) {
    941 
    942     const char *alertName = ALERT_GLOBAL_NAME;
    943     int res = 0;
    944 
    945     if (!globalAlertBytes) {
    946         ALOGE("No prior alert set");
    947         return -1;
    948     }
    949     res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
    950     if (globalAlertTetherCount) {
    951         res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
    952     }
    953     globalAlertBytes = 0;
    954     return res;
    955 }
    956 
    957 int BandwidthController::removeGlobalAlertInForwardChain(void) {
    958     int res = 0;
    959     const char *alertName = ALERT_GLOBAL_NAME;
    960 
    961     if (!globalAlertTetherCount) {
    962         ALOGE("No prior alert set");
    963         return -1;
    964     }
    965 
    966     globalAlertTetherCount--;
    967     /*
    968      * If there is no globalAlert active we are done.
    969      * If there is an active globalAlert but there are more
    970      * tethers, we are also done.
    971      */
    972     if (!globalAlertBytes || globalAlertTetherCount >= 1) {
    973         return 0;
    974     }
    975 
    976     /* We only detete the rule if this was the last tether removed. */
    977     res = runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
    978     return res;
    979 }
    980 
    981 int BandwidthController::setSharedAlert(int64_t bytes) {
    982     if (!sharedQuotaBytes) {
    983         ALOGE("Need to have a prior shared quota set to set an alert");
    984         return -1;
    985     }
    986     if (!bytes) {
    987         ALOGE("Invalid bytes value. 1..max_int64.");
    988         return -1;
    989     }
    990     return setCostlyAlert("shared", bytes, &sharedAlertBytes);
    991 }
    992 
    993 int BandwidthController::removeSharedAlert(void) {
    994     return removeCostlyAlert("shared", &sharedAlertBytes);
    995 }
    996 
    997 int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
    998     std::list<QuotaInfo>::iterator it;
    999 
   1000     if (!bytes) {
   1001         ALOGE("Invalid bytes value. 1..max_int64.");
   1002         return -1;
   1003     }
   1004     for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
   1005         if (it->ifaceName == iface)
   1006             break;
   1007     }
   1008 
   1009     if (it == quotaIfaces.end()) {
   1010         ALOGE("Need to have a prior interface quota set to set an alert");
   1011         return -1;
   1012     }
   1013 
   1014     return setCostlyAlert(iface, bytes, &it->alert);
   1015 }
   1016 
   1017 int BandwidthController::removeInterfaceAlert(const char *iface) {
   1018     std::list<QuotaInfo>::iterator it;
   1019 
   1020     for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
   1021         if (it->ifaceName == iface)
   1022             break;
   1023     }
   1024 
   1025     if (it == quotaIfaces.end()) {
   1026         ALOGE("No prior alert set for interface %s", iface);
   1027         return -1;
   1028     }
   1029 
   1030     return removeCostlyAlert(iface, &it->alert);
   1031 }
   1032 
   1033 int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
   1034     char *alertQuotaCmd;
   1035     char *chainName;
   1036     int res = 0;
   1037     char *alertName;
   1038 
   1039     if (!bytes) {
   1040         ALOGE("Invalid bytes value. 1..max_int64.");
   1041         return -1;
   1042     }
   1043     asprintf(&alertName, "%sAlert", costName);
   1044     if (*alertBytes) {
   1045         res = updateQuota(alertName, *alertBytes);
   1046     } else {
   1047         asprintf(&chainName, "bw_costly_%s", costName);
   1048         asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-A", chainName, bytes, alertName);
   1049         res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
   1050         free(alertQuotaCmd);
   1051         free(chainName);
   1052     }
   1053     *alertBytes = bytes;
   1054     free(alertName);
   1055     return res;
   1056 }
   1057 
   1058 int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
   1059     char *alertQuotaCmd;
   1060     char *chainName;
   1061     char *alertName;
   1062     int res = 0;
   1063 
   1064     asprintf(&alertName, "%sAlert", costName);
   1065     if (!*alertBytes) {
   1066         ALOGE("No prior alert set for %s alert", costName);
   1067         return -1;
   1068     }
   1069 
   1070     asprintf(&chainName, "bw_costly_%s", costName);
   1071     asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName);
   1072     res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
   1073     free(alertQuotaCmd);
   1074     free(chainName);
   1075 
   1076     *alertBytes = 0;
   1077     free(alertName);
   1078     return res;
   1079 }
   1080 
   1081 /*
   1082  * Parse the ptks and bytes out of:
   1083  *   Chain natctrl_tether_counters (4 references)
   1084  *       pkts      bytes target     prot opt in     out     source               destination
   1085  *         26     2373 RETURN     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0            counter wlan0_rmnet0: 0 bytes
   1086  *         27     2002 RETURN     all  --  rmnet0 wlan0   0.0.0.0/0            0.0.0.0/0            counter rmnet0_wlan0: 0 bytes
   1087  *       1040   107471 RETURN     all  --  bt-pan rmnet0  0.0.0.0/0            0.0.0.0/0            counter bt-pan_rmnet0: 0 bytes
   1088  *       1450  1708806 RETURN     all  --  rmnet0 bt-pan  0.0.0.0/0            0.0.0.0/0            counter rmnet0_bt-pan: 0 bytes
   1089  */
   1090 int BandwidthController::parseForwardChainStats(SocketClient *cli, const TetherStats filter,
   1091                                                 FILE *fp, std::string &extraProcessingInfo) {
   1092     int res;
   1093     char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
   1094     char iface0[MAX_IPT_OUTPUT_LINE_LEN];
   1095     char iface1[MAX_IPT_OUTPUT_LINE_LEN];
   1096     char rest[MAX_IPT_OUTPUT_LINE_LEN];
   1097 
   1098     TetherStats stats;
   1099     char *buffPtr;
   1100     int64_t packets, bytes;
   1101 
   1102     bool filterPair = filter.intIface[0] && filter.extIface[0];
   1103 
   1104     char *filterMsg = filter.getStatsLine();
   1105     ALOGV("filter: %s",  filterMsg);
   1106     free(filterMsg);
   1107 
   1108     stats = filter;
   1109 
   1110     while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
   1111         /* Clean up, so a failed parse can still print info */
   1112         iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
   1113         res = sscanf(buffPtr, "%lld %lld RETURN all -- %s %s 0.%s",
   1114                 &packets, &bytes, iface0, iface1, rest);
   1115         ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%lld bytes=%lld rest=<%s> orig line=<%s>", res,
   1116              iface0, iface1, packets, bytes, rest, buffPtr);
   1117         extraProcessingInfo += buffPtr;
   1118 
   1119         if (res != 5) {
   1120             continue;
   1121         }
   1122         /*
   1123          * The following assumes that the 1st rule has in:extIface out:intIface,
   1124          * which is what NatController sets up.
   1125          * If not filtering, the 1st match rx, and sets up the pair for the tx side.
   1126          */
   1127         if (filter.intIface[0] && filter.extIface[0]) {
   1128             if (filter.intIface == iface0 && filter.extIface == iface1) {
   1129                 ALOGV("2Filter RX iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
   1130                 stats.rxPackets = packets;
   1131                 stats.rxBytes = bytes;
   1132             } else if (filter.intIface == iface1 && filter.extIface == iface0) {
   1133                 ALOGV("2Filter TX iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
   1134                 stats.txPackets = packets;
   1135                 stats.txBytes = bytes;
   1136             }
   1137         } else if (filter.intIface[0] || filter.extIface[0]) {
   1138             if (filter.intIface == iface0 || filter.extIface == iface1) {
   1139                 ALOGV("1Filter RX iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
   1140                 stats.intIface = iface0;
   1141                 stats.extIface = iface1;
   1142                 stats.rxPackets = packets;
   1143                 stats.rxBytes = bytes;
   1144             } else if (filter.intIface == iface1 || filter.extIface == iface0) {
   1145                 ALOGV("1Filter TX iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
   1146                 stats.intIface = iface1;
   1147                 stats.extIface = iface0;
   1148                 stats.txPackets = packets;
   1149                 stats.txBytes = bytes;
   1150             }
   1151         } else /* if (!filter.intFace[0] && !filter.extIface[0]) */ {
   1152             if (!stats.intIface[0]) {
   1153                 ALOGV("0Filter RX iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
   1154                 stats.intIface = iface0;
   1155                 stats.extIface = iface1;
   1156                 stats.rxPackets = packets;
   1157                 stats.rxBytes = bytes;
   1158             } else if (stats.intIface == iface1 && stats.extIface == iface0) {
   1159                 ALOGV("0Filter TX iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
   1160                 stats.txPackets = packets;
   1161                 stats.txBytes = bytes;
   1162             }
   1163         }
   1164         if (stats.rxBytes != -1 && stats.txBytes != -1) {
   1165             ALOGV("rx_bytes=%lld tx_bytes=%lld filterPair=%d", stats.rxBytes, stats.txBytes, filterPair);
   1166             /* Send out stats, and prep for the next if needed. */
   1167             char *msg = stats.getStatsLine();
   1168             if (filterPair) {
   1169                 cli->sendMsg(ResponseCode::TetheringStatsResult, msg, false);
   1170                 return 0;
   1171             } else {
   1172                 cli->sendMsg(ResponseCode::TetheringStatsListResult, msg, false);
   1173                 stats = filter;
   1174             }
   1175             free(msg);
   1176         }
   1177     }
   1178     /* Successful if the last stats entry wasn't partial. */
   1179     if ((stats.rxBytes == -1) == (stats.txBytes == -1)) {
   1180         cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false);
   1181         return 0;
   1182     }
   1183     return -1;
   1184 }
   1185 
   1186 char *BandwidthController::TetherStats::getStatsLine(void) const {
   1187     char *msg;
   1188     asprintf(&msg, "%s %s %lld %lld %lld %lld", intIface.c_str(), extIface.c_str(),
   1189             rxBytes, rxPackets, txBytes, txPackets);
   1190     return msg;
   1191 }
   1192 
   1193 int BandwidthController::getTetherStats(SocketClient *cli, TetherStats &stats, std::string &extraProcessingInfo) {
   1194     int res;
   1195     std::string fullCmd;
   1196     FILE *iptOutput;
   1197     const char *cmd;
   1198 
   1199     /*
   1200      * Why not use some kind of lib to talk to iptables?
   1201      * Because the only libs are libiptc and libip6tc in iptables, and they are
   1202      * not easy to use. They require the known iptables match modules to be
   1203      * preloaded/linked, and require apparently a lot of wrapper code to get
   1204      * the wanted info.
   1205      */
   1206     fullCmd = IPTABLES_PATH;
   1207     fullCmd += " -nvx -L ";
   1208     fullCmd += NatController::LOCAL_TETHER_COUNTERS_CHAIN;
   1209     iptOutput = popen(fullCmd.c_str(), "r");
   1210     if (!iptOutput) {
   1211             ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
   1212             extraProcessingInfo += "Failed to run iptables.";
   1213         return -1;
   1214     }
   1215     res = parseForwardChainStats(cli, stats, iptOutput, extraProcessingInfo);
   1216     pclose(iptOutput);
   1217 
   1218     /* Currently NatController doesn't do ipv6 tethering, so we are done. */
   1219     return res;
   1220 }
   1221 
   1222 void BandwidthController::flushExistingCostlyTables(bool doClean) {
   1223     int res;
   1224     std::string fullCmd;
   1225     FILE *iptOutput;
   1226     const char *cmd;
   1227 
   1228     /* Only lookup ip4 table names as ip6 will have the same tables ... */
   1229     fullCmd = IPTABLES_PATH;
   1230     fullCmd += " -S";
   1231     iptOutput = popen(fullCmd.c_str(), "r");
   1232     if (!iptOutput) {
   1233             ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
   1234         return;
   1235     }
   1236     /* ... then flush/clean both ip4 and ip6 iptables. */
   1237     parseAndFlushCostlyTables(iptOutput, doClean);
   1238     pclose(iptOutput);
   1239 }
   1240 
   1241 void BandwidthController::parseAndFlushCostlyTables(FILE *fp, bool doRemove) {
   1242     int res;
   1243     char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
   1244     char costlyIfaceName[MAX_IPT_OUTPUT_LINE_LEN];
   1245     char cmd[MAX_CMD_LEN];
   1246     char *buffPtr;
   1247 
   1248     while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
   1249         costlyIfaceName[0] = '\0';   /* So that debugging output always works */
   1250         res = sscanf(buffPtr, "-N bw_costly_%s", costlyIfaceName);
   1251         ALOGV("parse res=%d costly=<%s> orig line=<%s>", res,
   1252             costlyIfaceName, buffPtr);
   1253         if (res != 1) {
   1254             continue;
   1255         }
   1256         /* Exclusions: "shared" is not an ifacename */
   1257         if (!strcmp(costlyIfaceName, "shared")) {
   1258             continue;
   1259         }
   1260 
   1261         snprintf(cmd, sizeof(cmd), "-F bw_costly_%s", costlyIfaceName);
   1262         runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
   1263         if (doRemove) {
   1264             snprintf(cmd, sizeof(cmd), "-X bw_costly_%s", costlyIfaceName);
   1265             runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
   1266         }
   1267     }
   1268 }
   1269