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 <cutils/log.h>
32 #include <logwrap/logwrap.h>
33 
34 #include "NatController.h"
35 #include "NetdConstants.h"
36 #include "RouteController.h"
37 
38 const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
39 const char* NatController::LOCAL_MANGLE_FORWARD = "natctrl_mangle_FORWARD";
40 const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
41 const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
42 
NatController()43 NatController::NatController() {
44 }
45 
~NatController()46 NatController::~NatController() {
47 }
48 
49 struct CommandsAndArgs {
50     /* The array size doesn't really matter as the compiler will barf if too many initializers are specified. */
51     const char *cmd[32];
52     bool checkRes;
53 };
54 
runCmd(int argc,const char ** argv)55 int NatController::runCmd(int argc, const char **argv) {
56     int res;
57 
58     res = android_fork_execvp(argc, (char **)argv, NULL, false, false);
59 
60 #if !LOG_NDEBUG
61     std::string full_cmd = argv[0];
62     argc--; argv++;
63     /*
64      * HACK: Sometimes runCmd() is called with a ridcously large value (32)
65      * and it works because the argv[] contains a NULL after the last
66      * true argv. So here we use the NULL argv[] to terminate when the argc
67      * is horribly wrong, and argc for the normal cases.
68      */
69     for (; argc && argv[0]; argc--, argv++) {
70         full_cmd += " ";
71         full_cmd += argv[0];
72     }
73     ALOGV("runCmd(%s) res=%d", full_cmd.c_str(), res);
74 #endif
75     return res;
76 }
77 
setupIptablesHooks()78 int NatController::setupIptablesHooks() {
79     int res;
80     res = setDefaults();
81     if (res < 0) {
82         return res;
83     }
84 
85     struct CommandsAndArgs defaultCommands[] = {
86         /*
87          * First chain is for tethering counters.
88          * This chain is reached via --goto, and then RETURNS.
89          *
90          * Second chain is used to limit downstream mss to the upstream pmtu
91          * so we don't end up fragmenting every large packet tethered devices
92          * send.  Note this feature requires kernel support with flag
93          * CONFIG_NETFILTER_XT_TARGET_TCPMSS=y, which not all builds will have,
94          * so the final rule is allowed to fail.
95          * Bug 17629786 asks to make the failure more obvious, or even fatal
96          * so that all builds eventually gain the performance improvement.
97          */
98         {{IPTABLES_PATH, "-w", "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
99         {{IPTABLES_PATH, "-w", "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
100         {{IPTABLES_PATH, "-w", "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
101         {{IPTABLES_PATH, "-w", "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags",
102                 "SYN", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"}, 0},
103     };
104     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
105         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
106             defaultCommands[cmdNum].checkRes) {
107                 return -1;
108         }
109     }
110     ifacePairList.clear();
111 
112     return 0;
113 }
114 
setDefaults()115 int NatController::setDefaults() {
116     /*
117      * The following only works because:
118      *  - the defaultsCommands[].cmd array is padded with NULL, and
119      *  - the 1st argc of runCmd() will just be the max for the CommandsAndArgs[].cmd, and
120      *  - internally it will be memcopied to an array and terminated with a NULL.
121      */
122     struct CommandsAndArgs defaultCommands[] = {
123         {{IPTABLES_PATH, "-w", "-F", LOCAL_FORWARD,}, 1},
124         {{IPTABLES_PATH, "-w", "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
125         {{IPTABLES_PATH, "-w", "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
126     };
127     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
128         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
129             defaultCommands[cmdNum].checkRes) {
130                 return -1;
131         }
132     }
133 
134     natCount = 0;
135 
136     return 0;
137 }
138 
enableNat(const char * intIface,const char * extIface)139 int NatController::enableNat(const char* intIface, const char* extIface) {
140     ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
141 
142     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
143         errno = ENODEV;
144         return -1;
145     }
146 
147     /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */
148     if (!strcmp(intIface, extIface)) {
149         ALOGE("Duplicate interface specified: %s %s", intIface, extIface);
150         errno = EINVAL;
151         return -1;
152     }
153 
154     // add this if we are the first added nat
155     if (natCount == 0) {
156         const char *cmd[] = {
157                 IPTABLES_PATH,
158                 "-w",
159                 "-t",
160                 "nat",
161                 "-A",
162                 LOCAL_NAT_POSTROUTING,
163                 "-o",
164                 extIface,
165                 "-j",
166                 "MASQUERADE"
167         };
168         if (runCmd(ARRAY_SIZE(cmd), cmd)) {
169             ALOGE("Error setting postroute rule: iface=%s", extIface);
170             // unwind what's been done, but don't care about success - what more could we do?
171             setDefaults();
172             return -1;
173         }
174     }
175 
176     if (setForwardRules(true, intIface, extIface) != 0) {
177         ALOGE("Error setting forward rules");
178         if (natCount == 0) {
179             setDefaults();
180         }
181         errno = ENODEV;
182         return -1;
183     }
184 
185     /* Always make sure the drop rule is at the end */
186     const char *cmd1[] = {
187             IPTABLES_PATH,
188             "-w",
189             "-D",
190             LOCAL_FORWARD,
191             "-j",
192             "DROP"
193     };
194     runCmd(ARRAY_SIZE(cmd1), cmd1);
195     const char *cmd2[] = {
196             IPTABLES_PATH,
197             "-w",
198             "-A",
199             LOCAL_FORWARD,
200             "-j",
201             "DROP"
202     };
203     runCmd(ARRAY_SIZE(cmd2), cmd2);
204 
205     natCount++;
206     return 0;
207 }
208 
checkTetherCountingRuleExist(const char * pair_name)209 bool NatController::checkTetherCountingRuleExist(const char *pair_name) {
210     std::list<std::string>::iterator it;
211 
212     for (it = ifacePairList.begin(); it != ifacePairList.end(); it++) {
213         if (*it == pair_name) {
214             /* We already have this counter */
215             return true;
216         }
217     }
218     return false;
219 }
220 
setTetherCountingRules(bool add,const char * intIface,const char * extIface)221 int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
222 
223     /* We only ever add tethering quota rules so that they stick. */
224     if (!add) {
225         return 0;
226     }
227     char *pair_name;
228     asprintf(&pair_name, "%s_%s", intIface, extIface);
229 
230     if (checkTetherCountingRuleExist(pair_name)) {
231         free(pair_name);
232         return 0;
233     }
234     const char *cmd2b[] = {
235             IPTABLES_PATH,
236             "-w",
237             "-A",
238             LOCAL_TETHER_COUNTERS_CHAIN,
239             "-i",
240             intIface,
241             "-o",
242             extIface,
243             "-j",
244           "RETURN"
245     };
246 
247     if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) {
248         free(pair_name);
249         return -1;
250     }
251     ifacePairList.push_front(pair_name);
252     free(pair_name);
253 
254     asprintf(&pair_name, "%s_%s", extIface, intIface);
255     if (checkTetherCountingRuleExist(pair_name)) {
256         free(pair_name);
257         return 0;
258     }
259 
260     const char *cmd3b[] = {
261             IPTABLES_PATH,
262             "-w",
263             "-A",
264             LOCAL_TETHER_COUNTERS_CHAIN,
265             "-i",
266             extIface,
267             "-o",
268             intIface,
269             "-j",
270             "RETURN"
271     };
272 
273     if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) {
274         // unwind what's been done, but don't care about success - what more could we do?
275         free(pair_name);
276         return -1;
277     }
278     ifacePairList.push_front(pair_name);
279     free(pair_name);
280     return 0;
281 }
282 
setForwardRules(bool add,const char * intIface,const char * extIface)283 int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
284     const char *cmd1[] = {
285             IPTABLES_PATH,
286             "-w",
287             add ? "-A" : "-D",
288             LOCAL_FORWARD,
289             "-i",
290             extIface,
291             "-o",
292             intIface,
293             "-m",
294             "state",
295             "--state",
296             "ESTABLISHED,RELATED",
297             "-g",
298             LOCAL_TETHER_COUNTERS_CHAIN
299     };
300     int rc = 0;
301 
302     if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
303         return -1;
304     }
305 
306     const char *cmd2[] = {
307             IPTABLES_PATH,
308             "-w",
309             add ? "-A" : "-D",
310             LOCAL_FORWARD,
311             "-i",
312             intIface,
313             "-o",
314             extIface,
315             "-m",
316             "state",
317             "--state",
318             "INVALID",
319             "-j",
320             "DROP"
321     };
322 
323     const char *cmd3[] = {
324             IPTABLES_PATH,
325             "-w",
326             add ? "-A" : "-D",
327             LOCAL_FORWARD,
328             "-i",
329             intIface,
330             "-o",
331             extIface,
332             "-g",
333             LOCAL_TETHER_COUNTERS_CHAIN
334     };
335 
336     if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
337         // bail on error, but only if adding
338         rc = -1;
339         goto err_invalid_drop;
340     }
341 
342     if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
343         // unwind what's been done, but don't care about success - what more could we do?
344         rc = -1;
345         goto err_return;
346     }
347 
348     if (setTetherCountingRules(add, intIface, extIface) && add) {
349         rc = -1;
350         goto err_return;
351     }
352 
353     return 0;
354 
355 err_return:
356     cmd2[2] = "-D";
357     runCmd(ARRAY_SIZE(cmd2), cmd2);
358 err_invalid_drop:
359     cmd1[2] = "-D";
360     runCmd(ARRAY_SIZE(cmd1), cmd1);
361     return rc;
362 }
363 
disableNat(const char * intIface,const char * extIface)364 int NatController::disableNat(const char* intIface, const char* extIface) {
365     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
366         errno = ENODEV;
367         return -1;
368     }
369 
370     setForwardRules(false, intIface, extIface);
371     if (--natCount <= 0) {
372         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
373         setDefaults();
374     }
375     return 0;
376 }
377