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