RouteController.cpp revision 5c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082f
15c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran/*
25c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran * Copyright (C) 2014 The Android Open Source Project
35c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran *
45c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran * Licensed under the Apache License, Version 2.0 (the "License");
55c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran * you may not use this file except in compliance with the License.
65c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran * You may obtain a copy of the License at
75c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran *
85c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran *      http://www.apache.org/licenses/LICENSE-2.0
95c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran *
105c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran * Unless required by applicable law or agreed to in writing, software
115c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran * distributed under the License is distributed on an "AS IS" BASIS,
125c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran * See the License for the specific language governing permissions and
145c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran * limitations under the License.
155c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran */
165c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
175c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran#include "RouteController.h"
185c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
195c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran#include "Fwmark.h"
205c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran#include "NetdConstants.h"
215c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
225c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran#include <logwrap/logwrap.h>
235c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran#include <net/if.h>
245c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran#include <stdio.h>
255c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
265c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandrannamespace {
275c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
285c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran// TODO: Keep this in sync with the kernel.
295c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranconst uint32_t ROUTE_TABLE_OFFSET_FROM_INDEX = 255;
305c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
315c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranconst char* const RULE_PRIORITY_PER_NETWORK_EXPLICIT = "300";
325c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranconst char* const RULE_PRIORITY_PER_NETWORK_OIF = "400";
335c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranconst char* const RULE_PRIORITY_PER_NETWORK_NORMAL = "700";
345c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
355c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranconst bool FWMARK_USE_NET_ID = true;
365c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranconst bool FWMARK_USE_EXPLICIT = true;
375c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranconst bool FWMARK_USE_PROTECT = true;
385c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
395c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran// TODO: Tell the kernel about the offset using sysctls during init.
405c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranuint32_t getRouteTableForInterface(const char* interface) {
415c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    uint32_t index = static_cast<uint32_t>(if_nametoindex(interface));
425c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    return index ? index + ROUTE_TABLE_OFFSET_FROM_INDEX : 0;
435c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}
445c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
455c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranbool runIpRuleCommand(const char* action, const char* priority, const char* table,
465c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran                      const char* fwmark, const char* oif) {
475c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    const char* version[] = {"-4", "-6"};
485c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    for (size_t i = 0; i < ARRAY_SIZE(version); ++i) {
495c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        int argc = 0;
505c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        const char* argv[16];
515c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
525c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = IP_PATH;
535c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = version[i];
545c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = "rule";
555c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = action;
565c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = "priority";
575c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = priority;
585c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = "table";
595c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = table;
605c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        if (fwmark) {
615c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran            argv[argc++] = "fwmark";
625c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran            argv[argc++] = fwmark;
635c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        }
645c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        if (oif) {
655c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran            argv[argc++] = "oif";
665c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran            argv[argc++] = oif;
675c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        }
685c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
695c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran            return false;
705c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        }
715c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
725c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
735c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    return true;
745c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}
755c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
765c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranbool modifyNetwork(unsigned netId, const char* interface, Permission permission, bool add) {
775c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    uint32_t table = getRouteTableForInterface(interface);
785c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    if (!table) {
795c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        return false;
805c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
815c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
825c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    char table_string[sizeof("0x12345678")];
835c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    snprintf(table_string, sizeof(table_string), "0x%x", table);
845c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
855c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    char mark_string[sizeof("0x12345678/0x12345678")];
865c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    const char* action = add ? ADD : DEL;
875c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
885c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // A rule to route traffic based on an explicitly chosen network.
895c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //
905c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // Supports apps that use the multinetwork APIs to restrict their traffic to a network.
915c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //
925c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // We don't really need to check the permission bits of the fwmark here, as they would've been
935c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // checked at the time the netId was set into the fwmark, but we do so to be consistent.
945c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    uint32_t fwmark = getFwmark(netId, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
955c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
965c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran                                  permission);
975c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
985c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table_string, mark_string,
995c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran                          NULL)) {
1005c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        return false;
1015c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
1025c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1035c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // A rule to route traffic based on a chosen outgoing interface.
1045c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //
1055c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already
1065c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // knows the outgoing interface (typically for link-local communications).
1075c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    fwmark = getFwmark(0, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
1085c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    mask = getFwmark(!FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
1095c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
1105c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_OIF, table_string, mark_string,
1115c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran                          interface)) {
1125c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        return false;
1135c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
1145c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1155c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // A rule to route traffic based on the chosen network.
1165c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //
1175c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // This is for sockets that have not explicitly requested a particular network, but have been
1185c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // bound to one when they called connect(). This ensures that sockets connected on a particular
1195c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // network stay on that network even if the default network changes.
1205c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    fwmark = getFwmark(netId, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
1215c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
1225c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
1235c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table_string, mark_string,
1245c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran                          NULL)) {
1255c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        return false;
1265c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
1275c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1285c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // An iptables rule to mark incoming packets on a network with the netId of the network.
1295c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //
1305c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // This is so that the kernel can:
1315c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // + Use the right fwmark for (and thus correctly route) replies (e.g.: TCP RST, ICMP errors,
1325c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //   ping replies, etc).
1335c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // + Mark sockets that accept connections from this interface so that the connection stays on
1345c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //   the same interface.
1355c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    action = add ? "-A" : "-D";
1365c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    snprintf(mark_string, sizeof(mark_string), "0x%x", netId);
1375c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK",
1385c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran                     "--set-mark", mark_string, NULL)) {
1395c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        return false;
1405c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
1415c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1425c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    return true;
1435c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}
1445c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1455c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}  // namespace
1465c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1475c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranbool RouteController::createNetwork(unsigned netId, const char* interface, Permission permission) {
1485c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    return modifyNetwork(netId, interface, permission, true);
1495c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}
1505c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1515c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranbool RouteController::destroyNetwork(unsigned netId, const char* interface, Permission permission) {
1525c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    return modifyNetwork(netId, interface, permission, false);
1535c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}
154