blob: 0e71fc7dd032b0fcea417dce1121688873310a2b [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>
18#include <errno.h>
19#include <fcntl.h>
20#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 Abgrall4a5f5ca2011-06-15 18:37:39 -070044
45/**
46 * Some comments about the rules:
47 * * Ordering
48 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
49 * E.g. "-I INPUT -i rmnet0 --goto costly"
50 * - quota'd rules in the costly chain should be before penalty_box lookups.
51 *
52 * * global quota vs per interface quota
53 * - global quota for all costly interfaces uses a single costly chain:
54 * . initial rules
55 * iptables -N costly
56 * iptables -I INPUT -i iface0 --goto costly
57 * iptables -I OUTPUT -o iface0 --goto costly
58 * iptables -I costly -m quota \! --quota 500000 --jump REJECT --reject-with icmp-net-prohibited
59 * iptables -A costly --jump penalty_box
60 * iptables -A costly -m owner --socket-exists
61 * . adding a new iface to this, E.g.:
62 * iptables -I INPUT -i iface1 --goto costly
63 * iptables -I OUTPUT -o iface1 --goto costly
64 *
65 * - quota per interface. This is achieve by having "costly" chains per quota.
66 * E.g. adding a new costly interface iface0 with its own quota:
67 * iptables -N costly_iface0
68 * iptables -I INPUT -i iface0 --goto costly_iface0
69 * iptables -I OUTPUT -o iface0 --goto costly_iface0
70 * iptables -A costly_iface0 -m quota \! --quota 500000 --jump REJECT --reject-with icmp-net-prohibited
71 * iptables -A costly_iface0 --jump penalty_box
72 * iptables -A costly_iface0 -m owner --socket-exists
73 *
74 * * penalty_box handling:
75 * - only one penalty_box for all interfaces
76 * E.g Adding an app:
77 * iptables -A penalty_box -m owner --uid-owner app_3 --jump REJECT --reject-with icmp-net-prohibited
78 */
79const char *BandwidthController::cleanupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070080 /* Cleanup rules. */
81 "-F",
82 "-t raw -F",
83 "-X costly",
84 "-X penalty_box",
85};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070086
87const char *BandwidthController::setupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070088 /* Created needed chains. */
89 "-N costly",
90 "-N penalty_box",
91};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070092
JP Abgrall0dad7c22011-06-24 11:58:14 -070093const char *BandwidthController::basicAccountingCommands[] = {
94 "-F INPUT",
95 "-A INPUT -i lo --jump ACCEPT",
96 "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070097
JP Abgrall0dad7c22011-06-24 11:58:14 -070098 "-F OUTPUT",
99 "-A OUTPUT -o lo --jump ACCEPT",
100 "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700101
JP Abgrall0dad7c22011-06-24 11:58:14 -0700102 "-F costly",
103 "-A costly --jump penalty_box",
104 "-A costly -m owner --socket-exists", /* This is a tracking rule. */
105 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
106 * chain. For now, hack the chain exit with an ACCEPT.
107 */
108 "-A costly --jump ACCEPT",
109};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700110
111BandwidthController::BandwidthController(void) {
112
113 char value[PROPERTY_VALUE_MAX];
114
115 property_get("persist.bandwidth.enable", value, "0");
116 if (!strcmp(value, "1")) {
117 enableBandwidthControl();
118 }
119
120}
121
JP Abgrall0dad7c22011-06-24 11:58:14 -0700122int BandwidthController::runIpxtablesCmd(const char *cmd, bool appendReject) {
123 int res = 0;
124 res |= runIptablesCmd(cmd, appendReject, false);
125 res |= runIptablesCmd(cmd, appendReject, true);
126 return res;
127}
128
129int BandwidthController::runIptablesCmd(const char *cmd, bool appendReject, bool isIp6) {
130 char buffer[MAX_CMD_LEN] = { 0 }; // strncpy() is not filling leftover with '\0'
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700131 const char *argv[MAX_CMD_ARGS];
JP Abgrall0dad7c22011-06-24 11:58:14 -0700132 int argc, nextArg;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700133 char *next = buffer;
134 char *tmp;
135
JP Abgrall0dad7c22011-06-24 11:58:14 -0700136 std::string fullCmd = cmd;
137 if (appendReject) {
138 fullCmd += " --jump REJECT --reject-with";
139 if (isIp6) {
140 fullCmd += " icmp6-adm-prohibited";
141 } else {
142 fullCmd += " icmp-net-prohibited";
143 }
144 argc = 4; // --jump ...
145 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700146
JP Abgrall0dad7c22011-06-24 11:58:14 -0700147 nextArg = 0;
148 argv[nextArg++] = isIp6 ? IP6TABLES_PATH : IPTABLES_PATH;
149 argc++;
150 LOGD("About to run: %s %s", argv[0], fullCmd.c_str());
151
152 strncpy(buffer, fullCmd.c_str(), sizeof(buffer) - 1);
153 if (buffer[sizeof(buffer) - 1]) {
154 LOGE("iptables command too long");
155 errno = E2BIG;
156 return -1;
157 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700158
159 while ((tmp = strsep(&next, " "))) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700160 argv[nextArg++] = tmp;
161 argc++;
162 if (argc >= MAX_CMD_ARGS) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700163 LOGE("iptables argument overflow");
164 errno = E2BIG;
165 return -1;
166 }
167 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700168
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700169 argv[argc] = NULL;
170 /* TODO(jpa): Once this stabilizes, remove logwrap() as it tends to wedge netd
171 * Then just talk directly to the kernel via rtnetlink.
172 */
173 return logwrap(argc, argv, 0);
174}
175
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700176int BandwidthController::enableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700177 int res;
178 /* Some of the initialCommands are allowed to fail */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700179 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, true);
180 runCommands(sizeof(setupCommands) / sizeof(char*), setupCommands, true);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700181 res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands,
JP Abgrall0dad7c22011-06-24 11:58:14 -0700182 false);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700183 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700184
185}
186
187int BandwidthController::disableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700188 /* The cleanupCommands are allowed to fail. */
189 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, true);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700190 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700191}
192
JP Abgrall0dad7c22011-06-24 11:58:14 -0700193int BandwidthController::runCommands(int numCommands, const char *commands[], bool allowFailure) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700194 int res = 0;
195 LOGD("runCommands(): %d commands", numCommands);
196 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700197 res = runIpxtablesCmd(commands[cmdNum], false);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700198 if (res && !allowFailure)
199 return res;
200 }
201 return allowFailure ? res : 0;
202}
203
JP Abgrall0dad7c22011-06-24 11:58:14 -0700204std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700205 std::string res;
206 char convBuff[21]; // log10(2^64) ~ 20
207
208 switch (op) {
209 case IptOpInsert:
210 res = "-I";
211 break;
212 case IptOpReplace:
213 res = "-R";
214 break;
215 default:
216 case IptOpDelete:
217 res = "-D";
218 break;
219 }
220 res += " penalty_box";
221 sprintf(convBuff, "%d", uid);
222 res += " -m owner --uid-owner ";
223 res += convBuff;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700224 return res;
225}
226
227int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
228 return maninpulateNaughtyApps(numUids, appUids, true);
229}
230
231int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
232 return maninpulateNaughtyApps(numUids, appUids, false);
233}
234
235int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], bool doAdd) {
236 char cmd[MAX_CMD_LEN];
237 int uidNum;
238 const char *addFailedTemplate = "Failed to add app uid %d to penalty box.";
239 const char *deleteFailedTemplate = "Failed to delete app uid %d from penalty box.";
240 IptOp op = doAdd ? IptOpInsert : IptOpDelete;
241
242 int appUids[numUids];
243 for (uidNum = 0; uidNum < numUids; uidNum++) {
244 appUids[uidNum] = atol(appStrUids[uidNum]);
245 if (appUids[uidNum] == 0) {
246 LOGE((doAdd ? addFailedTemplate : deleteFailedTemplate), appUids[uidNum]);
247 goto fail_parse;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700248 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700249 }
250
251 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700252 std::string naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
253 if (runIpxtablesCmd(naughtyCmd.c_str(), true)) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700254 LOGE((doAdd ? addFailedTemplate : deleteFailedTemplate), appUids[uidNum]);
255 goto fail_with_uidNum;
256 }
257 }
258 return 0;
259
260 fail_with_uidNum:
261 /* Try to remove the uid that failed in any case*/
JP Abgrall0dad7c22011-06-24 11:58:14 -0700262 runIpxtablesCmd(makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]).c_str(), true);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700263 fail_parse: return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700264}
265
JP Abgrall0dad7c22011-06-24 11:58:14 -0700266std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700267 std::string res;
268 char convBuff[21]; // log10(2^64) ~ 20
JP Abgrall0dad7c22011-06-24 11:58:14 -0700269
270 LOGD("makeIptablesQuotaCmd(%d, %llu)", op, quota);
271
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700272 switch (op) {
273 case IptOpInsert:
274 res = "-I";
275 break;
276 case IptOpReplace:
277 res = "-R";
278 break;
279 default:
280 case IptOpDelete:
281 res = "-D";
282 break;
283 }
284 res += " costly";
285 if (costName) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700286 res += "_";
287 res += costName;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700288 }
289 sprintf(convBuff, "%lld", quota);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700290 /* TODO(jpa): Use -m quota2 --name " + costName + " ! --quota "
291 * once available.
292 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700293 res += " -m quota ! --quota ";
294 res += convBuff;
295 ;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700296 // The requried --jump REJECT ... will be added later.
297 return res;
298}
299
300int BandwidthController::prepCostlyIface(const char *ifn, bool isShared) {
301 char cmd[MAX_CMD_LEN];
302 int res = 0;
303 std::string costString;
304 const char *costCString;
305
306 costString = "costly";
307 /* The "-N costly" is created upfront, no need to handle it here. */
308 if (!isShared) {
309 costString += "_";
310 costString += ifn;
311 costCString = costString.c_str();
312 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
313 res |= runIpxtablesCmd(cmd, false);
314 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
315 res |= runIpxtablesCmd(cmd, false);
316 snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
317 res |= runIpxtablesCmd(cmd, false);
318 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
319 * chain. For now, hack the chain exit with an ACCEPT.
320 */
321 snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
322 res |= runIpxtablesCmd(cmd, false);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700323 } else {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700324 costCString = costString.c_str();
325 }
326
327 snprintf(cmd, sizeof(cmd), "-I INPUT -i %s --goto %s", ifn, costCString);
328 res |= runIpxtablesCmd(cmd, false);
329 snprintf(cmd, sizeof(cmd), "-I OUTPUT -o %s --goto %s", ifn, costCString);
330 res |= runIpxtablesCmd(cmd, false);
331 return res;
332}
333
334int BandwidthController::cleanupCostlyIface(const char *ifn, bool isShared) {
335 char cmd[MAX_CMD_LEN];
336 int res = 0;
337 std::string costString;
338 const char *costCString;
339
340 costString = "costly";
341 if (!isShared) {
342 costString += "_";
343 costString += ifn;
344 costCString = costString.c_str();
345 } else {
346 costCString = costString.c_str();
347 }
348
349 snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
350 res |= runIpxtablesCmd(cmd, false);
351 snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
352 res |= runIpxtablesCmd(cmd, false);
353
354 /* The "-N costly" is created upfront, no need to handle it here. */
355 if (!isShared) {
356 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
357 res |= runIpxtablesCmd(cmd, false);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700358 }
359 return res;
360}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700361
JP Abgrall0dad7c22011-06-24 11:58:14 -0700362int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700363 char cmd[MAX_CMD_LEN];
364 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700365 int res = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700366
367 memset(ifn, 0, sizeof(ifn));
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700368 strncpy(ifn, iface, sizeof(ifn) - 1);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700369
370 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700371 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700372 }
373
JP Abgrall0dad7c22011-06-24 11:58:14 -0700374 char *costName = NULL; /* Shared quota */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700375
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700376 /* Insert ingress quota. */
377 std::string ifaceName(ifn);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700378 std::list<std::string>::iterator it;
379 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
380 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700381 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700382 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700383
JP Abgrall0dad7c22011-06-24 11:58:14 -0700384 if (it == sharedQuotaIfaces.end()) {
385 res |= prepCostlyIface(ifn, true);
386 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700387 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700388 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
389 res |= runIpxtablesCmd(quotaCmd.c_str(), true);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700390 if (res) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700391 LOGE("Failed set quota rule.");
392 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700393 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700394 sharedQuotaBytes = maxBytes;
395 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700396 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700397
398 }
399
400 if (maxBytes != sharedQuotaBytes) {
401 /* Instead of replacing, which requires being aware of the rules in
402 * the kernel, we just add a new one, then delete the older one.
403 */
404 std::string quotaCmd;
405
JP Abgrall0dad7c22011-06-24 11:58:14 -0700406 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
407 res |= runIpxtablesCmd(quotaCmd.c_str(), true);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700408
JP Abgrall0dad7c22011-06-24 11:58:14 -0700409 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
410 res |= runIpxtablesCmd(quotaCmd.c_str(), true);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700411
412 if (res) {
413 LOGE("Failed replace quota rule.");
414 goto fail;
415 }
416 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700417 }
418 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700419
420 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700421 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700422 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
423 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700424 * For now callers needs to choose if they want to "ndc bandwidth enable"
425 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700426 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700427 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700428 return -1;
429}
430
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700431int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700432 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700433 int res = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700434
435 memset(ifn, 0, sizeof(ifn));
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700436 strncpy(ifn, iface, sizeof(ifn) - 1);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700437
438 std::string ifaceName(ifn);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700439 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700440
JP Abgrall0dad7c22011-06-24 11:58:14 -0700441 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
442 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700443 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700444 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700445 if (it == sharedQuotaIfaces.end()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700446 LOGE("No such iface %s to delete.", ifn);
447 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700448 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700449
JP Abgrall0dad7c22011-06-24 11:58:14 -0700450 res |= cleanupCostlyIface(ifn, true);
451 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700452
JP Abgrall0dad7c22011-06-24 11:58:14 -0700453 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700454 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700455 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, NULL, sharedQuotaBytes);
456 res |= runIpxtablesCmd(quotaCmd.c_str(), true);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700457 sharedQuotaBytes = -1;
458 }
459
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700460 return res;
461}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700462
463int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
464 char ifn[MAX_IFACENAME_LEN];
465 int res = 0;
466
467 memset(ifn, 0, sizeof(ifn));
468 strncpy(ifn, iface, sizeof(ifn) - 1);
469
470 if (maxBytes == -1) {
471 return removeInterfaceQuota(ifn);
472 }
473
474 char *costName = ifn;
475
476 /* Insert ingress quota. */
477 std::string ifaceName(ifn);
478 std::list<QuotaInfo>::iterator it;
479 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
480 if (it->first == ifaceName)
481 break;
482 }
483
484 if (it == quotaIfaces.end()) {
485
486 res |= prepCostlyIface(ifn, false);
487
488 std::string quotaCmd;
489 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
490 res |= runIpxtablesCmd(quotaCmd.c_str(), true);
491 if (res) {
492 LOGE("Failed set quota rule.");
493 goto fail;
494 }
495
496 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes));
497
498 } else {
499 /* Instead of replacing, which requires being aware of the rules in
500 * the kernel, we just add a new one, then delete the older one.
501 */
502 std::string quotaCmd;
503
504 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
505 res |= runIpxtablesCmd(quotaCmd.c_str(), true);
506
507 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, it->second);
508 res |= runIpxtablesCmd(quotaCmd.c_str(), true);
509
510 if (res) {
511 LOGE("Failed replace quota rule.");
512 goto fail;
513 }
514 it->second = maxBytes;
515 }
516 return 0;
517
518 fail:
519 /*
520 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
521 * rules in the kernel to see which ones need cleaning up.
522 * For now callers needs to choose if they want to "ndc bandwidth enable"
523 * which resets everything.
524 */
525 removeInterfaceSharedQuota(ifn);
526 return -1;
527}
528
529int BandwidthController::removeInterfaceQuota(const char *iface) {
530
531 char ifn[MAX_IFACENAME_LEN];
532 int res = 0;
533
534 memset(ifn, 0, sizeof(ifn));
535 strncpy(ifn, iface, sizeof(ifn) - 1);
536
537 char *costName = ifn;
538
539 std::string ifaceName(ifn);
540 std::list<QuotaInfo>::iterator it;
541 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
542 if (it->first == ifaceName)
543 break;
544 }
545
546 if (it == quotaIfaces.end()) {
547 LOGE("No such iface %s to delete.", ifn);
548 return -1;
549 }
550
551 /* This also removes the quota command of CostlyIface chain. */
552 res |= cleanupCostlyIface(ifn, false);
553
554 quotaIfaces.erase(it);
555
556 return res;
557}