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