SecondaryTableController.cpp revision 2251c0fbcf24a9c8fd77b23851f60304087bab2b
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#include <stdlib.h>
18#include <errno.h>
19#include <fcntl.h>
20#include <string.h>
21
22#include <sys/socket.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25#include <sys/wait.h>
26
27#include <netinet/in.h>
28#include <arpa/inet.h>
29
30#define LOG_TAG "SecondaryTablController"
31#include <cutils/log.h>
32#include <cutils/properties.h>
33#include <logwrap/logwrap.h>
34
35#include "ResponseCode.h"
36#include "NetdConstants.h"
37#include "SecondaryTableController.h"
38
39const char* SecondaryTableController::LOCAL_MANGLE_OUTPUT = "st_mangle_OUTPUT";
40const char* SecondaryTableController::LOCAL_MANGLE_IFACE_FORMAT = "st_mangle_%s_OUTPUT";
41const char* SecondaryTableController::LOCAL_NAT_POSTROUTING = "st_nat_POSTROUTING";
42const char* SecondaryTableController::LOCAL_FILTER_OUTPUT = "st_filter_OUTPUT";
43
44SecondaryTableController::SecondaryTableController(UidMarkMap *map) : mUidMarkMap(map) {
45    int i;
46    for (i=0; i < INTERFACES_TRACKED; i++) {
47        mInterfaceTable[i][0] = 0;
48        // TODO - use a hashtable or other prebuilt container class
49        mInterfaceRuleCount[i] = 0;
50    }
51}
52
53SecondaryTableController::~SecondaryTableController() {
54}
55
56int SecondaryTableController::setupIptablesHooks() {
57    int res = execIptables(V4V6,
58            "-t",
59            "mangle",
60            "-F",
61            LOCAL_MANGLE_OUTPUT,
62            NULL);
63    //rule for skipping anything marked with the PROTECT_MARK
64    char protect_mark_str[11];
65    snprintf(protect_mark_str, sizeof(protect_mark_str), "%d", PROTECT_MARK);
66    res |= execIptables(V4V6,
67            "-t",
68            "mangle",
69            "-A",
70            LOCAL_MANGLE_OUTPUT,
71            "-m",
72            "mark",
73            "--mark",
74            protect_mark_str,
75            "-j",
76            "RETURN",
77            NULL);
78
79    //protect the legacy VPN daemons from routes.
80    //TODO: Remove this when legacy VPN's are removed.
81    res |= execIptables(V4V6,
82            "-t",
83            "mangle",
84            "-A",
85            LOCAL_MANGLE_OUTPUT,
86            "-m",
87            "owner",
88            "--uid-owner",
89            "vpn",
90            "-j",
91            "RETURN",
92            NULL);
93    return res;
94
95}
96
97int SecondaryTableController::findTableNumber(const char *iface) {
98    int i;
99    for (i = 0; i < INTERFACES_TRACKED; i++) {
100        // compare through the final null, hence +1
101        if (strncmp(iface, mInterfaceTable[i], IFNAMSIZ + 1) == 0) {
102            return i;
103        }
104    }
105    return -1;
106}
107
108int SecondaryTableController::addRoute(SocketClient *cli, char *iface, char *dest, int prefix,
109        char *gateway) {
110    int tableIndex = findTableNumber(iface);
111    if (tableIndex == -1) {
112        tableIndex = findTableNumber(""); // look for an empty slot
113        if (tableIndex == -1) {
114            ALOGE("Max number of NATed interfaces reached");
115            errno = ENODEV;
116            cli->sendMsg(ResponseCode::OperationFailed, "Max number NATed", true);
117            return -1;
118        }
119        strncpy(mInterfaceTable[tableIndex], iface, IFNAMSIZ);
120        // Ensure null termination even if truncation happened
121        mInterfaceTable[tableIndex][IFNAMSIZ] = 0;
122    }
123
124    return modifyRoute(cli, ADD, iface, dest, prefix, gateway, tableIndex);
125}
126
127int SecondaryTableController::modifyRoute(SocketClient *cli, const char *action, char *iface,
128        char *dest, int prefix, char *gateway, int tableIndex) {
129    char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask
130    char tableIndex_str[11];
131    int ret;
132
133    //  IP tool doesn't like "::" - the equiv of 0.0.0.0 that it accepts for ipv4
134    snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix);
135    snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex + BASE_TABLE_NUMBER);
136
137    if (strcmp("::", gateway) == 0) {
138        const char *cmd[] = {
139                IP_PATH,
140                "route",
141                action,
142                dest_str,
143                "dev",
144                iface,
145                "table",
146                tableIndex_str
147        };
148        ret = runCmd(ARRAY_SIZE(cmd), cmd);
149    } else {
150        const char *cmd[] = {
151                IP_PATH,
152                "route",
153                action,
154                dest_str,
155                "via",
156                gateway,
157                "dev",
158                iface,
159                "table",
160                tableIndex_str
161        };
162        ret = runCmd(ARRAY_SIZE(cmd), cmd);
163    }
164
165    if (ret) {
166        ALOGE("ip route %s failed: %s route %s %s/%d via %s dev %s table %d", action,
167                IP_PATH, action, dest, prefix, gateway, iface, tableIndex+BASE_TABLE_NUMBER);
168        errno = ENODEV;
169        cli->sendMsg(ResponseCode::OperationFailed, "ip route modification failed", true);
170        return -1;
171    }
172
173    if (strcmp(action, ADD) == 0) {
174        mInterfaceRuleCount[tableIndex]++;
175    } else {
176        if (--mInterfaceRuleCount[tableIndex] < 1) {
177            mInterfaceRuleCount[tableIndex] = 0;
178            mInterfaceTable[tableIndex][0] = 0;
179        }
180    }
181    modifyRuleCount(tableIndex, action);
182    cli->sendMsg(ResponseCode::CommandOkay, "Route modified", false);
183    return 0;
184}
185
186void SecondaryTableController::modifyRuleCount(int tableIndex, const char *action) {
187    if (strcmp(action, ADD) == 0) {
188        mInterfaceRuleCount[tableIndex]++;
189    } else {
190        if (--mInterfaceRuleCount[tableIndex] < 1) {
191            mInterfaceRuleCount[tableIndex] = 0;
192            mInterfaceTable[tableIndex][0] = 0;
193        }
194    }
195}
196
197int SecondaryTableController::verifyTableIndex(int tableIndex) {
198    if ((tableIndex < 0) ||
199            (tableIndex >= INTERFACES_TRACKED) ||
200            (mInterfaceTable[tableIndex][0] == 0)) {
201        return -1;
202    } else {
203        return 0;
204    }
205}
206
207const char *SecondaryTableController::getVersion(const char *addr) {
208    if (strchr(addr, ':') != NULL) {
209        return "-6";
210    } else {
211        return "-4";
212    }
213}
214
215IptablesTarget SecondaryTableController::getIptablesTarget(const char *addr) {
216    if (strchr(addr, ':') != NULL) {
217        return V6;
218    } else {
219        return V4;
220    }
221}
222
223int SecondaryTableController::removeRoute(SocketClient *cli, char *iface, char *dest, int prefix,
224        char *gateway) {
225    int tableIndex = findTableNumber(iface);
226    if (tableIndex == -1) {
227        ALOGE("Interface not found");
228        errno = ENODEV;
229        cli->sendMsg(ResponseCode::OperationFailed, "Interface not found", true);
230        return -1;
231    }
232
233    return modifyRoute(cli, DEL, iface, dest, prefix, gateway, tableIndex);
234}
235
236int SecondaryTableController::modifyFromRule(int tableIndex, const char *action,
237        const char *addr) {
238    char tableIndex_str[11];
239
240    if (verifyTableIndex(tableIndex)) {
241        return -1;
242    }
243
244    snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex +
245            BASE_TABLE_NUMBER);
246    const char *cmd[] = {
247            IP_PATH,
248            getVersion(addr),
249            "rule",
250            action,
251            "from",
252            addr,
253            "table",
254            tableIndex_str
255    };
256    if (runCmd(ARRAY_SIZE(cmd), cmd)) {
257        return -1;
258    }
259
260    modifyRuleCount(tableIndex, action);
261    return 0;
262}
263
264int SecondaryTableController::modifyLocalRoute(int tableIndex, const char *action,
265        const char *iface, const char *addr) {
266    char tableIndex_str[11];
267
268    if (verifyTableIndex(tableIndex)) {
269        return -1;
270    }
271
272    modifyRuleCount(tableIndex, action); // some del's will fail as the iface is already gone.
273
274    snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex +
275            BASE_TABLE_NUMBER);
276    const char *cmd[] = {
277            IP_PATH,
278            "route",
279            action,
280            addr,
281            "dev",
282            iface,
283            "table",
284            tableIndex_str
285    };
286
287    return runCmd(ARRAY_SIZE(cmd), cmd);
288}
289int SecondaryTableController::addFwmarkRule(const char *iface) {
290    return setFwmarkRule(iface, true);
291}
292
293int SecondaryTableController::removeFwmarkRule(const char *iface) {
294    return setFwmarkRule(iface, false);
295}
296
297int SecondaryTableController::setFwmarkRule(const char *iface, bool add) {
298    int tableIndex = findTableNumber(iface);
299    if (tableIndex == -1) {
300        tableIndex = findTableNumber(""); // look for an empty slot
301        if (tableIndex == -1) {
302            ALOGE("Max number of NATed interfaces reached");
303            errno = ENODEV;
304            return -1;
305        }
306        strncpy(mInterfaceTable[tableIndex], iface, IFNAMSIZ);
307        // Ensure null termination even if truncation happened
308        mInterfaceTable[tableIndex][IFNAMSIZ] = 0;
309    }
310    int mark = tableIndex + BASE_TABLE_NUMBER;
311    char mark_str[11];
312    int ret;
313
314    //fail fast if any rules already exist for this interface
315    if (mUidMarkMap->anyRulesForMark(mark)) {
316        errno = EBUSY;
317        return -1;
318    }
319
320    snprintf(mark_str, sizeof(mark_str), "%d", mark);
321    //add the catch all route to the tun. Route rules will make sure the right packets hit the table
322    const char *route_cmd[] = {
323        IP_PATH,
324        "route",
325        add ? "add" : "del",
326        "default",
327        "dev",
328        iface,
329        "table",
330        mark_str
331    };
332    ret = runCmd(ARRAY_SIZE(route_cmd), route_cmd);
333
334    const char *fwmark_cmd[] = {
335        IP_PATH,
336        "rule",
337        add ? "add" : "del",
338        "fwmark",
339        mark_str,
340        "table",
341        mark_str
342    };
343    ret = runCmd(ARRAY_SIZE(fwmark_cmd), fwmark_cmd);
344    if (ret) return ret;
345
346    //add rules for v6
347    const char *route6_cmd[] = {
348        IP_PATH,
349        "-6",
350        "route",
351        add ? "add" : "del",
352        "default",
353        "dev",
354        iface,
355        "table",
356        mark_str
357    };
358    ret = runCmd(ARRAY_SIZE(route6_cmd), route6_cmd);
359
360    const char *fwmark6_cmd[] = {
361        IP_PATH,
362        "-6",
363        "rule",
364        add ? "add" : "del",
365        "fwmark",
366        mark_str,
367        "table",
368        mark_str
369    };
370    ret = runCmd(ARRAY_SIZE(fwmark6_cmd), fwmark6_cmd);
371
372
373    if (ret) return ret;
374
375    //create the route rule chain
376    char chain_str[IFNAMSIZ + 18];
377    snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface);
378    //code split due to ordering requirements
379    if (add) {
380        ret = execIptables(V4V6,
381                "-t",
382                "mangle",
383                "-N",
384                chain_str,
385                NULL);
386        //set up the rule for sending premarked packets to the VPN chain
387        //Insert these at the top of the chain so they trigger before any UID rules
388        ret |= execIptables(V4V6,
389                "-t",
390                "mangle",
391                "-I",
392                LOCAL_MANGLE_OUTPUT,
393                "3",
394                "-m",
395                "mark",
396                "--mark",
397                mark_str,
398                "-g",
399                chain_str,
400                NULL);
401        //add a rule to clear the mark in the VPN chain
402        //packets marked with SO_MARK already have the iface's mark set but unless they match a
403        //route they should hit the network instead of the VPN
404        ret |= execIptables(V4V6,
405                "-t",
406                "mangle",
407                "-A",
408                chain_str,
409                "-j",
410                "MARK",
411                "--set-mark",
412                "0",
413                NULL);
414
415    } else {
416        ret = execIptables(V4V6,
417                "-t",
418                "mangle",
419                "-D",
420                LOCAL_MANGLE_OUTPUT,
421                "-m",
422                "mark",
423                "--mark",
424                mark_str,
425                "-g",
426                chain_str,
427                NULL);
428
429        //clear and delete the chain
430        ret |= execIptables(V4V6,
431                "-t",
432                "mangle",
433                "-F",
434                chain_str,
435                NULL);
436
437        ret |= execIptables(V4V6,
438                "-t",
439                "mangle",
440                "-X",
441                chain_str,
442                NULL);
443    }
444
445    //set up the needed source IP rewriting
446    //NOTE: Without ipv6 NAT in the kernel <3.7 only support V4 NAT
447    ret = execIptables(V4,
448            "-t",
449            "nat",
450            add ? "-A" : "-D",
451            LOCAL_NAT_POSTROUTING,
452            "-o",
453            iface,
454            "-m",
455            "mark",
456            "--mark",
457            mark_str,
458            "-j",
459            "MASQUERADE",
460            NULL);
461
462    if (ret) return ret;
463
464    //try and set up for ipv6. ipv6 nat came in the kernel only in 3.7, so this can fail
465    ret = execIptables(V6,
466            "-t",
467            "nat",
468            add ? "-A" : "-D",
469            LOCAL_NAT_POSTROUTING,
470            "-o",
471            iface,
472            "-m",
473            "mark",
474            "--mark",
475            mark_str,
476            "-j",
477            "MASQUERADE",
478            NULL);
479    if (ret) {
480        //Without V6 NAT we can't do V6 over VPNs.
481        ret = execIptables(V6,
482                "-t",
483                "filter",
484                add ? "-A" : "-D",
485                LOCAL_FILTER_OUTPUT,
486                "-m",
487                "mark",
488                "--mark",
489                mark_str,
490                "-j",
491                "REJECT",
492                NULL);
493    }
494    return ret;
495
496}
497
498int SecondaryTableController::addFwmarkRoute(const char* iface, const char *dest, int prefix) {
499    return setFwmarkRoute(iface, dest, prefix, true);
500}
501
502int SecondaryTableController::removeFwmarkRoute(const char* iface, const char *dest, int prefix) {
503    return setFwmarkRoute(iface, dest, prefix, true);
504}
505
506int SecondaryTableController::setFwmarkRoute(const char* iface, const char *dest, int prefix,
507                                             bool add) {
508    int tableIndex = findTableNumber(iface);
509    if (tableIndex == -1) {
510        errno = EINVAL;
511        return -1;
512    }
513    int mark = tableIndex + BASE_TABLE_NUMBER;
514    char mark_str[11] = {0};
515    char chain_str[IFNAMSIZ + 18];
516    char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask
517
518    snprintf(mark_str, sizeof(mark_str), "%d", mark);
519    snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface);
520    snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix);
521    return execIptables(getIptablesTarget(dest),
522            "-t",
523            "mangle",
524            add ? "-A" : "-D",
525            chain_str,
526            "-d",
527            dest_str,
528            "-j",
529            "MARK",
530            "--set-mark",
531            mark_str,
532            NULL);
533}
534
535int SecondaryTableController::addUidRule(const char *iface, int uid_start, int uid_end) {
536    return setUidRule(iface, uid_start, uid_end, true);
537}
538
539int SecondaryTableController::removeUidRule(const char *iface, int uid_start, int uid_end) {
540    return setUidRule(iface, uid_start, uid_end, false);
541}
542
543int SecondaryTableController::setUidRule(const char *iface, int uid_start, int uid_end, bool add) {
544    int tableIndex = findTableNumber(iface);
545    if (tableIndex == -1) {
546        errno = EINVAL;
547        return -1;
548    }
549    int mark = tableIndex + BASE_TABLE_NUMBER;
550    if (add) {
551        if (!mUidMarkMap->add(uid_start, uid_end, mark)) {
552            errno = EINVAL;
553            return -1;
554        }
555    } else {
556        if (!mUidMarkMap->remove(uid_start, uid_end, mark)) {
557            errno = EINVAL;
558            return -1;
559        }
560    }
561    char uid_str[24] = {0};
562    char chain_str[IFNAMSIZ + 18];
563    snprintf(uid_str, sizeof(uid_str), "%d-%d", uid_start, uid_end);
564    snprintf(chain_str, sizeof(chain_str), LOCAL_MANGLE_IFACE_FORMAT, iface);
565    return execIptables(V4V6,
566            "-t",
567            "mangle",
568            add ? "-A" : "-D",
569            LOCAL_MANGLE_OUTPUT,
570            "-m",
571            "owner",
572            "--uid-owner",
573            uid_str,
574            "-g",
575            chain_str,
576            NULL);
577}
578
579int SecondaryTableController::runCmd(int argc, const char **argv) {
580    int ret = 0;
581
582    ret = android_fork_execvp(argc, (char **)argv, NULL, false, false);
583    return ret;
584}
585