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