blob: 17de451328c053c43e6f96a8bcd3ccbc7022add3 [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);
JP Abgrall9e5e0ce2011-12-14 15:20:59 -080045extern "C" int system_nosh(const char *command);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070046
47#include "BandwidthController.h"
48
JP Abgralldb7da582011-09-18 12:57:32 -070049/* Alphabetical */
JP Abgrall87666692011-09-08 13:44:10 -070050const char BandwidthController::ALERT_IPT_TEMPLATE[] = "%s %s %s -m quota2 ! --quota %lld --name %s";
JP Abgralldb7da582011-09-18 12:57:32 -070051const int BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4;
JP Abgrallc6c67342011-10-07 16:28:54 -070052const char BandwidthController::ALERT_GLOBAL_NAME[] = "globalAlert";
JP Abgralldb7da582011-09-18 12:57:32 -070053const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
54const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
55const int BandwidthController::MAX_CMD_ARGS = 32;
56const int BandwidthController::MAX_CMD_LEN = 1024;
57const int BandwidthController::MAX_IFACENAME_LEN = 64;
58const int BandwidthController::MAX_IPT_OUTPUT_LINE_LEN = 256;
59
JP Abgrall11b4e9b2011-08-11 15:34:49 -070060bool BandwidthController::useLogwrapCall = false;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070061
62/**
63 * Some comments about the rules:
64 * * Ordering
65 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
66 * E.g. "-I INPUT -i rmnet0 --goto costly"
67 * - quota'd rules in the costly chain should be before penalty_box lookups.
68 *
69 * * global quota vs per interface quota
70 * - global quota for all costly interfaces uses a single costly chain:
71 * . initial rules
JP Abgrallbfa74662011-06-29 19:23:04 -070072 * iptables -N costly_shared
73 * iptables -I INPUT -i iface0 --goto costly_shared
74 * iptables -I OUTPUT -o iface0 --goto costly_shared
75 * iptables -I costly_shared -m quota \! --quota 500000 \
76 * --jump REJECT --reject-with icmp-net-prohibited
77 * iptables -A costly_shared --jump penalty_box
78 * iptables -A costly_shared -m owner --socket-exists
JP Abgrall8a932722011-07-13 19:17:35 -070079 *
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070080 * . adding a new iface to this, E.g.:
JP Abgrallbfa74662011-06-29 19:23:04 -070081 * iptables -I INPUT -i iface1 --goto costly_shared
82 * iptables -I OUTPUT -o iface1 --goto costly_shared
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070083 *
84 * - quota per interface. This is achieve by having "costly" chains per quota.
85 * E.g. adding a new costly interface iface0 with its own quota:
86 * iptables -N costly_iface0
87 * iptables -I INPUT -i iface0 --goto costly_iface0
88 * iptables -I OUTPUT -o iface0 --goto costly_iface0
JP Abgrallbfa74662011-06-29 19:23:04 -070089 * iptables -A costly_iface0 -m quota \! --quota 500000 \
90 * --jump REJECT --reject-with icmp-net-prohibited
91 * iptables -A costly_iface0 --jump penalty_box
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070092 * iptables -A costly_iface0 -m owner --socket-exists
93 *
94 * * penalty_box handling:
95 * - only one penalty_box for all interfaces
96 * E.g Adding an app:
JP Abgrallbfa74662011-06-29 19:23:04 -070097 * iptables -A penalty_box -m owner --uid-owner app_3 \
98 * --jump REJECT --reject-with icmp-net-prohibited
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070099 */
JP Abgralldb7da582011-09-18 12:57:32 -0700100const char *BandwidthController::IPT_CLEANUP_COMMANDS[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700101 /* Cleanup rules. */
102 "-F",
103 "-t raw -F",
JP Abgrall39f8f242011-06-29 19:21:58 -0700104 /* TODO: If at some point we need more user chains than here, then we will need
105 * a different cleanup approach.
106 */
JP Abgrallbfa74662011-06-29 19:23:04 -0700107 "-X", /* Should normally only be costly_shared, penalty_box, and costly_<iface> */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700108};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700109
JP Abgralldb7da582011-09-18 12:57:32 -0700110const char *BandwidthController::IPT_SETUP_COMMANDS[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700111 /* Created needed chains. */
JP Abgrallbfa74662011-06-29 19:23:04 -0700112 "-N costly_shared",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700113 "-N penalty_box",
114};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700115
JP Abgralldb7da582011-09-18 12:57:32 -0700116const char *BandwidthController::IPT_BASIC_ACCOUNTING_COMMANDS[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700117 "-F INPUT",
118 "-A INPUT -i lo --jump ACCEPT",
119 "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700120
JP Abgrall0dad7c22011-06-24 11:58:14 -0700121 "-F OUTPUT",
122 "-A OUTPUT -o lo --jump ACCEPT",
123 "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700124
JP Abgrallbfa74662011-06-29 19:23:04 -0700125 "-F costly_shared",
126 "-A costly_shared --jump penalty_box",
127 "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700128 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
129 * chain. For now, hack the chain exit with an ACCEPT.
130 */
JP Abgrallbfa74662011-06-29 19:23:04 -0700131 "-A costly_shared --jump ACCEPT",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700132};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700133
134BandwidthController::BandwidthController(void) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700135 char value[PROPERTY_VALUE_MAX];
136
137 property_get("persist.bandwidth.enable", value, "0");
138 if (!strcmp(value, "1")) {
139 enableBandwidthControl();
140 }
141
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700142 property_get("persist.bandwidth.uselogwrap", value, "0");
143 useLogwrapCall = !strcmp(value, "1");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700144}
145
JP Abgrall26e0d492011-06-24 19:21:51 -0700146int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700147 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700148
Steve Block3fb42e02011-10-20 11:55:56 +0100149 ALOGV("runIpxtablesCmd(cmd=%s)", cmd);
JP Abgrall26e0d492011-06-24 19:21:51 -0700150 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
151 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700152 return res;
153}
154
JP Abgrall26e0d492011-06-24 19:21:51 -0700155int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
156
157 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0'
158 strncpy(buffer, src, buffSize);
159 return buffer[buffSize - 1];
160}
161
JP Abgrall8a932722011-07-13 19:17:35 -0700162int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling,
163 IptIpVer iptVer) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700164 char buffer[MAX_CMD_LEN];
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700165 const char *argv[MAX_CMD_ARGS];
JP Abgrall26e0d492011-06-24 19:21:51 -0700166 int argc = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700167 char *next = buffer;
168 char *tmp;
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700169 int res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700170
JP Abgrall0dad7c22011-06-24 11:58:14 -0700171 std::string fullCmd = cmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700172
173 if (rejectHandling == IptRejectAdd) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700174 fullCmd += " --jump REJECT --reject-with";
JP Abgrall26e0d492011-06-24 19:21:51 -0700175 switch (iptVer) {
176 case IptIpV4:
JP Abgrall8a932722011-07-13 19:17:35 -0700177 fullCmd += " icmp-net-prohibited";
178 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700179 case IptIpV6:
JP Abgrall8a932722011-07-13 19:17:35 -0700180 fullCmd += " icmp6-adm-prohibited";
181 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700182 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700183 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700184
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700185 fullCmd.insert(0, " ");
186 fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700187
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700188 if (!useLogwrapCall) {
JP Abgrall9e5e0ce2011-12-14 15:20:59 -0800189 res = system_nosh(fullCmd.c_str());
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700190 } else {
191 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
192 LOGE("iptables command too long");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700193 return -1;
194 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700195
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700196 argc = 0;
197 while ((tmp = strsep(&next, " "))) {
198 argv[argc++] = tmp;
199 if (argc >= MAX_CMD_ARGS) {
200 LOGE("iptables argument overflow");
201 return -1;
202 }
203 }
204
205 argv[argc] = NULL;
206 res = logwrap(argc, argv, 0);
207 }
208 if (res) {
209 LOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res);
210 }
211 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700212}
213
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700214int BandwidthController::enableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700215 int res;
JP Abgrall8a932722011-07-13 19:17:35 -0700216
JP Abgralldb7da582011-09-18 12:57:32 -0700217 /* Let's pretend we started from scratch ... */
JP Abgrall8a932722011-07-13 19:17:35 -0700218 sharedQuotaIfaces.clear();
219 quotaIfaces.clear();
220 naughtyAppUids.clear();
JP Abgralldb7da582011-09-18 12:57:32 -0700221 globalAlertBytes = 0;
JP Abgrallc6c67342011-10-07 16:28:54 -0700222 globalAlertTetherCount = 0;
JP Abgralldb7da582011-09-18 12:57:32 -0700223 sharedQuotaBytes = sharedAlertBytes = 0;
224
225
226 /* Some of the initialCommands are allowed to fail */
227 runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
228 IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
229 runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*),
230 IPT_SETUP_COMMANDS, RunCmdFailureOk);
231 res = runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
232 IPT_BASIC_ACCOUNTING_COMMANDS, RunCmdFailureBad);
JP Abgrall8a932722011-07-13 19:17:35 -0700233
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700234 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700235
236}
237
238int BandwidthController::disableBandwidthControl(void) {
JP Abgralldb7da582011-09-18 12:57:32 -0700239 /* The IPT_CLEANUP_COMMANDS are allowed to fail. */
240 runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
241 IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700242 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700243}
244
JP Abgrall8a932722011-07-13 19:17:35 -0700245int BandwidthController::runCommands(int numCommands, const char *commands[],
246 RunCmdErrHandling cmdErrHandling) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700247 int res = 0;
Steve Block3fb42e02011-10-20 11:55:56 +0100248 ALOGV("runCommands(): %d commands", numCommands);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700249 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700250 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
251 if (res && cmdErrHandling != RunCmdFailureBad)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700252 return res;
253 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700254 return cmdErrHandling == RunCmdFailureBad ? res : 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700255}
256
JP Abgrall0dad7c22011-06-24 11:58:14 -0700257std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700258 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700259 char *buff;
260 const char *opFlag;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700261
262 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700263 case IptOpInsert:
264 opFlag = "-I";
265 break;
266 case IptOpReplace:
267 opFlag = "-R";
268 break;
269 default:
270 case IptOpDelete:
271 opFlag = "-D";
272 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700273 }
JP Abgrall8a932722011-07-13 19:17:35 -0700274 asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid);
275 res = buff;
276 free(buff);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700277 return res;
278}
279
280int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700281 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700282}
283
284int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700285 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700286}
287
JP Abgrall26e0d492011-06-24 19:21:51 -0700288int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700289 char cmd[MAX_CMD_LEN];
290 int uidNum;
JP Abgrall26e0d492011-06-24 19:21:51 -0700291 const char *failLogTemplate;
292 IptOp op;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700293 int appUids[numUids];
JP Abgrall26e0d492011-06-24 19:21:51 -0700294 std::string naughtyCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700295
JP Abgrall26e0d492011-06-24 19:21:51 -0700296 switch (appOp) {
297 case NaughtyAppOpAdd:
JP Abgrall8a932722011-07-13 19:17:35 -0700298 op = IptOpInsert;
299 failLogTemplate = "Failed to add app uid %d to penalty box.";
300 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700301 case NaughtyAppOpRemove:
JP Abgrall8a932722011-07-13 19:17:35 -0700302 op = IptOpDelete;
303 failLogTemplate = "Failed to delete app uid %d from penalty box.";
304 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700305 }
306
307 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700308 appUids[uidNum] = atol(appStrUids[uidNum]);
309 if (appUids[uidNum] == 0) {
310 LOGE(failLogTemplate, appUids[uidNum]);
311 goto fail_parse;
312 }
313 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700314
315 for (uidNum = 0; uidNum < numUids; uidNum++) {
316 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
317 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
318 LOGE(failLogTemplate, appUids[uidNum]);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700319 goto fail_with_uidNum;
320 }
321 }
322 return 0;
323
JP Abgrall26e0d492011-06-24 19:21:51 -0700324fail_with_uidNum:
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700325 /* Try to remove the uid that failed in any case*/
JP Abgrall26e0d492011-06-24 19:21:51 -0700326 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
327 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
328fail_parse:
329 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700330}
331
JP Abgrall26e0d492011-06-24 19:21:51 -0700332std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700333 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700334 char *buff;
335 const char *opFlag;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700336
Steve Block3fb42e02011-10-20 11:55:56 +0100337 ALOGV("makeIptablesQuotaCmd(%d, %lld)", op, quota);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700338
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700339 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700340 case IptOpInsert:
341 opFlag = "-I";
342 break;
343 case IptOpReplace:
344 opFlag = "-R";
345 break;
346 default:
347 case IptOpDelete:
348 opFlag = "-D";
349 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700350 }
JP Abgrall8a932722011-07-13 19:17:35 -0700351
JP Abgrallbfa74662011-06-29 19:23:04 -0700352 // The requried IP version specific --jump REJECT ... will be added later.
JP Abgrall8a932722011-07-13 19:17:35 -0700353 asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota,
354 costName);
355 res = buff;
356 free(buff);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700357 return res;
358}
359
JP Abgrall26e0d492011-06-24 19:21:51 -0700360int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700361 char cmd[MAX_CMD_LEN];
362 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700363 int ruleInsertPos = 1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700364 std::string costString;
365 const char *costCString;
366
JP Abgrall0dad7c22011-06-24 11:58:14 -0700367 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700368 switch (quotaType) {
369 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700370 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700371 costString += ifn;
372 costCString = costString.c_str();
373 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700374 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700375 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700376 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700377 snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700378 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700379 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
380 * chain. For now, hack the chain exit with an ACCEPT.
381 */
382 snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700383 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
384 break;
385 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700386 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700387 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700388 }
389
JP Abgrall8a932722011-07-13 19:17:35 -0700390 if (globalAlertBytes) {
391 /* The alert rule comes 1st */
392 ruleInsertPos = 2;
393 }
394 snprintf(cmd, sizeof(cmd), "-I INPUT %d -i %s --goto %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700395 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700396 snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700397 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700398 return res;
399}
400
JP Abgrall26e0d492011-06-24 19:21:51 -0700401int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700402 char cmd[MAX_CMD_LEN];
403 int res = 0;
404 std::string costString;
405 const char *costCString;
406
JP Abgrall26e0d492011-06-24 19:21:51 -0700407 switch (quotaType) {
408 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700409 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700410 costString += ifn;
411 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700412 break;
413 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700414 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700415 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700416 }
417
418 snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700419 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700420 snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700421 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700422
JP Abgrallbfa74662011-06-29 19:23:04 -0700423 /* The "-N costly_shared" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700424 if (quotaType == QuotaUnique) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700425 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700426 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgralla9f802c2011-06-29 15:46:45 -0700427 snprintf(cmd, sizeof(cmd), "-X %s", costCString);
428 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700429 }
430 return res;
431}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700432
JP Abgrall0dad7c22011-06-24 11:58:14 -0700433int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700434 char cmd[MAX_CMD_LEN];
435 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700436 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700437 std::string quotaCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700438 std::string ifaceName;
439 ;
JP Abgrallbfa74662011-06-29 19:23:04 -0700440 const char *costName = "shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700441 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700442
JP Abgrall8a932722011-07-13 19:17:35 -0700443 if (!maxBytes) {
444 /* Don't talk about -1, deprecate it. */
445 LOGE("Invalid bytes value. 1..max_int64.");
446 return -1;
447 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700448 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
449 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
450 return -1;
451 }
452 ifaceName = ifn;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700453
454 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700455 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700456 }
457
458 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700459 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
460 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700461 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700462 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700463
JP Abgrall0dad7c22011-06-24 11:58:14 -0700464 if (it == sharedQuotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700465 res |= prepCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700466 if (sharedQuotaIfaces.empty()) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700467 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700468 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700469 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700470 LOGE("Failed set quota rule");
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700471 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700472 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700473 sharedQuotaBytes = maxBytes;
474 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700475 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700476
477 }
478
479 if (maxBytes != sharedQuotaBytes) {
JP Abgrall8a932722011-07-13 19:17:35 -0700480 res |= updateQuota(costName, maxBytes);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700481 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700482 LOGE("Failed update quota for %s", costName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700483 goto fail;
484 }
485 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700486 }
487 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700488
489 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700490 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700491 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
492 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700493 * For now callers needs to choose if they want to "ndc bandwidth enable"
494 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700495 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700496 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700497 return -1;
498}
499
JP Abgrall8a932722011-07-13 19:17:35 -0700500/* It will also cleanup any shared alerts */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700501int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700502 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700503 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700504 std::string ifaceName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700505 std::list<std::string>::iterator it;
JP Abgrallbfa74662011-06-29 19:23:04 -0700506 const char *costName = "shared";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700507
JP Abgrall8a932722011-07-13 19:17:35 -0700508 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700509 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
510 return -1;
511 }
JP Abgrall8a932722011-07-13 19:17:35 -0700512 ifaceName = ifn;
JP Abgrall26e0d492011-06-24 19:21:51 -0700513
JP Abgrall0dad7c22011-06-24 11:58:14 -0700514 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
515 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700516 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700517 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700518 if (it == sharedQuotaIfaces.end()) {
JP Abgrall8a932722011-07-13 19:17:35 -0700519 LOGE("No such iface %s to delete", ifn);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700520 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700521 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700522
JP Abgrall26e0d492011-06-24 19:21:51 -0700523 res |= cleanupCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700524 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700525
JP Abgrall0dad7c22011-06-24 11:58:14 -0700526 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700527 std::string quotaCmd;
JP Abgrallbfa74662011-06-29 19:23:04 -0700528 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700529 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700530 sharedQuotaBytes = 0;
531 if (sharedAlertBytes) {
532 removeSharedAlert();
533 sharedAlertBytes = 0;
534 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700535 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700536 return res;
537}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700538
539int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
540 char ifn[MAX_IFACENAME_LEN];
541 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700542 std::string ifaceName;
543 const char *costName;
544 std::list<QuotaInfo>::iterator it;
545 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700546
JP Abgrall8a932722011-07-13 19:17:35 -0700547 if (!maxBytes) {
548 /* Don't talk about -1, deprecate it. */
549 LOGE("Invalid bytes value. 1..max_int64.");
550 return -1;
551 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700552 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700553 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700554 }
555
JP Abgrall8a932722011-07-13 19:17:35 -0700556 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700557 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
558 return -1;
559 }
560 ifaceName = ifn;
561 costName = iface;
562
JP Abgrall0dad7c22011-06-24 11:58:14 -0700563 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700564 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700565 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700566 break;
567 }
568
569 if (it == quotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700570 res |= prepCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700571 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700572 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700573 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700574 LOGE("Failed set quota rule");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700575 goto fail;
576 }
577
JP Abgrall8a932722011-07-13 19:17:35 -0700578 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
JP Abgrall0dad7c22011-06-24 11:58:14 -0700579
580 } else {
JP Abgrall8a932722011-07-13 19:17:35 -0700581 res |= updateQuota(costName, maxBytes);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700582 if (res) {
JP Abgrall8a932722011-07-13 19:17:35 -0700583 LOGE("Failed update quota for %s", iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700584 goto fail;
585 }
JP Abgrall8a932722011-07-13 19:17:35 -0700586 it->quota = maxBytes;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700587 }
588 return 0;
589
590 fail:
591 /*
592 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
593 * rules in the kernel to see which ones need cleaning up.
594 * For now callers needs to choose if they want to "ndc bandwidth enable"
595 * which resets everything.
596 */
597 removeInterfaceSharedQuota(ifn);
598 return -1;
599}
600
JP Abgrall8a932722011-07-13 19:17:35 -0700601int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
602 return getInterfaceQuota("shared", bytes);
603}
604
605int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
606 FILE *fp;
607 char *fname;
608 int scanRes;
609
610 asprintf(&fname, "/proc/net/xt_quota/%s", costName);
611 fp = fopen(fname, "r");
612 free(fname);
613 if (!fp) {
614 LOGE("Reading quota %s failed (%s)", costName, strerror(errno));
615 return -1;
616 }
617 scanRes = fscanf(fp, "%lld", bytes);
Steve Block3fb42e02011-10-20 11:55:56 +0100618 ALOGV("Read quota res=%d bytes=%lld", scanRes, *bytes);
JP Abgrall8a932722011-07-13 19:17:35 -0700619 fclose(fp);
620 return scanRes == 1 ? 0 : -1;
621}
622
JP Abgrall0dad7c22011-06-24 11:58:14 -0700623int BandwidthController::removeInterfaceQuota(const char *iface) {
624
625 char ifn[MAX_IFACENAME_LEN];
626 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700627 std::string ifaceName;
628 const char *costName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700629 std::list<QuotaInfo>::iterator it;
JP Abgrall26e0d492011-06-24 19:21:51 -0700630
JP Abgrall8a932722011-07-13 19:17:35 -0700631 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700632 LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
633 return -1;
634 }
635 ifaceName = ifn;
636 costName = iface;
637
JP Abgrall0dad7c22011-06-24 11:58:14 -0700638 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700639 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700640 break;
641 }
642
643 if (it == quotaIfaces.end()) {
JP Abgrall8a932722011-07-13 19:17:35 -0700644 LOGE("No such iface %s to delete", ifn);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700645 return -1;
646 }
647
648 /* This also removes the quota command of CostlyIface chain. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700649 res |= cleanupCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700650
651 quotaIfaces.erase(it);
652
653 return res;
654}
JP Abgrall8a932722011-07-13 19:17:35 -0700655
656int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
657 FILE *fp;
658 char *fname;
659
660 asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
661 fp = fopen(fname, "w");
662 free(fname);
663 if (!fp) {
664 LOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
665 return -1;
666 }
667 fprintf(fp, "%lld\n", bytes);
668 fclose(fp);
669 return 0;
670}
671
672int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
673 int res = 0;
674 const char *opFlag;
JP Abgrall87666692011-09-08 13:44:10 -0700675 const char *ifaceLimiting;
JP Abgrall8a932722011-07-13 19:17:35 -0700676 char *alertQuotaCmd;
677
678 switch (op) {
679 case IptOpInsert:
680 opFlag = "-I";
681 break;
682 case IptOpReplace:
683 opFlag = "-R";
684 break;
685 default:
686 case IptOpDelete:
687 opFlag = "-D";
688 break;
689 }
690
JP Abgrall87666692011-09-08 13:44:10 -0700691 ifaceLimiting = "! -i lo+";
692 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "INPUT",
693 bytes, alertName, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700694 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
695 free(alertQuotaCmd);
JP Abgrall87666692011-09-08 13:44:10 -0700696 ifaceLimiting = "! -o lo+";
697 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "OUTPUT",
698 bytes, alertName, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700699 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
700 free(alertQuotaCmd);
701 return res;
702}
703
JP Abgrallc6c67342011-10-07 16:28:54 -0700704int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const char *alertName, int64_t bytes) {
705 int res = 0;
706 const char *opFlag;
707 const char *ifaceLimiting;
JP Abgrall8a932722011-07-13 19:17:35 -0700708 char *alertQuotaCmd;
JP Abgrallc6c67342011-10-07 16:28:54 -0700709
710 switch (op) {
711 case IptOpInsert:
712 opFlag = "-I";
713 break;
714 case IptOpReplace:
715 opFlag = "-R";
716 break;
717 default:
718 case IptOpDelete:
719 opFlag = "-D";
720 break;
721 }
722
723 ifaceLimiting = "! -i lo+";
724 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "FORWARD",
725 bytes, alertName, alertName);
726 res = runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
727 free(alertQuotaCmd);
728 return res;
729}
730
731int BandwidthController::setGlobalAlert(int64_t bytes) {
732 const char *alertName = ALERT_GLOBAL_NAME;
JP Abgrall8a932722011-07-13 19:17:35 -0700733 int res = 0;
734
735 if (!bytes) {
736 LOGE("Invalid bytes value. 1..max_int64.");
737 return -1;
738 }
739 if (globalAlertBytes) {
740 res = updateQuota(alertName, bytes);
741 } else {
742 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700743 if (globalAlertTetherCount) {
Steve Block3fb42e02011-10-20 11:55:56 +0100744 ALOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
JP Abgrallc6c67342011-10-07 16:28:54 -0700745 res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
746 }
JP Abgrall8a932722011-07-13 19:17:35 -0700747 }
748 globalAlertBytes = bytes;
749 return res;
750}
751
JP Abgrallc6c67342011-10-07 16:28:54 -0700752int BandwidthController::setGlobalAlertInForwardChain(void) {
753 const char *alertName = ALERT_GLOBAL_NAME;
754 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700755
JP Abgrallc6c67342011-10-07 16:28:54 -0700756 globalAlertTetherCount++;
Steve Block3fb42e02011-10-20 11:55:56 +0100757 ALOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
JP Abgrallc6c67342011-10-07 16:28:54 -0700758
759 /*
760 * If there is no globalAlert active we are done.
761 * If there is an active globalAlert but this is not the 1st
762 * tether, we are also done.
763 */
764 if (!globalAlertBytes || globalAlertTetherCount != 1) {
765 return 0;
766 }
767
768 /* We only add the rule if this was the 1st tether added. */
769 res = runIptablesAlertFwdCmd(IptOpInsert, alertName, globalAlertBytes);
770 return res;
771}
772
773int BandwidthController::removeGlobalAlert(void) {
774
775 const char *alertName = ALERT_GLOBAL_NAME;
JP Abgrall8a932722011-07-13 19:17:35 -0700776 int res = 0;
777
778 if (!globalAlertBytes) {
779 LOGE("No prior alert set");
780 return -1;
781 }
782 res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700783 if (globalAlertTetherCount) {
784 res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
785 }
JP Abgrall8a932722011-07-13 19:17:35 -0700786 globalAlertBytes = 0;
787 return res;
788}
789
JP Abgrallc6c67342011-10-07 16:28:54 -0700790int BandwidthController::removeGlobalAlertInForwardChain(void) {
791 int res = 0;
792 const char *alertName = ALERT_GLOBAL_NAME;
793
794 if (!globalAlertTetherCount) {
795 LOGE("No prior alert set");
796 return -1;
797 }
798
799 globalAlertTetherCount--;
800 /*
801 * If there is no globalAlert active we are done.
802 * If there is an active globalAlert but there are more
803 * tethers, we are also done.
804 */
805 if (!globalAlertBytes || globalAlertTetherCount >= 1) {
806 return 0;
807 }
808
809 /* We only detete the rule if this was the last tether removed. */
810 res = runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
811 return res;
812}
813
JP Abgrall8a932722011-07-13 19:17:35 -0700814int BandwidthController::setSharedAlert(int64_t bytes) {
815 if (!sharedQuotaBytes) {
816 LOGE("Need to have a prior shared quota set to set an alert");
817 return -1;
818 }
819 if (!bytes) {
820 LOGE("Invalid bytes value. 1..max_int64.");
821 return -1;
822 }
823 return setCostlyAlert("shared", bytes, &sharedAlertBytes);
824}
825
826int BandwidthController::removeSharedAlert(void) {
827 return removeCostlyAlert("shared", &sharedAlertBytes);
828}
829
830int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
831 std::list<QuotaInfo>::iterator it;
832
833 if (!bytes) {
834 LOGE("Invalid bytes value. 1..max_int64.");
835 return -1;
836 }
837 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
838 if (it->ifaceName == iface)
839 break;
840 }
841
842 if (it == quotaIfaces.end()) {
843 LOGE("Need to have a prior interface quota set to set an alert");
844 return -1;
845 }
846
847 return setCostlyAlert(iface, bytes, &it->alert);
848}
849
850int BandwidthController::removeInterfaceAlert(const char *iface) {
851 std::list<QuotaInfo>::iterator it;
852
853 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
854 if (it->ifaceName == iface)
855 break;
856 }
857
858 if (it == quotaIfaces.end()) {
859 LOGE("No prior alert set for interface %s", iface);
860 return -1;
861 }
862
863 return removeCostlyAlert(iface, &it->alert);
864}
865
866int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
867 char *alertQuotaCmd;
868 char *chainNameAndPos;
869 int res = 0;
870 char *alertName;
871
872 if (!bytes) {
873 LOGE("Invalid bytes value. 1..max_int64.");
874 return -1;
875 }
876 asprintf(&alertName, "%sAlert", costName);
877 if (*alertBytes) {
878 res = updateQuota(alertName, *alertBytes);
879 } else {
880 asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN);
881 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-I", chainNameAndPos, bytes, alertName,
882 alertName);
883 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
884 free(alertQuotaCmd);
885 free(chainNameAndPos);
886 }
887 *alertBytes = bytes;
888 free(alertName);
889 return res;
890}
891
892int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
893 char *alertQuotaCmd;
894 char *chainName;
895 char *alertName;
896 int res = 0;
897
898 asprintf(&alertName, "%sAlert", costName);
899 if (!*alertBytes) {
900 LOGE("No prior alert set for %s alert", costName);
901 return -1;
902 }
903
904 asprintf(&chainName, "costly_%s", costName);
905 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName, alertName);
906 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
907 free(alertQuotaCmd);
908 free(chainName);
909
910 *alertBytes = 0;
911 free(alertName);
912 return res;
913}
JP Abgralldb7da582011-09-18 12:57:32 -0700914
915/*
916 * Parse the ptks and bytes out of:
917 * Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
918 * pkts bytes target prot opt in out source destination
919 * 0 0 ACCEPT all -- rmnet0 wlan0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
920 * 0 0 DROP all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 state INVALID
921 * 0 0 ACCEPT all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0
922 *
923 */
JP Abgralla2a64f02011-11-11 20:36:16 -0800924int BandwidthController::parseForwardChainStats(TetherStats &stats, FILE *fp,
925 std::string &extraProcessingInfo) {
JP Abgralldb7da582011-09-18 12:57:32 -0700926 int res;
927 char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
928 char iface0[MAX_IPT_OUTPUT_LINE_LEN];
929 char iface1[MAX_IPT_OUTPUT_LINE_LEN];
930 char rest[MAX_IPT_OUTPUT_LINE_LEN];
931
932 char *buffPtr;
933 int64_t packets, bytes;
934
935 while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
936 /* Clean up, so a failed parse can still print info */
937 iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
938 res = sscanf(buffPtr, "%lld %lld ACCEPT all -- %s %s 0.%s",
939 &packets, &bytes, iface0, iface1, rest);
Steve Block3fb42e02011-10-20 11:55:56 +0100940 ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%lld bytes=%lld rest=<%s> orig line=<%s>", res,
JP Abgralldb7da582011-09-18 12:57:32 -0700941 iface0, iface1, packets, bytes, rest, buffPtr);
JP Abgralla2a64f02011-11-11 20:36:16 -0800942 extraProcessingInfo += buffPtr;
943
JP Abgralldb7da582011-09-18 12:57:32 -0700944 if (res != 5) {
945 continue;
946 }
947 if ((stats.ifaceIn == iface0) && (stats.ifaceOut == iface1)) {
Steve Block3fb42e02011-10-20 11:55:56 +0100948 ALOGV("iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
JP Abgralldb7da582011-09-18 12:57:32 -0700949 stats.rxPackets = packets;
950 stats.rxBytes = bytes;
951 } else if ((stats.ifaceOut == iface0) && (stats.ifaceIn == iface1)) {
Steve Block3fb42e02011-10-20 11:55:56 +0100952 ALOGV("iface_in=%s iface_out=%s tx_bytes=%lld tx_packets=%lld ", iface1, iface0, bytes, packets);
JP Abgralldb7da582011-09-18 12:57:32 -0700953 stats.txPackets = packets;
954 stats.txBytes = bytes;
955 }
956 }
957 /* Failure if rx or tx was not found */
958 return (stats.rxBytes == -1 || stats.txBytes == -1) ? -1 : 0;
959}
960
961
962char *BandwidthController::TetherStats::getStatsLine(void) {
963 char *msg;
964 asprintf(&msg, "%s %s %lld %lld %lld %lld", ifaceIn.c_str(), ifaceOut.c_str(),
965 rxBytes, rxPackets, txBytes, txPackets);
966 return msg;
967}
968
JP Abgralla2a64f02011-11-11 20:36:16 -0800969int BandwidthController::getTetherStats(TetherStats &stats, std::string &extraProcessingInfo) {
JP Abgralldb7da582011-09-18 12:57:32 -0700970 int res;
971 std::string fullCmd;
972 FILE *iptOutput;
973 const char *cmd;
974
975 if (stats.rxBytes != -1 || stats.txBytes != -1) {
976 LOGE("Unexpected input stats. Byte counts should be -1.");
977 return -1;
978 }
979
980 /*
981 * Why not use some kind of lib to talk to iptables?
982 * Because the only libs are libiptc and libip6tc in iptables, and they are
983 * not easy to use. They require the known iptables match modules to be
984 * preloaded/linked, and require apparently a lot of wrapper code to get
985 * the wanted info.
986 */
987 fullCmd = IPTABLES_PATH;
988 fullCmd += " -nvx -L FORWARD";
989 iptOutput = popen(fullCmd.c_str(), "r");
990 if (!iptOutput) {
991 LOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
JP Abgralla2a64f02011-11-11 20:36:16 -0800992 extraProcessingInfo += "Failed to run iptables.";
JP Abgralldb7da582011-09-18 12:57:32 -0700993 return -1;
994 }
JP Abgralla2a64f02011-11-11 20:36:16 -0800995 res = parseForwardChainStats(stats, iptOutput, extraProcessingInfo);
JP Abgralldb7da582011-09-18 12:57:32 -0700996 pclose(iptOutput);
997
998 /* Currently NatController doesn't do ipv6 tethering, so we are done. */
999 return res;
1000}