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 192 BandwidthController::BandwidthController(void) { 193 } 194 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 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 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 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 276 int BandwidthController::setupIptablesHooks(void) { 277 /* flush+clean is allowed to fail */ 278 flushCleanTables(true); 279 return 0; 280 } 281 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 303 int BandwidthController::disableBandwidthControl(void) { 304 305 flushCleanTables(false); 306 return 0; 307 } 308 309 int BandwidthController::enableDataSaver(bool enable) { 310 return runIpxtablesCmd(DATA_SAVER_ENABLE_COMMAND.c_str(), 311 enable ? IptJumpReject : IptJumpReturn, IptFailShow); 312 } 313 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 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 358 int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) { 359 return manipulateNaughtyApps(numUids, appUids, SpecialAppOpAdd); 360 } 361 362 int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) { 363 return manipulateNaughtyApps(numUids, appUids, SpecialAppOpRemove); 364 } 365 366 int BandwidthController::addNiceApps(int numUids, char *appUids[]) { 367 return manipulateNiceApps(numUids, appUids, SpecialAppOpAdd); 368 } 369 370 int BandwidthController::removeNiceApps(int numUids, char *appUids[]) { 371 return manipulateNiceApps(numUids, appUids, SpecialAppOpRemove); 372 } 373 374 int BandwidthController::manipulateNaughtyApps(int numUids, char *appStrUids[], SpecialAppOp appOp) { 375 return manipulateSpecialApps(numUids, appStrUids, "bw_penalty_box", IptJumpReject, appOp); 376 } 377 378 int BandwidthController::manipulateNiceApps(int numUids, char *appStrUids[], SpecialAppOp appOp) { 379 return manipulateSpecialApps(numUids, appStrUids, "bw_happy_box", IptJumpReturn, appOp); 380 } 381 382 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 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 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 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 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 */ 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 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 743 int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) { 744 return getInterfaceQuota("shared", bytes); 745 } 746 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 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 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 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 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 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 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 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 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 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 977 int BandwidthController::removeSharedAlert(void) { 978 return removeCostlyAlert("shared", &sharedAlertBytes); 979 } 980 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 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 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 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 1085 /* 1086 * Parse the ptks and bytes out of: 1087 * Chain natctrl_tether_counters (4 references) 1088 * pkts bytes target prot opt in out source destination 1089 * 26 2373 RETURN all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 1090 * 27 2002 RETURN all -- rmnet0 wlan0 0.0.0.0/0 0.0.0.0/0 1091 * 1040 107471 RETURN all -- bt-pan rmnet0 0.0.0.0/0 0.0.0.0/0 1092 * 1450 1708806 RETURN all -- rmnet0 bt-pan 0.0.0.0/0 0.0.0.0/0 1093 * It results in an error if invoked and no tethering counter rules exist. The constraint 1094 * helps detect complete parsing failure. 1095 */ 1096 int BandwidthController::parseForwardChainStats(SocketClient *cli, const TetherStats filter, 1097 FILE *fp, std::string &extraProcessingInfo) { 1098 int res; 1099 char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN]; 1100 char iface0[MAX_IPT_OUTPUT_LINE_LEN]; 1101 char iface1[MAX_IPT_OUTPUT_LINE_LEN]; 1102 char rest[MAX_IPT_OUTPUT_LINE_LEN]; 1103 1104 TetherStats stats; 1105 char *buffPtr; 1106 int64_t packets, bytes; 1107 int statsFound = 0; 1108 1109 bool filterPair = filter.intIface[0] && filter.extIface[0]; 1110 1111 char *filterMsg = filter.getStatsLine(); 1112 ALOGV("filter: %s", filterMsg); 1113 free(filterMsg); 1114 1115 stats = filter; 1116 1117 while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) { 1118 /* Clean up, so a failed parse can still print info */ 1119 iface0[0] = iface1[0] = rest[0] = packets = bytes = 0; 1120 res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all -- %s %s 0.%s", 1121 &packets, &bytes, iface0, iface1, rest); 1122 ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%" PRId64" bytes=%" PRId64" rest=<%s> orig line=<%s>", res, 1123 iface0, iface1, packets, bytes, rest, buffPtr); 1124 extraProcessingInfo += buffPtr; 1125 1126 if (res != 5) { 1127 continue; 1128 } 1129 /* 1130 * The following assumes that the 1st rule has in:extIface out:intIface, 1131 * which is what NatController sets up. 1132 * If not filtering, the 1st match rx, and sets up the pair for the tx side. 1133 */ 1134 if (filter.intIface[0] && filter.extIface[0]) { 1135 if (filter.intIface == iface0 && filter.extIface == iface1) { 1136 ALOGV("2Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 1137 stats.rxPackets = packets; 1138 stats.rxBytes = bytes; 1139 } else if (filter.intIface == iface1 && filter.extIface == iface0) { 1140 ALOGV("2Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 1141 stats.txPackets = packets; 1142 stats.txBytes = bytes; 1143 } 1144 } else if (filter.intIface[0] || filter.extIface[0]) { 1145 if (filter.intIface == iface0 || filter.extIface == iface1) { 1146 ALOGV("1Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 1147 stats.intIface = iface0; 1148 stats.extIface = iface1; 1149 stats.rxPackets = packets; 1150 stats.rxBytes = bytes; 1151 } else if (filter.intIface == iface1 || filter.extIface == iface0) { 1152 ALOGV("1Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 1153 stats.intIface = iface1; 1154 stats.extIface = iface0; 1155 stats.txPackets = packets; 1156 stats.txBytes = bytes; 1157 } 1158 } else /* if (!filter.intFace[0] && !filter.extIface[0]) */ { 1159 if (!stats.intIface[0]) { 1160 ALOGV("0Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 1161 stats.intIface = iface0; 1162 stats.extIface = iface1; 1163 stats.rxPackets = packets; 1164 stats.rxBytes = bytes; 1165 } else if (stats.intIface == iface1 && stats.extIface == iface0) { 1166 ALOGV("0Filter 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 } 1171 if (stats.rxBytes != -1 && stats.txBytes != -1) { 1172 ALOGV("rx_bytes=%" PRId64" tx_bytes=%" PRId64" filterPair=%d", stats.rxBytes, stats.txBytes, filterPair); 1173 /* Send out stats, and prep for the next if needed. */ 1174 char *msg = stats.getStatsLine(); 1175 if (filterPair) { 1176 cli->sendMsg(ResponseCode::TetheringStatsResult, msg, false); 1177 return 0; 1178 } else { 1179 cli->sendMsg(ResponseCode::TetheringStatsListResult, msg, false); 1180 stats = filter; 1181 } 1182 free(msg); 1183 statsFound++; 1184 } 1185 } 1186 1187 /* It is always an error to find only one side of the stats. */ 1188 /* It is an error to find nothing when not filtering. */ 1189 if (((stats.rxBytes == -1) != (stats.txBytes == -1)) || 1190 (!statsFound && !filterPair)) { 1191 return -1; 1192 } 1193 cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false); 1194 return 0; 1195 } 1196 1197 char *BandwidthController::TetherStats::getStatsLine(void) const { 1198 char *msg; 1199 asprintf(&msg, "%s %s %" PRId64" %" PRId64" %" PRId64" %" PRId64, intIface.c_str(), extIface.c_str(), 1200 rxBytes, rxPackets, txBytes, txPackets); 1201 return msg; 1202 } 1203 1204 int BandwidthController::getTetherStats(SocketClient *cli, TetherStats &stats, std::string &extraProcessingInfo) { 1205 int res; 1206 std::string fullCmd; 1207 FILE *iptOutput; 1208 1209 /* 1210 * Why not use some kind of lib to talk to iptables? 1211 * Because the only libs are libiptc and libip6tc in iptables, and they are 1212 * not easy to use. They require the known iptables match modules to be 1213 * preloaded/linked, and require apparently a lot of wrapper code to get 1214 * the wanted info. 1215 */ 1216 fullCmd = IPTABLES_PATH; 1217 fullCmd += " -nvx -w -L "; 1218 fullCmd += NatController::LOCAL_TETHER_COUNTERS_CHAIN; 1219 iptOutput = popenFunction(fullCmd.c_str(), "r"); 1220 if (!iptOutput) { 1221 ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno)); 1222 extraProcessingInfo += "Failed to run iptables."; 1223 return -1; 1224 } 1225 res = parseForwardChainStats(cli, stats, iptOutput, extraProcessingInfo); 1226 pclose(iptOutput); 1227 1228 /* Currently NatController doesn't do ipv6 tethering, so we are done. */ 1229 return res; 1230 } 1231 1232 void BandwidthController::flushExistingCostlyTables(bool doClean) { 1233 std::string fullCmd; 1234 FILE *iptOutput; 1235 1236 /* Only lookup ip4 table names as ip6 will have the same tables ... */ 1237 fullCmd = IPTABLES_PATH; 1238 fullCmd += " -w -S"; 1239 iptOutput = popenFunction(fullCmd.c_str(), "r"); 1240 if (!iptOutput) { 1241 ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno)); 1242 return; 1243 } 1244 /* ... then flush/clean both ip4 and ip6 iptables. */ 1245 parseAndFlushCostlyTables(iptOutput, doClean); 1246 pclose(iptOutput); 1247 } 1248 1249 void BandwidthController::parseAndFlushCostlyTables(FILE *fp, bool doRemove) { 1250 int res; 1251 char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN]; 1252 char costlyIfaceName[MAX_IPT_OUTPUT_LINE_LEN]; 1253 char cmd[MAX_CMD_LEN]; 1254 char *buffPtr; 1255 1256 while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) { 1257 costlyIfaceName[0] = '\0'; /* So that debugging output always works */ 1258 res = sscanf(buffPtr, "-N bw_costly_%s", costlyIfaceName); 1259 ALOGV("parse res=%d costly=<%s> orig line=<%s>", res, 1260 costlyIfaceName, buffPtr); 1261 if (res != 1) { 1262 continue; 1263 } 1264 /* Exclusions: "shared" is not an ifacename */ 1265 if (!strcmp(costlyIfaceName, "shared")) { 1266 continue; 1267 } 1268 1269 snprintf(cmd, sizeof(cmd), "-F bw_costly_%s", costlyIfaceName); 1270 runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide); 1271 if (doRemove) { 1272 snprintf(cmd, sizeof(cmd), "-X bw_costly_%s", costlyIfaceName); 1273 runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide); 1274 } 1275 } 1276 } 1277