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