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