int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) { int res = 0; const char *opFlag; const char *ifaceLimiting; char *alertQuotaCmd; switch (op) { case IptOpInsert: opFlag = "-I"; break; case IptOpReplace: opFlag = "-R"; break; default: case IptOpDelete: opFlag = "-D"; break; } ifaceLimiting = "! -i lo+"; asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "INPUT", bytes, alertName, alertName); res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); free(alertQuotaCmd); ifaceLimiting = "! -o lo+"; asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "OUTPUT", bytes, alertName, alertName); res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); free(alertQuotaCmd); return res; }
int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) { char cmd[MAX_CMD_LEN]; int res = 0; std::string costString; const char *costCString; switch (quotaType) { case QuotaUnique: costString = "bw_costly_"; costString += ifn; costCString = costString.c_str(); break; case QuotaShared: costCString = "bw_costly_shared"; break; default: ALOGE("Unexpected quotatype %d", quotaType); return -1; } snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString); res |= runIpxtablesCmd(cmd, IptJumpNoAdd); snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString); res |= runIpxtablesCmd(cmd, IptJumpNoAdd); /* The "-N bw_costly_shared" is created upfront, no need to handle it here. */ if (quotaType == QuotaUnique) { snprintf(cmd, sizeof(cmd), "-F %s", costCString); res |= runIpxtablesCmd(cmd, IptJumpNoAdd); snprintf(cmd, sizeof(cmd), "-X %s", costCString); res |= runIpxtablesCmd(cmd, IptJumpNoAdd); } return res; }
void BandwidthController::parseAndFlushCostlyTables(FILE *fp, bool doRemove) { int res; char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN]; char costlyIfaceName[MAX_IPT_OUTPUT_LINE_LEN]; char cmd[MAX_CMD_LEN]; char *buffPtr; while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) { costlyIfaceName[0] = '\0'; /* So that debugging output always works */ res = sscanf(buffPtr, "-N bw_costly_%s", costlyIfaceName); ALOGV("parse res=%d costly=<%s> orig line=<%s>", res, costlyIfaceName, buffPtr); if (res != 1) { continue; } /* Exclusions: "shared" is not an ifacename */ if (!strcmp(costlyIfaceName, "shared")) { continue; } snprintf(cmd, sizeof(cmd), "-F bw_costly_%s", costlyIfaceName); runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide); if (doRemove) { snprintf(cmd, sizeof(cmd), "-X bw_costly_%s", costlyIfaceName); runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide); } } }
int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) { int res = 0; const char *opFlag; char *alertQuotaCmd; switch (op) { case IptOpInsert: opFlag = "-I"; break; case IptOpAppend: opFlag = "-A"; break; case IptOpReplace: opFlag = "-R"; break; default: case IptOpDelete: opFlag = "-D"; break; } asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_INPUT", bytes, alertName); res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd); free(alertQuotaCmd); asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_OUTPUT", bytes, alertName); res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd); free(alertQuotaCmd); return res; }
int BandwidthController::disableHappyBox(void) { char cmd[MAX_CMD_LEN]; /* Best effort */ snprintf(cmd, sizeof(cmd), "-D bw_penalty_box -j bw_happy_box"); runIpxtablesCmd(cmd, IptJumpNoAdd); niceAppUids.clear(); snprintf(cmd, sizeof(cmd), "-F bw_happy_box"); runIpxtablesCmd(cmd, IptJumpNoAdd); snprintf(cmd, sizeof(cmd), "-X bw_happy_box"); runIpxtablesCmd(cmd, IptJumpNoAdd); return 0; }
int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) { char *alertQuotaCmd; char *chainName; char *alertName; int res = 0; if (!isIfaceName(costName)) { ALOGE("removeCostlyAlert: Invalid costName \"%s\"", costName); return -1; } if (!*alertBytes) { ALOGE("No prior alert set for %s alert", costName); return -1; } asprintf(&alertName, "%sAlert", costName); asprintf(&chainName, "bw_costly_%s", costName); asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName); res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd); free(alertQuotaCmd); free(chainName); *alertBytes = 0; free(alertName); return res; }
int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) { char *alertQuotaCmd; char *chainName; int res = 0; char *alertName; if (!isIfaceName(costName)) { ALOGE("setCostlyAlert: Invalid costName \"%s\"", costName); return -1; } if (!bytes) { ALOGE("Invalid bytes value. 1..max_int64."); return -1; } asprintf(&alertName, "%sAlert", costName); if (*alertBytes) { res = updateQuota(alertName, *alertBytes); } else { asprintf(&chainName, "bw_costly_%s", costName); asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-A", chainName, bytes, alertName); res |= runIpxtablesCmd(alertQuotaCmd, IptJumpNoAdd); free(alertQuotaCmd); free(chainName); } *alertBytes = bytes; free(alertName); return res; }
int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) { char *alertQuotaCmd; char *chainNameAndPos; int res = 0; char *alertName; if (!bytes) { LOGE("Invalid bytes value. 1..max_int64."); return -1; } asprintf(&alertName, "%sAlert", costName); if (*alertBytes) { res = updateQuota(alertName, *alertBytes); } else { asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN); asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-I", chainNameAndPos, bytes, alertName, alertName); res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); free(alertQuotaCmd); free(chainNameAndPos); } *alertBytes = bytes; free(alertName); return res; }
int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) { char cmd[MAX_CMD_LEN]; int uidNum; const char *failLogTemplate; IptOp op; int appUids[numUids]; std::string naughtyCmd; switch (appOp) { case NaughtyAppOpAdd: op = IptOpInsert; failLogTemplate = "Failed to add app uid %d to penalty box."; break; case NaughtyAppOpRemove: op = IptOpDelete; failLogTemplate = "Failed to delete app uid %d from penalty box."; break; } for (uidNum = 0; uidNum < numUids; uidNum++) { appUids[uidNum] = atol(appStrUids[uidNum]); if (appUids[uidNum] == 0) { LOGE(failLogTemplate, appUids[uidNum]); goto fail_parse; } } for (uidNum = 0; uidNum < numUids; uidNum++) { naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]); if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) { LOGE(failLogTemplate, appUids[uidNum]); goto fail_with_uidNum; } } return 0; fail_with_uidNum: /* Try to remove the uid that failed in any case*/ naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]); runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd); fail_parse: return -1; }
int BandwidthController::runCommands(int numCommands, const char *commands[], RunCmdErrHandling cmdErrHandling) { int res = 0; LOGV("runCommands(): %d commands", numCommands); for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) { res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd); if (res && cmdErrHandling != RunCmdFailureBad) return res; } return cmdErrHandling == RunCmdFailureBad ? res : 0; }
int BandwidthController::enableHappyBox(void) { char cmd[MAX_CMD_LEN]; int res = 0; /* * We tentatively delete before adding, which helps recovering * from bad states (e.g. netd died). */ /* Should not exist, but ignore result if already there. */ snprintf(cmd, sizeof(cmd), "-N bw_happy_box"); runIpxtablesCmd(cmd, IptJumpNoAdd); /* Should be empty, but clear in case something was wrong. */ niceAppUids.clear(); snprintf(cmd, sizeof(cmd), "-F bw_happy_box"); res |= runIpxtablesCmd(cmd, IptJumpNoAdd); snprintf(cmd, sizeof(cmd), "-D bw_penalty_box -j bw_happy_box"); runIpxtablesCmd(cmd, IptJumpNoAdd); snprintf(cmd, sizeof(cmd), "-A bw_penalty_box -j bw_happy_box"); res |= runIpxtablesCmd(cmd, IptJumpNoAdd); /* Reject. Defaulting to prot-unreachable */ snprintf(cmd, sizeof(cmd), "-D bw_happy_box -j REJECT"); runIpxtablesCmd(cmd, IptJumpNoAdd); snprintf(cmd, sizeof(cmd), "-A bw_happy_box -j REJECT"); res |= runIpxtablesCmd(cmd, IptJumpNoAdd); return res; }
int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) { char cmd[MAX_CMD_LEN]; int res = 0, res1, res2; int ruleInsertPos = 1; std::string costString; const char *costCString; /* The "-N costly" is created upfront, no need to handle it here. */ switch (quotaType) { case QuotaUnique: costString = "bw_costly_"; costString += ifn; costCString = costString.c_str(); /* * Flush the bw_costly_<iface> is allowed to fail in case it didn't exist. * Creating a new one is allowed to fail in case it existed. * This helps with netd restarts. */ snprintf(cmd, sizeof(cmd), "-F %s", costCString); res1 = runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide); snprintf(cmd, sizeof(cmd), "-N %s", costCString); res2 = runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide); res = (res1 && res2) || (!res1 && !res2); snprintf(cmd, sizeof(cmd), "-A %s -j bw_penalty_box", costCString); res |= runIpxtablesCmd(cmd, IptJumpNoAdd); break; case QuotaShared: costCString = "bw_costly_shared"; break; default: ALOGE("Unexpected quotatype %d", quotaType); return -1; } if (globalAlertBytes) { /* The alert rule comes 1st */ ruleInsertPos = 2; } snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString); runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide); snprintf(cmd, sizeof(cmd), "-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, ifn, costCString); res |= runIpxtablesCmd(cmd, IptJumpNoAdd); snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString); runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide); snprintf(cmd, sizeof(cmd), "-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, ifn, costCString); res |= runIpxtablesCmd(cmd, IptJumpNoAdd); return res; }
int BandwidthController::runCommands(int numCommands, const char *commands[], RunCmdErrHandling cmdErrHandling) { int res = 0; IptFailureLog failureLogging = IptFailShow; if (cmdErrHandling == RunCmdFailureOk) { failureLogging = IptFailHide; } ALOGV("runCommands(): %d commands", numCommands); for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) { res = runIpxtablesCmd(commands[cmdNum], IptJumpNoAdd, failureLogging); if (res && cmdErrHandling != RunCmdFailureOk) return res; } return 0; }
/* It will also cleanup any shared alerts */ int BandwidthController::removeInterfaceSharedQuota(const char *iface) { char ifn[MAX_IFACENAME_LEN]; int res = 0; std::string ifaceName; std::list<std::string>::iterator it; const char *costName = "shared"; if (!isIfaceName(iface)) return -1; if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN); return -1; } ifaceName = ifn; for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) { if (*it == ifaceName) break; } if (it == sharedQuotaIfaces.end()) { ALOGE("No such iface %s to delete", ifn); return -1; } res |= cleanupCostlyIface(ifn, QuotaShared); sharedQuotaIfaces.erase(it); if (sharedQuotaIfaces.empty()) { std::string quotaCmd; quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes); res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject); sharedQuotaBytes = 0; if (sharedAlertBytes) { removeSharedAlert(); sharedAlertBytes = 0; } } return res; }
int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) { char *alertQuotaCmd; char *chainName; char *alertName; int res = 0; asprintf(&alertName, "%sAlert", costName); if (!*alertBytes) { LOGE("No prior alert set for %s alert", costName); return -1; } asprintf(&chainName, "costly_%s", costName); asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName, alertName); res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd); free(alertQuotaCmd); free(chainName); *alertBytes = 0; free(alertName); return res; }
int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) { char cmd[MAX_CMD_LEN]; int res = 0; int ruleInsertPos = 1; std::string costString; const char *costCString; /* The "-N costly" is created upfront, no need to handle it here. */ switch (quotaType) { case QuotaUnique: costString = "costly_"; costString += ifn; costCString = costString.c_str(); snprintf(cmd, sizeof(cmd), "-N %s", costCString); res |= runIpxtablesCmd(cmd, IptRejectNoAdd); snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString); res |= runIpxtablesCmd(cmd, IptRejectNoAdd); snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString); res |= runIpxtablesCmd(cmd, IptRejectNoAdd); /* TODO(jpa): Figure out why iptables doesn't correctly return from this * chain. For now, hack the chain exit with an ACCEPT. */ snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString); res |= runIpxtablesCmd(cmd, IptRejectNoAdd); break; case QuotaShared: costCString = "costly_shared"; break; } if (globalAlertBytes) { /* The alert rule comes 1st */ ruleInsertPos = 2; } snprintf(cmd, sizeof(cmd), "-I INPUT %d -i %s --goto %s", ruleInsertPos, ifn, costCString); res |= runIpxtablesCmd(cmd, IptRejectNoAdd); snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString); res |= runIpxtablesCmd(cmd, IptRejectNoAdd); return res; }
int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) { char ifn[MAX_IFACENAME_LEN]; int res = 0; std::string ifaceName; const char *costName; std::list<QuotaInfo>::iterator it; std::string quotaCmd; if (!isIfaceName(iface)) return -1; if (!maxBytes) { /* Don't talk about -1, deprecate it. */ ALOGE("Invalid bytes value. 1..max_int64."); return -1; } if (maxBytes == -1) { return removeInterfaceQuota(iface); } if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN); return -1; } ifaceName = ifn; costName = iface; /* Insert ingress quota. */ for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { if (it->ifaceName == ifaceName) break; } if (it == quotaIfaces.end()) { /* Preparing the iface adds a penalty/happy box check */ res |= prepCostlyIface(ifn, QuotaUnique); /* * The rejecting quota limit should go after the penalty/happy box checks * or else a naughty app could just eat up the quota. * So we append here. */ quotaCmd = makeIptablesQuotaCmd(IptOpAppend, costName, maxBytes); res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject); if (res) { ALOGE("Failed set quota rule"); goto fail; } quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0)); } else { res |= updateQuota(costName, maxBytes); if (res) { ALOGE("Failed update quota for %s", iface); goto fail; } it->quota = maxBytes; } return 0; fail: /* * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse * rules in the kernel to see which ones need cleaning up. * For now callers needs to choose if they want to "ndc bandwidth enable" * which resets everything. */ removeInterfaceSharedQuota(ifn); return -1; }
int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) { char ifn[MAX_IFACENAME_LEN]; int res = 0; std::string quotaCmd; std::string ifaceName; ; const char *costName = "shared"; std::list<std::string>::iterator it; if (!maxBytes) { /* Don't talk about -1, deprecate it. */ ALOGE("Invalid bytes value. 1..max_int64."); return -1; } if (!isIfaceName(iface)) return -1; if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN); return -1; } ifaceName = ifn; if (maxBytes == -1) { return removeInterfaceSharedQuota(ifn); } /* Insert ingress quota. */ for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) { if (*it == ifaceName) break; } if (it == sharedQuotaIfaces.end()) { res |= prepCostlyIface(ifn, QuotaShared); if (sharedQuotaIfaces.empty()) { quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes); res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject); if (res) { ALOGE("Failed set quota rule"); goto fail; } sharedQuotaBytes = maxBytes; } sharedQuotaIfaces.push_front(ifaceName); } if (maxBytes != sharedQuotaBytes) { res |= updateQuota(costName, maxBytes); if (res) { ALOGE("Failed update quota for %s", costName); goto fail; } sharedQuotaBytes = maxBytes; } return 0; fail: /* * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse * rules in the kernel to see which ones need cleaning up. * For now callers needs to choose if they want to "ndc bandwidth enable" * which resets everything. */ removeInterfaceSharedQuota(ifn); return -1; }
int BandwidthController::manipulateSpecialApps(int numUids, char *appStrUids[], const char *chain, std::list<int /*appUid*/> &specialAppUids, IptJumpOp jumpHandling, SpecialAppOp appOp) { int uidNum; const char *failLogTemplate; IptOp op; int appUids[numUids]; std::string iptCmd; std::list<int /*uid*/>::iterator it; switch (appOp) { case SpecialAppOpAdd: op = IptOpInsert; failLogTemplate = "Failed to add app uid %s(%d) to %s."; break; case SpecialAppOpRemove: op = IptOpDelete; failLogTemplate = "Failed to delete app uid %s(%d) from %s box."; break; default: ALOGE("Unexpected app Op %d", appOp); return -1; } for (uidNum = 0; uidNum < numUids; uidNum++) { char *end; appUids[uidNum] = strtoul(appStrUids[uidNum], &end, 0); if (*end || !*appStrUids[uidNum]) { ALOGE(failLogTemplate, appStrUids[uidNum], appUids[uidNum], chain); goto fail_parse; } } for (uidNum = 0; uidNum < numUids; uidNum++) { int uid = appUids[uidNum]; for (it = specialAppUids.begin(); it != specialAppUids.end(); it++) { if (*it == uid) break; } bool found = (it != specialAppUids.end()); if (appOp == SpecialAppOpRemove) { if (!found) { ALOGE("No such appUid %d to remove", uid); return -1; } specialAppUids.erase(it); } else { if (found) { ALOGE("appUid %d exists already", uid); return -1; } specialAppUids.push_front(uid); } iptCmd = makeIptablesSpecialAppCmd(op, uid, chain); if (runIpxtablesCmd(iptCmd.c_str(), jumpHandling)) { ALOGE(failLogTemplate, appStrUids[uidNum], uid, chain); goto fail_with_uidNum; } } return 0; fail_with_uidNum: /* Try to remove the uid that failed in any case*/ iptCmd = makeIptablesSpecialAppCmd(IptOpDelete, appUids[uidNum], chain); runIpxtablesCmd(iptCmd.c_str(), jumpHandling); fail_parse: return -1; }
int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) { char cmd[MAX_CMD_LEN]; int uidNum; const char *failLogTemplate; IptOp op; int appUids[numUids]; std::string naughtyCmd; std::list<int /*uid*/>::iterator it; switch (appOp) { case NaughtyAppOpAdd: op = IptOpInsert; failLogTemplate = "Failed to add app uid %d to penalty box."; break; case NaughtyAppOpRemove: op = IptOpDelete; failLogTemplate = "Failed to delete app uid %d from penalty box."; break; default: ALOGE("Unexpected app Op %d", appOp); return -1; } for (uidNum = 0; uidNum < numUids; uidNum++) { appUids[uidNum] = atol(appStrUids[uidNum]); if (appUids[uidNum] == 0) { ALOGE(failLogTemplate, appUids[uidNum]); goto fail_parse; } } for (uidNum = 0; uidNum < numUids; uidNum++) { int uid = appUids[uidNum]; for (it = naughtyAppUids.begin(); it != naughtyAppUids.end(); it++) { if (*it == uid) break; } bool found = (it != naughtyAppUids.end()); if (appOp == NaughtyAppOpRemove) { if (!found) { ALOGE("No such appUid %d to remove", uid); return -1; } naughtyAppUids.erase(it); } else { if (found) { ALOGE("appUid %d exists already", uid); return -1; } naughtyAppUids.push_front(uid); } naughtyCmd = makeIptablesNaughtyCmd(op, uid); if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) { ALOGE(failLogTemplate, uid); goto fail_with_uidNum; } } return 0; fail_with_uidNum: /* Try to remove the uid that failed in any case*/ naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]); runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd); fail_parse: return -1; }