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