BandwidthController.cpp revision 8a93272255f1b7e3083a97e1e28ddf675c0c7fb0
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#include <errno.h> 18#include <fcntl.h> 19#include <stdlib.h> 20#include <string.h> 21 22#include <sys/socket.h> 23#include <sys/stat.h> 24#include <sys/types.h> 25#include <sys/wait.h> 26 27#include <linux/netlink.h> 28#include <linux/rtnetlink.h> 29#include <linux/pkt_sched.h> 30 31#define LOG_TAG "BandwidthController" 32#include <cutils/log.h> 33#include <cutils/properties.h> 34 35extern "C" int logwrap(int argc, const char **argv, int background); 36 37#include "BandwidthController.h" 38 39const int BandwidthController::MAX_CMD_LEN = 1024; 40const int BandwidthController::MAX_IFACENAME_LEN = 64; 41const int BandwidthController::MAX_CMD_ARGS = 32; 42const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables"; 43const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables"; 44const char BandwidthController::ALERT_IPT_TEMPLATE[] = "%s %s -m quota2 ! --quota %lld --name %s"; 45const int BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4; 46 47/** 48 * Some comments about the rules: 49 * * Ordering 50 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains. 51 * E.g. "-I INPUT -i rmnet0 --goto costly" 52 * - quota'd rules in the costly chain should be before penalty_box lookups. 53 * 54 * * global quota vs per interface quota 55 * - global quota for all costly interfaces uses a single costly chain: 56 * . initial rules 57 * iptables -N costly_shared 58 * iptables -I INPUT -i iface0 --goto costly_shared 59 * iptables -I OUTPUT -o iface0 --goto costly_shared 60 * iptables -I costly_shared -m quota \! --quota 500000 \ 61 * --jump REJECT --reject-with icmp-net-prohibited 62 * iptables -A costly_shared --jump penalty_box 63 * iptables -A costly_shared -m owner --socket-exists 64 * 65 * . adding a new iface to this, E.g.: 66 * iptables -I INPUT -i iface1 --goto costly_shared 67 * iptables -I OUTPUT -o iface1 --goto costly_shared 68 * 69 * - quota per interface. This is achieve by having "costly" chains per quota. 70 * E.g. adding a new costly interface iface0 with its own quota: 71 * iptables -N costly_iface0 72 * iptables -I INPUT -i iface0 --goto costly_iface0 73 * iptables -I OUTPUT -o iface0 --goto costly_iface0 74 * iptables -A costly_iface0 -m quota \! --quota 500000 \ 75 * --jump REJECT --reject-with icmp-net-prohibited 76 * iptables -A costly_iface0 --jump penalty_box 77 * iptables -A costly_iface0 -m owner --socket-exists 78 * 79 * * penalty_box handling: 80 * - only one penalty_box for all interfaces 81 * E.g Adding an app: 82 * iptables -A penalty_box -m owner --uid-owner app_3 \ 83 * --jump REJECT --reject-with icmp-net-prohibited 84 */ 85const char *BandwidthController::cleanupCommands[] = { 86 /* Cleanup rules. */ 87 "-F", 88 "-t raw -F", 89 /* TODO: If at some point we need more user chains than here, then we will need 90 * a different cleanup approach. 91 */ 92 "-X", /* Should normally only be costly_shared, penalty_box, and costly_<iface> */ 93}; 94 95const char *BandwidthController::setupCommands[] = { 96 /* Created needed chains. */ 97 "-N costly_shared", 98 "-N penalty_box", 99}; 100 101const char *BandwidthController::basicAccountingCommands[] = { 102 "-F INPUT", 103 "-A INPUT -i lo --jump ACCEPT", 104 "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */ 105 106 "-F OUTPUT", 107 "-A OUTPUT -o lo --jump ACCEPT", 108 "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */ 109 110 "-F costly_shared", 111 "-A costly_shared --jump penalty_box", 112 "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */ 113 /* TODO(jpa): Figure out why iptables doesn't correctly return from this 114 * chain. For now, hack the chain exit with an ACCEPT. 115 */ 116 "-A costly_shared --jump ACCEPT", 117}; 118 119BandwidthController::BandwidthController(void) { 120 char value[PROPERTY_VALUE_MAX]; 121 122 property_get("persist.bandwidth.enable", value, "0"); 123 if (!strcmp(value, "1")) { 124 enableBandwidthControl(); 125 } 126 127} 128 129int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) { 130 int res = 0; 131 132 LOGD("runIpxtablesCmd(cmd=%s)", cmd); 133 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4); 134 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6); 135 return res; 136} 137 138int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) { 139 140 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0' 141 strncpy(buffer, src, buffSize); 142 return buffer[buffSize - 1]; 143} 144 145int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, 146 IptIpVer iptVer) { 147 char buffer[MAX_CMD_LEN]; 148 const char *argv[MAX_CMD_ARGS]; 149 int argc = 0; 150 char *next = buffer; 151 char *tmp; 152 153 std::string fullCmd = cmd; 154 155 if (rejectHandling == IptRejectAdd) { 156 fullCmd += " --jump REJECT --reject-with"; 157 switch (iptVer) { 158 case IptIpV4: 159 fullCmd += " icmp-net-prohibited"; 160 break; 161 case IptIpV6: 162 fullCmd += " icmp6-adm-prohibited"; 163 break; 164 } 165 } 166 167 argc = 0; 168 argv[argc++] = iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH; 169 170 LOGD("runIptablesCmd(): %s %s", argv[0], fullCmd.c_str()); 171 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) { 172 LOGE("iptables command too long"); 173 return -1; 174 } 175 176 while ((tmp = strsep(&next, " "))) { 177 argv[argc++] = tmp; 178 if (argc >= MAX_CMD_ARGS) { 179 LOGE("iptables argument overflow"); 180 return -1; 181 } 182 } 183 184 argv[argc] = NULL; 185 /* TODO(jpa): Once this stabilizes, remove logwrap() as it tends to wedge netd 186 * Then just talk directly to the kernel via rtnetlink. 187 */ 188 return logwrap(argc, argv, 0); 189} 190 191int BandwidthController::enableBandwidthControl(void) { 192 int res; 193 /* Some of the initialCommands are allowed to fail */ 194 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk); 195 runCommands(sizeof(setupCommands) / sizeof(char*), setupCommands, RunCmdFailureOk); 196 res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands, 197 RunCmdFailureBad); 198 199 sharedQuotaBytes = sharedAlertBytes = 0; 200 sharedQuotaIfaces.clear(); 201 quotaIfaces.clear(); 202 naughtyAppUids.clear(); 203 204 return res; 205 206} 207 208int BandwidthController::disableBandwidthControl(void) { 209 /* The cleanupCommands are allowed to fail. */ 210 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk); 211 return 0; 212} 213 214int BandwidthController::runCommands(int numCommands, const char *commands[], 215 RunCmdErrHandling cmdErrHandling) { 216 int res = 0; 217 LOGD("runCommands(): %d commands", numCommands); 218 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) { 219 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd); 220 if (res && cmdErrHandling != RunCmdFailureBad) 221 return res; 222 } 223 return cmdErrHandling == RunCmdFailureBad ? res : 0; 224} 225 226std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) { 227 std::string res; 228 char *buff; 229 const char *opFlag; 230 231 switch (op) { 232 case IptOpInsert: 233 opFlag = "-I"; 234 break; 235 case IptOpReplace: 236 opFlag = "-R"; 237 break; 238 default: 239 case IptOpDelete: 240 opFlag = "-D"; 241 break; 242 } 243 asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid); 244 res = buff; 245 free(buff); 246 return res; 247} 248 249int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) { 250 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd); 251} 252 253int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) { 254 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove); 255} 256 257int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) { 258 char cmd[MAX_CMD_LEN]; 259 int uidNum; 260 const char *failLogTemplate; 261 IptOp op; 262 int appUids[numUids]; 263 std::string naughtyCmd; 264 265 switch (appOp) { 266 case NaughtyAppOpAdd: 267 op = IptOpInsert; 268 failLogTemplate = "Failed to add app uid %d to penalty box."; 269 break; 270 case NaughtyAppOpRemove: 271 op = IptOpDelete; 272 failLogTemplate = "Failed to delete app uid %d from penalty box."; 273 break; 274 } 275 276 for (uidNum = 0; uidNum < numUids; uidNum++) { 277 appUids[uidNum] = atol(appStrUids[uidNum]); 278 if (appUids[uidNum] == 0) { 279 LOGE(failLogTemplate, appUids[uidNum]); 280 goto fail_parse; 281 } 282 } 283 284 for (uidNum = 0; uidNum < numUids; uidNum++) { 285 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]); 286 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) { 287 LOGE(failLogTemplate, appUids[uidNum]); 288 goto fail_with_uidNum; 289 } 290 } 291 return 0; 292 293fail_with_uidNum: 294 /* Try to remove the uid that failed in any case*/ 295 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]); 296 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd); 297fail_parse: 298 return -1; 299} 300 301std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) { 302 std::string res; 303 char *buff; 304 const char *opFlag; 305 306 LOGD("makeIptablesQuotaCmd(%d, %lld)", op, quota); 307 308 switch (op) { 309 case IptOpInsert: 310 opFlag = "-I"; 311 break; 312 case IptOpReplace: 313 opFlag = "-R"; 314 break; 315 default: 316 case IptOpDelete: 317 opFlag = "-D"; 318 break; 319 } 320 321 // The requried IP version specific --jump REJECT ... will be added later. 322 asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota, 323 costName); 324 res = buff; 325 free(buff); 326 return res; 327} 328 329int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) { 330 char cmd[MAX_CMD_LEN]; 331 int res = 0; 332 int ruleInsertPos = 1; 333 std::string costString; 334 const char *costCString; 335 336 /* The "-N costly" is created upfront, no need to handle it here. */ 337 switch (quotaType) { 338 case QuotaUnique: 339 costString = "costly_"; 340 costString += ifn; 341 costCString = costString.c_str(); 342 snprintf(cmd, sizeof(cmd), "-N %s", costCString); 343 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 344 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString); 345 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 346 snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString); 347 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 348 /* TODO(jpa): Figure out why iptables doesn't correctly return from this 349 * chain. For now, hack the chain exit with an ACCEPT. 350 */ 351 snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString); 352 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 353 break; 354 case QuotaShared: 355 costCString = "costly_shared"; 356 break; 357 } 358 359 if (globalAlertBytes) { 360 /* The alert rule comes 1st */ 361 ruleInsertPos = 2; 362 } 363 snprintf(cmd, sizeof(cmd), "-I INPUT %d -i %s --goto %s", ruleInsertPos, ifn, costCString); 364 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 365 snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString); 366 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 367 return res; 368} 369 370int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) { 371 char cmd[MAX_CMD_LEN]; 372 int res = 0; 373 std::string costString; 374 const char *costCString; 375 376 switch (quotaType) { 377 case QuotaUnique: 378 costString = "costly_"; 379 costString += ifn; 380 costCString = costString.c_str(); 381 break; 382 case QuotaShared: 383 costCString = "costly_shared"; 384 break; 385 } 386 387 snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString); 388 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 389 snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString); 390 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 391 392 /* The "-N costly_shared" is created upfront, no need to handle it here. */ 393 if (quotaType == QuotaUnique) { 394 snprintf(cmd, sizeof(cmd), "-F %s", costCString); 395 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 396 snprintf(cmd, sizeof(cmd), "-X %s", costCString); 397 res |= runIpxtablesCmd(cmd, IptRejectNoAdd); 398 } 399 return res; 400} 401 402int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) { 403 char cmd[MAX_CMD_LEN]; 404 char ifn[MAX_IFACENAME_LEN]; 405 int res = 0; 406 std::string quotaCmd; 407 std::string ifaceName; 408 ; 409 const char *costName = "shared"; 410 std::list<std::string>::iterator it; 411 412 if (!maxBytes) { 413 /* Don't talk about -1, deprecate it. */ 414 LOGE("Invalid bytes value. 1..max_int64."); 415 return -1; 416 } 417 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { 418 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN); 419 return -1; 420 } 421 ifaceName = ifn; 422 423 if (maxBytes == -1) { 424 return removeInterfaceSharedQuota(ifn); 425 } 426 427 /* Insert ingress quota. */ 428 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) { 429 if (*it == ifaceName) 430 break; 431 } 432 433 if (it == sharedQuotaIfaces.end()) { 434 res |= prepCostlyIface(ifn, QuotaShared); 435 if (sharedQuotaIfaces.empty()) { 436 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes); 437 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd); 438 if (res) { 439 LOGE("Failed set quota rule"); 440 goto fail; 441 } 442 sharedQuotaBytes = maxBytes; 443 } 444 sharedQuotaIfaces.push_front(ifaceName); 445 446 } 447 448 if (maxBytes != sharedQuotaBytes) { 449 res |= updateQuota(costName, maxBytes); 450 if (res) { 451 LOGE("Failed update quota for %s", costName); 452 goto fail; 453 } 454 sharedQuotaBytes = maxBytes; 455 } 456 return 0; 457 458 fail: 459 /* 460 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse 461 * rules in the kernel to see which ones need cleaning up. 462 * For now callers needs to choose if they want to "ndc bandwidth enable" 463 * which resets everything. 464 */ 465 removeInterfaceSharedQuota(ifn); 466 return -1; 467} 468 469/* It will also cleanup any shared alerts */ 470int BandwidthController::removeInterfaceSharedQuota(const char *iface) { 471 char ifn[MAX_IFACENAME_LEN]; 472 int res = 0; 473 std::string ifaceName; 474 std::list<std::string>::iterator it; 475 const char *costName = "shared"; 476 477 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { 478 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN); 479 return -1; 480 } 481 ifaceName = ifn; 482 483 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) { 484 if (*it == ifaceName) 485 break; 486 } 487 if (it == sharedQuotaIfaces.end()) { 488 LOGE("No such iface %s to delete", ifn); 489 return -1; 490 } 491 492 res |= cleanupCostlyIface(ifn, QuotaShared); 493 sharedQuotaIfaces.erase(it); 494 495 if (sharedQuotaIfaces.empty()) { 496 std::string quotaCmd; 497 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes); 498 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd); 499 sharedQuotaBytes = 0; 500 if (sharedAlertBytes) { 501 removeSharedAlert(); 502 sharedAlertBytes = 0; 503 } 504 } 505 return res; 506} 507 508int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) { 509 char ifn[MAX_IFACENAME_LEN]; 510 int res = 0; 511 std::string ifaceName; 512 const char *costName; 513 std::list<QuotaInfo>::iterator it; 514 std::string quotaCmd; 515 516 if (!maxBytes) { 517 /* Don't talk about -1, deprecate it. */ 518 LOGE("Invalid bytes value. 1..max_int64."); 519 return -1; 520 } 521 if (maxBytes == -1) { 522 return removeInterfaceQuota(iface); 523 } 524 525 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { 526 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN); 527 return -1; 528 } 529 ifaceName = ifn; 530 costName = iface; 531 532 /* Insert ingress quota. */ 533 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { 534 if (it->ifaceName == ifaceName) 535 break; 536 } 537 538 if (it == quotaIfaces.end()) { 539 res |= prepCostlyIface(ifn, QuotaUnique); 540 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes); 541 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd); 542 if (res) { 543 LOGE("Failed set quota rule"); 544 goto fail; 545 } 546 547 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0)); 548 549 } else { 550 res |= updateQuota(costName, maxBytes); 551 if (res) { 552 LOGE("Failed update quota for %s", iface); 553 goto fail; 554 } 555 it->quota = maxBytes; 556 } 557 return 0; 558 559 fail: 560 /* 561 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse 562 * rules in the kernel to see which ones need cleaning up. 563 * For now callers needs to choose if they want to "ndc bandwidth enable" 564 * which resets everything. 565 */ 566 removeInterfaceSharedQuota(ifn); 567 return -1; 568} 569 570int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) { 571 return getInterfaceQuota("shared", bytes); 572} 573 574int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) { 575 FILE *fp; 576 char *fname; 577 int scanRes; 578 579 asprintf(&fname, "/proc/net/xt_quota/%s", costName); 580 fp = fopen(fname, "r"); 581 free(fname); 582 if (!fp) { 583 LOGE("Reading quota %s failed (%s)", costName, strerror(errno)); 584 return -1; 585 } 586 scanRes = fscanf(fp, "%lld", bytes); 587 LOGD("Read quota res=%d bytes=%lld", scanRes, *bytes); 588 fclose(fp); 589 return scanRes == 1 ? 0 : -1; 590} 591 592int BandwidthController::removeInterfaceQuota(const char *iface) { 593 594 char ifn[MAX_IFACENAME_LEN]; 595 int res = 0; 596 std::string ifaceName; 597 const char *costName; 598 std::list<QuotaInfo>::iterator it; 599 600 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { 601 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN); 602 return -1; 603 } 604 ifaceName = ifn; 605 costName = iface; 606 607 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { 608 if (it->ifaceName == ifaceName) 609 break; 610 } 611 612 if (it == quotaIfaces.end()) { 613 LOGE("No such iface %s to delete", ifn); 614 return -1; 615 } 616 617 /* This also removes the quota command of CostlyIface chain. */ 618 res |= cleanupCostlyIface(ifn, QuotaUnique); 619 620 quotaIfaces.erase(it); 621 622 return res; 623} 624 625int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) { 626 FILE *fp; 627 char *fname; 628 629 asprintf(&fname, "/proc/net/xt_quota/%s", quotaName); 630 fp = fopen(fname, "w"); 631 free(fname); 632 if (!fp) { 633 LOGE("Updating quota %s failed (%s)", quotaName, strerror(errno)); 634 return -1; 635 } 636 fprintf(fp, "%lld\n", bytes); 637 fclose(fp); 638 return 0; 639} 640 641int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) { 642 int res = 0; 643 const char *opFlag; 644 char *alertQuotaCmd; 645 646 switch (op) { 647 case IptOpInsert: 648 opFlag = "-I"; 649 break; 650 case IptOpReplace: 651 opFlag = "-R"; 652 break; 653 default: 654 case IptOpDelete: 655 opFlag = "-D"; 656 break; 657 } 658 659 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "INPUT", bytes, alertName, alertName); 660 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); 661 free(alertQuotaCmd); 662 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "OUTPUT", bytes, alertName, alertName); 663 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); 664 free(alertQuotaCmd); 665 return res; 666} 667 668int BandwidthController::setGlobalAlert(int64_t bytes) { 669 char *alertQuotaCmd; 670 const char *alertName = "globalAlert"; 671 int res = 0; 672 673 if (!bytes) { 674 LOGE("Invalid bytes value. 1..max_int64."); 675 return -1; 676 } 677 if (globalAlertBytes) { 678 res = updateQuota(alertName, bytes); 679 } else { 680 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes); 681 } 682 globalAlertBytes = bytes; 683 return res; 684} 685 686int BandwidthController::removeGlobalAlert(void) { 687 char *alertQuotaCmd; 688 689 const char *alertName = "globalAlert"; 690 int res = 0; 691 692 if (!globalAlertBytes) { 693 LOGE("No prior alert set"); 694 return -1; 695 } 696 res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes); 697 globalAlertBytes = 0; 698 return res; 699} 700 701int BandwidthController::setSharedAlert(int64_t bytes) { 702 if (!sharedQuotaBytes) { 703 LOGE("Need to have a prior shared quota set to set an alert"); 704 return -1; 705 } 706 if (!bytes) { 707 LOGE("Invalid bytes value. 1..max_int64."); 708 return -1; 709 } 710 return setCostlyAlert("shared", bytes, &sharedAlertBytes); 711} 712 713int BandwidthController::removeSharedAlert(void) { 714 return removeCostlyAlert("shared", &sharedAlertBytes); 715} 716 717int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) { 718 std::list<QuotaInfo>::iterator it; 719 720 if (!bytes) { 721 LOGE("Invalid bytes value. 1..max_int64."); 722 return -1; 723 } 724 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { 725 if (it->ifaceName == iface) 726 break; 727 } 728 729 if (it == quotaIfaces.end()) { 730 LOGE("Need to have a prior interface quota set to set an alert"); 731 return -1; 732 } 733 734 return setCostlyAlert(iface, bytes, &it->alert); 735} 736 737int BandwidthController::removeInterfaceAlert(const char *iface) { 738 std::list<QuotaInfo>::iterator it; 739 740 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { 741 if (it->ifaceName == iface) 742 break; 743 } 744 745 if (it == quotaIfaces.end()) { 746 LOGE("No prior alert set for interface %s", iface); 747 return -1; 748 } 749 750 return removeCostlyAlert(iface, &it->alert); 751} 752 753int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) { 754 char *alertQuotaCmd; 755 char *chainNameAndPos; 756 int res = 0; 757 char *alertName; 758 759 if (!bytes) { 760 LOGE("Invalid bytes value. 1..max_int64."); 761 return -1; 762 } 763 asprintf(&alertName, "%sAlert", costName); 764 if (*alertBytes) { 765 res = updateQuota(alertName, *alertBytes); 766 } else { 767 asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN); 768 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-I", chainNameAndPos, bytes, alertName, 769 alertName); 770 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); 771 free(alertQuotaCmd); 772 free(chainNameAndPos); 773 } 774 *alertBytes = bytes; 775 free(alertName); 776 return res; 777} 778 779int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) { 780 char *alertQuotaCmd; 781 char *chainName; 782 char *alertName; 783 int res = 0; 784 785 asprintf(&alertName, "%sAlert", costName); 786 if (!*alertBytes) { 787 LOGE("No prior alert set for %s alert", costName); 788 return -1; 789 } 790 791 asprintf(&chainName, "costly_%s", costName); 792 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName, alertName); 793 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); 794 free(alertQuotaCmd); 795 free(chainName); 796 797 *alertBytes = 0; 798 free(alertName); 799 return res; 800} 801