NatController.cpp revision 0031cead820149e2fe3ccb3cc2fe05758a3cb5c2
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// #define LOG_NDEBUG 0
18
19#include <stdlib.h>
20#include <errno.h>
21#include <sys/socket.h>
22#include <sys/stat.h>
23#include <fcntl.h>
24#include <netinet/in.h>
25#include <arpa/inet.h>
26#include <string.h>
27#include <cutils/properties.h>
28
29#define LOG_TAG "NatController"
30#include <cutils/log.h>
31
32#include "NatController.h"
33#include "SecondaryTableController.h"
34#include "NetdConstants.h"
35
36extern "C" int system_nosh(const char *command);
37
38NatController::NatController(SecondaryTableController *ctrl) {
39    secondaryTableCtrl = ctrl;
40
41    setupIptablesHooks();
42    setDefaults();
43}
44
45NatController::~NatController() {
46}
47
48int NatController::runCmd(const char *path, const char *cmd) {
49    char *buffer;
50    size_t len = strnlen(cmd, 255);
51    int res;
52
53    if (len == 255) {
54        ALOGE("command too long");
55        errno = E2BIG;
56        return -1;
57    }
58
59    asprintf(&buffer, "%s %s", path, cmd);
60    res = system_nosh(buffer);
61    ALOGV("runCmd() buffer='%s' res=%d", buffer, res);
62    free(buffer);
63    return res;
64}
65
66int NatController::setupIptablesHooks() {
67    if (runCmd(IPTABLES_PATH, "-P INPUT ACCEPT"))
68        return -1;
69    if (runCmd(IPTABLES_PATH, "-P OUTPUT ACCEPT"))
70        return -1;
71    if (runCmd(IPTABLES_PATH, "-P FORWARD ACCEPT"))
72        return -1;
73
74    // Order is important!
75    // -D to delete any pre-existing jump rule, to prevent dupes (no-op if doesn't exist)
76    // -F to flush the chain (no-op if doesn't exist).
77    // -N to create the chain (no-op if already exist).
78
79    runCmd(IPTABLES_PATH, "-D FORWARD -j natctrl_FORWARD");
80    runCmd(IPTABLES_PATH, "-F natctrl_FORWARD");
81    runCmd(IPTABLES_PATH, "-N natctrl_FORWARD");
82    if (runCmd(IPTABLES_PATH, "-A FORWARD -j natctrl_FORWARD"))
83        return -1;
84
85    runCmd(IPTABLES_PATH, "-t nat -D POSTROUTING -j natctrl_nat_POSTROUTING");
86    runCmd(IPTABLES_PATH, "-t nat -F natctrl_nat_POSTROUTING");
87    runCmd(IPTABLES_PATH, "-t nat -N natctrl_nat_POSTROUTING");
88    if (runCmd(IPTABLES_PATH, "-t nat -A POSTROUTING -j natctrl_nat_POSTROUTING"))
89        return -1;
90
91    return 0;
92}
93
94int NatController::setDefaults() {
95    if (runCmd(IPTABLES_PATH, "-F natctrl_FORWARD"))
96        return -1;
97    if (runCmd(IPTABLES_PATH, "-t nat -F natctrl_nat_POSTROUTING"))
98        return -1;
99
100    runCmd(IP_PATH, "rule flush");
101    runCmd(IP_PATH, "-6 rule flush");
102    runCmd(IP_PATH, "rule add from all lookup default prio 32767");
103    runCmd(IP_PATH, "rule add from all lookup main prio 32766");
104    runCmd(IP_PATH, "-6 rule add from all lookup default prio 32767");
105    runCmd(IP_PATH, "-6 rule add from all lookup main prio 32766");
106    runCmd(IP_PATH, "route flush cache");
107
108    natCount = 0;
109
110    return 0;
111}
112
113bool NatController::checkInterface(const char *iface) {
114    if (strlen(iface) > IFNAMSIZ) return false;
115    return true;
116}
117
118//  0    1       2       3       4            5
119// nat enable intface extface addrcnt nated-ipaddr/prelength
120int NatController::enableNat(const int argc, char **argv) {
121    char cmd[255];
122    int i;
123    int addrCount = atoi(argv[4]);
124    int ret = 0;
125    const char *intIface = argv[2];
126    const char *extIface = argv[3];
127    int tableNumber;
128
129    if (!checkInterface(intIface) || !checkInterface(extIface)) {
130        ALOGE("Invalid interface specified");
131        errno = ENODEV;
132        return -1;
133    }
134
135    if (argc < 5 + addrCount) {
136        ALOGE("Missing Argument");
137        errno = EINVAL;
138        return -1;
139    }
140
141    tableNumber = secondaryTableCtrl->findTableNumber(extIface);
142    if (tableNumber != -1) {
143        for(i = 0; i < addrCount; i++) {
144            ret |= secondaryTableCtrl->modifyFromRule(tableNumber, ADD, argv[5+i]);
145
146            ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, ADD, intIface, argv[5+i]);
147        }
148        runCmd(IP_PATH, "route flush cache");
149    }
150
151    if (ret != 0 || setForwardRules(true, intIface, extIface) != 0) {
152        if (tableNumber != -1) {
153            for (i = 0; i < addrCount; i++) {
154                secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
155
156                secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
157            }
158            runCmd(IP_PATH, "route flush cache");
159        }
160        ALOGE("Error setting forward rules");
161        errno = ENODEV;
162        return -1;
163    }
164
165    /* Always make sure the drop rule is at the end */
166    snprintf(cmd, sizeof(cmd), "-D natctrl_FORWARD -j DROP");
167    runCmd(IPTABLES_PATH, cmd);
168    snprintf(cmd, sizeof(cmd), "-A natctrl_FORWARD -j DROP");
169    runCmd(IPTABLES_PATH, cmd);
170
171
172    natCount++;
173    // add this if we are the first added nat
174    if (natCount == 1) {
175        snprintf(cmd, sizeof(cmd), "-t nat -A natctrl_nat_POSTROUTING -o %s -j MASQUERADE", extIface);
176        if (runCmd(IPTABLES_PATH, cmd)) {
177            ALOGE("Error seting postroute rule: %s", cmd);
178            // unwind what's been done, but don't care about success - what more could we do?
179            for (i = 0; i < addrCount; i++) {
180                secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
181
182                secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
183            }
184            setDefaults();
185            return -1;
186        }
187    }
188
189    return 0;
190}
191
192int NatController::setForwardRules(bool add, const char *intIface, const char * extIface) {
193    char cmd[255];
194
195    snprintf(cmd, sizeof(cmd),
196             "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
197             (add ? "A" : "D"),
198             extIface, intIface);
199    if (runCmd(IPTABLES_PATH, cmd) && add) {
200        return -1;
201    }
202
203    snprintf(cmd, sizeof(cmd),
204            "-%s natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
205            (add ? "A" : "D"),
206            intIface, extIface);
207    if (runCmd(IPTABLES_PATH, cmd) && add) {
208        // bail on error, but only if adding
209        snprintf(cmd, sizeof(cmd),
210                "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
211                (!add ? "A" : "D"),
212                extIface, intIface);
213        runCmd(IPTABLES_PATH, cmd);
214        return -1;
215    }
216
217    snprintf(cmd, sizeof(cmd), "-%s natctrl_FORWARD -i %s -o %s -j RETURN", (add ? "A" : "D"),
218            intIface, extIface);
219    if (runCmd(IPTABLES_PATH, cmd) && add) {
220        // unwind what's been done, but don't care about success - what more could we do?
221        snprintf(cmd, sizeof(cmd),
222                "-%s natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
223                (!add ? "A" : "D"),
224                intIface, extIface);
225        runCmd(IPTABLES_PATH, cmd);
226
227        snprintf(cmd, sizeof(cmd),
228                 "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
229                 (!add ? "A" : "D"),
230                 extIface, intIface);
231        runCmd(IPTABLES_PATH, cmd);
232        return -1;
233    }
234
235    snprintf(cmd, sizeof(cmd), "-%s natctrl_FORWARD -j DROP", (add ? "A" : "D"),
236            intIface, extIface);
237    runCmd(IPTABLES_PATH, cmd);
238
239    return 0;
240}
241
242// nat disable intface extface
243//  0    1       2       3       4            5
244// nat enable intface extface addrcnt nated-ipaddr/prelength
245int NatController::disableNat(const int argc, char **argv) {
246    char cmd[255];
247    int i;
248    int addrCount = atoi(argv[4]);
249    const char *intIface = argv[2];
250    const char *extIface = argv[3];
251    int tableNumber;
252
253    if (!checkInterface(intIface) || !checkInterface(extIface)) {
254        ALOGE("Invalid interface specified");
255        errno = ENODEV;
256        return -1;
257    }
258
259    if (argc < 5 + addrCount) {
260        ALOGE("Missing Argument");
261        errno = EINVAL;
262        return -1;
263    }
264
265    setForwardRules(false, intIface, extIface);
266
267    tableNumber = secondaryTableCtrl->findTableNumber(extIface);
268    if (tableNumber != -1) {
269        for (i = 0; i < addrCount; i++) {
270            secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
271
272            secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
273        }
274
275        runCmd(IP_PATH, "route flush cache");
276    }
277
278    if (--natCount <= 0) {
279        // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
280        setDefaults();
281    }
282    return 0;
283}
284