OSDN Git Service

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