blob: fcbe266fe8b65e16caaa5786ca4898cd8038910d [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 Abgralle4788732013-07-02 20:28:45 -070017// #define LOG_NDEBUG 0
JP Abgralldb7da582011-09-18 12:57:32 -070018
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
Joel Scherpelz01cc5492017-06-16 10:45:14 +090025#include <ctype.h>
JP Abgrall8a932722011-07-13 19:17:35 -070026#include <errno.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070027#include <fcntl.h>
JP Abgralldb7da582011-09-18 12:57:32 -070028#include <stdio.h>
JP Abgrall8a932722011-07-13 19:17:35 -070029#include <stdlib.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070030#include <string.h>
Joel Scherpelz01cc5492017-06-16 10:45:14 +090031#include <string>
32#include <vector>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070033
Matthew Leach2a54d962013-01-14 15:07:12 +000034#define __STDC_FORMAT_MACROS 1
35#include <inttypes.h>
36
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070037#include <sys/socket.h>
38#include <sys/stat.h>
39#include <sys/types.h>
40#include <sys/wait.h>
41
42#include <linux/netlink.h>
43#include <linux/rtnetlink.h>
44#include <linux/pkt_sched.h>
45
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +090046#include "android-base/stringprintf.h"
Lorenzo Colitti13debb82016-03-27 17:46:30 +090047#include "android-base/strings.h"
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070048#define LOG_TAG "BandwidthController"
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070049#include <cutils/properties.h>
Logan Chien3f461482018-04-23 14:31:32 +080050#include <log/log.h>
Rom Lemarchand14150212013-01-24 10:01:04 -080051#include <logwrap/logwrap.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070052
Joel Scherpelz01cc5492017-06-16 10:45:14 +090053#include <netdutils/Syscalls.h>
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070054#include "BandwidthController.h"
Chenbo Feng5ed17992018-03-13 21:30:49 -070055#include "Controllers.h"
Lorenzo Colittiaff28792017-09-26 17:46:18 +090056#include "FirewallController.h" /* For makeCriticalCommands */
Benedict Wongb9baf262017-12-03 15:43:08 -080057#include "Fwmark.h"
Joel Scherpelz01cc5492017-06-16 10:45:14 +090058#include "NetdConstants.h"
Chenbo Feng95892f32018-06-07 14:52:02 -070059#include "TrafficController.h"
Chenbo Feng5ed17992018-03-13 21:30:49 -070060#include "bpf/BpfUtils.h"
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070061
JP Abgralldb7da582011-09-18 12:57:32 -070062/* Alphabetical */
Lorenzo Colitti3c272702017-04-26 15:48:13 +090063#define ALERT_IPT_TEMPLATE "%s %s -m quota2 ! --quota %" PRId64" --name %s\n"
Joel Scherpelzbcad6612017-05-30 10:55:11 +090064const char BandwidthController::LOCAL_INPUT[] = "bw_INPUT";
65const char BandwidthController::LOCAL_FORWARD[] = "bw_FORWARD";
66const char BandwidthController::LOCAL_OUTPUT[] = "bw_OUTPUT";
67const char BandwidthController::LOCAL_RAW_PREROUTING[] = "bw_raw_PREROUTING";
68const char BandwidthController::LOCAL_MANGLE_POSTROUTING[] = "bw_mangle_POSTROUTING";
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +090069
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +090070auto BandwidthController::iptablesRestoreFunction = execIptablesRestoreWithOutput;
Lorenzo Colitti86a47982016-03-18 17:52:25 +090071
Joel Scherpelzd59526a2017-06-28 16:24:09 +090072using android::base::Join;
Lorenzo Colitti3c272702017-04-26 15:48:13 +090073using android::base::StringAppendF;
74using android::base::StringPrintf;
Chenbo Feng95892f32018-06-07 14:52:02 -070075using android::bpf::XT_BPF_BLACKLIST_PROG_PATH;
Chenbo Feng5ed17992018-03-13 21:30:49 -070076using android::bpf::XT_BPF_EGRESS_PROG_PATH;
77using android::bpf::XT_BPF_INGRESS_PROG_PATH;
Chenbo Feng95892f32018-06-07 14:52:02 -070078using android::bpf::XT_BPF_WHITELIST_PROG_PATH;
79using android::net::gCtls;
Joel Scherpelz01cc5492017-06-16 10:45:14 +090080using android::netdutils::StatusOr;
Chenbo Feng95892f32018-06-07 14:52:02 -070081using android::netdutils::Status;
Joel Scherpelz01cc5492017-06-16 10:45:14 +090082using android::netdutils::UniqueFile;
Lorenzo Colitti3c272702017-04-26 15:48:13 +090083
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +090084namespace {
85
86const char ALERT_GLOBAL_NAME[] = "globalAlert";
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +090087const std::string NEW_CHAIN_COMMAND = "-N ";
Lorenzo Colittice6748a2017-02-02 01:34:33 +090088
Joel Scherpelzbcad6612017-05-30 10:55:11 +090089const char NAUGHTY_CHAIN[] = "bw_penalty_box";
90const char NICE_CHAIN[] = "bw_happy_box";
JP Abgralldb7da582011-09-18 12:57:32 -070091
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070092/**
93 * Some comments about the rules:
94 * * Ordering
95 * - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
JP Abgrall29e8de22012-05-03 12:52:15 -070096 * E.g. "-I bw_INPUT -i rmnet0 --jump costly"
JP Abgrall7e51cde2013-07-03 13:33:05 -070097 * - quota'd rules in the costly chain should be before bw_penalty_box lookups.
JP Abgrall29e8de22012-05-03 12:52:15 -070098 * - the qtaguid counting is done at the end of the bw_INPUT/bw_OUTPUT user chains.
JP Abgrall4a5f5ca2011-06-15 18:37:39 -070099 *
100 * * global quota vs per interface quota
101 * - global quota for all costly interfaces uses a single costly chain:
102 * . initial rules
JP Abgrall7e51cde2013-07-03 13:33:05 -0700103 * iptables -N bw_costly_shared
104 * iptables -I bw_INPUT -i iface0 --jump bw_costly_shared
105 * iptables -I bw_OUTPUT -o iface0 --jump bw_costly_shared
106 * iptables -I bw_costly_shared -m quota \! --quota 500000 \
JP Abgrallbfa74662011-06-29 19:23:04 -0700107 * --jump REJECT --reject-with icmp-net-prohibited
JP Abgrall7e51cde2013-07-03 13:33:05 -0700108 * iptables -A bw_costly_shared --jump bw_penalty_box
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +0900109 * iptables -A bw_penalty_box --jump bw_happy_box
Lorenzo Colitti464eabe2016-03-25 13:38:19 +0900110 * iptables -A bw_happy_box --jump bw_data_saver
JP Abgrall8a932722011-07-13 19:17:35 -0700111 *
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700112 * . adding a new iface to this, E.g.:
JP Abgrall7e51cde2013-07-03 13:33:05 -0700113 * iptables -I bw_INPUT -i iface1 --jump bw_costly_shared
114 * iptables -I bw_OUTPUT -o iface1 --jump bw_costly_shared
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700115 *
116 * - quota per interface. This is achieve by having "costly" chains per quota.
117 * E.g. adding a new costly interface iface0 with its own quota:
JP Abgrall7e51cde2013-07-03 13:33:05 -0700118 * iptables -N bw_costly_iface0
119 * iptables -I bw_INPUT -i iface0 --jump bw_costly_iface0
120 * iptables -I bw_OUTPUT -o iface0 --jump bw_costly_iface0
121 * iptables -A bw_costly_iface0 -m quota \! --quota 500000 \
JP Abgralle4788732013-07-02 20:28:45 -0700122 * --jump REJECT --reject-with icmp-port-unreachable
JP Abgrall7e51cde2013-07-03 13:33:05 -0700123 * iptables -A bw_costly_iface0 --jump bw_penalty_box
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700124 *
Lorenzo Colitti464eabe2016-03-25 13:38:19 +0900125 * * Penalty box, happy box and data saver.
126 * - bw_penalty box is a blacklist of apps that are rejected.
127 * - bw_happy_box is a whitelist of apps. It always includes all system apps
128 * - bw_data_saver implements data usage restrictions.
129 * - Via the UI the user can add and remove apps from the whitelist and
130 * blacklist, and turn on/off data saver.
131 * - The blacklist takes precedence over the whitelist and the whitelist
132 * takes precedence over data saver.
133 *
JP Abgrall7e51cde2013-07-03 13:33:05 -0700134 * * bw_penalty_box handling:
135 * - only one bw_penalty_box for all interfaces
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +0900136 * E.g Adding an app:
JP Abgrall7e51cde2013-07-03 13:33:05 -0700137 * iptables -I bw_penalty_box -m owner --uid-owner app_3 \
JP Abgralle4788732013-07-02 20:28:45 -0700138 * --jump REJECT --reject-with icmp-port-unreachable
139 *
JP Abgrall7e51cde2013-07-03 13:33:05 -0700140 * * bw_happy_box handling:
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +0900141 * - The bw_happy_box comes after the penalty box.
JP Abgralle4788732013-07-02 20:28:45 -0700142 * E.g Adding a happy app,
JP Abgrall7e51cde2013-07-03 13:33:05 -0700143 * iptables -I bw_happy_box -m owner --uid-owner app_3 \
JP Abgralle4788732013-07-02 20:28:45 -0700144 * --jump RETURN
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +0900145 *
Lorenzo Colitti464eabe2016-03-25 13:38:19 +0900146 * * bw_data_saver handling:
147 * - The bw_data_saver comes after the happy box.
148 * Enable data saver:
149 * iptables -R 1 bw_data_saver --jump REJECT --reject-with icmp-port-unreachable
150 * Disable data saver:
151 * iptables -R 1 bw_data_saver --jump RETURN
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700152 */
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +0900153
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +0900154const std::string COMMIT_AND_CLOSE = "COMMIT\n";
Chenbo Feng703798e2018-06-15 17:07:59 -0700155const std::string HAPPY_BOX_MATCH_WHITELIST_COMMAND =
156 StringPrintf("-I bw_happy_box -m owner --uid-owner %d-%d --jump RETURN", 0, MAX_SYSTEM_UID);
157const std::string BPF_HAPPY_BOX_MATCH_WHITELIST_COMMAND = StringPrintf(
158 "-I bw_happy_box -m bpf --object-pinned %s -j RETURN", XT_BPF_WHITELIST_PROG_PATH);
159const std::string BPF_PENALTY_BOX_MATCH_BLACKLIST_COMMAND = StringPrintf(
160 "-I bw_penalty_box -m bpf --object-pinned %s -j REJECT", XT_BPF_BLACKLIST_PROG_PATH);
Lorenzo Colitti13debb82016-03-27 17:46:30 +0900161
162static const std::vector<std::string> IPT_FLUSH_COMMANDS = {
JP Abgrall0031cea2012-04-17 16:38:23 -0700163 /*
164 * Cleanup rules.
JP Abgrall7e51cde2013-07-03 13:33:05 -0700165 * Should normally include bw_costly_<iface>, but we rely on the way they are setup
JP Abgrall0031cea2012-04-17 16:38:23 -0700166 * to allow coexistance.
JP Abgrall39f8f242011-06-29 19:21:58 -0700167 */
Lorenzo Colitti13debb82016-03-27 17:46:30 +0900168 "*filter",
169 ":bw_INPUT -",
170 ":bw_OUTPUT -",
171 ":bw_FORWARD -",
172 ":bw_happy_box -",
173 ":bw_penalty_box -",
174 ":bw_data_saver -",
175 ":bw_costly_shared -",
176 "COMMIT",
177 "*raw",
178 ":bw_raw_PREROUTING -",
179 "COMMIT",
180 "*mangle",
181 ":bw_mangle_POSTROUTING -",
182 COMMIT_AND_CLOSE
JP Abgrall0031cea2012-04-17 16:38:23 -0700183};
184
Benedict Wongb9baf262017-12-03 15:43:08 -0800185static const uint32_t uidBillingMask = Fwmark::getUidBillingMask();
186
187/**
188 * Basic commands for creation of hooks into data accounting and data boxes.
189 *
190 * Included in these commands are rules to prevent the double-counting of IPsec
191 * packets. The general overview is as follows:
192 * > All interface counters (counted in PREROUTING, POSTROUTING) must be
193 * completely accurate, and count only the outer packet. As such, the inner
194 * packet must be ignored, which is done through the use of two rules: use
195 * of the policy module (for tunnel mode), and VTI interface checks (for
196 * tunnel or transport-in-tunnel mode). The VTI interfaces should be named
197 * ipsec*
198 * > Outbound UID billing can always be done with the outer packets, due to the
199 * ability to always find the correct UID (based on the skb->sk). As such,
200 * the inner packets should be ignored based on the policy module, or the
201 * output interface if a VTI (ipsec+)
202 * > Inbound UDP-encap-ESP packets can be correctly mapped to the UID that
203 * opened the encap socket, and as such, should be billed as early as
204 * possible (for transport mode; tunnel mode usage should be billed to
205 * sending/receiving application). Due to the inner packet being
206 * indistinguishable from the inner packet of ESP, a uidBillingDone mark
207 * has to be applied to prevent counting a second time.
208 * > Inbound ESP has no socket, and as such must be accounted later. ESP
209 * protocol packets are skipped via a blanket rule.
210 * > Note that this solution is asymmetrical. Adding the VTI or policy matcher
211 * ignore rule in the input chain would actually break the INPUT chain;
212 * Those rules are designed to ignore inner packets, and in the tunnel
213 * mode UDP, or any ESP case, we would not have billed the outer packet.
214 *
215 * See go/ipsec-data-accounting for more information.
216 */
Benedict Wongb9baf262017-12-03 15:43:08 -0800217
Chenbo Feng95892f32018-06-07 14:52:02 -0700218const std::vector<std::string> getBasicAccountingCommands(const bool useBpf) {
Chenbo Feng5ed17992018-03-13 21:30:49 -0700219 const std::vector<std::string> ipt_basic_accounting_commands = {
Chenbo Feng703798e2018-06-15 17:07:59 -0700220 "*filter",
221 // Prevents IPSec double counting (ESP and UDP-encap-ESP respectively)
222 "-A bw_INPUT -p esp -j RETURN",
223 StringPrintf("-A bw_INPUT -m mark --mark 0x%x/0x%x -j RETURN", uidBillingMask,
224 uidBillingMask),
225 "-A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */
226 StringPrintf("-A bw_INPUT -j MARK --or-mark 0x%x", uidBillingMask),
Lorenzo Colitti13debb82016-03-27 17:46:30 +0900227
Chenbo Feng703798e2018-06-15 17:07:59 -0700228 // Prevents IPSec double counting (Tunnel mode and Transport mode,
229 // respectively)
230 "-A bw_OUTPUT -o " IPSEC_IFACE_PREFIX "+ -j RETURN",
231 "-A bw_OUTPUT -m policy --pol ipsec --dir out -j RETURN",
232 "-A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
Lorenzo Colitti13debb82016-03-27 17:46:30 +0900233
Chenbo Feng703798e2018-06-15 17:07:59 -0700234 "-A bw_costly_shared --jump bw_penalty_box",
235 useBpf ? BPF_PENALTY_BOX_MATCH_BLACKLIST_COMMAND : "",
236 "-A bw_penalty_box --jump bw_happy_box", "-A bw_happy_box --jump bw_data_saver",
237 "-A bw_data_saver -j RETURN",
238 useBpf ? BPF_HAPPY_BOX_MATCH_WHITELIST_COMMAND : HAPPY_BOX_MATCH_WHITELIST_COMMAND,
239 "COMMIT",
Chenbo Feng5ed17992018-03-13 21:30:49 -0700240
Chenbo Feng703798e2018-06-15 17:07:59 -0700241 "*raw",
242 // Prevents IPSec double counting (Tunnel mode and Transport mode,
243 // respectively)
244 "-A bw_raw_PREROUTING -i " IPSEC_IFACE_PREFIX "+ -j RETURN",
245 "-A bw_raw_PREROUTING -m policy --pol ipsec --dir in -j RETURN",
246 "-A bw_raw_PREROUTING -m owner --socket-exists", /* This is a tracking rule. */
247 useBpf ? StringPrintf("-A bw_raw_PREROUTING -m bpf --object-pinned %s",
248 XT_BPF_INGRESS_PROG_PATH)
249 : "",
250 "COMMIT",
Chenbo Feng5ed17992018-03-13 21:30:49 -0700251
Chenbo Feng703798e2018-06-15 17:07:59 -0700252 "*mangle",
253 // Prevents IPSec double counting (Tunnel mode and Transport mode,
254 // respectively)
255 "-A bw_mangle_POSTROUTING -o " IPSEC_IFACE_PREFIX "+ -j RETURN",
256 "-A bw_mangle_POSTROUTING -m policy --pol ipsec --dir out -j RETURN",
257 "-A bw_mangle_POSTROUTING -m owner --socket-exists", /* This is a tracking rule. */
258 StringPrintf("-A bw_mangle_POSTROUTING -j MARK --set-mark 0x0/0x%x",
259 uidBillingMask), // Clear the mark before sending this packet
260 useBpf ? StringPrintf("-A bw_mangle_POSTROUTING -m bpf --object-pinned %s",
261 XT_BPF_EGRESS_PROG_PATH)
262 : "",
263 COMMIT_AND_CLOSE};
Chenbo Feng5ed17992018-03-13 21:30:49 -0700264 return ipt_basic_accounting_commands;
265}
266
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +0900267
Bernie Innocenti15bb55c2018-06-03 16:19:51 +0900268std::vector<std::string> toStrVec(int num, const char* const strs[]) {
269 return std::vector<std::string>(strs, strs + num);
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900270}
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +0900271
272} // namespace
273
Chenbo Feng95892f32018-06-07 14:52:02 -0700274bool BandwidthController::getBpfStatus() {
Chenbo Feng33a4de12018-03-16 18:10:07 -0700275 return (access(XT_BPF_INGRESS_PROG_PATH, F_OK) != -1) &&
Chenbo Feng95892f32018-06-07 14:52:02 -0700276 (access(XT_BPF_EGRESS_PROG_PATH, F_OK) != -1) &&
277 (access(XT_BPF_WHITELIST_PROG_PATH, F_OK) != -1) &&
278 (access(XT_BPF_BLACKLIST_PROG_PATH, F_OK) != -1);
Chenbo Fenga121e202018-03-19 11:51:54 -0700279}
280
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900281BandwidthController::BandwidthController() {
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700282}
283
JP Abgrall0e540ec2013-08-26 15:13:10 -0700284void BandwidthController::flushCleanTables(bool doClean) {
285 /* Flush and remove the bw_costly_<iface> tables */
286 flushExistingCostlyTables(doClean);
JP Abgrall0031cea2012-04-17 16:38:23 -0700287
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900288 std::string commands = Join(IPT_FLUSH_COMMANDS, '\n');
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +0900289 iptablesRestoreFunction(V4V6, commands, nullptr);
JP Abgrall0e540ec2013-08-26 15:13:10 -0700290}
JP Abgrall0031cea2012-04-17 16:38:23 -0700291
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900292int BandwidthController::setupIptablesHooks() {
JP Abgrall0e540ec2013-08-26 15:13:10 -0700293 /* flush+clean is allowed to fail */
294 flushCleanTables(true);
JP Abgrall0031cea2012-04-17 16:38:23 -0700295 return 0;
JP Abgrall0031cea2012-04-17 16:38:23 -0700296}
297
298int BandwidthController::enableBandwidthControl(bool force) {
JP Abgrall0031cea2012-04-17 16:38:23 -0700299 char value[PROPERTY_VALUE_MAX];
300
301 if (!force) {
302 property_get("persist.bandwidth.enable", value, "1");
303 if (!strcmp(value, "0"))
304 return 0;
305 }
JP Abgrall8a932722011-07-13 19:17:35 -0700306
JP Abgralldb7da582011-09-18 12:57:32 -0700307 /* Let's pretend we started from scratch ... */
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900308 mSharedQuotaIfaces.clear();
309 mQuotaIfaces.clear();
310 mGlobalAlertBytes = 0;
311 mGlobalAlertTetherCount = 0;
312 mSharedQuotaBytes = mSharedAlertBytes = 0;
JP Abgralldb7da582011-09-18 12:57:32 -0700313
JP Abgrall0e540ec2013-08-26 15:13:10 -0700314 flushCleanTables(false);
Chenbo Feng5ed17992018-03-13 21:30:49 -0700315
Chenbo Feng95892f32018-06-07 14:52:02 -0700316 mBpfSupported = getBpfStatus();
317 std::string commands = Join(getBasicAccountingCommands(mBpfSupported), '\n');
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +0900318 return iptablesRestoreFunction(V4V6, commands, nullptr);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700319}
320
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900321int BandwidthController::disableBandwidthControl() {
JP Abgrall0e540ec2013-08-26 15:13:10 -0700322
323 flushCleanTables(false);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700324 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700325}
326
Lorenzo Colittiaff28792017-09-26 17:46:18 +0900327std::string BandwidthController::makeDataSaverCommand(IptablesTarget target, bool enable) {
328 std::string cmd;
329 const char *chainName = "bw_data_saver";
330 const char *op = jumpToString(enable ? IptJumpReject : IptJumpReturn);
331 std::string criticalCommands = enable ?
332 FirewallController::makeCriticalCommands(target, chainName) : "";
333 StringAppendF(&cmd,
Lorenzo Colitti911bc4c2017-04-28 14:34:01 +0900334 "*filter\n"
Lorenzo Colittiaff28792017-09-26 17:46:18 +0900335 ":%s -\n"
336 "%s"
337 "-A %s%s\n"
338 "COMMIT\n", chainName, criticalCommands.c_str(), chainName, op);
339 return cmd;
340}
341
342int BandwidthController::enableDataSaver(bool enable) {
343 int ret = iptablesRestoreFunction(V4, makeDataSaverCommand(V4, enable), nullptr);
344 ret |= iptablesRestoreFunction(V6, makeDataSaverCommand(V6, enable), nullptr);
345 return ret;
Lorenzo Colitti7618ccb2016-03-18 12:36:03 +0900346}
347
Bernie Innocenti15bb55c2018-06-03 16:19:51 +0900348int BandwidthController::addNaughtyApps(int numUids, const char* const appUids[]) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900349 return manipulateSpecialApps(toStrVec(numUids, appUids), NAUGHTY_CHAIN,
350 IptJumpReject, IptOpInsert);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700351}
352
Bernie Innocenti15bb55c2018-06-03 16:19:51 +0900353int BandwidthController::removeNaughtyApps(int numUids, const char* const appUids[]) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900354 return manipulateSpecialApps(toStrVec(numUids, appUids), NAUGHTY_CHAIN,
355 IptJumpReject, IptOpDelete);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700356}
357
Bernie Innocenti15bb55c2018-06-03 16:19:51 +0900358int BandwidthController::addNiceApps(int numUids, const char* const appUids[]) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900359 return manipulateSpecialApps(toStrVec(numUids, appUids), NICE_CHAIN,
360 IptJumpReturn, IptOpInsert);
JP Abgralle4788732013-07-02 20:28:45 -0700361}
362
Bernie Innocenti15bb55c2018-06-03 16:19:51 +0900363int BandwidthController::removeNiceApps(int numUids, const char* const appUids[]) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900364 return manipulateSpecialApps(toStrVec(numUids, appUids), NICE_CHAIN,
365 IptJumpReturn, IptOpDelete);
JP Abgralle4788732013-07-02 20:28:45 -0700366}
367
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900368int BandwidthController::manipulateSpecialApps(const std::vector<std::string>& appStrUids,
369 const std::string& chain, IptJumpOp jumpHandling,
370 IptOp op) {
Chenbo Feng95892f32018-06-07 14:52:02 -0700371 if (mBpfSupported) {
Chenbo Feng703798e2018-06-15 17:07:59 -0700372 Status status = gCtls->trafficCtrl.updateUidOwnerMap(appStrUids, jumpHandling, op);
373 if (!isOk(status)) {
374 ALOGE("unable to update the Bandwidth Uid Map: %s", toString(status).c_str());
Chenbo Feng95892f32018-06-07 14:52:02 -0700375 }
376 return status.code();
377 }
Lorenzo Colitti911bc4c2017-04-28 14:34:01 +0900378 std::string cmd = "*filter\n";
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900379 for (const auto& appStrUid : appStrUids) {
380 StringAppendF(&cmd, "%s %s -m owner --uid-owner %s%s\n", opToString(op), chain.c_str(),
381 appStrUid.c_str(), jumpToString(jumpHandling));
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700382 }
Lorenzo Colitti911bc4c2017-04-28 14:34:01 +0900383 StringAppendF(&cmd, "COMMIT\n");
384 return iptablesRestoreFunction(V4V6, cmd, nullptr);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700385}
386
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900387int BandwidthController::setInterfaceSharedQuota(const std::string& iface, int64_t maxBytes) {
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700388 int res = 0;
JP Abgrall26e0d492011-06-24 19:21:51 -0700389 std::string quotaCmd;
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900390 constexpr char cost[] = "shared";
391 constexpr char chain[] = "bw_costly_shared";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700392
JP Abgrall8a932722011-07-13 19:17:35 -0700393 if (!maxBytes) {
394 /* Don't talk about -1, deprecate it. */
Steve Block5ea0c052012-01-06 19:18:11 +0000395 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700396 return -1;
397 }
JP Abgrall69261cb2014-06-19 18:35:24 -0700398 if (!isIfaceName(iface))
399 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700400
401 if (maxBytes == -1) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900402 return removeInterfaceSharedQuota(iface);
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700403 }
404
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900405 auto it = mSharedQuotaIfaces.find(iface);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700406
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900407 if (it == mSharedQuotaIfaces.end()) {
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900408 const int ruleInsertPos = (mGlobalAlertBytes) ? 2 : 1;
409 std::vector<std::string> cmds = {
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900410 "*filter",
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900411 StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, iface.c_str(), chain),
412 StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, iface.c_str(), chain),
Erik Kline51eb3242017-09-20 18:30:47 +0900413 StringPrintf("-A bw_FORWARD -i %s --jump %s", iface.c_str(), chain),
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900414 StringPrintf("-A bw_FORWARD -o %s --jump %s", iface.c_str(), chain),
415 };
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900416 if (mSharedQuotaIfaces.empty()) {
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900417 cmds.push_back(StringPrintf("-I %s -m quota2 ! --quota %" PRId64
418 " --name %s --jump REJECT",
419 chain, maxBytes, cost));
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900420 }
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900421 cmds.push_back("COMMIT\n");
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900422
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900423 res |= iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr);
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900424 if (res) {
425 ALOGE("Failed set quota rule");
426 removeInterfaceSharedQuota(iface);
427 return -1;
428 }
429 mSharedQuotaBytes = maxBytes;
430 mSharedQuotaIfaces.insert(iface);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700431 }
432
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900433 if (maxBytes != mSharedQuotaBytes) {
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900434 res |= updateQuota(cost, maxBytes);
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700435 if (res) {
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900436 ALOGE("Failed update quota for %s", cost);
437 removeInterfaceSharedQuota(iface);
438 return -1;
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700439 }
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900440 mSharedQuotaBytes = maxBytes;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700441 }
442 return 0;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700443}
444
JP Abgrall8a932722011-07-13 19:17:35 -0700445/* It will also cleanup any shared alerts */
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900446int BandwidthController::removeInterfaceSharedQuota(const std::string& iface) {
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900447 constexpr char cost[] = "shared";
448 constexpr char chain[] = "bw_costly_shared";
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700449
JP Abgrall69261cb2014-06-19 18:35:24 -0700450 if (!isIfaceName(iface))
451 return -1;
JP Abgrall26e0d492011-06-24 19:21:51 -0700452
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900453 auto it = mSharedQuotaIfaces.find(iface);
454
455 if (it == mSharedQuotaIfaces.end()) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900456 ALOGE("No such iface %s to delete", iface.c_str());
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700457 return -1;
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700458 }
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700459
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900460 std::vector<std::string> cmds = {
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900461 "*filter",
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900462 StringPrintf("-D bw_INPUT -i %s --jump %s", iface.c_str(), chain),
463 StringPrintf("-D bw_OUTPUT -o %s --jump %s", iface.c_str(), chain),
Erik Kline51eb3242017-09-20 18:30:47 +0900464 StringPrintf("-D bw_FORWARD -i %s --jump %s", iface.c_str(), chain),
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900465 StringPrintf("-D bw_FORWARD -o %s --jump %s", iface.c_str(), chain),
466 };
Lorenzo Colittib7ac3f72017-07-06 16:52:52 +0900467 if (mSharedQuotaIfaces.size() == 1) {
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900468 cmds.push_back(StringPrintf("-D %s -m quota2 ! --quota %" PRIu64
469 " --name %s --jump REJECT",
470 chain, mSharedQuotaBytes, cost));
JP Abgrallfa6f46d2011-06-17 23:17:28 -0700471 }
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900472 cmds.push_back("COMMIT\n");
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900473
Lorenzo Colittib7ac3f72017-07-06 16:52:52 +0900474 if (iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr) != 0) {
475 ALOGE("Failed to remove shared quota on %s", iface.c_str());
476 return -1;
477 }
478
479 int res = 0;
480 mSharedQuotaIfaces.erase(it);
481 if (mSharedQuotaIfaces.empty()) {
482 mSharedQuotaBytes = 0;
483 if (mSharedAlertBytes) {
484 res = removeSharedAlert();
485 if (res == 0) {
486 mSharedAlertBytes = 0;
487 }
488 }
489 }
490
491 return res;
492
JP Abgrall4a5f5ca2011-06-15 18:37:39 -0700493}
JP Abgrall0dad7c22011-06-24 11:58:14 -0700494
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900495int BandwidthController::setInterfaceQuota(const std::string& iface, int64_t maxBytes) {
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900496 const std::string& cost = iface;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700497
JP Abgrall69261cb2014-06-19 18:35:24 -0700498 if (!isIfaceName(iface))
Nick Kralevich0b2b9022014-05-01 13:10:45 -0700499 return -1;
Nick Kralevich0b2b9022014-05-01 13:10:45 -0700500
JP Abgrall8a932722011-07-13 19:17:35 -0700501 if (!maxBytes) {
502 /* Don't talk about -1, deprecate it. */
Steve Block5ea0c052012-01-06 19:18:11 +0000503 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700504 return -1;
505 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700506 if (maxBytes == -1) {
JP Abgrall26e0d492011-06-24 19:21:51 -0700507 return removeInterfaceQuota(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700508 }
509
JP Abgrall0dad7c22011-06-24 11:58:14 -0700510 /* Insert ingress quota. */
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900511 auto it = mQuotaIfaces.find(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700512
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900513 if (it != mQuotaIfaces.end()) {
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900514 if (updateQuota(cost, maxBytes) != 0) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900515 ALOGE("Failed update quota for %s", iface.c_str());
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900516 removeInterfaceQuota(iface);
517 return -1;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700518 }
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900519 it->second.quota = maxBytes;
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900520 return 0;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700521 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700522
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900523 const std::string chain = "bw_costly_" + iface;
524 const int ruleInsertPos = (mGlobalAlertBytes) ? 2 : 1;
525 std::vector<std::string> cmds = {
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900526 "*filter",
527 StringPrintf(":%s -", chain.c_str()),
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900528 StringPrintf("-A %s -j bw_penalty_box", chain.c_str()),
529 StringPrintf("-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, iface.c_str(),
530 chain.c_str()),
531 StringPrintf("-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, iface.c_str(),
532 chain.c_str()),
Erik Kline51eb3242017-09-20 18:30:47 +0900533 StringPrintf("-A bw_FORWARD -i %s --jump %s", iface.c_str(), chain.c_str()),
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900534 StringPrintf("-A bw_FORWARD -o %s --jump %s", iface.c_str(), chain.c_str()),
535 StringPrintf("-A %s -m quota2 ! --quota %" PRId64 " --name %s --jump REJECT",
536 chain.c_str(), maxBytes, cost.c_str()),
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900537 "COMMIT\n",
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900538 };
539
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900540 if (iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr) != 0) {
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900541 ALOGE("Failed set quota rule");
542 removeInterfaceQuota(iface);
543 return -1;
544 }
545
546 mQuotaIfaces[iface] = QuotaInfo{maxBytes, 0};
547 return 0;
JP Abgrall0dad7c22011-06-24 11:58:14 -0700548}
549
JP Abgrall8a932722011-07-13 19:17:35 -0700550int BandwidthController::getInterfaceSharedQuota(int64_t *bytes) {
551 return getInterfaceQuota("shared", bytes);
552}
553
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900554int BandwidthController::getInterfaceQuota(const std::string& iface, int64_t* bytes) {
Joel Scherpelz01cc5492017-06-16 10:45:14 +0900555 const auto& sys = android::netdutils::sSyscalls.get();
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900556 const std::string fname = "/proc/net/xt_quota/" + iface;
JP Abgrall8a932722011-07-13 19:17:35 -0700557
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900558 if (!isIfaceName(iface)) return -1;
Nick Kralevich0b2b9022014-05-01 13:10:45 -0700559
Joel Scherpelz01cc5492017-06-16 10:45:14 +0900560 StatusOr<UniqueFile> file = sys.fopen(fname, "re");
561 if (!isOk(file)) {
562 ALOGE("Reading quota %s failed (%s)", iface.c_str(), toString(file).c_str());
JP Abgrall8a932722011-07-13 19:17:35 -0700563 return -1;
564 }
Joel Scherpelz01cc5492017-06-16 10:45:14 +0900565 auto rv = sys.fscanf(file.value().get(), "%" SCNd64, bytes);
566 if (!isOk(rv)) {
567 ALOGE("Reading quota %s failed (%s)", iface.c_str(), toString(rv).c_str());
568 return -1;
569 }
570 ALOGV("Read quota res=%d bytes=%" PRId64, rv.value(), *bytes);
571 return rv.value() == 1 ? 0 : -1;
JP Abgrall8a932722011-07-13 19:17:35 -0700572}
573
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900574int BandwidthController::removeInterfaceQuota(const std::string& iface) {
JP Abgrall69261cb2014-06-19 18:35:24 -0700575 if (!isIfaceName(iface))
Nick Kralevich0b2b9022014-05-01 13:10:45 -0700576 return -1;
JP Abgrall26e0d492011-06-24 19:21:51 -0700577
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900578 auto it = mQuotaIfaces.find(iface);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700579
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900580 if (it == mQuotaIfaces.end()) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900581 ALOGE("No such iface %s to delete", iface.c_str());
JP Abgrall0dad7c22011-06-24 11:58:14 -0700582 return -1;
583 }
584
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900585 const std::string chain = "bw_costly_" + iface;
586 std::vector<std::string> cmds = {
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900587 "*filter",
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900588 StringPrintf("-D bw_INPUT -i %s --jump %s", iface.c_str(), chain.c_str()),
589 StringPrintf("-D bw_OUTPUT -o %s --jump %s", iface.c_str(), chain.c_str()),
Erik Kline51eb3242017-09-20 18:30:47 +0900590 StringPrintf("-D bw_FORWARD -i %s --jump %s", iface.c_str(), chain.c_str()),
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900591 StringPrintf("-D bw_FORWARD -o %s --jump %s", iface.c_str(), chain.c_str()),
592 StringPrintf("-F %s", chain.c_str()),
593 StringPrintf("-X %s", chain.c_str()),
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900594 "COMMIT\n",
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900595 };
Lorenzo Colitti48f83002017-07-06 15:06:04 +0900596
597 const int res = iptablesRestoreFunction(V4V6, Join(cmds, "\n"), nullptr);
JP Abgrall0dad7c22011-06-24 11:58:14 -0700598
Lorenzo Colittib7ac3f72017-07-06 16:52:52 +0900599 if (res == 0) {
600 mQuotaIfaces.erase(it);
601 }
JP Abgrall0dad7c22011-06-24 11:58:14 -0700602
603 return res;
604}
JP Abgrall8a932722011-07-13 19:17:35 -0700605
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900606int BandwidthController::updateQuota(const std::string& quotaName, int64_t bytes) {
Joel Scherpelz01cc5492017-06-16 10:45:14 +0900607 const auto& sys = android::netdutils::sSyscalls.get();
608 const std::string fname = "/proc/net/xt_quota/" + quotaName;
JP Abgrall8a932722011-07-13 19:17:35 -0700609
JP Abgrall69261cb2014-06-19 18:35:24 -0700610 if (!isIfaceName(quotaName)) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900611 ALOGE("updateQuota: Invalid quotaName \"%s\"", quotaName.c_str());
Nick Kralevich0b2b9022014-05-01 13:10:45 -0700612 return -1;
613 }
614
Joel Scherpelz01cc5492017-06-16 10:45:14 +0900615 StatusOr<UniqueFile> file = sys.fopen(fname, "we");
616 if (!isOk(file)) {
617 ALOGE("Updating quota %s failed (%s)", quotaName.c_str(), toString(file).c_str());
JP Abgrall8a932722011-07-13 19:17:35 -0700618 return -1;
619 }
Joel Scherpelz01cc5492017-06-16 10:45:14 +0900620 sys.fprintf(file.value().get(), "%" PRId64 "\n", bytes);
JP Abgrall8a932722011-07-13 19:17:35 -0700621 return 0;
622}
623
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900624int BandwidthController::runIptablesAlertCmd(IptOp op, const std::string& alertName,
625 int64_t bytes) {
Lorenzo Colittid9db08c2017-04-28 11:06:40 +0900626 const char *opFlag = opToString(op);
Lorenzo Colitti3c272702017-04-26 15:48:13 +0900627 std::string alertQuotaCmd = "*filter\n";
JP Abgrall8a932722011-07-13 19:17:35 -0700628
Lorenzo Colitti3c272702017-04-26 15:48:13 +0900629 // TODO: consider using an alternate template for the delete that does not include the --quota
630 // value. This code works because the --quota value is ignored by deletes
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900631 StringAppendF(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_INPUT", bytes,
632 alertName.c_str());
633 StringAppendF(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_OUTPUT", bytes,
634 alertName.c_str());
Lorenzo Colitti3c272702017-04-26 15:48:13 +0900635 StringAppendF(&alertQuotaCmd, "COMMIT\n");
636
Lorenzo Colitti4773cb42017-04-27 14:03:25 +0900637 return iptablesRestoreFunction(V4V6, alertQuotaCmd, nullptr);
JP Abgrall8a932722011-07-13 19:17:35 -0700638}
639
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900640int BandwidthController::runIptablesAlertFwdCmd(IptOp op, const std::string& alertName,
641 int64_t bytes) {
Lorenzo Colittid9db08c2017-04-28 11:06:40 +0900642 const char *opFlag = opToString(op);
Lorenzo Colitti3c272702017-04-26 15:48:13 +0900643 std::string alertQuotaCmd = "*filter\n";
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900644 StringAppendF(&alertQuotaCmd, ALERT_IPT_TEMPLATE, opFlag, "bw_FORWARD", bytes,
645 alertName.c_str());
Lorenzo Colitti3c272702017-04-26 15:48:13 +0900646 StringAppendF(&alertQuotaCmd, "COMMIT\n");
647
648 return iptablesRestoreFunction(V4V6, alertQuotaCmd, nullptr);
JP Abgrallc6c67342011-10-07 16:28:54 -0700649}
650
651int BandwidthController::setGlobalAlert(int64_t bytes) {
652 const char *alertName = ALERT_GLOBAL_NAME;
JP Abgrall8a932722011-07-13 19:17:35 -0700653 int res = 0;
654
655 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000656 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700657 return -1;
658 }
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900659 if (mGlobalAlertBytes) {
JP Abgrall8a932722011-07-13 19:17:35 -0700660 res = updateQuota(alertName, bytes);
661 } else {
662 res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900663 if (mGlobalAlertTetherCount) {
664 ALOGV("setGlobalAlert for %d tether", mGlobalAlertTetherCount);
JP Abgrallc6c67342011-10-07 16:28:54 -0700665 res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
666 }
JP Abgrall8a932722011-07-13 19:17:35 -0700667 }
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900668 mGlobalAlertBytes = bytes;
JP Abgrall8a932722011-07-13 19:17:35 -0700669 return res;
670}
671
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900672int BandwidthController::setGlobalAlertInForwardChain() {
JP Abgrallc6c67342011-10-07 16:28:54 -0700673 const char *alertName = ALERT_GLOBAL_NAME;
674 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700675
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900676 mGlobalAlertTetherCount++;
677 ALOGV("setGlobalAlertInForwardChain(): %d tether", mGlobalAlertTetherCount);
JP Abgrallc6c67342011-10-07 16:28:54 -0700678
679 /*
680 * If there is no globalAlert active we are done.
681 * If there is an active globalAlert but this is not the 1st
682 * tether, we are also done.
683 */
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900684 if (!mGlobalAlertBytes || mGlobalAlertTetherCount != 1) {
JP Abgrallc6c67342011-10-07 16:28:54 -0700685 return 0;
686 }
687
688 /* We only add the rule if this was the 1st tether added. */
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900689 res = runIptablesAlertFwdCmd(IptOpInsert, alertName, mGlobalAlertBytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700690 return res;
691}
692
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900693int BandwidthController::removeGlobalAlert() {
JP Abgrallc6c67342011-10-07 16:28:54 -0700694
695 const char *alertName = ALERT_GLOBAL_NAME;
JP Abgrall8a932722011-07-13 19:17:35 -0700696 int res = 0;
697
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900698 if (!mGlobalAlertBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000699 ALOGE("No prior alert set");
JP Abgrall8a932722011-07-13 19:17:35 -0700700 return -1;
701 }
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900702 res = runIptablesAlertCmd(IptOpDelete, alertName, mGlobalAlertBytes);
703 if (mGlobalAlertTetherCount) {
704 res |= runIptablesAlertFwdCmd(IptOpDelete, alertName, mGlobalAlertBytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700705 }
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900706 mGlobalAlertBytes = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700707 return res;
708}
709
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900710int BandwidthController::removeGlobalAlertInForwardChain() {
JP Abgrallc6c67342011-10-07 16:28:54 -0700711 int res = 0;
712 const char *alertName = ALERT_GLOBAL_NAME;
713
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900714 if (!mGlobalAlertTetherCount) {
Steve Block5ea0c052012-01-06 19:18:11 +0000715 ALOGE("No prior alert set");
JP Abgrallc6c67342011-10-07 16:28:54 -0700716 return -1;
717 }
718
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900719 mGlobalAlertTetherCount--;
JP Abgrallc6c67342011-10-07 16:28:54 -0700720 /*
721 * If there is no globalAlert active we are done.
722 * If there is an active globalAlert but there are more
723 * tethers, we are also done.
724 */
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900725 if (!mGlobalAlertBytes || mGlobalAlertTetherCount >= 1) {
JP Abgrallc6c67342011-10-07 16:28:54 -0700726 return 0;
727 }
728
729 /* We only detete the rule if this was the last tether removed. */
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900730 res = runIptablesAlertFwdCmd(IptOpDelete, alertName, mGlobalAlertBytes);
JP Abgrallc6c67342011-10-07 16:28:54 -0700731 return res;
732}
733
JP Abgrall8a932722011-07-13 19:17:35 -0700734int BandwidthController::setSharedAlert(int64_t bytes) {
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900735 if (!mSharedQuotaBytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000736 ALOGE("Need to have a prior shared quota set to set an alert");
JP Abgrall8a932722011-07-13 19:17:35 -0700737 return -1;
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 }
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900743 return setCostlyAlert("shared", bytes, &mSharedAlertBytes);
JP Abgrall8a932722011-07-13 19:17:35 -0700744}
745
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900746int BandwidthController::removeSharedAlert() {
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900747 return removeCostlyAlert("shared", &mSharedAlertBytes);
JP Abgrall8a932722011-07-13 19:17:35 -0700748}
749
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900750int BandwidthController::setInterfaceAlert(const std::string& iface, int64_t bytes) {
JP Abgrall69261cb2014-06-19 18:35:24 -0700751 if (!isIfaceName(iface)) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900752 ALOGE("setInterfaceAlert: Invalid iface \"%s\"", iface.c_str());
Nick Kralevich0b2b9022014-05-01 13:10:45 -0700753 return -1;
754 }
755
JP Abgrall8a932722011-07-13 19:17:35 -0700756 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000757 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700758 return -1;
759 }
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900760 auto it = mQuotaIfaces.find(iface);
JP Abgrall8a932722011-07-13 19:17:35 -0700761
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900762 if (it == mQuotaIfaces.end()) {
Steve Block5ea0c052012-01-06 19:18:11 +0000763 ALOGE("Need to have a prior interface quota set to set an alert");
JP Abgrall8a932722011-07-13 19:17:35 -0700764 return -1;
765 }
766
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900767 return setCostlyAlert(iface, bytes, &it->second.alert);
JP Abgrall8a932722011-07-13 19:17:35 -0700768}
769
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900770int BandwidthController::removeInterfaceAlert(const std::string& iface) {
JP Abgrall69261cb2014-06-19 18:35:24 -0700771 if (!isIfaceName(iface)) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900772 ALOGE("removeInterfaceAlert: Invalid iface \"%s\"", iface.c_str());
Nick Kralevich0b2b9022014-05-01 13:10:45 -0700773 return -1;
774 }
775
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900776 auto it = mQuotaIfaces.find(iface);
JP Abgrall8a932722011-07-13 19:17:35 -0700777
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900778 if (it == mQuotaIfaces.end()) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900779 ALOGE("No prior alert set for interface %s", iface.c_str());
JP Abgrall8a932722011-07-13 19:17:35 -0700780 return -1;
781 }
782
Joel Scherpelzced1dd92017-06-28 10:19:52 +0900783 return removeCostlyAlert(iface, &it->second.alert);
JP Abgrall8a932722011-07-13 19:17:35 -0700784}
785
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900786int BandwidthController::setCostlyAlert(const std::string& costName, int64_t bytes,
787 int64_t* alertBytes) {
JP Abgrall8a932722011-07-13 19:17:35 -0700788 int res = 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700789
JP Abgrall69261cb2014-06-19 18:35:24 -0700790 if (!isIfaceName(costName)) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900791 ALOGE("setCostlyAlert: Invalid costName \"%s\"", costName.c_str());
Nick Kralevich0b2b9022014-05-01 13:10:45 -0700792 return -1;
793 }
794
JP Abgrall8a932722011-07-13 19:17:35 -0700795 if (!bytes) {
Steve Block5ea0c052012-01-06 19:18:11 +0000796 ALOGE("Invalid bytes value. 1..max_int64.");
JP Abgrall8a932722011-07-13 19:17:35 -0700797 return -1;
798 }
Lorenzo Colittie85ffe12017-07-06 17:25:37 +0900799
800 std::string alertName = costName + "Alert";
801 std::string chainName = "bw_costly_" + costName;
JP Abgrall8a932722011-07-13 19:17:35 -0700802 if (*alertBytes) {
803 res = updateQuota(alertName, *alertBytes);
804 } else {
Lorenzo Colittie85ffe12017-07-06 17:25:37 +0900805 std::vector<std::string> commands = {
806 "*filter\n",
807 StringPrintf(ALERT_IPT_TEMPLATE, "-A", chainName.c_str(), bytes, alertName.c_str()),
808 "COMMIT\n"
809 };
810 res = iptablesRestoreFunction(V4V6, Join(commands, ""), nullptr);
811 if (res) {
812 ALOGE("Failed to set costly alert for %s", costName.c_str());
813 }
JP Abgrall8a932722011-07-13 19:17:35 -0700814 }
Lorenzo Colittie85ffe12017-07-06 17:25:37 +0900815 if (res == 0) {
816 *alertBytes = bytes;
817 }
JP Abgrall8a932722011-07-13 19:17:35 -0700818 return res;
819}
820
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900821int BandwidthController::removeCostlyAlert(const std::string& costName, int64_t* alertBytes) {
JP Abgrall69261cb2014-06-19 18:35:24 -0700822 if (!isIfaceName(costName)) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900823 ALOGE("removeCostlyAlert: Invalid costName \"%s\"", costName.c_str());
Nick Kralevich0b2b9022014-05-01 13:10:45 -0700824 return -1;
825 }
826
JP Abgrall8a932722011-07-13 19:17:35 -0700827 if (!*alertBytes) {
Joel Scherpelzbcad6612017-05-30 10:55:11 +0900828 ALOGE("No prior alert set for %s alert", costName.c_str());
JP Abgrall8a932722011-07-13 19:17:35 -0700829 return -1;
830 }
831
Lorenzo Colittie85ffe12017-07-06 17:25:37 +0900832 std::string alertName = costName + "Alert";
833 std::string chainName = "bw_costly_" + costName;
834 std::vector<std::string> commands = {
835 "*filter\n",
836 StringPrintf(ALERT_IPT_TEMPLATE, "-D", chainName.c_str(), *alertBytes, alertName.c_str()),
837 "COMMIT\n"
838 };
839 if (iptablesRestoreFunction(V4V6, Join(commands, ""), nullptr) != 0) {
840 ALOGE("Failed to remove costly alert %s", costName.c_str());
841 return -1;
842 }
JP Abgrall8a932722011-07-13 19:17:35 -0700843
844 *alertBytes = 0;
Lorenzo Colittie85ffe12017-07-06 17:25:37 +0900845 return 0;
JP Abgrall8a932722011-07-13 19:17:35 -0700846}
JP Abgralldb7da582011-09-18 12:57:32 -0700847
JP Abgrall0e540ec2013-08-26 15:13:10 -0700848void BandwidthController::flushExistingCostlyTables(bool doClean) {
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +0900849 std::string fullCmd = "*filter\n-S\nCOMMIT\n";
850 std::string ruleList;
JP Abgrall0e540ec2013-08-26 15:13:10 -0700851
852 /* Only lookup ip4 table names as ip6 will have the same tables ... */
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +0900853 if (int ret = iptablesRestoreFunction(V4, fullCmd, &ruleList)) {
854 ALOGE("Failed to list existing costly tables ret=%d", ret);
JP Abgrall0e540ec2013-08-26 15:13:10 -0700855 return;
856 }
857 /* ... then flush/clean both ip4 and ip6 iptables. */
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +0900858 parseAndFlushCostlyTables(ruleList, doClean);
JP Abgrall0e540ec2013-08-26 15:13:10 -0700859}
860
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +0900861void BandwidthController::parseAndFlushCostlyTables(const std::string& ruleList, bool doRemove) {
862 std::stringstream stream(ruleList);
863 std::string rule;
864 std::vector<std::string> clearCommands = { "*filter" };
865 std::string chainName;
JP Abgrall0e540ec2013-08-26 15:13:10 -0700866
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +0900867 // Find and flush all rules starting with "-N bw_costly_<iface>" except "-N bw_costly_shared".
868 while (std::getline(stream, rule, '\n')) {
869 if (rule.find(NEW_CHAIN_COMMAND) != 0) continue;
870 chainName = rule.substr(NEW_CHAIN_COMMAND.size());
871 ALOGV("parse chainName=<%s> orig line=<%s>", chainName.c_str(), rule.c_str());
872
873 if (chainName.find("bw_costly_") != 0 || chainName == std::string("bw_costly_shared")) {
JP Abgrall0e540ec2013-08-26 15:13:10 -0700874 continue;
875 }
876
Lorenzo Colitti3c272702017-04-26 15:48:13 +0900877 clearCommands.push_back(StringPrintf(":%s -", chainName.c_str()));
JP Abgrall0e540ec2013-08-26 15:13:10 -0700878 if (doRemove) {
Lorenzo Colitti3c272702017-04-26 15:48:13 +0900879 clearCommands.push_back(StringPrintf("-X %s", chainName.c_str()));
JP Abgrall0e540ec2013-08-26 15:13:10 -0700880 }
881 }
Lorenzo Colitti56c4b1e2017-02-01 02:45:10 +0900882
883 if (clearCommands.size() == 1) {
884 // No rules found.
885 return;
886 }
887
888 clearCommands.push_back("COMMIT\n");
Joel Scherpelzd59526a2017-06-28 16:24:09 +0900889 iptablesRestoreFunction(V4V6, Join(clearCommands, '\n'), nullptr);
JP Abgrall0e540ec2013-08-26 15:13:10 -0700890}
Lorenzo Colittid9db08c2017-04-28 11:06:40 +0900891
892inline const char *BandwidthController::opToString(IptOp op) {
893 switch (op) {
894 case IptOpInsert:
895 return "-I";
896 case IptOpDelete:
897 return "-D";
898 }
899}
900
901inline const char *BandwidthController::jumpToString(IptJumpOp jumpHandling) {
902 /*
903 * Must be careful what one rejects with, as upper layer protocols will just
904 * keep on hammering the device until the number of retries are done.
905 * For port-unreachable (default), TCP should consider as an abort (RFC1122).
906 */
907 switch (jumpHandling) {
908 case IptJumpNoAdd:
909 return "";
910 case IptJumpReject:
911 return " --jump REJECT";
912 case IptJumpReturn:
913 return " --jump RETURN";
914 }
915}