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