OSDN Git Service

am 3c20787d: Increase the valid name of the iface to IFNAMSIZ
[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 #include <stdlib.h>
18 #include <errno.h>
19 #include <sys/socket.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <string.h>
25 #include <cutils/properties.h>
26
27 #define LOG_TAG "NatController"
28 #include <cutils/log.h>
29
30 #include "NatController.h"
31 #include "SecondaryTableController.h"
32 #include "oem_iptables_hook.h"
33
34 extern "C" int system_nosh(const char *command);
35
36 static char IPTABLES_PATH[] = "/system/bin/iptables";
37 static char IP_PATH[] = "/system/bin/ip";
38
39 NatController::NatController(SecondaryTableController *ctrl) {
40     secondaryTableCtrl = ctrl;
41     setDefaults();
42 }
43
44 NatController::~NatController() {
45 }
46
47 int NatController::runCmd(const char *path, const char *cmd) {
48     char *buffer;
49     size_t len = strnlen(cmd, 255);
50     int res;
51
52     if (len == 255) {
53         LOGE("command too long");
54         errno = E2BIG;
55         return -1;
56     }
57
58     asprintf(&buffer, "%s %s", path, cmd);
59     res = system_nosh(buffer);
60     free(buffer);
61     return res;
62 }
63
64 int NatController::setDefaults() {
65
66     if (runCmd(IPTABLES_PATH, "-P INPUT ACCEPT"))
67         return -1;
68     if (runCmd(IPTABLES_PATH, "-P OUTPUT ACCEPT"))
69         return -1;
70     if (runCmd(IPTABLES_PATH, "-P FORWARD DROP"))
71         return -1;
72     if (runCmd(IPTABLES_PATH, "-F FORWARD"))
73         return -1;
74     if (runCmd(IPTABLES_PATH, "-t nat -F"))
75         return -1;
76
77     runCmd(IP_PATH, "rule flush");
78     runCmd(IP_PATH, "-6 rule flush");
79     runCmd(IP_PATH, "rule add from all lookup default prio 32767");
80     runCmd(IP_PATH, "rule add from all lookup main prio 32766");
81     runCmd(IP_PATH, "-6 rule add from all lookup default prio 32767");
82     runCmd(IP_PATH, "-6 rule add from all lookup main prio 32766");
83     runCmd(IP_PATH, "route flush cache");
84
85     natCount = 0;
86
87     setupOemIptablesHook();
88     return 0;
89 }
90
91 bool NatController::checkInterface(const char *iface) {
92     if (strlen(iface) > IFNAMSIZ) return false;
93     return true;
94 }
95
96 const char *NatController::getVersion(const char *addr) {
97     if (strchr(addr, ':') != NULL) {
98         return "-6";
99     } else {
100         return "-4";
101     }
102 }
103
104 //  0    1       2       3       4            5
105 // nat enable intface extface addrcnt nated-ipaddr/prelength
106 int NatController::enableNat(const int argc, char **argv) {
107     char cmd[255];
108     int i;
109     int addrCount = atoi(argv[4]);
110     int ret = 0;
111     const char *intIface = argv[2];
112     const char *extIface = argv[3];
113     int tableNumber;
114
115     if (!checkInterface(intIface) || !checkInterface(extIface)) {
116         LOGE("Invalid interface specified");
117         errno = ENODEV;
118         return -1;
119     }
120
121     if (argc < 5 + addrCount) {
122         LOGE("Missing Argument");
123         errno = EINVAL;
124         return -1;
125     }
126
127     tableNumber = secondaryTableCtrl->findTableNumber(extIface);
128     if (tableNumber != -1) {
129         for(i = 0; i < addrCount && ret == 0; i++) {
130             snprintf(cmd, sizeof(cmd), "%s rule add from %s table %d", getVersion(argv[5+i]),
131                     argv[5+i], tableNumber + BASE_TABLE_NUMBER);
132             ret |= runCmd(IP_PATH, cmd);
133             if (ret) LOGE("IP rule %s got %d", cmd, ret);
134
135             snprintf(cmd, sizeof(cmd), "route add %s dev %s table %d", argv[5+i], intIface,
136                     tableNumber + BASE_TABLE_NUMBER);
137             ret |= runCmd(IP_PATH, cmd);
138             if (ret) LOGE("IP route %s got %d", cmd, ret);
139         }
140         runCmd(IP_PATH, "route flush cache");
141     }
142
143     if (ret != 0 || setForwardRules(true, intIface, extIface) != 0) {
144         if (tableNumber != -1) {
145             for (i = 0; i < addrCount; i++) {
146                 snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
147                         tableNumber + BASE_TABLE_NUMBER);
148                 runCmd(IP_PATH, cmd);
149
150                 snprintf(cmd, sizeof(cmd), "%s rule del from %s table %d", getVersion(argv[5+i]),
151                         argv[5+i], tableNumber + BASE_TABLE_NUMBER);
152                 runCmd(IP_PATH, cmd);
153             }
154             runCmd(IP_PATH, "route flush cache");
155         }
156         LOGE("Error setting forward rules");
157         errno = ENODEV;
158         return -1;
159     }
160
161     natCount++;
162     // add this if we are the first added nat
163     if (natCount == 1) {
164         snprintf(cmd, sizeof(cmd), "-t nat -A POSTROUTING -o %s -j MASQUERADE", extIface);
165         if (runCmd(IPTABLES_PATH, cmd)) {
166             LOGE("Error seting postroute rule: %s", cmd);
167             // unwind what's been done, but don't care about success - what more could we do?
168             for (i = 0; i < addrCount; i++) {
169                 snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
170                         tableNumber + BASE_TABLE_NUMBER);
171                 runCmd(IP_PATH, cmd);
172             }
173             setDefaults();
174             return -1;
175         }
176     }
177
178     return 0;
179 }
180
181 int NatController::setForwardRules(bool add, const char *intIface, const char * extIface) {
182     char cmd[255];
183
184     snprintf(cmd, sizeof(cmd),
185              "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
186              (add ? "A" : "D"),
187              extIface, intIface);
188     if (runCmd(IPTABLES_PATH, cmd) && add) {
189         return -1;
190     }
191
192     snprintf(cmd, sizeof(cmd),
193             "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP",
194             (add ? "A" : "D"),
195             intIface, extIface);
196     if (runCmd(IPTABLES_PATH, cmd) && add) {
197         // bail on error, but only if adding
198         snprintf(cmd, sizeof(cmd),
199                 "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
200                 (!add ? "A" : "D"),
201                 extIface, intIface);
202         runCmd(IPTABLES_PATH, cmd);
203         return -1;
204     }
205
206     snprintf(cmd, sizeof(cmd), "-%s FORWARD -i %s -o %s -j ACCEPT", (add ? "A" : "D"),
207             intIface, extIface);
208     if (runCmd(IPTABLES_PATH, cmd) && add) {
209         // unwind what's been done, but don't care about success - what more could we do?
210         snprintf(cmd, sizeof(cmd),
211                 "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP",
212                 (!add ? "A" : "D"),
213                 intIface, extIface);
214         runCmd(IPTABLES_PATH, cmd);
215
216         snprintf(cmd, sizeof(cmd),
217                  "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
218                  (!add ? "A" : "D"),
219                  extIface, intIface);
220         runCmd(IPTABLES_PATH, cmd);
221         return -1;
222     }
223     return 0;
224 }
225
226 // nat disable intface extface
227 //  0    1       2       3       4            5
228 // nat enable intface extface addrcnt nated-ipaddr/prelength
229 int NatController::disableNat(const int argc, char **argv) {
230     char cmd[255];
231     int i;
232     int addrCount = atoi(argv[4]);
233     const char *intIface = argv[2];
234     const char *extIface = argv[3];
235     int tableNumber;
236
237     if (!checkInterface(intIface) || !checkInterface(extIface)) {
238         LOGE("Invalid interface specified");
239         errno = ENODEV;
240         return -1;
241     }
242
243     if (argc < 5 + addrCount) {
244         LOGE("Missing Argument");
245         errno = EINVAL;
246         return -1;
247     }
248
249     setForwardRules(false, intIface, extIface);
250
251     tableNumber = secondaryTableCtrl->findTableNumber(extIface);
252     if (tableNumber != -1) {
253         for (i = 0; i < addrCount; i++) {
254             snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
255                     tableNumber + BASE_TABLE_NUMBER);
256             // if the interface has gone down these will be gone already and give errors
257             // ignore them.
258             runCmd(IP_PATH, cmd);
259
260             snprintf(cmd, sizeof(cmd), "%s rule del from %s table %d", getVersion(argv[5+i]),
261                     argv[5+i], tableNumber + BASE_TABLE_NUMBER);
262             runCmd(IP_PATH, cmd);
263         }
264
265         runCmd(IP_PATH, "route flush cache");
266     }
267
268     if (--natCount <= 0) {
269         // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
270         setDefaults();
271     }
272     return 0;
273 }