OSDN Git Service

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