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