NatController.cpp revision 56afacf838d24cf8e54d2cf0d8ab9182ab704125
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 <sys/wait.h>
24#include <fcntl.h>
25#include <netinet/in.h>
26#include <arpa/inet.h>
27#include <string.h>
28#include <cutils/properties.h>
29
30#define LOG_TAG "NatController"
31#include <cutils/log.h>
32#include <logwrap/logwrap.h>
33
34#include "NatController.h"
35#include "NetworkController.h"
36#include "SecondaryTableController.h"
37#include "NetdConstants.h"
38
39const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
40const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
41const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
42
43NatController::NatController(SecondaryTableController *table_ctrl, NetworkController* net_ctrl) :
44        mSecondaryTableCtrl(table_ctrl), mNetCtrl(net_ctrl) {
45}
46
47NatController::~NatController() {
48}
49
50struct CommandsAndArgs {
51    /* The array size doesn't really matter as the compiler will barf if too many initializers are specified. */
52    const char *cmd[32];
53    bool checkRes;
54};
55
56int NatController::runCmd(int argc, const char **argv) {
57    int res;
58
59    res = android_fork_execvp(argc, (char **)argv, NULL, false, false);
60
61#if !LOG_NDEBUG
62    std::string full_cmd = argv[0];
63    argc--; argv++;
64    /*
65     * HACK: Sometimes runCmd() is called with a ridcously large value (32)
66     * and it works because the argv[] contains a NULL after the last
67     * true argv. So here we use the NULL argv[] to terminate when the argc
68     * is horribly wrong, and argc for the normal cases.
69     */
70    for (; argc && argv[0]; argc--, argv++) {
71        full_cmd += " ";
72        full_cmd += argv[0];
73    }
74    ALOGV("runCmd(%s) res=%d", full_cmd.c_str(), res);
75#endif
76    return res;
77}
78
79int NatController::setupIptablesHooks() {
80    int res;
81    res = setDefaults();
82    if (res < 0) {
83        return res;
84    }
85
86    struct CommandsAndArgs defaultCommands[] = {
87        /*
88         * Chain for tethering counters.
89         * This chain is reached via --goto, and then RETURNS.
90         */
91        {{IPTABLES_PATH, "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
92        {{IPTABLES_PATH, "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
93        {{IPTABLES_PATH, "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
94    };
95    for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
96        if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
97            defaultCommands[cmdNum].checkRes) {
98                return -1;
99        }
100    }
101    ifacePairList.clear();
102
103    return 0;
104}
105
106int NatController::setDefaults() {
107    /*
108     * The following only works because:
109     *  - the defaultsCommands[].cmd array is padded with NULL, and
110     *  - the 1st argc of runCmd() will just be the max for the CommandsAndArgs[].cmd, and
111     *  - internally it will be memcopied to an array and terminated with a NULL.
112     */
113    struct CommandsAndArgs defaultCommands[] = {
114        {{IPTABLES_PATH, "-F", LOCAL_FORWARD,}, 1},
115        {{IPTABLES_PATH, "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
116        {{IPTABLES_PATH, "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
117        {{IP_PATH, "rule", "flush"}, 0},
118        {{IP_PATH, "-6", "rule", "flush"}, 0},
119        {{IP_PATH, "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
120        {{IP_PATH, "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
121        {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "default", "prio", "32767"}, 0},
122        {{IP_PATH, "-6", "rule", "add", "from", "all", "lookup", "main", "prio", "32766"}, 0},
123        {{IP_PATH, "route", "flush", "cache"}, 0},
124    };
125    for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
126        if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
127            defaultCommands[cmdNum].checkRes) {
128                return -1;
129        }
130    }
131
132    natCount = 0;
133
134    return 0;
135}
136
137bool NatController::checkInterface(const char *iface) {
138    if (strlen(iface) > IFNAMSIZ) return false;
139    return true;
140}
141
142int NatController::routesOp(bool add, const char *intIface, const char *extIface, char **argv, int addrCount) {
143    unsigned netId = mNetCtrl->getNetworkId(extIface);
144    int ret = 0;
145
146    for (int i = 0; i < addrCount; i++) {
147        if (add) {
148            ret |= mSecondaryTableCtrl->modifyFromRule(netId, ADD, argv[5+i]);
149            ret |= mSecondaryTableCtrl->modifyLocalRoute(netId, ADD, intIface, argv[5+i]);
150        } else {
151            ret |= mSecondaryTableCtrl->modifyLocalRoute(netId, DEL, intIface, argv[5+i]);
152            ret |= mSecondaryTableCtrl->modifyFromRule(netId, DEL, argv[5+i]);
153        }
154    }
155    const char *cmd[] = {
156            IP_PATH,
157            "route",
158            "flush",
159            "cache"
160    };
161    runCmd(ARRAY_SIZE(cmd), cmd);
162    return ret;
163}
164
165//  0    1       2       3       4            5
166// nat enable intface extface addrcnt nated-ipaddr/prelength
167int NatController::enableNat(const int argc, char **argv) {
168    int addrCount = atoi(argv[4]);
169    const char *intIface = argv[2];
170    const char *extIface = argv[3];
171
172    ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
173
174    if (!checkInterface(intIface) || !checkInterface(extIface)) {
175        ALOGE("Invalid interface specified");
176        errno = ENODEV;
177        return -1;
178    }
179
180    /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */
181    if (!strcmp(intIface, extIface)) {
182        ALOGE("Duplicate interface specified: %s %s", intIface, extIface);
183        errno = EINVAL;
184        return -1;
185    }
186
187    if (argc < 5 + addrCount) {
188        ALOGE("Missing Argument");
189        errno = EINVAL;
190        return -1;
191    }
192    if (routesOp(true, intIface, extIface, argv, addrCount)) {
193        ALOGE("Error setting route rules");
194        routesOp(false, intIface, extIface, argv, addrCount);
195        errno = ENODEV;
196        return -1;
197    }
198
199    // add this if we are the first added nat
200    if (natCount == 0) {
201        const char *cmd[] = {
202                IPTABLES_PATH,
203                "-t",
204                "nat",
205                "-A",
206                LOCAL_NAT_POSTROUTING,
207                "-o",
208                extIface,
209                "-j",
210                "MASQUERADE"
211        };
212        if (runCmd(ARRAY_SIZE(cmd), cmd)) {
213            ALOGE("Error seting postroute rule: iface=%s", extIface);
214            // unwind what's been done, but don't care about success - what more could we do?
215            routesOp(false, intIface, extIface, argv, addrCount);
216            setDefaults();
217            return -1;
218        }
219    }
220
221
222    if (setForwardRules(true, intIface, extIface) != 0) {
223        ALOGE("Error setting forward rules");
224        routesOp(false, intIface, extIface, argv, addrCount);
225        if (natCount == 0) {
226            setDefaults();
227        }
228        errno = ENODEV;
229        return -1;
230    }
231
232    /* Always make sure the drop rule is at the end */
233    const char *cmd1[] = {
234            IPTABLES_PATH,
235            "-D",
236            LOCAL_FORWARD,
237            "-j",
238            "DROP"
239    };
240    runCmd(ARRAY_SIZE(cmd1), cmd1);
241    const char *cmd2[] = {
242            IPTABLES_PATH,
243            "-A",
244            LOCAL_FORWARD,
245            "-j",
246            "DROP"
247    };
248    runCmd(ARRAY_SIZE(cmd2), cmd2);
249
250    natCount++;
251    return 0;
252}
253
254bool NatController::checkTetherCountingRuleExist(const char *pair_name) {
255    std::list<std::string>::iterator it;
256
257    for (it = ifacePairList.begin(); it != ifacePairList.end(); it++) {
258        if (*it == pair_name) {
259            /* We already have this counter */
260            return true;
261        }
262    }
263    return false;
264}
265
266int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
267
268    /* We only ever add tethering quota rules so that they stick. */
269    if (!add) {
270        return 0;
271    }
272    char *pair_name;
273    asprintf(&pair_name, "%s_%s", intIface, extIface);
274
275    if (checkTetherCountingRuleExist(pair_name)) {
276        free(pair_name);
277        return 0;
278    }
279    const char *cmd2b[] = {
280            IPTABLES_PATH,
281            "-A",
282            LOCAL_TETHER_COUNTERS_CHAIN,
283            "-i",
284            intIface,
285            "-o",
286            extIface,
287            "-j",
288          "RETURN"
289    };
290
291    if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) {
292        free(pair_name);
293        return -1;
294    }
295    ifacePairList.push_front(pair_name);
296    free(pair_name);
297
298    asprintf(&pair_name, "%s_%s", extIface, intIface);
299    if (checkTetherCountingRuleExist(pair_name)) {
300        free(pair_name);
301        return 0;
302    }
303
304    const char *cmd3b[] = {
305            IPTABLES_PATH,
306            "-A",
307            LOCAL_TETHER_COUNTERS_CHAIN,
308            "-i",
309            extIface,
310            "-o",
311            intIface,
312            "-j",
313            "RETURN"
314    };
315
316    if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) {
317        // unwind what's been done, but don't care about success - what more could we do?
318        free(pair_name);
319        return -1;
320    }
321    ifacePairList.push_front(pair_name);
322    free(pair_name);
323    return 0;
324}
325
326int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
327    const char *cmd1[] = {
328            IPTABLES_PATH,
329            add ? "-A" : "-D",
330            LOCAL_FORWARD,
331            "-i",
332            extIface,
333            "-o",
334            intIface,
335            "-m",
336            "state",
337            "--state",
338            "ESTABLISHED,RELATED",
339            "-g",
340            LOCAL_TETHER_COUNTERS_CHAIN
341    };
342    int rc = 0;
343
344    if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
345        return -1;
346    }
347
348    const char *cmd2[] = {
349            IPTABLES_PATH,
350            add ? "-A" : "-D",
351            LOCAL_FORWARD,
352            "-i",
353            intIface,
354            "-o",
355            extIface,
356            "-m",
357            "state",
358            "--state",
359            "INVALID",
360            "-j",
361            "DROP"
362    };
363
364    const char *cmd3[] = {
365            IPTABLES_PATH,
366            add ? "-A" : "-D",
367            LOCAL_FORWARD,
368            "-i",
369            intIface,
370            "-o",
371            extIface,
372            "-g",
373            LOCAL_TETHER_COUNTERS_CHAIN
374    };
375
376    if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
377        // bail on error, but only if adding
378        rc = -1;
379        goto err_invalid_drop;
380    }
381
382    if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
383        // unwind what's been done, but don't care about success - what more could we do?
384        rc = -1;
385        goto err_return;
386    }
387
388    if (setTetherCountingRules(add, intIface, extIface) && add) {
389        rc = -1;
390        goto err_return;
391    }
392
393    return 0;
394
395err_return:
396    cmd2[1] = "-D";
397    runCmd(ARRAY_SIZE(cmd2), cmd2);
398err_invalid_drop:
399    cmd1[1] = "-D";
400    runCmd(ARRAY_SIZE(cmd1), cmd1);
401    return rc;
402}
403
404// nat disable intface extface
405//  0    1       2       3       4            5
406// nat enable intface extface addrcnt nated-ipaddr/prelength
407int NatController::disableNat(const int argc, char **argv) {
408    int addrCount = atoi(argv[4]);
409    const char *intIface = argv[2];
410    const char *extIface = argv[3];
411
412    if (!checkInterface(intIface) || !checkInterface(extIface)) {
413        ALOGE("Invalid interface specified");
414        errno = ENODEV;
415        return -1;
416    }
417
418    if (argc < 5 + addrCount) {
419        ALOGE("Missing Argument");
420        errno = EINVAL;
421        return -1;
422    }
423
424    setForwardRules(false, intIface, extIface);
425    routesOp(false, intIface, extIface, argv, addrCount);
426    if (--natCount <= 0) {
427        // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
428        setDefaults();
429    }
430    return 0;
431}
432