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