NatController.cpp revision 5ea0c05a1e7d8e664b808aa1bb1efd08fdb2fb13
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
33extern "C" int system_nosh(const char *command);
34
35static char IPTABLES_PATH[] = "/system/bin/iptables";
36static char IP_PATH[] = "/system/bin/ip";
37
38NatController::NatController(SecondaryTableController *ctrl) {
39    secondaryTableCtrl = ctrl;
40    setDefaults();
41}
42
43NatController::~NatController() {
44}
45
46int 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        ALOGE("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
63int 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
88bool NatController::checkInterface(const char *iface) {
89    if (strlen(iface) > MAX_IFACE_LENGTH) return false;
90    return true;
91}
92
93const 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
103int 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        ALOGE("Invalid interface specified");
114        errno = ENODEV;
115        return -1;
116    }
117
118    if (argc < 5 + addrCount) {
119        ALOGE("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) ALOGE("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) ALOGE("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        ALOGE("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            ALOGE("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
178int 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
226int 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        ALOGE("Invalid interface specified");
236        errno = ENODEV;
237        return -1;
238    }
239
240    if (argc < 5 + addrCount) {
241        ALOGE("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}
276