NatController.cpp revision 3c20787d7935c2016e8e3cc49d8f15647c12c41c
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
34extern "C" int system_nosh(const char *command);
35
36static char IPTABLES_PATH[] = "/system/bin/iptables";
37static char IP_PATH[] = "/system/bin/ip";
38
39NatController::NatController(SecondaryTableController *ctrl) {
40    secondaryTableCtrl = ctrl;
41    setDefaults();
42}
43
44NatController::~NatController() {
45}
46
47int 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
64int 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
91bool NatController::checkInterface(const char *iface) {
92    if (strlen(iface) > IFNAMSIZ) return false;
93    return true;
94}
95
96const 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
106int 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
181int 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
229int 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}
274