int BandwidthController::removeInterfaceQuota(const char *iface) { char ifn[MAX_IFACENAME_LEN]; int res = 0; std::string ifaceName; const char *costName; std::list<QuotaInfo>::iterator it; if (!isIfaceName(iface)) return -1; if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) { ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN); return -1; } ifaceName = ifn; costName = iface; for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { if (it->ifaceName == ifaceName) break; } if (it == quotaIfaces.end()) { ALOGE("No such iface %s to delete", ifn); return -1; } /* This also removes the quota command of CostlyIface chain. */ res |= cleanupCostlyIface(ifn, QuotaUnique); quotaIfaces.erase(it); return res; }
int BandwidthController::runIptablesCmd(const char *cmd, IptJumpOp jumpHandling, IptIpVer iptVer, IptFailureLog failureHandling) { char buffer[MAX_CMD_LEN]; const char *argv[MAX_CMD_ARGS]; int argc = 0; char *next = buffer; char *tmp; int res; int status = 0; std::string fullCmd = cmd; switch (jumpHandling) { case IptJumpReject: /* * Must be carefull what one rejects with, as uper layer protocols will just * keep on hammering the device until the number of retries are done. * For port-unreachable (default), TCP should consider as an abort (RFC1122). */ fullCmd += " --jump REJECT"; break; case IptJumpReturn: fullCmd += " --jump RETURN"; break; case IptJumpNoAdd: break; } fullCmd.insert(0, " -w "); fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH); if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) { ALOGE("iptables command too long"); return -1; } argc = 0; while ((tmp = strsep(&next, " "))) { argv[argc++] = tmp; if (argc >= MAX_CMD_ARGS) { ALOGE("iptables argument overflow"); return -1; } } argv[argc] = NULL; res = android_fork_execvp(argc, (char **)argv, &status, false, failureHandling == IptFailShow); res = res || !WIFEXITED(status) || WEXITSTATUS(status); if (res && failureHandling == IptFailShow) { ALOGE("runIptablesCmd(): res=%d status=%d failed %s", res, status, fullCmd.c_str()); } return res; }
int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, IptIpVer iptVer, IptFailureLog failureHandling) { char buffer[MAX_CMD_LEN]; const char *argv[MAX_CMD_ARGS]; int argc = 0; char *next = buffer; char *tmp; int res; std::string fullCmd = cmd; if (rejectHandling == IptRejectAdd) { fullCmd += " --jump REJECT --reject-with"; switch (iptVer) { case IptIpV4: fullCmd += " icmp-net-prohibited"; break; case IptIpV6: fullCmd += " icmp6-adm-prohibited"; break; } } fullCmd.insert(0, " "); fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH); if (!useLogwrapCall) { res = system_nosh(fullCmd.c_str()); } else { if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) { ALOGE("iptables command too long"); return -1; } argc = 0; while ((tmp = strsep(&next, " "))) { argv[argc++] = tmp; if (argc >= MAX_CMD_ARGS) { ALOGE("iptables argument overflow"); return -1; } } argv[argc] = NULL; res = logwrap(argc, argv); } if (res && failureHandling == IptFailShow) { ALOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res); } return res; }
/* 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::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; }