blob: 50db1ec673c45e00ca12476a7d1c808d9163f734 [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",
82 "-X costly",
83 "-X penalty_box",
84};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070085
86const char *BandwidthController::setupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070087 /* Created needed chains. */
88 "-N costly",
89 "-N penalty_box",
90};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070091
JP Abgrall0dad7c22011-06-24 11:58:14 -070092const char *BandwidthController::basicAccountingCommands[] = {
93 "-F INPUT",
94 "-A INPUT -i lo --jump ACCEPT",
95 "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070096
JP Abgrall0dad7c22011-06-24 11:58:14 -070097 "-F OUTPUT",
98 "-A OUTPUT -o lo --jump ACCEPT",
99 "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700100
JP Abgrall0dad7c22011-06-24 11:58:14 -0700101 "-F costly",
102 "-A costly --jump penalty_box",
103 "-A costly -m owner --socket-exists", /* This is a tracking rule. */
104 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
105 * chain. For now, hack the chain exit with an ACCEPT.
106 */
107 "-A costly --jump ACCEPT",
108};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700109
110BandwidthController::BandwidthController(void) {
111
112 char value[PROPERTY_VALUE_MAX];
113
114 property_get("persist.bandwidth.enable", value, "0");
115 if (!strcmp(value, "1")) {
116 enableBandwidthControl();
117 }
118
119}
120
JP Abgrall26e0d492011-06-24 19:21:51 -0700121int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700122 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700123 LOGD("runIpxtablesCmd(cmd=%s)", cmd);
124 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
125 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700126 return res;
127}
128
JP Abgrall26e0d492011-06-24 19:21:51 -0700129int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
130
131 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0'
132 strncpy(buffer, src, buffSize);
133 return buffer[buffSize - 1];
134}
135
136int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, IptIpVer iptVer) {
137 char buffer[MAX_CMD_LEN];
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700138 const char *argv[MAX_CMD_ARGS];
JP Abgrall26e0d492011-06-24 19:21:51 -0700139 int argc = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700140 char *next = buffer;
141 char *tmp;
142
JP Abgrall0dad7c22011-06-24 11:58:14 -0700143 std::string fullCmd = cmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700144
145 if (rejectHandling == IptRejectAdd) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700146 fullCmd += " --jump REJECT --reject-with";
JP Abgrall26e0d492011-06-24 19:21:51 -0700147 switch (iptVer) {
148 case IptIpV4:
149 fullCmd += " icmp-net-prohibited";
150 break;
151 case IptIpV6:
152 fullCmd += " icmp6-adm-prohibited";
153 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700154 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700155 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700156
JP Abgrall26e0d492011-06-24 19:21:51 -0700157 argc = 0;
158 argv[argc++] = iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700159 LOGD("About to run: %s %s", argv[0], fullCmd.c_str());
160
JP Abgrall26e0d492011-06-24 19:21:51 -0700161 LOGD("runIpxtablesCmd(): fullCmd.c_str()=%s buffSize=%d", fullCmd.c_str(), sizeof(buffer));
162 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700163 LOGE("iptables command too long");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700164 return -1;
165 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700166
167 while ((tmp = strsep(&next, " "))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700168 argv[argc++] = tmp;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700169 if (argc >= MAX_CMD_ARGS) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700170 LOGE("iptables argument overflow");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700171 return -1;
172 }
173 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700174
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700175 argv[argc] = NULL;
JP Abgrall26e0d492011-06-24 19:21:51 -0700176 LOGD("runIpxtablesCmd(): argc=%d, argv[argc]=%p", argc, argv[argc]);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700177 /* TODO(jpa): Once this stabilizes, remove logwrap() as it tends to wedge netd
178 * Then just talk directly to the kernel via rtnetlink.
179 */
JP Abgrall26e0d492011-06-24 19:21:51 -0700180 for (int i = 0 ; i < argc; i++) {
181 LOGD("runIpxtablesCmd(): argv[%d]=%p:%s", i, argv[i], argv[i]?:"null");
182 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700183 return logwrap(argc, argv, 0);
184}
185
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700186int BandwidthController::enableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700187 int res;
188 /* Some of the initialCommands are allowed to fail */
JP Abgrall26e0d492011-06-24 19:21:51 -0700189 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
190 runCommands(sizeof(setupCommands) / sizeof(char*), setupCommands, RunCmdFailureOk);
191 res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands, RunCmdFailureBad);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700192 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700193
194}
195
196int BandwidthController::disableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700197 /* The cleanupCommands are allowed to fail. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700198 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700199 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700200}
201
JP Abgrall26e0d492011-06-24 19:21:51 -0700202int BandwidthController::runCommands(int numCommands, const char *commands[], RunCmdErrHandling cmdErrHandling) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700203 int res = 0;
204 LOGD("runCommands(): %d commands", numCommands);
205 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700206 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
207 if (res && cmdErrHandling != RunCmdFailureBad)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700208 return res;
209 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700210 return cmdErrHandling == RunCmdFailureBad ? res : 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700211}
212
JP Abgrall0dad7c22011-06-24 11:58:14 -0700213std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700214 std::string res;
JP Abgrall26e0d492011-06-24 19:21:51 -0700215 char *convBuff;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700216
217 switch (op) {
218 case IptOpInsert:
219 res = "-I";
220 break;
221 case IptOpReplace:
222 res = "-R";
223 break;
224 default:
225 case IptOpDelete:
226 res = "-D";
227 break;
228 }
229 res += " penalty_box";
JP Abgrall26e0d492011-06-24 19:21:51 -0700230 asprintf(&convBuff, "%d", uid);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700231 res += " -m owner --uid-owner ";
232 res += convBuff;
JP Abgrall26e0d492011-06-24 19:21:51 -0700233 free(convBuff);
234 LOGD("makeIptablesNaughtyCmd() res=%s", res.c_str());
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700235 return res;
236}
237
238int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700239 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700240}
241
242int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700243 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700244}
245
JP Abgrall26e0d492011-06-24 19:21:51 -0700246int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700247 char cmd[MAX_CMD_LEN];
248 int uidNum;
JP Abgrall26e0d492011-06-24 19:21:51 -0700249 const char *failLogTemplate;
250 IptOp op;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700251 int appUids[numUids];
JP Abgrall26e0d492011-06-24 19:21:51 -0700252 std::string naughtyCmd;
253 LOGD("manipulateNaughtyApps()");
254 switch (appOp) {
255 case NaughtyAppOpAdd:
256 op = IptOpInsert;
257 failLogTemplate = "Failed to add app uid %d to penalty box.";
258 break;
259 case NaughtyAppOpRemove:
260 op = IptOpDelete;
261 failLogTemplate = "Failed to delete app uid %d from penalty box.";
262 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700263 }
264
265 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700266 appUids[uidNum] = atol(appStrUids[uidNum]);
267 if (appUids[uidNum] == 0) {
268 LOGE(failLogTemplate, appUids[uidNum]);
269 goto fail_parse;
270 }
271 }
272 LOGD("manipulateNaughtyApps() got the appUids");
273
274 for (uidNum = 0; uidNum < numUids; uidNum++) {
275 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
276 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
277 LOGE(failLogTemplate, appUids[uidNum]);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700278 goto fail_with_uidNum;
279 }
280 }
281 return 0;
282
JP Abgrall26e0d492011-06-24 19:21:51 -0700283fail_with_uidNum:
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700284 /* Try to remove the uid that failed in any case*/
JP Abgrall26e0d492011-06-24 19:21:51 -0700285 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
286 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
287fail_parse:
288 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700289}
290
JP Abgrall26e0d492011-06-24 19:21:51 -0700291std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700292 std::string res;
293 char convBuff[21]; // log10(2^64) ~ 20
JP Abgrall0dad7c22011-06-24 11:58:14 -0700294
295 LOGD("makeIptablesQuotaCmd(%d, %llu)", op, quota);
296
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700297 switch (op) {
298 case IptOpInsert:
299 res = "-I";
300 break;
301 case IptOpReplace:
302 res = "-R";
303 break;
304 default:
305 case IptOpDelete:
306 res = "-D";
307 break;
308 }
309 res += " costly";
310 if (costName) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700311 res += "_";
312 res += costName;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700313 }
314 sprintf(convBuff, "%lld", quota);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700315 /* TODO(jpa): Use -m quota2 --name " + costName + " ! --quota "
316 * once available.
317 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700318 res += " -m quota ! --quota ";
319 res += convBuff;
320 ;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700321 // The requried --jump REJECT ... will be added later.
322 return res;
323}
324
JP Abgrall26e0d492011-06-24 19:21:51 -0700325int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700326 char cmd[MAX_CMD_LEN];
327 int res = 0;
328 std::string costString;
329 const char *costCString;
330
331 costString = "costly";
332 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700333 switch (quotaType) {
334 case QuotaUnique:
JP Abgrall0dad7c22011-06-24 11:58:14 -0700335 costString += "_";
336 costString += ifn;
337 costCString = costString.c_str();
338 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700339 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700340 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700341 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700342 snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700343 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700344 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
345 * chain. For now, hack the chain exit with an ACCEPT.
346 */
347 snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700348 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
349 break;
350 case QuotaShared:
JP Abgrall0dad7c22011-06-24 11:58:14 -0700351 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700352 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700353 }
354
355 snprintf(cmd, sizeof(cmd), "-I INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700356 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700357 snprintf(cmd, sizeof(cmd), "-I OUTPUT -o %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700358 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700359 return res;
360}
361
JP Abgrall26e0d492011-06-24 19:21:51 -0700362int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700363 char cmd[MAX_CMD_LEN];
364 int res = 0;
365 std::string costString;
366 const char *costCString;
367
368 costString = "costly";
JP Abgrall26e0d492011-06-24 19:21:51 -0700369 switch (quotaType) {
370 case QuotaUnique:
JP Abgrall0dad7c22011-06-24 11:58:14 -0700371 costString += "_";
372 costString += ifn;
373 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700374 break;
375 case QuotaShared:
JP Abgrall0dad7c22011-06-24 11:58:14 -0700376 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700377 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700378 }
379
380 snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700381 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700382 snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700383 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700384
385 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700386 if (quotaType == QuotaUnique) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700387 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700388 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700389 }
390 return res;
391}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700392
JP Abgrall0dad7c22011-06-24 11:58:14 -0700393int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700394 char cmd[MAX_CMD_LEN];
395 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700396 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700397 std::string quotaCmd;
398 std::string ifaceName;;
399 const char *costName = NULL; /* Shared quota */
400 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700401
JP Abgrall26e0d492011-06-24 19:21:51 -0700402 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
403 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
404 return -1;
405 }
406 ifaceName = ifn;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700407
408 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700409 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700410 }
411
412 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700413 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
414 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700415 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700416 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700417
JP Abgrall0dad7c22011-06-24 11:58:14 -0700418 if (it == sharedQuotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700419 res |= prepCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700420 if (sharedQuotaIfaces.empty()) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700421 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700422 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700423 if (res) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700424 LOGE("Failed set quota rule.");
425 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700426 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700427 sharedQuotaBytes = maxBytes;
428 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700429 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700430
431 }
432
433 if (maxBytes != sharedQuotaBytes) {
434 /* Instead of replacing, which requires being aware of the rules in
435 * the kernel, we just add a new one, then delete the older one.
436 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700437
JP Abgrall0dad7c22011-06-24 11:58:14 -0700438 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700439 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700440
JP Abgrall0dad7c22011-06-24 11:58:14 -0700441 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700442 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700443
444 if (res) {
445 LOGE("Failed replace quota rule.");
446 goto fail;
447 }
448 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700449 }
450 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700451
452 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700453 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700454 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
455 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700456 * For now callers needs to choose if they want to "ndc bandwidth enable"
457 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700458 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700459 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700460 return -1;
461}
462
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700463int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700464 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700465 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700466 std::string ifaceName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700467 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700468
JP Abgrall26e0d492011-06-24 19:21:51 -0700469 if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
470 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
471 return -1;
472 }
473 ifaceName =ifn;
474
JP Abgrall0dad7c22011-06-24 11:58:14 -0700475 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
476 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700477 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700478 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700479 if (it == sharedQuotaIfaces.end()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700480 LOGE("No such iface %s to delete.", ifn);
481 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700482 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700483
JP Abgrall26e0d492011-06-24 19:21:51 -0700484 res |= cleanupCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700485 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700486
JP Abgrall0dad7c22011-06-24 11:58:14 -0700487 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700488 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700489 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, NULL, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700490 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700491 sharedQuotaBytes = -1;
492 }
493
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700494 return res;
495}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700496
497int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
498 char ifn[MAX_IFACENAME_LEN];
499 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700500 std::string ifaceName;
501 const char *costName;
502 std::list<QuotaInfo>::iterator it;
503 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700504
505 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700506 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700507 }
508
JP Abgrall26e0d492011-06-24 19:21:51 -0700509 if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
510 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
511 return -1;
512 }
513 ifaceName = ifn;
514 costName = iface;
515
JP Abgrall0dad7c22011-06-24 11:58:14 -0700516
517 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700518 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
519 if (it->first == ifaceName)
520 break;
521 }
522
523 if (it == quotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700524 res |= prepCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700525 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700526 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700527 if (res) {
528 LOGE("Failed set quota rule.");
529 goto fail;
530 }
531
532 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes));
533
534 } else {
535 /* Instead of replacing, which requires being aware of the rules in
536 * the kernel, we just add a new one, then delete the older one.
537 */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700538 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700539 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700540
541 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, it->second);
JP Abgrall26e0d492011-06-24 19:21:51 -0700542 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700543
544 if (res) {
545 LOGE("Failed replace quota rule.");
546 goto fail;
547 }
548 it->second = maxBytes;
549 }
550 return 0;
551
552 fail:
553 /*
554 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
555 * rules in the kernel to see which ones need cleaning up.
556 * For now callers needs to choose if they want to "ndc bandwidth enable"
557 * which resets everything.
558 */
559 removeInterfaceSharedQuota(ifn);
560 return -1;
561}
562
563int BandwidthController::removeInterfaceQuota(const char *iface) {
564
565 char ifn[MAX_IFACENAME_LEN];
566 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700567 std::string ifaceName;
568 const char *costName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700569 std::list<QuotaInfo>::iterator it;
JP Abgrall26e0d492011-06-24 19:21:51 -0700570
571 if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
572 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
573 return -1;
574 }
575 ifaceName = ifn;
576 costName = iface;
577
JP Abgrall0dad7c22011-06-24 11:58:14 -0700578 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
579 if (it->first == ifaceName)
580 break;
581 }
582
583 if (it == quotaIfaces.end()) {
584 LOGE("No such iface %s to delete.", ifn);
585 return -1;
586 }
587
588 /* This also removes the quota command of CostlyIface chain. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700589 res |= cleanupCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700590
591 quotaIfaces.erase(it);
592
593 return res;
594}