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