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