blob: 17611459846e6cda12a2d78dc629e858708a8299 [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 Abgralldb7da582011-09-18 12:57:32 -070017// #define LOG_NDEBUG 0
18
19/*
20 * The CommandListener, FrameworkListener don't allow for
21 * multiple calls in parallel to reach the BandwidthController.
22 * If they ever were to allow it, then netd/ would need some tweaking.
23 */
24
JP Abgrall8a932722011-07-13 19:17:35 -070025#include <errno.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070026#include <fcntl.h>
JP Abgralldb7da582011-09-18 12:57:32 -070027#include <stdio.h>
JP Abgrall8a932722011-07-13 19:17:35 -070028#include <stdlib.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070029#include <string.h>
30
31#include <sys/socket.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34#include <sys/wait.h>
35
36#include <linux/netlink.h>
37#include <linux/rtnetlink.h>
38#include <linux/pkt_sched.h>
39
40#define LOG_TAG "BandwidthController"
41#include <cutils/log.h>
42#include <cutils/properties.h>
43
44extern "C" int logwrap(int argc, const char **argv, int background);
45
46#include "BandwidthController.h"
47
JP Abgralldb7da582011-09-18 12:57:32 -070048/* Alphabetical */
JP Abgrall87666692011-09-08 13:44:10 -070049const char BandwidthController::ALERT_IPT_TEMPLATE[] = "%s %s %s -m quota2 ! --quota %lld --name %s";
JP Abgralldb7da582011-09-18 12:57:32 -070050const int BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4;
51const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
52const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
53const int BandwidthController::MAX_CMD_ARGS = 32;
54const int BandwidthController::MAX_CMD_LEN = 1024;
55const int BandwidthController::MAX_IFACENAME_LEN = 64;
56const int BandwidthController::MAX_IPT_OUTPUT_LINE_LEN = 256;
57
JP Abgrall11b4e9b2011-08-11 15:34:49 -070058bool BandwidthController::useLogwrapCall = false;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070059
60/**
61 * Some comments about the rules:
62 * * Ordering
63 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
64 * E.g. "-I INPUT -i rmnet0 --goto costly"
65 * - quota'd rules in the costly chain should be before penalty_box lookups.
66 *
67 * * global quota vs per interface quota
68 * - global quota for all costly interfaces uses a single costly chain:
69 * . initial rules
JP Abgrallbfa74662011-06-29 19:23:04 -070070 * iptables -N costly_shared
71 * iptables -I INPUT -i iface0 --goto costly_shared
72 * iptables -I OUTPUT -o iface0 --goto costly_shared
73 * iptables -I costly_shared -m quota \! --quota 500000 \
74 * --jump REJECT --reject-with icmp-net-prohibited
75 * iptables -A costly_shared --jump penalty_box
76 * iptables -A costly_shared -m owner --socket-exists
JP Abgrall8a932722011-07-13 19:17:35 -070077 *
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070078 * . adding a new iface to this, E.g.:
JP Abgrallbfa74662011-06-29 19:23:04 -070079 * iptables -I INPUT -i iface1 --goto costly_shared
80 * iptables -I OUTPUT -o iface1 --goto costly_shared
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070081 *
82 * - quota per interface. This is achieve by having "costly" chains per quota.
83 * E.g. adding a new costly interface iface0 with its own quota:
84 * iptables -N costly_iface0
85 * iptables -I INPUT -i iface0 --goto costly_iface0
86 * iptables -I OUTPUT -o iface0 --goto costly_iface0
JP Abgrallbfa74662011-06-29 19:23:04 -070087 * iptables -A costly_iface0 -m quota \! --quota 500000 \
88 * --jump REJECT --reject-with icmp-net-prohibited
89 * iptables -A costly_iface0 --jump penalty_box
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070090 * iptables -A costly_iface0 -m owner --socket-exists
91 *
92 * * penalty_box handling:
93 * - only one penalty_box for all interfaces
94 * E.g Adding an app:
JP Abgrallbfa74662011-06-29 19:23:04 -070095 * iptables -A penalty_box -m owner --uid-owner app_3 \
96 * --jump REJECT --reject-with icmp-net-prohibited
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070097 */
JP Abgralldb7da582011-09-18 12:57:32 -070098const char *BandwidthController::IPT_CLEANUP_COMMANDS[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -070099 /* Cleanup rules. */
100 "-F",
101 "-t raw -F",
JP Abgrall39f8f242011-06-29 19:21:58 -0700102 /* TODO: If at some point we need more user chains than here, then we will need
103 * a different cleanup approach.
104 */
JP Abgrallbfa74662011-06-29 19:23:04 -0700105 "-X", /* Should normally only be costly_shared, penalty_box, and costly_<iface> */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700106};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700107
JP Abgralldb7da582011-09-18 12:57:32 -0700108const char *BandwidthController::IPT_SETUP_COMMANDS[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700109 /* Created needed chains. */
JP Abgrallbfa74662011-06-29 19:23:04 -0700110 "-N costly_shared",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700111 "-N penalty_box",
112};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700113
JP Abgralldb7da582011-09-18 12:57:32 -0700114const char *BandwidthController::IPT_BASIC_ACCOUNTING_COMMANDS[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700115 "-F INPUT",
116 "-A INPUT -i lo --jump ACCEPT",
117 "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700118
JP Abgrall0dad7c22011-06-24 11:58:14 -0700119 "-F OUTPUT",
120 "-A OUTPUT -o lo --jump ACCEPT",
121 "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700122
JP Abgrallbfa74662011-06-29 19:23:04 -0700123 "-F costly_shared",
124 "-A costly_shared --jump penalty_box",
125 "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700126 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
127 * chain. For now, hack the chain exit with an ACCEPT.
128 */
JP Abgrallbfa74662011-06-29 19:23:04 -0700129 "-A costly_shared --jump ACCEPT",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700130};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700131
132BandwidthController::BandwidthController(void) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700133 char value[PROPERTY_VALUE_MAX];
134
135 property_get("persist.bandwidth.enable", value, "0");
136 if (!strcmp(value, "1")) {
137 enableBandwidthControl();
138 }
139
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700140 property_get("persist.bandwidth.uselogwrap", value, "0");
141 useLogwrapCall = !strcmp(value, "1");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700142}
143
JP Abgrall26e0d492011-06-24 19:21:51 -0700144int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700145 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700146
JP Abgralldb7da582011-09-18 12:57:32 -0700147 LOGV("runIpxtablesCmd(cmd=%s)", cmd);
JP Abgrall26e0d492011-06-24 19:21:51 -0700148 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
149 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700150 return res;
151}
152
JP Abgrall26e0d492011-06-24 19:21:51 -0700153int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
154
155 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0'
156 strncpy(buffer, src, buffSize);
157 return buffer[buffSize - 1];
158}
159
JP Abgrall8a932722011-07-13 19:17:35 -0700160int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling,
161 IptIpVer iptVer) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700162 char buffer[MAX_CMD_LEN];
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700163 const char *argv[MAX_CMD_ARGS];
JP Abgrall26e0d492011-06-24 19:21:51 -0700164 int argc = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700165 char *next = buffer;
166 char *tmp;
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700167 int res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700168
JP Abgrall0dad7c22011-06-24 11:58:14 -0700169 std::string fullCmd = cmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700170
171 if (rejectHandling == IptRejectAdd) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700172 fullCmd += " --jump REJECT --reject-with";
JP Abgrall26e0d492011-06-24 19:21:51 -0700173 switch (iptVer) {
174 case IptIpV4:
JP Abgrall8a932722011-07-13 19:17:35 -0700175 fullCmd += " icmp-net-prohibited";
176 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700177 case IptIpV6:
JP Abgrall8a932722011-07-13 19:17:35 -0700178 fullCmd += " icmp6-adm-prohibited";
179 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700180 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700181 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700182
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700183 fullCmd.insert(0, " ");
184 fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700185
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700186 if (!useLogwrapCall) {
187 res = system(fullCmd.c_str());
188 } else {
189 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
190 LOGE("iptables command too long");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700191 return -1;
192 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700193
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700194 argc = 0;
195 while ((tmp = strsep(&next, " "))) {
196 argv[argc++] = tmp;
197 if (argc >= MAX_CMD_ARGS) {
198 LOGE("iptables argument overflow");
199 return -1;
200 }
201 }
202
203 argv[argc] = NULL;
204 res = logwrap(argc, argv, 0);
205 }
206 if (res) {
207 LOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res);
208 }
209 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700210}
211
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700212int BandwidthController::enableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700213 int res;
JP Abgrall8a932722011-07-13 19:17:35 -0700214
JP Abgralldb7da582011-09-18 12:57:32 -0700215 /* Let's pretend we started from scratch ... */
JP Abgrall8a932722011-07-13 19:17:35 -0700216 sharedQuotaIfaces.clear();
217 quotaIfaces.clear();
218 naughtyAppUids.clear();
JP Abgralldb7da582011-09-18 12:57:32 -0700219 globalAlertBytes = 0;
220 sharedQuotaBytes = sharedAlertBytes = 0;
221
222
223 /* Some of the initialCommands are allowed to fail */
224 runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
225 IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
226 runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*),
227 IPT_SETUP_COMMANDS, RunCmdFailureOk);
228 res = runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
229 IPT_BASIC_ACCOUNTING_COMMANDS, RunCmdFailureBad);
JP Abgrall8a932722011-07-13 19:17:35 -0700230
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700231 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700232
233}
234
235int BandwidthController::disableBandwidthControl(void) {
JP Abgralldb7da582011-09-18 12:57:32 -0700236 /* The IPT_CLEANUP_COMMANDS are allowed to fail. */
237 runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
238 IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700239 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700240}
241
JP Abgrall8a932722011-07-13 19:17:35 -0700242int BandwidthController::runCommands(int numCommands, const char *commands[],
243 RunCmdErrHandling cmdErrHandling) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700244 int res = 0;
JP Abgralldb7da582011-09-18 12:57:32 -0700245 LOGV("runCommands(): %d commands", numCommands);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700246 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700247 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
248 if (res && cmdErrHandling != RunCmdFailureBad)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700249 return res;
250 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700251 return cmdErrHandling == RunCmdFailureBad ? res : 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700252}
253
JP Abgrall0dad7c22011-06-24 11:58:14 -0700254std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700255 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700256 char *buff;
257 const char *opFlag;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700258
259 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700260 case IptOpInsert:
261 opFlag = "-I";
262 break;
263 case IptOpReplace:
264 opFlag = "-R";
265 break;
266 default:
267 case IptOpDelete:
268 opFlag = "-D";
269 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700270 }
JP Abgrall8a932722011-07-13 19:17:35 -0700271 asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid);
272 res = buff;
273 free(buff);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700274 return res;
275}
276
277int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700278 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700279}
280
281int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700282 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700283}
284
JP Abgrall26e0d492011-06-24 19:21:51 -0700285int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700286 char cmd[MAX_CMD_LEN];
287 int uidNum;
JP Abgrall26e0d492011-06-24 19:21:51 -0700288 const char *failLogTemplate;
289 IptOp op;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700290 int appUids[numUids];
JP Abgrall26e0d492011-06-24 19:21:51 -0700291 std::string naughtyCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700292
JP Abgrall26e0d492011-06-24 19:21:51 -0700293 switch (appOp) {
294 case NaughtyAppOpAdd:
JP Abgrall8a932722011-07-13 19:17:35 -0700295 op = IptOpInsert;
296 failLogTemplate = "Failed to add app uid %d to penalty box.";
297 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700298 case NaughtyAppOpRemove:
JP Abgrall8a932722011-07-13 19:17:35 -0700299 op = IptOpDelete;
300 failLogTemplate = "Failed to delete app uid %d from penalty box.";
301 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700302 }
303
304 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700305 appUids[uidNum] = atol(appStrUids[uidNum]);
306 if (appUids[uidNum] == 0) {
307 LOGE(failLogTemplate, appUids[uidNum]);
308 goto fail_parse;
309 }
310 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700311
312 for (uidNum = 0; uidNum < numUids; uidNum++) {
313 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
314 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
315 LOGE(failLogTemplate, appUids[uidNum]);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700316 goto fail_with_uidNum;
317 }
318 }
319 return 0;
320
JP Abgrall26e0d492011-06-24 19:21:51 -0700321fail_with_uidNum:
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700322 /* Try to remove the uid that failed in any case*/
JP Abgrall26e0d492011-06-24 19:21:51 -0700323 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
324 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
325fail_parse:
326 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700327}
328
JP Abgrall26e0d492011-06-24 19:21:51 -0700329std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700330 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700331 char *buff;
332 const char *opFlag;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700333
JP Abgralldb7da582011-09-18 12:57:32 -0700334 LOGV("makeIptablesQuotaCmd(%d, %lld)", op, quota);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700335
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700336 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700337 case IptOpInsert:
338 opFlag = "-I";
339 break;
340 case IptOpReplace:
341 opFlag = "-R";
342 break;
343 default:
344 case IptOpDelete:
345 opFlag = "-D";
346 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700347 }
JP Abgrall8a932722011-07-13 19:17:35 -0700348
JP Abgrallbfa74662011-06-29 19:23:04 -0700349 // The requried IP version specific --jump REJECT ... will be added later.
JP Abgrall8a932722011-07-13 19:17:35 -0700350 asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota,
351 costName);
352 res = buff;
353 free(buff);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700354 return res;
355}
356
JP Abgrall26e0d492011-06-24 19:21:51 -0700357int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700358 char cmd[MAX_CMD_LEN];
359 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700360 int ruleInsertPos = 1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700361 std::string costString;
362 const char *costCString;
363
JP Abgrall0dad7c22011-06-24 11:58:14 -0700364 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700365 switch (quotaType) {
366 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700367 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700368 costString += ifn;
369 costCString = costString.c_str();
370 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700371 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700372 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700373 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700374 snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700375 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700376 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
377 * chain. For now, hack the chain exit with an ACCEPT.
378 */
379 snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700380 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
381 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
JP Abgrall8a932722011-07-13 19:17:35 -0700387 if (globalAlertBytes) {
388 /* The alert rule comes 1st */
389 ruleInsertPos = 2;
390 }
391 snprintf(cmd, sizeof(cmd), "-I INPUT %d -i %s --goto %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700392 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700393 snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700394 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700395 return res;
396}
397
JP Abgrall26e0d492011-06-24 19:21:51 -0700398int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700399 char cmd[MAX_CMD_LEN];
400 int res = 0;
401 std::string costString;
402 const char *costCString;
403
JP Abgrall26e0d492011-06-24 19:21:51 -0700404 switch (quotaType) {
405 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700406 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700407 costString += ifn;
408 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700409 break;
410 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700411 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700412 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700413 }
414
415 snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700416 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700417 snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700418 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700419
JP Abgrallbfa74662011-06-29 19:23:04 -0700420 /* The "-N costly_shared" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700421 if (quotaType == QuotaUnique) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700422 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700423 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgralla9f802c2011-06-29 15:46:45 -0700424 snprintf(cmd, sizeof(cmd), "-X %s", costCString);
425 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700426 }
427 return res;
428}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700429
JP Abgrall0dad7c22011-06-24 11:58:14 -0700430int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700431 char cmd[MAX_CMD_LEN];
432 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700433 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700434 std::string quotaCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700435 std::string ifaceName;
436 ;
JP Abgrallbfa74662011-06-29 19:23:04 -0700437 const char *costName = "shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700438 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700439
JP Abgrall8a932722011-07-13 19:17:35 -0700440 if (!maxBytes) {
441 /* Don't talk about -1, deprecate it. */
442 LOGE("Invalid bytes value. 1..max_int64.");
443 return -1;
444 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700445 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
446 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
447 return -1;
448 }
449 ifaceName = ifn;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700450
451 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700452 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700453 }
454
455 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700456 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
457 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700458 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700459 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700460
JP Abgrall0dad7c22011-06-24 11:58:14 -0700461 if (it == sharedQuotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700462 res |= prepCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700463 if (sharedQuotaIfaces.empty()) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700464 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700465 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700466 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700467 LOGE("Failed set quota rule");
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700468 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700469 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700470 sharedQuotaBytes = maxBytes;
471 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700472 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700473
474 }
475
476 if (maxBytes != sharedQuotaBytes) {
JP Abgrall8a932722011-07-13 19:17:35 -0700477 res |= updateQuota(costName, maxBytes);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700478 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700479 LOGE("Failed update quota for %s", costName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700480 goto fail;
481 }
482 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700483 }
484 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700485
486 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700487 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700488 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
489 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700490 * For now callers needs to choose if they want to "ndc bandwidth enable"
491 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700492 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700493 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700494 return -1;
495}
496
JP Abgrall8a932722011-07-13 19:17:35 -0700497/* It will also cleanup any shared alerts */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700498int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700499 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700500 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700501 std::string ifaceName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700502 std::list<std::string>::iterator it;
JP Abgrallbfa74662011-06-29 19:23:04 -0700503 const char *costName = "shared";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700504
JP Abgrall8a932722011-07-13 19:17:35 -0700505 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700506 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
507 return -1;
508 }
JP Abgrall8a932722011-07-13 19:17:35 -0700509 ifaceName = ifn;
JP Abgrall26e0d492011-06-24 19:21:51 -0700510
JP Abgrall0dad7c22011-06-24 11:58:14 -0700511 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
512 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700513 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700514 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700515 if (it == sharedQuotaIfaces.end()) {
JP Abgrall8a932722011-07-13 19:17:35 -0700516 LOGE("No such iface %s to delete", ifn);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700517 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700518 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700519
JP Abgrall26e0d492011-06-24 19:21:51 -0700520 res |= cleanupCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700521 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700522
JP Abgrall0dad7c22011-06-24 11:58:14 -0700523 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700524 std::string quotaCmd;
JP Abgrallbfa74662011-06-29 19:23:04 -0700525 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700526 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700527 sharedQuotaBytes = 0;
528 if (sharedAlertBytes) {
529 removeSharedAlert();
530 sharedAlertBytes = 0;
531 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700532 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700533 return res;
534}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700535
536int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
537 char ifn[MAX_IFACENAME_LEN];
538 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700539 std::string ifaceName;
540 const char *costName;
541 std::list<QuotaInfo>::iterator it;
542 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700543
JP Abgrall8a932722011-07-13 19:17:35 -0700544 if (!maxBytes) {
545 /* Don't talk about -1, deprecate it. */
546 LOGE("Invalid bytes value. 1..max_int64.");
547 return -1;
548 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700549 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700550 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700551 }
552
JP Abgrall8a932722011-07-13 19:17:35 -0700553 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700554 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
555 return -1;
556 }
557 ifaceName = ifn;
558 costName = iface;
559
JP Abgrall0dad7c22011-06-24 11:58:14 -0700560 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700561 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700562 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700563 break;
564 }
565
566 if (it == quotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700567 res |= prepCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700568 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700569 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700570 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700571 LOGE("Failed set quota rule");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700572 goto fail;
573 }
574
JP Abgrall8a932722011-07-13 19:17:35 -0700575 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
JP Abgrall0dad7c22011-06-24 11:58:14 -0700576
577 } else {
JP Abgrall8a932722011-07-13 19:17:35 -0700578 res |= updateQuota(costName, maxBytes);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700579 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700580 LOGE("Failed update quota for %s", iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700581 goto fail;
582 }
JP Abgrall8a932722011-07-13 19:17:35 -0700583 it->quota = maxBytes;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700584 }
585 return 0;
586
587 fail:
588 /*
589 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
590 * rules in the kernel to see which ones need cleaning up.
591 * For now callers needs to choose if they want to "ndc bandwidth enable"
592 * which resets everything.
593 */
594 removeInterfaceSharedQuota(ifn);
595 return -1;
596}
597
JP Abgrall8a932722011-07-13 19:17:35 -0700598int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
599 return getInterfaceQuota("shared", bytes);
600}
601
602int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
603 FILE *fp;
604 char *fname;
605 int scanRes;
606
607 asprintf(&fname, "/proc/net/xt_quota/%s", costName);
608 fp = fopen(fname, "r");
609 free(fname);
610 if (!fp) {
611 LOGE("Reading quota %s failed (%s)", costName, strerror(errno));
612 return -1;
613 }
614 scanRes = fscanf(fp, "%lld", bytes);
JP Abgralldb7da582011-09-18 12:57:32 -0700615 LOGV("Read quota res=%d bytes=%lld", scanRes, *bytes);
JP Abgrall8a932722011-07-13 19:17:35 -0700616 fclose(fp);
617 return scanRes == 1 ? 0 : -1;
618}
619
JP Abgrall0dad7c22011-06-24 11:58:14 -0700620int BandwidthController::removeInterfaceQuota(const char *iface) {
621
622 char ifn[MAX_IFACENAME_LEN];
623 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700624 std::string ifaceName;
625 const char *costName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700626 std::list<QuotaInfo>::iterator it;
JP Abgrall26e0d492011-06-24 19:21:51 -0700627
JP Abgrall8a932722011-07-13 19:17:35 -0700628 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700629 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
630 return -1;
631 }
632 ifaceName = ifn;
633 costName = iface;
634
JP Abgrall0dad7c22011-06-24 11:58:14 -0700635 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700636 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700637 break;
638 }
639
640 if (it == quotaIfaces.end()) {
JP Abgrall8a932722011-07-13 19:17:35 -0700641 LOGE("No such iface %s to delete", ifn);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700642 return -1;
643 }
644
645 /* This also removes the quota command of CostlyIface chain. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700646 res |= cleanupCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700647
648 quotaIfaces.erase(it);
649
650 return res;
651}
JP Abgrall8a932722011-07-13 19:17:35 -0700652
653int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
654 FILE *fp;
655 char *fname;
656
657 asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
658 fp = fopen(fname, "w");
659 free(fname);
660 if (!fp) {
661 LOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
662 return -1;
663 }
664 fprintf(fp, "%lld\n", bytes);
665 fclose(fp);
666 return 0;
667}
668
669int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
670 int res = 0;
671 const char *opFlag;
JP Abgrall87666692011-09-08 13:44:10 -0700672 const char *ifaceLimiting;
JP Abgrall8a932722011-07-13 19:17:35 -0700673 char *alertQuotaCmd;
674
675 switch (op) {
676 case IptOpInsert:
677 opFlag = "-I";
678 break;
679 case IptOpReplace:
680 opFlag = "-R";
681 break;
682 default:
683 case IptOpDelete:
684 opFlag = "-D";
685 break;
686 }
687
JP Abgrall87666692011-09-08 13:44:10 -0700688 ifaceLimiting = "! -i lo+";
689 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "INPUT",
690 bytes, alertName, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700691 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
692 free(alertQuotaCmd);
JP Abgrall87666692011-09-08 13:44:10 -0700693 ifaceLimiting = "! -o lo+";
694 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "OUTPUT",
695 bytes, alertName, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700696 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
697 free(alertQuotaCmd);
698 return res;
699}
700
701int BandwidthController::setGlobalAlert(int64_t bytes) {
702 char *alertQuotaCmd;
703 const char *alertName = "globalAlert";
704 int res = 0;
705
706 if (!bytes) {
707 LOGE("Invalid bytes value. 1..max_int64.");
708 return -1;
709 }
710 if (globalAlertBytes) {
711 res = updateQuota(alertName, bytes);
712 } else {
713 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
714 }
715 globalAlertBytes = bytes;
716 return res;
717}
718
719int BandwidthController::removeGlobalAlert(void) {
720 char *alertQuotaCmd;
721
722 const char *alertName = "globalAlert";
723 int res = 0;
724
725 if (!globalAlertBytes) {
726 LOGE("No prior alert set");
727 return -1;
728 }
729 res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
730 globalAlertBytes = 0;
731 return res;
732}
733
734int BandwidthController::setSharedAlert(int64_t bytes) {
735 if (!sharedQuotaBytes) {
736 LOGE("Need to have a prior shared quota set to set an alert");
737 return -1;
738 }
739 if (!bytes) {
740 LOGE("Invalid bytes value. 1..max_int64.");
741 return -1;
742 }
743 return setCostlyAlert("shared", bytes, &sharedAlertBytes);
744}
745
746int BandwidthController::removeSharedAlert(void) {
747 return removeCostlyAlert("shared", &sharedAlertBytes);
748}
749
750int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
751 std::list<QuotaInfo>::iterator it;
752
753 if (!bytes) {
754 LOGE("Invalid bytes value. 1..max_int64.");
755 return -1;
756 }
757 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
758 if (it->ifaceName == iface)
759 break;
760 }
761
762 if (it == quotaIfaces.end()) {
763 LOGE("Need to have a prior interface quota set to set an alert");
764 return -1;
765 }
766
767 return setCostlyAlert(iface, bytes, &it->alert);
768}
769
770int BandwidthController::removeInterfaceAlert(const char *iface) {
771 std::list<QuotaInfo>::iterator it;
772
773 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
774 if (it->ifaceName == iface)
775 break;
776 }
777
778 if (it == quotaIfaces.end()) {
779 LOGE("No prior alert set for interface %s", iface);
780 return -1;
781 }
782
783 return removeCostlyAlert(iface, &it->alert);
784}
785
786int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
787 char *alertQuotaCmd;
788 char *chainNameAndPos;
789 int res = 0;
790 char *alertName;
791
792 if (!bytes) {
793 LOGE("Invalid bytes value. 1..max_int64.");
794 return -1;
795 }
796 asprintf(&alertName, "%sAlert", costName);
797 if (*alertBytes) {
798 res = updateQuota(alertName, *alertBytes);
799 } else {
800 asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN);
801 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-I", chainNameAndPos, bytes, alertName,
802 alertName);
803 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
804 free(alertQuotaCmd);
805 free(chainNameAndPos);
806 }
807 *alertBytes = bytes;
808 free(alertName);
809 return res;
810}
811
812int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
813 char *alertQuotaCmd;
814 char *chainName;
815 char *alertName;
816 int res = 0;
817
818 asprintf(&alertName, "%sAlert", costName);
819 if (!*alertBytes) {
820 LOGE("No prior alert set for %s alert", costName);
821 return -1;
822 }
823
824 asprintf(&chainName, "costly_%s", costName);
825 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName, alertName);
826 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
827 free(alertQuotaCmd);
828 free(chainName);
829
830 *alertBytes = 0;
831 free(alertName);
832 return res;
833}
JP Abgralldb7da582011-09-18 12:57:32 -0700834
835/*
836 * Parse the ptks and bytes out of:
837 * Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
838 * pkts bytes target prot opt in out source destination
839 * 0 0 ACCEPT all -- rmnet0 wlan0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
840 * 0 0 DROP all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 state INVALID
841 * 0 0 ACCEPT all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0
842 *
843 */
844int BandwidthController::parseForwardChainStats(TetherStats &stats, FILE *fp) {
845 int res;
846 char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
847 char iface0[MAX_IPT_OUTPUT_LINE_LEN];
848 char iface1[MAX_IPT_OUTPUT_LINE_LEN];
849 char rest[MAX_IPT_OUTPUT_LINE_LEN];
850
851 char *buffPtr;
852 int64_t packets, bytes;
853
854 while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
855 /* Clean up, so a failed parse can still print info */
856 iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
857 res = sscanf(buffPtr, "%lld %lld ACCEPT all -- %s %s 0.%s",
858 &packets, &bytes, iface0, iface1, rest);
859 LOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%lld bytes=%lld rest=<%s> orig line=<%s>", res,
860 iface0, iface1, packets, bytes, rest, buffPtr);
861 if (res != 5) {
862 continue;
863 }
864 if ((stats.ifaceIn == iface0) && (stats.ifaceOut == iface1)) {
865 LOGV("iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
866 stats.rxPackets = packets;
867 stats.rxBytes = bytes;
868 } else if ((stats.ifaceOut == iface0) && (stats.ifaceIn == iface1)) {
869 LOGV("iface_in=%s iface_out=%s tx_bytes=%lld tx_packets=%lld ", iface1, iface0, bytes, packets);
870 stats.txPackets = packets;
871 stats.txBytes = bytes;
872 }
873 }
874 /* Failure if rx or tx was not found */
875 return (stats.rxBytes == -1 || stats.txBytes == -1) ? -1 : 0;
876}
877
878
879char *BandwidthController::TetherStats::getStatsLine(void) {
880 char *msg;
881 asprintf(&msg, "%s %s %lld %lld %lld %lld", ifaceIn.c_str(), ifaceOut.c_str(),
882 rxBytes, rxPackets, txBytes, txPackets);
883 return msg;
884}
885
886int BandwidthController::getTetherStats(TetherStats &stats) {
887 int res;
888 std::string fullCmd;
889 FILE *iptOutput;
890 const char *cmd;
891
892 if (stats.rxBytes != -1 || stats.txBytes != -1) {
893 LOGE("Unexpected input stats. Byte counts should be -1.");
894 return -1;
895 }
896
897 /*
898 * Why not use some kind of lib to talk to iptables?
899 * Because the only libs are libiptc and libip6tc in iptables, and they are
900 * not easy to use. They require the known iptables match modules to be
901 * preloaded/linked, and require apparently a lot of wrapper code to get
902 * the wanted info.
903 */
904 fullCmd = IPTABLES_PATH;
905 fullCmd += " -nvx -L FORWARD";
906 iptOutput = popen(fullCmd.c_str(), "r");
907 if (!iptOutput) {
908 LOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
909 return -1;
910 }
911 res = parseForwardChainStats(stats, iptOutput);
912 pclose(iptOutput);
913
914 /* Currently NatController doesn't do ipv6 tethering, so we are done. */
915 return res;
916}