Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 // #define LOG_NDEBUG 0
     18 
     19 /*
     20  * The CommandListener, FrameworkListener don't allow for
     21  * multiple calls in parallel to reach the BandwidthController.
     22  * If they ever were to allow it, then netd/ would need some tweaking.
     23  */
     24 
     25 #include <ctype.h>
     26 #include <errno.h>
     27 #include <fcntl.h>
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <string>
     32 #include <vector>
     33 
     34 #define __STDC_FORMAT_MACROS 1
     35 #include <inttypes.h>
     36 
     37 #include <sys/socket.h>
     38 #include <sys/stat.h>
     39 #include <sys/types.h>
     40 #include <sys/wait.h>
     41 
     42 #include <linux/netlink.h>
     43 #include <linux/rtnetlink.h>
     44 #include <linux/pkt_sched.h>
     45 
     46 #include "android-base/stringprintf.h"
     47 #include "android-base/strings.h"
     48 #define LOG_TAG "BandwidthController"
     49 #include <cutils/log.h>
     50 #include <cutils/properties.h>
     51 #include <logwrap/logwrap.h>
     52 
     53 #include <netdutils/Syscalls.h>
     54 #include "BandwidthController.h"
     55 #include "Controllers.h"
     56 #include "FirewallController.h" /* For makeCriticalCommands */
     57 #include "Fwmark.h"
     58 #include "NetdConstants.h"
     59 #include "bpf/BpfUtils.h"
     60 
     61 /* Alphabetical */
     62 #define ALERT_IPT_TEMPLATE "%s %s -m quota2 ! --quota %" PRId64" --name %s\n"
     63 const char BandwidthController::LOCAL_INPUT[] = "bw_INPUT";
     64 const char BandwidthController::LOCAL_FORWARD[] = "bw_FORWARD";
     65 const char BandwidthController::LOCAL_OUTPUT[] = "bw_OUTPUT";
     66 const char BandwidthController::LOCAL_RAW_PREROUTING[] = "bw_raw_PREROUTING";
     67 const char BandwidthController::LOCAL_MANGLE_POSTROUTING[] = "bw_mangle_POSTROUTING";
     68 
     69 auto BandwidthController::iptablesRestoreFunction = execIptablesRestoreWithOutput;
     70 
     71 using android::base::Join;
     72 using android::base::StringAppendF;
     73 using android::base::StringPrintf;
     74 using android::bpf::XT_BPF_EGRESS_PROG_PATH;
     75 using android::bpf::XT_BPF_INGRESS_PROG_PATH;
     76 using android::netdutils::StatusOr;
     77 using android::netdutils::UniqueFile;
     78 
     79 namespace {
     80 
     81 const char ALERT_GLOBAL_NAME[] = "globalAlert";
     82 const std::string NEW_CHAIN_COMMAND = "-N ";
     83 
     84 const char NAUGHTY_CHAIN[] = "bw_penalty_box";
     85 const char NICE_CHAIN[] = "bw_happy_box";
     86 
     87 /**
     88  * Some comments about the rules:
     89  *  * Ordering
     90  *    - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
     91  *      E.g. "-I bw_INPUT -i rmnet0 --jump costly"
     92  *    - quota'd rules in the costly chain should be before bw_penalty_box lookups.
     93  *    - the qtaguid counting is done at the end of the bw_INPUT/bw_OUTPUT user chains.
     94  *
     95  * * global quota vs per interface quota
     96  *   - global quota for all costly interfaces uses a single costly chain:
     97  *    . initial rules
     98  *      iptables -N bw_costly_shared
     99  *      iptables -I bw_INPUT -i iface0 --jump bw_costly_shared
    100  *      iptables -I bw_OUTPUT -o iface0 --jump bw_costly_shared
    101  *      iptables -I bw_costly_shared -m quota \! --quota 500000 \
    102  *          --jump REJECT --reject-with icmp-net-prohibited
    103  *      iptables -A bw_costly_shared --jump bw_penalty_box
    104  *      iptables -A bw_penalty_box --jump bw_happy_box
    105  *      iptables -A bw_happy_box --jump bw_data_saver
    106  *
    107  *    . adding a new iface to this, E.g.:
    108  *      iptables -I bw_INPUT -i iface1 --jump bw_costly_shared
    109  *      iptables -I bw_OUTPUT -o iface1 --jump bw_costly_shared
    110  *
    111  *   - quota per interface. This is achieve by having "costly" chains per quota.
    112  *     E.g. adding a new costly interface iface0 with its own quota:
    113  *      iptables -N bw_costly_iface0
    114  *      iptables -I bw_INPUT -i iface0 --jump bw_costly_iface0
    115  *      iptables -I bw_OUTPUT -o iface0 --jump bw_costly_iface0
    116  *      iptables -A bw_costly_iface0 -m quota \! --quota 500000 \
    117  *          --jump REJECT --reject-with icmp-port-unreachable
    118  *      iptables -A bw_costly_iface0 --jump bw_penalty_box
    119  *
    120  * * Penalty box, happy box and data saver.
    121  *   - bw_penalty box is a blacklist of apps that are rejected.
    122  *   - bw_happy_box is a whitelist of apps. It always includes all system apps
    123  *   - bw_data_saver implements data usage restrictions.
    124  *   - Via the UI the user can add and remove apps from the whitelist and
    125  *     blacklist, and turn on/off data saver.
    126  *   - The blacklist takes precedence over the whitelist and the whitelist
    127  *     takes precedence over data saver.
    128  *
    129  * * bw_penalty_box handling:
    130  *  - only one bw_penalty_box for all interfaces
    131  *   E.g  Adding an app:
    132  *    iptables -I bw_penalty_box -m owner --uid-owner app_3 \
    133  *        --jump REJECT --reject-with icmp-port-unreachable
    134  *
    135  * * bw_happy_box handling:
    136  *  - The bw_happy_box comes after the penalty box.
    137  *   E.g  Adding a happy app,
    138  *    iptables -I bw_happy_box -m owner --uid-owner app_3 \
    139  *        --jump RETURN
    140  *
    141  * * bw_data_saver handling:
    142  *  - The bw_data_saver comes after the happy box.
    143  *    Enable data saver:
    144  *      iptables -R 1 bw_data_saver --jump REJECT --reject-with icmp-port-unreachable
    145  *    Disable data saver:
    146  *      iptables -R 1 bw_data_saver --jump RETURN
    147  */
    148 
    149 const std::string COMMIT_AND_CLOSE = "COMMIT\n";
    150 const std::string HAPPY_BOX_WHITELIST_COMMAND = StringPrintf(
    151     "-I bw_happy_box -m owner --uid-owner %d-%d --jump RETURN", 0, MAX_SYSTEM_UID);
    152 
    153 static const std::vector<std::string> IPT_FLUSH_COMMANDS = {
    154     /*
    155      * Cleanup rules.
    156      * Should normally include bw_costly_<iface>, but we rely on the way they are setup
    157      * to allow coexistance.
    158      */
    159     "*filter",
    160     ":bw_INPUT -",
    161     ":bw_OUTPUT -",
    162     ":bw_FORWARD -",
    163     ":bw_happy_box -",
    164     ":bw_penalty_box -",
    165     ":bw_data_saver -",
    166     ":bw_costly_shared -",
    167     "COMMIT",
    168     "*raw",
    169     ":bw_raw_PREROUTING -",
    170     "COMMIT",
    171     "*mangle",
    172     ":bw_mangle_POSTROUTING -",
    173     COMMIT_AND_CLOSE
    174 };
    175 
    176 static const uint32_t uidBillingMask = Fwmark::getUidBillingMask();
    177 
    178 /**
    179  * Basic commands for creation of hooks into data accounting and data boxes.
    180  *
    181  * Included in these commands are rules to prevent the double-counting of IPsec
    182  * packets. The general overview is as follows:
    183  * > All interface counters (counted in PREROUTING, POSTROUTING) must be
    184  *     completely accurate, and count only the outer packet. As such, the inner
    185  *     packet must be ignored, which is done through the use of two rules: use
    186  *     of the policy module (for tunnel mode), and VTI interface checks (for
    187  *     tunnel or transport-in-tunnel mode). The VTI interfaces should be named
    188  *     ipsec*
    189  * > Outbound UID billing can always be done with the outer packets, due to the
    190  *     ability to always find the correct UID (based on the skb->sk). As such,
    191  *     the inner packets should be ignored based on the policy module, or the
    192  *     output interface if a VTI (ipsec+)
    193  * > Inbound UDP-encap-ESP packets can be correctly mapped to the UID that
    194  *     opened the encap socket, and as such, should be billed as early as
    195  *     possible (for transport mode; tunnel mode usage should be billed to
    196  *     sending/receiving application). Due to the inner packet being
    197  *     indistinguishable from the inner packet of ESP, a uidBillingDone mark
    198  *     has to be applied to prevent counting a second time.
    199  * > Inbound ESP has no socket, and as such must be accounted later. ESP
    200  *     protocol packets are skipped via a blanket rule.
    201  * > Note that this solution is asymmetrical. Adding the VTI or policy matcher
    202  *     ignore rule in the input chain would actually break the INPUT chain;
    203  *     Those rules are designed to ignore inner packets, and in the tunnel
    204  *     mode UDP, or any ESP case, we would not have billed the outer packet.
    205  *
    206  * See go/ipsec-data-accounting for more information.
    207  */
    208 
    209 const std::vector<std::string> getBasicAccountingCommands() {
    210     bool useBpf = BandwidthController::getBpfStatsStatus();
    211     const std::vector<std::string> ipt_basic_accounting_commands = {
    212         "*filter",
    213         // Prevents IPSec double counting (ESP and UDP-encap-ESP respectively)
    214         "-A bw_INPUT -p esp -j RETURN",
    215         StringPrintf("-A bw_INPUT -m mark --mark 0x%x/0x%x -j RETURN",
    216                      uidBillingMask, uidBillingMask),
    217         "-A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */
    218         StringPrintf("-A bw_INPUT -j MARK --or-mark 0x%x", uidBillingMask),
    219 
    220         // Prevents IPSec double counting (Tunnel mode and Transport mode,
    221         // respectively)
    222         "-A bw_OUTPUT -o " IPSEC_IFACE_PREFIX "+ -j RETURN",
    223         "-A bw_OUTPUT -m policy --pol ipsec --dir out -j RETURN",
    224         "-A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
    225 
    226         "-A bw_costly_shared --jump bw_penalty_box",
    227         "-A bw_penalty_box --jump bw_happy_box",
    228         "-A bw_happy_box --jump bw_data_saver",
    229         "-A bw_data_saver -j RETURN",
    230         HAPPY_BOX_WHITELIST_COMMAND,
    231         "COMMIT",
    232 
    233         "*raw",
    234         // Prevents IPSec double counting (Tunnel mode and Transport mode,
    235         // respectively)
    236         "-A bw_raw_PREROUTING -i " IPSEC_IFACE_PREFIX "+ -j RETURN",
    237         "-A bw_raw_PREROUTING -m policy --pol ipsec --dir in -j RETURN",
    238         "-A bw_raw_PREROUTING -m owner --socket-exists", /* This is a tracking rule. */
    239         useBpf ? StringPrintf("-A bw_raw_PREROUTING -m bpf --object-pinned %s",
    240                               XT_BPF_INGRESS_PROG_PATH):"",
    241         "COMMIT",
    242 
    243         "*mangle",
    244         // Prevents IPSec double counting (Tunnel mode and Transport mode,
    245         // respectively)
    246         "-A bw_mangle_POSTROUTING -o " IPSEC_IFACE_PREFIX "+ -j RETURN",
    247         "-A bw_mangle_POSTROUTING -m policy --pol ipsec --dir out -j RETURN",
    248         "-A bw_mangle_POSTROUTING -m owner --socket-exists", /* This is a tracking rule. */
    249         StringPrintf("-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x%x",
    250                      uidBillingMask), // Clear the mark before sending this packet
    251         useBpf ? StringPrintf("-A bw_mangle_POSTROUTING -m bpf --object-pinned %s",
    252                               XT_BPF_EGRESS_PROG_PATH):"",
    253         COMMIT_AND_CLOSE
    254     };
    255     return ipt_basic_accounting_commands;
    256 }
    257 
    258 
    259 std::vector<std::string> toStrVec(int num, char* strs[]) {
    260     std::vector<std::string> tmp;
    261     for (int i = 0; i < num; ++i) {
    262         tmp.emplace_back(strs[i]);
    263     }
    264     return tmp;
    265 }
    266 
    267 }  // namespace
    268 
    269 bool BandwidthController::getBpfStatsStatus() {
    270     return (access(XT_BPF_INGRESS_PROG_PATH, F_OK) != -1) &&
    271            (access(XT_BPF_EGRESS_PROG_PATH, F_OK) != -1);
    272 }
    273 
    274 BandwidthController::BandwidthController() {
    275 }
    276 
    277 void BandwidthController::flushCleanTables(bool doClean) {
    278     /* Flush and remove the bw_costly_<iface> tables */
    279     flushExistingCostlyTables(doClean);
    280 
    281     std::string commands = Join(IPT_FLUSH_COMMANDS, '\n');
    282     iptablesRestoreFunction(V4V6, commands, nullptr);
    283 }
    284 
    285 int BandwidthController::setupIptablesHooks() {
    286     /* flush+clean is allowed to fail */
    287     flushCleanTables(true);
    288     return 0;
    289 }
    290 
    291 int BandwidthController::enableBandwidthControl(bool force) {
    292     char value[PROPERTY_VALUE_MAX];
    293 
    294     if (!force) {
    295             property_get("persist.bandwidth.enable", value, "1");
    296             if (!strcmp(value, "0"))
    297                     return 0;
    298     }
    299 
    300     /* Let's pretend we started from scratch ... */
    301     mSharedQuotaIfaces.clear();
    302     mQuotaIfaces.clear();
    303     mGlobalAlertBytes = 0;
    304     mGlobalAlertTetherCount = 0;
    305     mSharedQuotaBytes = mSharedAlertBytes = 0;
    306 
    307     flushCleanTables(false);
    308 
    309     std::string commands = Join(getBasicAccountingCommands(), '\n');
    310     return iptablesRestoreFunction(V4V6, commands, nullptr);
    311 }
    312 
    313 int BandwidthController::disableBandwidthControl() {
    314 
    315     flushCleanTables(false);
    316     return 0;
    317 }
    318 
    319 std::string BandwidthController::makeDataSaverCommand(IptablesTarget target, bool enable) {
    320     std::string cmd;
    321     const char *chainName = "bw_data_saver";
    322     const char *op = jumpToString(enable ? IptJumpReject : IptJumpReturn);
    323     std::string criticalCommands = enable ?
    324             FirewallController::makeCriticalCommands(target, chainName) : "";
    325     StringAppendF(&cmd,
    326         "*filter\n"
    327         ":%s -\n"
    328         "%s"
    329         "-A %s%s\n"
    330         "COMMIT\n", chainName, criticalCommands.c_str(), chainName, op);
    331     return cmd;
    332 }
    333 
    334 int BandwidthController::enableDataSaver(bool enable) {
    335     int ret = iptablesRestoreFunction(V4, makeDataSaverCommand(V4, enable), nullptr);
    336     ret |= iptablesRestoreFunction(V6, makeDataSaverCommand(V6, enable), nullptr);
    337     return ret;
    338 }
    339 
    340 int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
    341     return manipulateSpecialApps(toStrVec(numUids, appUids), NAUGHTY_CHAIN,
    342                                  IptJumpReject, IptOpInsert);
    343 }
    344 
    345 int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
    346     return manipulateSpecialApps(toStrVec(numUids, appUids), NAUGHTY_CHAIN,
    347                                  IptJumpReject, IptOpDelete);
    348 }
    349 
    350 int BandwidthController::addNiceApps(int numUids, char *appUids[]) {
    351     return manipulateSpecialApps(toStrVec(numUids, appUids), NICE_CHAIN,
    352                                  IptJumpReturn, IptOpInsert);
    353 }
    354 
    355 int BandwidthController::removeNiceApps(int numUids, char *appUids[]) {
    356     return manipulateSpecialApps(toStrVec(numUids, appUids), NICE_CHAIN,
    357                                  IptJumpReturn, IptOpDelete);
    358 }
    359 
    360 int BandwidthController::manipulateSpecialApps(const std::vector<std::string>& appStrUids,
    361                                                const std::string& chain, IptJumpOp jumpHandling,
    362                                                IptOp op) {
    363     std::string cmd = "*filter\n";
    364     for (const auto& appStrUid : appStrUids) {
    365         StringAppendF(&cmd, "%s %s -m owner --uid-owner %s%s\n", opToString(op), chain.c_str(),
    366                       appStrUid.c_str(), jumpToString(jumpHandling));
    367     }
    368     StringAppendF(&cmd, "COMMIT\n");
    369     return iptablesRestoreFunction(V4V6, cmd, nullptr);
    370 }
    371 
    372 int BandwidthController::setInterfaceSharedQuota(const std::string& iface, int64_t maxBytes) {
    373     int res = 0;
    374     std::string quotaCmd;
    375     constexpr char cost[] = "shared";
    376     constexpr char chain[] = "bw_costly_shared";
    377 
    378     if (!maxBytes) {
    379         /* Don't talk about -1, deprecate it. */
    380         ALOGE("Invalid bytes value. 1..max_int64.");
    381         return -1;
    382     }
    383     if (!isIfaceName(iface))
    384         return -1;
    385 
    386     if (maxBytes == -1) {
    387         return removeInterfaceSharedQuota(iface);
    388     }
    389 
    390     auto it = mSharedQuotaIfaces.find(iface);
    391 
    392     if (it == mSharedQuotaIfaces.end()) {
    393         const int ruleInsertPos = (mGlobalAlertBytes) ? 2 : 1;
    394         std::vector<std::string> cmds = {
    395             "*filter",
    396             StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, iface.c_str(), chain),
    397             StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, iface.c_str(), chain),
    398             StringPrintf("-A bw_FORWARD -i %s --jump %s", iface.c_str(), chain),
    399             StringPrintf("-A bw_FORWARD -o %s --jump %s", iface.c_str(), chain),
    400         };
    401         if (mSharedQuotaIfaces.empty()) {
    402             cmds.push_back(StringPrintf("-I %s -m quota2 ! --quota %" PRId64
    403                                         " --name %s --jump REJECT",
    404                                         chain, maxBytes, cost));
    405         }
    406         cmds.push_back("COMMIT\n");
    407 
    408         res |= iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr);
    409         if (res) {
    410             ALOGE("Failed set quota rule");
    411             removeInterfaceSharedQuota(iface);
    412             return -1;
    413         }
    414         mSharedQuotaBytes = maxBytes;
    415         mSharedQuotaIfaces.insert(iface);
    416     }
    417 
    418     if (maxBytes != mSharedQuotaBytes) {
    419         res |= updateQuota(cost, maxBytes);
    420         if (res) {
    421             ALOGE("Failed update quota for %s", cost);
    422             removeInterfaceSharedQuota(iface);
    423             return -1;
    424         }
    425         mSharedQuotaBytes = maxBytes;
    426     }
    427     return 0;
    428 }
    429 
    430 /* It will also cleanup any shared alerts */
    431 int BandwidthController::removeInterfaceSharedQuota(const std::string& iface) {
    432     constexpr char cost[] = "shared";
    433     constexpr char chain[] = "bw_costly_shared";
    434 
    435     if (!isIfaceName(iface))
    436         return -1;
    437 
    438     auto it = mSharedQuotaIfaces.find(iface);
    439 
    440     if (it == mSharedQuotaIfaces.end()) {
    441         ALOGE("No such iface %s to delete", iface.c_str());
    442         return -1;
    443     }
    444 
    445     std::vector<std::string> cmds = {
    446         "*filter",
    447         StringPrintf("-D bw_INPUT -i %s --jump %s", iface.c_str(), chain),
    448         StringPrintf("-D bw_OUTPUT -o %s --jump %s", iface.c_str(), chain),
    449         StringPrintf("-D bw_FORWARD -i %s --jump %s", iface.c_str(), chain),
    450         StringPrintf("-D bw_FORWARD -o %s --jump %s", iface.c_str(), chain),
    451     };
    452     if (mSharedQuotaIfaces.size() == 1) {
    453         cmds.push_back(StringPrintf("-D %s -m quota2 ! --quota %" PRIu64
    454                                     " --name %s --jump REJECT",
    455                                     chain, mSharedQuotaBytes, cost));
    456     }
    457     cmds.push_back("COMMIT\n");
    458 
    459     if (iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr) != 0) {
    460         ALOGE("Failed to remove shared quota on %s", iface.c_str());
    461         return -1;
    462     }
    463 
    464     int res = 0;
    465     mSharedQuotaIfaces.erase(it);
    466     if (mSharedQuotaIfaces.empty()) {
    467         mSharedQuotaBytes = 0;
    468         if (mSharedAlertBytes) {
    469             res = removeSharedAlert();
    470             if (res == 0) {
    471                 mSharedAlertBytes = 0;
    472             }
    473         }
    474     }
    475 
    476     return res;
    477 
    478 }
    479 
    480 int BandwidthController::setInterfaceQuota(const std::string& iface, int64_t maxBytes) {
    481     const std::string& cost = iface;
    482 
    483     if (!isIfaceName(iface))
    484         return -1;
    485 
    486     if (!maxBytes) {
    487         /* Don't talk about -1, deprecate it. */
    488         ALOGE("Invalid bytes value. 1..max_int64.");
    489         return -1;
    490     }
    491     if (maxBytes == -1) {
    492         return removeInterfaceQuota(iface);
    493     }
    494 
    495     /* Insert ingress quota. */
    496     auto it = mQuotaIfaces.find(iface);
    497 
    498     if (it != mQuotaIfaces.end()) {
    499         if (updateQuota(cost, maxBytes) != 0) {
    500             ALOGE("Failed update quota for %s", iface.c_str());
    501             removeInterfaceQuota(iface);
    502             return -1;
    503         }
    504         it->second.quota = maxBytes;
    505         return 0;
    506     }
    507 
    508     const std::string chain = "bw_costly_" + iface;
    509     const int ruleInsertPos = (mGlobalAlertBytes) ? 2 : 1;
    510     std::vector<std::string> cmds = {
    511         "*filter",
    512         StringPrintf(":%s -", chain.c_str()),
    513         StringPrintf("-A %s -j bw_penalty_box", chain.c_str()),
    514         StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, iface.c_str(),
    515                      chain.c_str()),
    516         StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, iface.c_str(),
    517                      chain.c_str()),
    518         StringPrintf("-A bw_FORWARD -i %s --jump %s", iface.c_str(), chain.c_str()),
    519         StringPrintf("-A bw_FORWARD -o %s --jump %s", iface.c_str(), chain.c_str()),
    520         StringPrintf("-A %s -m quota2 ! --quota %" PRId64 " --name %s --jump REJECT",
    521                      chain.c_str(), maxBytes, cost.c_str()),
    522         "COMMIT\n",
    523     };
    524 
    525     if (iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr) != 0) {
    526         ALOGE("Failed set quota rule");
    527         removeInterfaceQuota(iface);
    528         return -1;
    529     }
    530 
    531     mQuotaIfaces[iface] = QuotaInfo{maxBytes, 0};
    532     return 0;
    533 }
    534 
    535 int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
    536     return getInterfaceQuota("shared", bytes);
    537 }
    538 
    539 int BandwidthController::getInterfaceQuota(const std::string& iface, int64_t* bytes) {
    540     const auto& sys = android::netdutils::sSyscalls.get();
    541     const std::string fname = "/proc/net/xt_quota/" + iface;
    542 
    543     if (!isIfaceName(iface)) return -1;
    544 
    545     StatusOr<UniqueFile> file = sys.fopen(fname, "re");
    546     if (!isOk(file)) {
    547         ALOGE("Reading quota %s failed (%s)", iface.c_str(), toString(file).c_str());
    548         return -1;
    549     }
    550     auto rv = sys.fscanf(file.value().get(), "%" SCNd64, bytes);
    551     if (!isOk(rv)) {
    552         ALOGE("Reading quota %s failed (%s)", iface.c_str(), toString(rv).c_str());
    553         return -1;
    554     }
    555     ALOGV("Read quota res=%d bytes=%" PRId64, rv.value(), *bytes);
    556     return rv.value() == 1 ? 0 : -1;
    557 }
    558 
    559 int BandwidthController::removeInterfaceQuota(const std::string& iface) {
    560     if (!isIfaceName(iface))
    561         return -1;
    562 
    563     auto it = mQuotaIfaces.find(iface);
    564 
    565     if (it == mQuotaIfaces.end()) {
    566         ALOGE("No such iface %s to delete", iface.c_str());
    567         return -1;
    568     }
    569 
    570     const std::string chain = "bw_costly_" + iface;
    571     std::vector<std::string> cmds = {
    572         "*filter",
    573         StringPrintf("-D bw_INPUT -i %s --jump %s", iface.c_str(), chain.c_str()),
    574         StringPrintf("-D bw_OUTPUT -o %s --jump %s", iface.c_str(), chain.c_str()),
    575         StringPrintf("-D bw_FORWARD -i %s --jump %s", iface.c_str(), chain.c_str()),
    576         StringPrintf("-D bw_FORWARD -o %s --jump %s", iface.c_str(), chain.c_str()),
    577         StringPrintf("-F %s", chain.c_str()),
    578         StringPrintf("-X %s", chain.c_str()),
    579         "COMMIT\n",
    580     };
    581 
    582     const int res = iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr);
    583 
    584     if (res == 0) {
    585         mQuotaIfaces.erase(it);
    586     }
    587 
    588     return res;
    589 }
    590 
    591 int BandwidthController::updateQuota(const std::string& quotaName, int64_t bytes) {
    592     const auto& sys = android::netdutils::sSyscalls.get();
    593     const std::string fname = "/proc/net/xt_quota/" + quotaName;
    594 
    595     if (!isIfaceName(quotaName)) {
    596         ALOGE("updateQuota: Invalid quotaName \"%s\"", quotaName.c_str());
    597         return -1;
    598     }
    599 
    600     StatusOr<UniqueFile> file = sys.fopen(fname, "we");
    601     if (!isOk(file)) {
    602         ALOGE("Updating quota %s failed (%s)", quotaName.c_str(), toString(file).c_str());
    603         return -1;
    604     }
    605     sys.fprintf(file.value().get(), "%" PRId64 "\n", bytes);
    606     return 0;
    607 }
    608 
    609 int BandwidthController::runIptablesAlertCmd(IptOp op, const std::string& alertName,
    610                                              int64_t bytes) {
    611     const char *opFlag = opToString(op);
    612     std::string alertQuotaCmd = "*filter\n";
    613 
    614     // TODO: consider using an alternate template for the delete that does not include the --quota
    615     // value. This code works because the --quota value is ignored by deletes
    616     StringAppendF(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_INPUT", bytes,
    617                   alertName.c_str());
    618     StringAppendF(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_OUTPUT", bytes,
    619                   alertName.c_str());
    620     StringAppendF(&alertQuotaCmd, "COMMIT\n");
    621 
    622     return iptablesRestoreFunction(V4V6, alertQuotaCmd, nullptr);
    623 }
    624 
    625 int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const std::string& alertName,
    626                                                 int64_t bytes) {
    627     const char *opFlag = opToString(op);
    628     std::string alertQuotaCmd = "*filter\n";
    629     StringAppendF(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_FORWARD", bytes,
    630                   alertName.c_str());
    631     StringAppendF(&alertQuotaCmd, "COMMIT\n");
    632 
    633     return iptablesRestoreFunction(V4V6, alertQuotaCmd, nullptr);
    634 }
    635 
    636 int BandwidthController::setGlobalAlert(int64_t bytes) {
    637     const char *alertName = ALERT_GLOBAL_NAME;
    638     int res = 0;
    639 
    640     if (!bytes) {
    641         ALOGE("Invalid bytes value. 1..max_int64.");
    642         return -1;
    643     }
    644     if (mGlobalAlertBytes) {
    645         res = updateQuota(alertName, bytes);
    646     } else {
    647         res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
    648         if (mGlobalAlertTetherCount) {
    649             ALOGV("setGlobalAlert for %d tether", mGlobalAlertTetherCount);
    650             res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
    651         }
    652     }
    653     mGlobalAlertBytes = bytes;
    654     return res;
    655 }
    656 
    657 int BandwidthController::setGlobalAlertInForwardChain() {
    658     const char *alertName = ALERT_GLOBAL_NAME;
    659     int res = 0;
    660 
    661     mGlobalAlertTetherCount++;
    662     ALOGV("setGlobalAlertInForwardChain(): %d tether", mGlobalAlertTetherCount);
    663 
    664     /*
    665      * If there is no globalAlert active we are done.
    666      * If there is an active globalAlert but this is not the 1st
    667      * tether, we are also done.
    668      */
    669     if (!mGlobalAlertBytes || mGlobalAlertTetherCount != 1) {
    670         return 0;
    671     }
    672 
    673     /* We only add the rule if this was the 1st tether added. */
    674     res = runIptablesAlertFwdCmd(IptOpInsert, alertName, mGlobalAlertBytes);
    675     return res;
    676 }
    677 
    678 int BandwidthController::removeGlobalAlert() {
    679 
    680     const char *alertName = ALERT_GLOBAL_NAME;
    681     int res = 0;
    682 
    683     if (!mGlobalAlertBytes) {
    684         ALOGE("No prior alert set");
    685         return -1;
    686     }
    687     res = runIptablesAlertCmd(IptOpDelete, alertName, mGlobalAlertBytes);
    688     if (mGlobalAlertTetherCount) {
    689         res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, mGlobalAlertBytes);
    690     }
    691     mGlobalAlertBytes = 0;
    692     return res;
    693 }
    694 
    695 int BandwidthController::removeGlobalAlertInForwardChain() {
    696     int res = 0;
    697     const char *alertName = ALERT_GLOBAL_NAME;
    698 
    699     if (!mGlobalAlertTetherCount) {
    700         ALOGE("No prior alert set");
    701         return -1;
    702     }
    703 
    704     mGlobalAlertTetherCount--;
    705     /*
    706      * If there is no globalAlert active we are done.
    707      * If there is an active globalAlert but there are more
    708      * tethers, we are also done.
    709      */
    710     if (!mGlobalAlertBytes || mGlobalAlertTetherCount >= 1) {
    711         return 0;
    712     }
    713 
    714     /* We only detete the rule if this was the last tether removed. */
    715     res = runIptablesAlertFwdCmd(IptOpDelete, alertName, mGlobalAlertBytes);
    716     return res;
    717 }
    718 
    719 int BandwidthController::setSharedAlert(int64_t bytes) {
    720     if (!mSharedQuotaBytes) {
    721         ALOGE("Need to have a prior shared quota set to set an alert");
    722         return -1;
    723     }
    724     if (!bytes) {
    725         ALOGE("Invalid bytes value. 1..max_int64.");
    726         return -1;
    727     }
    728     return setCostlyAlert("shared", bytes, &mSharedAlertBytes);
    729 }
    730 
    731 int BandwidthController::removeSharedAlert() {
    732     return removeCostlyAlert("shared", &mSharedAlertBytes);
    733 }
    734 
    735 int BandwidthController::setInterfaceAlert(const std::string& iface, int64_t bytes) {
    736     if (!isIfaceName(iface)) {
    737         ALOGE("setInterfaceAlert: Invalid iface \"%s\"", iface.c_str());
    738         return -1;
    739     }
    740 
    741     if (!bytes) {
    742         ALOGE("Invalid bytes value. 1..max_int64.");
    743         return -1;
    744     }
    745     auto it = mQuotaIfaces.find(iface);
    746 
    747     if (it == mQuotaIfaces.end()) {
    748         ALOGE("Need to have a prior interface quota set to set an alert");
    749         return -1;
    750     }
    751 
    752     return setCostlyAlert(iface, bytes, &it->second.alert);
    753 }
    754 
    755 int BandwidthController::removeInterfaceAlert(const std::string& iface) {
    756     if (!isIfaceName(iface)) {
    757         ALOGE("removeInterfaceAlert: Invalid iface \"%s\"", iface.c_str());
    758         return -1;
    759     }
    760 
    761     auto it = mQuotaIfaces.find(iface);
    762 
    763     if (it == mQuotaIfaces.end()) {
    764         ALOGE("No prior alert set for interface %s", iface.c_str());
    765         return -1;
    766     }
    767 
    768     return removeCostlyAlert(iface, &it->second.alert);
    769 }
    770 
    771 int BandwidthController::setCostlyAlert(const std::string& costName, int64_t bytes,
    772                                         int64_t* alertBytes) {
    773     int res = 0;
    774 
    775     if (!isIfaceName(costName)) {
    776         ALOGE("setCostlyAlert: Invalid costName \"%s\"", costName.c_str());
    777         return -1;
    778     }
    779 
    780     if (!bytes) {
    781         ALOGE("Invalid bytes value. 1..max_int64.");
    782         return -1;
    783     }
    784 
    785     std::string alertName = costName + "Alert";
    786     std::string chainName = "bw_costly_" + costName;
    787     if (*alertBytes) {
    788         res = updateQuota(alertName, *alertBytes);
    789     } else {
    790         std::vector<std::string> commands = {
    791             "*filter\n",
    792             StringPrintf(ALERT_IPT_TEMPLATE, "-A", chainName.c_str(), bytes, alertName.c_str()),
    793             "COMMIT\n"
    794         };
    795         res = iptablesRestoreFunction(V4V6, Join(commands, ""), nullptr);
    796         if (res) {
    797             ALOGE("Failed to set costly alert for %s", costName.c_str());
    798         }
    799     }
    800     if (res == 0) {
    801         *alertBytes = bytes;
    802     }
    803     return res;
    804 }
    805 
    806 int BandwidthController::removeCostlyAlert(const std::string& costName, int64_t* alertBytes) {
    807     if (!isIfaceName(costName)) {
    808         ALOGE("removeCostlyAlert: Invalid costName \"%s\"", costName.c_str());
    809         return -1;
    810     }
    811 
    812     if (!*alertBytes) {
    813         ALOGE("No prior alert set for %s alert", costName.c_str());
    814         return -1;
    815     }
    816 
    817     std::string alertName = costName + "Alert";
    818     std::string chainName = "bw_costly_" + costName;
    819     std::vector<std::string> commands = {
    820         "*filter\n",
    821         StringPrintf(ALERT_IPT_TEMPLATE, "-D", chainName.c_str(), *alertBytes, alertName.c_str()),
    822         "COMMIT\n"
    823     };
    824     if (iptablesRestoreFunction(V4V6, Join(commands, ""), nullptr) != 0) {
    825         ALOGE("Failed to remove costly alert %s", costName.c_str());
    826         return -1;
    827     }
    828 
    829     *alertBytes = 0;
    830     return 0;
    831 }
    832 
    833 void BandwidthController::flushExistingCostlyTables(bool doClean) {
    834     std::string fullCmd = "*filter\n-S\nCOMMIT\n";
    835     std::string ruleList;
    836 
    837     /* Only lookup ip4 table names as ip6 will have the same tables ... */
    838     if (int ret = iptablesRestoreFunction(V4, fullCmd, &ruleList)) {
    839         ALOGE("Failed to list existing costly tables ret=%d", ret);
    840         return;
    841     }
    842     /* ... then flush/clean both ip4 and ip6 iptables. */
    843     parseAndFlushCostlyTables(ruleList, doClean);
    844 }
    845 
    846 void BandwidthController::parseAndFlushCostlyTables(const std::string& ruleList, bool doRemove) {
    847     std::stringstream stream(ruleList);
    848     std::string rule;
    849     std::vector<std::string> clearCommands = { "*filter" };
    850     std::string chainName;
    851 
    852     // Find and flush all rules starting with "-N bw_costly_<iface>" except "-N bw_costly_shared".
    853     while (std::getline(stream, rule, '\n')) {
    854         if (rule.find(NEW_CHAIN_COMMAND) != 0) continue;
    855         chainName = rule.substr(NEW_CHAIN_COMMAND.size());
    856         ALOGV("parse chainName=<%s> orig line=<%s>", chainName.c_str(), rule.c_str());
    857 
    858         if (chainName.find("bw_costly_") != 0 || chainName == std::string("bw_costly_shared")) {
    859             continue;
    860         }
    861 
    862         clearCommands.push_back(StringPrintf(":%s -", chainName.c_str()));
    863         if (doRemove) {
    864             clearCommands.push_back(StringPrintf("-X %s", chainName.c_str()));
    865         }
    866     }
    867 
    868     if (clearCommands.size() == 1) {
    869         // No rules found.
    870         return;
    871     }
    872 
    873     clearCommands.push_back("COMMIT\n");
    874     iptablesRestoreFunction(V4V6, Join(clearCommands, '\n'), nullptr);
    875 }
    876 
    877 inline const char *BandwidthController::opToString(IptOp op) {
    878     switch (op) {
    879     case IptOpInsert:
    880         return "-I";
    881     case IptOpDelete:
    882         return "-D";
    883     }
    884 }
    885 
    886 inline const char *BandwidthController::jumpToString(IptJumpOp jumpHandling) {
    887     /*
    888      * Must be careful what one rejects with, as upper layer protocols will just
    889      * keep on hammering the device until the number of retries are done.
    890      * For port-unreachable (default), TCP should consider as an abort (RFC1122).
    891      */
    892     switch (jumpHandling) {
    893     case IptJumpNoAdd:
    894         return "";
    895     case IptJumpReject:
    896         return " --jump REJECT";
    897     case IptJumpReturn:
    898         return " --jump RETURN";
    899     }
    900 }
    901