blob: eb33356ddc8fd0535912c14a27f40f6a106e0768 [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
17#include <stdlib.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070018#include <fcntl.h>
19#include <string.h>
20
21#include <sys/socket.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <sys/wait.h>
25
26#include <linux/netlink.h>
27#include <linux/rtnetlink.h>
28#include <linux/pkt_sched.h>
29
30#define LOG_TAG "BandwidthController"
31#include <cutils/log.h>
32#include <cutils/properties.h>
33
34extern "C" int logwrap(int argc, const char **argv, int background);
35
36#include "BandwidthController.h"
37
JP Abgrallfa6f46d2011-06-17 23:17:28 -070038const int BandwidthController::MAX_CMD_LEN = 1024;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070039const int BandwidthController::MAX_IFACENAME_LEN = 64;
40const int BandwidthController::MAX_CMD_ARGS = 32;
41const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
JP Abgrallfa6f46d2011-06-17 23:17:28 -070042const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070043
44/**
45 * Some comments about the rules:
46 * * Ordering
47 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
48 * E.g. "-I INPUT -i rmnet0 --goto costly"
49 * - quota'd rules in the costly chain should be before penalty_box lookups.
50 *
51 * * global quota vs per interface quota
52 * - global quota for all costly interfaces uses a single costly chain:
53 * . initial rules
54 * iptables -N costly
55 * iptables -I INPUT -i iface0 --goto costly
56 * iptables -I OUTPUT -o iface0 --goto costly
57 * iptables -I costly -m quota \! --quota 500000 --jump REJECT --reject-with icmp-net-prohibited
58 * iptables -A costly --jump penalty_box
59 * iptables -A costly -m owner --socket-exists
60 * . adding a new iface to this, E.g.:
61 * iptables -I INPUT -i iface1 --goto costly
62 * iptables -I OUTPUT -o iface1 --goto costly
63 *
64 * - quota per interface. This is achieve by having "costly" chains per quota.
65 * E.g. adding a new costly interface iface0 with its own quota:
66 * iptables -N costly_iface0
67 * iptables -I INPUT -i iface0 --goto costly_iface0
68 * iptables -I OUTPUT -o iface0 --goto costly_iface0
69 * iptables -A costly_iface0 -m quota \! --quota 500000 --jump REJECT --reject-with icmp-net-prohibited
70 * iptables -A costly_iface0 --jump penalty_box
71 * iptables -A costly_iface0 -m owner --socket-exists
72 *
73 * * penalty_box handling:
74 * - only one penalty_box for all interfaces
75 * E.g Adding an app:
76 * iptables -A penalty_box -m owner --uid-owner app_3 --jump REJECT --reject-with icmp-net-prohibited
77 */
78const char *BandwidthController::cleanupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070079 /* Cleanup rules. */
80 "-F",
81 "-t raw -F",
JP Abgrall39f8f242011-06-29 19:21:58 -070082 /* TODO: If at some point we need more user chains than here, then we will need
83 * a different cleanup approach.
84 */
85 "-X", /* Should normally only be costly, penalty_box, and costly_<iface> */
JP Abgrall0dad7c22011-06-24 11:58:14 -070086};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070087
88const char *BandwidthController::setupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070089 /* Created needed chains. */
90 "-N costly",
91 "-N penalty_box",
92};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070093
JP Abgrall0dad7c22011-06-24 11:58:14 -070094const char *BandwidthController::basicAccountingCommands[] = {
95 "-F INPUT",
96 "-A INPUT -i lo --jump ACCEPT",
97 "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070098
JP Abgrall0dad7c22011-06-24 11:58:14 -070099 "-F OUTPUT",
100 "-A OUTPUT -o lo --jump ACCEPT",
101 "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700102
JP Abgrall0dad7c22011-06-24 11:58:14 -0700103 "-F costly",
104 "-A costly --jump penalty_box",
105 "-A costly -m owner --socket-exists", /* This is a tracking rule. */
106 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
107 * chain. For now, hack the chain exit with an ACCEPT.
108 */
109 "-A costly --jump ACCEPT",
110};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700111
112BandwidthController::BandwidthController(void) {
113
114 char value[PROPERTY_VALUE_MAX];
115
116 property_get("persist.bandwidth.enable", value, "0");
117 if (!strcmp(value, "1")) {
118 enableBandwidthControl();
119 }
120
121}
122
JP Abgrall26e0d492011-06-24 19:21:51 -0700123int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700124 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700125 LOGD("runIpxtablesCmd(cmd=%s)", cmd);
126 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
127 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700128 return res;
129}
130
JP Abgrall26e0d492011-06-24 19:21:51 -0700131int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
132
133 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0'
134 strncpy(buffer, src, buffSize);
135 return buffer[buffSize - 1];
136}
137
138int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, IptIpVer iptVer) {
139 char buffer[MAX_CMD_LEN];
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700140 const char *argv[MAX_CMD_ARGS];
JP Abgrall26e0d492011-06-24 19:21:51 -0700141 int argc = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700142 char *next = buffer;
143 char *tmp;
144
JP Abgrall0dad7c22011-06-24 11:58:14 -0700145 std::string fullCmd = cmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700146
147 if (rejectHandling == IptRejectAdd) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700148 fullCmd += " --jump REJECT --reject-with";
JP Abgrall26e0d492011-06-24 19:21:51 -0700149 switch (iptVer) {
150 case IptIpV4:
151 fullCmd += " icmp-net-prohibited";
152 break;
153 case IptIpV6:
154 fullCmd += " icmp6-adm-prohibited";
155 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700156 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700157 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700158
JP Abgrall26e0d492011-06-24 19:21:51 -0700159 argc = 0;
160 argv[argc++] = iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700161
JP Abgralla9f802c2011-06-29 15:46:45 -0700162 LOGD("runIptablesCmd(): %s %s", argv[0], fullCmd.c_str());
JP Abgrall26e0d492011-06-24 19:21:51 -0700163 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700164 LOGE("iptables command too long");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700165 return -1;
166 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700167
168 while ((tmp = strsep(&next, " "))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700169 argv[argc++] = tmp;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700170 if (argc >= MAX_CMD_ARGS) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700171 LOGE("iptables argument overflow");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700172 return -1;
173 }
174 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700175
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700176 argv[argc] = NULL;
177 /* TODO(jpa): Once this stabilizes, remove logwrap() as it tends to wedge netd
178 * Then just talk directly to the kernel via rtnetlink.
179 */
180 return logwrap(argc, argv, 0);
181}
182
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700183int BandwidthController::enableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700184 int res;
185 /* Some of the initialCommands are allowed to fail */
JP Abgrall26e0d492011-06-24 19:21:51 -0700186 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
187 runCommands(sizeof(setupCommands) / sizeof(char*), setupCommands, RunCmdFailureOk);
188 res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands, RunCmdFailureBad);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700189 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700190
191}
192
193int BandwidthController::disableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700194 /* The cleanupCommands are allowed to fail. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700195 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700196 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700197}
198
JP Abgrall26e0d492011-06-24 19:21:51 -0700199int BandwidthController::runCommands(int numCommands, const char *commands[], RunCmdErrHandling cmdErrHandling) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700200 int res = 0;
201 LOGD("runCommands(): %d commands", numCommands);
202 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700203 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
204 if (res && cmdErrHandling != RunCmdFailureBad)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700205 return res;
206 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700207 return cmdErrHandling == RunCmdFailureBad ? res : 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700208}
209
JP Abgrall0dad7c22011-06-24 11:58:14 -0700210std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700211 std::string res;
JP Abgrall26e0d492011-06-24 19:21:51 -0700212 char *convBuff;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700213
214 switch (op) {
215 case IptOpInsert:
216 res = "-I";
217 break;
218 case IptOpReplace:
219 res = "-R";
220 break;
221 default:
222 case IptOpDelete:
223 res = "-D";
224 break;
225 }
226 res += " penalty_box";
JP Abgrall26e0d492011-06-24 19:21:51 -0700227 asprintf(&convBuff, "%d", uid);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700228 res += " -m owner --uid-owner ";
229 res += convBuff;
JP Abgrall26e0d492011-06-24 19:21:51 -0700230 free(convBuff);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700231 return res;
232}
233
234int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700235 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700236}
237
238int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700239 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700240}
241
JP Abgrall26e0d492011-06-24 19:21:51 -0700242int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700243 char cmd[MAX_CMD_LEN];
244 int uidNum;
JP Abgrall26e0d492011-06-24 19:21:51 -0700245 const char *failLogTemplate;
246 IptOp op;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700247 int appUids[numUids];
JP Abgrall26e0d492011-06-24 19:21:51 -0700248 std::string naughtyCmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700249 switch (appOp) {
250 case NaughtyAppOpAdd:
251 op = IptOpInsert;
252 failLogTemplate = "Failed to add app uid %d to penalty box.";
253 break;
254 case NaughtyAppOpRemove:
255 op = IptOpDelete;
256 failLogTemplate = "Failed to delete app uid %d from penalty box.";
257 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700258 }
259
260 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700261 appUids[uidNum] = atol(appStrUids[uidNum]);
262 if (appUids[uidNum] == 0) {
263 LOGE(failLogTemplate, appUids[uidNum]);
264 goto fail_parse;
265 }
266 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700267
268 for (uidNum = 0; uidNum < numUids; uidNum++) {
269 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
270 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
271 LOGE(failLogTemplate, appUids[uidNum]);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700272 goto fail_with_uidNum;
273 }
274 }
275 return 0;
276
JP Abgrall26e0d492011-06-24 19:21:51 -0700277fail_with_uidNum:
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700278 /* Try to remove the uid that failed in any case*/
JP Abgrall26e0d492011-06-24 19:21:51 -0700279 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
280 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
281fail_parse:
282 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700283}
284
JP Abgrall26e0d492011-06-24 19:21:51 -0700285std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700286 std::string res;
287 char convBuff[21]; // log10(2^64) ~ 20
JP Abgrall0dad7c22011-06-24 11:58:14 -0700288
289 LOGD("makeIptablesQuotaCmd(%d, %llu)", op, quota);
290
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700291 switch (op) {
292 case IptOpInsert:
293 res = "-I";
294 break;
295 case IptOpReplace:
296 res = "-R";
297 break;
298 default:
299 case IptOpDelete:
300 res = "-D";
301 break;
302 }
303 res += " costly";
304 if (costName) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700305 res += "_";
306 res += costName;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700307 }
308 sprintf(convBuff, "%lld", quota);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700309 /* TODO(jpa): Use -m quota2 --name " + costName + " ! --quota "
310 * once available.
311 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700312 res += " -m quota ! --quota ";
313 res += convBuff;
314 ;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700315 // The requried --jump REJECT ... will be added later.
316 return res;
317}
318
JP Abgrall26e0d492011-06-24 19:21:51 -0700319int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700320 char cmd[MAX_CMD_LEN];
321 int res = 0;
322 std::string costString;
323 const char *costCString;
324
325 costString = "costly";
326 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700327 switch (quotaType) {
328 case QuotaUnique:
JP Abgrall0dad7c22011-06-24 11:58:14 -0700329 costString += "_";
330 costString += ifn;
331 costCString = costString.c_str();
332 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700333 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700334 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700335 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700336 snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700337 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700338 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
339 * chain. For now, hack the chain exit with an ACCEPT.
340 */
341 snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700342 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
343 break;
344 case QuotaShared:
JP Abgrall0dad7c22011-06-24 11:58:14 -0700345 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700346 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700347 }
348
349 snprintf(cmd, sizeof(cmd), "-I INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700350 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700351 snprintf(cmd, sizeof(cmd), "-I OUTPUT -o %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700352 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700353 return res;
354}
355
JP Abgrall26e0d492011-06-24 19:21:51 -0700356int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700357 char cmd[MAX_CMD_LEN];
358 int res = 0;
359 std::string costString;
360 const char *costCString;
361
362 costString = "costly";
JP Abgrall26e0d492011-06-24 19:21:51 -0700363 switch (quotaType) {
364 case QuotaUnique:
JP Abgrall0dad7c22011-06-24 11:58:14 -0700365 costString += "_";
366 costString += ifn;
367 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700368 break;
369 case QuotaShared:
JP Abgrall0dad7c22011-06-24 11:58:14 -0700370 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700371 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700372 }
373
374 snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700375 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700376 snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700377 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700378
379 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700380 if (quotaType == QuotaUnique) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700381 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700382 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgralla9f802c2011-06-29 15:46:45 -0700383 snprintf(cmd, sizeof(cmd), "-X %s", costCString);
384 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700385 }
386 return res;
387}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700388
JP Abgrall0dad7c22011-06-24 11:58:14 -0700389int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700390 char cmd[MAX_CMD_LEN];
391 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700392 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700393 std::string quotaCmd;
394 std::string ifaceName;;
395 const char *costName = NULL; /* Shared quota */
396 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700397
JP Abgrall26e0d492011-06-24 19:21:51 -0700398 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
399 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
400 return -1;
401 }
402 ifaceName = ifn;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700403
404 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700405 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700406 }
407
408 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700409 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
410 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700411 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700412 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700413
JP Abgrall0dad7c22011-06-24 11:58:14 -0700414 if (it == sharedQuotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700415 res |= prepCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700416 if (sharedQuotaIfaces.empty()) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700417 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700418 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700419 if (res) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700420 LOGE("Failed set quota rule.");
421 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700422 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700423 sharedQuotaBytes = maxBytes;
424 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700425 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700426
427 }
428
429 if (maxBytes != sharedQuotaBytes) {
430 /* Instead of replacing, which requires being aware of the rules in
431 * the kernel, we just add a new one, then delete the older one.
432 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700433
JP Abgrall0dad7c22011-06-24 11:58:14 -0700434 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700435 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700436
JP Abgrall0dad7c22011-06-24 11:58:14 -0700437 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700438 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700439
440 if (res) {
441 LOGE("Failed replace quota rule.");
442 goto fail;
443 }
444 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700445 }
446 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700447
448 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700449 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700450 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
451 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700452 * For now callers needs to choose if they want to "ndc bandwidth enable"
453 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700454 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700455 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700456 return -1;
457}
458
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700459int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700460 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700461 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700462 std::string ifaceName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700463 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700464
JP Abgrall26e0d492011-06-24 19:21:51 -0700465 if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
466 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
467 return -1;
468 }
469 ifaceName =ifn;
470
JP Abgrall0dad7c22011-06-24 11:58:14 -0700471 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
472 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700473 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700474 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700475 if (it == sharedQuotaIfaces.end()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700476 LOGE("No such iface %s to delete.", ifn);
477 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700478 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700479
JP Abgrall26e0d492011-06-24 19:21:51 -0700480 res |= cleanupCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700481 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700482
JP Abgrall0dad7c22011-06-24 11:58:14 -0700483 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700484 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700485 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, NULL, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700486 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700487 sharedQuotaBytes = -1;
488 }
489
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700490 return res;
491}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700492
493int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
494 char ifn[MAX_IFACENAME_LEN];
495 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700496 std::string ifaceName;
497 const char *costName;
498 std::list<QuotaInfo>::iterator it;
499 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700500
501 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700502 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700503 }
504
JP Abgrall26e0d492011-06-24 19:21:51 -0700505 if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
506 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
507 return -1;
508 }
509 ifaceName = ifn;
510 costName = iface;
511
JP Abgrall0dad7c22011-06-24 11:58:14 -0700512
513 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700514 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
515 if (it->first == ifaceName)
516 break;
517 }
518
519 if (it == quotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700520 res |= prepCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700521 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700522 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700523 if (res) {
524 LOGE("Failed set quota rule.");
525 goto fail;
526 }
527
528 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes));
529
530 } else {
531 /* Instead of replacing, which requires being aware of the rules in
532 * the kernel, we just add a new one, then delete the older one.
533 */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700534 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700535 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700536
537 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, it->second);
JP Abgrall26e0d492011-06-24 19:21:51 -0700538 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700539
540 if (res) {
541 LOGE("Failed replace quota rule.");
542 goto fail;
543 }
544 it->second = maxBytes;
545 }
546 return 0;
547
548 fail:
549 /*
550 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
551 * rules in the kernel to see which ones need cleaning up.
552 * For now callers needs to choose if they want to "ndc bandwidth enable"
553 * which resets everything.
554 */
555 removeInterfaceSharedQuota(ifn);
556 return -1;
557}
558
559int BandwidthController::removeInterfaceQuota(const char *iface) {
560
561 char ifn[MAX_IFACENAME_LEN];
562 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700563 std::string ifaceName;
564 const char *costName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700565 std::list<QuotaInfo>::iterator it;
JP Abgrall26e0d492011-06-24 19:21:51 -0700566
567 if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
568 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
569 return -1;
570 }
571 ifaceName = ifn;
572 costName = iface;
573
JP Abgrall0dad7c22011-06-24 11:58:14 -0700574 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
575 if (it->first == ifaceName)
576 break;
577 }
578
579 if (it == quotaIfaces.end()) {
580 LOGE("No such iface %s to delete.", ifn);
581 return -1;
582 }
583
584 /* This also removes the quota command of CostlyIface chain. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700585 res |= cleanupCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700586
587 quotaIfaces.erase(it);
588
589 return res;
590}