NatController.cpp revision 8e188ed5c989ddcc07f0f5e9839493c22d17e7b6
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
38const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
39const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
40
41NatController::NatController(SecondaryTableController *ctrl) {
42    secondaryTableCtrl = ctrl;
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    setDefaults();
68    return 0;
69}
70
71int NatController::setDefaults() {
72    if (runCmd(IPTABLES_PATH, "-F natctrl_FORWARD"))
73        return -1;
74    if (runCmd(IPTABLES_PATH, "-t nat -F natctrl_nat_POSTROUTING"))
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    return 0;
88}
89
90bool NatController::checkInterface(const char *iface) {
91    if (strlen(iface) > IFNAMSIZ) return false;
92    return true;
93}
94
95//  0    1       2       3       4            5
96// nat enable intface extface addrcnt nated-ipaddr/prelength
97int NatController::enableNat(const int argc, char **argv) {
98    char cmd[255];
99    int i;
100    int addrCount = atoi(argv[4]);
101    int ret = 0;
102    const char *intIface = argv[2];
103    const char *extIface = argv[3];
104    int tableNumber;
105
106    if (!checkInterface(intIface) || !checkInterface(extIface)) {
107        ALOGE("Invalid interface specified");
108        errno = ENODEV;
109        return -1;
110    }
111
112    if (argc < 5 + addrCount) {
113        ALOGE("Missing Argument");
114        errno = EINVAL;
115        return -1;
116    }
117
118    tableNumber = secondaryTableCtrl->findTableNumber(extIface);
119    if (tableNumber != -1) {
120        for(i = 0; i < addrCount; i++) {
121            ret |= secondaryTableCtrl->modifyFromRule(tableNumber, ADD, argv[5+i]);
122
123            ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, ADD, intIface, argv[5+i]);
124        }
125        runCmd(IP_PATH, "route flush cache");
126    }
127
128    if (ret != 0 || setForwardRules(true, intIface, extIface) != 0) {
129        if (tableNumber != -1) {
130            for (i = 0; i < addrCount; i++) {
131                secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
132
133                secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
134            }
135            runCmd(IP_PATH, "route flush cache");
136        }
137        ALOGE("Error setting forward rules");
138        errno = ENODEV;
139        return -1;
140    }
141
142    /* Always make sure the drop rule is at the end */
143    snprintf(cmd, sizeof(cmd), "-D natctrl_FORWARD -j DROP");
144    runCmd(IPTABLES_PATH, cmd);
145    snprintf(cmd, sizeof(cmd), "-A natctrl_FORWARD -j DROP");
146    runCmd(IPTABLES_PATH, cmd);
147
148
149    natCount++;
150    // add this if we are the first added nat
151    if (natCount == 1) {
152        snprintf(cmd, sizeof(cmd), "-t nat -A natctrl_nat_POSTROUTING -o %s -j MASQUERADE", extIface);
153        if (runCmd(IPTABLES_PATH, cmd)) {
154            ALOGE("Error seting postroute rule: %s", cmd);
155            // unwind what's been done, but don't care about success - what more could we do?
156            for (i = 0; i < addrCount; i++) {
157                secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
158
159                secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
160            }
161            setDefaults();
162            return -1;
163        }
164    }
165
166    return 0;
167}
168
169int NatController::setForwardRules(bool add, const char *intIface, const char * extIface) {
170    char cmd[255];
171
172    snprintf(cmd, sizeof(cmd),
173             "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
174             (add ? "A" : "D"),
175             extIface, intIface);
176    if (runCmd(IPTABLES_PATH, cmd) && add) {
177        return -1;
178    }
179
180    snprintf(cmd, sizeof(cmd),
181            "-%s natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
182            (add ? "A" : "D"),
183            intIface, extIface);
184    if (runCmd(IPTABLES_PATH, cmd) && add) {
185        // bail on error, but only if adding
186        snprintf(cmd, sizeof(cmd),
187                "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
188                (!add ? "A" : "D"),
189                extIface, intIface);
190        runCmd(IPTABLES_PATH, cmd);
191        return -1;
192    }
193
194    snprintf(cmd, sizeof(cmd), "-%s natctrl_FORWARD -i %s -o %s -j RETURN", (add ? "A" : "D"),
195            intIface, extIface);
196    if (runCmd(IPTABLES_PATH, cmd) && add) {
197        // unwind what's been done, but don't care about success - what more could we do?
198        snprintf(cmd, sizeof(cmd),
199                "-%s natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
200                (!add ? "A" : "D"),
201                intIface, extIface);
202        runCmd(IPTABLES_PATH, cmd);
203
204        snprintf(cmd, sizeof(cmd),
205                 "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
206                 (!add ? "A" : "D"),
207                 extIface, intIface);
208        runCmd(IPTABLES_PATH, cmd);
209        return -1;
210    }
211
212    return 0;
213}
214
215// nat disable intface extface
216//  0    1       2       3       4            5
217// nat enable intface extface addrcnt nated-ipaddr/prelength
218int NatController::disableNat(const int argc, char **argv) {
219    char cmd[255];
220    int i;
221    int addrCount = atoi(argv[4]);
222    const char *intIface = argv[2];
223    const char *extIface = argv[3];
224    int tableNumber;
225
226    if (!checkInterface(intIface) || !checkInterface(extIface)) {
227        ALOGE("Invalid interface specified");
228        errno = ENODEV;
229        return -1;
230    }
231
232    if (argc < 5 + addrCount) {
233        ALOGE("Missing Argument");
234        errno = EINVAL;
235        return -1;
236    }
237
238    setForwardRules(false, intIface, extIface);
239
240    tableNumber = secondaryTableCtrl->findTableNumber(extIface);
241    if (tableNumber != -1) {
242        for (i = 0; i < addrCount; i++) {
243            secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
244
245            secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
246        }
247
248        runCmd(IP_PATH, "route flush cache");
249    }
250
251    if (--natCount <= 0) {
252        // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
253        setDefaults();
254    }
255    return 0;
256}
257