• 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 <string>
26 #include <vector>
27 
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 
35 #define __STDC_FORMAT_MACROS 1
36 #include <inttypes.h>
37 
38 #include <sys/socket.h>
39 #include <sys/stat.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 
43 #include <linux/netlink.h>
44 #include <linux/rtnetlink.h>
45 #include <linux/pkt_sched.h>
46 
47 #include "android-base/stringprintf.h"
48 #include "android-base/strings.h"
49 #define LOG_TAG "BandwidthController"
50 #include <cutils/log.h>
51 #include <cutils/properties.h>
52 #include <logwrap/logwrap.h>
53 
54 #include "NetdConstants.h"
55 #include "BandwidthController.h"
56 #include "NatController.h"  /* For LOCAL_TETHER_COUNTERS_CHAIN */
57 #include "ResponseCode.h"
58 
59 /* Alphabetical */
60 #define ALERT_IPT_TEMPLATE "%s %s -m quota2 ! --quota %" PRId64" --name %s"
61 const char* BandwidthController::LOCAL_INPUT = "bw_INPUT";
62 const char* BandwidthController::LOCAL_FORWARD = "bw_FORWARD";
63 const char* BandwidthController::LOCAL_OUTPUT = "bw_OUTPUT";
64 const char* BandwidthController::LOCAL_RAW_PREROUTING = "bw_raw_PREROUTING";
65 const char* BandwidthController::LOCAL_MANGLE_POSTROUTING = "bw_mangle_POSTROUTING";
66 
67 auto BandwidthController::execFunction = android_fork_execvp;
68 auto BandwidthController::popenFunction = popen;
69 auto BandwidthController::iptablesRestoreFunction = execIptablesRestore;
70 
71 namespace {
72 
73 const char ALERT_GLOBAL_NAME[] = "globalAlert";
74 const int  MAX_CMD_ARGS = 32;
75 const int  MAX_CMD_LEN = 1024;
76 const int  MAX_IFACENAME_LEN = 64;
77 const int  MAX_IPT_OUTPUT_LINE_LEN = 256;
78 
79 /**
80  * Some comments about the rules:
81  *  * Ordering
82  *    - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
83  *      E.g. "-I bw_INPUT -i rmnet0 --jump costly"
84  *    - quota'd rules in the costly chain should be before bw_penalty_box lookups.
85  *    - the qtaguid counting is done at the end of the bw_INPUT/bw_OUTPUT user chains.
86  *
87  * * global quota vs per interface quota
88  *   - global quota for all costly interfaces uses a single costly chain:
89  *    . initial rules
90  *      iptables -N bw_costly_shared
91  *      iptables -I bw_INPUT -i iface0 --jump bw_costly_shared
92  *      iptables -I bw_OUTPUT -o iface0 --jump bw_costly_shared
93  *      iptables -I bw_costly_shared -m quota \! --quota 500000 \
94  *          --jump REJECT --reject-with icmp-net-prohibited
95  *      iptables -A bw_costly_shared --jump bw_penalty_box
96  *      iptables -A bw_penalty_box --jump bw_happy_box
97  *      iptables -A bw_happy_box --jump bw_data_saver
98  *
99  *    . adding a new iface to this, E.g.:
100  *      iptables -I bw_INPUT -i iface1 --jump bw_costly_shared
101  *      iptables -I bw_OUTPUT -o iface1 --jump bw_costly_shared
102  *
103  *   - quota per interface. This is achieve by having "costly" chains per quota.
104  *     E.g. adding a new costly interface iface0 with its own quota:
105  *      iptables -N bw_costly_iface0
106  *      iptables -I bw_INPUT -i iface0 --jump bw_costly_iface0
107  *      iptables -I bw_OUTPUT -o iface0 --jump bw_costly_iface0
108  *      iptables -A bw_costly_iface0 -m quota \! --quota 500000 \
109  *          --jump REJECT --reject-with icmp-port-unreachable
110  *      iptables -A bw_costly_iface0 --jump bw_penalty_box
111  *
112  * * Penalty box, happy box and data saver.
113  *   - bw_penalty box is a blacklist of apps that are rejected.
114  *   - bw_happy_box is a whitelist of apps. It always includes all system apps
115  *   - bw_data_saver implements data usage restrictions.
116  *   - Via the UI the user can add and remove apps from the whitelist and
117  *     blacklist, and turn on/off data saver.
118  *   - The blacklist takes precedence over the whitelist and the whitelist
119  *     takes precedence over data saver.
120  *
121  * * bw_penalty_box handling:
122  *  - only one bw_penalty_box for all interfaces
123  *   E.g  Adding an app:
124  *    iptables -I bw_penalty_box -m owner --uid-owner app_3 \
125  *        --jump REJECT --reject-with icmp-port-unreachable
126  *
127  * * bw_happy_box handling:
128  *  - The bw_happy_box comes after the penalty box.
129  *   E.g  Adding a happy app,
130  *    iptables -I bw_happy_box -m owner --uid-owner app_3 \
131  *        --jump RETURN
132  *
133  * * bw_data_saver handling:
134  *  - The bw_data_saver comes after the happy box.
135  *    Enable data saver:
136  *      iptables -R 1 bw_data_saver --jump REJECT --reject-with icmp-port-unreachable
137  *    Disable data saver:
138  *      iptables -R 1 bw_data_saver --jump RETURN
139  */
140 
141 const std::string COMMIT_AND_CLOSE = "COMMIT\n\x04";
142 const std::string DATA_SAVER_ENABLE_COMMAND = "-R bw_data_saver 1";
143 const std::string HAPPY_BOX_WHITELIST_COMMAND = android::base::StringPrintf(
144     "-I bw_happy_box -m owner --uid-owner %d-%d --jump RETURN", 0, MAX_SYSTEM_UID);
145 
146 static const std::vector<std::string> IPT_FLUSH_COMMANDS = {
147     /*
148      * Cleanup rules.
149      * Should normally include bw_costly_<iface>, but we rely on the way they are setup
150      * to allow coexistance.
151      */
152     "*filter",
153     ":bw_INPUT -",
154     ":bw_OUTPUT -",
155     ":bw_FORWARD -",
156     ":bw_happy_box -",
157     ":bw_penalty_box -",
158     ":bw_data_saver -",
159     ":bw_costly_shared -",
160     "COMMIT",
161     "*raw",
162     ":bw_raw_PREROUTING -",
163     "COMMIT",
164     "*mangle",
165     ":bw_mangle_POSTROUTING -",
166     COMMIT_AND_CLOSE
167 };
168 
169 static const std::vector<std::string> IPT_BASIC_ACCOUNTING_COMMANDS = {
170     "*filter",
171     "-A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */
172     "-A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
173     "-A bw_costly_shared --jump bw_penalty_box",
174     "-A bw_penalty_box --jump bw_happy_box",
175     "-A bw_happy_box --jump bw_data_saver",
176     "-A bw_data_saver -j RETURN",
177     HAPPY_BOX_WHITELIST_COMMAND,
178     "COMMIT",
179 
180     "*raw",
181     "-A bw_raw_PREROUTING -m owner --socket-exists", /* This is a tracking rule. */
182     "COMMIT",
183 
184     "*mangle",
185     "-A bw_mangle_POSTROUTING -m owner --socket-exists", /* This is a tracking rule. */
186     COMMIT_AND_CLOSE
187 };
188 
189 
190 }  // namespace
191 
BandwidthController(void)192 BandwidthController::BandwidthController(void) {
193 }
194 
runIpxtablesCmd(const char * cmd,IptJumpOp jumpHandling,IptFailureLog failureHandling)195 int BandwidthController::runIpxtablesCmd(const char *cmd, IptJumpOp jumpHandling,
196                                          IptFailureLog failureHandling) {
197     int res = 0;
198 
199     ALOGV("runIpxtablesCmd(cmd=%s)", cmd);
200     res |= runIptablesCmd(cmd, jumpHandling, IptIpV4, failureHandling);
201     res |= runIptablesCmd(cmd, jumpHandling, IptIpV6, failureHandling);
202     return res;
203 }
204 
StrncpyAndCheck(char * buffer,const char * src,size_t buffSize)205 int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
206 
207     memset(buffer, '\0', buffSize);  // strncpy() is not filling leftover with '\0'
208     strncpy(buffer, src, buffSize);
209     return buffer[buffSize - 1];
210 }
211 
runIptablesCmd(const char * cmd,IptJumpOp jumpHandling,IptIpVer iptVer,IptFailureLog failureHandling)212 int BandwidthController::runIptablesCmd(const char *cmd, IptJumpOp jumpHandling,
213                                         IptIpVer iptVer, IptFailureLog failureHandling) {
214     char buffer[MAX_CMD_LEN];
215     const char *argv[MAX_CMD_ARGS];
216     int argc = 0;
217     char *next = buffer;
218     char *tmp;
219     int res;
220     int status = 0;
221 
222     std::string fullCmd = cmd;
223 
224     switch (jumpHandling) {
225     case IptJumpReject:
226         /*
227          * Must be carefull what one rejects with, as uper layer protocols will just
228          * keep on hammering the device until the number of retries are done.
229          * For port-unreachable (default), TCP should consider as an abort (RFC1122).
230          */
231         fullCmd += " --jump REJECT";
232         break;
233     case IptJumpReturn:
234         fullCmd += " --jump RETURN";
235         break;
236     case IptJumpNoAdd:
237         break;
238     }
239 
240     fullCmd.insert(0, " -w ");
241     fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
242 
243     if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
244         ALOGE("iptables command too long");
245         return -1;
246     }
247 
248     argc = 0;
249     while ((tmp = strsep(&next, " "))) {
250         argv[argc++] = tmp;
251         if (argc >= MAX_CMD_ARGS) {
252             ALOGE("iptables argument overflow");
253             return -1;
254         }
255     }
256 
257     argv[argc] = NULL;
258     res = execFunction(argc, (char **)argv, &status, false,
259             failureHandling == IptFailShow);
260     res = res || !WIFEXITED(status) || WEXITSTATUS(status);
261     if (res && failureHandling == IptFailShow) {
262       ALOGE("runIptablesCmd(): res=%d status=%d failed %s", res, status,
263             fullCmd.c_str());
264     }
265     return res;
266 }
267 
flushCleanTables(bool doClean)268 void BandwidthController::flushCleanTables(bool doClean) {
269     /* Flush and remove the bw_costly_<iface> tables */
270     flushExistingCostlyTables(doClean);
271 
272     std::string commands = android::base::Join(IPT_FLUSH_COMMANDS, '\n');
273     iptablesRestoreFunction(V4V6, commands);
274 }
275 
setupIptablesHooks(void)276 int BandwidthController::setupIptablesHooks(void) {
277     /* flush+clean is allowed to fail */
278     flushCleanTables(true);
279     return 0;
280 }
281 
enableBandwidthControl(bool force)282 int BandwidthController::enableBandwidthControl(bool force) {
283     char value[PROPERTY_VALUE_MAX];
284 
285     if (!force) {
286             property_get("persist.bandwidth.enable", value, "1");
287             if (!strcmp(value, "0"))
288                     return 0;
289     }
290 
291     /* Let's pretend we started from scratch ... */
292     sharedQuotaIfaces.clear();
293     quotaIfaces.clear();
294     globalAlertBytes = 0;
295     globalAlertTetherCount = 0;
296     sharedQuotaBytes = sharedAlertBytes = 0;
297 
298     flushCleanTables(false);
299     std::string commands = android::base::Join(IPT_BASIC_ACCOUNTING_COMMANDS, '\n');
300     return iptablesRestoreFunction(V4V6, commands);
301 }
302 
disableBandwidthControl(void)303 int BandwidthController::disableBandwidthControl(void) {
304 
305     flushCleanTables(false);
306     return 0;
307 }
308 
enableDataSaver(bool enable)309 int BandwidthController::enableDataSaver(bool enable) {
310     return runIpxtablesCmd(DATA_SAVER_ENABLE_COMMAND.c_str(),
311                            enable ? IptJumpReject : IptJumpReturn, IptFailShow);
312 }
313 
runCommands(int numCommands,const char * commands[],RunCmdErrHandling cmdErrHandling)314 int BandwidthController::runCommands(int numCommands, const char *commands[],
315                                      RunCmdErrHandling cmdErrHandling) {
316     int res = 0;
317     IptFailureLog failureLogging = IptFailShow;
318     if (cmdErrHandling == RunCmdFailureOk) {
319         failureLogging = IptFailHide;
320     }
321     ALOGV("runCommands(): %d commands", numCommands);
322     for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
323         res = runIpxtablesCmd(commands[cmdNum], IptJumpNoAdd, failureLogging);
324         if (res && cmdErrHandling != RunCmdFailureOk)
325             return res;
326     }
327     return 0;
328 }
329 
makeIptablesSpecialAppCmd(IptOp op,int uid,const char * chain)330 std::string BandwidthController::makeIptablesSpecialAppCmd(IptOp op, int uid, const char *chain) {
331     std::string res;
332     char *buff;
333     const char *opFlag;
334 
335     switch (op) {
336     case IptOpInsert:
337         opFlag = "-I";
338         break;
339     case IptOpAppend:
340         ALOGE("Append op not supported for %s uids", chain);
341         res = "";
342         return res;
343         break;
344     case IptOpReplace:
345         opFlag = "-R";
346         break;
347     default:
348     case IptOpDelete:
349         opFlag = "-D";
350         break;
351     }
352     asprintf(&buff, "%s %s -m owner --uid-owner %d", opFlag, chain, uid);
353     res = buff;
354     free(buff);
355     return res;
356 }
357 
addNaughtyApps(int numUids,char * appUids[])358 int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
359     return manipulateNaughtyApps(numUids, appUids, SpecialAppOpAdd);
360 }
361 
removeNaughtyApps(int numUids,char * appUids[])362 int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
363     return manipulateNaughtyApps(numUids, appUids, SpecialAppOpRemove);
364 }
365 
addNiceApps(int numUids,char * appUids[])366 int BandwidthController::addNiceApps(int numUids, char *appUids[]) {
367     return manipulateNiceApps(numUids, appUids, SpecialAppOpAdd);
368 }
369 
removeNiceApps(int numUids,char * appUids[])370 int BandwidthController::removeNiceApps(int numUids, char *appUids[]) {
371     return manipulateNiceApps(numUids, appUids, SpecialAppOpRemove);
372 }
373 
manipulateNaughtyApps(int numUids,char * appStrUids[],SpecialAppOp appOp)374 int BandwidthController::manipulateNaughtyApps(int numUids, char *appStrUids[], SpecialAppOp appOp) {
375     return manipulateSpecialApps(numUids, appStrUids, "bw_penalty_box", IptJumpReject, appOp);
376 }
377 
manipulateNiceApps(int numUids,char * appStrUids[],SpecialAppOp appOp)378 int BandwidthController::manipulateNiceApps(int numUids, char *appStrUids[], SpecialAppOp appOp) {
379     return manipulateSpecialApps(numUids, appStrUids, "bw_happy_box", IptJumpReturn, appOp);
380 }
381 
382 
manipulateSpecialApps(int numUids,char * appStrUids[],const char * chain,IptJumpOp jumpHandling,SpecialAppOp appOp)383 int BandwidthController::manipulateSpecialApps(int numUids, char *appStrUids[],
384                                                const char *chain,
385                                                IptJumpOp jumpHandling, SpecialAppOp appOp) {
386 
387     int uidNum;
388     const char *failLogTemplate;
389     IptOp op;
390     int appUids[numUids];
391     std::string iptCmd;
392 
393     switch (appOp) {
394     case SpecialAppOpAdd:
395         op = IptOpInsert;
396         failLogTemplate = "Failed to add app uid %s(%d) to %s.";
397         break;
398     case SpecialAppOpRemove:
399         op = IptOpDelete;
400         failLogTemplate = "Failed to delete app uid %s(%d) from %s box.";
401         break;
402     default:
403         ALOGE("Unexpected app Op %d", appOp);
404         return -1;
405     }
406 
407     for (uidNum = 0; uidNum < numUids; uidNum++) {
408         char *end;
409         appUids[uidNum] = strtoul(appStrUids[uidNum], &end, 0);
410         if (*end || !*appStrUids[uidNum]) {
411             ALOGE(failLogTemplate, appStrUids[uidNum], appUids[uidNum], chain);
412             goto fail_parse;
413         }
414     }
415 
416     for (uidNum = 0; uidNum < numUids; uidNum++) {
417         int uid = appUids[uidNum];
418 
419         iptCmd = makeIptablesSpecialAppCmd(op, uid, chain);
420         if (runIpxtablesCmd(iptCmd.c_str(), jumpHandling)) {
421             ALOGE(failLogTemplate, appStrUids[uidNum], uid, chain);
422             goto fail_with_uidNum;
423         }
424     }
425     return 0;
426 
427 fail_with_uidNum:
428     /* Try to remove the uid that failed in any case*/
429     iptCmd = makeIptablesSpecialAppCmd(IptOpDelete, appUids[uidNum], chain);
430     runIpxtablesCmd(iptCmd.c_str(), jumpHandling);
431 fail_parse:
432     return -1;
433 }
434 
makeIptablesQuotaCmd(IptOp op,const char * costName,int64_t quota)435 std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
436     std::string res;
437     char *buff;
438     const char *opFlag;
439 
440     ALOGV("makeIptablesQuotaCmd(%d, %" PRId64")", op, quota);
441 
442     switch (op) {
443     case IptOpInsert:
444         opFlag = "-I";
445         break;
446     case IptOpAppend:
447         opFlag = "-A";
448         break;
449     case IptOpReplace:
450         opFlag = "-R";
451         break;
452     default:
453     case IptOpDelete:
454         opFlag = "-D";
455         break;
456     }
457 
458     // The requried IP version specific --jump REJECT ... will be added later.
459     asprintf(&buff, "%s bw_costly_%s -m quota2 ! --quota %" PRId64" --name %s", opFlag, costName, quota,
460              costName);
461     res = buff;
462     free(buff);
463     return res;
464 }
465 
prepCostlyIface(const char * ifn,QuotaType quotaType)466 int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
467     char cmd[MAX_CMD_LEN];
468     int res = 0, res1, res2;
469     int ruleInsertPos = 1;
470     std::string costString;
471     const char *costCString;
472 
473     /* The "-N costly" is created upfront, no need to handle it here. */
474     switch (quotaType) {
475     case QuotaUnique:
476         costString = "bw_costly_";
477         costString += ifn;
478         costCString = costString.c_str();
479         /*
480          * Flush the bw_costly_<iface> is allowed to fail in case it didn't exist.
481          * Creating a new one is allowed to fail in case it existed.
482          * This helps with netd restarts.
483          */
484         snprintf(cmd, sizeof(cmd), "-F %s", costCString);
485         res1 = runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
486         snprintf(cmd, sizeof(cmd), "-N %s", costCString);
487         res2 = runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
488         res = (res1 && res2) || (!res1 && !res2);
489 
490         snprintf(cmd, sizeof(cmd), "-A %s -j bw_penalty_box", costCString);
491         res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
492         break;
493     case QuotaShared:
494         costCString = "bw_costly_shared";
495         break;
496     default:
497         ALOGE("Unexpected quotatype %d", quotaType);
498         return -1;
499     }
500 
501     if (globalAlertBytes) {
502         /* The alert rule comes 1st */
503         ruleInsertPos = 2;
504     }
505 
506     snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
507     runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
508 
509     snprintf(cmd, sizeof(cmd), "-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, ifn, costCString);
510     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
511 
512     snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
513     runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
514 
515     snprintf(cmd, sizeof(cmd), "-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, ifn, costCString);
516     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
517 
518     snprintf(cmd, sizeof(cmd), "-D bw_FORWARD -o %s --jump %s", ifn, costCString);
519     runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
520     snprintf(cmd, sizeof(cmd), "-A bw_FORWARD -o %s --jump %s", ifn, costCString);
521     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
522 
523     return res;
524 }
525 
cleanupCostlyIface(const char * ifn,QuotaType quotaType)526 int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
527     char cmd[MAX_CMD_LEN];
528     int res = 0;
529     std::string costString;
530     const char *costCString;
531 
532     switch (quotaType) {
533     case QuotaUnique:
534         costString = "bw_costly_";
535         costString += ifn;
536         costCString = costString.c_str();
537         break;
538     case QuotaShared:
539         costCString = "bw_costly_shared";
540         break;
541     default:
542         ALOGE("Unexpected quotatype %d", quotaType);
543         return -1;
544     }
545 
546     snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
547     res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
548     for (const auto tableName : {LOCAL_OUTPUT, LOCAL_FORWARD}) {
549         snprintf(cmd, sizeof(cmd), "-D %s -o %s --jump %s", tableName, ifn, costCString);
550         res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
551     }
552 
553     /* The "-N bw_costly_shared" is created upfront, no need to handle it here. */
554     if (quotaType == QuotaUnique) {
555         snprintf(cmd, sizeof(cmd), "-F %s", costCString);
556         res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
557         snprintf(cmd, sizeof(cmd), "-X %s", costCString);
558         res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
559     }
560     return res;
561 }
562 
setInterfaceSharedQuota(const char * iface,int64_t maxBytes)563 int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
564     char ifn[MAX_IFACENAME_LEN];
565     int res = 0;
566     std::string quotaCmd;
567     std::string ifaceName;
568     ;
569     const char *costName = "shared";
570     std::list<std::string>::iterator it;
571 
572     if (!maxBytes) {
573         /* Don't talk about -1, deprecate it. */
574         ALOGE("Invalid bytes value. 1..max_int64.");
575         return -1;
576     }
577     if (!isIfaceName(iface))
578         return -1;
579     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
580         ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
581         return -1;
582     }
583     ifaceName = ifn;
584 
585     if (maxBytes == -1) {
586         return removeInterfaceSharedQuota(ifn);
587     }
588 
589     /* Insert ingress quota. */
590     for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
591         if (*it == ifaceName)
592             break;
593     }
594 
595     if (it == sharedQuotaIfaces.end()) {
596         res |= prepCostlyIface(ifn, QuotaShared);
597         if (sharedQuotaIfaces.empty()) {
598             quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
599             res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
600             if (res) {
601                 ALOGE("Failed set quota rule");
602                 goto fail;
603             }
604             sharedQuotaBytes = maxBytes;
605         }
606         sharedQuotaIfaces.push_front(ifaceName);
607 
608     }
609 
610     if (maxBytes != sharedQuotaBytes) {
611         res |= updateQuota(costName, maxBytes);
612         if (res) {
613             ALOGE("Failed update quota for %s", costName);
614             goto fail;
615         }
616         sharedQuotaBytes = maxBytes;
617     }
618     return 0;
619 
620     fail:
621     /*
622      * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
623      * rules in the kernel to see which ones need cleaning up.
624      * For now callers needs to choose if they want to "ndc bandwidth enable"
625      * which resets everything.
626      */
627     removeInterfaceSharedQuota(ifn);
628     return -1;
629 }
630 
631 /* It will also cleanup any shared alerts */
removeInterfaceSharedQuota(const char * iface)632 int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
633     char ifn[MAX_IFACENAME_LEN];
634     int res = 0;
635     std::string ifaceName;
636     std::list<std::string>::iterator it;
637     const char *costName = "shared";
638 
639     if (!isIfaceName(iface))
640         return -1;
641     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
642         ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
643         return -1;
644     }
645     ifaceName = ifn;
646 
647     for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
648         if (*it == ifaceName)
649             break;
650     }
651     if (it == sharedQuotaIfaces.end()) {
652         ALOGE("No such iface %s to delete", ifn);
653         return -1;
654     }
655 
656     res |= cleanupCostlyIface(ifn, QuotaShared);
657     sharedQuotaIfaces.erase(it);
658 
659     if (sharedQuotaIfaces.empty()) {
660         std::string quotaCmd;
661         quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
662         res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
663         sharedQuotaBytes = 0;
664         if (sharedAlertBytes) {
665             removeSharedAlert();
666             sharedAlertBytes = 0;
667         }
668     }
669     return res;
670 }
671 
setInterfaceQuota(const char * iface,int64_t maxBytes)672 int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
673     char ifn[MAX_IFACENAME_LEN];
674     int res = 0;
675     std::string ifaceName;
676     const char *costName;
677     std::list<QuotaInfo>::iterator it;
678     std::string quotaCmd;
679 
680     if (!isIfaceName(iface))
681         return -1;
682 
683     if (!maxBytes) {
684         /* Don't talk about -1, deprecate it. */
685         ALOGE("Invalid bytes value. 1..max_int64.");
686         return -1;
687     }
688     if (maxBytes == -1) {
689         return removeInterfaceQuota(iface);
690     }
691 
692     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
693         ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
694         return -1;
695     }
696     ifaceName = ifn;
697     costName = iface;
698 
699     /* Insert ingress quota. */
700     for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
701         if (it->ifaceName == ifaceName)
702             break;
703     }
704 
705     if (it == quotaIfaces.end()) {
706         /* Preparing the iface adds a penalty/happy box check */
707         res |= prepCostlyIface(ifn, QuotaUnique);
708         /*
709          * The rejecting quota limit should go after the penalty/happy box checks
710          * or else a naughty app could just eat up the quota.
711          * So we append here.
712          */
713         quotaCmd = makeIptablesQuotaCmd(IptOpAppend, costName, maxBytes);
714         res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
715         if (res) {
716             ALOGE("Failed set quota rule");
717             goto fail;
718         }
719 
720         quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
721 
722     } else {
723         res |= updateQuota(costName, maxBytes);
724         if (res) {
725             ALOGE("Failed update quota for %s", iface);
726             goto fail;
727         }
728         it->quota = maxBytes;
729     }
730     return 0;
731 
732     fail:
733     /*
734      * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
735      * rules in the kernel to see which ones need cleaning up.
736      * For now callers needs to choose if they want to "ndc bandwidth enable"
737      * which resets everything.
738      */
739     removeInterfaceSharedQuota(ifn);
740     return -1;
741 }
742 
getInterfaceSharedQuota(int64_t * bytes)743 int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
744     return getInterfaceQuota("shared", bytes);
745 }
746 
getInterfaceQuota(const char * costName,int64_t * bytes)747 int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
748     FILE *fp;
749     char *fname;
750     int scanRes;
751 
752     if (!isIfaceName(costName))
753         return -1;
754 
755     asprintf(&fname, "/proc/net/xt_quota/%s", costName);
756     fp = fopen(fname, "re");
757     free(fname);
758     if (!fp) {
759         ALOGE("Reading quota %s failed (%s)", costName, strerror(errno));
760         return -1;
761     }
762     scanRes = fscanf(fp, "%" SCNd64, bytes);
763     ALOGV("Read quota res=%d bytes=%" PRId64, scanRes, *bytes);
764     fclose(fp);
765     return scanRes == 1 ? 0 : -1;
766 }
767 
removeInterfaceQuota(const char * iface)768 int BandwidthController::removeInterfaceQuota(const char *iface) {
769 
770     char ifn[MAX_IFACENAME_LEN];
771     int res = 0;
772     std::string ifaceName;
773     std::list<QuotaInfo>::iterator it;
774 
775     if (!isIfaceName(iface))
776         return -1;
777     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
778         ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
779         return -1;
780     }
781     ifaceName = ifn;
782 
783     for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
784         if (it->ifaceName == ifaceName)
785             break;
786     }
787 
788     if (it == quotaIfaces.end()) {
789         ALOGE("No such iface %s to delete", ifn);
790         return -1;
791     }
792 
793     /* This also removes the quota command of CostlyIface chain. */
794     res |= cleanupCostlyIface(ifn, QuotaUnique);
795 
796     quotaIfaces.erase(it);
797 
798     return res;
799 }
800 
updateQuota(const char * quotaName,int64_t bytes)801 int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
802     FILE *fp;
803     char *fname;
804 
805     if (!isIfaceName(quotaName)) {
806         ALOGE("updateQuota: Invalid quotaName \"%s\"", quotaName);
807         return -1;
808     }
809 
810     asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
811     fp = fopen(fname, "we");
812     free(fname);
813     if (!fp) {
814         ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
815         return -1;
816     }
817     fprintf(fp, "%" PRId64"\n", bytes);
818     fclose(fp);
819     return 0;
820 }
821 
runIptablesAlertCmd(IptOp op,const char * alertName,int64_t bytes)822 int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
823     int res = 0;
824     const char *opFlag;
825     char *alertQuotaCmd;
826 
827     switch (op) {
828     case IptOpInsert:
829         opFlag = "-I";
830         break;
831     case IptOpAppend:
832         opFlag = "-A";
833         break;
834     case IptOpReplace:
835         opFlag = "-R";
836         break;
837     default:
838     case IptOpDelete:
839         opFlag = "-D";
840         break;
841     }
842 
843     asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_INPUT",
844         bytes, alertName);
845     res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
846     free(alertQuotaCmd);
847     asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_OUTPUT",
848         bytes, alertName);
849     res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
850     free(alertQuotaCmd);
851     return res;
852 }
853 
runIptablesAlertFwdCmd(IptOp op,const char * alertName,int64_t bytes)854 int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const char *alertName, int64_t bytes) {
855     int res = 0;
856     const char *opFlag;
857     char *alertQuotaCmd;
858 
859     switch (op) {
860     case IptOpInsert:
861         opFlag = "-I";
862         break;
863     case IptOpAppend:
864         opFlag = "-A";
865         break;
866     case IptOpReplace:
867         opFlag = "-R";
868         break;
869     default:
870     case IptOpDelete:
871         opFlag = "-D";
872         break;
873     }
874 
875     asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_FORWARD",
876         bytes, alertName);
877     res = runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
878     free(alertQuotaCmd);
879     return res;
880 }
881 
setGlobalAlert(int64_t bytes)882 int BandwidthController::setGlobalAlert(int64_t bytes) {
883     const char *alertName = ALERT_GLOBAL_NAME;
884     int res = 0;
885 
886     if (!bytes) {
887         ALOGE("Invalid bytes value. 1..max_int64.");
888         return -1;
889     }
890     if (globalAlertBytes) {
891         res = updateQuota(alertName, bytes);
892     } else {
893         res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
894         if (globalAlertTetherCount) {
895             ALOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
896             res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
897         }
898     }
899     globalAlertBytes = bytes;
900     return res;
901 }
902 
setGlobalAlertInForwardChain(void)903 int BandwidthController::setGlobalAlertInForwardChain(void) {
904     const char *alertName = ALERT_GLOBAL_NAME;
905     int res = 0;
906 
907     globalAlertTetherCount++;
908     ALOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
909 
910     /*
911      * If there is no globalAlert active we are done.
912      * If there is an active globalAlert but this is not the 1st
913      * tether, we are also done.
914      */
915     if (!globalAlertBytes || globalAlertTetherCount != 1) {
916         return 0;
917     }
918 
919     /* We only add the rule if this was the 1st tether added. */
920     res = runIptablesAlertFwdCmd(IptOpInsert, alertName, globalAlertBytes);
921     return res;
922 }
923 
removeGlobalAlert(void)924 int BandwidthController::removeGlobalAlert(void) {
925 
926     const char *alertName = ALERT_GLOBAL_NAME;
927     int res = 0;
928 
929     if (!globalAlertBytes) {
930         ALOGE("No prior alert set");
931         return -1;
932     }
933     res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
934     if (globalAlertTetherCount) {
935         res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
936     }
937     globalAlertBytes = 0;
938     return res;
939 }
940 
removeGlobalAlertInForwardChain(void)941 int BandwidthController::removeGlobalAlertInForwardChain(void) {
942     int res = 0;
943     const char *alertName = ALERT_GLOBAL_NAME;
944 
945     if (!globalAlertTetherCount) {
946         ALOGE("No prior alert set");
947         return -1;
948     }
949 
950     globalAlertTetherCount--;
951     /*
952      * If there is no globalAlert active we are done.
953      * If there is an active globalAlert but there are more
954      * tethers, we are also done.
955      */
956     if (!globalAlertBytes || globalAlertTetherCount >= 1) {
957         return 0;
958     }
959 
960     /* We only detete the rule if this was the last tether removed. */
961     res = runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
962     return res;
963 }
964 
setSharedAlert(int64_t bytes)965 int BandwidthController::setSharedAlert(int64_t bytes) {
966     if (!sharedQuotaBytes) {
967         ALOGE("Need to have a prior shared quota set to set an alert");
968         return -1;
969     }
970     if (!bytes) {
971         ALOGE("Invalid bytes value. 1..max_int64.");
972         return -1;
973     }
974     return setCostlyAlert("shared", bytes, &sharedAlertBytes);
975 }
976 
removeSharedAlert(void)977 int BandwidthController::removeSharedAlert(void) {
978     return removeCostlyAlert("shared", &sharedAlertBytes);
979 }
980 
setInterfaceAlert(const char * iface,int64_t bytes)981 int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
982     std::list<QuotaInfo>::iterator it;
983 
984     if (!isIfaceName(iface)) {
985         ALOGE("setInterfaceAlert: Invalid iface \"%s\"", iface);
986         return -1;
987     }
988 
989     if (!bytes) {
990         ALOGE("Invalid bytes value. 1..max_int64.");
991         return -1;
992     }
993     for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
994         if (it->ifaceName == iface)
995             break;
996     }
997 
998     if (it == quotaIfaces.end()) {
999         ALOGE("Need to have a prior interface quota set to set an alert");
1000         return -1;
1001     }
1002 
1003     return setCostlyAlert(iface, bytes, &it->alert);
1004 }
1005 
removeInterfaceAlert(const char * iface)1006 int BandwidthController::removeInterfaceAlert(const char *iface) {
1007     std::list<QuotaInfo>::iterator it;
1008 
1009     if (!isIfaceName(iface)) {
1010         ALOGE("removeInterfaceAlert: Invalid iface \"%s\"", iface);
1011         return -1;
1012     }
1013 
1014     for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
1015         if (it->ifaceName == iface)
1016             break;
1017     }
1018 
1019     if (it == quotaIfaces.end()) {
1020         ALOGE("No prior alert set for interface %s", iface);
1021         return -1;
1022     }
1023 
1024     return removeCostlyAlert(iface, &it->alert);
1025 }
1026 
setCostlyAlert(const char * costName,int64_t bytes,int64_t * alertBytes)1027 int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
1028     char *alertQuotaCmd;
1029     char *chainName;
1030     int res = 0;
1031     char *alertName;
1032 
1033     if (!isIfaceName(costName)) {
1034         ALOGE("setCostlyAlert: Invalid costName \"%s\"", costName);
1035         return -1;
1036     }
1037 
1038     if (!bytes) {
1039         ALOGE("Invalid bytes value. 1..max_int64.");
1040         return -1;
1041     }
1042     asprintf(&alertName, "%sAlert", costName);
1043     if (*alertBytes) {
1044         res = updateQuota(alertName, *alertBytes);
1045     } else {
1046         asprintf(&chainName, "bw_costly_%s", costName);
1047         asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-A", chainName, bytes, alertName);
1048         res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
1049         free(alertQuotaCmd);
1050         free(chainName);
1051     }
1052     *alertBytes = bytes;
1053     free(alertName);
1054     return res;
1055 }
1056 
removeCostlyAlert(const char * costName,int64_t * alertBytes)1057 int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
1058     char *alertQuotaCmd;
1059     char *chainName;
1060     char *alertName;
1061     int res = 0;
1062 
1063     if (!isIfaceName(costName)) {
1064         ALOGE("removeCostlyAlert: Invalid costName \"%s\"", costName);
1065         return -1;
1066     }
1067 
1068     if (!*alertBytes) {
1069         ALOGE("No prior alert set for %s alert", costName);
1070         return -1;
1071     }
1072 
1073     asprintf(&alertName, "%sAlert", costName);
1074     asprintf(&chainName, "bw_costly_%s", costName);
1075     asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName);
1076     res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd);
1077     free(alertQuotaCmd);
1078     free(chainName);
1079 
1080     *alertBytes = 0;
1081     free(alertName);
1082     return res;
1083 }
1084 
addStats(TetherStatsList & statsList,const TetherStats & stats)1085 void BandwidthController::addStats(TetherStatsList& statsList, const TetherStats& stats) {
1086     for (TetherStats& existing : statsList) {
1087         if (existing.addStatsIfMatch(stats)) {
1088             return;
1089         }
1090     }
1091     // No match. Insert a new interface pair.
1092     statsList.push_back(stats);
1093 }
1094 
1095 /*
1096  * Parse the ptks and bytes out of:
1097  *   Chain natctrl_tether_counters (4 references)
1098  *       pkts      bytes target     prot opt in     out     source               destination
1099  *         26     2373 RETURN     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0
1100  *         27     2002 RETURN     all  --  rmnet0 wlan0   0.0.0.0/0            0.0.0.0/0
1101  *       1040   107471 RETURN     all  --  bt-pan rmnet0  0.0.0.0/0            0.0.0.0/0
1102  *       1450  1708806 RETURN     all  --  rmnet0 bt-pan  0.0.0.0/0            0.0.0.0/0
1103  * or:
1104  *   Chain natctrl_tether_counters (0 references)
1105  *       pkts      bytes target     prot opt in     out     source               destination
1106  *          0        0 RETURN     all      wlan0  rmnet_data0  ::/0                 ::/0
1107  *          0        0 RETURN     all      rmnet_data0 wlan0   ::/0                 ::/0
1108  *
1109  * It results in an error if invoked and no tethering counter rules exist. The constraint
1110  * helps detect complete parsing failure.
1111  */
addForwardChainStats(const TetherStats & filter,TetherStatsList & statsList,FILE * fp,std::string & extraProcessingInfo)1112 int BandwidthController::addForwardChainStats(const TetherStats& filter,
1113                                               TetherStatsList& statsList, FILE *fp,
1114                                               std::string &extraProcessingInfo) {
1115     int res;
1116     char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
1117     char iface0[MAX_IPT_OUTPUT_LINE_LEN];
1118     char iface1[MAX_IPT_OUTPUT_LINE_LEN];
1119     char rest[MAX_IPT_OUTPUT_LINE_LEN];
1120 
1121     TetherStats stats;
1122     char *buffPtr;
1123     int64_t packets, bytes;
1124     int statsFound = 0;
1125 
1126     bool filterPair = filter.intIface[0] && filter.extIface[0];
1127 
1128     char *filterMsg = filter.getStatsLine();
1129     ALOGV("filter: %s",  filterMsg);
1130     free(filterMsg);
1131 
1132     stats = filter;
1133 
1134     while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
1135         /* Clean up, so a failed parse can still print info */
1136         iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
1137         if (strstr(buffPtr, "0.0.0.0")) {
1138             // IPv4 has -- indicating what to do with fragments...
1139             //       26     2373 RETURN     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0
1140             res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all -- %s %s 0.%s",
1141                     &packets, &bytes, iface0, iface1, rest);
1142         } else {
1143             // ... but IPv6 does not.
1144             //       26     2373 RETURN     all      wlan0  rmnet0  ::/0                 ::/0
1145             res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all %s %s ::/%s",
1146                     &packets, &bytes, iface0, iface1, rest);
1147         }
1148         ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%" PRId64" bytes=%" PRId64" rest=<%s> orig line=<%s>", res,
1149              iface0, iface1, packets, bytes, rest, buffPtr);
1150         extraProcessingInfo += buffPtr;
1151 
1152         if (res != 5) {
1153             continue;
1154         }
1155         /*
1156          * The following assumes that the 1st rule has in:extIface out:intIface,
1157          * which is what NatController sets up.
1158          * If not filtering, the 1st match rx, and sets up the pair for the tx side.
1159          */
1160         if (filter.intIface[0] && filter.extIface[0]) {
1161             if (filter.intIface == iface0 && filter.extIface == iface1) {
1162                 ALOGV("2Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1163                 stats.rxPackets = packets;
1164                 stats.rxBytes = bytes;
1165             } else if (filter.intIface == iface1 && filter.extIface == iface0) {
1166                 ALOGV("2Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1167                 stats.txPackets = packets;
1168                 stats.txBytes = bytes;
1169             }
1170         } else if (filter.intIface[0] || filter.extIface[0]) {
1171             if (filter.intIface == iface0 || filter.extIface == iface1) {
1172                 ALOGV("1Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1173                 stats.intIface = iface0;
1174                 stats.extIface = iface1;
1175                 stats.rxPackets = packets;
1176                 stats.rxBytes = bytes;
1177             } else if (filter.intIface == iface1 || filter.extIface == iface0) {
1178                 ALOGV("1Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1179                 stats.intIface = iface1;
1180                 stats.extIface = iface0;
1181                 stats.txPackets = packets;
1182                 stats.txBytes = bytes;
1183             }
1184         } else /* if (!filter.intFace[0] && !filter.extIface[0]) */ {
1185             if (!stats.intIface[0]) {
1186                 ALOGV("0Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1187                 stats.intIface = iface0;
1188                 stats.extIface = iface1;
1189                 stats.rxPackets = packets;
1190                 stats.rxBytes = bytes;
1191             } else if (stats.intIface == iface1 && stats.extIface == iface0) {
1192                 ALOGV("0Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets);
1193                 stats.txPackets = packets;
1194                 stats.txBytes = bytes;
1195             }
1196         }
1197         if (stats.rxBytes != -1 && stats.txBytes != -1) {
1198             ALOGV("rx_bytes=%" PRId64" tx_bytes=%" PRId64" filterPair=%d", stats.rxBytes, stats.txBytes, filterPair);
1199             addStats(statsList, stats);
1200             if (filterPair) {
1201                 return 0;
1202             } else {
1203                 statsFound++;
1204                 stats = filter;
1205             }
1206         }
1207     }
1208 
1209     /* It is always an error to find only one side of the stats. */
1210     /* It is an error to find nothing when not filtering. */
1211     if (((stats.rxBytes == -1) != (stats.txBytes == -1)) ||
1212         (!statsFound && !filterPair)) {
1213         return -1;
1214     }
1215     return 0;
1216 }
1217 
getStatsLine(void) const1218 char *BandwidthController::TetherStats::getStatsLine(void) const {
1219     char *msg;
1220     asprintf(&msg, "%s %s %" PRId64" %" PRId64" %" PRId64" %" PRId64, intIface.c_str(), extIface.c_str(),
1221             rxBytes, rxPackets, txBytes, txPackets);
1222     return msg;
1223 }
1224 
getTetherStatsCommand(const char * binary)1225 std::string getTetherStatsCommand(const char *binary) {
1226     /*
1227      * Why not use some kind of lib to talk to iptables?
1228      * Because the only libs are libiptc and libip6tc in iptables, and they are
1229      * not easy to use. They require the known iptables match modules to be
1230      * preloaded/linked, and require apparently a lot of wrapper code to get
1231      * the wanted info.
1232      */
1233     return android::base::StringPrintf("%s -nvx -w -L %s", binary,
1234                                        NatController::LOCAL_TETHER_COUNTERS_CHAIN);
1235 }
1236 
getTetherStats(SocketClient * cli,TetherStats & filter,std::string & extraProcessingInfo)1237 int BandwidthController::getTetherStats(SocketClient *cli, TetherStats& filter,
1238                                         std::string &extraProcessingInfo) {
1239     int res = 0;
1240     std::string fullCmd;
1241     FILE *iptOutput;
1242 
1243     TetherStatsList statsList;
1244 
1245     for (const auto binary : {IPTABLES_PATH, IP6TABLES_PATH}) {
1246         fullCmd = getTetherStatsCommand(binary);
1247         iptOutput = popenFunction(fullCmd.c_str(), "r");
1248         if (!iptOutput) {
1249                 ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
1250                 extraProcessingInfo += "Failed to run iptables.";
1251             return -1;
1252         }
1253 
1254         res = addForwardChainStats(filter, statsList, iptOutput, extraProcessingInfo);
1255         pclose(iptOutput);
1256         if (res != 0) {
1257             return res;
1258         }
1259     }
1260 
1261     if (filter.intIface[0] && filter.extIface[0] && statsList.size() == 1) {
1262         cli->sendMsg(ResponseCode::TetheringStatsResult, statsList[0].getStatsLine(), false);
1263     } else {
1264         for (const auto& stats: statsList) {
1265             cli->sendMsg(ResponseCode::TetheringStatsListResult, stats.getStatsLine(), false);
1266         }
1267         if (res == 0) {
1268             cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false);
1269         }
1270     }
1271 
1272     return res;
1273 }
1274 
flushExistingCostlyTables(bool doClean)1275 void BandwidthController::flushExistingCostlyTables(bool doClean) {
1276     std::string fullCmd;
1277     FILE *iptOutput;
1278 
1279     /* Only lookup ip4 table names as ip6 will have the same tables ... */
1280     fullCmd = IPTABLES_PATH;
1281     fullCmd += " -w -S";
1282     iptOutput = popenFunction(fullCmd.c_str(), "r");
1283     if (!iptOutput) {
1284             ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
1285         return;
1286     }
1287     /* ... then flush/clean both ip4 and ip6 iptables. */
1288     parseAndFlushCostlyTables(iptOutput, doClean);
1289     pclose(iptOutput);
1290 }
1291 
parseAndFlushCostlyTables(FILE * fp,bool doRemove)1292 void BandwidthController::parseAndFlushCostlyTables(FILE *fp, bool doRemove) {
1293     int res;
1294     char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
1295     char costlyIfaceName[MAX_IPT_OUTPUT_LINE_LEN];
1296     char cmd[MAX_CMD_LEN];
1297     char *buffPtr;
1298 
1299     while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
1300         costlyIfaceName[0] = '\0';   /* So that debugging output always works */
1301         res = sscanf(buffPtr, "-N bw_costly_%s", costlyIfaceName);
1302         ALOGV("parse res=%d costly=<%s> orig line=<%s>", res,
1303             costlyIfaceName, buffPtr);
1304         if (res != 1) {
1305             continue;
1306         }
1307         /* Exclusions: "shared" is not an ifacename */
1308         if (!strcmp(costlyIfaceName, "shared")) {
1309             continue;
1310         }
1311 
1312         snprintf(cmd, sizeof(cmd), "-F bw_costly_%s", costlyIfaceName);
1313         runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
1314         if (doRemove) {
1315             snprintf(cmd, sizeof(cmd), "-X bw_costly_%s", costlyIfaceName);
1316             runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
1317         }
1318     }
1319 }
1320