blob: ed32d3dd2c3f79706c758eae89ad0ceb885d9830 [file] [log] [blame]
JP Abgrall4a5f5ca2011-06-15 18:37:39 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
JP Abgrall8a932722011-07-13 19:17:35 -070017#include <errno.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070018#include <fcntl.h>
JP Abgrall8a932722011-07-13 19:17:35 -070019#include <stdlib.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070020#include <string.h>
21
22#include <sys/socket.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <sys/wait.h>
26
27#include <linux/netlink.h>
28#include <linux/rtnetlink.h>
29#include <linux/pkt_sched.h>
30
31#define LOG_TAG "BandwidthController"
32#include <cutils/log.h>
33#include <cutils/properties.h>
34
35extern "C" int logwrap(int argc, const char **argv, int background);
36
37#include "BandwidthController.h"
38
JP Abgrallfa6f46d2011-06-17 23:17:28 -070039const int BandwidthController::MAX_CMD_LEN = 1024;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070040const int BandwidthController::MAX_IFACENAME_LEN = 64;
41const int BandwidthController::MAX_CMD_ARGS = 32;
42const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
JP Abgrallfa6f46d2011-06-17 23:17:28 -070043const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
JP Abgrall87666692011-09-08 13:44:10 -070044const char BandwidthController::ALERT_IPT_TEMPLATE[] = "%s %s %s -m quota2 ! --quota %lld --name %s";
JP Abgrall8a932722011-07-13 19:17:35 -070045const int BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4;
JP Abgrall11b4e9b2011-08-11 15:34:49 -070046bool BandwidthController::useLogwrapCall = false;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070047
48/**
49 * Some comments about the rules:
50 * * Ordering
51 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
52 * E.g. "-I INPUT -i rmnet0 --goto costly"
53 * - quota'd rules in the costly chain should be before penalty_box lookups.
54 *
55 * * global quota vs per interface quota
56 * - global quota for all costly interfaces uses a single costly chain:
57 * . initial rules
JP Abgrallbfa74662011-06-29 19:23:04 -070058 * iptables -N costly_shared
59 * iptables -I INPUT -i iface0 --goto costly_shared
60 * iptables -I OUTPUT -o iface0 --goto costly_shared
61 * iptables -I costly_shared -m quota \! --quota 500000 \
62 * --jump REJECT --reject-with icmp-net-prohibited
63 * iptables -A costly_shared --jump penalty_box
64 * iptables -A costly_shared -m owner --socket-exists
JP Abgrall8a932722011-07-13 19:17:35 -070065 *
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070066 * . adding a new iface to this, E.g.:
JP Abgrallbfa74662011-06-29 19:23:04 -070067 * iptables -I INPUT -i iface1 --goto costly_shared
68 * iptables -I OUTPUT -o iface1 --goto costly_shared
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070069 *
70 * - quota per interface. This is achieve by having "costly" chains per quota.
71 * E.g. adding a new costly interface iface0 with its own quota:
72 * iptables -N costly_iface0
73 * iptables -I INPUT -i iface0 --goto costly_iface0
74 * iptables -I OUTPUT -o iface0 --goto costly_iface0
JP Abgrallbfa74662011-06-29 19:23:04 -070075 * iptables -A costly_iface0 -m quota \! --quota 500000 \
76 * --jump REJECT --reject-with icmp-net-prohibited
77 * iptables -A costly_iface0 --jump penalty_box
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070078 * iptables -A costly_iface0 -m owner --socket-exists
79 *
80 * * penalty_box handling:
81 * - only one penalty_box for all interfaces
82 * E.g Adding an app:
JP Abgrallbfa74662011-06-29 19:23:04 -070083 * iptables -A penalty_box -m owner --uid-owner app_3 \
84 * --jump REJECT --reject-with icmp-net-prohibited
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070085 */
86const char *BandwidthController::cleanupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070087 /* Cleanup rules. */
88 "-F",
89 "-t raw -F",
JP Abgrall39f8f242011-06-29 19:21:58 -070090 /* TODO: If at some point we need more user chains than here, then we will need
91 * a different cleanup approach.
92 */
JP Abgrallbfa74662011-06-29 19:23:04 -070093 "-X", /* Should normally only be costly_shared, penalty_box, and costly_<iface> */
JP Abgrall0dad7c22011-06-24 11:58:14 -070094};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070095
96const char *BandwidthController::setupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070097 /* Created needed chains. */
JP Abgrallbfa74662011-06-29 19:23:04 -070098 "-N costly_shared",
JP Abgrall0dad7c22011-06-24 11:58:14 -070099 "-N penalty_box",
100};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700101
JP Abgrall0dad7c22011-06-24 11:58:14 -0700102const char *BandwidthController::basicAccountingCommands[] = {
103 "-F INPUT",
104 "-A INPUT -i lo --jump ACCEPT",
105 "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700106
JP Abgrall0dad7c22011-06-24 11:58:14 -0700107 "-F OUTPUT",
108 "-A OUTPUT -o lo --jump ACCEPT",
109 "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700110
JP Abgrallbfa74662011-06-29 19:23:04 -0700111 "-F costly_shared",
112 "-A costly_shared --jump penalty_box",
113 "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700114 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
115 * chain. For now, hack the chain exit with an ACCEPT.
116 */
JP Abgrallbfa74662011-06-29 19:23:04 -0700117 "-A costly_shared --jump ACCEPT",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700118};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700119
120BandwidthController::BandwidthController(void) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700121 char value[PROPERTY_VALUE_MAX];
122
123 property_get("persist.bandwidth.enable", value, "0");
124 if (!strcmp(value, "1")) {
125 enableBandwidthControl();
126 }
127
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700128 property_get("persist.bandwidth.uselogwrap", value, "0");
129 useLogwrapCall = !strcmp(value, "1");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700130}
131
JP Abgrall26e0d492011-06-24 19:21:51 -0700132int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700133 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700134
JP Abgrall26e0d492011-06-24 19:21:51 -0700135 LOGD("runIpxtablesCmd(cmd=%s)", cmd);
136 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
137 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700138 return res;
139}
140
JP Abgrall26e0d492011-06-24 19:21:51 -0700141int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
142
143 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0'
144 strncpy(buffer, src, buffSize);
145 return buffer[buffSize - 1];
146}
147
JP Abgrall8a932722011-07-13 19:17:35 -0700148int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling,
149 IptIpVer iptVer) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700150 char buffer[MAX_CMD_LEN];
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700151 const char *argv[MAX_CMD_ARGS];
JP Abgrall26e0d492011-06-24 19:21:51 -0700152 int argc = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700153 char *next = buffer;
154 char *tmp;
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700155 int res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700156
JP Abgrall0dad7c22011-06-24 11:58:14 -0700157 std::string fullCmd = cmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700158
159 if (rejectHandling == IptRejectAdd) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700160 fullCmd += " --jump REJECT --reject-with";
JP Abgrall26e0d492011-06-24 19:21:51 -0700161 switch (iptVer) {
162 case IptIpV4:
JP Abgrall8a932722011-07-13 19:17:35 -0700163 fullCmd += " icmp-net-prohibited";
164 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700165 case IptIpV6:
JP Abgrall8a932722011-07-13 19:17:35 -0700166 fullCmd += " icmp6-adm-prohibited";
167 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700168 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700169 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700170
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700171 fullCmd.insert(0, " ");
172 fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700173
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700174 if (!useLogwrapCall) {
175 res = system(fullCmd.c_str());
176 } else {
177 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
178 LOGE("iptables command too long");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700179 return -1;
180 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700181
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700182 argc = 0;
183 while ((tmp = strsep(&next, " "))) {
184 argv[argc++] = tmp;
185 if (argc >= MAX_CMD_ARGS) {
186 LOGE("iptables argument overflow");
187 return -1;
188 }
189 }
190
191 argv[argc] = NULL;
192 res = logwrap(argc, argv, 0);
193 }
194 if (res) {
195 LOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res);
196 }
197 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700198}
199
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700200int BandwidthController::enableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700201 int res;
202 /* Some of the initialCommands are allowed to fail */
JP Abgrall26e0d492011-06-24 19:21:51 -0700203 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
204 runCommands(sizeof(setupCommands) / sizeof(char*), setupCommands, RunCmdFailureOk);
JP Abgrall8a932722011-07-13 19:17:35 -0700205 res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands,
206 RunCmdFailureBad);
207
208 sharedQuotaBytes = sharedAlertBytes = 0;
209 sharedQuotaIfaces.clear();
210 quotaIfaces.clear();
211 naughtyAppUids.clear();
212
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700213 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700214
215}
216
217int BandwidthController::disableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700218 /* The cleanupCommands are allowed to fail. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700219 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700220 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700221}
222
JP Abgrall8a932722011-07-13 19:17:35 -0700223int BandwidthController::runCommands(int numCommands, const char *commands[],
224 RunCmdErrHandling cmdErrHandling) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700225 int res = 0;
226 LOGD("runCommands(): %d commands", numCommands);
227 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700228 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
229 if (res && cmdErrHandling != RunCmdFailureBad)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700230 return res;
231 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700232 return cmdErrHandling == RunCmdFailureBad ? res : 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700233}
234
JP Abgrall0dad7c22011-06-24 11:58:14 -0700235std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700236 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700237 char *buff;
238 const char *opFlag;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700239
240 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700241 case IptOpInsert:
242 opFlag = "-I";
243 break;
244 case IptOpReplace:
245 opFlag = "-R";
246 break;
247 default:
248 case IptOpDelete:
249 opFlag = "-D";
250 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700251 }
JP Abgrall8a932722011-07-13 19:17:35 -0700252 asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid);
253 res = buff;
254 free(buff);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700255 return res;
256}
257
258int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700259 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700260}
261
262int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700263 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700264}
265
JP Abgrall26e0d492011-06-24 19:21:51 -0700266int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700267 char cmd[MAX_CMD_LEN];
268 int uidNum;
JP Abgrall26e0d492011-06-24 19:21:51 -0700269 const char *failLogTemplate;
270 IptOp op;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700271 int appUids[numUids];
JP Abgrall26e0d492011-06-24 19:21:51 -0700272 std::string naughtyCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700273
JP Abgrall26e0d492011-06-24 19:21:51 -0700274 switch (appOp) {
275 case NaughtyAppOpAdd:
JP Abgrall8a932722011-07-13 19:17:35 -0700276 op = IptOpInsert;
277 failLogTemplate = "Failed to add app uid %d to penalty box.";
278 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700279 case NaughtyAppOpRemove:
JP Abgrall8a932722011-07-13 19:17:35 -0700280 op = IptOpDelete;
281 failLogTemplate = "Failed to delete app uid %d from penalty box.";
282 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700283 }
284
285 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700286 appUids[uidNum] = atol(appStrUids[uidNum]);
287 if (appUids[uidNum] == 0) {
288 LOGE(failLogTemplate, appUids[uidNum]);
289 goto fail_parse;
290 }
291 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700292
293 for (uidNum = 0; uidNum < numUids; uidNum++) {
294 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
295 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
296 LOGE(failLogTemplate, appUids[uidNum]);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700297 goto fail_with_uidNum;
298 }
299 }
300 return 0;
301
JP Abgrall26e0d492011-06-24 19:21:51 -0700302fail_with_uidNum:
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700303 /* Try to remove the uid that failed in any case*/
JP Abgrall26e0d492011-06-24 19:21:51 -0700304 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
305 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
306fail_parse:
307 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700308}
309
JP Abgrall26e0d492011-06-24 19:21:51 -0700310std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700311 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700312 char *buff;
313 const char *opFlag;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700314
JP Abgrall8a932722011-07-13 19:17:35 -0700315 LOGD("makeIptablesQuotaCmd(%d, %lld)", op, quota);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700316
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700317 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700318 case IptOpInsert:
319 opFlag = "-I";
320 break;
321 case IptOpReplace:
322 opFlag = "-R";
323 break;
324 default:
325 case IptOpDelete:
326 opFlag = "-D";
327 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700328 }
JP Abgrall8a932722011-07-13 19:17:35 -0700329
JP Abgrallbfa74662011-06-29 19:23:04 -0700330 // The requried IP version specific --jump REJECT ... will be added later.
JP Abgrall8a932722011-07-13 19:17:35 -0700331 asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota,
332 costName);
333 res = buff;
334 free(buff);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700335 return res;
336}
337
JP Abgrall26e0d492011-06-24 19:21:51 -0700338int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700339 char cmd[MAX_CMD_LEN];
340 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700341 int ruleInsertPos = 1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700342 std::string costString;
343 const char *costCString;
344
JP Abgrall0dad7c22011-06-24 11:58:14 -0700345 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700346 switch (quotaType) {
347 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700348 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700349 costString += ifn;
350 costCString = costString.c_str();
351 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700352 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700353 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700354 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700355 snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700356 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700357 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
358 * chain. For now, hack the chain exit with an ACCEPT.
359 */
360 snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700361 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
362 break;
363 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700364 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700365 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700366 }
367
JP Abgrall8a932722011-07-13 19:17:35 -0700368 if (globalAlertBytes) {
369 /* The alert rule comes 1st */
370 ruleInsertPos = 2;
371 }
372 snprintf(cmd, sizeof(cmd), "-I INPUT %d -i %s --goto %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700373 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700374 snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700375 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700376 return res;
377}
378
JP Abgrall26e0d492011-06-24 19:21:51 -0700379int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700380 char cmd[MAX_CMD_LEN];
381 int res = 0;
382 std::string costString;
383 const char *costCString;
384
JP Abgrall26e0d492011-06-24 19:21:51 -0700385 switch (quotaType) {
386 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700387 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700388 costString += ifn;
389 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700390 break;
391 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700392 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700393 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700394 }
395
396 snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700397 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700398 snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700399 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700400
JP Abgrallbfa74662011-06-29 19:23:04 -0700401 /* The "-N costly_shared" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700402 if (quotaType == QuotaUnique) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700403 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700404 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgralla9f802c2011-06-29 15:46:45 -0700405 snprintf(cmd, sizeof(cmd), "-X %s", costCString);
406 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700407 }
408 return res;
409}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700410
JP Abgrall0dad7c22011-06-24 11:58:14 -0700411int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700412 char cmd[MAX_CMD_LEN];
413 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700414 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700415 std::string quotaCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700416 std::string ifaceName;
417 ;
JP Abgrallbfa74662011-06-29 19:23:04 -0700418 const char *costName = "shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700419 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700420
JP Abgrall8a932722011-07-13 19:17:35 -0700421 if (!maxBytes) {
422 /* Don't talk about -1, deprecate it. */
423 LOGE("Invalid bytes value. 1..max_int64.");
424 return -1;
425 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700426 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
427 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
428 return -1;
429 }
430 ifaceName = ifn;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700431
432 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700433 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700434 }
435
436 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700437 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
438 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700439 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700440 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700441
JP Abgrall0dad7c22011-06-24 11:58:14 -0700442 if (it == sharedQuotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700443 res |= prepCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700444 if (sharedQuotaIfaces.empty()) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700445 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700446 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700447 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700448 LOGE("Failed set quota rule");
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700449 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700450 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700451 sharedQuotaBytes = maxBytes;
452 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700453 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700454
455 }
456
457 if (maxBytes != sharedQuotaBytes) {
JP Abgrall8a932722011-07-13 19:17:35 -0700458 res |= updateQuota(costName, maxBytes);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700459 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700460 LOGE("Failed update quota for %s", costName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700461 goto fail;
462 }
463 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700464 }
465 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700466
467 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700468 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700469 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
470 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700471 * For now callers needs to choose if they want to "ndc bandwidth enable"
472 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700473 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700474 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700475 return -1;
476}
477
JP Abgrall8a932722011-07-13 19:17:35 -0700478/* It will also cleanup any shared alerts */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700479int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700480 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700481 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700482 std::string ifaceName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700483 std::list<std::string>::iterator it;
JP Abgrallbfa74662011-06-29 19:23:04 -0700484 const char *costName = "shared";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700485
JP Abgrall8a932722011-07-13 19:17:35 -0700486 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700487 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
488 return -1;
489 }
JP Abgrall8a932722011-07-13 19:17:35 -0700490 ifaceName = ifn;
JP Abgrall26e0d492011-06-24 19:21:51 -0700491
JP Abgrall0dad7c22011-06-24 11:58:14 -0700492 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
493 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700494 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700495 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700496 if (it == sharedQuotaIfaces.end()) {
JP Abgrall8a932722011-07-13 19:17:35 -0700497 LOGE("No such iface %s to delete", ifn);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700498 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700499 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700500
JP Abgrall26e0d492011-06-24 19:21:51 -0700501 res |= cleanupCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700502 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700503
JP Abgrall0dad7c22011-06-24 11:58:14 -0700504 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700505 std::string quotaCmd;
JP Abgrallbfa74662011-06-29 19:23:04 -0700506 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700507 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700508 sharedQuotaBytes = 0;
509 if (sharedAlertBytes) {
510 removeSharedAlert();
511 sharedAlertBytes = 0;
512 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700513 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700514 return res;
515}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700516
517int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
518 char ifn[MAX_IFACENAME_LEN];
519 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700520 std::string ifaceName;
521 const char *costName;
522 std::list<QuotaInfo>::iterator it;
523 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700524
JP Abgrall8a932722011-07-13 19:17:35 -0700525 if (!maxBytes) {
526 /* Don't talk about -1, deprecate it. */
527 LOGE("Invalid bytes value. 1..max_int64.");
528 return -1;
529 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700530 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700531 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700532 }
533
JP Abgrall8a932722011-07-13 19:17:35 -0700534 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700535 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
536 return -1;
537 }
538 ifaceName = ifn;
539 costName = iface;
540
JP Abgrall0dad7c22011-06-24 11:58:14 -0700541 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700542 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700543 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700544 break;
545 }
546
547 if (it == quotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700548 res |= prepCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700549 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700550 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700551 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700552 LOGE("Failed set quota rule");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700553 goto fail;
554 }
555
JP Abgrall8a932722011-07-13 19:17:35 -0700556 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
JP Abgrall0dad7c22011-06-24 11:58:14 -0700557
558 } else {
JP Abgrall8a932722011-07-13 19:17:35 -0700559 res |= updateQuota(costName, maxBytes);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700560 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700561 LOGE("Failed update quota for %s", iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700562 goto fail;
563 }
JP Abgrall8a932722011-07-13 19:17:35 -0700564 it->quota = maxBytes;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700565 }
566 return 0;
567
568 fail:
569 /*
570 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
571 * rules in the kernel to see which ones need cleaning up.
572 * For now callers needs to choose if they want to "ndc bandwidth enable"
573 * which resets everything.
574 */
575 removeInterfaceSharedQuota(ifn);
576 return -1;
577}
578
JP Abgrall8a932722011-07-13 19:17:35 -0700579int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
580 return getInterfaceQuota("shared", bytes);
581}
582
583int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
584 FILE *fp;
585 char *fname;
586 int scanRes;
587
588 asprintf(&fname, "/proc/net/xt_quota/%s", costName);
589 fp = fopen(fname, "r");
590 free(fname);
591 if (!fp) {
592 LOGE("Reading quota %s failed (%s)", costName, strerror(errno));
593 return -1;
594 }
595 scanRes = fscanf(fp, "%lld", bytes);
596 LOGD("Read quota res=%d bytes=%lld", scanRes, *bytes);
597 fclose(fp);
598 return scanRes == 1 ? 0 : -1;
599}
600
JP Abgrall0dad7c22011-06-24 11:58:14 -0700601int BandwidthController::removeInterfaceQuota(const char *iface) {
602
603 char ifn[MAX_IFACENAME_LEN];
604 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700605 std::string ifaceName;
606 const char *costName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700607 std::list<QuotaInfo>::iterator it;
JP Abgrall26e0d492011-06-24 19:21:51 -0700608
JP Abgrall8a932722011-07-13 19:17:35 -0700609 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700610 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
611 return -1;
612 }
613 ifaceName = ifn;
614 costName = iface;
615
JP Abgrall0dad7c22011-06-24 11:58:14 -0700616 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700617 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700618 break;
619 }
620
621 if (it == quotaIfaces.end()) {
JP Abgrall8a932722011-07-13 19:17:35 -0700622 LOGE("No such iface %s to delete", ifn);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700623 return -1;
624 }
625
626 /* This also removes the quota command of CostlyIface chain. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700627 res |= cleanupCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700628
629 quotaIfaces.erase(it);
630
631 return res;
632}
JP Abgrall8a932722011-07-13 19:17:35 -0700633
634int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
635 FILE *fp;
636 char *fname;
637
638 asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
639 fp = fopen(fname, "w");
640 free(fname);
641 if (!fp) {
642 LOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
643 return -1;
644 }
645 fprintf(fp, "%lld\n", bytes);
646 fclose(fp);
647 return 0;
648}
649
650int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
651 int res = 0;
652 const char *opFlag;
JP Abgrall87666692011-09-08 13:44:10 -0700653 const char *ifaceLimiting;
JP Abgrall8a932722011-07-13 19:17:35 -0700654 char *alertQuotaCmd;
655
656 switch (op) {
657 case IptOpInsert:
658 opFlag = "-I";
659 break;
660 case IptOpReplace:
661 opFlag = "-R";
662 break;
663 default:
664 case IptOpDelete:
665 opFlag = "-D";
666 break;
667 }
668
JP Abgrall87666692011-09-08 13:44:10 -0700669 ifaceLimiting = "! -i lo+";
670 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "INPUT",
671 bytes, alertName, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700672 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
673 free(alertQuotaCmd);
JP Abgrall87666692011-09-08 13:44:10 -0700674 ifaceLimiting = "! -o lo+";
675 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "OUTPUT",
676 bytes, alertName, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700677 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
678 free(alertQuotaCmd);
679 return res;
680}
681
682int BandwidthController::setGlobalAlert(int64_t bytes) {
683 char *alertQuotaCmd;
684 const char *alertName = "globalAlert";
685 int res = 0;
686
687 if (!bytes) {
688 LOGE("Invalid bytes value. 1..max_int64.");
689 return -1;
690 }
691 if (globalAlertBytes) {
692 res = updateQuota(alertName, bytes);
693 } else {
694 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
695 }
696 globalAlertBytes = bytes;
697 return res;
698}
699
700int BandwidthController::removeGlobalAlert(void) {
701 char *alertQuotaCmd;
702
703 const char *alertName = "globalAlert";
704 int res = 0;
705
706 if (!globalAlertBytes) {
707 LOGE("No prior alert set");
708 return -1;
709 }
710 res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
711 globalAlertBytes = 0;
712 return res;
713}
714
715int BandwidthController::setSharedAlert(int64_t bytes) {
716 if (!sharedQuotaBytes) {
717 LOGE("Need to have a prior shared quota set to set an alert");
718 return -1;
719 }
720 if (!bytes) {
721 LOGE("Invalid bytes value. 1..max_int64.");
722 return -1;
723 }
724 return setCostlyAlert("shared", bytes, &sharedAlertBytes);
725}
726
727int BandwidthController::removeSharedAlert(void) {
728 return removeCostlyAlert("shared", &sharedAlertBytes);
729}
730
731int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
732 std::list<QuotaInfo>::iterator it;
733
734 if (!bytes) {
735 LOGE("Invalid bytes value. 1..max_int64.");
736 return -1;
737 }
738 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
739 if (it->ifaceName == iface)
740 break;
741 }
742
743 if (it == quotaIfaces.end()) {
744 LOGE("Need to have a prior interface quota set to set an alert");
745 return -1;
746 }
747
748 return setCostlyAlert(iface, bytes, &it->alert);
749}
750
751int BandwidthController::removeInterfaceAlert(const char *iface) {
752 std::list<QuotaInfo>::iterator it;
753
754 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
755 if (it->ifaceName == iface)
756 break;
757 }
758
759 if (it == quotaIfaces.end()) {
760 LOGE("No prior alert set for interface %s", iface);
761 return -1;
762 }
763
764 return removeCostlyAlert(iface, &it->alert);
765}
766
767int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
768 char *alertQuotaCmd;
769 char *chainNameAndPos;
770 int res = 0;
771 char *alertName;
772
773 if (!bytes) {
774 LOGE("Invalid bytes value. 1..max_int64.");
775 return -1;
776 }
777 asprintf(&alertName, "%sAlert", costName);
778 if (*alertBytes) {
779 res = updateQuota(alertName, *alertBytes);
780 } else {
781 asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN);
782 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-I", chainNameAndPos, bytes, alertName,
783 alertName);
784 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
785 free(alertQuotaCmd);
786 free(chainNameAndPos);
787 }
788 *alertBytes = bytes;
789 free(alertName);
790 return res;
791}
792
793int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
794 char *alertQuotaCmd;
795 char *chainName;
796 char *alertName;
797 int res = 0;
798
799 asprintf(&alertName, "%sAlert", costName);
800 if (!*alertBytes) {
801 LOGE("No prior alert set for %s alert", costName);
802 return -1;
803 }
804
805 asprintf(&chainName, "costly_%s", costName);
806 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName, alertName);
807 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
808 free(alertQuotaCmd);
809 free(chainName);
810
811 *alertBytes = 0;
812 free(alertName);
813 return res;
814}