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