blob: 441818d81cc61083de26aa6670aa201cf68371a1 [file] [log] [blame]
JP Abgrall4a5f5ca2011-06-15 18:37:39 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
JP Abgrall8a932722011-07-13 19:17:35 -070017#include <errno.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070018#include <fcntl.h>
JP Abgrall8a932722011-07-13 19:17:35 -070019#include <stdlib.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070020#include <string.h>
21
22#include <sys/socket.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <sys/wait.h>
26
27#include <linux/netlink.h>
28#include <linux/rtnetlink.h>
29#include <linux/pkt_sched.h>
30
31#define LOG_TAG "BandwidthController"
32#include <cutils/log.h>
33#include <cutils/properties.h>
34
35extern "C" int logwrap(int argc, const char **argv, int background);
36
37#include "BandwidthController.h"
38
JP Abgrallfa6f46d2011-06-17 23:17:28 -070039const int BandwidthController::MAX_CMD_LEN = 1024;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070040const int BandwidthController::MAX_IFACENAME_LEN = 64;
41const int BandwidthController::MAX_CMD_ARGS = 32;
42const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
JP Abgrallfa6f46d2011-06-17 23:17:28 -070043const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
JP Abgrall8a932722011-07-13 19:17:35 -070044const char BandwidthController::ALERT_IPT_TEMPLATE[] = "%s %s -m quota2 ! --quota %lld --name %s";
45const int BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070046
47/**
48 * Some comments about the rules:
49 * * Ordering
50 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
51 * E.g. "-I INPUT -i rmnet0 --goto costly"
52 * - quota'd rules in the costly chain should be before penalty_box lookups.
53 *
54 * * global quota vs per interface quota
55 * - global quota for all costly interfaces uses a single costly chain:
56 * . initial rules
JP Abgrallbfa74662011-06-29 19:23:04 -070057 * iptables -N costly_shared
58 * iptables -I INPUT -i iface0 --goto costly_shared
59 * iptables -I OUTPUT -o iface0 --goto costly_shared
60 * iptables -I costly_shared -m quota \! --quota 500000 \
61 * --jump REJECT --reject-with icmp-net-prohibited
62 * iptables -A costly_shared --jump penalty_box
63 * iptables -A costly_shared -m owner --socket-exists
JP Abgrall8a932722011-07-13 19:17:35 -070064 *
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070065 * . adding a new iface to this, E.g.:
JP Abgrallbfa74662011-06-29 19:23:04 -070066 * iptables -I INPUT -i iface1 --goto costly_shared
67 * iptables -I OUTPUT -o iface1 --goto costly_shared
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070068 *
69 * - quota per interface. This is achieve by having "costly" chains per quota.
70 * E.g. adding a new costly interface iface0 with its own quota:
71 * iptables -N costly_iface0
72 * iptables -I INPUT -i iface0 --goto costly_iface0
73 * iptables -I OUTPUT -o iface0 --goto costly_iface0
JP Abgrallbfa74662011-06-29 19:23:04 -070074 * iptables -A costly_iface0 -m quota \! --quota 500000 \
75 * --jump REJECT --reject-with icmp-net-prohibited
76 * iptables -A costly_iface0 --jump penalty_box
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070077 * iptables -A costly_iface0 -m owner --socket-exists
78 *
79 * * penalty_box handling:
80 * - only one penalty_box for all interfaces
81 * E.g Adding an app:
JP Abgrallbfa74662011-06-29 19:23:04 -070082 * iptables -A penalty_box -m owner --uid-owner app_3 \
83 * --jump REJECT --reject-with icmp-net-prohibited
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070084 */
85const char *BandwidthController::cleanupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070086 /* Cleanup rules. */
87 "-F",
88 "-t raw -F",
JP Abgrall39f8f242011-06-29 19:21:58 -070089 /* TODO: If at some point we need more user chains than here, then we will need
90 * a different cleanup approach.
91 */
JP Abgrallbfa74662011-06-29 19:23:04 -070092 "-X", /* Should normally only be costly_shared, penalty_box, and costly_<iface> */
JP Abgrall0dad7c22011-06-24 11:58:14 -070093};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070094
95const char *BandwidthController::setupCommands[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070096 /* Created needed chains. */
JP Abgrallbfa74662011-06-29 19:23:04 -070097 "-N costly_shared",
JP Abgrall0dad7c22011-06-24 11:58:14 -070098 "-N penalty_box",
99};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700100
JP Abgrall0dad7c22011-06-24 11:58:14 -0700101const char *BandwidthController::basicAccountingCommands[] = {
102 "-F INPUT",
103 "-A INPUT -i lo --jump ACCEPT",
104 "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700105
JP Abgrall0dad7c22011-06-24 11:58:14 -0700106 "-F OUTPUT",
107 "-A OUTPUT -o lo --jump ACCEPT",
108 "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700109
JP Abgrallbfa74662011-06-29 19:23:04 -0700110 "-F costly_shared",
111 "-A costly_shared --jump penalty_box",
112 "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700113 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
114 * chain. For now, hack the chain exit with an ACCEPT.
115 */
JP Abgrallbfa74662011-06-29 19:23:04 -0700116 "-A costly_shared --jump ACCEPT",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700117};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700118
119BandwidthController::BandwidthController(void) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700120 char value[PROPERTY_VALUE_MAX];
121
122 property_get("persist.bandwidth.enable", value, "0");
123 if (!strcmp(value, "1")) {
124 enableBandwidthControl();
125 }
126
127}
128
JP Abgrall26e0d492011-06-24 19:21:51 -0700129int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700130 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700131
JP Abgrall26e0d492011-06-24 19:21:51 -0700132 LOGD("runIpxtablesCmd(cmd=%s)", cmd);
133 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
134 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700135 return res;
136}
137
JP Abgrall26e0d492011-06-24 19:21:51 -0700138int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
139
140 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0'
141 strncpy(buffer, src, buffSize);
142 return buffer[buffSize - 1];
143}
144
JP Abgrall8a932722011-07-13 19:17:35 -0700145int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling,
146 IptIpVer iptVer) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700147 char buffer[MAX_CMD_LEN];
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700148 const char *argv[MAX_CMD_ARGS];
JP Abgrall26e0d492011-06-24 19:21:51 -0700149 int argc = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700150 char *next = buffer;
151 char *tmp;
152
JP Abgrall0dad7c22011-06-24 11:58:14 -0700153 std::string fullCmd = cmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700154
155 if (rejectHandling == IptRejectAdd) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700156 fullCmd += " --jump REJECT --reject-with";
JP Abgrall26e0d492011-06-24 19:21:51 -0700157 switch (iptVer) {
158 case IptIpV4:
JP Abgrall8a932722011-07-13 19:17:35 -0700159 fullCmd += " icmp-net-prohibited";
160 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700161 case IptIpV6:
JP Abgrall8a932722011-07-13 19:17:35 -0700162 fullCmd += " icmp6-adm-prohibited";
163 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700164 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700165 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700166
JP Abgrall26e0d492011-06-24 19:21:51 -0700167 argc = 0;
168 argv[argc++] = iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700169
JP Abgralla9f802c2011-06-29 15:46:45 -0700170 LOGD("runIptablesCmd(): %s %s", argv[0], fullCmd.c_str());
JP Abgrall26e0d492011-06-24 19:21:51 -0700171 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700172 LOGE("iptables command too long");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700173 return -1;
174 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700175
176 while ((tmp = strsep(&next, " "))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700177 argv[argc++] = tmp;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700178 if (argc >= MAX_CMD_ARGS) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700179 LOGE("iptables argument overflow");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700180 return -1;
181 }
182 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700183
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700184 argv[argc] = NULL;
185 /* TODO(jpa): Once this stabilizes, remove logwrap() as it tends to wedge netd
186 * Then just talk directly to the kernel via rtnetlink.
187 */
188 return logwrap(argc, argv, 0);
189}
190
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700191int BandwidthController::enableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700192 int res;
193 /* Some of the initialCommands are allowed to fail */
JP Abgrall26e0d492011-06-24 19:21:51 -0700194 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
195 runCommands(sizeof(setupCommands) / sizeof(char*), setupCommands, RunCmdFailureOk);
JP Abgrall8a932722011-07-13 19:17:35 -0700196 res = runCommands(sizeof(basicAccountingCommands) / sizeof(char*), basicAccountingCommands,
197 RunCmdFailureBad);
198
199 sharedQuotaBytes = sharedAlertBytes = 0;
200 sharedQuotaIfaces.clear();
201 quotaIfaces.clear();
202 naughtyAppUids.clear();
203
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700204 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700205
206}
207
208int BandwidthController::disableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700209 /* The cleanupCommands are allowed to fail. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700210 runCommands(sizeof(cleanupCommands) / sizeof(char*), cleanupCommands, RunCmdFailureOk);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700211 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700212}
213
JP Abgrall8a932722011-07-13 19:17:35 -0700214int BandwidthController::runCommands(int numCommands, const char *commands[],
215 RunCmdErrHandling cmdErrHandling) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700216 int res = 0;
217 LOGD("runCommands(): %d commands", numCommands);
218 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700219 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
220 if (res && cmdErrHandling != RunCmdFailureBad)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700221 return res;
222 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700223 return cmdErrHandling == RunCmdFailureBad ? res : 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700224}
225
JP Abgrall0dad7c22011-06-24 11:58:14 -0700226std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700227 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700228 char *buff;
229 const char *opFlag;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700230
231 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700232 case IptOpInsert:
233 opFlag = "-I";
234 break;
235 case IptOpReplace:
236 opFlag = "-R";
237 break;
238 default:
239 case IptOpDelete:
240 opFlag = "-D";
241 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700242 }
JP Abgrall8a932722011-07-13 19:17:35 -0700243 asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid);
244 res = buff;
245 free(buff);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700246 return res;
247}
248
249int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700250 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700251}
252
253int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700254 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700255}
256
JP Abgrall26e0d492011-06-24 19:21:51 -0700257int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700258 char cmd[MAX_CMD_LEN];
259 int uidNum;
JP Abgrall26e0d492011-06-24 19:21:51 -0700260 const char *failLogTemplate;
261 IptOp op;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700262 int appUids[numUids];
JP Abgrall26e0d492011-06-24 19:21:51 -0700263 std::string naughtyCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700264
JP Abgrall26e0d492011-06-24 19:21:51 -0700265 switch (appOp) {
266 case NaughtyAppOpAdd:
JP Abgrall8a932722011-07-13 19:17:35 -0700267 op = IptOpInsert;
268 failLogTemplate = "Failed to add app uid %d to penalty box.";
269 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700270 case NaughtyAppOpRemove:
JP Abgrall8a932722011-07-13 19:17:35 -0700271 op = IptOpDelete;
272 failLogTemplate = "Failed to delete app uid %d from penalty box.";
273 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700274 }
275
276 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700277 appUids[uidNum] = atol(appStrUids[uidNum]);
278 if (appUids[uidNum] == 0) {
279 LOGE(failLogTemplate, appUids[uidNum]);
280 goto fail_parse;
281 }
282 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700283
284 for (uidNum = 0; uidNum < numUids; uidNum++) {
285 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
286 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
287 LOGE(failLogTemplate, appUids[uidNum]);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700288 goto fail_with_uidNum;
289 }
290 }
291 return 0;
292
JP Abgrall26e0d492011-06-24 19:21:51 -0700293fail_with_uidNum:
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700294 /* Try to remove the uid that failed in any case*/
JP Abgrall26e0d492011-06-24 19:21:51 -0700295 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
296 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
297fail_parse:
298 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700299}
300
JP Abgrall26e0d492011-06-24 19:21:51 -0700301std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700302 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700303 char *buff;
304 const char *opFlag;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700305
JP Abgrall8a932722011-07-13 19:17:35 -0700306 LOGD("makeIptablesQuotaCmd(%d, %lld)", op, quota);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700307
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700308 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700309 case IptOpInsert:
310 opFlag = "-I";
311 break;
312 case IptOpReplace:
313 opFlag = "-R";
314 break;
315 default:
316 case IptOpDelete:
317 opFlag = "-D";
318 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700319 }
JP Abgrall8a932722011-07-13 19:17:35 -0700320
JP Abgrallbfa74662011-06-29 19:23:04 -0700321 // The requried IP version specific --jump REJECT ... will be added later.
JP Abgrall8a932722011-07-13 19:17:35 -0700322 asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota,
323 costName);
324 res = buff;
325 free(buff);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700326 return res;
327}
328
JP Abgrall26e0d492011-06-24 19:21:51 -0700329int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700330 char cmd[MAX_CMD_LEN];
331 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700332 int ruleInsertPos = 1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700333 std::string costString;
334 const char *costCString;
335
JP Abgrall0dad7c22011-06-24 11:58:14 -0700336 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700337 switch (quotaType) {
338 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700339 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700340 costString += ifn;
341 costCString = costString.c_str();
342 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700343 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700344 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700345 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700346 snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700347 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700348 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
349 * chain. For now, hack the chain exit with an ACCEPT.
350 */
351 snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700352 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
353 break;
354 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700355 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700356 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700357 }
358
JP Abgrall8a932722011-07-13 19:17:35 -0700359 if (globalAlertBytes) {
360 /* The alert rule comes 1st */
361 ruleInsertPos = 2;
362 }
363 snprintf(cmd, sizeof(cmd), "-I INPUT %d -i %s --goto %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700364 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700365 snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700366 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700367 return res;
368}
369
JP Abgrall26e0d492011-06-24 19:21:51 -0700370int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700371 char cmd[MAX_CMD_LEN];
372 int res = 0;
373 std::string costString;
374 const char *costCString;
375
JP Abgrall26e0d492011-06-24 19:21:51 -0700376 switch (quotaType) {
377 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700378 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700379 costString += ifn;
380 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700381 break;
382 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700383 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700384 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700385 }
386
387 snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700388 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700389 snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700390 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700391
JP Abgrallbfa74662011-06-29 19:23:04 -0700392 /* The "-N costly_shared" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700393 if (quotaType == QuotaUnique) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700394 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700395 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgralla9f802c2011-06-29 15:46:45 -0700396 snprintf(cmd, sizeof(cmd), "-X %s", costCString);
397 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700398 }
399 return res;
400}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700401
JP Abgrall0dad7c22011-06-24 11:58:14 -0700402int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700403 char cmd[MAX_CMD_LEN];
404 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700405 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700406 std::string quotaCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700407 std::string ifaceName;
408 ;
JP Abgrallbfa74662011-06-29 19:23:04 -0700409 const char *costName = "shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700410 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700411
JP Abgrall8a932722011-07-13 19:17:35 -0700412 if (!maxBytes) {
413 /* Don't talk about -1, deprecate it. */
414 LOGE("Invalid bytes value. 1..max_int64.");
415 return -1;
416 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700417 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
418 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
419 return -1;
420 }
421 ifaceName = ifn;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700422
423 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700424 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700425 }
426
427 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700428 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
429 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700430 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700431 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700432
JP Abgrall0dad7c22011-06-24 11:58:14 -0700433 if (it == sharedQuotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700434 res |= prepCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700435 if (sharedQuotaIfaces.empty()) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700436 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700437 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700438 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700439 LOGE("Failed set quota rule");
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700440 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700441 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700442 sharedQuotaBytes = maxBytes;
443 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700444 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700445
446 }
447
448 if (maxBytes != sharedQuotaBytes) {
JP Abgrall8a932722011-07-13 19:17:35 -0700449 res |= updateQuota(costName, maxBytes);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700450 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700451 LOGE("Failed update quota for %s", costName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700452 goto fail;
453 }
454 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700455 }
456 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700457
458 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700459 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700460 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
461 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700462 * For now callers needs to choose if they want to "ndc bandwidth enable"
463 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700464 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700465 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700466 return -1;
467}
468
JP Abgrall8a932722011-07-13 19:17:35 -0700469/* It will also cleanup any shared alerts */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700470int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700471 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700472 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700473 std::string ifaceName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700474 std::list<std::string>::iterator it;
JP Abgrallbfa74662011-06-29 19:23:04 -0700475 const char *costName = "shared";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700476
JP Abgrall8a932722011-07-13 19:17:35 -0700477 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700478 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
479 return -1;
480 }
JP Abgrall8a932722011-07-13 19:17:35 -0700481 ifaceName = ifn;
JP Abgrall26e0d492011-06-24 19:21:51 -0700482
JP Abgrall0dad7c22011-06-24 11:58:14 -0700483 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
484 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700485 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700486 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700487 if (it == sharedQuotaIfaces.end()) {
JP Abgrall8a932722011-07-13 19:17:35 -0700488 LOGE("No such iface %s to delete", ifn);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700489 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700490 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700491
JP Abgrall26e0d492011-06-24 19:21:51 -0700492 res |= cleanupCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700493 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700494
JP Abgrall0dad7c22011-06-24 11:58:14 -0700495 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700496 std::string quotaCmd;
JP Abgrallbfa74662011-06-29 19:23:04 -0700497 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700498 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700499 sharedQuotaBytes = 0;
500 if (sharedAlertBytes) {
501 removeSharedAlert();
502 sharedAlertBytes = 0;
503 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700504 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700505 return res;
506}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700507
508int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
509 char ifn[MAX_IFACENAME_LEN];
510 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700511 std::string ifaceName;
512 const char *costName;
513 std::list<QuotaInfo>::iterator it;
514 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700515
JP Abgrall8a932722011-07-13 19:17:35 -0700516 if (!maxBytes) {
517 /* Don't talk about -1, deprecate it. */
518 LOGE("Invalid bytes value. 1..max_int64.");
519 return -1;
520 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700521 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700522 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700523 }
524
JP Abgrall8a932722011-07-13 19:17:35 -0700525 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700526 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
527 return -1;
528 }
529 ifaceName = ifn;
530 costName = iface;
531
JP Abgrall0dad7c22011-06-24 11:58:14 -0700532 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700533 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700534 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700535 break;
536 }
537
538 if (it == quotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700539 res |= prepCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700540 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700541 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700542 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700543 LOGE("Failed set quota rule");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700544 goto fail;
545 }
546
JP Abgrall8a932722011-07-13 19:17:35 -0700547 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
JP Abgrall0dad7c22011-06-24 11:58:14 -0700548
549 } else {
JP Abgrall8a932722011-07-13 19:17:35 -0700550 res |= updateQuota(costName, maxBytes);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700551 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700552 LOGE("Failed update quota for %s", iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700553 goto fail;
554 }
JP Abgrall8a932722011-07-13 19:17:35 -0700555 it->quota = maxBytes;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700556 }
557 return 0;
558
559 fail:
560 /*
561 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
562 * rules in the kernel to see which ones need cleaning up.
563 * For now callers needs to choose if they want to "ndc bandwidth enable"
564 * which resets everything.
565 */
566 removeInterfaceSharedQuota(ifn);
567 return -1;
568}
569
JP Abgrall8a932722011-07-13 19:17:35 -0700570int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
571 return getInterfaceQuota("shared", bytes);
572}
573
574int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
575 FILE *fp;
576 char *fname;
577 int scanRes;
578
579 asprintf(&fname, "/proc/net/xt_quota/%s", costName);
580 fp = fopen(fname, "r");
581 free(fname);
582 if (!fp) {
583 LOGE("Reading quota %s failed (%s)", costName, strerror(errno));
584 return -1;
585 }
586 scanRes = fscanf(fp, "%lld", bytes);
587 LOGD("Read quota res=%d bytes=%lld", scanRes, *bytes);
588 fclose(fp);
589 return scanRes == 1 ? 0 : -1;
590}
591
JP Abgrall0dad7c22011-06-24 11:58:14 -0700592int BandwidthController::removeInterfaceQuota(const char *iface) {
593
594 char ifn[MAX_IFACENAME_LEN];
595 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700596 std::string ifaceName;
597 const char *costName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700598 std::list<QuotaInfo>::iterator it;
JP Abgrall26e0d492011-06-24 19:21:51 -0700599
JP Abgrall8a932722011-07-13 19:17:35 -0700600 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700601 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
602 return -1;
603 }
604 ifaceName = ifn;
605 costName = iface;
606
JP Abgrall0dad7c22011-06-24 11:58:14 -0700607 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700608 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700609 break;
610 }
611
612 if (it == quotaIfaces.end()) {
JP Abgrall8a932722011-07-13 19:17:35 -0700613 LOGE("No such iface %s to delete", ifn);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700614 return -1;
615 }
616
617 /* This also removes the quota command of CostlyIface chain. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700618 res |= cleanupCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700619
620 quotaIfaces.erase(it);
621
622 return res;
623}
JP Abgrall8a932722011-07-13 19:17:35 -0700624
625int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
626 FILE *fp;
627 char *fname;
628
629 asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
630 fp = fopen(fname, "w");
631 free(fname);
632 if (!fp) {
633 LOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
634 return -1;
635 }
636 fprintf(fp, "%lld\n", bytes);
637 fclose(fp);
638 return 0;
639}
640
641int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
642 int res = 0;
643 const char *opFlag;
644 char *alertQuotaCmd;
645
646 switch (op) {
647 case IptOpInsert:
648 opFlag = "-I";
649 break;
650 case IptOpReplace:
651 opFlag = "-R";
652 break;
653 default:
654 case IptOpDelete:
655 opFlag = "-D";
656 break;
657 }
658
659 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "INPUT", bytes, alertName, alertName);
660 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
661 free(alertQuotaCmd);
662 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "OUTPUT", bytes, alertName, alertName);
663 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
664 free(alertQuotaCmd);
665 return res;
666}
667
668int BandwidthController::setGlobalAlert(int64_t bytes) {
669 char *alertQuotaCmd;
670 const char *alertName = "globalAlert";
671 int res = 0;
672
673 if (!bytes) {
674 LOGE("Invalid bytes value. 1..max_int64.");
675 return -1;
676 }
677 if (globalAlertBytes) {
678 res = updateQuota(alertName, bytes);
679 } else {
680 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
681 }
682 globalAlertBytes = bytes;
683 return res;
684}
685
686int BandwidthController::removeGlobalAlert(void) {
687 char *alertQuotaCmd;
688
689 const char *alertName = "globalAlert";
690 int res = 0;
691
692 if (!globalAlertBytes) {
693 LOGE("No prior alert set");
694 return -1;
695 }
696 res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
697 globalAlertBytes = 0;
698 return res;
699}
700
701int BandwidthController::setSharedAlert(int64_t bytes) {
702 if (!sharedQuotaBytes) {
703 LOGE("Need to have a prior shared quota set to set an alert");
704 return -1;
705 }
706 if (!bytes) {
707 LOGE("Invalid bytes value. 1..max_int64.");
708 return -1;
709 }
710 return setCostlyAlert("shared", bytes, &sharedAlertBytes);
711}
712
713int BandwidthController::removeSharedAlert(void) {
714 return removeCostlyAlert("shared", &sharedAlertBytes);
715}
716
717int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
718 std::list<QuotaInfo>::iterator it;
719
720 if (!bytes) {
721 LOGE("Invalid bytes value. 1..max_int64.");
722 return -1;
723 }
724 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
725 if (it->ifaceName == iface)
726 break;
727 }
728
729 if (it == quotaIfaces.end()) {
730 LOGE("Need to have a prior interface quota set to set an alert");
731 return -1;
732 }
733
734 return setCostlyAlert(iface, bytes, &it->alert);
735}
736
737int BandwidthController::removeInterfaceAlert(const char *iface) {
738 std::list<QuotaInfo>::iterator it;
739
740 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
741 if (it->ifaceName == iface)
742 break;
743 }
744
745 if (it == quotaIfaces.end()) {
746 LOGE("No prior alert set for interface %s", iface);
747 return -1;
748 }
749
750 return removeCostlyAlert(iface, &it->alert);
751}
752
753int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
754 char *alertQuotaCmd;
755 char *chainNameAndPos;
756 int res = 0;
757 char *alertName;
758
759 if (!bytes) {
760 LOGE("Invalid bytes value. 1..max_int64.");
761 return -1;
762 }
763 asprintf(&alertName, "%sAlert", costName);
764 if (*alertBytes) {
765 res = updateQuota(alertName, *alertBytes);
766 } else {
767 asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN);
768 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-I", chainNameAndPos, bytes, alertName,
769 alertName);
770 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
771 free(alertQuotaCmd);
772 free(chainNameAndPos);
773 }
774 *alertBytes = bytes;
775 free(alertName);
776 return res;
777}
778
779int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
780 char *alertQuotaCmd;
781 char *chainName;
782 char *alertName;
783 int res = 0;
784
785 asprintf(&alertName, "%sAlert", costName);
786 if (!*alertBytes) {
787 LOGE("No prior alert set for %s alert", costName);
788 return -1;
789 }
790
791 asprintf(&chainName, "costly_%s", costName);
792 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName, alertName);
793 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
794 free(alertQuotaCmd);
795 free(chainName);
796
797 *alertBytes = 0;
798 free(alertName);
799 return res;
800}