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