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::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 ClatdController::startClatd(char *interface) { pid_t pid; if(mClatdPid != 0) { ALOGE("clatd already running"); errno = EBUSY; return -1; } if (!isIfaceName(interface)) { errno = ENOENT; return -1; } ALOGD("starting clatd"); if ((pid = fork()) < 0) { ALOGE("fork failed (%s)", strerror(errno)); return -1; } if (!pid) { // Pass in the interface, a netid to use for DNS lookups, and a fwmark for outgoing packets. unsigned netId = mNetCtrl->getNetworkForInterface(interface); char netIdString[UINT32_STRLEN]; snprintf(netIdString, sizeof(netIdString), "%u", netId); Fwmark fwmark; fwmark.netId = netId; fwmark.explicitlySelected = true; fwmark.protectedFromVpn = true; fwmark.permission = PERMISSION_SYSTEM; char fwmarkString[UINT32_HEX_STRLEN]; snprintf(fwmarkString, sizeof(fwmarkString), "0x%x", fwmark.intValue); char *args[] = { (char *) "/system/bin/clatd", (char *) "-i", interface, (char *) "-n", netIdString, (char *) "-m", fwmarkString, NULL }; if (execv(args[0], args)) { ALOGE("execv failed (%s)", strerror(errno)); } ALOGE("Should never get here!"); _exit(0); } else { mClatdPid = pid; ALOGD("clatd started"); } return 0; }
int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) { FILE *fp; char *fname; if (!isIfaceName(quotaName)) { ALOGE("updateQuota: Invalid quotaName \"%s\"", quotaName); return -1; } asprintf(&fname, "/proc/net/xt_quota/%s", quotaName); fp = fopen(fname, "we"); free(fname); if (!fp) { ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno)); return -1; } fprintf(fp, "%" PRId64"\n", bytes); fclose(fp); return 0; }
int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) { FILE *fp; char *fname; int scanRes; if (!isIfaceName(costName)) return -1; asprintf(&fname, "/proc/net/xt_quota/%s", costName); fp = fopen(fname, "re"); free(fname); if (!fp) { ALOGE("Reading quota %s failed (%s)", costName, strerror(errno)); return -1; } scanRes = fscanf(fp, "%" SCNd64, bytes); ALOGV("Read quota res=%d bytes=%" PRId64, scanRes, *bytes); fclose(fp); return scanRes == 1 ? 0 : -1; }
int BandwidthController::removeInterfaceAlert(const char *iface) { std::list<QuotaInfo>::iterator it; if (!isIfaceName(iface)) { ALOGE("removeInterfaceAlert: Invalid iface \"%s\"", iface); return -1; } for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { if (it->ifaceName == iface) break; } if (it == quotaIfaces.end()) { ALOGE("No prior alert set for interface %s", iface); return -1; } return removeCostlyAlert(iface, &it->alert); }
/* 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::setInterfaceAlert(const char *iface, int64_t bytes) { std::list<QuotaInfo>::iterator it; if (!isIfaceName(iface)) { ALOGE("setInterfaceAlert: Invalid iface \"%s\"", iface); return -1; } if (!bytes) { ALOGE("Invalid bytes value. 1..max_int64."); return -1; } for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) { if (it->ifaceName == iface) break; } if (it == quotaIfaces.end()) { ALOGE("Need to have a prior interface quota set to set an alert"); return -1; } return setCostlyAlert(iface, bytes, &it->alert); }
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; }