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