OSDN Git Service

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