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