netd: bandwidthcontroler: add support for alerts via iptables' quota2 log messages
* Fix quota2 updating. The old insert-new/delete-old scheme doesn't work as the kernel
keeps the old counter assigned to the new rule.
* Add support for setting dummy quotas used only for alerts.
This needs:
- new kernel with quota2 changes that support logging via NETLINK's
NETLINK_NFLOG family.
- NetlinkManager support for receiving the events.
- java land handler for these new events.
* new commands
- add/remove a dummy quota to generate an alert after <bytes> are seen including loopback.
alerts are only triggered once.
. ndc bandwidth setglobalalert <bytes>
calling it multiple times, just re-arms the alert for the specified number of bytes.
Use "ndc bandwidth getiquota singleAlert" to get what is left.
. ndc bandwidth removeglobalalert
- add/remove alert on a shared quota (similar accounting as shared quota)
. ndc bandwidth setsharedalert <bytes>
Requires that a shared quota already exist.
. ndc bandwidth removesharedalert
Removing the last of the shared quotas will remove the matching alert.
- add/remove alert on an interface (similar accounting as interface quota)
. ndc bandwidth setinterfacealert <iface> <bytes>
Requires that a interface quota already exist.
. ndc bandwidth removeinterfacealert <iface>
Removing the interface quota will remove the matching alert.
- get the quotas and alert leftovers
. ndc bandwidth getquota
shared quota leftover
. ndc bandwidth getiquota <quota_name_or_iface>
iface specific quota leftover
Can be used to read-out alerts. E.g.
setglobalalert 12345 -> getiquota globalAlert
setsharedalert 12345 -> getiquota sharedAlert
setinterfacealert iface0 12345 -> getiquota iface0Alert
Change-Id: Iea9698b9d20e713281755dac32b4772a6cf0e84e
diff --git a/BandwidthController.cpp b/BandwidthController.cpp
index d4db4a7..441818d 100644
--- a/BandwidthController.cpp
+++ b/BandwidthController.cpp
@@ -14,8 +14,9 @@
* limitations under the License.
*/
-#include <stdlib.h>
+#include <errno.h>
#include <fcntl.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
@@ -40,6 +41,8 @@
const int BandwidthController::MAX_CMD_ARGS = 32;
const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
+const char BandwidthController::ALERT_IPT_TEMPLATE[] = "%s %s -m quota2 ! --quota %lld --name %s";
+const int BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4;
/**
* Some comments about the rules:
@@ -58,6 +61,7 @@
* --jump REJECT --reject-with icmp-net-prohibited
* iptables -A costly_shared --jump penalty_box
* iptables -A costly_shared -m owner --socket-exists
+ *
* . adding a new iface to this, E.g.:
* iptables -I INPUT -i iface1 --goto costly_shared
* iptables -I OUTPUT -o iface1 --goto costly_shared
@@ -113,7 +117,6 @@
};
BandwidthController::BandwidthController(void) {
-
char value[PROPERTY_VALUE_MAX];
property_get("persist.bandwidth.enable", value, "0");
@@ -125,6 +128,7 @@
int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
int res = 0;
+
LOGD("runIpxtablesCmd(cmd=%s)", cmd);
res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
@@ -138,7 +142,8 @@
return buffer[buffSize - 1];
}
-int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, IptIpVer iptVer) {
+int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling,
+ IptIpVer iptVer) {
char buffer[MAX_CMD_LEN];
const char *argv[MAX_CMD_ARGS];
int argc = 0;
@@ -151,11 +156,11 @@
fullCmd += " --jump REJECT --reject-with";
switch (iptVer) {
case IptIpV4:
- fullCmd += " icmp-net-prohibited";
- break;
+ fullCmd += " icmp-net-prohibited";
+ break;
case IptIpV6:
- fullCmd += " icmp6-adm-prohibited";
- break;
+ fullCmd += " icmp6-adm-prohibited";
+ break;
}
}
@@ -188,7 +193,14 @@
/* Some of the initialCommands are allowed to fail */
runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
runCommands(sizeof(setupCommands) / sizeof(char*), setupCommands, RunCmdFailureOk);
- res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands, RunCmdFailureBad);
+ res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands,
+ RunCmdFailureBad);
+
+ sharedQuotaBytes = sharedAlertBytes = 0;
+ sharedQuotaIfaces.clear();
+ quotaIfaces.clear();
+ naughtyAppUids.clear();
+
return res;
}
@@ -199,7 +211,8 @@
return 0;
}
-int BandwidthController::runCommands(int numCommands, const char *commands[], RunCmdErrHandling cmdErrHandling) {
+int BandwidthController::runCommands(int numCommands, const char *commands[],
+ RunCmdErrHandling cmdErrHandling) {
int res = 0;
LOGD("runCommands(): %d commands", numCommands);
for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
@@ -212,25 +225,24 @@
std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
std::string res;
- char *convBuff;
+ char *buff;
+ const char *opFlag;
switch (op) {
- case IptOpInsert:
- res = "-I";
- break;
- case IptOpReplace:
- res = "-R";
- break;
- default:
- case IptOpDelete:
- res = "-D";
- break;
+ case IptOpInsert:
+ opFlag = "-I";
+ break;
+ case IptOpReplace:
+ opFlag = "-R";
+ break;
+ default:
+ case IptOpDelete:
+ opFlag = "-D";
+ break;
}
- res += " penalty_box";
- asprintf(&convBuff, "%d", uid);
- res += " -m owner --uid-owner ";
- res += convBuff;
- free(convBuff);
+ asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid);
+ res = buff;
+ free(buff);
return res;
}
@@ -249,15 +261,16 @@
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;
+ 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;
+ op = IptOpDelete;
+ failLogTemplate = "Failed to delete app uid %d from penalty box.";
+ break;
}
for (uidNum = 0; uidNum < numUids; uidNum++) {
@@ -287,37 +300,36 @@
std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
std::string res;
- char *convBuff;
+ char *buff;
+ const char *opFlag;
- LOGD("makeIptablesQuotaCmd(%d, %llu)", op, quota);
+ LOGD("makeIptablesQuotaCmd(%d, %lld)", op, quota);
switch (op) {
- case IptOpInsert:
- res = "-I";
- break;
- case IptOpReplace:
- res = "-R";
- break;
- default:
- case IptOpDelete:
- res = "-D";
- break;
+ case IptOpInsert:
+ opFlag = "-I";
+ break;
+ case IptOpReplace:
+ opFlag = "-R";
+ break;
+ default:
+ case IptOpDelete:
+ opFlag = "-D";
+ break;
}
- res += " costly_";
- res += costName;
- asprintf(&convBuff, "%lld", quota);
- res += " -m quota2 --name ";
- res += costName;
- res += " ! --quota ";
- res += convBuff;
- free(convBuff);
+
// The requried IP version specific --jump REJECT ... will be added later.
+ asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota,
+ costName);
+ res = buff;
+ free(buff);
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;
@@ -344,9 +356,13 @@
break;
}
- snprintf(cmd, sizeof(cmd), "-I INPUT -i %s --goto %s", ifn, costCString);
+ 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 -o %s --goto %s", ifn, costCString);
+ snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString);
res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
return res;
}
@@ -388,10 +404,16 @@
char ifn[MAX_IFACENAME_LEN];
int res = 0;
std::string quotaCmd;
- std::string ifaceName;;
+ std::string ifaceName;
+ ;
const char *costName = "shared";
std::list<std::string>::iterator it;
+ if (!maxBytes) {
+ /* Don't talk about -1, deprecate it. */
+ LOGE("Invalid bytes value. 1..max_int64.");
+ return -1;
+ }
if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
return -1;
@@ -414,7 +436,7 @@
quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
if (res) {
- LOGE("Failed set quota rule.");
+ LOGE("Failed set quota rule");
goto fail;
}
sharedQuotaBytes = maxBytes;
@@ -424,18 +446,9 @@
}
if (maxBytes != sharedQuotaBytes) {
- /* Instead of replacing, which requires being aware of the rules in
- * the kernel, we just add a new one, then delete the older one.
- */
-
- quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
- res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
-
- quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
- res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
-
+ res |= updateQuota(costName, maxBytes);
if (res) {
- LOGE("Failed replace quota rule.");
+ LOGE("Failed update quota for %s", costName);
goto fail;
}
sharedQuotaBytes = maxBytes;
@@ -453,6 +466,7 @@
return -1;
}
+/* It will also cleanup any shared alerts */
int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
char ifn[MAX_IFACENAME_LEN];
int res = 0;
@@ -460,18 +474,18 @@
std::list<std::string>::iterator it;
const char *costName = "shared";
- if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
+ if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
return -1;
}
- ifaceName =ifn;
+ ifaceName = ifn;
for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
if (*it == ifaceName)
break;
}
if (it == sharedQuotaIfaces.end()) {
- LOGE("No such iface %s to delete.", ifn);
+ LOGE("No such iface %s to delete", ifn);
return -1;
}
@@ -482,9 +496,12 @@
std::string quotaCmd;
quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
- sharedQuotaBytes = -1;
+ sharedQuotaBytes = 0;
+ if (sharedAlertBytes) {
+ removeSharedAlert();
+ sharedAlertBytes = 0;
+ }
}
-
return res;
}
@@ -496,21 +513,25 @@
std::list<QuotaInfo>::iterator it;
std::string quotaCmd;
+ if (!maxBytes) {
+ /* Don't talk about -1, deprecate it. */
+ LOGE("Invalid bytes value. 1..max_int64.");
+ return -1;
+ }
if (maxBytes == -1) {
return removeInterfaceQuota(iface);
}
- if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
+ if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
LOGE("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->first == ifaceName)
+ if (it->ifaceName == ifaceName)
break;
}
@@ -519,27 +540,19 @@
quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
if (res) {
- LOGE("Failed set quota rule.");
+ LOGE("Failed set quota rule");
goto fail;
}
- quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes));
+ quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
} else {
- /* Instead of replacing, which requires being aware of the rules in
- * the kernel, we just add a new one, then delete the older one.
- */
- quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
- res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
-
- quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, it->second);
- res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
-
+ res |= updateQuota(costName, maxBytes);
if (res) {
- LOGE("Failed replace quota rule.");
+ LOGE("Failed update quota for %s", iface);
goto fail;
}
- it->second = maxBytes;
+ it->quota = maxBytes;
}
return 0;
@@ -554,6 +567,28 @@
return -1;
}
+int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
+ return getInterfaceQuota("shared", bytes);
+}
+
+int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
+ FILE *fp;
+ char *fname;
+ int scanRes;
+
+ asprintf(&fname, "/proc/net/xt_quota/%s", costName);
+ fp = fopen(fname, "r");
+ free(fname);
+ if (!fp) {
+ LOGE("Reading quota %s failed (%s)", costName, strerror(errno));
+ return -1;
+ }
+ scanRes = fscanf(fp, "%lld", bytes);
+ LOGD("Read quota res=%d bytes=%lld", scanRes, *bytes);
+ fclose(fp);
+ return scanRes == 1 ? 0 : -1;
+}
+
int BandwidthController::removeInterfaceQuota(const char *iface) {
char ifn[MAX_IFACENAME_LEN];
@@ -562,7 +597,7 @@
const char *costName;
std::list<QuotaInfo>::iterator it;
- if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
+ if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
return -1;
}
@@ -570,12 +605,12 @@
costName = iface;
for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
- if (it->first == ifaceName)
+ if (it->ifaceName == ifaceName)
break;
}
if (it == quotaIfaces.end()) {
- LOGE("No such iface %s to delete.", ifn);
+ LOGE("No such iface %s to delete", ifn);
return -1;
}
@@ -586,3 +621,180 @@
return res;
}
+
+int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
+ FILE *fp;
+ char *fname;
+
+ asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
+ fp = fopen(fname, "w");
+ free(fname);
+ if (!fp) {
+ LOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
+ return -1;
+ }
+ fprintf(fp, "%lld\n", bytes);
+ fclose(fp);
+ return 0;
+}
+
+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 IptOpReplace:
+ opFlag = "-R";
+ break;
+ default:
+ case IptOpDelete:
+ opFlag = "-D";
+ break;
+ }
+
+ asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "INPUT", bytes, alertName, alertName);
+ res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
+ free(alertQuotaCmd);
+ asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "OUTPUT", bytes, alertName, alertName);
+ res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
+ free(alertQuotaCmd);
+ return res;
+}
+
+int BandwidthController::setGlobalAlert(int64_t bytes) {
+ char *alertQuotaCmd;
+ const char *alertName = "globalAlert";
+ int res = 0;
+
+ if (!bytes) {
+ LOGE("Invalid bytes value. 1..max_int64.");
+ return -1;
+ }
+ if (globalAlertBytes) {
+ res = updateQuota(alertName, bytes);
+ } else {
+ res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
+ }
+ globalAlertBytes = bytes;
+ return res;
+}
+
+int BandwidthController::removeGlobalAlert(void) {
+ char *alertQuotaCmd;
+
+ const char *alertName = "globalAlert";
+ int res = 0;
+
+ if (!globalAlertBytes) {
+ LOGE("No prior alert set");
+ return -1;
+ }
+ res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
+ globalAlertBytes = 0;
+ return res;
+}
+
+int BandwidthController::setSharedAlert(int64_t bytes) {
+ if (!sharedQuotaBytes) {
+ LOGE("Need to have a prior shared quota set to set an alert");
+ return -1;
+ }
+ if (!bytes) {
+ LOGE("Invalid bytes value. 1..max_int64.");
+ return -1;
+ }
+ return setCostlyAlert("shared", bytes, &sharedAlertBytes);
+}
+
+int BandwidthController::removeSharedAlert(void) {
+ return removeCostlyAlert("shared", &sharedAlertBytes);
+}
+
+int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
+ std::list<QuotaInfo>::iterator it;
+
+ if (!bytes) {
+ LOGE("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()) {
+ LOGE("Need to have a prior interface quota set to set an alert");
+ return -1;
+ }
+
+ return setCostlyAlert(iface, bytes, &it->alert);
+}
+
+int BandwidthController::removeInterfaceAlert(const char *iface) {
+ std::list<QuotaInfo>::iterator it;
+
+ for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
+ if (it->ifaceName == iface)
+ break;
+ }
+
+ if (it == quotaIfaces.end()) {
+ LOGE("No prior alert set for interface %s", iface);
+ return -1;
+ }
+
+ return removeCostlyAlert(iface, &it->alert);
+}
+
+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::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;
+}
diff --git a/BandwidthController.h b/BandwidthController.h
index f4fce50..70a4e1e 100644
--- a/BandwidthController.h
+++ b/BandwidthController.h
@@ -26,17 +26,34 @@
int disableBandwidthControl(void);
int setInterfaceSharedQuota(const char *iface, int64_t bytes);
+ int getInterfaceSharedQuota(int64_t *bytes);
int removeInterfaceSharedQuota(const char *iface);
int setInterfaceQuota(const char *iface, int64_t bytes);
+ int getInterfaceQuota(const char *iface, int64_t *bytes);
int removeInterfaceQuota(const char *iface);
int addNaughtyApps(int numUids, char *appUids[]);
int removeNaughtyApps(int numUids, char *appUids[]);
+ int setGlobalAlert(int64_t bytes);
+ int removeGlobalAlert(void);
+
+ int setSharedAlert(int64_t bytes);
+ int removeSharedAlert(void);
+
+ int setInterfaceAlert(const char *iface, int64_t bytes);
+ int removeInterfaceAlert(const char *iface);
protected:
- typedef std::pair<std::string /*ifaceName*/, int64_t /*quota*/> QuotaInfo;
+ class QuotaInfo {
+ public:
+ QuotaInfo(std::string ifn, int64_t q, int64_t a)
+ : ifaceName(ifn), quota(q), alert(a) {};
+ std::string ifaceName;
+ int64_t quota;
+ int64_t alert;
+ };
enum IptIpVer { IptIpV4, IptIpV6 };
enum IptOp { IptOpInsert, IptOpReplace, IptOpDelete };
enum IptRejectOp { IptRejectAdd, IptRejectNoAdd };
@@ -44,8 +61,9 @@
enum QuotaType { QuotaUnique, QuotaShared };
enum RunCmdErrHandling { RunCmdFailureBad, RunCmdFailureOk };
- int64_t sharedQuotaBytes;
std::list<std::string> sharedQuotaIfaces;
+ int64_t sharedQuotaBytes;
+ int64_t sharedAlertBytes;
std::list<QuotaInfo> quotaIfaces;
@@ -58,6 +76,8 @@
std::string makeIptablesNaughtyCmd(IptOp op, int uid);
std::string makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota);
+ int runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes);
+
/* Runs for both ipv4 and ipv6 iptables */
int runCommands(int numCommands, const char *commands[], RunCmdErrHandling cmdErrHandling);
/* Runs for both ipv4 and ipv6 iptables, appends -j REJECT --reject-with ... */
@@ -67,6 +87,13 @@
// Provides strncpy() + check overflow.
static int StrncpyAndCheck(char *buffer, const char *src, size_t buffSize);
+ int updateQuota(const char *alertName, int64_t bytes);
+
+ int64_t globalAlertBytes;
+ int setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes);
+ int removeCostlyAlert(const char *costName, int64_t *alertBytes);
+
+
private:
static const char *cleanupCommands[];
static const char *setupCommands[];
@@ -76,7 +103,8 @@
static const int MAX_CMD_ARGS;
static const char IPTABLES_PATH[];
static const char IP6TABLES_PATH[];
-
+ static const char ALERT_IPT_TEMPLATE[];
+ static const int ALERT_RULE_POS_IN_COSTLY_CHAIN;
};
#endif
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 9236605..9e2374c 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -835,6 +835,40 @@
}
rc = sBandwidthCtrl->removeInterfaceSharedQuota(argv[2]);
+ } else if (!strcmp(argv[1], "getquota") || !strcmp(argv[1], "gq")) {
+ int64_t bytes;
+ if (argc != 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: bandwidth getquota", false);
+ return 0;
+ }
+ rc = sBandwidthCtrl->getInterfaceSharedQuota(&bytes);
+ if (rc) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Failed to get quota", true);
+ return 0;
+ }
+ char *msg;
+ asprintf(&msg, "%lld", bytes);
+ cli->sendMsg(ResponseCode::QuotaCounterResult, msg, false);
+ free(msg);
+
+ } else if (!strcmp(argv[1], "getiquota") || !strcmp(argv[1], "giq")) {
+ int64_t bytes;
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: bandwidth getiquota <iface>", false);
+ return 0;
+ }
+ rc = sBandwidthCtrl->getInterfaceQuota(argv[2], &bytes);
+ if (rc) {
+ cli->sendMsg(ResponseCode::OperationFailed, "Failed to get quota", true);
+ return 0;
+ }
+ char *msg;
+ asprintf(&msg, "%lld", bytes);
+ cli->sendMsg(ResponseCode::QuotaCounterResult, msg, false);
+ free(msg);
+
} else if (!strcmp(argv[1], "setquota") || !strcmp(argv[1], "sq")) {
if (argc != 4) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
@@ -849,7 +883,7 @@
"Usage: bandwidth setquotas <bytes> <interface> ...", false);
return 0;
}
- for (int q=3; argc >= 4; q++, argc--) {
+ for (int q = 3; argc >= 4; q++, argc--) {
rc = sBandwidthCtrl->setInterfaceSharedQuota(argv[q], atoll(argv[2]));
if (rc) {
char *msg;
@@ -867,7 +901,7 @@
"Usage: bandwidth removequotas <interface> ...", false);
return 0;
}
- for (int q=2; argc >= 3; q++, argc--) {
+ for (int q = 2; argc >= 3; q++, argc--) {
rc = sBandwidthCtrl->removeInterfaceSharedQuota(argv[q]);
if (rc) {
char *msg;
@@ -906,11 +940,59 @@
} else if (!strcmp(argv[1], "removenaughtyapps") || !strcmp(argv[1], "rna")) {
if (argc < 3) {
cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: bandwidth remnaughtyapps <appUid> ...", false);
+ "Usage: bandwidth removenaughtyapps <appUid> ...", false);
return 0;
}
rc = sBandwidthCtrl->removeNaughtyApps(argc - 2, argv + 2);
+ } else if (!strcmp(argv[1], "setglobalalert") || !strcmp(argv[1], "sga")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: bandwidth setglobalalert <bytes>", false);
+ return 0;
+ }
+ rc = sBandwidthCtrl->setGlobalAlert(atoll(argv[2]));
+
+ } else if (!strcmp(argv[1], "removeglobalalert") || !strcmp(argv[1], "rga")) {
+ if (argc != 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: bandwidth removeglobalalert", false);
+ return 0;
+ }
+ rc = sBandwidthCtrl->removeGlobalAlert();
+
+ } else if (!strcmp(argv[1], "setsharedalert") || !strcmp(argv[1], "ssa")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: bandwidth setsharedalert <bytes>", false);
+ return 0;
+ }
+ rc = sBandwidthCtrl->setSharedAlert(atoll(argv[2]));
+
+ } else if (!strcmp(argv[1], "removesharedalert") || !strcmp(argv[1], "rsa")) {
+ if (argc != 2) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: bandwidth removesharedalert", false);
+ return 0;
+ }
+ rc = sBandwidthCtrl->removeSharedAlert();
+
+ } else if (!strcmp(argv[1], "setinterfacealert") || !strcmp(argv[1], "sia")) {
+ if (argc != 4) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: bandwidth setinterfacealert <interface> <bytes>", false);
+ return 0;
+ }
+ rc = sBandwidthCtrl->setInterfaceAlert(argv[2], atoll(argv[3]));
+
+ } else if (!strcmp(argv[1], "removeinterfacealert") || !strcmp(argv[1], "ria")) {
+ if (argc != 3) {
+ cli->sendMsg(ResponseCode::CommandSyntaxError,
+ "Usage: bandwidth removeinterfacealert <interface>", false);
+ return 0;
+ }
+ rc = sBandwidthCtrl->removeInterfaceAlert(argv[2]);
+
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown bandwidth cmd", false);
return 0;
diff --git a/ResponseCode.h b/ResponseCode.h
index 320b6a4..be60eda 100644
--- a/ResponseCode.h
+++ b/ResponseCode.h
@@ -18,6 +18,8 @@
#define _RESPONSECODE_H
class ResponseCode {
+ // Keep in sync with
+ // frameworks/base/services/java/com/android/server/NetworkManagementService.java
public:
// 100 series - Requestion action was initiated; expect another reply
// before proceeding with a new command.
@@ -40,6 +42,7 @@
static const int InterfaceTxCounterResult = 217;
static const int InterfaceRxThrottleResult = 218;
static const int InterfaceTxThrottleResult = 219;
+ static const int QuotaCounterResult = 220;
// 400 series - The command was accepted but the requested action
// did not take place.