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 "NetdConstants.h"
36#include "RouteController.h"
37
38const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
39const char* NatController::LOCAL_MANGLE_FORWARD = "natctrl_mangle_FORWARD";
40const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
41const char* NatController::LOCAL_TETHER_COUNTERS_CHAIN = "natctrl_tether_counters";
42
43NatController::NatController() {
44}
45
46NatController::~NatController() {
47}
48
49struct CommandsAndArgs {
50    /* The array size doesn't really matter as the compiler will barf if too many initializers are specified. */
51    const char *cmd[32];
52    bool checkRes;
53};
54
55int NatController::runCmd(int argc, const char **argv) {
56    int res;
57
58    res = android_fork_execvp(argc, (char **)argv, NULL, false, false);
59
60#if !LOG_NDEBUG
61    std::string full_cmd = argv[0];
62    argc--; argv++;
63    /*
64     * HACK: Sometimes runCmd() is called with a ridcously large value (32)
65     * and it works because the argv[] contains a NULL after the last
66     * true argv. So here we use the NULL argv[] to terminate when the argc
67     * is horribly wrong, and argc for the normal cases.
68     */
69    for (; argc && argv[0]; argc--, argv++) {
70        full_cmd += " ";
71        full_cmd += argv[0];
72    }
73    ALOGV("runCmd(%s) res=%d", full_cmd.c_str(), res);
74#endif
75    return res;
76}
77
78int NatController::setupIptablesHooks() {
79    int res;
80    res = setDefaults();
81    if (res < 0) {
82        return res;
83    }
84
85    struct CommandsAndArgs defaultCommands[] = {
86        /*
87         * First chain is for tethering counters.
88         * This chain is reached via --goto, and then RETURNS.
89         *
90         * Second chain is used to limit downstream mss to the upstream pmtu
91         * so we don't end up fragmenting every large packet tethered devices
92         * send.  Note this feature requires kernel support with flag
93         * CONFIG_NETFILTER_XT_TARGET_TCPMSS=y, which not all builds will have,
94         * so the final rule is allowed to fail.
95         * Bug 17629786 asks to make the failure more obvious, or even fatal
96         * so that all builds eventually gain the performance improvement.
97         */
98        {{IPTABLES_PATH, "-w", "-F", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
99        {{IPTABLES_PATH, "-w", "-X", LOCAL_TETHER_COUNTERS_CHAIN,}, 0},
100        {{IPTABLES_PATH, "-w", "-N", LOCAL_TETHER_COUNTERS_CHAIN,}, 1},
101        {{IPTABLES_PATH, "-w", "-t", "mangle", "-A", LOCAL_MANGLE_FORWARD, "-p", "tcp", "--tcp-flags",
102                "SYN", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu"}, 0},
103    };
104    for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
105        if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
106            defaultCommands[cmdNum].checkRes) {
107                return -1;
108        }
109    }
110    ifacePairList.clear();
111
112    return 0;
113}
114
115int NatController::setDefaults() {
116    /*
117     * The following only works because:
118     *  - the defaultsCommands[].cmd array is padded with NULL, and
119     *  - the 1st argc of runCmd() will just be the max for the CommandsAndArgs[].cmd, and
120     *  - internally it will be memcopied to an array and terminated with a NULL.
121     */
122    struct CommandsAndArgs defaultCommands[] = {
123        {{IPTABLES_PATH, "-w", "-F", LOCAL_FORWARD,}, 1},
124        {{IPTABLES_PATH, "-w", "-A", LOCAL_FORWARD, "-j", "DROP"}, 1},
125        {{IPTABLES_PATH, "-w", "-t", "nat", "-F", LOCAL_NAT_POSTROUTING}, 1},
126    };
127    for (unsigned int cmdNum = 0; cmdNum < ARRAY_SIZE(defaultCommands); cmdNum++) {
128        if (runCmd(ARRAY_SIZE(defaultCommands[cmdNum].cmd), defaultCommands[cmdNum].cmd) &&
129            defaultCommands[cmdNum].checkRes) {
130                return -1;
131        }
132    }
133
134    natCount = 0;
135
136    return 0;
137}
138
139int NatController::enableNat(const char* intIface, const char* extIface) {
140    ALOGV("enableNat(intIface=<%s>, extIface=<%s>)",intIface, extIface);
141
142    if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
143        errno = ENODEV;
144        return -1;
145    }
146
147    /* Bug: b/9565268. "enableNat wlan0 wlan0". For now we fail until java-land is fixed */
148    if (!strcmp(intIface, extIface)) {
149        ALOGE("Duplicate interface specified: %s %s", intIface, extIface);
150        errno = EINVAL;
151        return -1;
152    }
153
154    // add this if we are the first added nat
155    if (natCount == 0) {
156        const char *cmd[] = {
157                IPTABLES_PATH,
158                "-w",
159                "-t",
160                "nat",
161                "-A",
162                LOCAL_NAT_POSTROUTING,
163                "-o",
164                extIface,
165                "-j",
166                "MASQUERADE"
167        };
168        if (runCmd(ARRAY_SIZE(cmd), cmd)) {
169            ALOGE("Error setting postroute rule: iface=%s", extIface);
170            // unwind what's been done, but don't care about success - what more could we do?
171            setDefaults();
172            return -1;
173        }
174    }
175
176    if (setForwardRules(true, intIface, extIface) != 0) {
177        ALOGE("Error setting forward rules");
178        if (natCount == 0) {
179            setDefaults();
180        }
181        errno = ENODEV;
182        return -1;
183    }
184
185    /* Always make sure the drop rule is at the end */
186    const char *cmd1[] = {
187            IPTABLES_PATH,
188            "-w",
189            "-D",
190            LOCAL_FORWARD,
191            "-j",
192            "DROP"
193    };
194    runCmd(ARRAY_SIZE(cmd1), cmd1);
195    const char *cmd2[] = {
196            IPTABLES_PATH,
197            "-w",
198            "-A",
199            LOCAL_FORWARD,
200            "-j",
201            "DROP"
202    };
203    runCmd(ARRAY_SIZE(cmd2), cmd2);
204
205    natCount++;
206    return 0;
207}
208
209bool NatController::checkTetherCountingRuleExist(const char *pair_name) {
210    std::list<std::string>::iterator it;
211
212    for (it = ifacePairList.begin(); it != ifacePairList.end(); it++) {
213        if (*it == pair_name) {
214            /* We already have this counter */
215            return true;
216        }
217    }
218    return false;
219}
220
221int NatController::setTetherCountingRules(bool add, const char *intIface, const char *extIface) {
222
223    /* We only ever add tethering quota rules so that they stick. */
224    if (!add) {
225        return 0;
226    }
227    char *pair_name;
228    asprintf(&pair_name, "%s_%s", intIface, extIface);
229
230    if (checkTetherCountingRuleExist(pair_name)) {
231        free(pair_name);
232        return 0;
233    }
234    const char *cmd2b[] = {
235            IPTABLES_PATH,
236            "-w",
237            "-A",
238            LOCAL_TETHER_COUNTERS_CHAIN,
239            "-i",
240            intIface,
241            "-o",
242            extIface,
243            "-j",
244          "RETURN"
245    };
246
247    if (runCmd(ARRAY_SIZE(cmd2b), cmd2b) && add) {
248        free(pair_name);
249        return -1;
250    }
251    ifacePairList.push_front(pair_name);
252    free(pair_name);
253
254    asprintf(&pair_name, "%s_%s", extIface, intIface);
255    if (checkTetherCountingRuleExist(pair_name)) {
256        free(pair_name);
257        return 0;
258    }
259
260    const char *cmd3b[] = {
261            IPTABLES_PATH,
262            "-w",
263            "-A",
264            LOCAL_TETHER_COUNTERS_CHAIN,
265            "-i",
266            extIface,
267            "-o",
268            intIface,
269            "-j",
270            "RETURN"
271    };
272
273    if (runCmd(ARRAY_SIZE(cmd3b), cmd3b) && add) {
274        // unwind what's been done, but don't care about success - what more could we do?
275        free(pair_name);
276        return -1;
277    }
278    ifacePairList.push_front(pair_name);
279    free(pair_name);
280    return 0;
281}
282
283int NatController::setForwardRules(bool add, const char *intIface, const char *extIface) {
284    const char *cmd1[] = {
285            IPTABLES_PATH,
286            "-w",
287            add ? "-A" : "-D",
288            LOCAL_FORWARD,
289            "-i",
290            extIface,
291            "-o",
292            intIface,
293            "-m",
294            "state",
295            "--state",
296            "ESTABLISHED,RELATED",
297            "-g",
298            LOCAL_TETHER_COUNTERS_CHAIN
299    };
300    int rc = 0;
301
302    if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
303        return -1;
304    }
305
306    const char *cmd2[] = {
307            IPTABLES_PATH,
308            "-w",
309            add ? "-A" : "-D",
310            LOCAL_FORWARD,
311            "-i",
312            intIface,
313            "-o",
314            extIface,
315            "-m",
316            "state",
317            "--state",
318            "INVALID",
319            "-j",
320            "DROP"
321    };
322
323    const char *cmd3[] = {
324            IPTABLES_PATH,
325            "-w",
326            add ? "-A" : "-D",
327            LOCAL_FORWARD,
328            "-i",
329            intIface,
330            "-o",
331            extIface,
332            "-g",
333            LOCAL_TETHER_COUNTERS_CHAIN
334    };
335
336    if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
337        // bail on error, but only if adding
338        rc = -1;
339        goto err_invalid_drop;
340    }
341
342    if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
343        // unwind what's been done, but don't care about success - what more could we do?
344        rc = -1;
345        goto err_return;
346    }
347
348    if (setTetherCountingRules(add, intIface, extIface) && add) {
349        rc = -1;
350        goto err_return;
351    }
352
353    return 0;
354
355err_return:
356    cmd2[2] = "-D";
357    runCmd(ARRAY_SIZE(cmd2), cmd2);
358err_invalid_drop:
359    cmd1[2] = "-D";
360    runCmd(ARRAY_SIZE(cmd1), cmd1);
361    return rc;
362}
363
364int NatController::disableNat(const char* intIface, const char* extIface) {
365    if (!isIfaceName(intIface) || !isIfaceName(extIface)) {
366        errno = ENODEV;
367        return -1;
368    }
369
370    setForwardRules(false, intIface, extIface);
371    if (--natCount <= 0) {
372        // handle decrement to 0 case (do reset to defaults) and erroneous dec below 0
373        setDefaults();
374    }
375    return 0;
376}
377