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