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