1 /*
2  * Copyright (C) 2008 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 #define LOG_NDEBUG 0
18 
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <sys/socket.h>
22 #include <sys/stat.h>
23 #include <sys/wait.h>
24 #include <fcntl.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #include <string.h>
28 #include <cutils/properties.h>
29 
30 #define LOG_TAG "NatController"
31 #include <android-base/stringprintf.h>
32 #include <cutils/log.h>
33 #include <logwrap/logwrap.h>
34 
35 #include "NetdConstants.h"
36 #include "NatController.h"
37 #include "NetdConstants.h"
38 #include "RouteController.h"
39 
40 using android::base::StringPrintf;
41 
42 const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
43 const char* NatController::LOCAL_MANGLE_FORWARD = "natctrl_mangle_FORWARD";
44 const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
45 const char* NatController::LOCAL_RAW_PREROUTING = "natctrl_raw_PREROUTING";
46 const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
47 
48 auto NatController::execFunction = android_fork_execvp;
49 auto NatController::iptablesRestoreFunction = execIptablesRestore;
50 
NatController()51 NatController::NatController() {
52 }
53 
~NatController()54 NatController::~NatController() {
55 }
56 
57 struct CommandsAndArgs {
58     /* The array size doesn't really matter as the compiler will barf if too many initializers are specified. */
59     const char *cmd[32];
60     bool checkRes;
61 };
62 
runCmd(int argc,const char ** argv)63 int NatController::runCmd(int argc, const char **argv) {
64     int res;
65 
66     res = execFunction(argc, (char **)argv, NULL, false, false);
67 
68 #if !LOG_NDEBUG
69     std::string full_cmd = argv[0];
70     argc--; argv++;
71     /*
72      * HACK: Sometimes runCmd() is called with a ridcously large value (32)
73      * and it works because the argv[] contains a NULL after the last
74      * true argv. So here we use the NULL argv[] to terminate when the argc
75      * is horribly wrong, and argc for the normal cases.
76      */
77     for (; argc && argv[0]; argc--, argv++) {
78         full_cmd += " ";
79         full_cmd += argv[0];
80     }
81     ALOGV("runCmd(%s) res=%d", full_cmd.c_str(), res);
82 #endif
83     return res;
84 }
85 
setupIptablesHooks()86 int NatController::setupIptablesHooks() {
87     int res;
88     res = setDefaults();
89     if (res < 0) {
90         return res;
91     }
92 
93     // Used to limit downstream mss to the upstream pmtu so we don't end up fragmenting every large
94     // packet tethered devices send. This is IPv4-only, because in IPv6 we send the MTU in the RA.
95     // This is no longer optional and tethering will fail to start if it fails.
96     std::string mssRewriteCommand = StringPrintf(
97         "*mangle\n"
98         "-A %s -p tcp --tcp-flags SYN SYN -j TCPMSS --clamp-mss-to-pmtu\n"
99         "COMMIT\n", LOCAL_MANGLE_FORWARD);
100 
101     // This is for tethering counters. This chain is reached via --goto, and then RETURNS.
102     std::string defaultCommands = StringPrintf(
103         "*filter\n"
104         ":%s -\n"
105         "COMMIT\n", LOCAL_TETHER_COUNTERS_CHAIN);
106 
107     res = iptablesRestoreFunction(V4, mssRewriteCommand);
108     if (res < 0) {
109         return res;
110     }
111 
112     res = iptablesRestoreFunction(V4V6, defaultCommands);
113     if (res < 0) {
114         return res;
115     }
116 
117     ifacePairList.clear();
118 
119     return 0;
120 }
121 
setDefaults()122 int NatController::setDefaults() {
123     std::string v4Cmd = StringPrintf(
124         "*filter\n"
125         ":%s -\n"
126         "-A %s -j DROP\n"
127         "COMMIT\n"
128         "*nat\n"
129         ":%s -\n"
130         "COMMIT\n", LOCAL_FORWARD, LOCAL_FORWARD, LOCAL_NAT_POSTROUTING);
131 
132     std::string v6Cmd = StringPrintf(
133         "*filter\n"
134         ":%s -\n"
135         "COMMIT\n"
136         "*raw\n"
137         ":%s -\n"
138         "COMMIT\n", LOCAL_FORWARD, LOCAL_RAW_PREROUTING);
139 
140     int res = iptablesRestoreFunction(V4, v4Cmd);
141     if (res < 0) {
142         return res;
143     }
144 
145     res = iptablesRestoreFunction(V6, v6Cmd);
146     if (res < 0) {
147         return res;
148     }
149 
150     natCount = 0;
151 
152     return 0;
153 }
154 
enableNat(const char * intIface,const char * extIface)155 int NatController::enableNat(const char* intIface, const char* extIface) {
156     ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
157 
158     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
159         errno = ENODEV;
160         return -1;
161     }
162 
163     /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */
164     if (!strcmp(intIface, extIface)) {
165         ALOGE("Duplicate interface specified: %s %s", intIface, extIface);
166         errno = EINVAL;
167         return -1;
168     }
169 
170     // add this if we are the first added nat
171     if (natCount == 0) {
172         const char *v4Cmd[] = {
173                 IPTABLES_PATH,
174                 "-w",
175                 "-t",
176                 "nat",
177                 "-A",
178                 LOCAL_NAT_POSTROUTING,
179                 "-o",
180                 extIface,
181                 "-j",
182                 "MASQUERADE"
183         };
184 
185         /*
186          * IPv6 tethering doesn't need the state-based conntrack rules, so
187          * it unconditionally jumps to the tether counters chain all the time.
188          */
189         const char *v6Cmd[] = {IP6TABLES_PATH, "-w", "-A", LOCAL_FORWARD,
190                                "-g", LOCAL_TETHER_COUNTERS_CHAIN};
191 
192         if (runCmd(ARRAY_SIZE(v4Cmd), v4Cmd) || runCmd(ARRAY_SIZE(v6Cmd), v6Cmd)) {
193             ALOGE("Error setting postroute rule: iface=%s", extIface);
194             // unwind what's been done, but don't care about success - what more could we do?
195             setDefaults();
196             return -1;
197         }
198     }
199 
200     if (setForwardRules(true, intIface, extIface) != 0) {
201         ALOGE("Error setting forward rules");
202         if (natCount == 0) {
203             setDefaults();
204         }
205         errno = ENODEV;
206         return -1;
207     }
208 
209     /* Always make sure the drop rule is at the end */
210     const char *cmd1[] = {
211             IPTABLES_PATH,
212             "-w",
213             "-D",
214             LOCAL_FORWARD,
215             "-j",
216             "DROP"
217     };
218     runCmd(ARRAY_SIZE(cmd1), cmd1);
219     const char *cmd2[] = {
220             IPTABLES_PATH,
221             "-w",
222             "-A",
223             LOCAL_FORWARD,
224             "-j",
225             "DROP"
226     };
227     runCmd(ARRAY_SIZE(cmd2), cmd2);
228 
229     natCount++;
230     return 0;
231 }
232 
checkTetherCountingRuleExist(const char * pair_name)233 bool NatController::checkTetherCountingRuleExist(const char *pair_name) {
234     std::list<std::string>::iterator it;
235 
236     for (it = ifacePairList.begin(); it != ifacePairList.end(); it++) {
237         if (*it == pair_name) {
238             /* We already have this counter */
239             return true;
240         }
241     }
242     return false;
243 }
244 
setTetherCountingRules(bool add,const char * intIface,const char * extIface)245 int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
246 
247     /* We only ever add tethering quota rules so that they stick. */
248     if (!add) {
249         return 0;
250     }
251     char *pair_name;
252     asprintf(&pair_name, "%s_%s", intIface, extIface);
253 
254     if (checkTetherCountingRuleExist(pair_name)) {
255         free(pair_name);
256         return 0;
257     }
258     const char *cmd2b[] = {
259         IPTABLES_PATH,
260         "-w", "-A", LOCAL_TETHER_COUNTERS_CHAIN, "-i", intIface, "-o", extIface, "-j", "RETURN"
261     };
262 
263     const char *cmd2c[] = {
264         IP6TABLES_PATH,
265         "-w", "-A", LOCAL_TETHER_COUNTERS_CHAIN, "-i", intIface, "-o", extIface, "-j", "RETURN"
266     };
267 
268     if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) || runCmd(ARRAY_SIZE(cmd2c), cmd2c)) {
269         free(pair_name);
270         return -1;
271     }
272     ifacePairList.push_front(pair_name);
273     free(pair_name);
274 
275     asprintf(&pair_name, "%s_%s", extIface, intIface);
276     if (checkTetherCountingRuleExist(pair_name)) {
277         free(pair_name);
278         return 0;
279     }
280 
281     const char *cmd3b[] = {
282         IPTABLES_PATH,
283         "-w", "-A", LOCAL_TETHER_COUNTERS_CHAIN, "-i", extIface, "-o", intIface, "-j", "RETURN"
284     };
285 
286     const char *cmd3c[] = {
287         IP6TABLES_PATH,
288         "-w", "-A", LOCAL_TETHER_COUNTERS_CHAIN, "-i", extIface, "-o", intIface, "-j", "RETURN"
289     };
290 
291     if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) || runCmd(ARRAY_SIZE(cmd3c), cmd3c)) {
292         // unwind what's been done, but don't care about success - what more could we do?
293         free(pair_name);
294         return -1;
295     }
296     ifacePairList.push_front(pair_name);
297     free(pair_name);
298     return 0;
299 }
300 
setForwardRules(bool add,const char * intIface,const char * extIface)301 int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
302     const char *cmd1[] = {
303             IPTABLES_PATH,
304             "-w",
305             add ? "-A" : "-D",
306             LOCAL_FORWARD,
307             "-i",
308             extIface,
309             "-o",
310             intIface,
311             "-m",
312             "state",
313             "--state",
314             "ESTABLISHED,RELATED",
315             "-g",
316             LOCAL_TETHER_COUNTERS_CHAIN
317     };
318     int rc = 0;
319 
320     if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
321         return -1;
322     }
323 
324     const char *cmd2[] = {
325             IPTABLES_PATH,
326             "-w",
327             add ? "-A" : "-D",
328             LOCAL_FORWARD,
329             "-i",
330             intIface,
331             "-o",
332             extIface,
333             "-m",
334             "state",
335             "--state",
336             "INVALID",
337             "-j",
338             "DROP"
339     };
340 
341     const char *cmd3[] = {
342             IPTABLES_PATH,
343             "-w",
344             add ? "-A" : "-D",
345             LOCAL_FORWARD,
346             "-i",
347             intIface,
348             "-o",
349             extIface,
350             "-g",
351             LOCAL_TETHER_COUNTERS_CHAIN
352     };
353 
354     const char *cmd4[] = {
355             IP6TABLES_PATH,
356             "-w",
357             "-t",
358             "raw",
359             add ? "-A" : "-D",
360             LOCAL_RAW_PREROUTING,
361             "-i",
362             intIface,
363             "-m",
364             "rpfilter",
365             "--invert",
366             "!",
367             "-s",
368             "fe80::/64",
369             "-j",
370             "DROP"
371     };
372 
373     if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
374         // bail on error, but only if adding
375         rc = -1;
376         goto err_invalid_drop;
377     }
378 
379     if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
380         // unwind what's been done, but don't care about success - what more could we do?
381         rc = -1;
382         goto err_return;
383     }
384 
385     if (runCmd(ARRAY_SIZE(cmd4), cmd4) && add) {
386         rc = -1;
387         goto err_rpfilter;
388     }
389 
390     if (setTetherCountingRules(add, intIface, extIface) && add) {
391         rc = -1;
392         goto err_return;
393     }
394 
395     return 0;
396 
397 err_rpfilter:
398     cmd3[2] = "-D";
399     runCmd(ARRAY_SIZE(cmd3), cmd3);
400 err_return:
401     cmd2[2] = "-D";
402     runCmd(ARRAY_SIZE(cmd2), cmd2);
403 err_invalid_drop:
404     cmd1[2] = "-D";
405     runCmd(ARRAY_SIZE(cmd1), cmd1);
406     return rc;
407 }
408 
disableNat(const char * intIface,const char * extIface)409 int NatController::disableNat(const char* intIface, const char* extIface) {
410     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
411         errno = ENODEV;
412         return -1;
413     }
414 
415     setForwardRules(false, intIface, extIface);
416     if (--natCount <= 0) {
417         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
418         setDefaults();
419     }
420     return 0;
421 }
422