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