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 <ctype.h> 26 #include <errno.h> 27 #include <fcntl.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <string> 32 #include <vector> 33 34 #define __STDC_FORMAT_MACROS 1 35 #include <inttypes.h> 36 37 #include <sys/socket.h> 38 #include <sys/stat.h> 39 #include <sys/types.h> 40 #include <sys/wait.h> 41 42 #include <linux/netlink.h> 43 #include <linux/rtnetlink.h> 44 #include <linux/pkt_sched.h> 45 46 #include "android-base/stringprintf.h" 47 #include "android-base/strings.h" 48 #define LOG_TAG "BandwidthController" 49 #include <cutils/log.h> 50 #include <cutils/properties.h> 51 #include <logwrap/logwrap.h> 52 53 #include <netdutils/Syscalls.h> 54 #include "BandwidthController.h" 55 #include "FirewallController.h" /* For makeCriticalCommands */ 56 #include "NatController.h" /* For LOCAL_TETHER_COUNTERS_CHAIN */ 57 #include "NetdConstants.h" 58 #include "ResponseCode.h" 59 60 /* Alphabetical */ 61 #define ALERT_IPT_TEMPLATE "%s %s -m quota2 ! --quota %" PRId64" --name %s\n" 62 const char BandwidthController::LOCAL_INPUT[] = "bw_INPUT"; 63 const char BandwidthController::LOCAL_FORWARD[] = "bw_FORWARD"; 64 const char BandwidthController::LOCAL_OUTPUT[] = "bw_OUTPUT"; 65 const char BandwidthController::LOCAL_RAW_PREROUTING[] = "bw_raw_PREROUTING"; 66 const char BandwidthController::LOCAL_MANGLE_POSTROUTING[] = "bw_mangle_POSTROUTING"; 67 68 auto BandwidthController::iptablesRestoreFunction = execIptablesRestoreWithOutput; 69 70 using android::base::Join; 71 using android::base::StringAppendF; 72 using android::base::StringPrintf; 73 using android::netdutils::StatusOr; 74 using android::netdutils::UniqueFile; 75 76 namespace { 77 78 const char ALERT_GLOBAL_NAME[] = "globalAlert"; 79 const int MAX_IPT_OUTPUT_LINE_LEN = 256; 80 const std::string NEW_CHAIN_COMMAND = "-N "; 81 const std::string GET_TETHER_STATS_COMMAND = StringPrintf( 82 "*filter\n" 83 "-nvx -L %s\n" 84 "COMMIT\n", NatController::LOCAL_TETHER_COUNTERS_CHAIN); 85 86 const char NAUGHTY_CHAIN[] = "bw_penalty_box"; 87 const char NICE_CHAIN[] = "bw_happy_box"; 88 89 /** 90 * Some comments about the rules: 91 * * Ordering 92 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains. 93 * E.g. "-I bw_INPUT -i rmnet0 --jump costly" 94 * - quota'd rules in the costly chain should be before bw_penalty_box lookups. 95 * - the qtaguid counting is done at the end of the bw_INPUT/bw_OUTPUT user chains. 96 * 97 * * global quota vs per interface quota 98 * - global quota for all costly interfaces uses a single costly chain: 99 * . initial rules 100 * iptables -N bw_costly_shared 101 * iptables -I bw_INPUT -i iface0 --jump bw_costly_shared 102 * iptables -I bw_OUTPUT -o iface0 --jump bw_costly_shared 103 * iptables -I bw_costly_shared -m quota \! --quota 500000 \ 104 * --jump REJECT --reject-with icmp-net-prohibited 105 * iptables -A bw_costly_shared --jump bw_penalty_box 106 * iptables -A bw_penalty_box --jump bw_happy_box 107 * iptables -A bw_happy_box --jump bw_data_saver 108 * 109 * . adding a new iface to this, E.g.: 110 * iptables -I bw_INPUT -i iface1 --jump bw_costly_shared 111 * iptables -I bw_OUTPUT -o iface1 --jump bw_costly_shared 112 * 113 * - quota per interface. This is achieve by having "costly" chains per quota. 114 * E.g. adding a new costly interface iface0 with its own quota: 115 * iptables -N bw_costly_iface0 116 * iptables -I bw_INPUT -i iface0 --jump bw_costly_iface0 117 * iptables -I bw_OUTPUT -o iface0 --jump bw_costly_iface0 118 * iptables -A bw_costly_iface0 -m quota \! --quota 500000 \ 119 * --jump REJECT --reject-with icmp-port-unreachable 120 * iptables -A bw_costly_iface0 --jump bw_penalty_box 121 * 122 * * Penalty box, happy box and data saver. 123 * - bw_penalty box is a blacklist of apps that are rejected. 124 * - bw_happy_box is a whitelist of apps. It always includes all system apps 125 * - bw_data_saver implements data usage restrictions. 126 * - Via the UI the user can add and remove apps from the whitelist and 127 * blacklist, and turn on/off data saver. 128 * - The blacklist takes precedence over the whitelist and the whitelist 129 * takes precedence over data saver. 130 * 131 * * bw_penalty_box handling: 132 * - only one bw_penalty_box for all interfaces 133 * E.g Adding an app: 134 * iptables -I bw_penalty_box -m owner --uid-owner app_3 \ 135 * --jump REJECT --reject-with icmp-port-unreachable 136 * 137 * * bw_happy_box handling: 138 * - The bw_happy_box comes after the penalty box. 139 * E.g Adding a happy app, 140 * iptables -I bw_happy_box -m owner --uid-owner app_3 \ 141 * --jump RETURN 142 * 143 * * bw_data_saver handling: 144 * - The bw_data_saver comes after the happy box. 145 * Enable data saver: 146 * iptables -R 1 bw_data_saver --jump REJECT --reject-with icmp-port-unreachable 147 * Disable data saver: 148 * iptables -R 1 bw_data_saver --jump RETURN 149 */ 150 151 const std::string COMMIT_AND_CLOSE = "COMMIT\n"; 152 const 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 155 static 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 178 static 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 std::vector<std::string> toStrVec(int num, char* strs[]) { 199 std::vector<std::string> tmp; 200 for (int i = 0; i < num; ++i) { 201 tmp.emplace_back(strs[i]); 202 } 203 return tmp; 204 } 205 206 } // namespace 207 208 BandwidthController::BandwidthController() { 209 } 210 211 void BandwidthController::flushCleanTables(bool doClean) { 212 /* Flush and remove the bw_costly_<iface> tables */ 213 flushExistingCostlyTables(doClean); 214 215 std::string commands = Join(IPT_FLUSH_COMMANDS, '\n'); 216 iptablesRestoreFunction(V4V6, commands, nullptr); 217 } 218 219 int BandwidthController::setupIptablesHooks() { 220 /* flush+clean is allowed to fail */ 221 flushCleanTables(true); 222 return 0; 223 } 224 225 int BandwidthController::enableBandwidthControl(bool force) { 226 char value[PROPERTY_VALUE_MAX]; 227 228 if (!force) { 229 property_get("persist.bandwidth.enable", value, "1"); 230 if (!strcmp(value, "0")) 231 return 0; 232 } 233 234 /* Let's pretend we started from scratch ... */ 235 mSharedQuotaIfaces.clear(); 236 mQuotaIfaces.clear(); 237 mGlobalAlertBytes = 0; 238 mGlobalAlertTetherCount = 0; 239 mSharedQuotaBytes = mSharedAlertBytes = 0; 240 241 flushCleanTables(false); 242 std::string commands = Join(IPT_BASIC_ACCOUNTING_COMMANDS, '\n'); 243 return iptablesRestoreFunction(V4V6, commands, nullptr); 244 } 245 246 int BandwidthController::disableBandwidthControl() { 247 248 flushCleanTables(false); 249 return 0; 250 } 251 252 std::string BandwidthController::makeDataSaverCommand(IptablesTarget target, bool enable) { 253 std::string cmd; 254 const char *chainName = "bw_data_saver"; 255 const char *op = jumpToString(enable ? IptJumpReject : IptJumpReturn); 256 std::string criticalCommands = enable ? 257 FirewallController::makeCriticalCommands(target, chainName) : ""; 258 StringAppendF(&cmd, 259 "*filter\n" 260 ":%s -\n" 261 "%s" 262 "-A %s%s\n" 263 "COMMIT\n", chainName, criticalCommands.c_str(), chainName, op); 264 return cmd; 265 } 266 267 int BandwidthController::enableDataSaver(bool enable) { 268 int ret = iptablesRestoreFunction(V4, makeDataSaverCommand(V4, enable), nullptr); 269 ret |= iptablesRestoreFunction(V6, makeDataSaverCommand(V6, enable), nullptr); 270 return ret; 271 } 272 273 int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) { 274 return manipulateSpecialApps(toStrVec(numUids, appUids), NAUGHTY_CHAIN, 275 IptJumpReject, IptOpInsert); 276 } 277 278 int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) { 279 return manipulateSpecialApps(toStrVec(numUids, appUids), NAUGHTY_CHAIN, 280 IptJumpReject, IptOpDelete); 281 } 282 283 int BandwidthController::addNiceApps(int numUids, char *appUids[]) { 284 return manipulateSpecialApps(toStrVec(numUids, appUids), NICE_CHAIN, 285 IptJumpReturn, IptOpInsert); 286 } 287 288 int BandwidthController::removeNiceApps(int numUids, char *appUids[]) { 289 return manipulateSpecialApps(toStrVec(numUids, appUids), NICE_CHAIN, 290 IptJumpReturn, IptOpDelete); 291 } 292 293 int BandwidthController::manipulateSpecialApps(const std::vector<std::string>& appStrUids, 294 const std::string& chain, IptJumpOp jumpHandling, 295 IptOp op) { 296 std::string cmd = "*filter\n"; 297 for (const auto& appStrUid : appStrUids) { 298 StringAppendF(&cmd, "%s %s -m owner --uid-owner %s%s\n", opToString(op), chain.c_str(), 299 appStrUid.c_str(), jumpToString(jumpHandling)); 300 } 301 StringAppendF(&cmd, "COMMIT\n"); 302 return iptablesRestoreFunction(V4V6, cmd, nullptr); 303 } 304 305 int BandwidthController::setInterfaceSharedQuota(const std::string& iface, int64_t maxBytes) { 306 int res = 0; 307 std::string quotaCmd; 308 constexpr char cost[] = "shared"; 309 constexpr char chain[] = "bw_costly_shared"; 310 311 if (!maxBytes) { 312 /* Don't talk about -1, deprecate it. */ 313 ALOGE("Invalid bytes value. 1..max_int64."); 314 return -1; 315 } 316 if (!isIfaceName(iface)) 317 return -1; 318 319 if (maxBytes == -1) { 320 return removeInterfaceSharedQuota(iface); 321 } 322 323 auto it = mSharedQuotaIfaces.find(iface); 324 325 if (it == mSharedQuotaIfaces.end()) { 326 const int ruleInsertPos = (mGlobalAlertBytes) ? 2 : 1; 327 std::vector<std::string> cmds = { 328 "*filter", 329 StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, iface.c_str(), chain), 330 StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, iface.c_str(), chain), 331 StringPrintf("-A bw_FORWARD -o %s --jump %s", iface.c_str(), chain), 332 }; 333 if (mSharedQuotaIfaces.empty()) { 334 cmds.push_back(StringPrintf("-I %s -m quota2 ! --quota %" PRId64 335 " --name %s --jump REJECT", 336 chain, maxBytes, cost)); 337 } 338 cmds.push_back("COMMIT\n"); 339 340 res |= iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr); 341 if (res) { 342 ALOGE("Failed set quota rule"); 343 removeInterfaceSharedQuota(iface); 344 return -1; 345 } 346 mSharedQuotaBytes = maxBytes; 347 mSharedQuotaIfaces.insert(iface); 348 } 349 350 if (maxBytes != mSharedQuotaBytes) { 351 res |= updateQuota(cost, maxBytes); 352 if (res) { 353 ALOGE("Failed update quota for %s", cost); 354 removeInterfaceSharedQuota(iface); 355 return -1; 356 } 357 mSharedQuotaBytes = maxBytes; 358 } 359 return 0; 360 } 361 362 /* It will also cleanup any shared alerts */ 363 int BandwidthController::removeInterfaceSharedQuota(const std::string& iface) { 364 constexpr char cost[] = "shared"; 365 constexpr char chain[] = "bw_costly_shared"; 366 367 if (!isIfaceName(iface)) 368 return -1; 369 370 auto it = mSharedQuotaIfaces.find(iface); 371 372 if (it == mSharedQuotaIfaces.end()) { 373 ALOGE("No such iface %s to delete", iface.c_str()); 374 return -1; 375 } 376 377 std::vector<std::string> cmds = { 378 "*filter", 379 StringPrintf("-D bw_INPUT -i %s --jump %s", iface.c_str(), chain), 380 StringPrintf("-D bw_OUTPUT -o %s --jump %s", iface.c_str(), chain), 381 StringPrintf("-D bw_FORWARD -o %s --jump %s", iface.c_str(), chain), 382 }; 383 if (mSharedQuotaIfaces.size() == 1) { 384 cmds.push_back(StringPrintf("-D %s -m quota2 ! --quota %" PRIu64 385 " --name %s --jump REJECT", 386 chain, mSharedQuotaBytes, cost)); 387 } 388 cmds.push_back("COMMIT\n"); 389 390 if (iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr) != 0) { 391 ALOGE("Failed to remove shared quota on %s", iface.c_str()); 392 return -1; 393 } 394 395 int res = 0; 396 mSharedQuotaIfaces.erase(it); 397 if (mSharedQuotaIfaces.empty()) { 398 mSharedQuotaBytes = 0; 399 if (mSharedAlertBytes) { 400 res = removeSharedAlert(); 401 if (res == 0) { 402 mSharedAlertBytes = 0; 403 } 404 } 405 } 406 407 return res; 408 409 } 410 411 int BandwidthController::setInterfaceQuota(const std::string& iface, int64_t maxBytes) { 412 const std::string& cost = iface; 413 414 if (!isIfaceName(iface)) 415 return -1; 416 417 if (!maxBytes) { 418 /* Don't talk about -1, deprecate it. */ 419 ALOGE("Invalid bytes value. 1..max_int64."); 420 return -1; 421 } 422 if (maxBytes == -1) { 423 return removeInterfaceQuota(iface); 424 } 425 426 /* Insert ingress quota. */ 427 auto it = mQuotaIfaces.find(iface); 428 429 if (it != mQuotaIfaces.end()) { 430 if (updateQuota(cost, maxBytes) != 0) { 431 ALOGE("Failed update quota for %s", iface.c_str()); 432 removeInterfaceQuota(iface); 433 return -1; 434 } 435 it->second.quota = maxBytes; 436 return 0; 437 } 438 439 const std::string chain = "bw_costly_" + iface; 440 const int ruleInsertPos = (mGlobalAlertBytes) ? 2 : 1; 441 std::vector<std::string> cmds = { 442 "*filter", 443 StringPrintf(":%s -", chain.c_str()), 444 StringPrintf("-A %s -j bw_penalty_box", chain.c_str()), 445 StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, iface.c_str(), 446 chain.c_str()), 447 StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, iface.c_str(), 448 chain.c_str()), 449 StringPrintf("-A bw_FORWARD -o %s --jump %s", iface.c_str(), chain.c_str()), 450 StringPrintf("-A %s -m quota2 ! --quota %" PRId64 " --name %s --jump REJECT", 451 chain.c_str(), maxBytes, cost.c_str()), 452 "COMMIT\n", 453 }; 454 455 if (iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr) != 0) { 456 ALOGE("Failed set quota rule"); 457 removeInterfaceQuota(iface); 458 return -1; 459 } 460 461 mQuotaIfaces[iface] = QuotaInfo{maxBytes, 0}; 462 return 0; 463 } 464 465 int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) { 466 return getInterfaceQuota("shared", bytes); 467 } 468 469 int BandwidthController::getInterfaceQuota(const std::string& iface, int64_t* bytes) { 470 const auto& sys = android::netdutils::sSyscalls.get(); 471 const std::string fname = "/proc/net/xt_quota/" + iface; 472 473 if (!isIfaceName(iface)) return -1; 474 475 StatusOr<UniqueFile> file = sys.fopen(fname, "re"); 476 if (!isOk(file)) { 477 ALOGE("Reading quota %s failed (%s)", iface.c_str(), toString(file).c_str()); 478 return -1; 479 } 480 auto rv = sys.fscanf(file.value().get(), "%" SCNd64, bytes); 481 if (!isOk(rv)) { 482 ALOGE("Reading quota %s failed (%s)", iface.c_str(), toString(rv).c_str()); 483 return -1; 484 } 485 ALOGV("Read quota res=%d bytes=%" PRId64, rv.value(), *bytes); 486 return rv.value() == 1 ? 0 : -1; 487 } 488 489 int BandwidthController::removeInterfaceQuota(const std::string& iface) { 490 if (!isIfaceName(iface)) 491 return -1; 492 493 auto it = mQuotaIfaces.find(iface); 494 495 if (it == mQuotaIfaces.end()) { 496 ALOGE("No such iface %s to delete", iface.c_str()); 497 return -1; 498 } 499 500 const std::string chain = "bw_costly_" + iface; 501 std::vector<std::string> cmds = { 502 "*filter", 503 StringPrintf("-D bw_INPUT -i %s --jump %s", iface.c_str(), chain.c_str()), 504 StringPrintf("-D bw_OUTPUT -o %s --jump %s", iface.c_str(), chain.c_str()), 505 StringPrintf("-D bw_FORWARD -o %s --jump %s", iface.c_str(), chain.c_str()), 506 StringPrintf("-F %s", chain.c_str()), 507 StringPrintf("-X %s", chain.c_str()), 508 "COMMIT\n", 509 }; 510 511 const int res = iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr); 512 513 if (res == 0) { 514 mQuotaIfaces.erase(it); 515 } 516 517 return res; 518 } 519 520 int BandwidthController::updateQuota(const std::string& quotaName, int64_t bytes) { 521 const auto& sys = android::netdutils::sSyscalls.get(); 522 const std::string fname = "/proc/net/xt_quota/" + quotaName; 523 524 if (!isIfaceName(quotaName)) { 525 ALOGE("updateQuota: Invalid quotaName \"%s\"", quotaName.c_str()); 526 return -1; 527 } 528 529 StatusOr<UniqueFile> file = sys.fopen(fname, "we"); 530 if (!isOk(file)) { 531 ALOGE("Updating quota %s failed (%s)", quotaName.c_str(), toString(file).c_str()); 532 return -1; 533 } 534 sys.fprintf(file.value().get(), "%" PRId64 "\n", bytes); 535 return 0; 536 } 537 538 int BandwidthController::runIptablesAlertCmd(IptOp op, const std::string& alertName, 539 int64_t bytes) { 540 const char *opFlag = opToString(op); 541 std::string alertQuotaCmd = "*filter\n"; 542 543 // TODO: consider using an alternate template for the delete that does not include the --quota 544 // value. This code works because the --quota value is ignored by deletes 545 StringAppendF(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_INPUT", bytes, 546 alertName.c_str()); 547 StringAppendF(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_OUTPUT", bytes, 548 alertName.c_str()); 549 StringAppendF(&alertQuotaCmd, "COMMIT\n"); 550 551 return iptablesRestoreFunction(V4V6, alertQuotaCmd, nullptr); 552 } 553 554 int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const std::string& alertName, 555 int64_t bytes) { 556 const char *opFlag = opToString(op); 557 std::string alertQuotaCmd = "*filter\n"; 558 StringAppendF(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_FORWARD", bytes, 559 alertName.c_str()); 560 StringAppendF(&alertQuotaCmd, "COMMIT\n"); 561 562 return iptablesRestoreFunction(V4V6, alertQuotaCmd, nullptr); 563 } 564 565 int BandwidthController::setGlobalAlert(int64_t bytes) { 566 const char *alertName = ALERT_GLOBAL_NAME; 567 int res = 0; 568 569 if (!bytes) { 570 ALOGE("Invalid bytes value. 1..max_int64."); 571 return -1; 572 } 573 if (mGlobalAlertBytes) { 574 res = updateQuota(alertName, bytes); 575 } else { 576 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes); 577 if (mGlobalAlertTetherCount) { 578 ALOGV("setGlobalAlert for %d tether", mGlobalAlertTetherCount); 579 res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes); 580 } 581 } 582 mGlobalAlertBytes = bytes; 583 return res; 584 } 585 586 int BandwidthController::setGlobalAlertInForwardChain() { 587 const char *alertName = ALERT_GLOBAL_NAME; 588 int res = 0; 589 590 mGlobalAlertTetherCount++; 591 ALOGV("setGlobalAlertInForwardChain(): %d tether", mGlobalAlertTetherCount); 592 593 /* 594 * If there is no globalAlert active we are done. 595 * If there is an active globalAlert but this is not the 1st 596 * tether, we are also done. 597 */ 598 if (!mGlobalAlertBytes || mGlobalAlertTetherCount != 1) { 599 return 0; 600 } 601 602 /* We only add the rule if this was the 1st tether added. */ 603 res = runIptablesAlertFwdCmd(IptOpInsert, alertName, mGlobalAlertBytes); 604 return res; 605 } 606 607 int BandwidthController::removeGlobalAlert() { 608 609 const char *alertName = ALERT_GLOBAL_NAME; 610 int res = 0; 611 612 if (!mGlobalAlertBytes) { 613 ALOGE("No prior alert set"); 614 return -1; 615 } 616 res = runIptablesAlertCmd(IptOpDelete, alertName, mGlobalAlertBytes); 617 if (mGlobalAlertTetherCount) { 618 res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, mGlobalAlertBytes); 619 } 620 mGlobalAlertBytes = 0; 621 return res; 622 } 623 624 int BandwidthController::removeGlobalAlertInForwardChain() { 625 int res = 0; 626 const char *alertName = ALERT_GLOBAL_NAME; 627 628 if (!mGlobalAlertTetherCount) { 629 ALOGE("No prior alert set"); 630 return -1; 631 } 632 633 mGlobalAlertTetherCount--; 634 /* 635 * If there is no globalAlert active we are done. 636 * If there is an active globalAlert but there are more 637 * tethers, we are also done. 638 */ 639 if (!mGlobalAlertBytes || mGlobalAlertTetherCount >= 1) { 640 return 0; 641 } 642 643 /* We only detete the rule if this was the last tether removed. */ 644 res = runIptablesAlertFwdCmd(IptOpDelete, alertName, mGlobalAlertBytes); 645 return res; 646 } 647 648 int BandwidthController::setSharedAlert(int64_t bytes) { 649 if (!mSharedQuotaBytes) { 650 ALOGE("Need to have a prior shared quota set to set an alert"); 651 return -1; 652 } 653 if (!bytes) { 654 ALOGE("Invalid bytes value. 1..max_int64."); 655 return -1; 656 } 657 return setCostlyAlert("shared", bytes, &mSharedAlertBytes); 658 } 659 660 int BandwidthController::removeSharedAlert() { 661 return removeCostlyAlert("shared", &mSharedAlertBytes); 662 } 663 664 int BandwidthController::setInterfaceAlert(const std::string& iface, int64_t bytes) { 665 if (!isIfaceName(iface)) { 666 ALOGE("setInterfaceAlert: Invalid iface \"%s\"", iface.c_str()); 667 return -1; 668 } 669 670 if (!bytes) { 671 ALOGE("Invalid bytes value. 1..max_int64."); 672 return -1; 673 } 674 auto it = mQuotaIfaces.find(iface); 675 676 if (it == mQuotaIfaces.end()) { 677 ALOGE("Need to have a prior interface quota set to set an alert"); 678 return -1; 679 } 680 681 return setCostlyAlert(iface, bytes, &it->second.alert); 682 } 683 684 int BandwidthController::removeInterfaceAlert(const std::string& iface) { 685 if (!isIfaceName(iface)) { 686 ALOGE("removeInterfaceAlert: Invalid iface \"%s\"", iface.c_str()); 687 return -1; 688 } 689 690 auto it = mQuotaIfaces.find(iface); 691 692 if (it == mQuotaIfaces.end()) { 693 ALOGE("No prior alert set for interface %s", iface.c_str()); 694 return -1; 695 } 696 697 return removeCostlyAlert(iface, &it->second.alert); 698 } 699 700 int BandwidthController::setCostlyAlert(const std::string& costName, int64_t bytes, 701 int64_t* alertBytes) { 702 int res = 0; 703 704 if (!isIfaceName(costName)) { 705 ALOGE("setCostlyAlert: Invalid costName \"%s\"", costName.c_str()); 706 return -1; 707 } 708 709 if (!bytes) { 710 ALOGE("Invalid bytes value. 1..max_int64."); 711 return -1; 712 } 713 714 std::string alertName = costName + "Alert"; 715 std::string chainName = "bw_costly_" + costName; 716 if (*alertBytes) { 717 res = updateQuota(alertName, *alertBytes); 718 } else { 719 std::vector<std::string> commands = { 720 "*filter\n", 721 StringPrintf(ALERT_IPT_TEMPLATE, "-A", chainName.c_str(), bytes, alertName.c_str()), 722 "COMMIT\n" 723 }; 724 res = iptablesRestoreFunction(V4V6, Join(commands, ""), nullptr); 725 if (res) { 726 ALOGE("Failed to set costly alert for %s", costName.c_str()); 727 } 728 } 729 if (res == 0) { 730 *alertBytes = bytes; 731 } 732 return res; 733 } 734 735 int BandwidthController::removeCostlyAlert(const std::string& costName, int64_t* alertBytes) { 736 if (!isIfaceName(costName)) { 737 ALOGE("removeCostlyAlert: Invalid costName \"%s\"", costName.c_str()); 738 return -1; 739 } 740 741 if (!*alertBytes) { 742 ALOGE("No prior alert set for %s alert", costName.c_str()); 743 return -1; 744 } 745 746 std::string alertName = costName + "Alert"; 747 std::string chainName = "bw_costly_" + costName; 748 std::vector<std::string> commands = { 749 "*filter\n", 750 StringPrintf(ALERT_IPT_TEMPLATE, "-D", chainName.c_str(), *alertBytes, alertName.c_str()), 751 "COMMIT\n" 752 }; 753 if (iptablesRestoreFunction(V4V6, Join(commands, ""), nullptr) != 0) { 754 ALOGE("Failed to remove costly alert %s", costName.c_str()); 755 return -1; 756 } 757 758 *alertBytes = 0; 759 return 0; 760 } 761 762 void BandwidthController::addStats(TetherStatsList& statsList, const TetherStats& stats) { 763 for (TetherStats& existing : statsList) { 764 if (existing.addStatsIfMatch(stats)) { 765 return; 766 } 767 } 768 // No match. Insert a new interface pair. 769 statsList.push_back(stats); 770 } 771 772 /* 773 * Parse the ptks and bytes out of: 774 * Chain natctrl_tether_counters (4 references) 775 * pkts bytes target prot opt in out source destination 776 * 26 2373 RETURN all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 777 * 27 2002 RETURN all -- rmnet0 wlan0 0.0.0.0/0 0.0.0.0/0 778 * 1040 107471 RETURN all -- bt-pan rmnet0 0.0.0.0/0 0.0.0.0/0 779 * 1450 1708806 RETURN all -- rmnet0 bt-pan 0.0.0.0/0 0.0.0.0/0 780 * or: 781 * Chain natctrl_tether_counters (0 references) 782 * pkts bytes target prot opt in out source destination 783 * 0 0 RETURN all wlan0 rmnet_data0 ::/0 ::/0 784 * 0 0 RETURN all rmnet_data0 wlan0 ::/0 ::/0 785 * 786 * It results in an error if invoked and no tethering counter rules exist. The constraint 787 * helps detect complete parsing failure. 788 */ 789 int BandwidthController::addForwardChainStats(const TetherStats& filter, 790 TetherStatsList& statsList, 791 const std::string& statsOutput, 792 std::string &extraProcessingInfo) { 793 int res; 794 std::string statsLine; 795 char iface0[MAX_IPT_OUTPUT_LINE_LEN]; 796 char iface1[MAX_IPT_OUTPUT_LINE_LEN]; 797 char rest[MAX_IPT_OUTPUT_LINE_LEN]; 798 799 TetherStats stats; 800 const char *buffPtr; 801 int64_t packets, bytes; 802 int statsFound = 0; 803 804 bool filterPair = filter.intIface[0] && filter.extIface[0]; 805 806 ALOGV("filter: %s", filter.getStatsLine().c_str()); 807 808 stats = filter; 809 810 std::stringstream stream(statsOutput); 811 while (std::getline(stream, statsLine, '\n')) { 812 buffPtr = statsLine.c_str(); 813 814 /* Clean up, so a failed parse can still print info */ 815 iface0[0] = iface1[0] = rest[0] = packets = bytes = 0; 816 if (strstr(buffPtr, "0.0.0.0")) { 817 // IPv4 has -- indicating what to do with fragments... 818 // 26 2373 RETURN all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 819 res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all -- %s %s 0.%s", 820 &packets, &bytes, iface0, iface1, rest); 821 } else { 822 // ... but IPv6 does not. 823 // 26 2373 RETURN all wlan0 rmnet0 ::/0 ::/0 824 res = sscanf(buffPtr, "%" SCNd64" %" SCNd64" RETURN all %s %s ::/%s", 825 &packets, &bytes, iface0, iface1, rest); 826 } 827 ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%" PRId64" bytes=%" PRId64" rest=<%s> orig line=<%s>", res, 828 iface0, iface1, packets, bytes, rest, buffPtr); 829 extraProcessingInfo += buffPtr; 830 extraProcessingInfo += "\n"; 831 832 if (res != 5) { 833 continue; 834 } 835 /* 836 * The following assumes that the 1st rule has in:extIface out:intIface, 837 * which is what NatController sets up. 838 * If not filtering, the 1st match rx, and sets up the pair for the tx side. 839 */ 840 if (filter.intIface[0] && filter.extIface[0]) { 841 if (filter.intIface == iface0 && filter.extIface == iface1) { 842 ALOGV("2Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 843 stats.rxPackets = packets; 844 stats.rxBytes = bytes; 845 } else if (filter.intIface == iface1 && filter.extIface == iface0) { 846 ALOGV("2Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 847 stats.txPackets = packets; 848 stats.txBytes = bytes; 849 } 850 } else if (filter.intIface[0] || filter.extIface[0]) { 851 if (filter.intIface == iface0 || filter.extIface == iface1) { 852 ALOGV("1Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 853 stats.intIface = iface0; 854 stats.extIface = iface1; 855 stats.rxPackets = packets; 856 stats.rxBytes = bytes; 857 } else if (filter.intIface == iface1 || filter.extIface == iface0) { 858 ALOGV("1Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 859 stats.intIface = iface1; 860 stats.extIface = iface0; 861 stats.txPackets = packets; 862 stats.txBytes = bytes; 863 } 864 } else /* if (!filter.intFace[0] && !filter.extIface[0]) */ { 865 if (!stats.intIface[0]) { 866 ALOGV("0Filter RX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 867 stats.intIface = iface0; 868 stats.extIface = iface1; 869 stats.rxPackets = packets; 870 stats.rxBytes = bytes; 871 } else if (stats.intIface == iface1 && stats.extIface == iface0) { 872 ALOGV("0Filter TX iface_in=%s iface_out=%s rx_bytes=%" PRId64" rx_packets=%" PRId64" ", iface0, iface1, bytes, packets); 873 stats.txPackets = packets; 874 stats.txBytes = bytes; 875 } 876 } 877 if (stats.rxBytes != -1 && stats.txBytes != -1) { 878 ALOGV("rx_bytes=%" PRId64" tx_bytes=%" PRId64" filterPair=%d", stats.rxBytes, stats.txBytes, filterPair); 879 addStats(statsList, stats); 880 if (filterPair) { 881 return 0; 882 } else { 883 statsFound++; 884 stats = filter; 885 } 886 } 887 } 888 889 /* It is always an error to find only one side of the stats. */ 890 /* It is an error to find nothing when not filtering. */ 891 if (((stats.rxBytes == -1) != (stats.txBytes == -1)) || 892 (!statsFound && !filterPair)) { 893 return -1; 894 } 895 return 0; 896 } 897 898 std::string BandwidthController::TetherStats::getStatsLine() const { 899 std::string msg; 900 StringAppendF(&msg, "%s %s %" PRId64" %" PRId64" %" PRId64" %" PRId64, intIface.c_str(), 901 extIface.c_str(), rxBytes, rxPackets, txBytes, txPackets); 902 return msg; 903 } 904 905 int BandwidthController::getTetherStats(SocketClient *cli, TetherStats& filter, 906 std::string &extraProcessingInfo) { 907 int res = 0; 908 909 TetherStatsList statsList; 910 911 for (const IptablesTarget target : {V4, V6}) { 912 std::string statsString; 913 res = iptablesRestoreFunction(target, GET_TETHER_STATS_COMMAND, &statsString); 914 if (res != 0) { 915 ALOGE("Failed to run %s err=%d", GET_TETHER_STATS_COMMAND.c_str(), res); 916 return -1; 917 } 918 919 res = addForwardChainStats(filter, statsList, statsString, extraProcessingInfo); 920 if (res != 0) { 921 return res; 922 } 923 } 924 925 if (filter.intIface[0] && filter.extIface[0] && statsList.size() == 1) { 926 cli->sendMsg(ResponseCode::TetheringStatsResult, 927 statsList[0].getStatsLine().c_str(), false); 928 } else { 929 for (const auto& stats: statsList) { 930 cli->sendMsg(ResponseCode::TetheringStatsListResult, 931 stats.getStatsLine().c_str(), false); 932 } 933 if (res == 0) { 934 cli->sendMsg(ResponseCode::CommandOkay, "Tethering stats list completed", false); 935 } 936 } 937 938 return res; 939 } 940 941 void BandwidthController::flushExistingCostlyTables(bool doClean) { 942 std::string fullCmd = "*filter\n-S\nCOMMIT\n"; 943 std::string ruleList; 944 945 /* Only lookup ip4 table names as ip6 will have the same tables ... */ 946 if (int ret = iptablesRestoreFunction(V4, fullCmd, &ruleList)) { 947 ALOGE("Failed to list existing costly tables ret=%d", ret); 948 return; 949 } 950 /* ... then flush/clean both ip4 and ip6 iptables. */ 951 parseAndFlushCostlyTables(ruleList, doClean); 952 } 953 954 void BandwidthController::parseAndFlushCostlyTables(const std::string& ruleList, bool doRemove) { 955 std::stringstream stream(ruleList); 956 std::string rule; 957 std::vector<std::string> clearCommands = { "*filter" }; 958 std::string chainName; 959 960 // Find and flush all rules starting with "-N bw_costly_<iface>" except "-N bw_costly_shared". 961 while (std::getline(stream, rule, '\n')) { 962 if (rule.find(NEW_CHAIN_COMMAND) != 0) continue; 963 chainName = rule.substr(NEW_CHAIN_COMMAND.size()); 964 ALOGV("parse chainName=<%s> orig line=<%s>", chainName.c_str(), rule.c_str()); 965 966 if (chainName.find("bw_costly_") != 0 || chainName == std::string("bw_costly_shared")) { 967 continue; 968 } 969 970 clearCommands.push_back(StringPrintf(":%s -", chainName.c_str())); 971 if (doRemove) { 972 clearCommands.push_back(StringPrintf("-X %s", chainName.c_str())); 973 } 974 } 975 976 if (clearCommands.size() == 1) { 977 // No rules found. 978 return; 979 } 980 981 clearCommands.push_back("COMMIT\n"); 982 iptablesRestoreFunction(V4V6, Join(clearCommands, '\n'), nullptr); 983 } 984 985 inline const char *BandwidthController::opToString(IptOp op) { 986 switch (op) { 987 case IptOpInsert: 988 return "-I"; 989 case IptOpDelete: 990 return "-D"; 991 } 992 } 993 994 inline const char *BandwidthController::jumpToString(IptJumpOp jumpHandling) { 995 /* 996 * Must be careful what one rejects with, as upper layer protocols will just 997 * keep on hammering the device until the number of retries are done. 998 * For port-unreachable (default), TCP should consider as an abort (RFC1122). 999 */ 1000 switch (jumpHandling) { 1001 case IptJumpNoAdd: 1002 return ""; 1003 case IptJumpReject: 1004 return " --jump REJECT"; 1005 case IptJumpReturn: 1006 return " --jump RETURN"; 1007 } 1008 } 1009