blob: 7f1aaf5d44a8bddd927acccbf38dcac916b42436 [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 Abgrallad729ac2012-04-24 23:27:44 -0700158int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling,
159 IptFailureLog failureHandling) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700160 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700161
Steve Block3fb42e02011-10-20 11:55:56 +0100162 ALOGV("runIpxtablesCmd(cmd=%s)", cmd);
JP Abgrallad729ac2012-04-24 23:27:44 -0700163 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4, failureHandling);
164 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6, failureHandling);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700165 return res;
166}
167
JP Abgrall26e0d492011-06-24 19:21:51 -0700168int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
169
170 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0'
171 strncpy(buffer, src, buffSize);
172 return buffer[buffSize - 1];
173}
174
JP Abgrall8a932722011-07-13 19:17:35 -0700175int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling,
JP Abgrallad729ac2012-04-24 23:27:44 -0700176 IptIpVer iptVer, IptFailureLog failureHandling) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700177 char buffer[MAX_CMD_LEN];
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700178 const char *argv[MAX_CMD_ARGS];
JP Abgrall26e0d492011-06-24 19:21:51 -0700179 int argc = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700180 char *next = buffer;
181 char *tmp;
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700182 int res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700183
JP Abgrall0dad7c22011-06-24 11:58:14 -0700184 std::string fullCmd = cmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700185
186 if (rejectHandling == IptRejectAdd) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700187 fullCmd += " --jump REJECT --reject-with";
JP Abgrall26e0d492011-06-24 19:21:51 -0700188 switch (iptVer) {
189 case IptIpV4:
JP Abgrall8a932722011-07-13 19:17:35 -0700190 fullCmd += " icmp-net-prohibited";
191 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700192 case IptIpV6:
JP Abgrall8a932722011-07-13 19:17:35 -0700193 fullCmd += " icmp6-adm-prohibited";
194 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700195 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700196 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700197
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700198 fullCmd.insert(0, " ");
199 fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700200
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700201 if (!useLogwrapCall) {
JP Abgrall9e5e0ce2011-12-14 15:20:59 -0800202 res = system_nosh(fullCmd.c_str());
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700203 } else {
204 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000205 ALOGE("iptables command too long");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700206 return -1;
207 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700208
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700209 argc = 0;
210 while ((tmp = strsep(&next, " "))) {
211 argv[argc++] = tmp;
212 if (argc >= MAX_CMD_ARGS) {
Steve Block5ea0c052012-01-06 19:18:11 +0000213 ALOGE("iptables argument overflow");
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700214 return -1;
215 }
216 }
217
218 argv[argc] = NULL;
Glenn Kastenc4bbfa22012-03-05 15:13:58 -0800219 res = logwrap(argc, argv);
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700220 }
JP Abgrallad729ac2012-04-24 23:27:44 -0700221 if (res && failureHandling == IptFailShow) {
Steve Block5ea0c052012-01-06 19:18:11 +0000222 ALOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res);
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700223 }
224 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700225}
226
JP Abgrall0031cea2012-04-17 16:38:23 -0700227int BandwidthController::setupIptablesHooks(void) {
228
229 /* Some of the initialCommands are allowed to fail */
230 runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
231 IPT_FLUSH_COMMANDS, RunCmdFailureOk);
232
233 runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
234 IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
235
236 runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*),
237 IPT_SETUP_COMMANDS, RunCmdFailureBad);
238
239 return 0;
240
241}
242
243int BandwidthController::enableBandwidthControl(bool force) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700244 int res;
JP Abgrall0031cea2012-04-17 16:38:23 -0700245 char value[PROPERTY_VALUE_MAX];
246
247 if (!force) {
248 property_get("persist.bandwidth.enable", value, "1");
249 if (!strcmp(value, "0"))
250 return 0;
251 }
JP Abgrall8a932722011-07-13 19:17:35 -0700252
JP Abgralldb7da582011-09-18 12:57:32 -0700253 /* Let's pretend we started from scratch ... */
JP Abgrall8a932722011-07-13 19:17:35 -0700254 sharedQuotaIfaces.clear();
255 quotaIfaces.clear();
256 naughtyAppUids.clear();
JP Abgralldb7da582011-09-18 12:57:32 -0700257 globalAlertBytes = 0;
JP Abgrallc6c67342011-10-07 16:28:54 -0700258 globalAlertTetherCount = 0;
JP Abgralldb7da582011-09-18 12:57:32 -0700259 sharedQuotaBytes = sharedAlertBytes = 0;
260
JP Abgrall0031cea2012-04-17 16:38:23 -0700261 res = runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
262 IPT_FLUSH_COMMANDS, RunCmdFailureOk);
JP Abgralldb7da582011-09-18 12:57:32 -0700263
JP Abgrall0031cea2012-04-17 16:38:23 -0700264 res |= runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
JP Abgralldb7da582011-09-18 12:57:32 -0700265 IPT_BASIC_ACCOUNTING_COMMANDS, RunCmdFailureBad);
JP Abgrall8a932722011-07-13 19:17:35 -0700266
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700267 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700268
269}
270
271int BandwidthController::disableBandwidthControl(void) {
JP Abgrall0031cea2012-04-17 16:38:23 -0700272 runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
273 IPT_FLUSH_COMMANDS, RunCmdFailureOk);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700274 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700275}
276
JP Abgrall8a932722011-07-13 19:17:35 -0700277int BandwidthController::runCommands(int numCommands, const char *commands[],
278 RunCmdErrHandling cmdErrHandling) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700279 int res = 0;
JP Abgrallad729ac2012-04-24 23:27:44 -0700280 IptFailureLog failureLogging = IptFailShow;
281 if (cmdErrHandling == RunCmdFailureOk) {
282 failureLogging = IptFailHide;
283 }
Steve Block3fb42e02011-10-20 11:55:56 +0100284 ALOGV("runCommands(): %d commands", numCommands);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700285 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
JP Abgrallad729ac2012-04-24 23:27:44 -0700286 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd, failureLogging);
JP Abgrall0031cea2012-04-17 16:38:23 -0700287 if (res && cmdErrHandling != RunCmdFailureOk)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700288 return res;
289 }
JP Abgrall0031cea2012-04-17 16:38:23 -0700290 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700291}
292
JP Abgrall0dad7c22011-06-24 11:58:14 -0700293std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700294 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700295 char *buff;
296 const char *opFlag;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700297
298 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700299 case IptOpInsert:
300 opFlag = "-I";
301 break;
302 case IptOpReplace:
303 opFlag = "-R";
304 break;
305 default:
306 case IptOpDelete:
307 opFlag = "-D";
308 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700309 }
JP Abgrall8a932722011-07-13 19:17:35 -0700310 asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid);
311 res = buff;
312 free(buff);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700313 return res;
314}
315
316int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700317 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700318}
319
320int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700321 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700322}
323
JP Abgrall26e0d492011-06-24 19:21:51 -0700324int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700325 char cmd[MAX_CMD_LEN];
326 int uidNum;
JP Abgrall26e0d492011-06-24 19:21:51 -0700327 const char *failLogTemplate;
328 IptOp op;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700329 int appUids[numUids];
JP Abgrall26e0d492011-06-24 19:21:51 -0700330 std::string naughtyCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700331
JP Abgrall26e0d492011-06-24 19:21:51 -0700332 switch (appOp) {
333 case NaughtyAppOpAdd:
JP Abgrall8a932722011-07-13 19:17:35 -0700334 op = IptOpInsert;
335 failLogTemplate = "Failed to add app uid %d to penalty box.";
336 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700337 case NaughtyAppOpRemove:
JP Abgrall8a932722011-07-13 19:17:35 -0700338 op = IptOpDelete;
339 failLogTemplate = "Failed to delete app uid %d from penalty box.";
340 break;
JP Abgrall0031cea2012-04-17 16:38:23 -0700341 default:
342 ALOGE("Unexpected app Op %d", appOp);
343 return -1;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700344 }
345
346 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700347 appUids[uidNum] = atol(appStrUids[uidNum]);
348 if (appUids[uidNum] == 0) {
Steve Block5ea0c052012-01-06 19:18:11 +0000349 ALOGE(failLogTemplate, appUids[uidNum]);
JP Abgrall26e0d492011-06-24 19:21:51 -0700350 goto fail_parse;
351 }
352 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700353
354 for (uidNum = 0; uidNum < numUids; uidNum++) {
355 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
356 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
Steve Block5ea0c052012-01-06 19:18:11 +0000357 ALOGE(failLogTemplate, appUids[uidNum]);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700358 goto fail_with_uidNum;
359 }
360 }
361 return 0;
362
JP Abgrall26e0d492011-06-24 19:21:51 -0700363fail_with_uidNum:
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700364 /* Try to remove the uid that failed in any case*/
JP Abgrall26e0d492011-06-24 19:21:51 -0700365 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
366 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
367fail_parse:
368 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700369}
370
JP Abgrall26e0d492011-06-24 19:21:51 -0700371std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700372 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700373 char *buff;
374 const char *opFlag;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700375
Steve Block3fb42e02011-10-20 11:55:56 +0100376 ALOGV("makeIptablesQuotaCmd(%d, %lld)", op, quota);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700377
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700378 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700379 case IptOpInsert:
380 opFlag = "-I";
381 break;
382 case IptOpReplace:
383 opFlag = "-R";
384 break;
385 default:
386 case IptOpDelete:
387 opFlag = "-D";
388 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700389 }
JP Abgrall8a932722011-07-13 19:17:35 -0700390
JP Abgrallbfa74662011-06-29 19:23:04 -0700391 // The requried IP version specific --jump REJECT ... will be added later.
JP Abgrall8a932722011-07-13 19:17:35 -0700392 asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota,
393 costName);
394 res = buff;
395 free(buff);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700396 return res;
397}
398
JP Abgrall26e0d492011-06-24 19:21:51 -0700399int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700400 char cmd[MAX_CMD_LEN];
JP Abgrall0031cea2012-04-17 16:38:23 -0700401 int res = 0, res1, res2;
JP Abgrall8a932722011-07-13 19:17:35 -0700402 int ruleInsertPos = 1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700403 std::string costString;
404 const char *costCString;
405
JP Abgrall0dad7c22011-06-24 11:58:14 -0700406 /* The "-N costly" is created upfront, no need to handle it here. */
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 Abgrall0031cea2012-04-17 16:38:23 -0700412 /*
413 * Flush the costly_<iface> is allowed to fail in case it didn't exist.
414 * Creating a new one is allowed to fail in case it existed.
415 * This helps with netd restarts.
416 */
417 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrallad729ac2012-04-24 23:27:44 -0700418 res1 = runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700419 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
JP Abgrallad729ac2012-04-24 23:27:44 -0700420 res2 = runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide);
JP Abgrall0031cea2012-04-17 16:38:23 -0700421 res = (res1 && res2) || (!res1 && !res2);
422
JP Abgrall0dad7c22011-06-24 11:58:14 -0700423 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700424 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700425 snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700426 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall26e0d492011-06-24 19:21:51 -0700427 break;
428 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700429 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700430 break;
JP Abgrall0031cea2012-04-17 16:38:23 -0700431 default:
432 ALOGE("Unexpected quotatype %d", quotaType);
433 return -1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700434 }
435
JP Abgrall8a932722011-07-13 19:17:35 -0700436 if (globalAlertBytes) {
437 /* The alert rule comes 1st */
438 ruleInsertPos = 2;
439 }
JP Abgrall0031cea2012-04-17 16:38:23 -0700440
441 snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
JP Abgrallad729ac2012-04-24 23:27:44 -0700442 runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide);
JP Abgrall0031cea2012-04-17 16:38:23 -0700443
444 snprintf(cmd, sizeof(cmd), "-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700445 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0031cea2012-04-17 16:38:23 -0700446
447 snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
JP Abgrallad729ac2012-04-24 23:27:44 -0700448 runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide);
JP Abgrall0031cea2012-04-17 16:38:23 -0700449
450 snprintf(cmd, sizeof(cmd), "-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700451 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700452 return res;
453}
454
JP Abgrall26e0d492011-06-24 19:21:51 -0700455int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700456 char cmd[MAX_CMD_LEN];
457 int res = 0;
458 std::string costString;
459 const char *costCString;
460
JP Abgrall26e0d492011-06-24 19:21:51 -0700461 switch (quotaType) {
462 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700463 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700464 costString += ifn;
465 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700466 break;
467 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700468 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700469 break;
JP Abgrall0031cea2012-04-17 16:38:23 -0700470 default:
471 ALOGE("Unexpected quotatype %d", quotaType);
472 return -1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700473 }
474
JP Abgrall0031cea2012-04-17 16:38:23 -0700475 snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700476 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0031cea2012-04-17 16:38:23 -0700477 snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700478 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700479
JP Abgrallbfa74662011-06-29 19:23:04 -0700480 /* The "-N costly_shared" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700481 if (quotaType == QuotaUnique) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700482 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700483 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgralla9f802c2011-06-29 15:46:45 -0700484 snprintf(cmd, sizeof(cmd), "-X %s", costCString);
485 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700486 }
487 return res;
488}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700489
JP Abgrall0dad7c22011-06-24 11:58:14 -0700490int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700491 char cmd[MAX_CMD_LEN];
492 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700493 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700494 std::string quotaCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700495 std::string ifaceName;
496 ;
JP Abgrallbfa74662011-06-29 19:23:04 -0700497 const char *costName = "shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700498 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700499
JP Abgrall8a932722011-07-13 19:17:35 -0700500 if (!maxBytes) {
501 /* Don't talk about -1, deprecate it. */
Steve Block5ea0c052012-01-06 19:18:11 +0000502 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700503 return -1;
504 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700505 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000506 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700507 return -1;
508 }
509 ifaceName = ifn;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700510
511 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700512 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700513 }
514
515 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700516 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
517 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700518 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700519 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700520
JP Abgrall0dad7c22011-06-24 11:58:14 -0700521 if (it == sharedQuotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700522 res |= prepCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700523 if (sharedQuotaIfaces.empty()) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700524 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700525 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700526 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000527 ALOGE("Failed set quota rule");
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700528 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700529 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700530 sharedQuotaBytes = maxBytes;
531 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700532 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700533
534 }
535
536 if (maxBytes != sharedQuotaBytes) {
JP Abgrall8a932722011-07-13 19:17:35 -0700537 res |= updateQuota(costName, maxBytes);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700538 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000539 ALOGE("Failed update quota for %s", costName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700540 goto fail;
541 }
542 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700543 }
544 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700545
546 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700547 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700548 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
549 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700550 * For now callers needs to choose if they want to "ndc bandwidth enable"
551 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700552 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700553 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700554 return -1;
555}
556
JP Abgrall8a932722011-07-13 19:17:35 -0700557/* It will also cleanup any shared alerts */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700558int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700559 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700560 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700561 std::string ifaceName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700562 std::list<std::string>::iterator it;
JP Abgrallbfa74662011-06-29 19:23:04 -0700563 const char *costName = "shared";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700564
JP Abgrall8a932722011-07-13 19:17:35 -0700565 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000566 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700567 return -1;
568 }
JP Abgrall8a932722011-07-13 19:17:35 -0700569 ifaceName = ifn;
JP Abgrall26e0d492011-06-24 19:21:51 -0700570
JP Abgrall0dad7c22011-06-24 11:58:14 -0700571 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
572 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700573 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700574 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700575 if (it == sharedQuotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000576 ALOGE("No such iface %s to delete", ifn);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700577 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700578 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700579
JP Abgrall26e0d492011-06-24 19:21:51 -0700580 res |= cleanupCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700581 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700582
JP Abgrall0dad7c22011-06-24 11:58:14 -0700583 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700584 std::string quotaCmd;
JP Abgrallbfa74662011-06-29 19:23:04 -0700585 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700586 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700587 sharedQuotaBytes = 0;
588 if (sharedAlertBytes) {
589 removeSharedAlert();
590 sharedAlertBytes = 0;
591 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700592 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700593 return res;
594}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700595
596int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
597 char ifn[MAX_IFACENAME_LEN];
598 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700599 std::string ifaceName;
600 const char *costName;
601 std::list<QuotaInfo>::iterator it;
602 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700603
JP Abgrall8a932722011-07-13 19:17:35 -0700604 if (!maxBytes) {
605 /* Don't talk about -1, deprecate it. */
Steve Block5ea0c052012-01-06 19:18:11 +0000606 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700607 return -1;
608 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700609 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700610 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700611 }
612
JP Abgrall8a932722011-07-13 19:17:35 -0700613 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000614 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700615 return -1;
616 }
617 ifaceName = ifn;
618 costName = iface;
619
JP Abgrall0dad7c22011-06-24 11:58:14 -0700620 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700621 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700622 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700623 break;
624 }
625
626 if (it == quotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700627 res |= prepCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700628 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700629 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700630 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000631 ALOGE("Failed set quota rule");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700632 goto fail;
633 }
634
JP Abgrall8a932722011-07-13 19:17:35 -0700635 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
JP Abgrall0dad7c22011-06-24 11:58:14 -0700636
637 } else {
JP Abgrall8a932722011-07-13 19:17:35 -0700638 res |= updateQuota(costName, maxBytes);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700639 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000640 ALOGE("Failed update quota for %s", iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700641 goto fail;
642 }
JP Abgrall8a932722011-07-13 19:17:35 -0700643 it->quota = maxBytes;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700644 }
645 return 0;
646
647 fail:
648 /*
649 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
650 * rules in the kernel to see which ones need cleaning up.
651 * For now callers needs to choose if they want to "ndc bandwidth enable"
652 * which resets everything.
653 */
654 removeInterfaceSharedQuota(ifn);
655 return -1;
656}
657
JP Abgrall8a932722011-07-13 19:17:35 -0700658int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
659 return getInterfaceQuota("shared", bytes);
660}
661
662int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
663 FILE *fp;
664 char *fname;
665 int scanRes;
666
667 asprintf(&fname, "/proc/net/xt_quota/%s", costName);
668 fp = fopen(fname, "r");
669 free(fname);
670 if (!fp) {
Steve Block5ea0c052012-01-06 19:18:11 +0000671 ALOGE("Reading quota %s failed (%s)", costName, strerror(errno));
JP Abgrall8a932722011-07-13 19:17:35 -0700672 return -1;
673 }
674 scanRes = fscanf(fp, "%lld", bytes);
Steve Block3fb42e02011-10-20 11:55:56 +0100675 ALOGV("Read quota res=%d bytes=%lld", scanRes, *bytes);
JP Abgrall8a932722011-07-13 19:17:35 -0700676 fclose(fp);
677 return scanRes == 1 ? 0 : -1;
678}
679
JP Abgrall0dad7c22011-06-24 11:58:14 -0700680int BandwidthController::removeInterfaceQuota(const char *iface) {
681
682 char ifn[MAX_IFACENAME_LEN];
683 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700684 std::string ifaceName;
685 const char *costName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700686 std::list<QuotaInfo>::iterator it;
JP Abgrall26e0d492011-06-24 19:21:51 -0700687
JP Abgrall8a932722011-07-13 19:17:35 -0700688 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000689 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700690 return -1;
691 }
692 ifaceName = ifn;
693 costName = iface;
694
JP Abgrall0dad7c22011-06-24 11:58:14 -0700695 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700696 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700697 break;
698 }
699
700 if (it == quotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000701 ALOGE("No such iface %s to delete", ifn);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700702 return -1;
703 }
704
705 /* This also removes the quota command of CostlyIface chain. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700706 res |= cleanupCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700707
708 quotaIfaces.erase(it);
709
710 return res;
711}
JP Abgrall8a932722011-07-13 19:17:35 -0700712
713int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
714 FILE *fp;
715 char *fname;
716
717 asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
718 fp = fopen(fname, "w");
719 free(fname);
720 if (!fp) {
Steve Block5ea0c052012-01-06 19:18:11 +0000721 ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
JP Abgrall8a932722011-07-13 19:17:35 -0700722 return -1;
723 }
724 fprintf(fp, "%lld\n", bytes);
725 fclose(fp);
726 return 0;
727}
728
729int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
730 int res = 0;
731 const char *opFlag;
JP Abgrall87666692011-09-08 13:44:10 -0700732 const char *ifaceLimiting;
JP Abgrall8a932722011-07-13 19:17:35 -0700733 char *alertQuotaCmd;
734
735 switch (op) {
736 case IptOpInsert:
737 opFlag = "-I";
738 break;
739 case IptOpReplace:
740 opFlag = "-R";
741 break;
742 default:
743 case IptOpDelete:
744 opFlag = "-D";
745 break;
746 }
747
JP Abgrall87666692011-09-08 13:44:10 -0700748 ifaceLimiting = "! -i lo+";
JP Abgrall0031cea2012-04-17 16:38:23 -0700749 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_INPUT",
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);
JP Abgrall87666692011-09-08 13:44:10 -0700753 ifaceLimiting = "! -o lo+";
JP Abgrall0031cea2012-04-17 16:38:23 -0700754 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_OUTPUT",
Nick Kralevichc2b26cb2012-02-23 13:04:26 -0800755 bytes, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700756 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
757 free(alertQuotaCmd);
758 return res;
759}
760
JP Abgrallc6c67342011-10-07 16:28:54 -0700761int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const char *alertName, int64_t bytes) {
762 int res = 0;
763 const char *opFlag;
764 const char *ifaceLimiting;
JP Abgrall8a932722011-07-13 19:17:35 -0700765 char *alertQuotaCmd;
JP Abgrallc6c67342011-10-07 16:28:54 -0700766
767 switch (op) {
768 case IptOpInsert:
769 opFlag = "-I";
770 break;
771 case IptOpReplace:
772 opFlag = "-R";
773 break;
774 default:
775 case IptOpDelete:
776 opFlag = "-D";
777 break;
778 }
779
780 ifaceLimiting = "! -i lo+";
JP Abgrall0031cea2012-04-17 16:38:23 -0700781 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_FORWARD",
Nick Kralevichc2b26cb2012-02-23 13:04:26 -0800782 bytes, alertName);
JP Abgrallc6c67342011-10-07 16:28:54 -0700783 res = runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
784 free(alertQuotaCmd);
785 return res;
786}
787
788int BandwidthController::setGlobalAlert(int64_t bytes) {
789 const char *alertName = ALERT_GLOBAL_NAME;
JP Abgrall8a932722011-07-13 19:17:35 -0700790 int res = 0;
791
792 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000793 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700794 return -1;
795 }
796 if (globalAlertBytes) {
797 res = updateQuota(alertName, bytes);
798 } else {
799 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700800 if (globalAlertTetherCount) {
Steve Block3fb42e02011-10-20 11:55:56 +0100801 ALOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
JP Abgrallc6c67342011-10-07 16:28:54 -0700802 res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
803 }
JP Abgrall8a932722011-07-13 19:17:35 -0700804 }
805 globalAlertBytes = bytes;
806 return res;
807}
808
JP Abgrallc6c67342011-10-07 16:28:54 -0700809int BandwidthController::setGlobalAlertInForwardChain(void) {
810 const char *alertName = ALERT_GLOBAL_NAME;
811 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700812
JP Abgrallc6c67342011-10-07 16:28:54 -0700813 globalAlertTetherCount++;
Steve Block3fb42e02011-10-20 11:55:56 +0100814 ALOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
JP Abgrallc6c67342011-10-07 16:28:54 -0700815
816 /*
817 * If there is no globalAlert active we are done.
818 * If there is an active globalAlert but this is not the 1st
819 * tether, we are also done.
820 */
821 if (!globalAlertBytes || globalAlertTetherCount != 1) {
822 return 0;
823 }
824
825 /* We only add the rule if this was the 1st tether added. */
826 res = runIptablesAlertFwdCmd(IptOpInsert, alertName, globalAlertBytes);
827 return res;
828}
829
830int BandwidthController::removeGlobalAlert(void) {
831
832 const char *alertName = ALERT_GLOBAL_NAME;
JP Abgrall8a932722011-07-13 19:17:35 -0700833 int res = 0;
834
835 if (!globalAlertBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000836 ALOGE("No prior alert set");
JP Abgrall8a932722011-07-13 19:17:35 -0700837 return -1;
838 }
839 res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700840 if (globalAlertTetherCount) {
841 res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
842 }
JP Abgrall8a932722011-07-13 19:17:35 -0700843 globalAlertBytes = 0;
844 return res;
845}
846
JP Abgrallc6c67342011-10-07 16:28:54 -0700847int BandwidthController::removeGlobalAlertInForwardChain(void) {
848 int res = 0;
849 const char *alertName = ALERT_GLOBAL_NAME;
850
851 if (!globalAlertTetherCount) {
Steve Block5ea0c052012-01-06 19:18:11 +0000852 ALOGE("No prior alert set");
JP Abgrallc6c67342011-10-07 16:28:54 -0700853 return -1;
854 }
855
856 globalAlertTetherCount--;
857 /*
858 * If there is no globalAlert active we are done.
859 * If there is an active globalAlert but there are more
860 * tethers, we are also done.
861 */
862 if (!globalAlertBytes || globalAlertTetherCount >= 1) {
863 return 0;
864 }
865
866 /* We only detete the rule if this was the last tether removed. */
867 res = runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
868 return res;
869}
870
JP Abgrall8a932722011-07-13 19:17:35 -0700871int BandwidthController::setSharedAlert(int64_t bytes) {
872 if (!sharedQuotaBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000873 ALOGE("Need to have a prior shared quota set to set an alert");
JP Abgrall8a932722011-07-13 19:17:35 -0700874 return -1;
875 }
876 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000877 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700878 return -1;
879 }
880 return setCostlyAlert("shared", bytes, &sharedAlertBytes);
881}
882
883int BandwidthController::removeSharedAlert(void) {
884 return removeCostlyAlert("shared", &sharedAlertBytes);
885}
886
887int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
888 std::list<QuotaInfo>::iterator it;
889
890 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000891 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700892 return -1;
893 }
894 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
895 if (it->ifaceName == iface)
896 break;
897 }
898
899 if (it == quotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000900 ALOGE("Need to have a prior interface quota set to set an alert");
JP Abgrall8a932722011-07-13 19:17:35 -0700901 return -1;
902 }
903
904 return setCostlyAlert(iface, bytes, &it->alert);
905}
906
907int BandwidthController::removeInterfaceAlert(const char *iface) {
908 std::list<QuotaInfo>::iterator it;
909
910 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
911 if (it->ifaceName == iface)
912 break;
913 }
914
915 if (it == quotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000916 ALOGE("No prior alert set for interface %s", iface);
JP Abgrall8a932722011-07-13 19:17:35 -0700917 return -1;
918 }
919
920 return removeCostlyAlert(iface, &it->alert);
921}
922
923int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
924 char *alertQuotaCmd;
925 char *chainNameAndPos;
926 int res = 0;
927 char *alertName;
928
929 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000930 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700931 return -1;
932 }
933 asprintf(&alertName, "%sAlert", costName);
934 if (*alertBytes) {
935 res = updateQuota(alertName, *alertBytes);
936 } else {
937 asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN);
Nick Kralevichc2b26cb2012-02-23 13:04:26 -0800938 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "", "-I", chainNameAndPos, bytes, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700939 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
940 free(alertQuotaCmd);
941 free(chainNameAndPos);
942 }
943 *alertBytes = bytes;
944 free(alertName);
945 return res;
946}
947
948int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
949 char *alertQuotaCmd;
950 char *chainName;
951 char *alertName;
952 int res = 0;
953
954 asprintf(&alertName, "%sAlert", costName);
955 if (!*alertBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000956 ALOGE("No prior alert set for %s alert", costName);
JP Abgrall8a932722011-07-13 19:17:35 -0700957 return -1;
958 }
959
960 asprintf(&chainName, "costly_%s", costName);
Nick Kralevichc2b26cb2012-02-23 13:04:26 -0800961 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "", "-D", chainName, *alertBytes, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700962 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
963 free(alertQuotaCmd);
964 free(chainName);
965
966 *alertBytes = 0;
967 free(alertName);
968 return res;
969}
JP Abgralldb7da582011-09-18 12:57:32 -0700970
971/*
972 * Parse the ptks and bytes out of:
JP Abgrall0031cea2012-04-17 16:38:23 -0700973 * Chain FORWARD (policy RETURN 0 packets, 0 bytes)
JP Abgralldb7da582011-09-18 12:57:32 -0700974 * pkts bytes target prot opt in out source destination
JP Abgrall0031cea2012-04-17 16:38:23 -0700975 * 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 -0700976 * 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 -0700977 * 0 0 RETURN all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0
JP Abgralldb7da582011-09-18 12:57:32 -0700978 *
979 */
JP Abgralla2a64f02011-11-11 20:36:16 -0800980int BandwidthController::parseForwardChainStats(TetherStats &stats, FILE *fp,
981 std::string &extraProcessingInfo) {
JP Abgralldb7da582011-09-18 12:57:32 -0700982 int res;
983 char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
984 char iface0[MAX_IPT_OUTPUT_LINE_LEN];
985 char iface1[MAX_IPT_OUTPUT_LINE_LEN];
986 char rest[MAX_IPT_OUTPUT_LINE_LEN];
987
988 char *buffPtr;
989 int64_t packets, bytes;
990
991 while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
992 /* Clean up, so a failed parse can still print info */
993 iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
JP Abgrall0031cea2012-04-17 16:38:23 -0700994 res = sscanf(buffPtr, "%lld %lld RETURN all -- %s %s 0.%s",
JP Abgralldb7da582011-09-18 12:57:32 -0700995 &packets, &bytes, iface0, iface1, rest);
Steve Block3fb42e02011-10-20 11:55:56 +0100996 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 -0700997 iface0, iface1, packets, bytes, rest, buffPtr);
JP Abgralla2a64f02011-11-11 20:36:16 -0800998 extraProcessingInfo += buffPtr;
999
JP Abgralldb7da582011-09-18 12:57:32 -07001000 if (res != 5) {
1001 continue;
1002 }
1003 if ((stats.ifaceIn == iface0) && (stats.ifaceOut == iface1)) {
Steve Block3fb42e02011-10-20 11:55:56 +01001004 ALOGV("iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
JP Abgralldb7da582011-09-18 12:57:32 -07001005 stats.rxPackets = packets;
1006 stats.rxBytes = bytes;
1007 } else if ((stats.ifaceOut == iface0) && (stats.ifaceIn == iface1)) {
Steve Block3fb42e02011-10-20 11:55:56 +01001008 ALOGV("iface_in=%s iface_out=%s tx_bytes=%lld tx_packets=%lld ", iface1, iface0, bytes, packets);
JP Abgralldb7da582011-09-18 12:57:32 -07001009 stats.txPackets = packets;
1010 stats.txBytes = bytes;
1011 }
1012 }
1013 /* Failure if rx or tx was not found */
1014 return (stats.rxBytes == -1 || stats.txBytes == -1) ? -1 : 0;
1015}
1016
1017
1018char *BandwidthController::TetherStats::getStatsLine(void) {
1019 char *msg;
1020 asprintf(&msg, "%s %s %lld %lld %lld %lld", ifaceIn.c_str(), ifaceOut.c_str(),
1021 rxBytes, rxPackets, txBytes, txPackets);
1022 return msg;
1023}
1024
JP Abgralla2a64f02011-11-11 20:36:16 -08001025int BandwidthController::getTetherStats(TetherStats &stats, std::string &extraProcessingInfo) {
JP Abgralldb7da582011-09-18 12:57:32 -07001026 int res;
1027 std::string fullCmd;
1028 FILE *iptOutput;
1029 const char *cmd;
1030
1031 if (stats.rxBytes != -1 || stats.txBytes != -1) {
Steve Block5ea0c052012-01-06 19:18:11 +00001032 ALOGE("Unexpected input stats. Byte counts should be -1.");
JP Abgralldb7da582011-09-18 12:57:32 -07001033 return -1;
1034 }
1035
1036 /*
1037 * Why not use some kind of lib to talk to iptables?
1038 * Because the only libs are libiptc and libip6tc in iptables, and they are
1039 * not easy to use. They require the known iptables match modules to be
1040 * preloaded/linked, and require apparently a lot of wrapper code to get
1041 * the wanted info.
1042 */
1043 fullCmd = IPTABLES_PATH;
JP Abgrall0031cea2012-04-17 16:38:23 -07001044 fullCmd += " -nvx -L natctrl_FORWARD";
JP Abgralldb7da582011-09-18 12:57:32 -07001045 iptOutput = popen(fullCmd.c_str(), "r");
1046 if (!iptOutput) {
Steve Block5ea0c052012-01-06 19:18:11 +00001047 ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
JP Abgralla2a64f02011-11-11 20:36:16 -08001048 extraProcessingInfo += "Failed to run iptables.";
JP Abgralldb7da582011-09-18 12:57:32 -07001049 return -1;
1050 }
JP Abgralla2a64f02011-11-11 20:36:16 -08001051 res = parseForwardChainStats(stats, iptOutput, extraProcessingInfo);
JP Abgralldb7da582011-09-18 12:57:32 -07001052 pclose(iptOutput);
1053
1054 /* Currently NatController doesn't do ipv6 tethering, so we are done. */
1055 return res;
1056}