1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // #define LOG_NDEBUG 0 18 19 /* 20 * The CommandListener, FrameworkListener don't allow for 21 * multiple calls in parallel to reach the BandwidthController. 22 * If they ever were to allow it, then netd/ would need some tweaking. 23 */ 24 25 #include <errno.h> 26 #include <fcntl.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 31 #include <sys/socket.h> 32 #include <sys/stat.h> 33 #include <sys/types.h> 34 #include <sys/wait.h> 35 36 #include <linux/netlink.h> 37 #include <linux/rtnetlink.h> 38 #include <linux/pkt_sched.h> 39 40 #define LOG_TAG "BandwidthController" 41 #include <cutils/log.h> 42 #include <cutils/properties.h> 43 44 extern "C" int logwrap(int argc, const char **argv); 45 extern "C" int system_nosh(const char *command); 46 47 #include "NetdConstants.h" 48 #include "BandwidthController.h" 49 50 /* Alphabetical */ 51 #define ALERT_IPT_TEMPLATE "%s %s %s -m quota2 ! --quota %lld --name %s" 52 const int BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4; 53 const char BandwidthController::ALERT_GLOBAL_NAME[] = "globalAlert"; 54 const int BandwidthController::MAX_CMD_ARGS = 32; 55 const int BandwidthController::MAX_CMD_LEN = 1024; 56 const int BandwidthController::MAX_IFACENAME_LEN = 64; 57 const int BandwidthController::MAX_IPT_OUTPUT_LINE_LEN = 256; 58 59 bool BandwidthController::useLogwrapCall = false; 60 61 /** 62 * Some comments about the rules: 63 * * Ordering 64 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains. 65 * E.g. "-I bw_INPUT -i rmnet0 --jump costly" 66 * - quota'd rules in the costly chain should be before penalty_box lookups. 67 * - the qtaguid counting is done at the end of the bw_INPUT/bw_OUTPUT user chains. 68 * 69 * * global quota vs per interface quota 70 * - global quota for all costly interfaces uses a single costly chain: 71 * . initial rules 72 * iptables -N costly_shared 73 * iptables -I bw_INPUT -i iface0 --jump costly_shared 74 * iptables -I bw_OUTPUT -o iface0 --jump costly_shared 75 * iptables -I costly_shared -m quota \! --quota 500000 \ 76 * --jump REJECT --reject-with icmp-net-prohibited 77 * iptables -A costly_shared --jump penalty_box 78 * 79 * . adding a new iface to this, E.g.: 80 * iptables -I bw_INPUT -i iface1 --jump costly_shared 81 * iptables -I bw_OUTPUT -o iface1 --jump costly_shared 82 * 83 * - quota per interface. This is achieve by having "costly" chains per quota. 84 * E.g. adding a new costly interface iface0 with its own quota: 85 * iptables -N costly_iface0 86 * iptables -I bw_INPUT -i iface0 --jump costly_iface0 87 * iptables -I bw_OUTPUT -o iface0 --jump costly_iface0 88 * iptables -A costly_iface0 -m quota \! --quota 500000 \ 89 * --jump REJECT --reject-with icmp-net-prohibited 90 * iptables -A costly_iface0 --jump penalty_box 91 * 92 * * penalty_box handling: 93 * - only one penalty_box for all interfaces 94 * E.g Adding an app: 95 * iptables -A penalty_box -m owner --uid-owner app_3 \ 96 * --jump REJECT --reject-with icmp-net-prohibited 97 */ 98 const char *BandwidthController::IPT_FLUSH_COMMANDS[] = { 99 /* 100 * Cleanup rules. 101 * Should normally include costly_<iface>, but we rely on the way they are setup 102 * to allow coexistance. 103 */ 104 "-F bw_INPUT", 105 "-F bw_OUTPUT", 106 "-F bw_FORWARD", 107 "-F penalty_box", 108 "-F costly_shared", 109 110 "-t raw -F bw_raw_PREROUTING", 111 "-t mangle -F bw_mangle_POSTROUTING", 112 }; 113 114 /* The cleanup commands assume flushing has been done. */ 115 const char *BandwidthController::IPT_CLEANUP_COMMANDS[] = { 116 /* Delete hooks to custom chains. */ 117 "-D INPUT -j bw_INPUT", 118 "-D OUTPUT -j bw_OUTPUT", 119 "-D FORWARD -j bw_FORWARD", 120 121 "-t raw -D bw_raw_PREROUTING", 122 "-t mangle -D bw_mangle_POSTROUTING", 123 124 "-X bw_INPUT", 125 "-X bw_OUTPUT", 126 "-X bw_FORWARD", 127 "-X penalty_box", 128 "-X costly_shared", 129 130 "-t raw -X bw_raw_PREROUTING", 131 "-t mangle -X bw_mangle_POSTROUTING", 132 }; 133 134 const char *BandwidthController::IPT_SETUP_COMMANDS[] = { 135 /* Created needed chains. */ 136 "-N bw_INPUT", 137 "-A INPUT -j bw_INPUT", 138 139 "-N bw_OUTPUT", 140 "-A OUTPUT -j bw_OUTPUT", 141 142 "-N bw_FORWARD", 143 "-I FORWARD -j bw_FORWARD", 144 145 "-N costly_shared", 146 "-N penalty_box", 147 148 "-t raw -N bw_raw_PREROUTING", 149 "-t raw -A PREROUTING -j bw_raw_PREROUTING", 150 "-t mangle -N bw_mangle_POSTROUTING", 151 "-t mangle -A POSTROUTING -j bw_mangle_POSTROUTING", 152 }; 153 154 const char *BandwidthController::IPT_BASIC_ACCOUNTING_COMMANDS[] = { 155 "-A bw_INPUT -i lo --jump RETURN", 156 "-A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */ 157 158 "-A bw_OUTPUT -o lo --jump RETURN", 159 "-A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */ 160 161 "-A costly_shared --jump penalty_box", 162 163 "-t raw -A bw_raw_PREROUTING ! -i lo+ -m owner --socket-exists", /* This is a tracking rule. */ 164 "-t mangle -A bw_mangle_POSTROUTING ! -o lo+ -m owner --socket-exists", /* This is a tracking rule. */ 165 }; 166 167 BandwidthController::BandwidthController(void) { 168 char value[PROPERTY_VALUE_MAX]; 169 170 property_get("persist.bandwidth.uselogwrap", value, "0"); 171 useLogwrapCall = !strcmp(value, "1"); 172 } 173 174 int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling, 175 IptFailureLog failureHandling) { 176 int res = 0; 177 178 ALOGV("runIpxtablesCmd(cmd=%s)", cmd); 179 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4, failureHandling); 180 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6, failureHandling); 181 return res; 182 } 183 184 int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) { 185 186 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0' 187 strncpy(buffer, src, buffSize); 188 return buffer[buffSize - 1]; 189 } 190 191 int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, 192 IptIpVer iptVer, IptFailureLog failureHandling) { 193 char buffer[MAX_CMD_LEN]; 194 const char *argv[MAX_CMD_ARGS]; 195 int argc = 0; 196 char *next = buffer; 197 char *tmp; 198 int res; 199 200 std::string fullCmd = cmd; 201 202 if (rejectHandling == IptRejectAdd) { 203 fullCmd += " --jump REJECT --reject-with"; 204 switch (iptVer) { 205 case IptIpV4: 206 fullCmd += " icmp-net-prohibited"; 207 break; 208 case IptIpV6: 209 fullCmd += " icmp6-adm-prohibited"; 210 break; 211 } 212 } 213 214 fullCmd.insert(0, " "); 215 fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH); 216 217 if (!useLogwrapCall) { 218 res = system_nosh(fullCmd.c_str()); 219 } else { 220 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) { 221 ALOGE("iptables command too long"); 222 return -1; 223 } 224 225 argc = 0; 226 while ((tmp = strsep(&next, " "))) { 227 argv[argc++] = tmp; 228 if (argc >= MAX_CMD_ARGS) { 229 ALOGE("iptables argument overflow"); 230 return -1; 231 } 232 } 233 234 argv[argc] = NULL; 235 res = logwrap(argc, argv); 236 } 237 if (res && failureHandling == IptFailShow) { 238 ALOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res); 239 } 240 return res; 241 } 242 243 int BandwidthController::setupIptablesHooks(void) { 244 245 /* Some of the initialCommands are allowed to fail */ 246 runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*), 247 IPT_FLUSH_COMMANDS, RunCmdFailureOk); 248 249 runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*), 250 IPT_CLEANUP_COMMANDS, RunCmdFailureOk); 251 252 runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*), 253 IPT_SETUP_COMMANDS, RunCmdFailureBad); 254 255 return 0; 256 257 } 258 259 int BandwidthController::enableBandwidthControl(bool force) { 260 int res; 261 char value[PROPERTY_VALUE_MAX]; 262 263 if (!force) { 264 property_get("persist.bandwidth.enable", value, "1"); 265 if (!strcmp(value, "0")) 266 return 0; 267 } 268 269 /* Let's pretend we started from scratch ... */ 270 sharedQuotaIfaces.clear(); 271 quotaIfaces.clear(); 272 naughtyAppUids.clear(); 273 globalAlertBytes = 0; 274 globalAlertTetherCount = 0; 275 sharedQuotaBytes = sharedAlertBytes = 0; 276 277 res = runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*), 278 IPT_FLUSH_COMMANDS, RunCmdFailureOk); 279 280 res |= runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*), 281 IPT_BASIC_ACCOUNTING_COMMANDS, RunCmdFailureBad); 282 283 return res; 284 285 } 286 287 int BandwidthController::disableBandwidthControl(void) { 288 runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*), 289 IPT_FLUSH_COMMANDS, RunCmdFailureOk); 290 return 0; 291 } 292 293 int BandwidthController::runCommands(int numCommands, const char *commands[], 294 RunCmdErrHandling cmdErrHandling) { 295 int res = 0; 296 IptFailureLog failureLogging = IptFailShow; 297 if (cmdErrHandling == RunCmdFailureOk) { 298 failureLogging = IptFailHide; 299 } 300 ALOGV("runCommands(): %d commands", numCommands); 301 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) { 302 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd, failureLogging); 303 if (res && cmdErrHandling != RunCmdFailureOk) 304 return res; 305 } 306 return 0; 307 } 308 309 std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) { 310 std::string res; 311 char *buff; 312 const char *opFlag; 313 314 switch (op) { 315 case IptOpInsert: 316 opFlag = "-I"; 317 break; 318 case IptOpReplace: 319 opFlag = "-R"; 320 break; 321 default: 322 case IptOpDelete: 323 opFlag = "-D"; 324 break; 325 } 326 asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid); 327 res = buff; 328 free(buff); 329 return res; 330 } 331 332 int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) { 333 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd); 334 } 335 336 int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) { 337 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove); 338 } 339 340 int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) { 341 char cmd[MAX_CMD_LEN]; 342 int uidNum; 343 const char *failLogTemplate; 344 IptOp op; 345 int appUids[numUids]; 346 std::string naughtyCmd; 347 std::list<int /*uid*/>::iterator it; 348 349 switch (appOp) { 350 case NaughtyAppOpAdd: 351 op = IptOpInsert; 352 failLogTemplate = "Failed to add app uid %d to penalty box."; 353 break; 354 case NaughtyAppOpRemove: 355 op = IptOpDelete; 356 failLogTemplate = "Failed to delete app uid %d from penalty box."; 357 break; 358 default: 359 ALOGE("Unexpected app Op %d", appOp); 360 return -1; 361 } 362 363 for (uidNum = 0; uidNum < numUids; uidNum++) { 364 appUids[uidNum] = atol(appStrUids[uidNum]); 365 if (appUids[uidNum] == 0) { 366 ALOGE(failLogTemplate, appUids[uidNum]); 367 goto fail_parse; 368 } 369 } 370 371 for (uidNum = 0; uidNum < numUids; uidNum++) { 372 int uid = appUids[uidNum]; 373 for (it = naughtyAppUids.begin(); it != naughtyAppUids.end(); it++) { 374 if (*it == uid) 375 break; 376 } 377 bool found = (it != naughtyAppUids.end()); 378 379 if (appOp == NaughtyAppOpRemove) { 380 if (!found) { 381 ALOGE("No such appUid %d to remove", uid); 382 return -1; 383 } 384 naughtyAppUids.erase(it); 385 } else { 386 if (found) { 387 ALOGE("appUid %d exists already", uid); 388 return -1; 389 } 390 naughtyAppUids.push_front(uid); 391 } 392 393 naughtyCmd = makeIptablesNaughtyCmd(op, uid); 394 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) { 395 ALOGE(failLogTemplate, uid); 396 goto fail_with_uidNum; 397 } 398 } 399 return 0; 400 401 fail_with_uidNum: 402 /* Try to remove the uid that failed in any case*/ 403 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]); 404 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd); 405 fail_parse: 406 return -1; 407 } 408 409 std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) { 410 std::string res; 411 char *buff; 412 const char *opFlag; 413 414 ALOGV("makeIptablesQuotaCmd(%d, %lld)", op, quota); 415 416 switch (op) { 417 case IptOpInsert: 418 opFlag = "-I"; 419 break; 420 case IptOpReplace: 421 opFlag = "-R"; 422 break; 423 default: 424 case IptOpDelete: 425 opFlag = "-D"; 426 break; 427 } 428 429 // The requried IP version specific --jump REJECT ... will be added later. 430 asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota, 431 costName); 432 res = buff; 433 free(buff); 434 return res; 435 } 436 437 int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) { 438 char cmd[MAX_CMD_LEN]; 439 int res = 0, res1, res2; 440 int ruleInsertPos = 1; 441 std::string costString; 442 const char *costCString; 443 444 /* The "-N costly" is created upfront, no need to handle it here. */ 445 switch (quotaType) { 446 case QuotaUnique: 447 costString = "costly_"; 448 costString += ifn; 449 costCString = costString.c_str(); 450 /* 451 * Flush the costly_<iface> is allowed to fail in case it didn't exist. 452 * Creating a new one is allowed to fail in case it existed. 453 * This helps with netd restarts. 454 */ 455 snprintf(cmd, sizeof(cmd), "-F %s", costCString); 456 res1 = runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide); 457 snprintf(cmd, sizeof(cmd), "-N %s", costCString); 458 res2 = runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide); 459 res = (res1 && res2) || (!res1 && !res2); 460 461 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString); 462 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 463 break; 464 case QuotaShared: 465 costCString = "costly_shared"; 466 break; 467 default: 468 ALOGE("Unexpected quotatype %d", quotaType); 469 return -1; 470 } 471 472 if (globalAlertBytes) { 473 /* The alert rule comes 1st */ 474 ruleInsertPos = 2; 475 } 476 477 snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString); 478 runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide); 479 480 snprintf(cmd, sizeof(cmd), "-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, ifn, costCString); 481 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 482 483 snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString); 484 runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide); 485 486 snprintf(cmd, sizeof(cmd), "-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, ifn, costCString); 487 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 488 return res; 489 } 490 491 int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) { 492 char cmd[MAX_CMD_LEN]; 493 int res = 0; 494 std::string costString; 495 const char *costCString; 496 497 switch (quotaType) { 498 case QuotaUnique: 499 costString = "costly_"; 500 costString += ifn; 501 costCString = costString.c_str(); 502 break; 503 case QuotaShared: 504 costCString = "costly_shared"; 505 break; 506 default: 507 ALOGE("Unexpected quotatype %d", quotaType); 508 return -1; 509 } 510 511 snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString); 512 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 513 snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString); 514 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 515 516 /* The "-N costly_shared" is created upfront, no need to handle it here. */ 517 if (quotaType == QuotaUnique) { 518 snprintf(cmd, sizeof(cmd), "-F %s", costCString); 519 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 520 snprintf(cmd, sizeof(cmd), "-X %s", costCString); 521 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 522 } 523 return res; 524 } 525 526 int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) { 527 char cmd[MAX_CMD_LEN]; 528 char ifn[MAX_IFACENAME_LEN]; 529 int res = 0; 530 std::string quotaCmd; 531 std::string ifaceName; 532 ; 533 const char *costName = "shared"; 534 std::list<std::string>::iterator it; 535 536 if (!maxBytes) { 537 /* Don't talk about -1, deprecate it. */ 538 ALOGE("Invalid bytes value. 1..max_int64."); 539 return -1; 540 } 541 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { 542 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN); 543 return -1; 544 } 545 ifaceName = ifn; 546 547 if (maxBytes == -1) { 548 return removeInterfaceSharedQuota(ifn); 549 } 550 551 /* Insert ingress quota. */ 552 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) { 553 if (*it == ifaceName) 554 break; 555 } 556 557 if (it == sharedQuotaIfaces.end()) { 558 res |= prepCostlyIface(ifn, QuotaShared); 559 if (sharedQuotaIfaces.empty()) { 560 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes); 561 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd); 562 if (res) { 563 ALOGE("Failed set quota rule"); 564 goto fail; 565 } 566 sharedQuotaBytes = maxBytes; 567 } 568 sharedQuotaIfaces.push_front(ifaceName); 569 570 } 571 572 if (maxBytes != sharedQuotaBytes) { 573 res |= updateQuota(costName, maxBytes); 574 if (res) { 575 ALOGE("Failed update quota for %s", costName); 576 goto fail; 577 } 578 sharedQuotaBytes = maxBytes; 579 } 580 return 0; 581 582 fail: 583 /* 584 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse 585 * rules in the kernel to see which ones need cleaning up. 586 * For now callers needs to choose if they want to "ndc bandwidth enable" 587 * which resets everything. 588 */ 589 removeInterfaceSharedQuota(ifn); 590 return -1; 591 } 592 593 /* It will also cleanup any shared alerts */ 594 int BandwidthController::removeInterfaceSharedQuota(const char *iface) { 595 char ifn[MAX_IFACENAME_LEN]; 596 int res = 0; 597 std::string ifaceName; 598 std::list<std::string>::iterator it; 599 const char *costName = "shared"; 600 601 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { 602 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN); 603 return -1; 604 } 605 ifaceName = ifn; 606 607 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) { 608 if (*it == ifaceName) 609 break; 610 } 611 if (it == sharedQuotaIfaces.end()) { 612 ALOGE("No such iface %s to delete", ifn); 613 return -1; 614 } 615 616 res |= cleanupCostlyIface(ifn, QuotaShared); 617 sharedQuotaIfaces.erase(it); 618 619 if (sharedQuotaIfaces.empty()) { 620 std::string quotaCmd; 621 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes); 622 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd); 623 sharedQuotaBytes = 0; 624 if (sharedAlertBytes) { 625 removeSharedAlert(); 626 sharedAlertBytes = 0; 627 } 628 } 629 return res; 630 } 631 632 int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) { 633 char ifn[MAX_IFACENAME_LEN]; 634 int res = 0; 635 std::string ifaceName; 636 const char *costName; 637 std::list<QuotaInfo>::iterator it; 638 std::string quotaCmd; 639 640 if (!maxBytes) { 641 /* Don't talk about -1, deprecate it. */ 642 ALOGE("Invalid bytes value. 1..max_int64."); 643 return -1; 644 } 645 if (maxBytes == -1) { 646 return removeInterfaceQuota(iface); 647 } 648 649 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { 650 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN); 651 return -1; 652 } 653 ifaceName = ifn; 654 costName = iface; 655 656 /* Insert ingress quota. */ 657 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { 658 if (it->ifaceName == ifaceName) 659 break; 660 } 661 662 if (it == quotaIfaces.end()) { 663 res |= prepCostlyIface(ifn, QuotaUnique); 664 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes); 665 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd); 666 if (res) { 667 ALOGE("Failed set quota rule"); 668 goto fail; 669 } 670 671 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0)); 672 673 } else { 674 res |= updateQuota(costName, maxBytes); 675 if (res) { 676 ALOGE("Failed update quota for %s", iface); 677 goto fail; 678 } 679 it->quota = maxBytes; 680 } 681 return 0; 682 683 fail: 684 /* 685 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse 686 * rules in the kernel to see which ones need cleaning up. 687 * For now callers needs to choose if they want to "ndc bandwidth enable" 688 * which resets everything. 689 */ 690 removeInterfaceSharedQuota(ifn); 691 return -1; 692 } 693 694 int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) { 695 return getInterfaceQuota("shared", bytes); 696 } 697 698 int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) { 699 FILE *fp; 700 char *fname; 701 int scanRes; 702 703 asprintf(&fname, "/proc/net/xt_quota/%s", costName); 704 fp = fopen(fname, "r"); 705 free(fname); 706 if (!fp) { 707 ALOGE("Reading quota %s failed (%s)", costName, strerror(errno)); 708 return -1; 709 } 710 scanRes = fscanf(fp, "%lld", bytes); 711 ALOGV("Read quota res=%d bytes=%lld", scanRes, *bytes); 712 fclose(fp); 713 return scanRes == 1 ? 0 : -1; 714 } 715 716 int BandwidthController::removeInterfaceQuota(const char *iface) { 717 718 char ifn[MAX_IFACENAME_LEN]; 719 int res = 0; 720 std::string ifaceName; 721 const char *costName; 722 std::list<QuotaInfo>::iterator it; 723 724 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { 725 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN); 726 return -1; 727 } 728 ifaceName = ifn; 729 costName = iface; 730 731 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { 732 if (it->ifaceName == ifaceName) 733 break; 734 } 735 736 if (it == quotaIfaces.end()) { 737 ALOGE("No such iface %s to delete", ifn); 738 return -1; 739 } 740 741 /* This also removes the quota command of CostlyIface chain. */ 742 res |= cleanupCostlyIface(ifn, QuotaUnique); 743 744 quotaIfaces.erase(it); 745 746 return res; 747 } 748 749 int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) { 750 FILE *fp; 751 char *fname; 752 753 asprintf(&fname, "/proc/net/xt_quota/%s", quotaName); 754 fp = fopen(fname, "w"); 755 free(fname); 756 if (!fp) { 757 ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno)); 758 return -1; 759 } 760 fprintf(fp, "%lld\n", bytes); 761 fclose(fp); 762 return 0; 763 } 764 765 int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) { 766 int res = 0; 767 const char *opFlag; 768 const char *ifaceLimiting; 769 char *alertQuotaCmd; 770 771 switch (op) { 772 case IptOpInsert: 773 opFlag = "-I"; 774 break; 775 case IptOpReplace: 776 opFlag = "-R"; 777 break; 778 default: 779 case IptOpDelete: 780 opFlag = "-D"; 781 break; 782 } 783 784 ifaceLimiting = "! -i lo+"; 785 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_INPUT", 786 bytes, alertName); 787 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); 788 free(alertQuotaCmd); 789 ifaceLimiting = "! -o lo+"; 790 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_OUTPUT", 791 bytes, alertName); 792 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); 793 free(alertQuotaCmd); 794 return res; 795 } 796 797 int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const char *alertName, int64_t bytes) { 798 int res = 0; 799 const char *opFlag; 800 const char *ifaceLimiting; 801 char *alertQuotaCmd; 802 803 switch (op) { 804 case IptOpInsert: 805 opFlag = "-I"; 806 break; 807 case IptOpReplace: 808 opFlag = "-R"; 809 break; 810 default: 811 case IptOpDelete: 812 opFlag = "-D"; 813 break; 814 } 815 816 ifaceLimiting = "! -i lo+"; 817 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_FORWARD", 818 bytes, alertName); 819 res = runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); 820 free(alertQuotaCmd); 821 return res; 822 } 823 824 int BandwidthController::setGlobalAlert(int64_t bytes) { 825 const char *alertName = ALERT_GLOBAL_NAME; 826 int res = 0; 827 828 if (!bytes) { 829 ALOGE("Invalid bytes value. 1..max_int64."); 830 return -1; 831 } 832 if (globalAlertBytes) { 833 res = updateQuota(alertName, bytes); 834 } else { 835 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes); 836 if (globalAlertTetherCount) { 837 ALOGV("setGlobalAlert for %d tether", globalAlertTetherCount); 838 res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes); 839 } 840 } 841 globalAlertBytes = bytes; 842 return res; 843 } 844 845 int BandwidthController::setGlobalAlertInForwardChain(void) { 846 const char *alertName = ALERT_GLOBAL_NAME; 847 int res = 0; 848 849 globalAlertTetherCount++; 850 ALOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount); 851 852 /* 853 * If there is no globalAlert active we are done. 854 * If there is an active globalAlert but this is not the 1st 855 * tether, we are also done. 856 */ 857 if (!globalAlertBytes || globalAlertTetherCount != 1) { 858 return 0; 859 } 860 861 /* We only add the rule if this was the 1st tether added. */ 862 res = runIptablesAlertFwdCmd(IptOpInsert, alertName, globalAlertBytes); 863 return res; 864 } 865 866 int BandwidthController::removeGlobalAlert(void) { 867 868 const char *alertName = ALERT_GLOBAL_NAME; 869 int res = 0; 870 871 if (!globalAlertBytes) { 872 ALOGE("No prior alert set"); 873 return -1; 874 } 875 res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes); 876 if (globalAlertTetherCount) { 877 res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes); 878 } 879 globalAlertBytes = 0; 880 return res; 881 } 882 883 int BandwidthController::removeGlobalAlertInForwardChain(void) { 884 int res = 0; 885 const char *alertName = ALERT_GLOBAL_NAME; 886 887 if (!globalAlertTetherCount) { 888 ALOGE("No prior alert set"); 889 return -1; 890 } 891 892 globalAlertTetherCount--; 893 /* 894 * If there is no globalAlert active we are done. 895 * If there is an active globalAlert but there are more 896 * tethers, we are also done. 897 */ 898 if (!globalAlertBytes || globalAlertTetherCount >= 1) { 899 return 0; 900 } 901 902 /* We only detete the rule if this was the last tether removed. */ 903 res = runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes); 904 return res; 905 } 906 907 int BandwidthController::setSharedAlert(int64_t bytes) { 908 if (!sharedQuotaBytes) { 909 ALOGE("Need to have a prior shared quota set to set an alert"); 910 return -1; 911 } 912 if (!bytes) { 913 ALOGE("Invalid bytes value. 1..max_int64."); 914 return -1; 915 } 916 return setCostlyAlert("shared", bytes, &sharedAlertBytes); 917 } 918 919 int BandwidthController::removeSharedAlert(void) { 920 return removeCostlyAlert("shared", &sharedAlertBytes); 921 } 922 923 int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) { 924 std::list<QuotaInfo>::iterator it; 925 926 if (!bytes) { 927 ALOGE("Invalid bytes value. 1..max_int64."); 928 return -1; 929 } 930 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { 931 if (it->ifaceName == iface) 932 break; 933 } 934 935 if (it == quotaIfaces.end()) { 936 ALOGE("Need to have a prior interface quota set to set an alert"); 937 return -1; 938 } 939 940 return setCostlyAlert(iface, bytes, &it->alert); 941 } 942 943 int BandwidthController::removeInterfaceAlert(const char *iface) { 944 std::list<QuotaInfo>::iterator it; 945 946 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { 947 if (it->ifaceName == iface) 948 break; 949 } 950 951 if (it == quotaIfaces.end()) { 952 ALOGE("No prior alert set for interface %s", iface); 953 return -1; 954 } 955 956 return removeCostlyAlert(iface, &it->alert); 957 } 958 959 int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) { 960 char *alertQuotaCmd; 961 char *chainNameAndPos; 962 int res = 0; 963 char *alertName; 964 965 if (!bytes) { 966 ALOGE("Invalid bytes value. 1..max_int64."); 967 return -1; 968 } 969 asprintf(&alertName, "%sAlert", costName); 970 if (*alertBytes) { 971 res = updateQuota(alertName, *alertBytes); 972 } else { 973 asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN); 974 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "", "-I", chainNameAndPos, bytes, alertName); 975 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); 976 free(alertQuotaCmd); 977 free(chainNameAndPos); 978 } 979 *alertBytes = bytes; 980 free(alertName); 981 return res; 982 } 983 984 int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) { 985 char *alertQuotaCmd; 986 char *chainName; 987 char *alertName; 988 int res = 0; 989 990 asprintf(&alertName, "%sAlert", costName); 991 if (!*alertBytes) { 992 ALOGE("No prior alert set for %s alert", costName); 993 return -1; 994 } 995 996 asprintf(&chainName, "costly_%s", costName); 997 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "", "-D", chainName, *alertBytes, alertName); 998 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); 999 free(alertQuotaCmd); 1000 free(chainName); 1001 1002 *alertBytes = 0; 1003 free(alertName); 1004 return res; 1005 } 1006 1007 /* 1008 * Parse the ptks and bytes out of: 1009 * Chain FORWARD (policy RETURN 0 packets, 0 bytes) 1010 * pkts bytes target prot opt in out source destination 1011 * 0 0 RETURN all -- rmnet0 wlan0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED 1012 * 0 0 DROP all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 state INVALID 1013 * 0 0 RETURN all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 1014 * 1015 */ 1016 int BandwidthController::parseForwardChainStats(TetherStats &stats, FILE *fp, 1017 std::string &extraProcessingInfo) { 1018 int res; 1019 char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN]; 1020 char iface0[MAX_IPT_OUTPUT_LINE_LEN]; 1021 char iface1[MAX_IPT_OUTPUT_LINE_LEN]; 1022 char rest[MAX_IPT_OUTPUT_LINE_LEN]; 1023 1024 char *buffPtr; 1025 int64_t packets, bytes; 1026 1027 while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) { 1028 /* Clean up, so a failed parse can still print info */ 1029 iface0[0] = iface1[0] = rest[0] = packets = bytes = 0; 1030 res = sscanf(buffPtr, "%lld %lld RETURN all -- %s %s 0.%s", 1031 &packets, &bytes, iface0, iface1, rest); 1032 ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%lld bytes=%lld rest=<%s> orig line=<%s>", res, 1033 iface0, iface1, packets, bytes, rest, buffPtr); 1034 extraProcessingInfo += buffPtr; 1035 1036 if (res != 5) { 1037 continue; 1038 } 1039 if ((stats.ifaceIn == iface0) && (stats.ifaceOut == iface1)) { 1040 ALOGV("iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets); 1041 stats.rxPackets = packets; 1042 stats.rxBytes = bytes; 1043 } else if ((stats.ifaceOut == iface0) && (stats.ifaceIn == iface1)) { 1044 ALOGV("iface_in=%s iface_out=%s tx_bytes=%lld tx_packets=%lld ", iface1, iface0, bytes, packets); 1045 stats.txPackets = packets; 1046 stats.txBytes = bytes; 1047 } 1048 } 1049 /* Failure if rx or tx was not found */ 1050 return (stats.rxBytes == -1 || stats.txBytes == -1) ? -1 : 0; 1051 } 1052 1053 1054 char *BandwidthController::TetherStats::getStatsLine(void) { 1055 char *msg; 1056 asprintf(&msg, "%s %s %lld %lld %lld %lld", ifaceIn.c_str(), ifaceOut.c_str(), 1057 rxBytes, rxPackets, txBytes, txPackets); 1058 return msg; 1059 } 1060 1061 int BandwidthController::getTetherStats(TetherStats &stats, std::string &extraProcessingInfo) { 1062 int res; 1063 std::string fullCmd; 1064 FILE *iptOutput; 1065 const char *cmd; 1066 1067 if (stats.rxBytes != -1 || stats.txBytes != -1) { 1068 ALOGE("Unexpected input stats. Byte counts should be -1."); 1069 return -1; 1070 } 1071 1072 /* 1073 * Why not use some kind of lib to talk to iptables? 1074 * Because the only libs are libiptc and libip6tc in iptables, and they are 1075 * not easy to use. They require the known iptables match modules to be 1076 * preloaded/linked, and require apparently a lot of wrapper code to get 1077 * the wanted info. 1078 */ 1079 fullCmd = IPTABLES_PATH; 1080 fullCmd += " -nvx -L natctrl_FORWARD"; 1081 iptOutput = popen(fullCmd.c_str(), "r"); 1082 if (!iptOutput) { 1083 ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno)); 1084 extraProcessingInfo += "Failed to run iptables."; 1085 return -1; 1086 } 1087 res = parseForwardChainStats(stats, iptOutput, extraProcessingInfo); 1088 pclose(iptOutput); 1089 1090 /* Currently NatController doesn't do ipv6 tethering, so we are done. */ 1091 return res; 1092 } 1093