blob: f66df0a45b9e87f3c59313db7959d02be0e69fcd [file] [log] [blame]
JP Abgrall4a5f5ca2011-06-15 18:37:39 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
JP Abgralldb7da582011-09-18 12:57:32 -070017// #define LOG_NDEBUG 0
18
19/*
20 * The CommandListener, FrameworkListener don't allow for
21 * multiple calls in parallel to reach the BandwidthController.
22 * If they ever were to allow it, then netd/ would need some tweaking.
23 */
24
JP Abgrall8a932722011-07-13 19:17:35 -070025#include <errno.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070026#include <fcntl.h>
JP Abgralldb7da582011-09-18 12:57:32 -070027#include <stdio.h>
JP Abgrall8a932722011-07-13 19:17:35 -070028#include <stdlib.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070029#include <string.h>
30
31#include <sys/socket.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34#include <sys/wait.h>
35
36#include <linux/netlink.h>
37#include <linux/rtnetlink.h>
38#include <linux/pkt_sched.h>
39
40#define LOG_TAG "BandwidthController"
41#include <cutils/log.h>
42#include <cutils/properties.h>
43
44extern "C" int logwrap(int argc, const char **argv, int background);
JP Abgrall9e5e0ce2011-12-14 15:20:59 -080045extern "C" int system_nosh(const char *command);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070046
47#include "BandwidthController.h"
Kazuhiro Ondo4ab46852012-01-12 16:15:06 -060048#include "oem_iptables_hook.h"
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070049
JP Abgralldb7da582011-09-18 12:57:32 -070050/* Alphabetical */
JP Abgrall87666692011-09-08 13:44:10 -070051const char BandwidthController::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 char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
55const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
56const int BandwidthController::MAX_CMD_ARGS = 32;
57const int BandwidthController::MAX_CMD_LEN = 1024;
58const int BandwidthController::MAX_IFACENAME_LEN = 64;
59const int BandwidthController::MAX_IPT_OUTPUT_LINE_LEN = 256;
60
JP Abgrall11b4e9b2011-08-11 15:34:49 -070061bool BandwidthController::useLogwrapCall = false;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070062
63/**
64 * Some comments about the rules:
65 * * Ordering
66 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
67 * E.g. "-I INPUT -i rmnet0 --goto costly"
68 * - quota'd rules in the costly chain should be before penalty_box lookups.
69 *
70 * * global quota vs per interface quota
71 * - global quota for all costly interfaces uses a single costly chain:
72 * . initial rules
JP Abgrallbfa74662011-06-29 19:23:04 -070073 * iptables -N costly_shared
74 * iptables -I INPUT -i iface0 --goto costly_shared
75 * iptables -I OUTPUT -o iface0 --goto costly_shared
76 * iptables -I costly_shared -m quota \! --quota 500000 \
77 * --jump REJECT --reject-with icmp-net-prohibited
78 * iptables -A costly_shared --jump penalty_box
79 * iptables -A costly_shared -m owner --socket-exists
JP Abgrall8a932722011-07-13 19:17:35 -070080 *
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070081 * . adding a new iface to this, E.g.:
JP Abgrallbfa74662011-06-29 19:23:04 -070082 * iptables -I INPUT -i iface1 --goto costly_shared
83 * iptables -I OUTPUT -o iface1 --goto costly_shared
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070084 *
85 * - quota per interface. This is achieve by having "costly" chains per quota.
86 * E.g. adding a new costly interface iface0 with its own quota:
87 * iptables -N costly_iface0
88 * iptables -I INPUT -i iface0 --goto costly_iface0
89 * iptables -I OUTPUT -o iface0 --goto costly_iface0
JP Abgrallbfa74662011-06-29 19:23:04 -070090 * iptables -A costly_iface0 -m quota \! --quota 500000 \
91 * --jump REJECT --reject-with icmp-net-prohibited
92 * iptables -A costly_iface0 --jump penalty_box
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070093 * iptables -A costly_iface0 -m owner --socket-exists
94 *
95 * * penalty_box handling:
96 * - only one penalty_box for all interfaces
97 * E.g Adding an app:
JP Abgrallbfa74662011-06-29 19:23:04 -070098 * iptables -A penalty_box -m owner --uid-owner app_3 \
99 * --jump REJECT --reject-with icmp-net-prohibited
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700100 */
JP Abgralldb7da582011-09-18 12:57:32 -0700101const char *BandwidthController::IPT_CLEANUP_COMMANDS[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700102 /* Cleanup rules. */
103 "-F",
104 "-t raw -F",
JP Abgrall39f8f242011-06-29 19:21:58 -0700105 /* TODO: If at some point we need more user chains than here, then we will need
106 * a different cleanup approach.
107 */
JP Abgrallbfa74662011-06-29 19:23:04 -0700108 "-X", /* Should normally only be costly_shared, penalty_box, and costly_<iface> */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700109};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700110
JP Abgralldb7da582011-09-18 12:57:32 -0700111const char *BandwidthController::IPT_SETUP_COMMANDS[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700112 /* Created needed chains. */
JP Abgrallbfa74662011-06-29 19:23:04 -0700113 "-N costly_shared",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700114 "-N penalty_box",
115};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700116
JP Abgralldb7da582011-09-18 12:57:32 -0700117const char *BandwidthController::IPT_BASIC_ACCOUNTING_COMMANDS[] = {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700118 "-F INPUT",
119 "-A INPUT -i lo --jump ACCEPT",
120 "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700121
JP Abgrall0dad7c22011-06-24 11:58:14 -0700122 "-F OUTPUT",
123 "-A OUTPUT -o lo --jump ACCEPT",
124 "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700125
JP Abgrallbfa74662011-06-29 19:23:04 -0700126 "-F costly_shared",
127 "-A costly_shared --jump penalty_box",
128 "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700129 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
130 * chain. For now, hack the chain exit with an ACCEPT.
131 */
JP Abgrallbfa74662011-06-29 19:23:04 -0700132 "-A costly_shared --jump ACCEPT",
JP Abgrall0dad7c22011-06-24 11:58:14 -0700133};
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700134
135BandwidthController::BandwidthController(void) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700136 char value[PROPERTY_VALUE_MAX];
137
138 property_get("persist.bandwidth.enable", value, "0");
139 if (!strcmp(value, "1")) {
140 enableBandwidthControl();
141 }
142
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700143 property_get("persist.bandwidth.uselogwrap", value, "0");
144 useLogwrapCall = !strcmp(value, "1");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700145}
146
JP Abgrall26e0d492011-06-24 19:21:51 -0700147int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700148 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700149
Steve Block3fb42e02011-10-20 11:55:56 +0100150 ALOGV("runIpxtablesCmd(cmd=%s)", cmd);
JP Abgrall26e0d492011-06-24 19:21:51 -0700151 res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
152 res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700153 return res;
154}
155
JP Abgrall26e0d492011-06-24 19:21:51 -0700156int BandwidthController::StrncpyAndCheck(char *buffer, const char *src, size_t buffSize) {
157
158 memset(buffer, '\0', buffSize); // strncpy() is not filling leftover with '\0'
159 strncpy(buffer, src, buffSize);
160 return buffer[buffSize - 1];
161}
162
JP Abgrall8a932722011-07-13 19:17:35 -0700163int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling,
164 IptIpVer iptVer) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700165 char buffer[MAX_CMD_LEN];
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700166 const char *argv[MAX_CMD_ARGS];
JP Abgrall26e0d492011-06-24 19:21:51 -0700167 int argc = 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700168 char *next = buffer;
169 char *tmp;
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700170 int res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700171
JP Abgrall0dad7c22011-06-24 11:58:14 -0700172 std::string fullCmd = cmd;
JP Abgrall26e0d492011-06-24 19:21:51 -0700173
174 if (rejectHandling == IptRejectAdd) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700175 fullCmd += " --jump REJECT --reject-with";
JP Abgrall26e0d492011-06-24 19:21:51 -0700176 switch (iptVer) {
177 case IptIpV4:
JP Abgrall8a932722011-07-13 19:17:35 -0700178 fullCmd += " icmp-net-prohibited";
179 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700180 case IptIpV6:
JP Abgrall8a932722011-07-13 19:17:35 -0700181 fullCmd += " icmp6-adm-prohibited";
182 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700183 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700184 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700185
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700186 fullCmd.insert(0, " ");
187 fullCmd.insert(0, iptVer == IptIpV4 ? IPTABLES_PATH : IP6TABLES_PATH);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700188
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700189 if (!useLogwrapCall) {
JP Abgrall9e5e0ce2011-12-14 15:20:59 -0800190 res = system_nosh(fullCmd.c_str());
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700191 } else {
192 if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000193 ALOGE("iptables command too long");
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700194 return -1;
195 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700196
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700197 argc = 0;
198 while ((tmp = strsep(&next, " "))) {
199 argv[argc++] = tmp;
200 if (argc >= MAX_CMD_ARGS) {
Steve Block5ea0c052012-01-06 19:18:11 +0000201 ALOGE("iptables argument overflow");
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700202 return -1;
203 }
204 }
205
206 argv[argc] = NULL;
207 res = logwrap(argc, argv, 0);
208 }
209 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000210 ALOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res);
JP Abgrall11b4e9b2011-08-11 15:34:49 -0700211 }
212 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700213}
214
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700215int BandwidthController::enableBandwidthControl(void) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700216 int res;
JP Abgrall8a932722011-07-13 19:17:35 -0700217
JP Abgralldb7da582011-09-18 12:57:32 -0700218 /* Let's pretend we started from scratch ... */
JP Abgrall8a932722011-07-13 19:17:35 -0700219 sharedQuotaIfaces.clear();
220 quotaIfaces.clear();
221 naughtyAppUids.clear();
JP Abgralldb7da582011-09-18 12:57:32 -0700222 globalAlertBytes = 0;
JP Abgrallc6c67342011-10-07 16:28:54 -0700223 globalAlertTetherCount = 0;
JP Abgralldb7da582011-09-18 12:57:32 -0700224 sharedQuotaBytes = sharedAlertBytes = 0;
225
226
227 /* Some of the initialCommands are allowed to fail */
228 runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
229 IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
230 runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*),
231 IPT_SETUP_COMMANDS, RunCmdFailureOk);
232 res = runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
233 IPT_BASIC_ACCOUNTING_COMMANDS, RunCmdFailureBad);
JP Abgrall8a932722011-07-13 19:17:35 -0700234
Kazuhiro Ondo4ab46852012-01-12 16:15:06 -0600235 setupOemIptablesHook();
236
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700237 return res;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700238
239}
240
241int BandwidthController::disableBandwidthControl(void) {
JP Abgralldb7da582011-09-18 12:57:32 -0700242 /* The IPT_CLEANUP_COMMANDS are allowed to fail. */
243 runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
244 IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
Kazuhiro Ondo4ab46852012-01-12 16:15:06 -0600245 setupOemIptablesHook();
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700246 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700247}
248
JP Abgrall8a932722011-07-13 19:17:35 -0700249int BandwidthController::runCommands(int numCommands, const char *commands[],
250 RunCmdErrHandling cmdErrHandling) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700251 int res = 0;
Steve Block3fb42e02011-10-20 11:55:56 +0100252 ALOGV("runCommands(): %d commands", numCommands);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700253 for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700254 res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
255 if (res && cmdErrHandling != RunCmdFailureBad)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700256 return res;
257 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700258 return cmdErrHandling == RunCmdFailureBad ? res : 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700259}
260
JP Abgrall0dad7c22011-06-24 11:58:14 -0700261std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700262 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700263 char *buff;
264 const char *opFlag;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700265
266 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700267 case IptOpInsert:
268 opFlag = "-I";
269 break;
270 case IptOpReplace:
271 opFlag = "-R";
272 break;
273 default:
274 case IptOpDelete:
275 opFlag = "-D";
276 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700277 }
JP Abgrall8a932722011-07-13 19:17:35 -0700278 asprintf(&buff, "%s penalty_box -m owner --uid-owner %d", opFlag, uid);
279 res = buff;
280 free(buff);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700281 return res;
282}
283
284int BandwidthController::addNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700285 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700286}
287
288int BandwidthController::removeNaughtyApps(int numUids, char *appUids[]) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700289 return maninpulateNaughtyApps(numUids, appUids, NaughtyAppOpRemove);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700290}
291
JP Abgrall26e0d492011-06-24 19:21:51 -0700292int BandwidthController::maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700293 char cmd[MAX_CMD_LEN];
294 int uidNum;
JP Abgrall26e0d492011-06-24 19:21:51 -0700295 const char *failLogTemplate;
296 IptOp op;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700297 int appUids[numUids];
JP Abgrall26e0d492011-06-24 19:21:51 -0700298 std::string naughtyCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700299
JP Abgrall26e0d492011-06-24 19:21:51 -0700300 switch (appOp) {
301 case NaughtyAppOpAdd:
JP Abgrall8a932722011-07-13 19:17:35 -0700302 op = IptOpInsert;
303 failLogTemplate = "Failed to add app uid %d to penalty box.";
304 break;
JP Abgrall26e0d492011-06-24 19:21:51 -0700305 case NaughtyAppOpRemove:
JP Abgrall8a932722011-07-13 19:17:35 -0700306 op = IptOpDelete;
307 failLogTemplate = "Failed to delete app uid %d from penalty box.";
308 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700309 }
310
311 for (uidNum = 0; uidNum < numUids; uidNum++) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700312 appUids[uidNum] = atol(appStrUids[uidNum]);
313 if (appUids[uidNum] == 0) {
Steve Block5ea0c052012-01-06 19:18:11 +0000314 ALOGE(failLogTemplate, appUids[uidNum]);
JP Abgrall26e0d492011-06-24 19:21:51 -0700315 goto fail_parse;
316 }
317 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700318
319 for (uidNum = 0; uidNum < numUids; uidNum++) {
320 naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
321 if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
Steve Block5ea0c052012-01-06 19:18:11 +0000322 ALOGE(failLogTemplate, appUids[uidNum]);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700323 goto fail_with_uidNum;
324 }
325 }
326 return 0;
327
JP Abgrall26e0d492011-06-24 19:21:51 -0700328fail_with_uidNum:
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700329 /* Try to remove the uid that failed in any case*/
JP Abgrall26e0d492011-06-24 19:21:51 -0700330 naughtyCmd = makeIptablesNaughtyCmd(IptOpDelete, appUids[uidNum]);
331 runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd);
332fail_parse:
333 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700334}
335
JP Abgrall26e0d492011-06-24 19:21:51 -0700336std::string BandwidthController::makeIptablesQuotaCmd(IptOp op, const char *costName, int64_t quota) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700337 std::string res;
JP Abgrall8a932722011-07-13 19:17:35 -0700338 char *buff;
339 const char *opFlag;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700340
Steve Block3fb42e02011-10-20 11:55:56 +0100341 ALOGV("makeIptablesQuotaCmd(%d, %lld)", op, quota);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700342
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700343 switch (op) {
JP Abgrall8a932722011-07-13 19:17:35 -0700344 case IptOpInsert:
345 opFlag = "-I";
346 break;
347 case IptOpReplace:
348 opFlag = "-R";
349 break;
350 default:
351 case IptOpDelete:
352 opFlag = "-D";
353 break;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700354 }
JP Abgrall8a932722011-07-13 19:17:35 -0700355
JP Abgrallbfa74662011-06-29 19:23:04 -0700356 // The requried IP version specific --jump REJECT ... will be added later.
JP Abgrall8a932722011-07-13 19:17:35 -0700357 asprintf(&buff, "%s costly_%s -m quota2 ! --quota %lld --name %s", opFlag, costName, quota,
358 costName);
359 res = buff;
360 free(buff);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700361 return res;
362}
363
JP Abgrall26e0d492011-06-24 19:21:51 -0700364int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700365 char cmd[MAX_CMD_LEN];
366 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700367 int ruleInsertPos = 1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700368 std::string costString;
369 const char *costCString;
370
JP Abgrall0dad7c22011-06-24 11:58:14 -0700371 /* The "-N costly" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700372 switch (quotaType) {
373 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700374 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700375 costString += ifn;
376 costCString = costString.c_str();
377 snprintf(cmd, sizeof(cmd), "-N %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700378 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700379 snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700380 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700381 snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700382 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700383 /* TODO(jpa): Figure out why iptables doesn't correctly return from this
384 * chain. For now, hack the chain exit with an ACCEPT.
385 */
386 snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700387 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
388 break;
389 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700390 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700391 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700392 }
393
JP Abgrall8a932722011-07-13 19:17:35 -0700394 if (globalAlertBytes) {
395 /* The alert rule comes 1st */
396 ruleInsertPos = 2;
397 }
398 snprintf(cmd, sizeof(cmd), "-I INPUT %d -i %s --goto %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700399 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700400 snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700401 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700402 return res;
403}
404
JP Abgrall26e0d492011-06-24 19:21:51 -0700405int BandwidthController::cleanupCostlyIface(const char *ifn, QuotaType quotaType) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700406 char cmd[MAX_CMD_LEN];
407 int res = 0;
408 std::string costString;
409 const char *costCString;
410
JP Abgrall26e0d492011-06-24 19:21:51 -0700411 switch (quotaType) {
412 case QuotaUnique:
JP Abgrallbfa74662011-06-29 19:23:04 -0700413 costString = "costly_";
JP Abgrall0dad7c22011-06-24 11:58:14 -0700414 costString += ifn;
415 costCString = costString.c_str();
JP Abgrall26e0d492011-06-24 19:21:51 -0700416 break;
417 case QuotaShared:
JP Abgrallbfa74662011-06-29 19:23:04 -0700418 costCString = "costly_shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700419 break;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700420 }
421
422 snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700423 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700424 snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700425 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700426
JP Abgrallbfa74662011-06-29 19:23:04 -0700427 /* The "-N costly_shared" is created upfront, no need to handle it here. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700428 if (quotaType == QuotaUnique) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700429 snprintf(cmd, sizeof(cmd), "-F %s", costCString);
JP Abgrall26e0d492011-06-24 19:21:51 -0700430 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgralla9f802c2011-06-29 15:46:45 -0700431 snprintf(cmd, sizeof(cmd), "-X %s", costCString);
432 res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700433 }
434 return res;
435}
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700436
JP Abgrall0dad7c22011-06-24 11:58:14 -0700437int BandwidthController::setInterfaceSharedQuota(const char *iface, int64_t maxBytes) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700438 char cmd[MAX_CMD_LEN];
439 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700440 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700441 std::string quotaCmd;
JP Abgrall8a932722011-07-13 19:17:35 -0700442 std::string ifaceName;
443 ;
JP Abgrallbfa74662011-06-29 19:23:04 -0700444 const char *costName = "shared";
JP Abgrall26e0d492011-06-24 19:21:51 -0700445 std::list<std::string>::iterator it;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700446
JP Abgrall8a932722011-07-13 19:17:35 -0700447 if (!maxBytes) {
448 /* Don't talk about -1, deprecate it. */
Steve Block5ea0c052012-01-06 19:18:11 +0000449 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700450 return -1;
451 }
JP Abgrall26e0d492011-06-24 19:21:51 -0700452 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000453 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700454 return -1;
455 }
456 ifaceName = ifn;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700457
458 if (maxBytes == -1) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700459 return removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700460 }
461
462 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700463 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
464 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700465 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700466 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700467
JP Abgrall0dad7c22011-06-24 11:58:14 -0700468 if (it == sharedQuotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700469 res |= prepCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700470 if (sharedQuotaIfaces.empty()) {
JP Abgrall0dad7c22011-06-24 11:58:14 -0700471 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700472 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700473 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000474 ALOGE("Failed set quota rule");
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700475 goto fail;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700476 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700477 sharedQuotaBytes = maxBytes;
478 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700479 sharedQuotaIfaces.push_front(ifaceName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700480
481 }
482
483 if (maxBytes != sharedQuotaBytes) {
JP Abgrall8a932722011-07-13 19:17:35 -0700484 res |= updateQuota(costName, maxBytes);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700485 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000486 ALOGE("Failed update quota for %s", costName);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700487 goto fail;
488 }
489 sharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700490 }
491 return 0;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700492
493 fail:
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700494 /*
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700495 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
496 * rules in the kernel to see which ones need cleaning up.
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700497 * For now callers needs to choose if they want to "ndc bandwidth enable"
498 * which resets everything.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700499 */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700500 removeInterfaceSharedQuota(ifn);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700501 return -1;
502}
503
JP Abgrall8a932722011-07-13 19:17:35 -0700504/* It will also cleanup any shared alerts */
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700505int BandwidthController::removeInterfaceSharedQuota(const char *iface) {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700506 char ifn[MAX_IFACENAME_LEN];
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700507 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700508 std::string ifaceName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700509 std::list<std::string>::iterator it;
JP Abgrallbfa74662011-06-29 19:23:04 -0700510 const char *costName = "shared";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700511
JP Abgrall8a932722011-07-13 19:17:35 -0700512 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000513 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700514 return -1;
515 }
JP Abgrall8a932722011-07-13 19:17:35 -0700516 ifaceName = ifn;
JP Abgrall26e0d492011-06-24 19:21:51 -0700517
JP Abgrall0dad7c22011-06-24 11:58:14 -0700518 for (it = sharedQuotaIfaces.begin(); it != sharedQuotaIfaces.end(); it++) {
519 if (*it == ifaceName)
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700520 break;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700521 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700522 if (it == sharedQuotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000523 ALOGE("No such iface %s to delete", ifn);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700524 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700525 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700526
JP Abgrall26e0d492011-06-24 19:21:51 -0700527 res |= cleanupCostlyIface(ifn, QuotaShared);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700528 sharedQuotaIfaces.erase(it);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700529
JP Abgrall0dad7c22011-06-24 11:58:14 -0700530 if (sharedQuotaIfaces.empty()) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700531 std::string quotaCmd;
JP Abgrallbfa74662011-06-29 19:23:04 -0700532 quotaCmd = makeIptablesQuotaCmd(IptOpDelete, costName, sharedQuotaBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700533 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall8a932722011-07-13 19:17:35 -0700534 sharedQuotaBytes = 0;
535 if (sharedAlertBytes) {
536 removeSharedAlert();
537 sharedAlertBytes = 0;
538 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700539 }
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700540 return res;
541}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700542
543int BandwidthController::setInterfaceQuota(const char *iface, int64_t maxBytes) {
544 char ifn[MAX_IFACENAME_LEN];
545 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700546 std::string ifaceName;
547 const char *costName;
548 std::list<QuotaInfo>::iterator it;
549 std::string quotaCmd;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700550
JP Abgrall8a932722011-07-13 19:17:35 -0700551 if (!maxBytes) {
552 /* Don't talk about -1, deprecate it. */
Steve Block5ea0c052012-01-06 19:18:11 +0000553 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700554 return -1;
555 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700556 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700557 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700558 }
559
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 }
564 ifaceName = ifn;
565 costName = iface;
566
JP Abgrall0dad7c22011-06-24 11:58:14 -0700567 /* Insert ingress quota. */
JP Abgrall0dad7c22011-06-24 11:58:14 -0700568 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700569 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700570 break;
571 }
572
573 if (it == quotaIfaces.end()) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700574 res |= prepCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700575 quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
JP Abgrall26e0d492011-06-24 19:21:51 -0700576 res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700577 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000578 ALOGE("Failed set quota rule");
JP Abgrall0dad7c22011-06-24 11:58:14 -0700579 goto fail;
580 }
581
JP Abgrall8a932722011-07-13 19:17:35 -0700582 quotaIfaces.push_front(QuotaInfo(ifaceName, maxBytes, 0));
JP Abgrall0dad7c22011-06-24 11:58:14 -0700583
584 } else {
JP Abgrall8a932722011-07-13 19:17:35 -0700585 res |= updateQuota(costName, maxBytes);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700586 if (res) {
Steve Block5ea0c052012-01-06 19:18:11 +0000587 ALOGE("Failed update quota for %s", iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700588 goto fail;
589 }
JP Abgrall8a932722011-07-13 19:17:35 -0700590 it->quota = maxBytes;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700591 }
592 return 0;
593
594 fail:
595 /*
596 * TODO(jpa): once we get rid of iptables in favor of rtnetlink, reparse
597 * rules in the kernel to see which ones need cleaning up.
598 * For now callers needs to choose if they want to "ndc bandwidth enable"
599 * which resets everything.
600 */
601 removeInterfaceSharedQuota(ifn);
602 return -1;
603}
604
JP Abgrall8a932722011-07-13 19:17:35 -0700605int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
606 return getInterfaceQuota("shared", bytes);
607}
608
609int BandwidthController::getInterfaceQuota(const char *costName, int64_t *bytes) {
610 FILE *fp;
611 char *fname;
612 int scanRes;
613
614 asprintf(&fname, "/proc/net/xt_quota/%s", costName);
615 fp = fopen(fname, "r");
616 free(fname);
617 if (!fp) {
Steve Block5ea0c052012-01-06 19:18:11 +0000618 ALOGE("Reading quota %s failed (%s)", costName, strerror(errno));
JP Abgrall8a932722011-07-13 19:17:35 -0700619 return -1;
620 }
621 scanRes = fscanf(fp, "%lld", bytes);
Steve Block3fb42e02011-10-20 11:55:56 +0100622 ALOGV("Read quota res=%d bytes=%lld", scanRes, *bytes);
JP Abgrall8a932722011-07-13 19:17:35 -0700623 fclose(fp);
624 return scanRes == 1 ? 0 : -1;
625}
626
JP Abgrall0dad7c22011-06-24 11:58:14 -0700627int BandwidthController::removeInterfaceQuota(const char *iface) {
628
629 char ifn[MAX_IFACENAME_LEN];
630 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700631 std::string ifaceName;
632 const char *costName;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700633 std::list<QuotaInfo>::iterator it;
JP Abgrall26e0d492011-06-24 19:21:51 -0700634
JP Abgrall8a932722011-07-13 19:17:35 -0700635 if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
Steve Block5ea0c052012-01-06 19:18:11 +0000636 ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
JP Abgrall26e0d492011-06-24 19:21:51 -0700637 return -1;
638 }
639 ifaceName = ifn;
640 costName = iface;
641
JP Abgrall0dad7c22011-06-24 11:58:14 -0700642 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
JP Abgrall8a932722011-07-13 19:17:35 -0700643 if (it->ifaceName == ifaceName)
JP Abgrall0dad7c22011-06-24 11:58:14 -0700644 break;
645 }
646
647 if (it == quotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000648 ALOGE("No such iface %s to delete", ifn);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700649 return -1;
650 }
651
652 /* This also removes the quota command of CostlyIface chain. */
JP Abgrall26e0d492011-06-24 19:21:51 -0700653 res |= cleanupCostlyIface(ifn, QuotaUnique);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700654
655 quotaIfaces.erase(it);
656
657 return res;
658}
JP Abgrall8a932722011-07-13 19:17:35 -0700659
660int BandwidthController::updateQuota(const char *quotaName, int64_t bytes) {
661 FILE *fp;
662 char *fname;
663
664 asprintf(&fname, "/proc/net/xt_quota/%s", quotaName);
665 fp = fopen(fname, "w");
666 free(fname);
667 if (!fp) {
Steve Block5ea0c052012-01-06 19:18:11 +0000668 ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
JP Abgrall8a932722011-07-13 19:17:35 -0700669 return -1;
670 }
671 fprintf(fp, "%lld\n", bytes);
672 fclose(fp);
673 return 0;
674}
675
676int BandwidthController::runIptablesAlertCmd(IptOp op, const char *alertName, int64_t bytes) {
677 int res = 0;
678 const char *opFlag;
JP Abgrall87666692011-09-08 13:44:10 -0700679 const char *ifaceLimiting;
JP Abgrall8a932722011-07-13 19:17:35 -0700680 char *alertQuotaCmd;
681
682 switch (op) {
683 case IptOpInsert:
684 opFlag = "-I";
685 break;
686 case IptOpReplace:
687 opFlag = "-R";
688 break;
689 default:
690 case IptOpDelete:
691 opFlag = "-D";
692 break;
693 }
694
JP Abgrall87666692011-09-08 13:44:10 -0700695 ifaceLimiting = "! -i lo+";
696 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "INPUT",
697 bytes, alertName, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700698 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
699 free(alertQuotaCmd);
JP Abgrall87666692011-09-08 13:44:10 -0700700 ifaceLimiting = "! -o lo+";
701 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "OUTPUT",
702 bytes, alertName, alertName);
JP Abgrall8a932722011-07-13 19:17:35 -0700703 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
704 free(alertQuotaCmd);
705 return res;
706}
707
JP Abgrallc6c67342011-10-07 16:28:54 -0700708int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const char *alertName, int64_t bytes) {
709 int res = 0;
710 const char *opFlag;
711 const char *ifaceLimiting;
JP Abgrall8a932722011-07-13 19:17:35 -0700712 char *alertQuotaCmd;
JP Abgrallc6c67342011-10-07 16:28:54 -0700713
714 switch (op) {
715 case IptOpInsert:
716 opFlag = "-I";
717 break;
718 case IptOpReplace:
719 opFlag = "-R";
720 break;
721 default:
722 case IptOpDelete:
723 opFlag = "-D";
724 break;
725 }
726
727 ifaceLimiting = "! -i lo+";
728 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "FORWARD",
729 bytes, alertName, alertName);
730 res = runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
731 free(alertQuotaCmd);
732 return res;
733}
734
735int BandwidthController::setGlobalAlert(int64_t bytes) {
736 const char *alertName = ALERT_GLOBAL_NAME;
JP Abgrall8a932722011-07-13 19:17:35 -0700737 int res = 0;
738
739 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000740 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700741 return -1;
742 }
743 if (globalAlertBytes) {
744 res = updateQuota(alertName, bytes);
745 } else {
746 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700747 if (globalAlertTetherCount) {
Steve Block3fb42e02011-10-20 11:55:56 +0100748 ALOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
JP Abgrallc6c67342011-10-07 16:28:54 -0700749 res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
750 }
JP Abgrall8a932722011-07-13 19:17:35 -0700751 }
752 globalAlertBytes = bytes;
753 return res;
754}
755
JP Abgrallc6c67342011-10-07 16:28:54 -0700756int BandwidthController::setGlobalAlertInForwardChain(void) {
757 const char *alertName = ALERT_GLOBAL_NAME;
758 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700759
JP Abgrallc6c67342011-10-07 16:28:54 -0700760 globalAlertTetherCount++;
Steve Block3fb42e02011-10-20 11:55:56 +0100761 ALOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
JP Abgrallc6c67342011-10-07 16:28:54 -0700762
763 /*
764 * If there is no globalAlert active we are done.
765 * If there is an active globalAlert but this is not the 1st
766 * tether, we are also done.
767 */
768 if (!globalAlertBytes || globalAlertTetherCount != 1) {
769 return 0;
770 }
771
772 /* We only add the rule if this was the 1st tether added. */
773 res = runIptablesAlertFwdCmd(IptOpInsert, alertName, globalAlertBytes);
774 return res;
775}
776
777int BandwidthController::removeGlobalAlert(void) {
778
779 const char *alertName = ALERT_GLOBAL_NAME;
JP Abgrall8a932722011-07-13 19:17:35 -0700780 int res = 0;
781
782 if (!globalAlertBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000783 ALOGE("No prior alert set");
JP Abgrall8a932722011-07-13 19:17:35 -0700784 return -1;
785 }
786 res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700787 if (globalAlertTetherCount) {
788 res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
789 }
JP Abgrall8a932722011-07-13 19:17:35 -0700790 globalAlertBytes = 0;
791 return res;
792}
793
JP Abgrallc6c67342011-10-07 16:28:54 -0700794int BandwidthController::removeGlobalAlertInForwardChain(void) {
795 int res = 0;
796 const char *alertName = ALERT_GLOBAL_NAME;
797
798 if (!globalAlertTetherCount) {
Steve Block5ea0c052012-01-06 19:18:11 +0000799 ALOGE("No prior alert set");
JP Abgrallc6c67342011-10-07 16:28:54 -0700800 return -1;
801 }
802
803 globalAlertTetherCount--;
804 /*
805 * If there is no globalAlert active we are done.
806 * If there is an active globalAlert but there are more
807 * tethers, we are also done.
808 */
809 if (!globalAlertBytes || globalAlertTetherCount >= 1) {
810 return 0;
811 }
812
813 /* We only detete the rule if this was the last tether removed. */
814 res = runIptablesAlertFwdCmd(IptOpDelete, alertName, globalAlertBytes);
815 return res;
816}
817
JP Abgrall8a932722011-07-13 19:17:35 -0700818int BandwidthController::setSharedAlert(int64_t bytes) {
819 if (!sharedQuotaBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000820 ALOGE("Need to have a prior shared quota set to set an alert");
JP Abgrall8a932722011-07-13 19:17:35 -0700821 return -1;
822 }
823 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000824 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700825 return -1;
826 }
827 return setCostlyAlert("shared", bytes, &sharedAlertBytes);
828}
829
830int BandwidthController::removeSharedAlert(void) {
831 return removeCostlyAlert("shared", &sharedAlertBytes);
832}
833
834int BandwidthController::setInterfaceAlert(const char *iface, int64_t bytes) {
835 std::list<QuotaInfo>::iterator it;
836
837 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000838 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700839 return -1;
840 }
841 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
842 if (it->ifaceName == iface)
843 break;
844 }
845
846 if (it == quotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000847 ALOGE("Need to have a prior interface quota set to set an alert");
JP Abgrall8a932722011-07-13 19:17:35 -0700848 return -1;
849 }
850
851 return setCostlyAlert(iface, bytes, &it->alert);
852}
853
854int BandwidthController::removeInterfaceAlert(const char *iface) {
855 std::list<QuotaInfo>::iterator it;
856
857 for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
858 if (it->ifaceName == iface)
859 break;
860 }
861
862 if (it == quotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000863 ALOGE("No prior alert set for interface %s", iface);
JP Abgrall8a932722011-07-13 19:17:35 -0700864 return -1;
865 }
866
867 return removeCostlyAlert(iface, &it->alert);
868}
869
870int BandwidthController::setCostlyAlert(const char *costName, int64_t bytes, int64_t *alertBytes) {
871 char *alertQuotaCmd;
872 char *chainNameAndPos;
873 int res = 0;
874 char *alertName;
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 asprintf(&alertName, "%sAlert", costName);
881 if (*alertBytes) {
882 res = updateQuota(alertName, *alertBytes);
883 } else {
884 asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN);
885 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-I", chainNameAndPos, bytes, alertName,
886 alertName);
887 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
888 free(alertQuotaCmd);
889 free(chainNameAndPos);
890 }
891 *alertBytes = bytes;
892 free(alertName);
893 return res;
894}
895
896int BandwidthController::removeCostlyAlert(const char *costName, int64_t *alertBytes) {
897 char *alertQuotaCmd;
898 char *chainName;
899 char *alertName;
900 int res = 0;
901
902 asprintf(&alertName, "%sAlert", costName);
903 if (!*alertBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000904 ALOGE("No prior alert set for %s alert", costName);
JP Abgrall8a932722011-07-13 19:17:35 -0700905 return -1;
906 }
907
908 asprintf(&chainName, "costly_%s", costName);
909 asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName, alertName);
910 res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
911 free(alertQuotaCmd);
912 free(chainName);
913
914 *alertBytes = 0;
915 free(alertName);
916 return res;
917}
JP Abgralldb7da582011-09-18 12:57:32 -0700918
919/*
920 * Parse the ptks and bytes out of:
921 * Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
922 * pkts bytes target prot opt in out source destination
923 * 0 0 ACCEPT all -- rmnet0 wlan0 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
924 * 0 0 DROP all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0 state INVALID
925 * 0 0 ACCEPT all -- wlan0 rmnet0 0.0.0.0/0 0.0.0.0/0
926 *
927 */
JP Abgralla2a64f02011-11-11 20:36:16 -0800928int BandwidthController::parseForwardChainStats(TetherStats &stats, FILE *fp,
929 std::string &extraProcessingInfo) {
JP Abgralldb7da582011-09-18 12:57:32 -0700930 int res;
931 char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
932 char iface0[MAX_IPT_OUTPUT_LINE_LEN];
933 char iface1[MAX_IPT_OUTPUT_LINE_LEN];
934 char rest[MAX_IPT_OUTPUT_LINE_LEN];
935
936 char *buffPtr;
937 int64_t packets, bytes;
938
939 while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
940 /* Clean up, so a failed parse can still print info */
941 iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
942 res = sscanf(buffPtr, "%lld %lld ACCEPT all -- %s %s 0.%s",
943 &packets, &bytes, iface0, iface1, rest);
Steve Block3fb42e02011-10-20 11:55:56 +0100944 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 -0700945 iface0, iface1, packets, bytes, rest, buffPtr);
JP Abgralla2a64f02011-11-11 20:36:16 -0800946 extraProcessingInfo += buffPtr;
947
JP Abgralldb7da582011-09-18 12:57:32 -0700948 if (res != 5) {
949 continue;
950 }
951 if ((stats.ifaceIn == iface0) && (stats.ifaceOut == iface1)) {
Steve Block3fb42e02011-10-20 11:55:56 +0100952 ALOGV("iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
JP Abgralldb7da582011-09-18 12:57:32 -0700953 stats.rxPackets = packets;
954 stats.rxBytes = bytes;
955 } else if ((stats.ifaceOut == iface0) && (stats.ifaceIn == iface1)) {
Steve Block3fb42e02011-10-20 11:55:56 +0100956 ALOGV("iface_in=%s iface_out=%s tx_bytes=%lld tx_packets=%lld ", iface1, iface0, bytes, packets);
JP Abgralldb7da582011-09-18 12:57:32 -0700957 stats.txPackets = packets;
958 stats.txBytes = bytes;
959 }
960 }
961 /* Failure if rx or tx was not found */
962 return (stats.rxBytes == -1 || stats.txBytes == -1) ? -1 : 0;
963}
964
965
966char *BandwidthController::TetherStats::getStatsLine(void) {
967 char *msg;
968 asprintf(&msg, "%s %s %lld %lld %lld %lld", ifaceIn.c_str(), ifaceOut.c_str(),
969 rxBytes, rxPackets, txBytes, txPackets);
970 return msg;
971}
972
JP Abgralla2a64f02011-11-11 20:36:16 -0800973int BandwidthController::getTetherStats(TetherStats &stats, std::string &extraProcessingInfo) {
JP Abgralldb7da582011-09-18 12:57:32 -0700974 int res;
975 std::string fullCmd;
976 FILE *iptOutput;
977 const char *cmd;
978
979 if (stats.rxBytes != -1 || stats.txBytes != -1) {
Steve Block5ea0c052012-01-06 19:18:11 +0000980 ALOGE("Unexpected input stats. Byte counts should be -1.");
JP Abgralldb7da582011-09-18 12:57:32 -0700981 return -1;
982 }
983
984 /*
985 * Why not use some kind of lib to talk to iptables?
986 * Because the only libs are libiptc and libip6tc in iptables, and they are
987 * not easy to use. They require the known iptables match modules to be
988 * preloaded/linked, and require apparently a lot of wrapper code to get
989 * the wanted info.
990 */
991 fullCmd = IPTABLES_PATH;
992 fullCmd += " -nvx -L FORWARD";
993 iptOutput = popen(fullCmd.c_str(), "r");
994 if (!iptOutput) {
Steve Block5ea0c052012-01-06 19:18:11 +0000995 ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
JP Abgralla2a64f02011-11-11 20:36:16 -0800996 extraProcessingInfo += "Failed to run iptables.";
JP Abgralldb7da582011-09-18 12:57:32 -0700997 return -1;
998 }
JP Abgralla2a64f02011-11-11 20:36:16 -0800999 res = parseForwardChainStats(stats, iptOutput, extraProcessingInfo);
JP Abgralldb7da582011-09-18 12:57:32 -07001000 pclose(iptOutput);
1001
1002 /* Currently NatController doesn't do ipv6 tethering, so we are done. */
1003 return res;
1004}