OSDN Git Service

Merge "Add support for fwmark split tunneling"
[android-x86/system-netd.git] / 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 "SecondaryTableController.h"
36 #include "NetdConstants.h"
37
38 const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
39 const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
40 const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
41
42 NatController::NatController(SecondaryTableController *ctrl) {
43     secondaryTableCtrl = ctrl;
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          * Chain for tethering counters.
88          * This chain is reached via --goto, and then RETURNS.
89          */
90         {{IPTABLES_PATH, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
91         {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
92         {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
93     };
94     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
95         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
96             defaultCommands[cmdNum].checkRes) {
97                 return -1;
98         }
99     }
100
101     return 0;
102 }
103
104 int NatController::setDefaults() {
105     /*
106      * The following only works because:
107      *  - the defaultsCommands[].cmd array is padded with NULL, and
108      *  - the 1st argc of runCmd() will just be the max for the CommandsAndArgs[].cmd, and
109      *  - internally it will be memcopied to an array and terminated with a NULL.
110      */
111     struct CommandsAndArgs defaultCommands[] = {
112         {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1},
113         {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
114         {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
115         {{IP_PATH, "rule", "flush"}, 0},
116         {{IP_PATH, "-6", "rule", "flush"}, 0},
117         {{IP_PATH, "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
118         {{IP_PATH, "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
119         {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
120         {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
121         {{IP_PATH, "route", "flush", "cache"}, 0},
122     };
123     for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
124         if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
125             defaultCommands[cmdNum].checkRes) {
126                 return -1;
127         }
128     }
129
130     natCount = 0;
131
132     return 0;
133 }
134
135 bool NatController::checkInterface(const char *iface) {
136     if (strlen(iface) > IFNAMSIZ) return false;
137     return true;
138 }
139
140 int NatController::routesOp(bool add, const char *intIface, const char *extIface, char **argv, int addrCount) {
141     int tableNumber = secondaryTableCtrl->findTableNumber(extIface);
142     int ret = 0;
143
144     if (tableNumber != -1) {
145         for (int i = 0; i < addrCount; i++) {
146             if (add) {
147                 ret |= secondaryTableCtrl->modifyFromRule(tableNumber, ADD, argv[5+i]);
148                 ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, ADD, intIface, argv[5+i]);
149             } else {
150                 ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
151                 ret |= secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
152             }
153         }
154         const char *cmd[] = {
155                 IP_PATH,
156                 "route",
157                 "flush",
158                 "cache"
159         };
160         runCmd(ARRAY_SIZE(cmd), cmd);
161     }
162     return ret;
163 }
164
165 //  0    1       2       3       4            5
166 // nat enable intface extface addrcnt nated-ipaddr/prelength
167 int NatController::enableNat(const int argc, char **argv) {
168     int i;
169     int addrCount = atoi(argv[4]);
170     const char *intIface = argv[2];
171     const char *extIface = argv[3];
172     int tableNumber;
173
174     ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
175
176     if (!checkInterface(intIface) || !checkInterface(extIface)) {
177         ALOGE("Invalid interface specified");
178         errno = ENODEV;
179         return -1;
180     }
181
182     /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */
183     if (!strcmp(intIface, extIface)) {
184         ALOGE("Duplicate interface specified: %s %s", intIface, extIface);
185         errno = EINVAL;
186         return -1;
187     }
188
189     if (argc < 5 + addrCount) {
190         ALOGE("Missing Argument");
191         errno = EINVAL;
192         return -1;
193     }
194     if (routesOp(true, intIface, extIface, argv, addrCount)) {
195         ALOGE("Error setting route rules");
196         routesOp(false, intIface, extIface, argv, addrCount);
197         errno = ENODEV;
198         return -1;
199     }
200
201     // add this if we are the first added nat
202     if (natCount == 0) {
203         const char *cmd[] = {
204                 IPTABLES_PATH,
205                 "-t",
206                 "nat",
207                 "-A",
208                 LOCAL_NAT_POSTROUTING,
209                 "-o",
210                 extIface,
211                 "-j",
212                 "MASQUERADE"
213         };
214         if (runCmd(ARRAY_SIZE(cmd), cmd)) {
215             ALOGE("Error seting postroute rule: iface=%s", extIface);
216             // unwind what's been done, but don't care about success - what more could we do?
217             routesOp(false, intIface, extIface, argv, addrCount);
218             setDefaults();
219             return -1;
220         }
221     }
222
223
224     if (setForwardRules(true, intIface, extIface) != 0) {
225         ALOGE("Error setting forward rules");
226         routesOp(false, intIface, extIface, argv, addrCount);
227         if (natCount == 0) {
228             setDefaults();
229         }
230         errno = ENODEV;
231         return -1;
232     }
233
234     /* Always make sure the drop rule is at the end */
235     const char *cmd1[] = {
236             IPTABLES_PATH,
237             "-D",
238             LOCAL_FORWARD,
239             "-j",
240             "DROP"
241     };
242     runCmd(ARRAY_SIZE(cmd1), cmd1);
243     const char *cmd2[] = {
244             IPTABLES_PATH,
245             "-A",
246             LOCAL_FORWARD,
247             "-j",
248             "DROP"
249     };
250     runCmd(ARRAY_SIZE(cmd2), cmd2);
251
252     natCount++;
253     return 0;
254 }
255
256 int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
257
258     /* We only ever add tethering quota rules so that they stick. */
259     if (!add) {
260         return 0;
261     }
262     char *quota_name, *proc_path;
263     int quota_fd;
264     asprintf(&quota_name, "%s_%s", intIface, extIface);
265
266     asprintf(&proc_path, "/proc/net/xt_quota/%s", quota_name);
267     quota_fd = open(proc_path, O_RDONLY);
268     if (quota_fd >= 0) {
269         /* quota for iface pair already exists */
270         free(proc_path);
271         free(quota_name);
272         return 0;
273     }
274     close(quota_fd);
275     free(proc_path);
276
277     const char *cmd2b[] = {
278             IPTABLES_PATH,
279             "-A",
280             LOCAL_TETHER_COUNTERS_CHAIN,
281             "-i",
282             intIface,
283             "-o",
284             extIface,
285             "-m",
286             "quota2",
287             "--name",
288             quota_name,
289             "--grow",
290             "-j",
291           "RETURN"
292     };
293
294     if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) {
295         free(quota_name);
296         return -1;
297     }
298     free(quota_name);
299
300     asprintf(&quota_name, "%s_%s", extIface, intIface);
301     asprintf(&proc_path, "/proc/net/xt_quota/%s", quota_name);
302     quota_fd = open(proc_path, O_RDONLY);
303     if (quota_fd >= 0) {
304         /* quota for iface pair already exists */
305         free(proc_path);
306         free(quota_name);
307         return 0;
308     }
309     close(quota_fd);
310     free(proc_path);
311
312     const char *cmd3b[] = {
313             IPTABLES_PATH,
314             "-A",
315             LOCAL_TETHER_COUNTERS_CHAIN,
316             "-i",
317             extIface,
318             "-o",
319             intIface,
320             "-m",
321             "quota2",
322             "--name",
323             quota_name,
324             "--grow",
325             "-j",
326             "RETURN"
327     };
328
329     if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) {
330         // unwind what's been done, but don't care about success - what more could we do?
331         free(quota_name);
332         return -1;
333     }
334     free(quota_name);
335     return 0;
336 }
337
338 int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
339     const char *cmd1[] = {
340             IPTABLES_PATH,
341             add ? "-A" : "-D",
342             LOCAL_FORWARD,
343             "-i",
344             extIface,
345             "-o",
346             intIface,
347             "-m",
348             "state",
349             "--state",
350             "ESTABLISHED,RELATED",
351             "-g",
352             LOCAL_TETHER_COUNTERS_CHAIN
353     };
354     int rc = 0;
355
356     if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
357         return -1;
358     }
359
360     const char *cmd2[] = {
361             IPTABLES_PATH,
362             add ? "-A" : "-D",
363             LOCAL_FORWARD,
364             "-i",
365             intIface,
366             "-o",
367             extIface,
368             "-m",
369             "state",
370             "--state",
371             "INVALID",
372             "-j",
373             "DROP"
374     };
375
376     const char *cmd3[] = {
377             IPTABLES_PATH,
378             add ? "-A" : "-D",
379             LOCAL_FORWARD,
380             "-i",
381             intIface,
382             "-o",
383             extIface,
384             "-g",
385             LOCAL_TETHER_COUNTERS_CHAIN
386     };
387
388     if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
389         // bail on error, but only if adding
390         rc = -1;
391         goto err_invalid_drop;
392     }
393
394     if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
395         // unwind what's been done, but don't care about success - what more could we do?
396         rc = -1;
397         goto err_return;
398     }
399
400     if (setTetherCountingRules(add, intIface, extIface) && add) {
401         rc = -1;
402         goto err_return;
403     }
404
405     return 0;
406
407 err_return:
408     cmd2[1] = "-D";
409     runCmd(ARRAY_SIZE(cmd2), cmd2);
410 err_invalid_drop:
411     cmd1[1] = "-D";
412     runCmd(ARRAY_SIZE(cmd1), cmd1);
413     return rc;
414 }
415
416 // nat disable intface extface
417 //  0    1       2       3       4            5
418 // nat enable intface extface addrcnt nated-ipaddr/prelength
419 int NatController::disableNat(const int argc, char **argv) {
420     int i;
421     int addrCount = atoi(argv[4]);
422     const char *intIface = argv[2];
423     const char *extIface = argv[3];
424     int tableNumber;
425
426     if (!checkInterface(intIface) || !checkInterface(extIface)) {
427         ALOGE("Invalid interface specified");
428         errno = ENODEV;
429         return -1;
430     }
431
432     if (argc < 5 + addrCount) {
433         ALOGE("Missing Argument");
434         errno = EINVAL;
435         return -1;
436     }
437
438     setForwardRules(false, intIface, extIface);
439     routesOp(false, intIface, extIface, argv, addrCount);
440     if (--natCount <= 0) {
441         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
442         setDefaults();
443     }
444     return 0;
445 }