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