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