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