NatController.cpp revision fc97b82e02979f246d56a4bfd60e4aab8686d3f6
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 logwrap(int argc, const char **argv, int background);
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        LOGE("command too long");
53        errno = E2BIG;
54        return -1;
55    }
56
57    asprintf(&buffer, "%s %s", path, cmd);
58    res = system(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, "rule add from all lookup default prio 32767");
78    runCmd(IP_PATH, "rule add from all lookup main prio 32766");
79
80    natCount = 0;
81    return 0;
82}
83
84bool NatController::checkInterface(const char *iface) {
85    if (strlen(iface) > MAX_IFACE_LENGTH) return false;
86    return true;
87}
88
89//  0    1       2       3       4            5
90// nat enable intface extface addrcnt nated-ipaddr/prelength
91int NatController::enableNat(const int argc, char **argv) {
92    char cmd[255];
93    int i;
94    int addrCount = atoi(argv[4]);
95    int ret = 0;
96    const char *intIface = argv[2];
97    const char *extIface = argv[3];
98    int tableNumber;
99
100    if (!checkInterface(intIface) || !checkInterface(extIface)) {
101        LOGE("Invalid interface specified");
102        errno = ENODEV;
103        return -1;
104    }
105
106    if (argc < 5 + addrCount) {
107        LOGE("Missing Argument");
108        errno = EINVAL;
109        return -1;
110    }
111
112    tableNumber = secondaryTableCtrl->findTableNumber(extIface);
113    if (tableNumber != -1) {
114        for(i = 0; i < addrCount && ret == 0; i++) {
115            snprintf(cmd, sizeof(cmd), "rule add from %s table %d", argv[5+i],
116                    tableNumber + BASE_TABLE_NUMBER);
117            ret |= runCmd(IP_PATH, cmd);
118            if (ret) LOGE("IP rule %s got %d", cmd, ret);
119
120            snprintf(cmd, sizeof(cmd), "route add %s dev %s table %d", argv[5+i], intIface,
121                    tableNumber + BASE_TABLE_NUMBER);
122            ret |= runCmd(IP_PATH, cmd);
123            if (ret) LOGE("IP route %s got %d", cmd, ret);
124        }
125    }
126
127    if (ret != 0 || setForwardRules(true, intIface, extIface) != 0) {
128        if (tableNumber != -1) {
129            for (i = 0; i < addrCount; i++) {
130                snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
131                        tableNumber + BASE_TABLE_NUMBER);
132                runCmd(IP_PATH, cmd);
133
134                snprintf(cmd, sizeof(cmd), "rule del from %s table %d", argv[5+i],
135                        tableNumber + BASE_TABLE_NUMBER);
136                runCmd(IP_PATH, cmd);
137            }
138        }
139        LOGE("Error setting forward rules");
140        errno = ENODEV;
141        return -1;
142    }
143
144    natCount++;
145    // add this if we are the first added nat
146    if (natCount == 1) {
147        snprintf(cmd, sizeof(cmd), "-t nat -A POSTROUTING -o %s -j MASQUERADE", extIface);
148        if (runCmd(IPTABLES_PATH, cmd)) {
149            LOGE("Error seting postroute rule: %s", cmd);
150            // unwind what's been done, but don't care about success - what more could we do?
151            for (i = 0; i < addrCount; i++) {
152                snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
153                        tableNumber + BASE_TABLE_NUMBER);
154                runCmd(IP_PATH, cmd);
155            }
156            setDefaults();
157            return -1;
158        }
159    }
160
161    return 0;
162}
163
164int NatController::setForwardRules(bool add, const char *intIface, const char * extIface) {
165    char cmd[255];
166
167    snprintf(cmd, sizeof(cmd),
168             "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
169             (add ? "A" : "D"),
170             extIface, intIface);
171    if (runCmd(IPTABLES_PATH, cmd) && add) {
172        return -1;
173    }
174
175    snprintf(cmd, sizeof(cmd),
176            "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP",
177            (add ? "A" : "D"),
178            intIface, extIface);
179    if (runCmd(IPTABLES_PATH, cmd) && add) {
180        // bail on error, but only if adding
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        runCmd(IPTABLES_PATH, cmd);
186        return -1;
187    }
188
189    snprintf(cmd, sizeof(cmd), "-%s FORWARD -i %s -o %s -j ACCEPT", (add ? "A" : "D"),
190            intIface, extIface);
191    if (runCmd(IPTABLES_PATH, cmd) && add) {
192        // unwind what's been done, but don't care about success - what more could we do?
193        snprintf(cmd, sizeof(cmd),
194                "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP",
195                (!add ? "A" : "D"),
196                intIface, extIface);
197        runCmd(IPTABLES_PATH, cmd);
198
199        snprintf(cmd, sizeof(cmd),
200                 "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
201                 (!add ? "A" : "D"),
202                 extIface, intIface);
203        runCmd(IPTABLES_PATH, cmd);
204        return -1;
205    }
206    return 0;
207}
208
209// nat disable intface extface
210//  0    1       2       3       4            5
211// nat enable intface extface addrcnt nated-ipaddr/prelength
212int NatController::disableNat(const int argc, char **argv) {
213    char cmd[255];
214    int i;
215    int addrCount = atoi(argv[4]);
216    const char *intIface = argv[2];
217    const char *extIface = argv[3];
218    int tableNumber;
219
220    if (!checkInterface(intIface) || !checkInterface(extIface)) {
221        LOGE("Invalid interface specified");
222        errno = ENODEV;
223        return -1;
224    }
225
226    if (argc < 5 + addrCount) {
227        LOGE("Missing Argument");
228        errno = EINVAL;
229        return -1;
230    }
231
232    setForwardRules(false, intIface, extIface);
233
234    tableNumber = secondaryTableCtrl->findTableNumber(extIface);
235    if (tableNumber != -1) {
236        for (i = 0; i < addrCount; i++) {
237            snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
238                    tableNumber + BASE_TABLE_NUMBER);
239            // if the interface has gone down these will be gone already and give errors
240            // ignore them.
241            runCmd(IP_PATH, cmd);
242        }
243    }
244
245    if (--natCount <= 0) {
246        char bootmode[PROPERTY_VALUE_MAX] = {0};
247        property_get("ro.bootmode", bootmode, "unknown");
248        if (0 != strcmp("bp-tools", bootmode)) {
249            // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
250            setDefaults();
251        }
252        natCount = 0;
253    }
254    return 0;
255}
256