blob: 5233f3efa9439ba22c05e8c98c046b713b900a7b [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
Glenn Kastenc4bbfa22012-03-05 15:13:58 -080044extern "C" int logwrap(int argc, const char **argv);
JP Abgrall9e5e0ce2011-12-14 15:20:59 -080045extern "C" int system_nosh(const char *command);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070046
JP Abgrall0031cea2012-04-17 16:38:23 -070047#include "NetdConstants.h"
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070048#include "BandwidthController.h"
49
JP Abgralldb7da582011-09-18 12:57:32 -070050/* Alphabetical */
Nick Kralevichc2b26cb2012-02-23 13:04:26 -080051#define ALERT_IPT_TEMPLATE "%s %s %s -m quota2 ! --quota %lld --name %s"
JP Abgralldb7da582011-09-18 12:57:32 -070052const int BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4;
JP Abgrallc6c67342011-10-07 16:28:54 -070053const char BandwidthController::ALERT_GLOBAL_NAME[] = "globalAlert";
JP Abgralldb7da582011-09-18 12:57:32 -070054const int BandwidthController::MAX_CMD_ARGS = 32;
55const int BandwidthController::MAX_CMD_LEN = 1024;
56const int BandwidthController::MAX_IFACENAME_LEN = 64;
57const int BandwidthController::MAX_IPT_OUTPUT_LINE_LEN = 256;
58
JP Abgrall11b4e9b2011-08-11 15:34:49 -070059bool BandwidthController::useLogwrapCall = false;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070060
61/**
62 * Some comments about the rules:
63 * * Ordering
64 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
JP Abgrall0031cea2012-04-17 16:38:23 -070065 * E.g. "-I INPUT -i rmnet0 --jump costly"
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070066 * - quota'd rules in the costly chain should be before penalty_box lookups.
67 *
68 * * global quota vs per interface quota
69 * - global quota for all costly interfaces uses a single costly chain:
70 * . initial rules
JP Abgrallbfa74662011-06-29 19:23:04 -070071 * iptables -N costly_shared
JP Abgrall0031cea2012-04-17 16:38:23 -070072 * iptables -I INPUT -i iface0 --jump costly_shared
73 * iptables -I OUTPUT -o iface0 --jump costly_shared
JP Abgrallbfa74662011-06-29 19:23:04 -070074 * iptables -I costly_shared -m quota \! --quota 500000 \
75 * --jump REJECT --reject-with icmp-net-prohibited
76 * iptables -A costly_shared --jump penalty_box
77 * iptables -A costly_shared -m owner --socket-exists
JP Abgrall8a932722011-07-13 19:17:35 -070078 *
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070079 * . adding a new iface to this, E.g.:
JP Abgrall0031cea2012-04-17 16:38:23 -070080 * iptables -I INPUT -i iface1 --jump costly_shared
81 * iptables -I OUTPUT -o iface1 --jump costly_shared
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070082 *
83 * - quota per interface. This is achieve by having "costly" chains per quota.
84 * E.g. adding a new costly interface iface0 with its own quota:
85 * iptables -N costly_iface0
JP Abgrall0031cea2012-04-17 16:38:23 -070086 * iptables -I INPUT -i iface0 --jump costly_iface0
87 * iptables -I OUTPUT -o iface0 --jump costly_iface0
JP Abgrallbfa74662011-06-29 19:23:04 -070088 * iptables -A costly_iface0 -m quota \! --quota 500000 \
89 * --jump REJECT --reject-with icmp-net-prohibited
90 * iptables -A costly_iface0 --jump penalty_box
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070091 * iptables -A costly_iface0 -m owner --socket-exists
92 *
93 * * penalty_box handling:
94 * - only one penalty_box for all interfaces
95 * E.g Adding an app:
JP Abgrallbfa74662011-06-29 19:23:04 -070096 * iptables -A penalty_box -m owner --uid-owner app_3 \
97 * --jump REJECT --reject-with icmp-net-prohibited
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070098 */
JP Abgrall0031cea2012-04-17 16:38:23 -070099const char *BandwidthController::IPT_FLUSH_COMMANDS[] = {
100 /*
101 * Cleanup rules.
102 * Should normally include costly_<iface>, but we rely on the way they are setup
103 * to allow coexistance.
JP Abgrall39f8f242011-06-29 19:21:58 -0700104 */
JP Abgrall0031cea2012-04-17 16:38:23 -0700105 "-F bw_INPUT",
106 "-F bw_OUTPUT",
107 "-F bw_FORWARD",
108 "-F penalty_box",
109 "-F costly_shared",
110};
111
112/* The cleanup commands assume flushing has been done. */
113const char *BandwidthController::IPT_CLEANUP_COMMANDS[] = {
114 /* Delete hooks to custom chains. */
115 "-D INPUT -j bw_INPUT",
116 "-D OUTPUT -j bw_OUTPUT",
117 "-D FORWARD -j bw_FORWARD",
118 "-X bw_INPUT",
119 "-X bw_OUTPUT",
120 "-X bw_FORWARD",
121 "-X penalty_box",
122 "-X costly_shared",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700123};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700124
JP Abgralldb7da582011-09-18 12:57:32 -0700125const char *BandwidthController::IPT_SETUP_COMMANDS[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700126 /* Created needed chains. */
JP Abgrall0031cea2012-04-17 16:38:23 -0700127 "-N bw_INPUT",
128 "-A INPUT -j bw_INPUT",
129
130 "-N bw_OUTPUT",
131 "-A OUTPUT -j bw_OUTPUT",
132
133 "-N bw_FORWARD",
134 "-I FORWARD -j bw_FORWARD",
135
JP Abgrallbfa74662011-06-29 19:23:04 -0700136 "-N costly_shared",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700137 "-N penalty_box",
138};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700139
JP Abgralldb7da582011-09-18 12:57:32 -0700140const char *BandwidthController::IPT_BASIC_ACCOUNTING_COMMANDS[] = {
JP Abgrall0031cea2012-04-17 16:38:23 -0700141 "-A bw_INPUT -i lo --jump RETURN",
142 "-A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700143
JP Abgrall0031cea2012-04-17 16:38:23 -0700144 "-A bw_OUTPUT -o lo --jump RETURN",
145 "-A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700146
JP Abgrallbfa74662011-06-29 19:23:04 -0700147 "-A costly_shared --jump penalty_box",
148 "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700149};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700150
151BandwidthController::BandwidthController(void) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700152 char value[PROPERTY_VALUE_MAX];
153
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700154 property_get("persist.bandwidth.uselogwrap", value, "0");
155 useLogwrapCall = !strcmp(value, "1");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700156}
157
JP Abgrall26e0d492011-06-24 19:21:51 -0700158int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700159 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700160
Steve Block3fb42e02011-10-20 11:55:56 +0100161 ALOGV("runIpxtablesCmd(cmd=%s)", cmd);
JP Abgrall26e0d492011-06-24 19:21:51 -0700162 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
163 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700164 return res;
165}
166
JP Abgrall26e0d492011-06-24 19:21:51 -0700167int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
168
169 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0'
170 strncpy(buffer, src, buffSize);
171 return buffer[buffSize - 1];
172}
173
JP Abgrall8a932722011-07-13 19:17:35 -0700174int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling,
175 IptIpVer iptVer) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700176 char buffer[MAX_CMD_LEN];
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700177 const char *argv[MAX_CMD_ARGS];
JP Abgrall26e0d492011-06-24 19:21:51 -0700178 int argc = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700179 char *next = buffer;
180 char *tmp;
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700181 int res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700182
JP Abgrall0dad7c22011-06-24 11:58:14 -0700183 std::string fullCmd = cmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700184
185 if (rejectHandling == IptRejectAdd) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700186 fullCmd += " --jump REJECT --reject-with";
JP Abgrall26e0d492011-06-24 19:21:51 -0700187 switch (iptVer) {
188 case IptIpV4:
JP Abgrall8a932722011-07-13 19:17:35 -0700189 fullCmd += " icmp-net-prohibited";
190 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700191 case IptIpV6:
JP Abgrall8a932722011-07-13 19:17:35 -0700192 fullCmd += " icmp6-adm-prohibited";
193 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700194 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700195 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700196
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700197 fullCmd.insert(0, " ");
198 fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700199
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700200 if (!useLogwrapCall) {
JP Abgrall9e5e0ce2011-12-14 15:20:59 -0800201 res = system_nosh(fullCmd.c_str());
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700202 } else {
203 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000204 ALOGE("iptables command too long");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700205 return -1;
206 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700207
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700208 argc = 0;
209 while ((tmp = strsep(&next, " "))) {
210 argv[argc++] = tmp;
211 if (argc >= MAX_CMD_ARGS) {
Steve Block5ea0c052012-01-06 19:18:11 +0000212 ALOGE("iptables argument overflow");
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700213 return -1;
214 }
215 }
216
217 argv[argc] = NULL;
Glenn Kastenc4bbfa22012-03-05 15:13:58 -0800218 res = logwrap(argc, argv);
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700219 }
220 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000221 ALOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res);
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700222 }
223 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700224}
225
JP Abgrall0031cea2012-04-17 16:38:23 -0700226int BandwidthController::setupIptablesHooks(void) {
227
228 /* Some of the initialCommands are allowed to fail */
229 runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
230 IPT_FLUSH_COMMANDS, RunCmdFailureOk);
231
232 runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
233 IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
234
235 runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*),
236 IPT_SETUP_COMMANDS, RunCmdFailureBad);
237
238 return 0;
239
240}
241
242int BandwidthController::enableBandwidthControl(bool force) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700243 int res;
JP Abgrall0031cea2012-04-17 16:38:23 -0700244 char value[PROPERTY_VALUE_MAX];
245
246 if (!force) {
247 property_get("persist.bandwidth.enable", value, "1");
248 if (!strcmp(value, "0"))
249 return 0;
250 }
JP Abgrall8a932722011-07-13 19:17:35 -0700251
JP Abgralldb7da582011-09-18 12:57:32 -0700252 /* Let's pretend we started from scratch ... */
JP Abgrall8a932722011-07-13 19:17:35 -0700253 sharedQuotaIfaces.clear();
254 quotaIfaces.clear();
255 naughtyAppUids.clear();
JP Abgralldb7da582011-09-18 12:57:32 -0700256 globalAlertBytes = 0;
JP Abgrallc6c67342011-10-07 16:28:54 -0700257 globalAlertTetherCount = 0;
JP Abgralldb7da582011-09-18 12:57:32 -0700258 sharedQuotaBytes = sharedAlertBytes = 0;
259
JP Abgrall0031cea2012-04-17 16:38:23 -0700260 res = runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
261 IPT_FLUSH_COMMANDS, RunCmdFailureOk);
JP Abgralldb7da582011-09-18 12:57:32 -0700262
JP Abgrall0031cea2012-04-17 16:38:23 -0700263 res |= runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
JP Abgralldb7da582011-09-18 12:57:32 -0700264 IPT_BASIC_ACCOUNTING_COMMANDS, RunCmdFailureBad);
JP Abgrall8a932722011-07-13 19:17:35 -0700265
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700266 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700267
268}
269
270int BandwidthController::disableBandwidthControl(void) {
JP Abgrall0031cea2012-04-17 16:38:23 -0700271 runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
272 IPT_FLUSH_COMMANDS, RunCmdFailureOk);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700273 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700274}
275
JP Abgrall8a932722011-07-13 19:17:35 -0700276int BandwidthController::runCommands(int numCommands, const char *commands[],
277 RunCmdErrHandling cmdErrHandling) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700278 int res = 0;
Steve Block3fb42e02011-10-20 11:55:56 +0100279 ALOGV("runCommands(): %d commands", numCommands);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700280 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700281 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
JP Abgrall0031cea2012-04-17 16:38:23 -0700282 if (res && cmdErrHandling != RunCmdFailureOk)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700283 return res;
284 }
JP Abgrall0031cea2012-04-17 16:38:23 -0700285 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700286}
287
JP Abgrall0dad7c22011-06-24 11:58:14 -0700288std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700289 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700290 char *buff;
291 const char *opFlag;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700292
293 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700294 case IptOpInsert:
295 opFlag = "-I";
296 break;
297 case IptOpReplace:
298 opFlag = "-R";
299 break;
300 default:
301 case IptOpDelete:
302 opFlag = "-D";
303 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700304 }
JP Abgrall8a932722011-07-13 19:17:35 -0700305 asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid);
306 res = buff;
307 free(buff);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700308 return res;
309}
310
311int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700312 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700313}
314
315int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700316 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700317}
318
JP Abgrall26e0d492011-06-24 19:21:51 -0700319int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700320 char cmd[MAX_CMD_LEN];
321 int uidNum;
JP Abgrall26e0d492011-06-24 19:21:51 -0700322 const char *failLogTemplate;
323 IptOp op;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700324 int appUids[numUids];
JP Abgrall26e0d492011-06-24 19:21:51 -0700325 std::string naughtyCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700326
JP Abgrall26e0d492011-06-24 19:21:51 -0700327 switch (appOp) {
328 case NaughtyAppOpAdd:
JP Abgrall8a932722011-07-13 19:17:35 -0700329 op = IptOpInsert;
330 failLogTemplate = "Failed to add app uid %d to penalty box.";
331 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700332 case NaughtyAppOpRemove:
JP Abgrall8a932722011-07-13 19:17:35 -0700333 op = IptOpDelete;
334 failLogTemplate = "Failed to delete app uid %d from penalty box.";
335 break;
JP Abgrall0031cea2012-04-17 16:38:23 -0700336 default:
337 ALOGE("Unexpected app Op %d", appOp);
338 return -1;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700339 }
340
341 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700342 appUids[uidNum] = atol(appStrUids[uidNum]);
343 if (appUids[uidNum] == 0) {
Steve Block5ea0c052012-01-06 19:18:11 +0000344 ALOGE(failLogTemplate, appUids[uidNum]);
JP Abgrall26e0d492011-06-24 19:21:51 -0700345 goto fail_parse;
346 }
347 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700348
349 for (uidNum = 0; uidNum < numUids; uidNum++) {
350 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
351 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
Steve Block5ea0c052012-01-06 19:18:11 +0000352 ALOGE(failLogTemplate, appUids[uidNum]);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700353 goto fail_with_uidNum;
354 }
355 }
356 return 0;
357
JP Abgrall26e0d492011-06-24 19:21:51 -0700358fail_with_uidNum:
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700359 /* Try to remove the uid that failed in any case*/
JP Abgrall26e0d492011-06-24 19:21:51 -0700360 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
361 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
362fail_parse:
363 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700364}
365
JP Abgrall26e0d492011-06-24 19:21:51 -0700366std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700367 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700368 char *buff;
369 const char *opFlag;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700370
Steve Block3fb42e02011-10-20 11:55:56 +0100371 ALOGV("makeIptablesQuotaCmd(%d, %lld)", op, quota);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700372
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700373 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700374 case IptOpInsert:
375 opFlag = "-I";
376 break;
377 case IptOpReplace:
378 opFlag = "-R";
379 break;
380 default:
381 case IptOpDelete:
382 opFlag = "-D";
383 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700384 }
JP Abgrall8a932722011-07-13 19:17:35 -0700385
JP Abgrallbfa74662011-06-29 19:23:04 -0700386 // The requried IP version specific --jump REJECT ... will be added later.
JP Abgrall8a932722011-07-13 19:17:35 -0700387 asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota,
388 costName);
389 res = buff;
390 free(buff);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700391 return res;
392}
393
JP Abgrall26e0d492011-06-24 19:21:51 -0700394int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700395 char cmd[MAX_CMD_LEN];
JP Abgrall0031cea2012-04-17 16:38:23 -0700396 int res = 0, res1, res2;
JP Abgrall8a932722011-07-13 19:17:35 -0700397 int ruleInsertPos = 1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700398 std::string costString;
399 const char *costCString;
400
JP Abgrall0dad7c22011-06-24 11:58:14 -0700401 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700402 switch (quotaType) {
403 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700404 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700405 costString += ifn;
406 costCString = costString.c_str();
JP Abgrall0031cea2012-04-17 16:38:23 -0700407 /*
408 * Flush the costly_<iface> is allowed to fail in case it didn't exist.
409 * Creating a new one is allowed to fail in case it existed.
410 * This helps with netd restarts.
411 */
412 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
413 res1 = runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700414 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
JP Abgrall0031cea2012-04-17 16:38:23 -0700415 res2 = runIpxtablesCmd(cmd, IptRejectNoAdd);
416 res = (res1 && res2) || (!res1 && !res2);
417
JP Abgrall0dad7c22011-06-24 11:58:14 -0700418 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", 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), "-A %s -m owner --socket-exists", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700421 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall26e0d492011-06-24 19:21:51 -0700422 break;
423 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700424 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700425 break;
JP Abgrall0031cea2012-04-17 16:38:23 -0700426 default:
427 ALOGE("Unexpected quotatype %d", quotaType);
428 return -1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700429 }
430
JP Abgrall8a932722011-07-13 19:17:35 -0700431 if (globalAlertBytes) {
432 /* The alert rule comes 1st */
433 ruleInsertPos = 2;
434 }
JP Abgrall0031cea2012-04-17 16:38:23 -0700435
436 snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
437 runIpxtablesCmd(cmd, IptRejectNoAdd);
438
439 snprintf(cmd, sizeof(cmd), "-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700440 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0031cea2012-04-17 16:38:23 -0700441
442 snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
443 runIpxtablesCmd(cmd, IptRejectNoAdd);
444
445 snprintf(cmd, sizeof(cmd), "-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700446 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700447 return res;
448}
449
JP Abgrall26e0d492011-06-24 19:21:51 -0700450int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700451 char cmd[MAX_CMD_LEN];
452 int res = 0;
453 std::string costString;
454 const char *costCString;
455
JP Abgrall26e0d492011-06-24 19:21:51 -0700456 switch (quotaType) {
457 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700458 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700459 costString += ifn;
460 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700461 break;
462 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700463 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700464 break;
JP Abgrall0031cea2012-04-17 16:38:23 -0700465 default:
466 ALOGE("Unexpected quotatype %d", quotaType);
467 return -1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700468 }
469
JP Abgrall0031cea2012-04-17 16:38:23 -0700470 snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700471 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0031cea2012-04-17 16:38:23 -0700472 snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700473 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700474
JP Abgrallbfa74662011-06-29 19:23:04 -0700475 /* The "-N costly_shared" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700476 if (quotaType == QuotaUnique) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700477 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700478 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgralla9f802c2011-06-29 15:46:45 -0700479 snprintf(cmd, sizeof(cmd), "-X %s", costCString);
480 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700481 }
482 return res;
483}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700484
JP Abgrall0dad7c22011-06-24 11:58:14 -0700485int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700486 char cmd[MAX_CMD_LEN];
487 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700488 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700489 std::string quotaCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700490 std::string ifaceName;
491 ;
JP Abgrallbfa74662011-06-29 19:23:04 -0700492 const char *costName = "shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700493 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700494
JP Abgrall8a932722011-07-13 19:17:35 -0700495 if (!maxBytes) {
496 /* Don't talk about -1, deprecate it. */
Steve Block5ea0c052012-01-06 19:18:11 +0000497 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700498 return -1;
499 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700500 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000501 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700502 return -1;
503 }
504 ifaceName = ifn;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700505
506 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700507 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700508 }
509
510 /* Insert ingress quota. */
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 Abgrallfa6f46d2011-06-17 23:17:28 -0700515
JP Abgrall0dad7c22011-06-24 11:58:14 -0700516 if (it == sharedQuotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700517 res |= prepCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700518 if (sharedQuotaIfaces.empty()) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700519 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700520 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700521 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000522 ALOGE("Failed set quota rule");
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700523 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700524 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700525 sharedQuotaBytes = maxBytes;
526 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700527 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700528
529 }
530
531 if (maxBytes != sharedQuotaBytes) {
JP Abgrall8a932722011-07-13 19:17:35 -0700532 res |= updateQuota(costName, maxBytes);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700533 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000534 ALOGE("Failed update quota for %s", costName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700535 goto fail;
536 }
537 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700538 }
539 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700540
541 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700542 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700543 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
544 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700545 * For now callers needs to choose if they want to "ndc bandwidth enable"
546 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700547 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700548 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700549 return -1;
550}
551
JP Abgrall8a932722011-07-13 19:17:35 -0700552/* It will also cleanup any shared alerts */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700553int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700554 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700555 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700556 std::string ifaceName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700557 std::list<std::string>::iterator it;
JP Abgrallbfa74662011-06-29 19:23:04 -0700558 const char *costName = "shared";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700559
JP Abgrall8a932722011-07-13 19:17:35 -0700560 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000561 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700562 return -1;
563 }
JP Abgrall8a932722011-07-13 19:17:35 -0700564 ifaceName = ifn;
JP Abgrall26e0d492011-06-24 19:21:51 -0700565
JP Abgrall0dad7c22011-06-24 11:58:14 -0700566 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
567 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700568 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700569 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700570 if (it == sharedQuotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000571 ALOGE("No such iface %s to delete", ifn);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700572 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700573 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700574
JP Abgrall26e0d492011-06-24 19:21:51 -0700575 res |= cleanupCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700576 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700577
JP Abgrall0dad7c22011-06-24 11:58:14 -0700578 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700579 std::string quotaCmd;
JP Abgrallbfa74662011-06-29 19:23:04 -0700580 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700581 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700582 sharedQuotaBytes = 0;
583 if (sharedAlertBytes) {
584 removeSharedAlert();
585 sharedAlertBytes = 0;
586 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700587 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700588 return res;
589}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700590
591int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
592 char ifn[MAX_IFACENAME_LEN];
593 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700594 std::string ifaceName;
595 const char *costName;
596 std::list<QuotaInfo>::iterator it;
597 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700598
JP Abgrall8a932722011-07-13 19:17:35 -0700599 if (!maxBytes) {
600 /* Don't talk about -1, deprecate it. */
Steve Block5ea0c052012-01-06 19:18:11 +0000601 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700602 return -1;
603 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700604 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700605 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700606 }
607
JP Abgrall8a932722011-07-13 19:17:35 -0700608 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000609 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700610 return -1;
611 }
612 ifaceName = ifn;
613 costName = iface;
614
JP Abgrall0dad7c22011-06-24 11:58:14 -0700615 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700616 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700617 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700618 break;
619 }
620
621 if (it == quotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700622 res |= prepCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700623 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700624 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700625 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000626 ALOGE("Failed set quota rule");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700627 goto fail;
628 }
629
JP Abgrall8a932722011-07-13 19:17:35 -0700630 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
JP Abgrall0dad7c22011-06-24 11:58:14 -0700631
632 } else {
JP Abgrall8a932722011-07-13 19:17:35 -0700633 res |= updateQuota(costName, maxBytes);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700634 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000635 ALOGE("Failed update quota for %s", iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700636 goto fail;
637 }
JP Abgrall8a932722011-07-13 19:17:35 -0700638 it->quota = maxBytes;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700639 }
640 return 0;
641
642 fail:
643 /*
644 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
645 * rules in the kernel to see which ones need cleaning up.
646 * For now callers needs to choose if they want to "ndc bandwidth enable"
647 * which resets everything.
648 */
649 removeInterfaceSharedQuota(ifn);
650 return -1;
651}
652
JP Abgrall8a932722011-07-13 19:17:35 -0700653int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
654 return getInterfaceQuota("shared", bytes);
655}
656
657int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
658 FILE *fp;
659 char *fname;
660 int scanRes;
661
662 asprintf(&fname, "/proc/net/xt_quota/%s", costName);
663 fp = fopen(fname, "r");
664 free(fname);
665 if (!fp) {
Steve Block5ea0c052012-01-06 19:18:11 +0000666 ALOGE("Reading quota %s failed (%s)", costName, strerror(errno));
JP Abgrall8a932722011-07-13 19:17:35 -0700667 return -1;
668 }
669 scanRes = fscanf(fp, "%lld", bytes);
Steve Block3fb42e02011-10-20 11:55:56 +0100670 ALOGV("Read quota res=%d bytes=%lld", scanRes, *bytes);
JP Abgrall8a932722011-07-13 19:17:35 -0700671 fclose(fp);
672 return scanRes == 1 ? 0 : -1;
673}
674
JP Abgrall0dad7c22011-06-24 11:58:14 -0700675int BandwidthController::removeInterfaceQuota(const char *iface) {
676
677 char ifn[MAX_IFACENAME_LEN];
678 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700679 std::string ifaceName;
680 const char *costName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700681 std::list<QuotaInfo>::iterator it;
JP Abgrall26e0d492011-06-24 19:21:51 -0700682
JP Abgrall8a932722011-07-13 19:17:35 -0700683 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000684 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700685 return -1;
686 }
687 ifaceName = ifn;
688 costName = iface;
689
JP Abgrall0dad7c22011-06-24 11:58:14 -0700690 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700691 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700692 break;
693 }
694
695 if (it == quotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000696 ALOGE("No such iface %s to delete", ifn);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700697 return -1;
698 }
699
700 /* This also removes the quota command of CostlyIface chain. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700701 res |= cleanupCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700702
703 quotaIfaces.erase(it);
704
705 return res;
706}
JP Abgrall8a932722011-07-13 19:17:35 -0700707
708int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
709 FILE *fp;
710 char *fname;
711
712 asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
713 fp = fopen(fname, "w");
714 free(fname);
715 if (!fp) {
Steve Block5ea0c052012-01-06 19:18:11 +0000716 ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
JP Abgrall8a932722011-07-13 19:17:35 -0700717 return -1;
718 }
719 fprintf(fp, "%lld\n", bytes);
720 fclose(fp);
721 return 0;
722}
723
724int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
725 int res = 0;
726 const char *opFlag;
JP Abgrall87666692011-09-08 13:44:10 -0700727 const char *ifaceLimiting;
JP Abgrall8a932722011-07-13 19:17:35 -0700728 char *alertQuotaCmd;
729
730 switch (op) {
731 case IptOpInsert:
732 opFlag = "-I";
733 break;
734 case IptOpReplace:
735 opFlag = "-R";
736 break;
737 default:
738 case IptOpDelete:
739 opFlag = "-D";
740 break;
741 }
742
JP Abgrall87666692011-09-08 13:44:10 -0700743 ifaceLimiting = "! -i lo+";
JP Abgrall0031cea2012-04-17 16:38:23 -0700744 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_INPUT",
Nick Kralevichc2b26cb2012-02-23 13:04:26 -0800745 bytes, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700746 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
747 free(alertQuotaCmd);
JP Abgrall87666692011-09-08 13:44:10 -0700748 ifaceLimiting = "! -o lo+";
JP Abgrall0031cea2012-04-17 16:38:23 -0700749 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_OUTPUT",
Nick Kralevichc2b26cb2012-02-23 13:04:26 -0800750 bytes, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700751 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
752 free(alertQuotaCmd);
753 return res;
754}
755
JP Abgrallc6c67342011-10-07 16:28:54 -0700756int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const char *alertName, int64_t bytes) {
757 int res = 0;
758 const char *opFlag;
759 const char *ifaceLimiting;
JP Abgrall8a932722011-07-13 19:17:35 -0700760 char *alertQuotaCmd;
JP Abgrallc6c67342011-10-07 16:28:54 -0700761
762 switch (op) {
763 case IptOpInsert:
764 opFlag = "-I";
765 break;
766 case IptOpReplace:
767 opFlag = "-R";
768 break;
769 default:
770 case IptOpDelete:
771 opFlag = "-D";
772 break;
773 }
774
775 ifaceLimiting = "! -i lo+";
JP Abgrall0031cea2012-04-17 16:38:23 -0700776 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_FORWARD",
Nick Kralevichc2b26cb2012-02-23 13:04:26 -0800777 bytes, alertName);
JP Abgrallc6c67342011-10-07 16:28:54 -0700778 res = runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
779 free(alertQuotaCmd);
780 return res;
781}
782
783int BandwidthController::setGlobalAlert(int64_t bytes) {
784 const char *alertName = ALERT_GLOBAL_NAME;
JP Abgrall8a932722011-07-13 19:17:35 -0700785 int res = 0;
786
787 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000788 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700789 return -1;
790 }
791 if (globalAlertBytes) {
792 res = updateQuota(alertName, bytes);
793 } else {
794 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700795 if (globalAlertTetherCount) {
Steve Block3fb42e02011-10-20 11:55:56 +0100796 ALOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
JP Abgrallc6c67342011-10-07 16:28:54 -0700797 res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
798 }
JP Abgrall8a932722011-07-13 19:17:35 -0700799 }
800 globalAlertBytes = bytes;
801 return res;
802}
803
JP Abgrallc6c67342011-10-07 16:28:54 -0700804int BandwidthController::setGlobalAlertInForwardChain(void) {
805 const char *alertName = ALERT_GLOBAL_NAME;
806 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700807
JP Abgrallc6c67342011-10-07 16:28:54 -0700808 globalAlertTetherCount++;
Steve Block3fb42e02011-10-20 11:55:56 +0100809 ALOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
JP Abgrallc6c67342011-10-07 16:28:54 -0700810
811 /*
812 * If there is no globalAlert active we are done.
813 * If there is an active globalAlert but this is not the 1st
814 * tether, we are also done.
815 */
816 if (!globalAlertBytes || globalAlertTetherCount != 1) {
817 return 0;
818 }
819
820 /* We only add the rule if this was the 1st tether added. */
821 res = runIptablesAlertFwdCmd(IptOpInsert, alertName, globalAlertBytes);
822 return res;
823}
824
825int BandwidthController::removeGlobalAlert(void) {
826
827 const char *alertName = ALERT_GLOBAL_NAME;
JP Abgrall8a932722011-07-13 19:17:35 -0700828 int res = 0;
829
830 if (!globalAlertBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000831 ALOGE("No prior alert set");
JP Abgrall8a932722011-07-13 19:17:35 -0700832 return -1;
833 }
834 res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700835 if (globalAlertTetherCount) {
836 res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
837 }
JP Abgrall8a932722011-07-13 19:17:35 -0700838 globalAlertBytes = 0;
839 return res;
840}
841
JP Abgrallc6c67342011-10-07 16:28:54 -0700842int BandwidthController::removeGlobalAlertInForwardChain(void) {
843 int res = 0;
844 const char *alertName = ALERT_GLOBAL_NAME;
845
846 if (!globalAlertTetherCount) {
Steve Block5ea0c052012-01-06 19:18:11 +0000847 ALOGE("No prior alert set");
JP Abgrallc6c67342011-10-07 16:28:54 -0700848 return -1;
849 }
850
851 globalAlertTetherCount--;
852 /*
853 * If there is no globalAlert active we are done.
854 * If there is an active globalAlert but there are more
855 * tethers, we are also done.
856 */
857 if (!globalAlertBytes || globalAlertTetherCount >= 1) {
858 return 0;
859 }
860
861 /* We only detete the rule if this was the last tether removed. */
862 res = runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
863 return res;
864}
865
JP Abgrall8a932722011-07-13 19:17:35 -0700866int BandwidthController::setSharedAlert(int64_t bytes) {
867 if (!sharedQuotaBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000868 ALOGE("Need to have a prior shared quota set to set an alert");
JP Abgrall8a932722011-07-13 19:17:35 -0700869 return -1;
870 }
871 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000872 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700873 return -1;
874 }
875 return setCostlyAlert("shared", bytes, &sharedAlertBytes);
876}
877
878int BandwidthController::removeSharedAlert(void) {
879 return removeCostlyAlert("shared", &sharedAlertBytes);
880}
881
882int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
883 std::list<QuotaInfo>::iterator it;
884
885 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000886 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700887 return -1;
888 }
889 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
890 if (it->ifaceName == iface)
891 break;
892 }
893
894 if (it == quotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000895 ALOGE("Need to have a prior interface quota set to set an alert");
JP Abgrall8a932722011-07-13 19:17:35 -0700896 return -1;
897 }
898
899 return setCostlyAlert(iface, bytes, &it->alert);
900}
901
902int BandwidthController::removeInterfaceAlert(const char *iface) {
903 std::list<QuotaInfo>::iterator it;
904
905 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
906 if (it->ifaceName == iface)
907 break;
908 }
909
910 if (it == quotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000911 ALOGE("No prior alert set for interface %s", iface);
JP Abgrall8a932722011-07-13 19:17:35 -0700912 return -1;
913 }
914
915 return removeCostlyAlert(iface, &it->alert);
916}
917
918int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
919 char *alertQuotaCmd;
920 char *chainNameAndPos;
921 int res = 0;
922 char *alertName;
923
924 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000925 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700926 return -1;
927 }
928 asprintf(&alertName, "%sAlert", costName);
929 if (*alertBytes) {
930 res = updateQuota(alertName, *alertBytes);
931 } else {
932 asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN);
Nick Kralevichc2b26cb2012-02-23 13:04:26 -0800933 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "", "-I", chainNameAndPos, bytes, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700934 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
935 free(alertQuotaCmd);
936 free(chainNameAndPos);
937 }
938 *alertBytes = bytes;
939 free(alertName);
940 return res;
941}
942
943int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
944 char *alertQuotaCmd;
945 char *chainName;
946 char *alertName;
947 int res = 0;
948
949 asprintf(&alertName, "%sAlert", costName);
950 if (!*alertBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000951 ALOGE("No prior alert set for %s alert", costName);
JP Abgrall8a932722011-07-13 19:17:35 -0700952 return -1;
953 }
954
955 asprintf(&chainName, "costly_%s", costName);
Nick Kralevichc2b26cb2012-02-23 13:04:26 -0800956 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "", "-D", chainName, *alertBytes, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700957 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
958 free(alertQuotaCmd);
959 free(chainName);
960
961 *alertBytes = 0;
962 free(alertName);
963 return res;
964}
JP Abgralldb7da582011-09-18 12:57:32 -0700965
966/*
967 * Parse the ptks and bytes out of:
JP Abgrall0031cea2012-04-17 16:38:23 -0700968 * Chain FORWARD (policy RETURN 0 packets, 0 bytes)
JP Abgralldb7da582011-09-18 12:57:32 -0700969 * pkts bytes target prot opt in out source destination
JP Abgrall0031cea2012-04-17 16:38:23 -0700970 * 0 0 RETURN all -- rmnet0 wlan0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
JP Abgralldb7da582011-09-18 12:57:32 -0700971 * 0 0 DROP all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 state INVALID
JP Abgrall0031cea2012-04-17 16:38:23 -0700972 * 0 0 RETURN all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0
JP Abgralldb7da582011-09-18 12:57:32 -0700973 *
974 */
JP Abgralla2a64f02011-11-11 20:36:16 -0800975int BandwidthController::parseForwardChainStats(TetherStats &stats, FILE *fp,
976 std::string &extraProcessingInfo) {
JP Abgralldb7da582011-09-18 12:57:32 -0700977 int res;
978 char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
979 char iface0[MAX_IPT_OUTPUT_LINE_LEN];
980 char iface1[MAX_IPT_OUTPUT_LINE_LEN];
981 char rest[MAX_IPT_OUTPUT_LINE_LEN];
982
983 char *buffPtr;
984 int64_t packets, bytes;
985
986 while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
987 /* Clean up, so a failed parse can still print info */
988 iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
JP Abgrall0031cea2012-04-17 16:38:23 -0700989 res = sscanf(buffPtr, "%lld %lld RETURN all -- %s %s 0.%s",
JP Abgralldb7da582011-09-18 12:57:32 -0700990 &packets, &bytes, iface0, iface1, rest);
Steve Block3fb42e02011-10-20 11:55:56 +0100991 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 -0700992 iface0, iface1, packets, bytes, rest, buffPtr);
JP Abgralla2a64f02011-11-11 20:36:16 -0800993 extraProcessingInfo += buffPtr;
994
JP Abgralldb7da582011-09-18 12:57:32 -0700995 if (res != 5) {
996 continue;
997 }
998 if ((stats.ifaceIn == iface0) && (stats.ifaceOut == iface1)) {
Steve Block3fb42e02011-10-20 11:55:56 +0100999 ALOGV("iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
JP Abgralldb7da582011-09-18 12:57:32 -07001000 stats.rxPackets = packets;
1001 stats.rxBytes = bytes;
1002 } else if ((stats.ifaceOut == iface0) && (stats.ifaceIn == iface1)) {
Steve Block3fb42e02011-10-20 11:55:56 +01001003 ALOGV("iface_in=%s iface_out=%s tx_bytes=%lld tx_packets=%lld ", iface1, iface0, bytes, packets);
JP Abgralldb7da582011-09-18 12:57:32 -07001004 stats.txPackets = packets;
1005 stats.txBytes = bytes;
1006 }
1007 }
1008 /* Failure if rx or tx was not found */
1009 return (stats.rxBytes == -1 || stats.txBytes == -1) ? -1 : 0;
1010}
1011
1012
1013char *BandwidthController::TetherStats::getStatsLine(void) {
1014 char *msg;
1015 asprintf(&msg, "%s %s %lld %lld %lld %lld", ifaceIn.c_str(), ifaceOut.c_str(),
1016 rxBytes, rxPackets, txBytes, txPackets);
1017 return msg;
1018}
1019
JP Abgralla2a64f02011-11-11 20:36:16 -08001020int BandwidthController::getTetherStats(TetherStats &stats, std::string &extraProcessingInfo) {
JP Abgralldb7da582011-09-18 12:57:32 -07001021 int res;
1022 std::string fullCmd;
1023 FILE *iptOutput;
1024 const char *cmd;
1025
1026 if (stats.rxBytes != -1 || stats.txBytes != -1) {
Steve Block5ea0c052012-01-06 19:18:11 +00001027 ALOGE("Unexpected input stats. Byte counts should be -1.");
JP Abgralldb7da582011-09-18 12:57:32 -07001028 return -1;
1029 }
1030
1031 /*
1032 * Why not use some kind of lib to talk to iptables?
1033 * Because the only libs are libiptc and libip6tc in iptables, and they are
1034 * not easy to use. They require the known iptables match modules to be
1035 * preloaded/linked, and require apparently a lot of wrapper code to get
1036 * the wanted info.
1037 */
1038 fullCmd = IPTABLES_PATH;
JP Abgrall0031cea2012-04-17 16:38:23 -07001039 fullCmd += " -nvx -L natctrl_FORWARD";
JP Abgralldb7da582011-09-18 12:57:32 -07001040 iptOutput = popen(fullCmd.c_str(), "r");
1041 if (!iptOutput) {
Steve Block5ea0c052012-01-06 19:18:11 +00001042 ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
JP Abgralla2a64f02011-11-11 20:36:16 -08001043 extraProcessingInfo += "Failed to run iptables.";
JP Abgralldb7da582011-09-18 12:57:32 -07001044 return -1;
1045 }
JP Abgralla2a64f02011-11-11 20:36:16 -08001046 res = parseForwardChainStats(stats, iptOutput, extraProcessingInfo);
JP Abgralldb7da582011-09-18 12:57:32 -07001047 pclose(iptOutput);
1048
1049 /* Currently NatController doesn't do ipv6 tethering, so we are done. */
1050 return res;
1051}