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