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