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