RouteController.cpp revision 38b7af1f2cb9579895465fabc37865f5dadcac25
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
228fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran#include <linux/rtnetlink.h>
235c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran#include <logwrap/logwrap.h>
245c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran#include <net/if.h>
255c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
265c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandrannamespace {
275c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
2838b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandranconst uint32_t RULE_PRIORITY_PRIVILEGED_LEGACY     = 11000;
298fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT  = 13000;
308fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_PER_NETWORK_INTERFACE = 14000;
3138b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandranconst uint32_t RULE_PRIORITY_LEGACY                = 16000;
328fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL    = 17000;
338fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_DEFAULT_NETWORK       = 19000;
348fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_MAIN                  = 20000;
358fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_UNREACHABLE           = 21000;
365c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
3738b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran// TODO: These should be turned into per-UID tables once the kernel supports UID-based routing.
3838b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandranconst int ROUTE_TABLE_PRIVILEGED_LEGACY = RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX - 901;
3938b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandranconst int ROUTE_TABLE_LEGACY            = RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX - 902;
4038b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran
415c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranuint32_t getRouteTableForInterface(const char* interface) {
42a48118062412f16ae712bfc8c8a539d3b6a85e47Sreeram Ramachandran    uint32_t index = if_nametoindex(interface);
43a48118062412f16ae712bfc8c8a539d3b6a85e47Sreeram Ramachandran    return index ? index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX : 0;
445c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}
455c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
468fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// Adds or removes a routing rule for IPv4 and IPv6.
478fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran//
488fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the rule
498fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran//   returns ENETUNREACH.
508fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// + If |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is
518fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran//   ignored.
528fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// + If |interface| is non-NULL, the rule matches the specified outgoing interface.
538fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranbool runIpRuleCommand(const char* action, uint32_t priority, uint32_t table, uint32_t fwmark,
548fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran                      uint32_t mask, const char* interface) {
55a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti    char priorityString[UINT32_STRLEN];
56a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti    snprintf(priorityString, sizeof(priorityString), "%u", priority);
577619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran
587619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    char tableString[UINT32_STRLEN];
59a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti    snprintf(tableString, sizeof(tableString), "%u", table);
60a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti
61a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti    char fwmarkString[sizeof("0x12345678/0x12345678")];
62a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti    snprintf(fwmarkString, sizeof(fwmarkString), "0x%x/0x%x", fwmark, mask);
63a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti
645c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    const char* version[] = {"-4", "-6"};
655c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    for (size_t i = 0; i < ARRAY_SIZE(version); ++i) {
665c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        int argc = 0;
675c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        const char* argv[16];
685c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
695c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = IP_PATH;
705c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = version[i];
715c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = "rule";
725c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = action;
735c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        argv[argc++] = "priority";
74a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti        argv[argc++] = priorityString;
758fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran        if (table) {
768fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran            argv[argc++] = "table";
778fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran            argv[argc++] = tableString;
788fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran        } else {
798fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran            argv[argc++] = "unreachable";
808fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran        }
81a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti        if (mask) {
825c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran            argv[argc++] = "fwmark";
83a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti            argv[argc++] = fwmarkString;
845c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        }
859c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran        if (interface) {
865c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran            argv[argc++] = "oif";
879c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran            argv[argc++] = interface;
885c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        }
895c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
905c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran            return false;
915c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        }
925c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
935c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
945c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    return true;
955c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}
965c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
977619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandranbool runIpRouteCommand(const char* action, uint32_t table, const char* interface,
987619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran                       const char* destination, const char* nexthop) {
997619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    char tableString[UINT32_STRLEN];
1007619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    snprintf(tableString, sizeof(tableString), "%u", table);
1017619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran
1027619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    int argc = 0;
1037619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    const char* argv[16];
1047619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran
1057619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    argv[argc++] = IP_PATH;
1067619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    argv[argc++] = "route";
1077619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    argv[argc++] = action;
1087619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    argv[argc++] = "table";
1097619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    argv[argc++] = tableString;
1107619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    if (destination) {
1117619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran        argv[argc++] = destination;
1127619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran        argv[argc++] = "dev";
1137619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran        argv[argc++] = interface;
1147619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran        if (nexthop) {
1157619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran            argv[argc++] = "via";
1167619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran            argv[argc++] = nexthop;
1177619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran        }
1187619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    }
1197619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran
120a79f6182a338621a32f350d7c2985c6872cccf84Sreeram Ramachandran    return !android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false);
1217619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran}
1227619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran
1239c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandranbool modifyPerNetworkRules(unsigned netId, const char* interface, Permission permission, bool add,
1249c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran                           bool modifyIptables) {
1255c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    uint32_t table = getRouteTableForInterface(interface);
1265c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    if (!table) {
1275c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        return false;
1285c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
1295c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1305c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    const char* action = add ? ADD : DEL;
1315c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
132122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    Fwmark fwmark;
133122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    fwmark.permission = permission;
134122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran
135122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    Fwmark mask;
136122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    mask.permission = permission;
1375c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1385c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // A rule to route traffic based on a chosen outgoing interface.
1395c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //
1405c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already
1415c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // knows the outgoing interface (typically for link-local communications).
142122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_INTERFACE, table, fwmark.intValue,
143122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran                          mask.intValue, interface)) {
1445c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        return false;
1455c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
1465c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1475c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // A rule to route traffic based on the chosen network.
1485c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //
1495c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // This is for sockets that have not explicitly requested a particular network, but have been
1505c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // bound to one when they called connect(). This ensures that sockets connected on a particular
1515c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // network stay on that network even if the default network changes.
152122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    fwmark.netId = netId;
153122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    mask.netId = FWMARK_NET_ID_MASK;
154122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table, fwmark.intValue,
155122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran                          mask.intValue, NULL)) {
156122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran        return false;
157122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    }
158122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran
159122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    // A rule to route traffic based on an explicitly chosen network.
160122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    //
161122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    // Supports apps that use the multinetwork APIs to restrict their traffic to a network.
162122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    //
163122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    // We don't really need to check the permission bits of the fwmark here, as they would've been
164122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    // checked at the time the netId was set into the fwmark, but we do so to be consistent.
165122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    fwmark.explicitlySelected = true;
166122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    mask.explicitlySelected = true;
167122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table, fwmark.intValue,
168122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran                          mask.intValue, NULL)) {
1695c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran        return false;
1705c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
1715c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1725c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // An iptables rule to mark incoming packets on a network with the netId of the network.
1735c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //
1745c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // This is so that the kernel can:
1755c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // + Use the right fwmark for (and thus correctly route) replies (e.g.: TCP RST, ICMP errors,
176a48118062412f16ae712bfc8c8a539d3b6a85e47Sreeram Ramachandran    //   ping replies).
1775c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    // + Mark sockets that accept connections from this interface so that the connection stays on
1785c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    //   the same interface.
179379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran    if (modifyIptables) {
180379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran        action = add ? "-A" : "-D";
181379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran        char markString[UINT32_HEX_STRLEN];
182379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran        snprintf(markString, sizeof(markString), "0x%x", netId);
183379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran        if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK",
184379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran                         "--set-mark", markString, NULL)) {
185379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran            return false;
186379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran        }
1875c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    }
1885c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1895c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran    return true;
1905c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}
1915c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
1929c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandranbool modifyDefaultNetworkRules(const char* interface, Permission permission, const char* action) {
1939c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran    uint32_t table = getRouteTableForInterface(interface);
1949c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran    if (!table) {
1959c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran        return false;
1969c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran    }
1979c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran
198122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    Fwmark fwmark;
199122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    fwmark.netId = 0;
200122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    fwmark.permission = permission;
201122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran
202122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    Fwmark mask;
203122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    mask.netId = FWMARK_NET_ID_MASK;
204122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    mask.permission = permission;
2059c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran
206122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    return runIpRuleCommand(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark.intValue,
207122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran                            mask.intValue, NULL);
2087619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran}
2097619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran
210c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandranbool modifyRoute(const char* interface, const char* destination, const char* nexthop,
21138b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran                 const char* action, RouteController::TableType tableType, unsigned /* uid */) {
21238b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    uint32_t table = 0;
21338b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    switch (tableType) {
21438b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran        case RouteController::INTERFACE: {
21538b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran            table = getRouteTableForInterface(interface);
21638b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran            break;
21738b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran        }
21838b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran        case RouteController::LEGACY: {
21938b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran            // TODO: Use the UID to assign a unique table per UID instead of this fixed table.
22038b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran            table = ROUTE_TABLE_LEGACY;
22138b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran            break;
22238b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran        }
22338b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran        case RouteController::PRIVILEGED_LEGACY: {
22438b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran            // TODO: Use the UID to assign a unique table per UID instead of this fixed table.
22538b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran            table = ROUTE_TABLE_PRIVILEGED_LEGACY;
22638b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran            break;
22738b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran        }
22838b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    }
2297619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran    if (!table) {
2309c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran        return false;
2319c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran    }
2329c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran
233c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran    if (!runIpRouteCommand(action, table, interface, destination, nexthop)) {
234c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran        return false;
235c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran    }
236c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran
237c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran    // If there's no nexthop, this is a directly connected route. Add it to the main table also, to
238c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran    // let the kernel find it when validating nexthops when global routes are added. Don't do this
239c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran    // for IPv6, since all directly-connected routes in v6 are link-local and should already be in
240c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran    // the main table.
241fd6424fdf2cd0516a6c95288610e323dbdfe84ffSreeram Ramachandran    // TODO: A failure here typically means that the route already exists in the main table, so we
242fd6424fdf2cd0516a6c95288610e323dbdfe84ffSreeram Ramachandran    // ignore it. It's wrong to ignore other kinds of failures, but we have no way to distinguish
243fd6424fdf2cd0516a6c95288610e323dbdfe84ffSreeram Ramachandran    // them based on the return status of the 'ip' command. Fix this situation by ignoring errors
244fd6424fdf2cd0516a6c95288610e323dbdfe84ffSreeram Ramachandran    // only when action == ADD && error == EEXIST.
245fd6424fdf2cd0516a6c95288610e323dbdfe84ffSreeram Ramachandran    if (!nexthop && !strchr(destination, ':')) {
246fd6424fdf2cd0516a6c95288610e323dbdfe84ffSreeram Ramachandran        runIpRouteCommand(action, RT_TABLE_MAIN, interface, destination, NULL);
247c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran    }
248c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran
249c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran    return true;
2509c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran}
2519c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran
25292b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandranbool flushRoutes(const char* interface) {
25392b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran    uint32_t table = getRouteTableForInterface(interface);
25492b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran    if (!table) {
25592b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran        return false;
25692b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran    }
25792b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran
25892b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran    return runIpRouteCommand("flush", table, NULL, NULL, NULL);
25992b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran}
26092b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran
2615c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}  // namespace
2625c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
2638fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranvoid RouteController::Init() {
2648fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran    // Add a new rule to look up the 'main' table, with the same selectors as the "default network"
2658fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran    // rule, but with a lower priority. Since the default network rule points to a table with a
2668fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran    // default route, the rule we're adding will never be used for normal routing lookups. However,
2678fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran    // the kernel may fall-through to it to find directly-connected routes when it validates that a
2688fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran    // nexthop (in a route being added) is reachable.
269122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    Fwmark fwmark;
270122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    fwmark.netId = 0;
271122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran
272122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    Fwmark mask;
273122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    mask.netId = FWMARK_NET_ID_MASK;
274122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran
275122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran    runIpRuleCommand(ADD, RULE_PRIORITY_MAIN, RT_TABLE_MAIN, fwmark.intValue, mask.intValue, NULL);
2768fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran
27738b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    // Add rules to allow lookup of legacy routes.
27838b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    //
27938b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    // TODO: Remove these once the kernel supports UID-based routing. Instead, add them on demand
28038b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    // when routes are added.
28138b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    fwmark.netId = 0;
28238b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    mask.netId = 0;
28338b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran
28438b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    fwmark.explicitlySelected = false;
28538b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    mask.explicitlySelected = true;
28638b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran
28738b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    runIpRuleCommand(ADD, RULE_PRIORITY_LEGACY, ROUTE_TABLE_LEGACY, fwmark.intValue, mask.intValue,
28838b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran                     NULL);
28938b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran
29038b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    fwmark.permission = PERMISSION_CONNECTIVITY_INTERNAL;
29138b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    mask.permission = PERMISSION_CONNECTIVITY_INTERNAL;
29238b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran
29338b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    runIpRuleCommand(ADD, RULE_PRIORITY_PRIVILEGED_LEGACY, ROUTE_TABLE_PRIVILEGED_LEGACY,
29438b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran                     fwmark.intValue, mask.intValue, NULL);
29538b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran
2968fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// TODO: Uncomment once we are sure everything works.
2978fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran#if 0
2988fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran    // Add a rule to preempt the pre-defined "from all lookup main" rule. This ensures that packets
2998fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran    // that are already marked with a specific NetId don't fall-through to the main table.
3008fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran    runIpRuleCommand(ADD, RULE_PRIORITY_UNREACHABLE, 0, 0, 0, NULL);
3018fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran#endif
3028fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran}
3038fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran
304ae37e8a4f42b658d5aaf43f312f063944b4aeecbPaul Jensenbool RouteController::addInterfaceToNetwork(unsigned netId, const char* interface,
305ae37e8a4f42b658d5aaf43f312f063944b4aeecbPaul Jensen                                            Permission permission) {
3069c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran    return modifyPerNetworkRules(netId, interface, permission, true, true);
3075c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}
3085c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran
309ae37e8a4f42b658d5aaf43f312f063944b4aeecbPaul Jensenbool RouteController::removeInterfaceFromNetwork(unsigned netId, const char* interface,
310ae37e8a4f42b658d5aaf43f312f063944b4aeecbPaul Jensen                                                 Permission permission) {
31192b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran    return modifyPerNetworkRules(netId, interface, permission, false, true) &&
31292b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran           flushRoutes(interface);
313379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran}
314379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran
315379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandranbool RouteController::modifyNetworkPermission(unsigned netId, const char* interface,
316379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran                                              Permission oldPermission, Permission newPermission) {
317379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran    // Add the new rules before deleting the old ones, to avoid race conditions.
3189c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran    return modifyPerNetworkRules(netId, interface, newPermission, true, false) &&
3199c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran           modifyPerNetworkRules(netId, interface, oldPermission, false, false);
3209c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran}
3219c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran
32272604075e74af459fb4637404fbf030422c6b6b6Sreeram Ramachandranbool RouteController::addToDefaultNetwork(const char* interface, Permission permission) {
3239c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran    return modifyDefaultNetworkRules(interface, permission, ADD);
3249c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran}
3259c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran
32672604075e74af459fb4637404fbf030422c6b6b6Sreeram Ramachandranbool RouteController::removeFromDefaultNetwork(const char* interface, Permission permission) {
3279c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran    return modifyDefaultNetworkRules(interface, permission, DEL);
3285c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran}
3297619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran
3307619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandranbool RouteController::addRoute(const char* interface, const char* destination,
33138b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran                               const char* nexthop, TableType tableType, unsigned uid) {
33238b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    return modifyRoute(interface, destination, nexthop, ADD, tableType, uid);
3337619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran}
3347619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran
3357619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandranbool RouteController::removeRoute(const char* interface, const char* destination,
33638b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran                                  const char* nexthop, TableType tableType, unsigned uid) {
33738b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran    return modifyRoute(interface, destination, nexthop, DEL, tableType, uid);
3387619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran}
339