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