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