OSDN Git Service

Add missing <malloc.h> include.
[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 NatController::NatController() {
44 }
45
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
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
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, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
99         {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
100         {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
101         {{IPTABLES_PATH, "-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
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, "-F", LOCAL_FORWARD,}, 1},
124         {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
125         {{IPTABLES_PATH, "-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
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                 "-t",
159                 "nat",
160                 "-A",
161                 LOCAL_NAT_POSTROUTING,
162                 "-o",
163                 extIface,
164                 "-j",
165                 "MASQUERADE"
166         };
167         if (runCmd(ARRAY_SIZE(cmd), cmd)) {
168             ALOGE("Error setting postroute rule: iface=%s", extIface);
169             // unwind what's been done, but don't care about success - what more could we do?
170             setDefaults();
171             return -1;
172         }
173     }
174
175     if (setForwardRules(true, intIface, extIface) != 0) {
176         ALOGE("Error setting forward rules");
177         if (natCount == 0) {
178             setDefaults();
179         }
180         errno = ENODEV;
181         return -1;
182     }
183
184     /* Always make sure the drop rule is at the end */
185     const char *cmd1[] = {
186             IPTABLES_PATH,
187             "-D",
188             LOCAL_FORWARD,
189             "-j",
190             "DROP"
191     };
192     runCmd(ARRAY_SIZE(cmd1), cmd1);
193     const char *cmd2[] = {
194             IPTABLES_PATH,
195             "-A",
196             LOCAL_FORWARD,
197             "-j",
198             "DROP"
199     };
200     runCmd(ARRAY_SIZE(cmd2), cmd2);
201
202     if (int ret = RouteController::enableTethering(intIface, extIface)) {
203         ALOGE("failed to add tethering rule for iif=%s oif=%s", intIface, extIface);
204         if (natCount == 0) {
205             setDefaults();
206         }
207         errno = -ret;
208         return -1;
209     }
210
211     natCount++;
212     return 0;
213 }
214
215 bool NatController::checkTetherCountingRuleExist(const char *pair_name) {
216     std::list<std::string>::iterator it;
217
218     for (it = ifacePairList.begin(); it != ifacePairList.end(); it++) {
219         if (*it == pair_name) {
220             /* We already have this counter */
221             return true;
222         }
223     }
224     return false;
225 }
226
227 int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
228
229     /* We only ever add tethering quota rules so that they stick. */
230     if (!add) {
231         return 0;
232     }
233     char *pair_name;
234     asprintf(&pair_name, "%s_%s", intIface, extIface);
235
236     if (checkTetherCountingRuleExist(pair_name)) {
237         free(pair_name);
238         return 0;
239     }
240     const char *cmd2b[] = {
241             IPTABLES_PATH,
242             "-A",
243             LOCAL_TETHER_COUNTERS_CHAIN,
244             "-i",
245             intIface,
246             "-o",
247             extIface,
248             "-j",
249           "RETURN"
250     };
251
252     if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) {
253         free(pair_name);
254         return -1;
255     }
256     ifacePairList.push_front(pair_name);
257     free(pair_name);
258
259     asprintf(&pair_name, "%s_%s", extIface, intIface);
260     if (checkTetherCountingRuleExist(pair_name)) {
261         free(pair_name);
262         return 0;
263     }
264
265     const char *cmd3b[] = {
266             IPTABLES_PATH,
267             "-A",
268             LOCAL_TETHER_COUNTERS_CHAIN,
269             "-i",
270             extIface,
271             "-o",
272             intIface,
273             "-j",
274             "RETURN"
275     };
276
277     if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) {
278         // unwind what's been done, but don't care about success - what more could we do?
279         free(pair_name);
280         return -1;
281     }
282     ifacePairList.push_front(pair_name);
283     free(pair_name);
284     return 0;
285 }
286
287 int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
288     const char *cmd1[] = {
289             IPTABLES_PATH,
290             add ? "-A" : "-D",
291             LOCAL_FORWARD,
292             "-i",
293             extIface,
294             "-o",
295             intIface,
296             "-m",
297             "state",
298             "--state",
299             "ESTABLISHED,RELATED",
300             "-g",
301             LOCAL_TETHER_COUNTERS_CHAIN
302     };
303     int rc = 0;
304
305     if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
306         return -1;
307     }
308
309     const char *cmd2[] = {
310             IPTABLES_PATH,
311             add ? "-A" : "-D",
312             LOCAL_FORWARD,
313             "-i",
314             intIface,
315             "-o",
316             extIface,
317             "-m",
318             "state",
319             "--state",
320             "INVALID",
321             "-j",
322             "DROP"
323     };
324
325     const char *cmd3[] = {
326             IPTABLES_PATH,
327             add ? "-A" : "-D",
328             LOCAL_FORWARD,
329             "-i",
330             intIface,
331             "-o",
332             extIface,
333             "-g",
334             LOCAL_TETHER_COUNTERS_CHAIN
335     };
336
337     if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
338         // bail on error, but only if adding
339         rc = -1;
340         goto err_invalid_drop;
341     }
342
343     if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
344         // unwind what's been done, but don't care about success - what more could we do?
345         rc = -1;
346         goto err_return;
347     }
348
349     if (setTetherCountingRules(add, intIface, extIface) && add) {
350         rc = -1;
351         goto err_return;
352     }
353
354     return 0;
355
356 err_return:
357     cmd2[1] = "-D";
358     runCmd(ARRAY_SIZE(cmd2), cmd2);
359 err_invalid_drop:
360     cmd1[1] = "-D";
361     runCmd(ARRAY_SIZE(cmd1), cmd1);
362     return rc;
363 }
364
365 int NatController::disableNat(const char* intIface, const char* extIface) {
366     if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
367         errno = ENODEV;
368         return -1;
369     }
370
371     if (int ret = RouteController::disableTethering(intIface, extIface)) {
372         ALOGE("failed to remove tethering rule for iif=%s oif=%s", intIface, extIface);
373         errno = -ret;
374         return -1;
375     }
376
377     setForwardRules(false, intIface, extIface);
378     if (--natCount <= 0) {
379         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
380         setDefaults();
381     }
382     return 0;
383 }