Refactor interface quota code before moving to iptables-restore.
This change substantially refactors the interface and shared
quota code in BandwidthController in preparation for making it
use IptablesRestoreController.
There should be no functional change.
Bug: 28362720
Test: bullhead builds,boots
Test: netd_{unit,integration}_test pass
Test: quota rules are added and removed when quotas are enabled/disabled
Change-Id: I7379fac58da4d98958bb050055a3c6bd7c617aa3
diff --git a/server/BandwidthController.cpp b/server/BandwidthController.cpp
index f1ba45d..89831ff 100644
--- a/server/BandwidthController.cpp
+++ b/server/BandwidthController.cpp
@@ -68,6 +68,7 @@
auto BandwidthController::popenFunction = popen;
auto BandwidthController::iptablesRestoreFunction = execIptablesRestoreWithOutput;
+using android::base::Join;
using android::base::StringAppendF;
using android::base::StringPrintf;
using android::netdutils::StatusOr;
@@ -271,7 +272,7 @@
/* Flush and remove the bw_costly_<iface> tables */
flushExistingCostlyTables(doClean);
- std::string commands = android::base::Join(IPT_FLUSH_COMMANDS, '\n');
+ std::string commands = Join(IPT_FLUSH_COMMANDS, '\n');
iptablesRestoreFunction(V4V6, commands, nullptr);
}
@@ -298,7 +299,7 @@
mSharedQuotaBytes = mSharedAlertBytes = 0;
flushCleanTables(false);
- std::string commands = android::base::Join(IPT_BASIC_ACCOUNTING_COMMANDS, '\n');
+ std::string commands = Join(IPT_BASIC_ACCOUNTING_COMMANDS, '\n');
return iptablesRestoreFunction(V4V6, commands, nullptr);
}
@@ -348,119 +349,11 @@
return iptablesRestoreFunction(V4V6, cmd, nullptr);
}
-std::string BandwidthController::makeIptablesQuotaCmd(IptFullOp op, const std::string& costName,
- int64_t quota) {
- std::string res;
- const char *opFlag;
-
- ALOGV("makeIptablesQuotaCmd(%d, %" PRId64")", op, quota);
-
- switch (op) {
- case IptFullOpInsert:
- opFlag = "-I";
- break;
- case IptFullOpAppend:
- opFlag = "-A";
- break;
- case IptFullOpDelete:
- opFlag = "-D";
- break;
- }
-
- // The requried IP version specific --jump REJECT ... will be added later.
- StringAppendF(&res, "%s bw_costly_%s -m quota2 ! --quota %" PRId64 " --name %s", opFlag,
- costName.c_str(), quota, costName.c_str());
- return res;
-}
-
-int BandwidthController::prepCostlyIface(const std::string& 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 = "bw_costly_";
- costString += ifn;
- costCString = costString.c_str();
- /*
- * Creating a new bw_costly_<iface> is allowed to fail in case it existed.
- * Regardless of whether it previously existed or not, flushing it should never fail.
- * This helps with netd restarts.
- */
- snprintf(cmd, sizeof(cmd), "-N %s", costCString);
- runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailHide);
- snprintf(cmd, sizeof(cmd), "-F %s", costCString);
- res |= runIpxtablesCmd(cmd, IptJumpNoAdd, IptFailShow);
-
- snprintf(cmd, sizeof(cmd), "-A %s -j bw_penalty_box", costCString);
- res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
- break;
- case QuotaShared:
- costCString = "bw_costly_shared";
- break;
- }
-
- if (mGlobalAlertBytes) {
- /* The alert rule comes 1st */
- ruleInsertPos = 2;
- }
-
- snprintf(cmd, sizeof(cmd), "-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, ifn.c_str(),
- costCString);
- res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
-
- snprintf(cmd, sizeof(cmd), "-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, ifn.c_str(),
- costCString);
- res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
-
- snprintf(cmd, sizeof(cmd), "-A bw_FORWARD -o %s --jump %s", ifn.c_str(), costCString);
- res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
-
- return res;
-}
-
-int BandwidthController::cleanupCostlyIface(const std::string& 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;
- }
-
- snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn.c_str(), costCString);
- res |= runIpxtablesCmd(cmd, IptJumpNoAdd);
- for (const auto tableName : {LOCAL_OUTPUT, LOCAL_FORWARD}) {
- snprintf(cmd, sizeof(cmd), "-D %s -o %s --jump %s", tableName, ifn.c_str(), 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;
-}
-
int BandwidthController::setInterfaceSharedQuota(const std::string& iface, int64_t maxBytes) {
int res = 0;
std::string quotaCmd;
- const char costName[] = "shared";
+ constexpr char cost[] = "shared";
+ constexpr char chain[] = "bw_costly_shared";
if (!maxBytes) {
/* Don't talk about -1, deprecate it. */
@@ -477,45 +370,48 @@
auto it = mSharedQuotaIfaces.find(iface);
if (it == mSharedQuotaIfaces.end()) {
- res |= prepCostlyIface(iface, QuotaShared);
- if (mSharedQuotaIfaces.empty()) {
- quotaCmd = makeIptablesQuotaCmd(IptFullOpInsert, costName, maxBytes);
- res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
- if (res) {
- ALOGE("Failed set quota rule");
- goto fail;
- }
- mSharedQuotaBytes = maxBytes;
+ const int ruleInsertPos = (mGlobalAlertBytes) ? 2 : 1;
+ std::vector<std::string> cmds = {
+ StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, iface.c_str(), chain),
+ StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, iface.c_str(), chain),
+ StringPrintf("-A bw_FORWARD -o %s --jump %s", iface.c_str(), chain),
+ };
+ for (const auto& cmd : cmds) {
+ res |= runIpxtablesCmd(cmd.c_str(), IptJumpNoAdd);
}
- mSharedQuotaIfaces.insert(iface);
+ if (mSharedQuotaIfaces.empty()) {
+ res |= runIpxtablesCmd(StringPrintf("-I %s -m quota2 ! --quota %" PRId64
+ " --name %s --jump REJECT",
+ chain, maxBytes, cost).c_str(), IptJumpNoAdd);
+ }
+
+ if (res) {
+ ALOGE("Failed set quota rule");
+ removeInterfaceSharedQuota(iface);
+ return -1;
+ }
+ mSharedQuotaBytes = maxBytes;
+ mSharedQuotaIfaces.insert(iface);
}
if (maxBytes != mSharedQuotaBytes) {
- res |= updateQuota(costName, maxBytes);
+ res |= updateQuota(cost, maxBytes);
if (res) {
- ALOGE("Failed update quota for %s", costName);
- goto fail;
+ ALOGE("Failed update quota for %s", cost);
+ removeInterfaceSharedQuota(iface);
+ return -1;
}
mSharedQuotaBytes = 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(iface);
- return -1;
}
/* It will also cleanup any shared alerts */
int BandwidthController::removeInterfaceSharedQuota(const std::string& iface) {
int res = 0;
- const char costName[] = "shared";
+ constexpr char cost[] = "shared";
+ constexpr char chain[] = "bw_costly_shared";
if (!isIfaceName(iface))
return -1;
@@ -527,26 +423,35 @@
return -1;
}
- res |= cleanupCostlyIface(iface, QuotaShared);
mSharedQuotaIfaces.erase(it);
+ std::vector<std::string> cmds = {
+ StringPrintf("-D bw_INPUT -i %s --jump %s", iface.c_str(), chain),
+ StringPrintf("-D bw_OUTPUT -o %s --jump %s", iface.c_str(), chain),
+ StringPrintf("-D bw_FORWARD -o %s --jump %s", iface.c_str(), chain),
+ };
+ for (const auto& cmd : cmds) {
+ res |= runIpxtablesCmd(cmd.c_str(), IptJumpNoAdd);
+ }
if (mSharedQuotaIfaces.empty()) {
- std::string quotaCmd;
- quotaCmd = makeIptablesQuotaCmd(IptFullOpDelete, costName, mSharedQuotaBytes);
- res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
+ res |= runIpxtablesCmd(StringPrintf("-D %s -m quota2 ! --quota %" PRIu64
+ " --name %s --jump REJECT",
+ chain, mSharedQuotaBytes, cost).c_str(), IptJumpNoAdd);
+
+
mSharedQuotaBytes = 0;
if (mSharedAlertBytes) {
removeSharedAlert();
mSharedAlertBytes = 0;
}
}
+
return res;
}
int BandwidthController::setInterfaceQuota(const std::string& iface, int64_t maxBytes) {
int res = 0;
- const auto& costName = iface;
- std::string quotaCmd;
+ const std::string& cost = iface;
if (!isIfaceName(iface))
return -1;
@@ -563,42 +468,43 @@
/* Insert ingress quota. */
auto it = mQuotaIfaces.find(iface);
- if (it == mQuotaIfaces.end()) {
- /* Preparing the iface adds a penalty/happy box check */
- res |= prepCostlyIface(iface, 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(IptFullOpAppend, costName, maxBytes);
- res |= runIpxtablesCmd(quotaCmd.c_str(), IptJumpReject);
- if (res) {
- ALOGE("Failed set quota rule");
- goto fail;
- }
-
- mQuotaIfaces[iface] = QuotaInfo{maxBytes, 0};
-
- } else {
- res |= updateQuota(costName, maxBytes);
+ if (it != mQuotaIfaces.end()) {
+ res |= updateQuota(cost, maxBytes);
if (res) {
ALOGE("Failed update quota for %s", iface.c_str());
- goto fail;
+ removeInterfaceQuota(iface);
+ return -1;
}
it->second.quota = maxBytes;
+ return 0;
}
- 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(iface);
- return -1;
+ const std::string chain = "bw_costly_" + iface;
+ const int ruleInsertPos = (mGlobalAlertBytes) ? 2 : 1;
+ std::vector<std::string> cmds = {
+ StringPrintf("-N %s", chain.c_str()),
+ StringPrintf("-F %s", chain.c_str()),
+ StringPrintf("-A %s -j bw_penalty_box", chain.c_str()),
+ StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, iface.c_str(),
+ chain.c_str()),
+ StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, iface.c_str(),
+ chain.c_str()),
+ StringPrintf("-A bw_FORWARD -o %s --jump %s", iface.c_str(), chain.c_str()),
+ StringPrintf("-A %s -m quota2 ! --quota %" PRId64 " --name %s --jump REJECT",
+ chain.c_str(), maxBytes, cost.c_str()),
+ };
+
+ for (const auto& cmd : cmds) {
+ res |= runIpxtablesCmd(cmd.c_str(), IptJumpNoAdd);
+ }
+ if (res) {
+ ALOGE("Failed set quota rule");
+ removeInterfaceQuota(iface);
+ return -1;
+ }
+
+ mQuotaIfaces[iface] = QuotaInfo{maxBytes, 0};
+ return 0;
}
int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
@@ -638,8 +544,17 @@
return -1;
}
- /* This also removes the quota command of CostlyIface chain. */
- res |= cleanupCostlyIface(iface, QuotaUnique);
+ const std::string chain = "bw_costly_" + iface;
+ std::vector<std::string> cmds = {
+ StringPrintf("-D bw_INPUT -i %s --jump %s", iface.c_str(), chain.c_str()),
+ StringPrintf("-D bw_OUTPUT -o %s --jump %s", iface.c_str(), chain.c_str()),
+ StringPrintf("-D bw_FORWARD -o %s --jump %s", iface.c_str(), chain.c_str()),
+ StringPrintf("-F %s", chain.c_str()),
+ StringPrintf("-X %s", chain.c_str()),
+ };
+ for (const auto& cmd : cmds) {
+ res |= runIpxtablesCmd(cmd.c_str(), IptJumpNoAdd);
+ }
mQuotaIfaces.erase(it);
@@ -1105,7 +1020,7 @@
}
clearCommands.push_back("COMMIT\n");
- iptablesRestoreFunction(V4V6, android::base::Join(clearCommands, '\n'), nullptr);
+ iptablesRestoreFunction(V4V6, Join(clearCommands, '\n'), nullptr);
}
inline const char *BandwidthController::opToString(IptOp op) {