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