blob: d4db4a7f35cccf80ecdbd32b8e941050bd0e0b68 [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
JP Abgrallbfa74662011-06-29 19:23:04 -070054 * iptables -N costly_shared
55 * iptables -I INPUT -i iface0 --goto costly_shared
56 * iptables -I OUTPUT -o iface0 --goto costly_shared
57 * iptables -I costly_shared -m quota \! --quota 500000 \
58 * --jump REJECT --reject-with icmp-net-prohibited
59 * iptables -A costly_shared --jump penalty_box
60 * iptables -A costly_shared -m owner --socket-exists
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070061 * . adding a new iface to this, E.g.:
JP Abgrallbfa74662011-06-29 19:23:04 -070062 * iptables -I INPUT -i iface1 --goto costly_shared
63 * iptables -I OUTPUT -o iface1 --goto costly_shared
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070064 *
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
JP Abgrallbfa74662011-06-29 19:23:04 -070070 * iptables -A costly_iface0 -m quota \! --quota 500000 \
71 * --jump REJECT --reject-with icmp-net-prohibited
72 * iptables -A costly_iface0 --jump penalty_box
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070073 * iptables -A costly_iface0 -m owner --socket-exists
74 *
75 * * penalty_box handling:
76 * - only one penalty_box for all interfaces
77 * E.g Adding an app:
JP Abgrallbfa74662011-06-29 19:23:04 -070078 * iptables -A penalty_box -m owner --uid-owner app_3 \
79 * --jump REJECT --reject-with icmp-net-prohibited
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070080 */
81const char *BandwidthController::cleanupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070082 /* Cleanup rules. */
83 "-F",
84 "-t raw -F",
JP Abgrall39f8f242011-06-29 19:21:58 -070085 /* TODO: If at some point we need more user chains than here, then we will need
86 * a different cleanup approach.
87 */
JP Abgrallbfa74662011-06-29 19:23:04 -070088 "-X", /* Should normally only be costly_shared, penalty_box, and costly_<iface> */
JP Abgrall0dad7c22011-06-24 11:58:14 -070089};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070090
91const char *BandwidthController::setupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070092 /* Created needed chains. */
JP Abgrallbfa74662011-06-29 19:23:04 -070093 "-N costly_shared",
JP Abgrall0dad7c22011-06-24 11:58:14 -070094 "-N penalty_box",
95};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070096
JP Abgrall0dad7c22011-06-24 11:58:14 -070097const char *BandwidthController::basicAccountingCommands[] = {
98 "-F INPUT",
99 "-A INPUT -i lo --jump ACCEPT",
100 "-A INPUT -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 OUTPUT",
103 "-A OUTPUT -o lo --jump ACCEPT",
104 "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700105
JP Abgrallbfa74662011-06-29 19:23:04 -0700106 "-F costly_shared",
107 "-A costly_shared --jump penalty_box",
108 "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700109 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
110 * chain. For now, hack the chain exit with an ACCEPT.
111 */
JP Abgrallbfa74662011-06-29 19:23:04 -0700112 "-A costly_shared --jump ACCEPT",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700113};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700114
115BandwidthController::BandwidthController(void) {
116
117 char value[PROPERTY_VALUE_MAX];
118
119 property_get("persist.bandwidth.enable", value, "0");
120 if (!strcmp(value, "1")) {
121 enableBandwidthControl();
122 }
123
124}
125
JP Abgrall26e0d492011-06-24 19:21:51 -0700126int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700127 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700128 LOGD("runIpxtablesCmd(cmd=%s)", cmd);
129 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
130 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700131 return res;
132}
133
JP Abgrall26e0d492011-06-24 19:21:51 -0700134int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
135
136 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0'
137 strncpy(buffer, src, buffSize);
138 return buffer[buffSize - 1];
139}
140
141int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, IptIpVer iptVer) {
142 char buffer[MAX_CMD_LEN];
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700143 const char *argv[MAX_CMD_ARGS];
JP Abgrall26e0d492011-06-24 19:21:51 -0700144 int argc = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700145 char *next = buffer;
146 char *tmp;
147
JP Abgrall0dad7c22011-06-24 11:58:14 -0700148 std::string fullCmd = cmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700149
150 if (rejectHandling == IptRejectAdd) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700151 fullCmd += " --jump REJECT --reject-with";
JP Abgrall26e0d492011-06-24 19:21:51 -0700152 switch (iptVer) {
153 case IptIpV4:
154 fullCmd += " icmp-net-prohibited";
155 break;
156 case IptIpV6:
157 fullCmd += " icmp6-adm-prohibited";
158 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700159 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700160 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700161
JP Abgrall26e0d492011-06-24 19:21:51 -0700162 argc = 0;
163 argv[argc++] = iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700164
JP Abgralla9f802c2011-06-29 15:46:45 -0700165 LOGD("runIptablesCmd(): %s %s", argv[0], fullCmd.c_str());
JP Abgrall26e0d492011-06-24 19:21:51 -0700166 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700167 LOGE("iptables command too long");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700168 return -1;
169 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700170
171 while ((tmp = strsep(&next, " "))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700172 argv[argc++] = tmp;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700173 if (argc >= MAX_CMD_ARGS) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700174 LOGE("iptables argument overflow");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700175 return -1;
176 }
177 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700178
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700179 argv[argc] = NULL;
180 /* TODO(jpa): Once this stabilizes, remove logwrap() as it tends to wedge netd
181 * Then just talk directly to the kernel via rtnetlink.
182 */
183 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);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700234 return res;
235}
236
237int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700238 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700239}
240
241int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700242 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700243}
244
JP Abgrall26e0d492011-06-24 19:21:51 -0700245int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700246 char cmd[MAX_CMD_LEN];
247 int uidNum;
JP Abgrall26e0d492011-06-24 19:21:51 -0700248 const char *failLogTemplate;
249 IptOp op;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700250 int appUids[numUids];
JP Abgrall26e0d492011-06-24 19:21:51 -0700251 std::string naughtyCmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700252 switch (appOp) {
253 case NaughtyAppOpAdd:
254 op = IptOpInsert;
255 failLogTemplate = "Failed to add app uid %d to penalty box.";
256 break;
257 case NaughtyAppOpRemove:
258 op = IptOpDelete;
259 failLogTemplate = "Failed to delete app uid %d from penalty box.";
260 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700261 }
262
263 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700264 appUids[uidNum] = atol(appStrUids[uidNum]);
265 if (appUids[uidNum] == 0) {
266 LOGE(failLogTemplate, appUids[uidNum]);
267 goto fail_parse;
268 }
269 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700270
271 for (uidNum = 0; uidNum < numUids; uidNum++) {
272 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
273 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
274 LOGE(failLogTemplate, appUids[uidNum]);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700275 goto fail_with_uidNum;
276 }
277 }
278 return 0;
279
JP Abgrall26e0d492011-06-24 19:21:51 -0700280fail_with_uidNum:
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700281 /* Try to remove the uid that failed in any case*/
JP Abgrall26e0d492011-06-24 19:21:51 -0700282 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
283 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
284fail_parse:
285 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700286}
287
JP Abgrall26e0d492011-06-24 19:21:51 -0700288std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700289 std::string res;
JP Abgrallbfa74662011-06-29 19:23:04 -0700290 char *convBuff;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700291
292 LOGD("makeIptablesQuotaCmd(%d, %llu)", op, quota);
293
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700294 switch (op) {
295 case IptOpInsert:
296 res = "-I";
297 break;
298 case IptOpReplace:
299 res = "-R";
300 break;
301 default:
302 case IptOpDelete:
303 res = "-D";
304 break;
305 }
JP Abgrallbfa74662011-06-29 19:23:04 -0700306 res += " costly_";
307 res += costName;
308 asprintf(&convBuff, "%lld", quota);
309 res += " -m quota2 --name ";
310 res += costName;
311 res += " ! --quota ";
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700312 res += convBuff;
JP Abgrallbfa74662011-06-29 19:23:04 -0700313 free(convBuff);
314 // The requried IP version specific --jump REJECT ... will be added later.
JP Abgrall0dad7c22011-06-24 11:58:14 -0700315 return res;
316}
317
JP Abgrall26e0d492011-06-24 19:21:51 -0700318int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700319 char cmd[MAX_CMD_LEN];
320 int res = 0;
321 std::string costString;
322 const char *costCString;
323
JP Abgrall0dad7c22011-06-24 11:58:14 -0700324 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700325 switch (quotaType) {
326 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700327 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700328 costString += ifn;
329 costCString = costString.c_str();
330 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700331 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700332 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", 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 -m owner --socket-exists", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700335 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700336 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
337 * chain. For now, hack the chain exit with an ACCEPT.
338 */
339 snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700340 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
341 break;
342 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700343 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700344 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700345 }
346
347 snprintf(cmd, sizeof(cmd), "-I INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700348 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700349 snprintf(cmd, sizeof(cmd), "-I OUTPUT -o %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 return res;
352}
353
JP Abgrall26e0d492011-06-24 19:21:51 -0700354int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700355 char cmd[MAX_CMD_LEN];
356 int res = 0;
357 std::string costString;
358 const char *costCString;
359
JP Abgrall26e0d492011-06-24 19:21:51 -0700360 switch (quotaType) {
361 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700362 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700363 costString += ifn;
364 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700365 break;
366 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700367 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700368 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700369 }
370
371 snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700372 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700373 snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700374 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700375
JP Abgrallbfa74662011-06-29 19:23:04 -0700376 /* The "-N costly_shared" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700377 if (quotaType == QuotaUnique) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700378 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700379 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgralla9f802c2011-06-29 15:46:45 -0700380 snprintf(cmd, sizeof(cmd), "-X %s", costCString);
381 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700382 }
383 return res;
384}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700385
JP Abgrall0dad7c22011-06-24 11:58:14 -0700386int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700387 char cmd[MAX_CMD_LEN];
388 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700389 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700390 std::string quotaCmd;
391 std::string ifaceName;;
JP Abgrallbfa74662011-06-29 19:23:04 -0700392 const char *costName = "shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700393 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700394
JP Abgrall26e0d492011-06-24 19:21:51 -0700395 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
396 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
397 return -1;
398 }
399 ifaceName = ifn;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700400
401 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700402 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700403 }
404
405 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700406 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
407 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700408 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700409 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700410
JP Abgrall0dad7c22011-06-24 11:58:14 -0700411 if (it == sharedQuotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700412 res |= prepCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700413 if (sharedQuotaIfaces.empty()) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700414 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700415 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700416 if (res) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700417 LOGE("Failed set quota rule.");
418 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700419 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700420 sharedQuotaBytes = maxBytes;
421 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700422 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700423
424 }
425
426 if (maxBytes != sharedQuotaBytes) {
427 /* Instead of replacing, which requires being aware of the rules in
428 * the kernel, we just add a new one, then delete the older one.
429 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700430
JP Abgrall0dad7c22011-06-24 11:58:14 -0700431 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700432 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700433
JP Abgrall0dad7c22011-06-24 11:58:14 -0700434 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700435 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700436
437 if (res) {
438 LOGE("Failed replace quota rule.");
439 goto fail;
440 }
441 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700442 }
443 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700444
445 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700446 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700447 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
448 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700449 * For now callers needs to choose if they want to "ndc bandwidth enable"
450 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700451 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700452 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700453 return -1;
454}
455
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700456int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700457 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700458 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700459 std::string ifaceName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700460 std::list<std::string>::iterator it;
JP Abgrallbfa74662011-06-29 19:23:04 -0700461 const char *costName = "shared";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700462
JP Abgrall26e0d492011-06-24 19:21:51 -0700463 if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
464 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
465 return -1;
466 }
467 ifaceName =ifn;
468
JP Abgrall0dad7c22011-06-24 11:58:14 -0700469 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
470 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700471 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700472 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700473 if (it == sharedQuotaIfaces.end()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700474 LOGE("No such iface %s to delete.", ifn);
475 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700476 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700477
JP Abgrall26e0d492011-06-24 19:21:51 -0700478 res |= cleanupCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700479 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700480
JP Abgrall0dad7c22011-06-24 11:58:14 -0700481 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700482 std::string quotaCmd;
JP Abgrallbfa74662011-06-29 19:23:04 -0700483 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700484 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700485 sharedQuotaBytes = -1;
486 }
487
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700488 return res;
489}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700490
491int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
492 char ifn[MAX_IFACENAME_LEN];
493 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700494 std::string ifaceName;
495 const char *costName;
496 std::list<QuotaInfo>::iterator it;
497 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700498
499 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700500 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700501 }
502
JP Abgrall26e0d492011-06-24 19:21:51 -0700503 if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
504 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
505 return -1;
506 }
507 ifaceName = ifn;
508 costName = iface;
509
JP Abgrall0dad7c22011-06-24 11:58:14 -0700510
511 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700512 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
513 if (it->first == ifaceName)
514 break;
515 }
516
517 if (it == quotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700518 res |= prepCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700519 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700520 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700521 if (res) {
522 LOGE("Failed set quota rule.");
523 goto fail;
524 }
525
526 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes));
527
528 } else {
529 /* Instead of replacing, which requires being aware of the rules in
530 * the kernel, we just add a new one, then delete the older one.
531 */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700532 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700533 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700534
535 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, it->second);
JP Abgrall26e0d492011-06-24 19:21:51 -0700536 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700537
538 if (res) {
539 LOGE("Failed replace quota rule.");
540 goto fail;
541 }
542 it->second = maxBytes;
543 }
544 return 0;
545
546 fail:
547 /*
548 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
549 * rules in the kernel to see which ones need cleaning up.
550 * For now callers needs to choose if they want to "ndc bandwidth enable"
551 * which resets everything.
552 */
553 removeInterfaceSharedQuota(ifn);
554 return -1;
555}
556
557int BandwidthController::removeInterfaceQuota(const char *iface) {
558
559 char ifn[MAX_IFACENAME_LEN];
560 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700561 std::string ifaceName;
562 const char *costName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700563 std::list<QuotaInfo>::iterator it;
JP Abgrall26e0d492011-06-24 19:21:51 -0700564
565 if(StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
566 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
567 return -1;
568 }
569 ifaceName = ifn;
570 costName = iface;
571
JP Abgrall0dad7c22011-06-24 11:58:14 -0700572 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
573 if (it->first == ifaceName)
574 break;
575 }
576
577 if (it == quotaIfaces.end()) {
578 LOGE("No such iface %s to delete.", ifn);
579 return -1;
580 }
581
582 /* This also removes the quota command of CostlyIface chain. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700583 res |= cleanupCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700584
585 quotaIfaces.erase(it);
586
587 return res;
588}