OSDN Git Service

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