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