• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 
17 #include <cinttypes>
18 #include <regex>
19 #include <set>
20 #include <string>
21 
22 #include <android-base/stringprintf.h>
23 #include <android-base/strings.h>
24 #include <netdutils/Stopwatch.h>
25 
26 #define LOG_TAG "Netd"
27 #include <log/log.h>
28 
29 #include "ConnmarkFlags.h"
30 #include "Controllers.h"
31 #include "IdletimerController.h"
32 #include "NetworkController.h"
33 #include "RouteController.h"
34 #include "XfrmController.h"
35 #include "oem_iptables_hook.h"
36 
37 namespace android {
38 namespace net {
39 
40 using android::base::Join;
41 using android::base::StringAppendF;
42 using android::base::StringPrintf;
43 using android::netdutils::Stopwatch;
44 
45 auto Controllers::execIptablesRestore  = ::execIptablesRestore;
46 auto Controllers::execIptablesRestoreWithOutput = ::execIptablesRestoreWithOutput;
47 
48 netdutils::Log gLog("netd");
49 netdutils::Log gUnsolicitedLog("netdUnsolicited");
50 
51 namespace {
52 
53 static constexpr char CONNMARK_MANGLE_INPUT[] = "connmark_mangle_INPUT";
54 static constexpr char CONNMARK_MANGLE_OUTPUT[] = "connmark_mangle_OUTPUT";
55 
56 /**
57  * List of module chains to be created, along with explicit ordering. ORDERING
58  * IS CRITICAL, AND SHOULD BE TRIPLE-CHECKED WITH EACH CHANGE.
59  */
60 static const std::vector<const char*> FILTER_INPUT = {
61         // Bandwidth should always be early in input chain, to make sure we
62         // correctly count incoming traffic against data plan.
63         OEM_IPTABLES_FILTER_INPUT,
64         BandwidthController::LOCAL_INPUT,
65         FirewallController::LOCAL_INPUT,
66 };
67 
68 static const std::vector<const char*> FILTER_FORWARD = {
69         OEM_IPTABLES_FILTER_FORWARD,
70         FirewallController::LOCAL_FORWARD,
71         BandwidthController::LOCAL_FORWARD,
72         TetherController::LOCAL_FORWARD,
73 };
74 
75 static const std::vector<const char*> FILTER_OUTPUT = {
76         OEM_IPTABLES_FILTER_OUTPUT,
77         FirewallController::LOCAL_OUTPUT,
78         StrictController::LOCAL_OUTPUT,
79         BandwidthController::LOCAL_OUTPUT,
80 };
81 
82 static const std::vector<const char*> RAW_PREROUTING = {
83         IdletimerController::LOCAL_RAW_PREROUTING,
84         BandwidthController::LOCAL_RAW_PREROUTING,
85         TetherController::LOCAL_RAW_PREROUTING,
86 };
87 
88 static const std::vector<const char*> MANGLE_POSTROUTING = {
89         OEM_IPTABLES_MANGLE_POSTROUTING,
90         BandwidthController::LOCAL_MANGLE_POSTROUTING,
91         IdletimerController::LOCAL_MANGLE_POSTROUTING,
92 };
93 
94 static const std::vector<const char*> MANGLE_INPUT = {
95         CONNMARK_MANGLE_INPUT,
96         WakeupController::LOCAL_MANGLE_INPUT,
97         RouteController::LOCAL_MANGLE_INPUT,
98 };
99 
100 static const std::vector<const char*> MANGLE_FORWARD = {
101         TetherController::LOCAL_MANGLE_FORWARD,
102 };
103 
104 static const std::vector<const char*> MANGLE_OUTPUT = {
105         CONNMARK_MANGLE_OUTPUT,
106 };
107 
108 static const std::vector<const char*> NAT_PREROUTING = {
109         OEM_IPTABLES_NAT_PREROUTING,
110 };
111 
112 static const std::vector<const char*> NAT_POSTROUTING = {
113         TetherController::LOCAL_NAT_POSTROUTING,
114 };
115 
116 // Commands to create child chains and to match created chains in iptables -S output. Keep in sync.
117 static const char* CHILD_CHAIN_TEMPLATE = "-A %s -j %s\n";
118 static const std::regex CHILD_CHAIN_REGEX("^-A ([^ ]+) -j ([^ ]+)$",
119                                           std::regex_constants::extended);
120 
121 }  // namespace
122 
123 /* static */
findExistingChildChains(const IptablesTarget target,const char * table,const char * parentChain)124 std::set<std::string> Controllers::findExistingChildChains(const IptablesTarget target,
125                                                            const char* table,
126                                                            const char* parentChain) {
127     if (target == V4V6) {
128         ALOGE("findExistingChildChains only supports one protocol at a time");
129         abort();
130     }
131 
132     std::set<std::string> existing;
133 
134     // List the current contents of parentChain.
135     //
136     // TODO: there is no guarantee that nothing else modifies the chain in the few milliseconds
137     // between when we list the existing rules and when we delete them. However:
138     // - Since this code is only run on startup, nothing else in netd will be running.
139     // - While vendor code is known to add its own rules to chains created by netd, it should never
140     //   be modifying the rules in childChains or the rules that hook said chains into their parent
141     //   chains.
142     std::string command = StringPrintf("*%s\n-S %s\nCOMMIT\n", table, parentChain);
143     std::string output;
144     if (Controllers::execIptablesRestoreWithOutput(target, command, &output) == -1) {
145         ALOGE("Error listing chain %s in table %s", parentChain, table);
146         return existing;
147     }
148 
149     // The only rules added by createChildChains are of the simple form "-A <parent> -j <child>".
150     // Find those rules and add each one's child chain to existing.
151     std::smatch matches;
152     std::stringstream stream(output);
153     std::string rule;
154     while (std::getline(stream, rule, '\n')) {
155         if (std::regex_search(rule, matches, CHILD_CHAIN_REGEX) && matches[1] == parentChain) {
156             existing.insert(matches[2]);
157         }
158     }
159 
160     return existing;
161 }
162 
163 /* static */
createChildChains(IptablesTarget target,const char * table,const char * parentChain,const std::vector<const char * > & childChains,bool exclusive)164 void Controllers::createChildChains(IptablesTarget target, const char* table,
165                                     const char* parentChain,
166                                     const std::vector<const char*>& childChains,
167                                     bool exclusive) {
168     std::string command = StringPrintf("*%s\n", table);
169 
170     // We cannot just clear all the chains we create because vendor code modifies filter OUTPUT and
171     // mangle POSTROUTING directly. So:
172     //
173     // - If we're the exclusive owner of this chain, simply clear it entirely.
174     // - If not, then list the chain's current contents to ensure that if we restart after a crash,
175     //   we leave the existing rules alone in the positions they currently occupy. This is faster
176     //   than blindly deleting our rules and recreating them, because deleting a rule that doesn't
177     //   exists causes iptables-restore to quit, which takes ~30ms per delete. It's also more
178     //   correct, because if we delete rules and re-add them, they'll be in the wrong position with
179     //   regards to the vendor rules.
180     //
181     // TODO: Make all chains exclusive once vendor code uses the oem_* rules.
182     std::set<std::string> existingChildChains;
183     if (exclusive) {
184         // Just running ":chain -" flushes user-defined chains, but not built-in chains like INPUT.
185         // Since at this point we don't know if parentChain is a built-in chain, do both.
186         StringAppendF(&command, ":%s -\n", parentChain);
187         StringAppendF(&command, "-F %s\n", parentChain);
188     } else {
189         existingChildChains = findExistingChildChains(target, table, parentChain);
190     }
191 
192     for (const auto& childChain : childChains) {
193         // Always clear the child chain.
194         StringAppendF(&command, ":%s -\n", childChain);
195         // But only add it to the parent chain if it's not already there.
196         if (existingChildChains.find(childChain) == existingChildChains.end()) {
197             StringAppendF(&command, CHILD_CHAIN_TEMPLATE, parentChain, childChain);
198         }
199     }
200     command += "COMMIT\n";
201     execIptablesRestore(target, command);
202 }
203 
Controllers()204 Controllers::Controllers()
205     : wakeupCtrl(
206               [this](const WakeupController::ReportArgs& args) {
207                   const auto listener = eventReporter.getNetdEventListener();
208                   if (listener == nullptr) {
209                       gLog.error("getNetdEventListener() returned nullptr. dropping wakeup event");
210                       return;
211                   }
212                   String16 prefix = String16(args.prefix.c_str());
213                   String16 srcIp = String16(args.srcIp.c_str());
214                   String16 dstIp = String16(args.dstIp.c_str());
215                   listener->onWakeupEvent(prefix, args.uid, args.ethertype, args.ipNextHeader,
216                                           args.dstHw, srcIp, dstIp, args.srcPort, args.dstPort,
217                                           args.timestampNs);
218               },
219               &iptablesRestoreCtrl) {
220     InterfaceController::initializeAll();
221 }
222 
initChildChains()223 void Controllers::initChildChains() {
224     /*
225      * This is the only time we touch top-level chains in iptables; controllers
226      * should only mutate rules inside of their children chains, as created by
227      * the constants above.
228      *
229      * Modules should never ACCEPT packets (except in well-justified cases);
230      * they should instead defer to any remaining modules using RETURN, or
231      * otherwise DROP/REJECT.
232      */
233 
234     // Create chains for child modules.
235     createChildChains(V4V6, "filter", "INPUT", FILTER_INPUT, true);
236     createChildChains(V4V6, "filter", "FORWARD", FILTER_FORWARD, true);
237     createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING, true);
238     createChildChains(V4V6, "mangle", "FORWARD", MANGLE_FORWARD, true);
239     createChildChains(V4V6, "mangle", "INPUT", MANGLE_INPUT, true);
240     createChildChains(V4V6, "mangle", "OUTPUT", MANGLE_OUTPUT, true);
241     createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING, true);
242     createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING, true);
243 
244     createChildChains(V4, "filter", "OUTPUT", FILTER_OUTPUT, false);
245     createChildChains(V6, "filter", "OUTPUT", FILTER_OUTPUT, false);
246     createChildChains(V4, "mangle", "POSTROUTING", MANGLE_POSTROUTING, false);
247     createChildChains(V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING, false);
248 }
249 
setupConnmarkIptablesHooks()250 static void setupConnmarkIptablesHooks() {
251     // Rules to store parts of the fwmark (namely: netId, explicitlySelected, protectedFromVpn,
252     // permission) in connmark.
253     // Only saves the mark if no mark has been set before.
254     static_assert(std::string_view(CONNMARK_MANGLE_INPUT) == "connmark_mangle_INPUT");
255     static_assert(std::string_view(CONNMARK_MANGLE_OUTPUT) == "connmark_mangle_OUTPUT");
256     static_assert(CONNMARK_FWMARK_MASK == 0x000FFFFF);
257     const std::string cmd(
258             // CONNMARK:
259             // --save-mark [--nfmask nfmask] [--ctmask ctmask]
260             // Copy the packet mark (nfmark) to the connection mark (ctmark) using the given
261             // masks. The new nfmark value is determined as follows:
262             // ctmark = (ctmark & ~ctmask) ^ (nfmark & nfmask)
263             // i.e. ctmask defines what bits to clear and nfmask what bits of the nfmark to
264             // XOR into the ctmark. ctmask and nfmask default to 0xFFFFFFFF.
265             "*mangle\n"
266             "-A connmark_mangle_INPUT -m connmark --mark 0/0x000FFFFF "
267             "-j CONNMARK --save-mark --ctmask 0x000FFFFF --nfmask 0x000FFFFF\n"
268             "-A connmark_mangle_OUTPUT -m connmark --mark 0/0x000FFFFF "
269             "-j CONNMARK --save-mark --ctmask 0x000FFFFF --nfmask 0x000FFFFF\n"
270             "COMMIT\n");
271     execIptablesRestore(V4V6, cmd);
272 }
273 
initIptablesRules()274 void Controllers::initIptablesRules() {
275     Stopwatch s;
276     initChildChains();
277     gLog.info("Creating child chains: %" PRId64 "us", s.getTimeAndResetUs());
278 
279     // Let each module setup their child chains
280     setupOemIptablesHook();
281     gLog.info("Setting up OEM hooks: %" PRId64 "us", s.getTimeAndResetUs());
282 
283     /* When enabled, DROPs all packets except those matching rules. */
284     firewallCtrl.setupIptablesHooks();
285     gLog.info("Setting up FirewallController hooks: %" PRId64 "us", s.getTimeAndResetUs());
286 
287     /* Does DROPs in FORWARD by default */
288     tetherCtrl.setupIptablesHooks();
289     gLog.info("Setting up TetherController hooks: %" PRId64 "us", s.getTimeAndResetUs());
290 
291     /*
292      * Does REJECT in INPUT, OUTPUT. Does counting also.
293      * No DROP/REJECT allowed later in netfilter-flow hook order.
294      */
295     bandwidthCtrl.setupIptablesHooks();
296     gLog.info("Setting up BandwidthController hooks: %" PRId64 "us", s.getTimeAndResetUs());
297 
298     /*
299      * Counts in nat: PREROUTING, POSTROUTING.
300      * No DROP/REJECT allowed later in netfilter-flow hook order.
301      */
302     idletimerCtrl.setupIptablesHooks();
303     gLog.info("Setting up IdletimerController hooks: %" PRId64 "us", s.getTimeAndResetUs());
304 
305     /*
306      * Add rules for detecting IPv6/IPv4 TCP/UDP connections with TLS/DTLS header
307      */
308     strictCtrl.setupIptablesHooks();
309     gLog.info("Setting up StrictController hooks: %" PRId64 "us", s.getTimeAndResetUs());
310 
311     /*
312      * Add rules for storing netid in connmark.
313      */
314     setupConnmarkIptablesHooks();
315     gLog.info("Setting up connmark hooks: %" PRId64 "us", s.getTimeAndResetUs());
316 }
317 
init()318 void Controllers::init() {
319     initIptablesRules();
320     Stopwatch s;
321 
322     if (int ret = bandwidthCtrl.enableBandwidthControl()) {
323         gLog.error("Failed to initialize BandwidthController (%s)", strerror(-ret));
324         // A failure to init almost definitely means that iptables failed to load
325         // our static ruleset, which then basically means network accounting will not work.
326         // As such simply exit netd.  This may crash loop the system, but by failing
327         // to bootup we will trigger rollback and thus this offers us protection against
328         // a mainline update breaking things.
329         exit(1);
330     }
331     gLog.info("Enabling bandwidth control: %" PRId64 "us", s.getTimeAndResetUs());
332 
333     if (int ret = RouteController::Init(NetworkController::LOCAL_NET_ID)) {
334         gLog.error("Failed to initialize RouteController (%s)", strerror(-ret));
335         exit(2);
336     }
337     gLog.info("Initializing RouteController: %" PRId64 "us", s.getTimeAndResetUs());
338 
339     netdutils::Status xStatus = XfrmController::Init();
340     if (!isOk(xStatus)) {
341         gLog.error("Failed to initialize XfrmController (%s)", netdutils::toString(xStatus).c_str());
342         exit(3);
343     };
344     gLog.info("Initializing XfrmController: %" PRId64 "us", s.getTimeAndResetUs());
345 }
346 
347 Controllers* gCtls = nullptr;
348 
349 }  // namespace net
350 }  // namespace android
351