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 <string>
26#include <vector>
27
28#include <errno.h>
29#include <fcntl.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <ctype.h>
34
35#define __STDC_FORMAT_MACROS 1
36#include <inttypes.h>
37
38#include <sys/socket.h>
39#include <sys/stat.h>
40#include <sys/types.h>
41#include <sys/wait.h>
42
43#include <linux/netlink.h>
44#include <linux/rtnetlink.h>
45#include <linux/pkt_sched.h>
46
47#include "android-base/stringprintf.h"
48#include "android-base/strings.h"
49#define LOG_TAG "BandwidthController"
50#include <cutils/log.h>
51#include <cutils/properties.h>
52#include <logwrap/logwrap.h>
53
54#include "NetdConstants.h"
55#include "BandwidthController.h"
56#include "NatController.h"  /* For LOCAL_TETHER_COUNTERS_CHAIN */
57#include "ResponseCode.h"
58
59/* Alphabetical */
60#define ALERT_IPT_TEMPLATE "%s %s -m quota2 ! --quota %" PRId64" --name %s"
61const char* BandwidthController::LOCAL_INPUT = "bw_INPUT";
62const char* BandwidthController::LOCAL_FORWARD = "bw_FORWARD";
63const char* BandwidthController::LOCAL_OUTPUT = "bw_OUTPUT";
64const char* BandwidthController::LOCAL_RAW_PREROUTING = "bw_raw_PREROUTING";
65const char* BandwidthController::LOCAL_MANGLE_POSTROUTING = "bw_mangle_POSTROUTING";
66
67auto BandwidthController::execFunction = android_fork_execvp;
68auto BandwidthController::popenFunction = popen;
69auto BandwidthController::iptablesRestoreFunction = execIptablesRestore;
70
71namespace {
72
73const char ALERT_GLOBAL_NAME[] = "globalAlert";
74const int  MAX_CMD_ARGS = 32;
75const int  MAX_CMD_LEN = 1024;
76const int  MAX_IFACENAME_LEN = 64;
77const int  MAX_IPT_OUTPUT_LINE_LEN = 256;
78
79/**
80 * Some comments about the rules:
81 *  * Ordering
82 *    - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
83 *      E.g. "-I bw_INPUT -i rmnet0 --jump costly"
84 *    - quota'd rules in the costly chain should be before bw_penalty_box lookups.
85 *    - the qtaguid counting is done at the end of the bw_INPUT/bw_OUTPUT user chains.
86 *
87 * * global quota vs per interface quota
88 *   - global quota for all costly interfaces uses a single costly chain:
89 *    . initial rules
90 *      iptables -N bw_costly_shared
91 *      iptables -I bw_INPUT -i iface0 --jump bw_costly_shared
92 *      iptables -I bw_OUTPUT -o iface0 --jump bw_costly_shared
93 *      iptables -I bw_costly_shared -m quota \! --quota 500000 \
94 *          --jump REJECT --reject-with icmp-net-prohibited
95 *      iptables -A bw_costly_shared --jump bw_penalty_box
96 *      iptables -A bw_penalty_box --jump bw_happy_box
97 *      iptables -A bw_happy_box --jump bw_data_saver
98 *
99 *    . adding a new iface to this, E.g.:
100 *      iptables -I bw_INPUT -i iface1 --jump bw_costly_shared
101 *      iptables -I bw_OUTPUT -o iface1 --jump bw_costly_shared
102 *
103 *   - quota per interface. This is achieve by having "costly" chains per quota.
104 *     E.g. adding a new costly interface iface0 with its own quota:
105 *      iptables -N bw_costly_iface0
106 *      iptables -I bw_INPUT -i iface0 --jump bw_costly_iface0
107 *      iptables -I bw_OUTPUT -o iface0 --jump bw_costly_iface0
108 *      iptables -A bw_costly_iface0 -m quota \! --quota 500000 \
109 *          --jump REJECT --reject-with icmp-port-unreachable
110 *      iptables -A bw_costly_iface0 --jump bw_penalty_box
111 *
112 * * Penalty box, happy box and data saver.
113 *   - bw_penalty box is a blacklist of apps that are rejected.
114 *   - bw_happy_box is a whitelist of apps. It always includes all system apps
115 *   - bw_data_saver implements data usage restrictions.
116 *   - Via the UI the user can add and remove apps from the whitelist and
117 *     blacklist, and turn on/off data saver.
118 *   - The blacklist takes precedence over the whitelist and the whitelist
119 *     takes precedence over data saver.
120 *
121 * * bw_penalty_box handling:
122 *  - only one bw_penalty_box for all interfaces
123 *   E.g  Adding an app:
124 *    iptables -I bw_penalty_box -m owner --uid-owner app_3 \
125 *        --jump REJECT --reject-with icmp-port-unreachable
126 *
127 * * bw_happy_box handling:
128 *  - The bw_happy_box comes after the penalty box.
129 *   E.g  Adding a happy app,
130 *    iptables -I bw_happy_box -m owner --uid-owner app_3 \
131 *        --jump RETURN
132 *
133 * * bw_data_saver handling:
134 *  - The bw_data_saver comes after the happy box.
135 *    Enable data saver:
136 *      iptables -R 1 bw_data_saver --jump REJECT --reject-with icmp-port-unreachable
137 *    Disable data saver:
138 *      iptables -R 1 bw_data_saver --jump RETURN
139 */
140
141const std::string COMMIT_AND_CLOSE = "COMMIT\n\x04";
142const std::string DATA_SAVER_ENABLE_COMMAND = "-R bw_data_saver 1";
143const std::string HAPPY_BOX_WHITELIST_COMMAND = android::base::StringPrintf(
144    "-I bw_happy_box -m owner --uid-owner %d-%d --jump RETURN", 0, MAX_SYSTEM_UID);
145
146static const std::vector<std::string> IPT_FLUSH_COMMANDS = {
147    /*
148     * Cleanup rules.
149     * Should normally include bw_costly_<iface>, but we rely on the way they are setup
150     * to allow coexistance.
151     */
152    "*filter",
153    ":bw_INPUT -",
154    ":bw_OUTPUT -",
155    ":bw_FORWARD -",
156    ":bw_happy_box -",
157    ":bw_penalty_box -",
158    ":bw_data_saver -",
159    ":bw_costly_shared -",
160    "COMMIT",
161    "*raw",
162    ":bw_raw_PREROUTING -",
163    "COMMIT",
164    "*mangle",
165    ":bw_mangle_POSTROUTING -",
166    COMMIT_AND_CLOSE
167};
168
169static const std::vector<std::string> IPT_BASIC_ACCOUNTING_COMMANDS = {
170    "*filter",
171    "-A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */
172    "-A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
173    "-A bw_costly_shared --jump bw_penalty_box",
174    "-A bw_penalty_box --jump bw_happy_box",
175    "-A bw_happy_box --jump bw_data_saver",
176    "-A bw_data_saver -j RETURN",
177    HAPPY_BOX_WHITELIST_COMMAND,
178    "COMMIT",
179
180    "*raw",
181    "-A bw_raw_PREROUTING -m owner --socket-exists", /* This is a tracking rule. */
182    "COMMIT",
183
184    "*mangle",
185    "-A bw_mangle_POSTROUTING -m owner --socket-exists", /* This is a tracking rule. */
186    COMMIT_AND_CLOSE
187};
188
189
190}  // namespace
191
192BandwidthController::BandwidthController(void) {
193}
194
195int BandwidthController::runIpxtablesCmd(const char *cmd, IptJumpOp jumpHandling,
196                                         IptFailureLog failureHandling) {
197    int res = 0;
198
199    ALOGV("runIpxtablesCmd(cmd=%s)", cmd);
200    res |= runIptablesCmd(cmd, jumpHandling, IptIpV4, failureHandling);
201    res |= runIptablesCmd(cmd, jumpHandling, IptIpV6, failureHandling);
202    return res;
203}
204
205int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
206
207    memset(buffer, '\0', buffSize);  // strncpy() is not filling leftover with '\0'
208    strncpy(buffer, src, buffSize);
209    return buffer[buffSize - 1];
210}
211
212int BandwidthController::runIptablesCmd(const char *cmd, IptJumpOp jumpHandling,
213                                        IptIpVer iptVer, IptFailureLog failureHandling) {
214    char buffer[MAX_CMD_LEN];
215    const char *argv[MAX_CMD_ARGS];
216    int argc = 0;
217    char *next = buffer;
218    char *tmp;
219    int res;
220    int status = 0;
221
222    std::string fullCmd = cmd;
223
224    switch (jumpHandling) {
225    case IptJumpReject:
226        /*
227         * Must be carefull what one rejects with, as uper layer protocols will just
228         * keep on hammering the device until the number of retries are done.
229         * For port-unreachable (default), TCP should consider as an abort (RFC1122).
230         */
231        fullCmd += " --jump REJECT";
232        break;
233    case IptJumpReturn:
234        fullCmd += " --jump RETURN";
235        break;
236    case IptJumpNoAdd:
237        break;
238    }
239
240    fullCmd.insert(0, " -w ");
241    fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
242
243    if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
244        ALOGE("iptables command too long");
245        return -1;
246    }
247
248    argc = 0;
249    while ((tmp = strsep(&next, " "))) {
250        argv[argc++] = tmp;
251        if (argc >= MAX_CMD_ARGS) {
252            ALOGE("iptables argument overflow");
253            return -1;
254        }
255    }
256
257    argv[argc] = NULL;
258    res = execFunction(argc, (char **)argv, &status, false,
259            failureHandling == IptFailShow);
260    res = res || !WIFEXITED(status) || WEXITSTATUS(status);
261    if (res && failureHandling == IptFailShow) {
262      ALOGE("runIptablesCmd(): res=%d status=%d failed %s", res, status,
263            fullCmd.c_str());
264    }
265    return res;
266}
267
268void BandwidthController::flushCleanTables(bool doClean) {
269    /* Flush and remove the bw_costly_<iface> tables */
270    flushExistingCostlyTables(doClean);
271
272    std::string commands = android::base::Join(IPT_FLUSH_COMMANDS, '\n');
273    iptablesRestoreFunction(V4V6, commands);
274}
275
276int BandwidthController::setupIptablesHooks(void) {
277    /* flush+clean is allowed to fail */
278    flushCleanTables(true);
279    return 0;
280}
281
282int BandwidthController::enableBandwidthControl(bool force) {
283    char value[PROPERTY_VALUE_MAX];
284
285    if (!force) {
286            property_get("persist.bandwidth.enable", value, "1");
287            if (!strcmp(value, "0"))
288                    return 0;
289    }
290
291    /* Let's pretend we started from scratch ... */
292    sharedQuotaIfaces.clear();
293    quotaIfaces.clear();
294    globalAlertBytes = 0;
295    globalAlertTetherCount = 0;
296    sharedQuotaBytes = sharedAlertBytes = 0;
297
298    flushCleanTables(false);
299    std::string commands = android::base::Join(IPT_BASIC_ACCOUNTING_COMMANDS, '\n');
300    return iptablesRestoreFunction(V4V6, commands);
301}
302
303int BandwidthController::disableBandwidthControl(void) {
304
305    flushCleanTables(false);
306    return 0;
307}
308
309int BandwidthController::enableDataSaver(bool enable) {
310    return runIpxtablesCmd(DATA_SAVER_ENABLE_COMMAND.c_str(),
311                           enable ? IptJumpReject : IptJumpReturn, IptFailShow);
312}
313
314int BandwidthController::runCommands(int numCommands, const char *commands[],
315                                     RunCmdErrHandling cmdErrHandling) {
316    int res = 0;
317    IptFailureLog failureLogging = IptFailShow;
318    if (cmdErrHandling == RunCmdFailureOk) {
319        failureLogging = IptFailHide;
320    }
321    ALOGV("runCommands(): %d commands", numCommands);
322    for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
323        res = runIpxtablesCmd(commands[cmdNum], IptJumpNoAdd, failureLogging);
324        if (res && cmdErrHandling != RunCmdFailureOk)
325            return res;
326    }
327    return 0;
328}
329
330std::string BandwidthController::makeIptablesSpecialAppCmd(IptOp op, int uid, const char *chain) {
331    std::string res;
332    char *buff;
333    const char *opFlag;
334
335    switch (op) {
336    case IptOpInsert:
337        opFlag = "-I";
338        break;
339    case IptOpAppend:
340        ALOGE("Append op not supported for %s uids", chain);
341        res = "";
342        return res;
343        break;
344    case IptOpReplace:
345        opFlag = "-R";
346        break;
347    default:
348    case IptOpDelete:
349        opFlag = "-D";
350        break;
351    }
352    asprintf(&buff, "%s %s -m owner --uid-owner %d", opFlag, chain, uid);
353    res = buff;
354    free(buff);
355    return res;
356}
357
358int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
359    return manipulateNaughtyApps(numUids, appUids, SpecialAppOpAdd);
360}
361
362int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
363    return manipulateNaughtyApps(numUids, appUids, SpecialAppOpRemove);
364}
365
366int BandwidthController::addNiceApps(int numUids, char *appUids[]) {
367    return manipulateNiceApps(numUids, appUids, SpecialAppOpAdd);
368}
369
370int BandwidthController::removeNiceApps(int numUids, char *appUids[]) {
371    return manipulateNiceApps(numUids, appUids, SpecialAppOpRemove);
372}
373
374int BandwidthController::manipulateNaughtyApps(int numUids, char *appStrUids[], SpecialAppOp appOp) {
375    return manipulateSpecialApps(numUids, appStrUids, "bw_penalty_box", IptJumpReject, appOp);
376}
377
378int BandwidthController::manipulateNiceApps(int numUids, char *appStrUids[], SpecialAppOp appOp) {
379    return manipulateSpecialApps(numUids, appStrUids, "bw_happy_box", IptJumpReturn, appOp);
380}
381
382
383int BandwidthController::manipulateSpecialApps(int numUids, char *appStrUids[],
384                                               const char *chain,
385                                               IptJumpOp jumpHandling, SpecialAppOp appOp) {
386
387    int uidNum;
388    const char *failLogTemplate;
389    IptOp op;
390    int appUids[numUids];
391    std::string iptCmd;
392
393    switch (appOp) {
394    case SpecialAppOpAdd:
395        op = IptOpInsert;
396        failLogTemplate = "Failed to add app uid %s(%d) to %s.";
397        break;
398    case SpecialAppOpRemove:
399        op = IptOpDelete;
400        failLogTemplate = "Failed to delete app uid %s(%d) from %s box.";
401        break;
402    default:
403        ALOGE("Unexpected app Op %d", appOp);
404        return -1;
405    }
406
407    for (uidNum = 0; uidNum < numUids; uidNum++) {
408        char *end;
409        appUids[uidNum] = strtoul(appStrUids[uidNum], &end, 0);
410        if (*end || !*appStrUids[uidNum]) {
411            ALOGE(failLogTemplate, appStrUids[uidNum], appUids[uidNum], chain);
412            goto fail_parse;
413        }
414    }
415
416    for (uidNum = 0; uidNum < numUids; uidNum++) {
417        int uid = appUids[uidNum];
418
419        iptCmd = makeIptablesSpecialAppCmd(op, uid, chain);
420        if (runIpxtablesCmd(iptCmd.c_str(), jumpHandling)) {
421            ALOGE(failLogTemplate, appStrUids[uidNum], uid, chain);
422            goto fail_with_uidNum;
423        }
424    }
425    return 0;
426
427fail_with_uidNum:
428    /* Try to remove the uid that failed in any case*/
429    iptCmd = makeIptablesSpecialAppCmd(IptOpDelete, appUids[uidNum], chain);
430    runIpxtablesCmd(iptCmd.c_str(), jumpHandling);
431fail_parse:
432    return -1;
433}
434
435std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
436    std::string res;
437    char *buff;
438    const char *opFlag;
439
440    ALOGV("makeIptablesQuotaCmd(%d, %" PRId64")", op, quota);
441
442    switch (op) {
443    case IptOpInsert:
444        opFlag = "-I";
445        break;
446    case IptOpAppend:
447        opFlag = "-A";
448        break;
449    case IptOpReplace:
450        opFlag = "-R";
451        break;
452    default:
453    case IptOpDelete:
454        opFlag = "-D";
455        break;
456    }
457
458    // The requried IP version specific --jump REJECT ... will be added later.
459    asprintf(&buff, "%s bw_costly_%s -m quota2 ! --quota %" PRId64" --name %s", opFlag, costName, quota,
460             costName);
461    res = buff;
462    free(buff);
463    return res;
464}
465
466int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
467    char cmd[MAX_CMD_LEN];
468    int res = 0, res1, res2;
469    int ruleInsertPos = 1;
470    std::string costString;
471    const char *costCString;
472
473    /* The "-N costly" is created upfront, no need to handle it here. */
474    switch (quotaType) {
475    case QuotaUnique:
476        costString = "bw_costly_";
477        costString += ifn;
478        costCString = costString.c_str();
479        /*
480         * Flush the bw_costly_<iface> is allowed to fail in case it didn't exist.
481         * Creating a new one is allowed to fail in case it existed.
482         * This helps with netd restarts.
483         */
484        snprintf(cmd, sizeof(cmd), "-F %s", costCString);
485        res1 = runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
486        snprintf(cmd, sizeof(cmd), "-N %s", costCString);
487        res2 = runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
488        res = (res1 && res2) || (!res1 && !res2);
489
490        snprintf(cmd, sizeof(cmd), "-A %s -j bw_penalty_box", costCString);
491        res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
492        break;
493    case QuotaShared:
494        costCString = "bw_costly_shared";
495        break;
496    default:
497        ALOGE("Unexpected quotatype %d", quotaType);
498        return -1;
499    }
500
501    if (globalAlertBytes) {
502        /* The alert rule comes 1st */
503        ruleInsertPos = 2;
504    }
505
506    snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
507    runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
508
509    snprintf(cmd, sizeof(cmd), "-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, ifn, costCString);
510    res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
511
512    snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
513    runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
514
515    snprintf(cmd, sizeof(cmd), "-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, ifn, costCString);
516    res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
517
518    snprintf(cmd, sizeof(cmd), "-D bw_FORWARD -o %s --jump %s", ifn, costCString);
519    runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
520    snprintf(cmd, sizeof(cmd), "-A bw_FORWARD -o %s --jump %s", ifn, costCString);
521    res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
522
523    return res;
524}
525
526int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
527    char cmd[MAX_CMD_LEN];
528    int res = 0;
529    std::string costString;
530    const char *costCString;
531
532    switch (quotaType) {
533    case QuotaUnique:
534        costString = "bw_costly_";
535        costString += ifn;
536        costCString = costString.c_str();
537        break;
538    case QuotaShared:
539        costCString = "bw_costly_shared";
540        break;
541    default:
542        ALOGE("Unexpected quotatype %d", quotaType);
543        return -1;
544    }
545
546    snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
547    res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
548    for (const auto tableName : {LOCAL_OUTPUT, LOCAL_FORWARD}) {
549        snprintf(cmd, sizeof(cmd), "-D %s -o %s --jump %s", tableName, ifn, costCString);
550        res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
551    }
552
553    /* The "-N bw_costly_shared" is created upfront, no need to handle it here. */
554    if (quotaType == QuotaUnique) {
555        snprintf(cmd, sizeof(cmd), "-F %s", costCString);
556        res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
557        snprintf(cmd, sizeof(cmd), "-X %s", costCString);
558        res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
559    }
560    return res;
561}
562
563int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
564    char ifn[MAX_IFACENAME_LEN];
565    int res = 0;
566    std::string quotaCmd;
567    std::string ifaceName;
568    ;
569    const char *costName = "shared";
570    std::list<std::string>::iterator it;
571
572    if (!maxBytes) {
573        /* Don't talk about -1, deprecate it. */
574        ALOGE("Invalid bytes value. 1..max_int64.");
575        return -1;
576    }
577    if (!isIfaceName(iface))
578        return -1;
579    if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
580        ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
581        return -1;
582    }
583    ifaceName = ifn;
584
585    if (maxBytes == -1) {
586        return removeInterfaceSharedQuota(ifn);
587    }
588
589    /* Insert ingress quota. */
590    for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
591        if (*it == ifaceName)
592            break;
593    }
594
595    if (it == sharedQuotaIfaces.end()) {
596        res |= prepCostlyIface(ifn, QuotaShared);
597        if (sharedQuotaIfaces.empty()) {
598            quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
599            res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
600            if (res) {
601                ALOGE("Failed set quota rule");
602                goto fail;
603            }
604            sharedQuotaBytes = maxBytes;
605        }
606        sharedQuotaIfaces.push_front(ifaceName);
607
608    }
609
610    if (maxBytes != sharedQuotaBytes) {
611        res |= updateQuota(costName, maxBytes);
612        if (res) {
613            ALOGE("Failed update quota for %s", costName);
614            goto fail;
615        }
616        sharedQuotaBytes = maxBytes;
617    }
618    return 0;
619
620    fail:
621    /*
622     * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
623     * rules in the kernel to see which ones need cleaning up.
624     * For now callers needs to choose if they want to "ndc bandwidth enable"
625     * which resets everything.
626     */
627    removeInterfaceSharedQuota(ifn);
628    return -1;
629}
630
631/* It will also cleanup any shared alerts */
632int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
633    char ifn[MAX_IFACENAME_LEN];
634    int res = 0;
635    std::string ifaceName;
636    std::list<std::string>::iterator it;
637    const char *costName = "shared";
638
639    if (!isIfaceName(iface))
640        return -1;
641    if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
642        ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
643        return -1;
644    }
645    ifaceName = ifn;
646
647    for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
648        if (*it == ifaceName)
649            break;
650    }
651    if (it == sharedQuotaIfaces.end()) {
652        ALOGE("No such iface %s to delete", ifn);
653        return -1;
654    }
655
656    res |= cleanupCostlyIface(ifn, QuotaShared);
657    sharedQuotaIfaces.erase(it);
658
659    if (sharedQuotaIfaces.empty()) {
660        std::string quotaCmd;
661        quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
662        res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
663        sharedQuotaBytes = 0;
664        if (sharedAlertBytes) {
665            removeSharedAlert();
666            sharedAlertBytes = 0;
667        }
668    }
669    return res;
670}
671
672int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
673    char ifn[MAX_IFACENAME_LEN];
674    int res = 0;
675    std::string ifaceName;
676    const char *costName;
677    std::list<QuotaInfo>::iterator it;
678    std::string quotaCmd;
679
680    if (!isIfaceName(iface))
681        return -1;
682
683    if (!maxBytes) {
684        /* Don't talk about -1, deprecate it. */
685        ALOGE("Invalid bytes value. 1..max_int64.");
686        return -1;
687    }
688    if (maxBytes == -1) {
689        return removeInterfaceQuota(iface);
690    }
691
692    if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
693        ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
694        return -1;
695    }
696    ifaceName = ifn;
697    costName = iface;
698
699    /* Insert ingress quota. */
700    for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
701        if (it->ifaceName == ifaceName)
702            break;
703    }
704
705    if (it == quotaIfaces.end()) {
706        /* Preparing the iface adds a penalty/happy box check */
707        res |= prepCostlyIface(ifn, QuotaUnique);
708        /*
709         * The rejecting quota limit should go after the penalty/happy box checks
710         * or else a naughty app could just eat up the quota.
711         * So we append here.
712         */
713        quotaCmd = makeIptablesQuotaCmd(IptOpAppend, costName, maxBytes);
714        res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
715        if (res) {
716            ALOGE("Failed set quota rule");
717            goto fail;
718        }
719
720        quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
721
722    } else {
723        res |= updateQuota(costName, maxBytes);
724        if (res) {
725            ALOGE("Failed update quota for %s", iface);
726            goto fail;
727        }
728        it->quota = maxBytes;
729    }
730    return 0;
731
732    fail:
733    /*
734     * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
735     * rules in the kernel to see which ones need cleaning up.
736     * For now callers needs to choose if they want to "ndc bandwidth enable"
737     * which resets everything.
738     */
739    removeInterfaceSharedQuota(ifn);
740    return -1;
741}
742
743int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
744    return getInterfaceQuota("shared", bytes);
745}
746
747int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
748    FILE *fp;
749    char *fname;
750    int scanRes;
751
752    if (!isIfaceName(costName))
753        return -1;
754
755    asprintf(&fname, "/proc/net/xt_quota/%s", costName);
756    fp = fopen(fname, "re");
757    free(fname);
758    if (!fp) {
759        ALOGE("Reading quota %s failed (%s)", costName, strerror(errno));
760        return -1;
761    }
762    scanRes = fscanf(fp, "%" SCNd64, bytes);
763    ALOGV("Read quota res=%d bytes=%" PRId64, scanRes, *bytes);
764    fclose(fp);
765    return scanRes == 1 ? 0 : -1;
766}
767
768int BandwidthController::removeInterfaceQuota(const char *iface) {
769
770    char ifn[MAX_IFACENAME_LEN];
771    int res = 0;
772    std::string ifaceName;
773    std::list<QuotaInfo>::iterator it;
774
775    if (!isIfaceName(iface))
776        return -1;
777    if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
778        ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
779        return -1;
780    }
781    ifaceName = ifn;
782
783    for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
784        if (it->ifaceName == ifaceName)
785            break;
786    }
787
788    if (it == quotaIfaces.end()) {
789        ALOGE("No such iface %s to delete", ifn);
790        return -1;
791    }
792
793    /* This also removes the quota command of CostlyIface chain. */
794    res |= cleanupCostlyIface(ifn, QuotaUnique);
795
796    quotaIfaces.erase(it);
797
798    return res;
799}
800
801int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
802    FILE *fp;
803    char *fname;
804
805    if (!isIfaceName(quotaName)) {
806        ALOGE("updateQuota: Invalid quotaName \"%s\"", quotaName);
807        return -1;
808    }
809
810    asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
811    fp = fopen(fname, "we");
812    free(fname);
813    if (!fp) {
814        ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
815        return -1;
816    }
817    fprintf(fp, "%" PRId64"\n", bytes);
818    fclose(fp);
819    return 0;
820}
821
822int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
823    int res = 0;
824    const char *opFlag;
825    char *alertQuotaCmd;
826
827    switch (op) {
828    case IptOpInsert:
829        opFlag = "-I";
830        break;
831    case IptOpAppend:
832        opFlag = "-A";
833        break;
834    case IptOpReplace:
835        opFlag = "-R";
836        break;
837    default:
838    case IptOpDelete:
839        opFlag = "-D";
840        break;
841    }
842
843    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_INPUT",
844        bytes, alertName);
845    res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
846    free(alertQuotaCmd);
847    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_OUTPUT",
848        bytes, alertName);
849    res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
850    free(alertQuotaCmd);
851    return res;
852}
853
854int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const char *alertName, int64_t bytes) {
855    int res = 0;
856    const char *opFlag;
857    char *alertQuotaCmd;
858
859    switch (op) {
860    case IptOpInsert:
861        opFlag = "-I";
862        break;
863    case IptOpAppend:
864        opFlag = "-A";
865        break;
866    case IptOpReplace:
867        opFlag = "-R";
868        break;
869    default:
870    case IptOpDelete:
871        opFlag = "-D";
872        break;
873    }
874
875    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_FORWARD",
876        bytes, alertName);
877    res = runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
878    free(alertQuotaCmd);
879    return res;
880}
881
882int BandwidthController::setGlobalAlert(int64_t bytes) {
883    const char *alertName = ALERT_GLOBAL_NAME;
884    int res = 0;
885
886    if (!bytes) {
887        ALOGE("Invalid bytes value. 1..max_int64.");
888        return -1;
889    }
890    if (globalAlertBytes) {
891        res = updateQuota(alertName, bytes);
892    } else {
893        res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
894        if (globalAlertTetherCount) {
895            ALOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
896            res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
897        }
898    }
899    globalAlertBytes = bytes;
900    return res;
901}
902
903int BandwidthController::setGlobalAlertInForwardChain(void) {
904    const char *alertName = ALERT_GLOBAL_NAME;
905    int res = 0;
906
907    globalAlertTetherCount++;
908    ALOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
909
910    /*
911     * If there is no globalAlert active we are done.
912     * If there is an active globalAlert but this is not the 1st
913     * tether, we are also done.
914     */
915    if (!globalAlertBytes || globalAlertTetherCount != 1) {
916        return 0;
917    }
918
919    /* We only add the rule if this was the 1st tether added. */
920    res = runIptablesAlertFwdCmd(IptOpInsert, alertName, globalAlertBytes);
921    return res;
922}
923
924int BandwidthController::removeGlobalAlert(void) {
925
926    const char *alertName = ALERT_GLOBAL_NAME;
927    int res = 0;
928
929    if (!globalAlertBytes) {
930        ALOGE("No prior alert set");
931        return -1;
932    }
933    res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
934    if (globalAlertTetherCount) {
935        res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
936    }
937    globalAlertBytes = 0;
938    return res;
939}
940
941int BandwidthController::removeGlobalAlertInForwardChain(void) {
942    int res = 0;
943    const char *alertName = ALERT_GLOBAL_NAME;
944
945    if (!globalAlertTetherCount) {
946        ALOGE("No prior alert set");
947        return -1;
948    }
949
950    globalAlertTetherCount--;
951    /*
952     * If there is no globalAlert active we are done.
953     * If there is an active globalAlert but there are more
954     * tethers, we are also done.
955     */
956    if (!globalAlertBytes || globalAlertTetherCount >= 1) {
957        return 0;
958    }
959
960    /* We only detete the rule if this was the last tether removed. */
961    res = runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
962    return res;
963}
964
965int BandwidthController::setSharedAlert(int64_t bytes) {
966    if (!sharedQuotaBytes) {
967        ALOGE("Need to have a prior shared quota set to set an alert");
968        return -1;
969    }
970    if (!bytes) {
971        ALOGE("Invalid bytes value. 1..max_int64.");
972        return -1;
973    }
974    return setCostlyAlert("shared", bytes, &sharedAlertBytes);
975}
976
977int BandwidthController::removeSharedAlert(void) {
978    return removeCostlyAlert("shared", &sharedAlertBytes);
979}
980
981int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
982    std::list<QuotaInfo>::iterator it;
983
984    if (!isIfaceName(iface)) {
985        ALOGE("setInterfaceAlert: Invalid iface \"%s\"", iface);
986        return -1;
987    }
988
989    if (!bytes) {
990        ALOGE("Invalid bytes value. 1..max_int64.");
991        return -1;
992    }
993    for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
994        if (it->ifaceName == iface)
995            break;
996    }
997
998    if (it == quotaIfaces.end()) {
999        ALOGE("Need to have a prior interface quota set to set an alert");
1000        return -1;
1001    }
1002
1003    return setCostlyAlert(iface, bytes, &it->alert);
1004}
1005
1006int BandwidthController::removeInterfaceAlert(const char *iface) {
1007    std::list<QuotaInfo>::iterator it;
1008
1009    if (!isIfaceName(iface)) {
1010        ALOGE("removeInterfaceAlert: Invalid iface \"%s\"", iface);
1011        return -1;
1012    }
1013
1014    for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
1015        if (it->ifaceName == iface)
1016            break;
1017    }
1018
1019    if (it == quotaIfaces.end()) {
1020        ALOGE("No prior alert set for interface %s", iface);
1021        return -1;
1022    }
1023
1024    return removeCostlyAlert(iface, &it->alert);
1025}
1026
1027int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
1028    char *alertQuotaCmd;
1029    char *chainName;
1030    int res = 0;
1031    char *alertName;
1032
1033    if (!isIfaceName(costName)) {
1034        ALOGE("setCostlyAlert: Invalid costName \"%s\"", costName);
1035        return -1;
1036    }
1037
1038    if (!bytes) {
1039        ALOGE("Invalid bytes value. 1..max_int64.");
1040        return -1;
1041    }
1042    asprintf(&alertName, "%sAlert", costName);
1043    if (*alertBytes) {
1044        res = updateQuota(alertName, *alertBytes);
1045    } else {
1046        asprintf(&chainName, "bw_costly_%s", costName);
1047        asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-A", chainName, bytes, alertName);
1048        res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
1049        free(alertQuotaCmd);
1050        free(chainName);
1051    }
1052    *alertBytes = bytes;
1053    free(alertName);
1054    return res;
1055}
1056
1057int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
1058    char *alertQuotaCmd;
1059    char *chainName;
1060    char *alertName;
1061    int res = 0;
1062
1063    if (!isIfaceName(costName)) {
1064        ALOGE("removeCostlyAlert: Invalid costName \"%s\"", costName);
1065        return -1;
1066    }
1067
1068    if (!*alertBytes) {
1069        ALOGE("No prior alert set for %s alert", costName);
1070        return -1;
1071    }
1072
1073    asprintf(&alertName, "%sAlert", costName);
1074    asprintf(&chainName, "bw_costly_%s", costName);
1075    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName);
1076    res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
1077    free(alertQuotaCmd);
1078    free(chainName);
1079
1080    *alertBytes = 0;
1081    free(alertName);
1082    return res;
1083}
1084
1085void BandwidthController::addStats(TetherStatsList& statsList, const TetherStats& stats) {
1086    for (TetherStats& existing : statsList) {
1087        if (existing.addStatsIfMatch(stats)) {
1088            return;
1089        }
1090    }
1091    // No match. Insert a new interface pair.
1092    statsList.push_back(stats);
1093}
1094
1095/*
1096 * Parse the ptks and bytes out of:
1097 *   Chain natctrl_tether_counters (4 references)
1098 *       pkts      bytes target     prot opt in     out     source               destination
1099 *         26     2373 RETURN     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0
1100 *         27     2002 RETURN     all  --  rmnet0 wlan0   0.0.0.0/0            0.0.0.0/0
1101 *       1040   107471 RETURN     all  --  bt-pan rmnet0  0.0.0.0/0            0.0.0.0/0
1102 *       1450  1708806 RETURN     all  --  rmnet0 bt-pan  0.0.0.0/0            0.0.0.0/0
1103 * or:
1104 *   Chain natctrl_tether_counters (0 references)
1105 *       pkts      bytes target     prot opt in     out     source               destination
1106 *          0        0 RETURN     all      wlan0  rmnet_data0  ::/0                 ::/0
1107 *          0        0 RETURN     all      rmnet_data0 wlan0   ::/0                 ::/0
1108 *
1109 * It results in an error if invoked and no tethering counter rules exist. The constraint
1110 * helps detect complete parsing failure.
1111 */
1112int BandwidthController::addForwardChainStats(const TetherStats& filter,
1113                                              TetherStatsList& statsList, FILE *fp,
1114                                              std::string &extraProcessingInfo) {
1115    int res;
1116    char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
1117    char iface0[MAX_IPT_OUTPUT_LINE_LEN];
1118    char iface1[MAX_IPT_OUTPUT_LINE_LEN];
1119    char rest[MAX_IPT_OUTPUT_LINE_LEN];
1120
1121    TetherStats stats;
1122    char *buffPtr;
1123    int64_t packets, bytes;
1124    int statsFound = 0;
1125
1126    bool filterPair = filter.intIface[0] && filter.extIface[0];
1127
1128    char *filterMsg = filter.getStatsLine();
1129    ALOGV("filter: %s",  filterMsg);
1130    free(filterMsg);
1131
1132    stats = filter;
1133
1134    while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
1135        /* Clean up, so a failed parse can still print info */
1136        iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
1137        if (strstr(buffPtr, "0.0.0.0")) {
1138            // IPv4 has -- indicating what to do with fragments...
1139            //       26     2373 RETURN     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0
1140            res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all -- %s %s 0.%s",
1141                    &packets, &bytes, iface0, iface1, rest);
1142        } else {
1143            // ... but IPv6 does not.
1144            //       26     2373 RETURN     all      wlan0  rmnet0  ::/0                 ::/0
1145            res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all %s %s ::/%s",
1146                    &packets, &bytes, iface0, iface1, rest);
1147        }
1148        ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%" PRId64" bytes=%" PRId64" rest=<%s> orig line=<%s>", res,
1149             iface0, iface1, packets, bytes, rest, buffPtr);
1150        extraProcessingInfo += buffPtr;
1151
1152        if (res != 5) {
1153            continue;
1154        }
1155        /*
1156         * The following assumes that the 1st rule has in:extIface out:intIface,
1157         * which is what NatController sets up.
1158         * If not filtering, the 1st match rx, and sets up the pair for the tx side.
1159         */
1160        if (filter.intIface[0] && filter.extIface[0]) {
1161            if (filter.intIface == iface0 && filter.extIface == iface1) {
1162                ALOGV("2Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1163                stats.rxPackets = packets;
1164                stats.rxBytes = bytes;
1165            } else if (filter.intIface == iface1 && filter.extIface == iface0) {
1166                ALOGV("2Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1167                stats.txPackets = packets;
1168                stats.txBytes = bytes;
1169            }
1170        } else if (filter.intIface[0] || filter.extIface[0]) {
1171            if (filter.intIface == iface0 || filter.extIface == iface1) {
1172                ALOGV("1Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1173                stats.intIface = iface0;
1174                stats.extIface = iface1;
1175                stats.rxPackets = packets;
1176                stats.rxBytes = bytes;
1177            } else if (filter.intIface == iface1 || filter.extIface == iface0) {
1178                ALOGV("1Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1179                stats.intIface = iface1;
1180                stats.extIface = iface0;
1181                stats.txPackets = packets;
1182                stats.txBytes = bytes;
1183            }
1184        } else /* if (!filter.intFace[0] && !filter.extIface[0]) */ {
1185            if (!stats.intIface[0]) {
1186                ALOGV("0Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1187                stats.intIface = iface0;
1188                stats.extIface = iface1;
1189                stats.rxPackets = packets;
1190                stats.rxBytes = bytes;
1191            } else if (stats.intIface == iface1 && stats.extIface == iface0) {
1192                ALOGV("0Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1193                stats.txPackets = packets;
1194                stats.txBytes = bytes;
1195            }
1196        }
1197        if (stats.rxBytes != -1 && stats.txBytes != -1) {
1198            ALOGV("rx_bytes=%" PRId64" tx_bytes=%" PRId64" filterPair=%d", stats.rxBytes, stats.txBytes, filterPair);
1199            addStats(statsList, stats);
1200            if (filterPair) {
1201                return 0;
1202            } else {
1203                statsFound++;
1204                stats = filter;
1205            }
1206        }
1207    }
1208
1209    /* It is always an error to find only one side of the stats. */
1210    /* It is an error to find nothing when not filtering. */
1211    if (((stats.rxBytes == -1) != (stats.txBytes == -1)) ||
1212        (!statsFound && !filterPair)) {
1213        return -1;
1214    }
1215    return 0;
1216}
1217
1218char *BandwidthController::TetherStats::getStatsLine(void) const {
1219    char *msg;
1220    asprintf(&msg, "%s %s %" PRId64" %" PRId64" %" PRId64" %" PRId64, intIface.c_str(), extIface.c_str(),
1221            rxBytes, rxPackets, txBytes, txPackets);
1222    return msg;
1223}
1224
1225std::string getTetherStatsCommand(const char *binary) {
1226    /*
1227     * Why not use some kind of lib to talk to iptables?
1228     * Because the only libs are libiptc and libip6tc in iptables, and they are
1229     * not easy to use. They require the known iptables match modules to be
1230     * preloaded/linked, and require apparently a lot of wrapper code to get
1231     * the wanted info.
1232     */
1233    return android::base::StringPrintf("%s -nvx -w -L %s", binary,
1234                                       NatController::LOCAL_TETHER_COUNTERS_CHAIN);
1235}
1236
1237int BandwidthController::getTetherStats(SocketClient *cli, TetherStats& filter,
1238                                        std::string &extraProcessingInfo) {
1239    int res = 0;
1240    std::string fullCmd;
1241    FILE *iptOutput;
1242
1243    TetherStatsList statsList;
1244
1245    for (const auto binary : {IPTABLES_PATH, IP6TABLES_PATH}) {
1246        fullCmd = getTetherStatsCommand(binary);
1247        iptOutput = popenFunction(fullCmd.c_str(), "r");
1248        if (!iptOutput) {
1249                ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
1250                extraProcessingInfo += "Failed to run iptables.";
1251            return -1;
1252        }
1253
1254        res = addForwardChainStats(filter, statsList, iptOutput, extraProcessingInfo);
1255        pclose(iptOutput);
1256        if (res != 0) {
1257            return res;
1258        }
1259    }
1260
1261    if (filter.intIface[0] && filter.extIface[0] && statsList.size() == 1) {
1262        cli->sendMsg(ResponseCode::TetheringStatsResult, statsList[0].getStatsLine(), false);
1263    } else {
1264        for (const auto& stats: statsList) {
1265            cli->sendMsg(ResponseCode::TetheringStatsListResult, stats.getStatsLine(), false);
1266        }
1267        if (res == 0) {
1268            cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false);
1269        }
1270    }
1271
1272    return res;
1273}
1274
1275void BandwidthController::flushExistingCostlyTables(bool doClean) {
1276    std::string fullCmd;
1277    FILE *iptOutput;
1278
1279    /* Only lookup ip4 table names as ip6 will have the same tables ... */
1280    fullCmd = IPTABLES_PATH;
1281    fullCmd += " -w -S";
1282    iptOutput = popenFunction(fullCmd.c_str(), "r");
1283    if (!iptOutput) {
1284            ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
1285        return;
1286    }
1287    /* ... then flush/clean both ip4 and ip6 iptables. */
1288    parseAndFlushCostlyTables(iptOutput, doClean);
1289    pclose(iptOutput);
1290}
1291
1292void BandwidthController::parseAndFlushCostlyTables(FILE *fp, bool doRemove) {
1293    int res;
1294    char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
1295    char costlyIfaceName[MAX_IPT_OUTPUT_LINE_LEN];
1296    char cmd[MAX_CMD_LEN];
1297    char *buffPtr;
1298
1299    while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
1300        costlyIfaceName[0] = '\0';   /* So that debugging output always works */
1301        res = sscanf(buffPtr, "-N bw_costly_%s", costlyIfaceName);
1302        ALOGV("parse res=%d costly=<%s> orig line=<%s>", res,
1303            costlyIfaceName, buffPtr);
1304        if (res != 1) {
1305            continue;
1306        }
1307        /* Exclusions: "shared" is not an ifacename */
1308        if (!strcmp(costlyIfaceName, "shared")) {
1309            continue;
1310        }
1311
1312        snprintf(cmd, sizeof(cmd), "-F bw_costly_%s", costlyIfaceName);
1313        runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
1314        if (doRemove) {
1315            snprintf(cmd, sizeof(cmd), "-X bw_costly_%s", costlyIfaceName);
1316            runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
1317        }
1318    }
1319}
1320