BandwidthController.cpp revision 11b4e9b26fe7b878992162afb39f5a8acfd143ed
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#include <errno.h>
18#include <fcntl.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include <sys/socket.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <sys/wait.h>
26
27#include <linux/netlink.h>
28#include <linux/rtnetlink.h>
29#include <linux/pkt_sched.h>
30
31#define LOG_TAG "BandwidthController"
32#include <cutils/log.h>
33#include <cutils/properties.h>
34
35extern "C" int logwrap(int argc, const char **argv, int background);
36
37#include "BandwidthController.h"
38
39const int BandwidthController::MAX_CMD_LEN = 1024;
40const int BandwidthController::MAX_IFACENAME_LEN = 64;
41const int BandwidthController::MAX_CMD_ARGS = 32;
42const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
43const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
44const char BandwidthController::ALERT_IPT_TEMPLATE[] = "%s %s -m quota2 ! --quota %lld --name %s";
45const int BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4;
46bool BandwidthController::useLogwrapCall = false;
47
48/**
49 * Some comments about the rules:
50 *  * Ordering
51 *    - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
52 *      E.g. "-I INPUT -i rmnet0 --goto costly"
53 *    - quota'd rules in the costly chain should be before penalty_box lookups.
54 *
55 * * global quota vs per interface quota
56 *   - global quota for all costly interfaces uses a single costly chain:
57 *    . initial rules
58 *      iptables -N costly_shared
59 *      iptables -I INPUT -i iface0 --goto costly_shared
60 *      iptables -I OUTPUT -o iface0 --goto costly_shared
61 *      iptables -I costly_shared -m quota \! --quota 500000 \
62 *          --jump REJECT --reject-with icmp-net-prohibited
63 *      iptables -A costly_shared --jump penalty_box
64 *      iptables -A costly_shared -m owner --socket-exists
65 *
66 *    . adding a new iface to this, E.g.:
67 *      iptables -I INPUT -i iface1 --goto costly_shared
68 *      iptables -I OUTPUT -o iface1 --goto costly_shared
69 *
70 *   - quota per interface. This is achieve by having "costly" chains per quota.
71 *     E.g. adding a new costly interface iface0 with its own quota:
72 *      iptables -N costly_iface0
73 *      iptables -I INPUT -i iface0 --goto costly_iface0
74 *      iptables -I OUTPUT -o iface0 --goto costly_iface0
75 *      iptables -A costly_iface0 -m quota \! --quota 500000 \
76 *          --jump REJECT --reject-with icmp-net-prohibited
77 *      iptables -A costly_iface0 --jump penalty_box
78 *      iptables -A costly_iface0 -m owner --socket-exists
79 *
80 * * penalty_box handling:
81 *  - only one penalty_box for all interfaces
82 *   E.g  Adding an app:
83 *    iptables -A penalty_box -m owner --uid-owner app_3 \
84 *        --jump REJECT --reject-with icmp-net-prohibited
85 */
86const char *BandwidthController::cleanupCommands[] = {
87    /* Cleanup rules. */
88    "-F",
89    "-t raw -F",
90    /* TODO: If at some point we need more user chains than here, then we will need
91     * a different cleanup approach.
92     */
93    "-X",  /* Should normally only be costly_shared, penalty_box, and costly_<iface>  */
94};
95
96const char *BandwidthController::setupCommands[] = {
97    /* Created needed chains. */
98    "-N costly_shared",
99    "-N penalty_box",
100};
101
102const char *BandwidthController::basicAccountingCommands[] = {
103    "-F INPUT",
104    "-A INPUT -i lo --jump ACCEPT",
105    "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
106
107    "-F OUTPUT",
108    "-A OUTPUT -o lo --jump ACCEPT",
109    "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
110
111    "-F costly_shared",
112    "-A costly_shared --jump penalty_box",
113    "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */
114    /* TODO(jpa): Figure out why iptables doesn't correctly return from this
115     * chain. For now, hack the chain exit with an ACCEPT.
116     */
117    "-A costly_shared --jump ACCEPT",
118};
119
120BandwidthController::BandwidthController(void) {
121    char value[PROPERTY_VALUE_MAX];
122
123    property_get("persist.bandwidth.enable", value, "0");
124    if (!strcmp(value, "1")) {
125        enableBandwidthControl();
126    }
127
128    property_get("persist.bandwidth.uselogwrap", value, "0");
129    useLogwrapCall = !strcmp(value, "1");
130}
131
132int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
133    int res = 0;
134
135    LOGD("runIpxtablesCmd(cmd=%s)", cmd);
136    res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
137    res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
138    return res;
139}
140
141int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
142
143    memset(buffer, '\0', buffSize);  // strncpy() is not filling leftover with '\0'
144    strncpy(buffer, src, buffSize);
145    return buffer[buffSize - 1];
146}
147
148int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling,
149                                        IptIpVer iptVer) {
150    char buffer[MAX_CMD_LEN];
151    const char *argv[MAX_CMD_ARGS];
152    int argc = 0;
153    char *next = buffer;
154    char *tmp;
155    int res;
156
157    std::string fullCmd = cmd;
158
159    if (rejectHandling == IptRejectAdd) {
160        fullCmd += " --jump REJECT --reject-with";
161        switch (iptVer) {
162        case IptIpV4:
163            fullCmd += " icmp-net-prohibited";
164            break;
165        case IptIpV6:
166            fullCmd += " icmp6-adm-prohibited";
167            break;
168        }
169    }
170
171    fullCmd.insert(0, " ");
172    fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
173
174    if (!useLogwrapCall) {
175        res = system(fullCmd.c_str());
176    } else {
177        if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
178            LOGE("iptables command too long");
179            return -1;
180        }
181
182        argc = 0;
183        while ((tmp = strsep(&next, " "))) {
184            argv[argc++] = tmp;
185            if (argc >= MAX_CMD_ARGS) {
186                LOGE("iptables argument overflow");
187                return -1;
188            }
189        }
190
191        argv[argc] = NULL;
192        res = logwrap(argc, argv, 0);
193    }
194    if (res) {
195        LOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res);
196    }
197    return res;
198}
199
200int BandwidthController::enableBandwidthControl(void) {
201    int res;
202    /* Some of the initialCommands are allowed to fail */
203    runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
204    runCommands(sizeof(setupCommands) / sizeof(char*), setupCommands, RunCmdFailureOk);
205    res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands,
206                      RunCmdFailureBad);
207
208    sharedQuotaBytes = sharedAlertBytes = 0;
209    sharedQuotaIfaces.clear();
210    quotaIfaces.clear();
211    naughtyAppUids.clear();
212
213    return res;
214
215}
216
217int BandwidthController::disableBandwidthControl(void) {
218    /* The cleanupCommands are allowed to fail. */
219    runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
220    return 0;
221}
222
223int BandwidthController::runCommands(int numCommands, const char *commands[],
224                                     RunCmdErrHandling cmdErrHandling) {
225    int res = 0;
226    LOGD("runCommands(): %d commands", numCommands);
227    for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
228        res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
229        if (res && cmdErrHandling != RunCmdFailureBad)
230            return res;
231    }
232    return cmdErrHandling == RunCmdFailureBad ? res : 0;
233}
234
235std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
236    std::string res;
237    char *buff;
238    const char *opFlag;
239
240    switch (op) {
241    case IptOpInsert:
242        opFlag = "-I";
243        break;
244    case IptOpReplace:
245        opFlag = "-R";
246        break;
247    default:
248    case IptOpDelete:
249        opFlag = "-D";
250        break;
251    }
252    asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid);
253    res = buff;
254    free(buff);
255    return res;
256}
257
258int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
259    return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
260}
261
262int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
263    return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
264}
265
266int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
267    char cmd[MAX_CMD_LEN];
268    int uidNum;
269    const char *failLogTemplate;
270    IptOp op;
271    int appUids[numUids];
272    std::string naughtyCmd;
273
274    switch (appOp) {
275    case NaughtyAppOpAdd:
276        op = IptOpInsert;
277        failLogTemplate = "Failed to add app uid %d to penalty box.";
278        break;
279    case NaughtyAppOpRemove:
280        op = IptOpDelete;
281        failLogTemplate = "Failed to delete app uid %d from penalty box.";
282        break;
283    }
284
285    for (uidNum = 0; uidNum < numUids; uidNum++) {
286        appUids[uidNum] = atol(appStrUids[uidNum]);
287        if (appUids[uidNum] == 0) {
288            LOGE(failLogTemplate, appUids[uidNum]);
289            goto fail_parse;
290        }
291    }
292
293    for (uidNum = 0; uidNum < numUids; uidNum++) {
294        naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
295        if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
296            LOGE(failLogTemplate, appUids[uidNum]);
297            goto fail_with_uidNum;
298        }
299    }
300    return 0;
301
302fail_with_uidNum:
303    /* Try to remove the uid that failed in any case*/
304    naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
305    runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
306fail_parse:
307    return -1;
308}
309
310std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
311    std::string res;
312    char *buff;
313    const char *opFlag;
314
315    LOGD("makeIptablesQuotaCmd(%d, %lld)", op, quota);
316
317    switch (op) {
318    case IptOpInsert:
319        opFlag = "-I";
320        break;
321    case IptOpReplace:
322        opFlag = "-R";
323        break;
324    default:
325    case IptOpDelete:
326        opFlag = "-D";
327        break;
328    }
329
330    // The requried IP version specific --jump REJECT ... will be added later.
331    asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota,
332             costName);
333    res = buff;
334    free(buff);
335    return res;
336}
337
338int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
339    char cmd[MAX_CMD_LEN];
340    int res = 0;
341    int ruleInsertPos = 1;
342    std::string costString;
343    const char *costCString;
344
345    /* The "-N costly" is created upfront, no need to handle it here. */
346    switch (quotaType) {
347    case QuotaUnique:
348        costString = "costly_";
349        costString += ifn;
350        costCString = costString.c_str();
351        snprintf(cmd, sizeof(cmd), "-N %s", costCString);
352        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
353        snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
354        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
355        snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
356        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
357        /* TODO(jpa): Figure out why iptables doesn't correctly return from this
358         * chain. For now, hack the chain exit with an ACCEPT.
359         */
360        snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
361        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
362        break;
363    case QuotaShared:
364        costCString = "costly_shared";
365        break;
366    }
367
368    if (globalAlertBytes) {
369        /* The alert rule comes 1st */
370        ruleInsertPos = 2;
371    }
372    snprintf(cmd, sizeof(cmd), "-I INPUT %d -i %s --goto %s", ruleInsertPos, ifn, costCString);
373    res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
374    snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString);
375    res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
376    return res;
377}
378
379int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
380    char cmd[MAX_CMD_LEN];
381    int res = 0;
382    std::string costString;
383    const char *costCString;
384
385    switch (quotaType) {
386    case QuotaUnique:
387        costString = "costly_";
388        costString += ifn;
389        costCString = costString.c_str();
390        break;
391    case QuotaShared:
392        costCString = "costly_shared";
393        break;
394    }
395
396    snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
397    res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
398    snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
399    res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
400
401    /* The "-N costly_shared" is created upfront, no need to handle it here. */
402    if (quotaType == QuotaUnique) {
403        snprintf(cmd, sizeof(cmd), "-F %s", costCString);
404        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
405        snprintf(cmd, sizeof(cmd), "-X %s", costCString);
406        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
407    }
408    return res;
409}
410
411int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
412    char cmd[MAX_CMD_LEN];
413    char ifn[MAX_IFACENAME_LEN];
414    int res = 0;
415    std::string quotaCmd;
416    std::string ifaceName;
417    ;
418    const char *costName = "shared";
419    std::list<std::string>::iterator it;
420
421    if (!maxBytes) {
422        /* Don't talk about -1, deprecate it. */
423        LOGE("Invalid bytes value. 1..max_int64.");
424        return -1;
425    }
426    if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
427        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
428        return -1;
429    }
430    ifaceName = ifn;
431
432    if (maxBytes == -1) {
433        return removeInterfaceSharedQuota(ifn);
434    }
435
436    /* Insert ingress quota. */
437    for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
438        if (*it == ifaceName)
439            break;
440    }
441
442    if (it == sharedQuotaIfaces.end()) {
443        res |= prepCostlyIface(ifn, QuotaShared);
444        if (sharedQuotaIfaces.empty()) {
445            quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
446            res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
447            if (res) {
448                LOGE("Failed set quota rule");
449                goto fail;
450            }
451            sharedQuotaBytes = maxBytes;
452        }
453        sharedQuotaIfaces.push_front(ifaceName);
454
455    }
456
457    if (maxBytes != sharedQuotaBytes) {
458        res |= updateQuota(costName, maxBytes);
459        if (res) {
460            LOGE("Failed update quota for %s", costName);
461            goto fail;
462        }
463        sharedQuotaBytes = maxBytes;
464    }
465    return 0;
466
467    fail:
468    /*
469     * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
470     * rules in the kernel to see which ones need cleaning up.
471     * For now callers needs to choose if they want to "ndc bandwidth enable"
472     * which resets everything.
473     */
474    removeInterfaceSharedQuota(ifn);
475    return -1;
476}
477
478/* It will also cleanup any shared alerts */
479int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
480    char ifn[MAX_IFACENAME_LEN];
481    int res = 0;
482    std::string ifaceName;
483    std::list<std::string>::iterator it;
484    const char *costName = "shared";
485
486    if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
487        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
488        return -1;
489    }
490    ifaceName = ifn;
491
492    for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
493        if (*it == ifaceName)
494            break;
495    }
496    if (it == sharedQuotaIfaces.end()) {
497        LOGE("No such iface %s to delete", ifn);
498        return -1;
499    }
500
501    res |= cleanupCostlyIface(ifn, QuotaShared);
502    sharedQuotaIfaces.erase(it);
503
504    if (sharedQuotaIfaces.empty()) {
505        std::string quotaCmd;
506        quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
507        res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
508        sharedQuotaBytes = 0;
509        if (sharedAlertBytes) {
510            removeSharedAlert();
511            sharedAlertBytes = 0;
512        }
513    }
514    return res;
515}
516
517int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
518    char ifn[MAX_IFACENAME_LEN];
519    int res = 0;
520    std::string ifaceName;
521    const char *costName;
522    std::list<QuotaInfo>::iterator it;
523    std::string quotaCmd;
524
525    if (!maxBytes) {
526        /* Don't talk about -1, deprecate it. */
527        LOGE("Invalid bytes value. 1..max_int64.");
528        return -1;
529    }
530    if (maxBytes == -1) {
531        return removeInterfaceQuota(iface);
532    }
533
534    if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
535        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
536        return -1;
537    }
538    ifaceName = ifn;
539    costName = iface;
540
541    /* Insert ingress quota. */
542    for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
543        if (it->ifaceName == ifaceName)
544            break;
545    }
546
547    if (it == quotaIfaces.end()) {
548        res |= prepCostlyIface(ifn, QuotaUnique);
549        quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
550        res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
551        if (res) {
552            LOGE("Failed set quota rule");
553            goto fail;
554        }
555
556        quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
557
558    } else {
559        res |= updateQuota(costName, maxBytes);
560        if (res) {
561            LOGE("Failed update quota for %s", iface);
562            goto fail;
563        }
564        it->quota = maxBytes;
565    }
566    return 0;
567
568    fail:
569    /*
570     * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
571     * rules in the kernel to see which ones need cleaning up.
572     * For now callers needs to choose if they want to "ndc bandwidth enable"
573     * which resets everything.
574     */
575    removeInterfaceSharedQuota(ifn);
576    return -1;
577}
578
579int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
580    return getInterfaceQuota("shared", bytes);
581}
582
583int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
584    FILE *fp;
585    char *fname;
586    int scanRes;
587
588    asprintf(&fname, "/proc/net/xt_quota/%s", costName);
589    fp = fopen(fname, "r");
590    free(fname);
591    if (!fp) {
592        LOGE("Reading quota %s failed (%s)", costName, strerror(errno));
593        return -1;
594    }
595    scanRes = fscanf(fp, "%lld", bytes);
596    LOGD("Read quota res=%d bytes=%lld", scanRes, *bytes);
597    fclose(fp);
598    return scanRes == 1 ? 0 : -1;
599}
600
601int BandwidthController::removeInterfaceQuota(const char *iface) {
602
603    char ifn[MAX_IFACENAME_LEN];
604    int res = 0;
605    std::string ifaceName;
606    const char *costName;
607    std::list<QuotaInfo>::iterator it;
608
609    if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
610        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
611        return -1;
612    }
613    ifaceName = ifn;
614    costName = iface;
615
616    for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
617        if (it->ifaceName == ifaceName)
618            break;
619    }
620
621    if (it == quotaIfaces.end()) {
622        LOGE("No such iface %s to delete", ifn);
623        return -1;
624    }
625
626    /* This also removes the quota command of CostlyIface chain. */
627    res |= cleanupCostlyIface(ifn, QuotaUnique);
628
629    quotaIfaces.erase(it);
630
631    return res;
632}
633
634int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
635    FILE *fp;
636    char *fname;
637
638    asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
639    fp = fopen(fname, "w");
640    free(fname);
641    if (!fp) {
642        LOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
643        return -1;
644    }
645    fprintf(fp, "%lld\n", bytes);
646    fclose(fp);
647    return 0;
648}
649
650int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
651    int res = 0;
652    const char *opFlag;
653    char *alertQuotaCmd;
654
655    switch (op) {
656    case IptOpInsert:
657        opFlag = "-I";
658        break;
659    case IptOpReplace:
660        opFlag = "-R";
661        break;
662    default:
663    case IptOpDelete:
664        opFlag = "-D";
665        break;
666    }
667
668    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "INPUT", bytes, alertName, alertName);
669    res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
670    free(alertQuotaCmd);
671    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "OUTPUT", bytes, alertName, alertName);
672    res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
673    free(alertQuotaCmd);
674    return res;
675}
676
677int BandwidthController::setGlobalAlert(int64_t bytes) {
678    char *alertQuotaCmd;
679    const char *alertName = "globalAlert";
680    int res = 0;
681
682    if (!bytes) {
683        LOGE("Invalid bytes value. 1..max_int64.");
684        return -1;
685    }
686    if (globalAlertBytes) {
687        res = updateQuota(alertName, bytes);
688    } else {
689        res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
690    }
691    globalAlertBytes = bytes;
692    return res;
693}
694
695int BandwidthController::removeGlobalAlert(void) {
696    char *alertQuotaCmd;
697
698    const char *alertName = "globalAlert";
699    int res = 0;
700
701    if (!globalAlertBytes) {
702        LOGE("No prior alert set");
703        return -1;
704    }
705    res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
706    globalAlertBytes = 0;
707    return res;
708}
709
710int BandwidthController::setSharedAlert(int64_t bytes) {
711    if (!sharedQuotaBytes) {
712        LOGE("Need to have a prior shared quota set to set an alert");
713        return -1;
714    }
715    if (!bytes) {
716        LOGE("Invalid bytes value. 1..max_int64.");
717        return -1;
718    }
719    return setCostlyAlert("shared", bytes, &sharedAlertBytes);
720}
721
722int BandwidthController::removeSharedAlert(void) {
723    return removeCostlyAlert("shared", &sharedAlertBytes);
724}
725
726int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
727    std::list<QuotaInfo>::iterator it;
728
729    if (!bytes) {
730        LOGE("Invalid bytes value. 1..max_int64.");
731        return -1;
732    }
733    for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
734        if (it->ifaceName == iface)
735            break;
736    }
737
738    if (it == quotaIfaces.end()) {
739        LOGE("Need to have a prior interface quota set to set an alert");
740        return -1;
741    }
742
743    return setCostlyAlert(iface, bytes, &it->alert);
744}
745
746int BandwidthController::removeInterfaceAlert(const char *iface) {
747    std::list<QuotaInfo>::iterator it;
748
749    for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
750        if (it->ifaceName == iface)
751            break;
752    }
753
754    if (it == quotaIfaces.end()) {
755        LOGE("No prior alert set for interface %s", iface);
756        return -1;
757    }
758
759    return removeCostlyAlert(iface, &it->alert);
760}
761
762int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
763    char *alertQuotaCmd;
764    char *chainNameAndPos;
765    int res = 0;
766    char *alertName;
767
768    if (!bytes) {
769        LOGE("Invalid bytes value. 1..max_int64.");
770        return -1;
771    }
772    asprintf(&alertName, "%sAlert", costName);
773    if (*alertBytes) {
774        res = updateQuota(alertName, *alertBytes);
775    } else {
776        asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN);
777        asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-I", chainNameAndPos, bytes, alertName,
778                 alertName);
779        res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
780        free(alertQuotaCmd);
781        free(chainNameAndPos);
782    }
783    *alertBytes = bytes;
784    free(alertName);
785    return res;
786}
787
788int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
789    char *alertQuotaCmd;
790    char *chainName;
791    char *alertName;
792    int res = 0;
793
794    asprintf(&alertName, "%sAlert", costName);
795    if (!*alertBytes) {
796        LOGE("No prior alert set for %s alert", costName);
797        return -1;
798    }
799
800    asprintf(&chainName, "costly_%s", costName);
801    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName, alertName);
802    res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
803    free(alertQuotaCmd);
804    free(chainName);
805
806    *alertBytes = 0;
807    free(alertName);
808    return res;
809}
810