RouteController.cpp revision 5965651602fb8373b75b6ae2d59c6a4d753f2f49
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 22ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti#include <arpa/inet.h> 23ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti#include <errno.h> 244753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti#include <linux/fib_rules.h> 25ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti#include <linux/netlink.h> 268fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran#include <linux/rtnetlink.h> 275c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran#include <logwrap/logwrap.h> 28a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen#include <map> 29ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti#include <netinet/in.h> 305c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran#include <net/if.h> 31ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti#include <sys/socket.h> 32ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti#include <sys/uio.h> 33ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti#include <unistd.h> 34ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti 35ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti// Avoids "non-constant-expression cannot be narrowed from type 'unsigned int' to 'unsigned short'" 36ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti// warnings when using RTA_LENGTH(x) inside static initializers (even when x is already uint16_t). 37ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti#define U16_RTA_LENGTH(x) static_cast<uint16_t>(RTA_LENGTH((x))) 385c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 395c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandrannamespace { 405c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 4138b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandranconst uint32_t RULE_PRIORITY_PRIVILEGED_LEGACY = 11000; 428fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT = 13000; 438fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_PER_NETWORK_INTERFACE = 14000; 4438b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandranconst uint32_t RULE_PRIORITY_LEGACY = 16000; 458fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL = 17000; 468fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 19000; 478fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_MAIN = 20000; 4856afacf838d24cf8e54d2cf0d8ab9182ab704125Sreeram Ramachandran// TODO: Uncomment once we are sure everything works. 4956afacf838d24cf8e54d2cf0d8ab9182ab704125Sreeram Ramachandran#if 0 508fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranconst uint32_t RULE_PRIORITY_UNREACHABLE = 21000; 5156afacf838d24cf8e54d2cf0d8ab9182ab704125Sreeram Ramachandran#endif 525c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 535965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitticonst uid_t kInvalidUid = (uid_t) -1; 545965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti 5538b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran// TODO: These should be turned into per-UID tables once the kernel supports UID-based routing. 5638b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandranconst int ROUTE_TABLE_PRIVILEGED_LEGACY = RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX - 901; 5738b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandranconst int ROUTE_TABLE_LEGACY = RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX - 902; 5838b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran 595965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti// TODO: These values aren't defined by the Linux kernel, because our UID routing changes are not 605965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti// upstream (yet?), so we can't just pick them up from kernel headers. When (if?) the changes make 615965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti// it upstream, we'll remove this and rely on the kernel header values. For now, add a static assert 625965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti// that will warn us if upstream has given these values some other meaning. 635965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitticonst uint16_t FRA_UID_START = 18; 645965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitticonst uint16_t FRA_UID_END = 19; 655965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colittistatic_assert(FRA_UID_START > FRA_MAX, 665965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti "Android-specific FRA_UID_{START,END} values also assigned in Linux uapi. " 675965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti "Check that these values match what the kernel does and then update this assertion."); 685965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti 694753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitticonst uint16_t kNetlinkRequestFlags = NLM_F_REQUEST | NLM_F_ACK; 704753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitticonst uint16_t kNetlinkCreateRequestFlags = kNetlinkRequestFlags | NLM_F_CREATE | NLM_F_EXCL; 714753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti 72a561e121c724e9163b2e256e15eef660e3a326daPaul Jensenstd::map<std::string, uint32_t> interfaceToIndex; 73a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen 745c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandranuint32_t getRouteTableForInterface(const char* interface) { 75a48118062412f16ae712bfc8c8a539d3b6a85e47Sreeram Ramachandran uint32_t index = if_nametoindex(interface); 76a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen if (index) { 77a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen interfaceToIndex[interface] = index; 78a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen } else { 79a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen // If the interface goes away if_nametoindex() will return 0 but we still need to know 80a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen // the index so we can remove the rules and routes. 81a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen std::map<std::string, uint32_t>::iterator it = interfaceToIndex.find(interface); 82a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen if (it != interfaceToIndex.end()) 83a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen index = it->second; 84a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen } 85a48118062412f16ae712bfc8c8a539d3b6a85e47Sreeram Ramachandran return index ? index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX : 0; 865c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran} 875c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 884753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti// Sends a netlink request and expects an ack. 894753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti// |iov| is an array of struct iovec that contains the netlink message payload. 904753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti// The netlink header is generated by this function based on |action| and |flags|. 914753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti// Returns -errno if there was an error or if the kernel reported an error. 924753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colittiint sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen) { 934753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti nlmsghdr nlmsg = { 944753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti .nlmsg_type = action, 954753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti .nlmsg_flags = flags, 964753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti }; 974753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti iov[0].iov_base = &nlmsg; 984753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti iov[0].iov_len = sizeof(nlmsg); 994753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti for (int i = 0; i < iovlen; ++i) { 1004753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti nlmsg.nlmsg_len += iov[i].iov_len; 1014753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti } 1024753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti 1034753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti int ret; 1044753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti struct { 1054753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti nlmsghdr msg; 1064753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti nlmsgerr err; 1074753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti } response; 1084753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti 1094753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti sockaddr_nl kernel = {AF_NETLINK, 0, 0, 0}; 1104753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti int sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); 1114753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti if (sock != -1 && 1124753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti connect(sock, reinterpret_cast<sockaddr*>(&kernel), sizeof(kernel)) != -1 && 1134753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti writev(sock, iov, iovlen) != -1 && 1144753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti (ret = recv(sock, &response, sizeof(response), 0)) != -1) { 1154753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti if (ret == sizeof(response)) { 1164753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti ret = response.err.error; // Netlink errors are negative errno. 1174753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti } else { 1184753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti ret = -EBADMSG; 1194753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti } 1204753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti } else { 1214753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti ret = -errno; 1224753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti } 1234753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti 1244753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti if (sock != -1) { 1254753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti close(sock); 1264753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti } 1274753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti 1284753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti return ret; 1294753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti} 1304753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti 1318fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// Adds or removes a routing rule for IPv4 and IPv6. 1328fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// 1338fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the rule 1348fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// returns ENETUNREACH. 1358fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// + If |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is 1368fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// ignored. 1378fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// + If |interface| is non-NULL, the rule matches the specified outgoing interface. 13896f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti// 13996f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti// Returns 0 on success or negative errno on failure. 14096f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colittiint modifyIpRule(uint16_t action, uint32_t priority, uint32_t table, uint32_t fwmark, uint32_t mask, 1415965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti const char* interface, uid_t uidStart, uid_t uidEnd) { 1424753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti // The interface name must include exactly one terminating NULL and be properly padded, or older 1434753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti // kernels will refuse to delete rules. 1444753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti uint8_t padding[RTA_ALIGNTO] = {0, 0, 0, 0}; 1454753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti uint16_t paddingLength = 0; 1464753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti size_t interfaceLength = 0; 1474753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti char oifname[IFNAMSIZ]; 1484753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti if (interface) { 1494753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti interfaceLength = strlcpy(oifname, interface, IFNAMSIZ) + 1; 1504753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti if (interfaceLength > IFNAMSIZ) { 1514753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti return -ENAMETOOLONG; 1524753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti } 1534753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti paddingLength = RTA_SPACE(interfaceLength) - RTA_LENGTH(interfaceLength); 1544753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti } 1557619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran 1565965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti // Either both start and end UID must be specified, or neither. 1575965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti if ((uidStart == kInvalidUid) != (uidStart == kInvalidUid)) { 1585965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti return -EUSERS; 1595965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti } 1605965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti bool isUidRule = (uidStart != kInvalidUid); 1615965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti 1624753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti // Assemble a rule request and put it in an array of iovec structures. 1634753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti fib_rule_hdr rule = { 1644753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti .action = static_cast<uint8_t>(table ? FR_ACT_TO_TBL : FR_ACT_UNREACHABLE), 1654753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti }; 166a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti 1675965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti rtattr fra_priority = { U16_RTA_LENGTH(sizeof(priority)), FRA_PRIORITY }; 1685965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti rtattr fra_table = { U16_RTA_LENGTH(sizeof(table)), FRA_TABLE }; 1695965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti rtattr fra_fwmark = { U16_RTA_LENGTH(sizeof(fwmark)), FRA_FWMARK }; 1705965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti rtattr fra_fwmask = { U16_RTA_LENGTH(sizeof(mask)), FRA_FWMASK }; 1715965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti rtattr fra_oifname = { U16_RTA_LENGTH(interfaceLength), FRA_OIFNAME }; 1725965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti rtattr fra_uid_start = { U16_RTA_LENGTH(sizeof(uidStart)), FRA_UID_START }; 1735965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti rtattr fra_uid_end = { U16_RTA_LENGTH(sizeof(uidEnd)), FRA_UID_END }; 174a10ac3214f6a582b7fdb66acc43c702731e53d81Lorenzo Colitti 1754753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti iovec iov[] = { 1765965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { NULL, 0 }, 1775965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &rule, sizeof(rule) }, 1785965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &fra_priority, sizeof(fra_priority) }, 1795965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &priority, sizeof(priority) }, 1805965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &fra_table, table ? sizeof(fra_table) : 0 }, 1815965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &table, table ? sizeof(table) : 0 }, 1825965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &fra_fwmark, mask ? sizeof(fra_fwmark) : 0 }, 1835965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &fwmark, mask ? sizeof(fwmark) : 0 }, 1845965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &fra_fwmask, mask ? sizeof(fra_fwmask) : 0 }, 1855965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &mask, mask ? sizeof(mask) : 0 }, 1865965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &fra_uid_start, isUidRule ? sizeof(fra_uid_start) : 0 }, 1875965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &uidStart, isUidRule ? sizeof(uidStart) : 0 }, 1885965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &fra_uid_end, isUidRule ? sizeof(fra_uid_end) : 0 }, 1895965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &uidEnd, isUidRule ? sizeof(uidEnd) : 0 }, 1905965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { &fra_oifname, interface ? sizeof(fra_oifname) : 0 }, 1915965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { oifname, interfaceLength }, 1925965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti { padding, paddingLength }, 1934753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti }; 1944753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti 1954753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti uint16_t flags = (action == RTM_NEWRULE) ? kNetlinkCreateRequestFlags : kNetlinkRequestFlags; 1964753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti uint8_t family[] = {AF_INET, AF_INET6}; 1974753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti for (size_t i = 0; i < ARRAY_SIZE(family); ++i) { 1984753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti rule.family = family[i]; 1994753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti int ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov)); 2004753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti if (ret) { 20196f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti return ret; 2025c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran } 2035c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran } 2045c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 20596f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti return 0; 2065c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran} 2075c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 2085965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colittiint modifyIpRule(uint16_t action, uint32_t priority, uint32_t table, 2095965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti uint32_t fwmark, uint32_t mask, const char* interface) { 2105965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti return modifyIpRule(action, priority, table, fwmark, mask, interface, kInvalidUid, kInvalidUid); 2115965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti} 2125965651602fb8373b75b6ae2d59c6a4d753f2f49Lorenzo Colitti 213ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti// Adds or deletes an IPv4 or IPv6 route. 214ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti// Returns 0 on success or negative errno on failure. 215ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colittiint modifyIpRoute(uint16_t action, uint32_t table, const char* interface, const char* destination, 216ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti const char* nexthop) { 217ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti // At least the destination must be non-null. 218ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti if (!destination) { 219ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti return -EFAULT; 220ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti } 221ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti 222ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti // Parse the prefix. 223ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti uint8_t rawAddress[sizeof(in6_addr)]; 224ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti uint8_t family, prefixLength; 225ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti int rawLength = parsePrefix(destination, &family, rawAddress, sizeof(rawAddress), 226ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti &prefixLength); 227ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti if (rawLength < 0) { 228ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti return rawLength; 229ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti } 2307619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran 231ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti if (static_cast<size_t>(rawLength) > sizeof(rawAddress)) { 232ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti return -ENOBUFS; // Cannot happen; parsePrefix only supports IPv4 and IPv6. 233ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti } 234ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti 235ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti // If an interface was specified, find the ifindex. 236ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti uint32_t ifindex; 237ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti if (interface) { 238ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti ifindex = if_nametoindex(interface); 239ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti if (!ifindex) { 240ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti return -ENODEV; 2417619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran } 2427619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran } 2437619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran 244ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti // If a nexthop was specified, parse it as the same family as the prefix. 245ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti uint8_t rawNexthop[sizeof(in6_addr)]; 246ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti if (nexthop && !inet_pton(family, nexthop, rawNexthop)) { 247ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti return -EINVAL; 248ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti } 249ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti 2504753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti // Assemble a rtmsg and put it in an array of iovec structures. 251ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti rtmsg rtmsg = { 252ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti .rtm_protocol = RTPROT_STATIC, 253ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti .rtm_type = RTN_UNICAST, 254ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti .rtm_family = family, 255ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti .rtm_dst_len = prefixLength, 256ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti }; 2574753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti 2584753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti rtattr rta_table = { U16_RTA_LENGTH(sizeof(table)), RTA_TABLE }; 2594753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti rtattr rta_oif = { U16_RTA_LENGTH(sizeof(ifindex)), RTA_OIF }; 2604753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti rtattr rta_dst = { U16_RTA_LENGTH(rawLength), RTA_DST }; 2614753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti rtattr rta_gateway = { U16_RTA_LENGTH(rawLength), RTA_GATEWAY }; 262ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti 263ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti iovec iov[] = { 2644753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti { NULL, 0 }, 265ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti { &rtmsg, sizeof(rtmsg) }, 266ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti { &rta_table, sizeof(rta_table) }, 267ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti { &table, sizeof(table) }, 268ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti { &rta_dst, sizeof(rta_dst) }, 269ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti { rawAddress, static_cast<size_t>(rawLength) }, 270ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti { &rta_oif, interface ? sizeof(rta_oif) : 0 }, 2717f972fb1cd3c26af76779a7a3220b9cf5fb63a0aSreeram Ramachandran { &ifindex, interface ? sizeof(ifindex) : 0 }, 272ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti { &rta_gateway, nexthop ? sizeof(rta_gateway) : 0 }, 273ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti { rawNexthop, nexthop ? static_cast<size_t>(rawLength) : 0 }, 274ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti }; 275ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti 2764753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti uint16_t flags = (action == RTM_NEWROUTE) ? kNetlinkCreateRequestFlags : kNetlinkRequestFlags; 2774753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti return sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov)); 2787619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran} 2797619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran 28096f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colittiint modifyPerNetworkRules(unsigned netId, const char* interface, Permission permission, bool add, 28196f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti bool modifyIptables) { 2825c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran uint32_t table = getRouteTableForInterface(interface); 2835c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran if (!table) { 28496f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti return -ESRCH; 2855c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran } 2865c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 2874753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti uint16_t action = add ? RTM_NEWRULE : RTM_DELRULE; 28896f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti int ret; 2895c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 290122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran Fwmark fwmark; 291122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran fwmark.permission = permission; 292122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran 293122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran Fwmark mask; 294122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran mask.permission = permission; 2955c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 2965c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // A rule to route traffic based on a chosen outgoing interface. 2975c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // 2985c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already 2995c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // knows the outgoing interface (typically for link-local communications). 30096f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti if ((ret = modifyIpRule(action, RULE_PRIORITY_PER_NETWORK_INTERFACE, table, fwmark.intValue, 30196f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti mask.intValue, interface)) != 0) { 30296f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti return ret; 3035c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran } 3045c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 3055c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // A rule to route traffic based on the chosen network. 3065c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // 3075c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // This is for sockets that have not explicitly requested a particular network, but have been 3085c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // bound to one when they called connect(). This ensures that sockets connected on a particular 3095c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // network stay on that network even if the default network changes. 310122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran fwmark.netId = netId; 311122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran mask.netId = FWMARK_NET_ID_MASK; 31296f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti if ((ret = modifyIpRule(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table, fwmark.intValue, 31396f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti mask.intValue, NULL)) != 0) { 31496f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti return ret; 315122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran } 316122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran 317122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran // A rule to route traffic based on an explicitly chosen network. 318122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran // 319122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran // Supports apps that use the multinetwork APIs to restrict their traffic to a network. 320122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran // 321122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran // We don't really need to check the permission bits of the fwmark here, as they would've been 322122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran // checked at the time the netId was set into the fwmark, but we do so to be consistent. 323122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran fwmark.explicitlySelected = true; 324122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran mask.explicitlySelected = true; 32596f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti if ((ret = modifyIpRule(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table, fwmark.intValue, 32696f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti mask.intValue, NULL)) != 0) { 32796f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti return ret; 3285c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran } 3295c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 3305c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // An iptables rule to mark incoming packets on a network with the netId of the network. 3315c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // 3325c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // This is so that the kernel can: 3335c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // + Use the right fwmark for (and thus correctly route) replies (e.g.: TCP RST, ICMP errors, 334a48118062412f16ae712bfc8c8a539d3b6a85e47Sreeram Ramachandran // ping replies). 3355c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // + Mark sockets that accept connections from this interface so that the connection stays on 3365c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran // the same interface. 337379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran if (modifyIptables) { 3384753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti const char* iptablesAction = add ? "-A" : "-D"; 339379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran char markString[UINT32_HEX_STRLEN]; 340379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran snprintf(markString, sizeof(markString), "0x%x", netId); 3414753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti if (execIptables(V4V6, "-t", "mangle", iptablesAction, "INPUT", "-i", interface, 3424753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti "-j", "MARK", "--set-mark", markString, NULL)) { 34396f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti return -EREMOTEIO; 344379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran } 3455c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran } 3465c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 34796f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti return 0; 3485c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran} 3495c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 35096f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colittiint modifyDefaultNetworkRules(const char* interface, Permission permission, uint16_t action) { 3519c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran uint32_t table = getRouteTableForInterface(interface); 3529c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran if (!table) { 35396f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti return -ESRCH; 3549c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran } 3559c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran 356122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran Fwmark fwmark; 357122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran fwmark.netId = 0; 358122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran fwmark.permission = permission; 359122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran 360122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran Fwmark mask; 361122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran mask.netId = FWMARK_NET_ID_MASK; 362122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran mask.permission = permission; 3639c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran 3644753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti return modifyIpRule(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark.intValue, 3654753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti mask.intValue, NULL); 3667619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran} 3677619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran 368f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti// Adds or removes an IPv4 or IPv6 route to the specified table and, if it's directly-connected 369f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti// route, to the main table as well. 370f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti// Returns 0 on success or negative errno on failure. 371f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colittiint modifyRoute(const char* interface, const char* destination, const char* nexthop, 3724753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti uint16_t action, RouteController::TableType tableType, unsigned /* uid */) { 37338b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran uint32_t table = 0; 37438b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran switch (tableType) { 37538b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran case RouteController::INTERFACE: { 37638b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran table = getRouteTableForInterface(interface); 37738b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran break; 37838b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran } 37938b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran case RouteController::LEGACY: { 38038b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran // TODO: Use the UID to assign a unique table per UID instead of this fixed table. 38138b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran table = ROUTE_TABLE_LEGACY; 38238b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran break; 38338b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran } 38438b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran case RouteController::PRIVILEGED_LEGACY: { 38538b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran // TODO: Use the UID to assign a unique table per UID instead of this fixed table. 38638b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran table = ROUTE_TABLE_PRIVILEGED_LEGACY; 38738b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran break; 38838b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran } 38938b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran } 3907619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran if (!table) { 391f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti return -ESRCH; 3929c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran } 3939c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran 394f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti int ret = modifyIpRoute(action, table, interface, destination, nexthop); 395f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti if (ret != 0) { 396f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti return ret; 397c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran } 398c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran 399c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran // If there's no nexthop, this is a directly connected route. Add it to the main table also, to 400f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti // let the kernel find it when validating nexthops when global routes are added. 401f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti if (!nexthop) { 402f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti ret = modifyIpRoute(action, RT_TABLE_MAIN, interface, destination, NULL); 403f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti // A failure with action == ADD && errno == EEXIST means that the route already exists in 404f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti // the main table, perhaps because the kernel added it automatically as part of adding the 405f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti // IP address to the interface. Ignore this, but complain about everything else. 406f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti if (ret != 0 && !(action == RTM_NEWROUTE && ret == -EEXIST)) { 407f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti return ret; 408f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti } 409c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran } 410c92133732378aae815120c39edd62a7b4eb773b3Sreeram Ramachandran 411f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti return 0; 4129c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran} 4139c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran 41492b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandranbool flushRoutes(const char* interface) { 41592b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran uint32_t table = getRouteTableForInterface(interface); 41692b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran if (!table) { 41792b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran return false; 41892b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran } 419a561e121c724e9163b2e256e15eef660e3a326daPaul Jensen interfaceToIndex.erase(interface); 42092b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran 421357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti char tableString[UINT32_STRLEN]; 422357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti snprintf(tableString, sizeof(tableString), "%u", table); 423357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti 424357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti const char* version[] = {"-4", "-6"}; 425357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti for (size_t i = 0; i < ARRAY_SIZE(version); ++i) { 426357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti const char* argv[] = { 427357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti IP_PATH, 428357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti version[i], 429357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti "route" 430357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti "flush", 431357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti "table", 432357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti tableString, 433357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti }; 434357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti int argc = ARRAY_SIZE(argv); 435357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti 436357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti if (!android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) { 437357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti return false; 438357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti } 439357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti } 440357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti 441357e5629bb4c745296ab40340ec8679372337155Lorenzo Colitti return true; 44292b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran} 44392b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran 4445c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran} // namespace 4455c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 4468fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandranvoid RouteController::Init() { 4478fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran // Add a new rule to look up the 'main' table, with the same selectors as the "default network" 4488fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran // rule, but with a lower priority. Since the default network rule points to a table with a 4498fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran // default route, the rule we're adding will never be used for normal routing lookups. However, 4508fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran // the kernel may fall-through to it to find directly-connected routes when it validates that a 4518fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran // nexthop (in a route being added) is reachable. 452122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran Fwmark fwmark; 453122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran fwmark.netId = 0; 454122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran 455122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran Fwmark mask; 456122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran mask.netId = FWMARK_NET_ID_MASK; 457122f581eb16e06c70cbbc40bd40995775075151fSreeram Ramachandran 4584753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_MAIN, RT_TABLE_MAIN, fwmark.intValue, mask.intValue, 4594753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti NULL); 4608fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran 46138b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran // Add rules to allow lookup of legacy routes. 46238b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran // 46338b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran // TODO: Remove these once the kernel supports UID-based routing. Instead, add them on demand 46438b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran // when routes are added. 46538b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran fwmark.netId = 0; 46638b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran mask.netId = 0; 46738b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran 46838b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran fwmark.explicitlySelected = false; 46938b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran mask.explicitlySelected = true; 47038b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran 4714753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_LEGACY, ROUTE_TABLE_LEGACY, fwmark.intValue, 4724753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti mask.intValue, NULL); 47338b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran 47438b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran fwmark.permission = PERMISSION_CONNECTIVITY_INTERNAL; 47538b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran mask.permission = PERMISSION_CONNECTIVITY_INTERNAL; 47638b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran 4774753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_PRIVILEGED_LEGACY, ROUTE_TABLE_PRIVILEGED_LEGACY, 4784753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti fwmark.intValue, mask.intValue, NULL); 47938b7af1f2cb9579895465fabc37865f5dadcac25Sreeram Ramachandran 4808fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran// TODO: Uncomment once we are sure everything works. 4818fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran#if 0 4828fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran // Add a rule to preempt the pre-defined "from all lookup main" rule. This ensures that packets 4838fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran // that are already marked with a specific NetId don't fall-through to the main table. 4844753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_UNREACHABLE, 0, 0, 0, NULL); 4858fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran#endif 4868fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran} 4878fe9c8e0a2b1c5cd2a34720efaccc641d9ab8fb6Sreeram Ramachandran 48896f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colittiint RouteController::addInterfaceToNetwork(unsigned netId, const char* interface, 48996f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti Permission permission) { 4909c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran return modifyPerNetworkRules(netId, interface, permission, true, true); 4915c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran} 4925c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran 49396f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colittiint RouteController::removeInterfaceFromNetwork(unsigned netId, const char* interface, 49496f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti Permission permission) { 49592b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran return modifyPerNetworkRules(netId, interface, permission, false, true) && 49692b66c4990b4a0ab608aa9c31da946f36085203bSreeram Ramachandran flushRoutes(interface); 497379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran} 498379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran 49996f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colittiint RouteController::modifyNetworkPermission(unsigned netId, const char* interface, 50096f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colitti Permission oldPermission, Permission newPermission) { 501379bd33f7640e2c4bef902be0ed6cb96378c8c2eSreeram Ramachandran // Add the new rules before deleting the old ones, to avoid race conditions. 5029c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran return modifyPerNetworkRules(netId, interface, newPermission, true, false) && 5039c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran modifyPerNetworkRules(netId, interface, oldPermission, false, false); 5049c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran} 5059c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran 50696f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colittiint RouteController::addToDefaultNetwork(const char* interface, Permission permission) { 5074753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti return modifyDefaultNetworkRules(interface, permission, RTM_NEWRULE); 5089c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran} 5099c0d313de6a3157fadd3b52a9927c77216ca435eSreeram Ramachandran 51096f261e8b28048b8cb48f5a4e81822c73bb813f4Lorenzo Colittiint RouteController::removeFromDefaultNetwork(const char* interface, Permission permission) { 5114753afd79e130d5f1c888f549c36b4da92dbe680Lorenzo Colitti return modifyDefaultNetworkRules(interface, permission, RTM_DELRULE); 5125c181bf8ca0c89bd9e3e6d8e40bac53d0ee7082fSreeram Ramachandran} 5137619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran 514f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colittiint RouteController::addRoute(const char* interface, const char* destination, 515f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti const char* nexthop, TableType tableType, unsigned uid) { 516ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti return modifyRoute(interface, destination, nexthop, RTM_NEWROUTE, tableType, uid); 5177619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran} 5187619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran 519f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colittiint RouteController::removeRoute(const char* interface, const char* destination, 520f7fc8eccb0a6a4fbca4cafdf53f5c167c8f1d755Lorenzo Colitti const char* nexthop, TableType tableType, unsigned uid) { 521ba25df989b48f36b784ad39307a49a4fd9c3fd66Lorenzo Colitti return modifyRoute(interface, destination, nexthop, RTM_DELROUTE, tableType, uid); 5227619e1bbebdfe643c35ee6be4ac054f5255f0706Sreeram Ramachandran} 523