RouteController.cpp revision 122f581eb16e06c70cbbc40bd40995775075151f
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "RouteController.h" 18 19#include "Fwmark.h" 20#include "NetdConstants.h" 21 22#include <linux/rtnetlink.h> 23#include <logwrap/logwrap.h> 24#include <net/if.h> 25 26namespace { 27 28const uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT = 13000; 29const uint32_t RULE_PRIORITY_PER_NETWORK_INTERFACE = 14000; 30const uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL = 17000; 31const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 19000; 32const uint32_t RULE_PRIORITY_MAIN = 20000; 33const uint32_t RULE_PRIORITY_UNREACHABLE = 21000; 34 35uint32_t getRouteTableForInterface(const char* interface) { 36 uint32_t index = if_nametoindex(interface); 37 return index ? index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX : 0; 38} 39 40// Adds or removes a routing rule for IPv4 and IPv6. 41// 42// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the rule 43// returns ENETUNREACH. 44// + If |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is 45// ignored. 46// + If |interface| is non-NULL, the rule matches the specified outgoing interface. 47bool runIpRuleCommand(const char* action, uint32_t priority, uint32_t table, uint32_t fwmark, 48 uint32_t mask, const char* interface) { 49 char priorityString[UINT32_STRLEN]; 50 snprintf(priorityString, sizeof(priorityString), "%u", priority); 51 52 char tableString[UINT32_STRLEN]; 53 snprintf(tableString, sizeof(tableString), "%u", table); 54 55 char fwmarkString[sizeof("0x12345678/0x12345678")]; 56 snprintf(fwmarkString, sizeof(fwmarkString), "0x%x/0x%x", fwmark, mask); 57 58 const char* version[] = {"-4", "-6"}; 59 for (size_t i = 0; i < ARRAY_SIZE(version); ++i) { 60 int argc = 0; 61 const char* argv[16]; 62 63 argv[argc++] = IP_PATH; 64 argv[argc++] = version[i]; 65 argv[argc++] = "rule"; 66 argv[argc++] = action; 67 argv[argc++] = "priority"; 68 argv[argc++] = priorityString; 69 if (table) { 70 argv[argc++] = "table"; 71 argv[argc++] = tableString; 72 } else { 73 argv[argc++] = "unreachable"; 74 } 75 if (mask) { 76 argv[argc++] = "fwmark"; 77 argv[argc++] = fwmarkString; 78 } 79 if (interface) { 80 argv[argc++] = "oif"; 81 argv[argc++] = interface; 82 } 83 if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) { 84 return false; 85 } 86 } 87 88 return true; 89} 90 91bool runIpRouteCommand(const char* action, uint32_t table, const char* interface, 92 const char* destination, const char* nexthop) { 93 char tableString[UINT32_STRLEN]; 94 snprintf(tableString, sizeof(tableString), "%u", table); 95 96 int argc = 0; 97 const char* argv[16]; 98 99 argv[argc++] = IP_PATH; 100 argv[argc++] = "route"; 101 argv[argc++] = action; 102 argv[argc++] = "table"; 103 argv[argc++] = tableString; 104 if (destination) { 105 argv[argc++] = destination; 106 argv[argc++] = "dev"; 107 argv[argc++] = interface; 108 if (nexthop) { 109 argv[argc++] = "via"; 110 argv[argc++] = nexthop; 111 } 112 } 113 114 return !android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false); 115} 116 117bool modifyPerNetworkRules(unsigned netId, const char* interface, Permission permission, bool add, 118 bool modifyIptables) { 119 uint32_t table = getRouteTableForInterface(interface); 120 if (!table) { 121 return false; 122 } 123 124 const char* action = add ? ADD : DEL; 125 126 Fwmark fwmark; 127 fwmark.permission = permission; 128 129 Fwmark mask; 130 mask.permission = permission; 131 132 // A rule to route traffic based on a chosen outgoing interface. 133 // 134 // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already 135 // knows the outgoing interface (typically for link-local communications). 136 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_INTERFACE, table, fwmark.intValue, 137 mask.intValue, interface)) { 138 return false; 139 } 140 141 // A rule to route traffic based on the chosen network. 142 // 143 // This is for sockets that have not explicitly requested a particular network, but have been 144 // bound to one when they called connect(). This ensures that sockets connected on a particular 145 // network stay on that network even if the default network changes. 146 fwmark.netId = netId; 147 mask.netId = FWMARK_NET_ID_MASK; 148 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table, fwmark.intValue, 149 mask.intValue, NULL)) { 150 return false; 151 } 152 153 // A rule to route traffic based on an explicitly chosen network. 154 // 155 // Supports apps that use the multinetwork APIs to restrict their traffic to a network. 156 // 157 // We don't really need to check the permission bits of the fwmark here, as they would've been 158 // checked at the time the netId was set into the fwmark, but we do so to be consistent. 159 fwmark.explicitlySelected = true; 160 mask.explicitlySelected = true; 161 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table, fwmark.intValue, 162 mask.intValue, NULL)) { 163 return false; 164 } 165 166 // An iptables rule to mark incoming packets on a network with the netId of the network. 167 // 168 // This is so that the kernel can: 169 // + Use the right fwmark for (and thus correctly route) replies (e.g.: TCP RST, ICMP errors, 170 // ping replies). 171 // + Mark sockets that accept connections from this interface so that the connection stays on 172 // the same interface. 173 if (modifyIptables) { 174 action = add ? "-A" : "-D"; 175 char markString[UINT32_HEX_STRLEN]; 176 snprintf(markString, sizeof(markString), "0x%x", netId); 177 if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK", 178 "--set-mark", markString, NULL)) { 179 return false; 180 } 181 } 182 183 return true; 184} 185 186bool modifyDefaultNetworkRules(const char* interface, Permission permission, const char* action) { 187 uint32_t table = getRouteTableForInterface(interface); 188 if (!table) { 189 return false; 190 } 191 192 Fwmark fwmark; 193 fwmark.netId = 0; 194 fwmark.permission = permission; 195 196 Fwmark mask; 197 mask.netId = FWMARK_NET_ID_MASK; 198 mask.permission = permission; 199 200 return runIpRuleCommand(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark.intValue, 201 mask.intValue, NULL); 202} 203 204bool modifyRoute(const char* interface, const char* destination, const char* nexthop, 205 const char* action) { 206 uint32_t table = getRouteTableForInterface(interface); 207 if (!table) { 208 return false; 209 } 210 211 if (!runIpRouteCommand(action, table, interface, destination, nexthop)) { 212 return false; 213 } 214 215 // If there's no nexthop, this is a directly connected route. Add it to the main table also, to 216 // let the kernel find it when validating nexthops when global routes are added. Don't do this 217 // for IPv6, since all directly-connected routes in v6 are link-local and should already be in 218 // the main table. 219 // TODO: A failure here typically means that the route already exists in the main table, so we 220 // ignore it. It's wrong to ignore other kinds of failures, but we have no way to distinguish 221 // them based on the return status of the 'ip' command. Fix this situation by ignoring errors 222 // only when action == ADD && error == EEXIST. 223 if (!nexthop && !strchr(destination, ':')) { 224 runIpRouteCommand(action, RT_TABLE_MAIN, interface, destination, NULL); 225 } 226 227 return true; 228} 229 230bool flushRoutes(const char* interface) { 231 uint32_t table = getRouteTableForInterface(interface); 232 if (!table) { 233 return false; 234 } 235 236 return runIpRouteCommand("flush", table, NULL, NULL, NULL); 237} 238 239} // namespace 240 241void RouteController::Init() { 242 // Add a new rule to look up the 'main' table, with the same selectors as the "default network" 243 // rule, but with a lower priority. Since the default network rule points to a table with a 244 // default route, the rule we're adding will never be used for normal routing lookups. However, 245 // the kernel may fall-through to it to find directly-connected routes when it validates that a 246 // nexthop (in a route being added) is reachable. 247 Fwmark fwmark; 248 fwmark.netId = 0; 249 250 Fwmark mask; 251 mask.netId = FWMARK_NET_ID_MASK; 252 253 runIpRuleCommand(ADD, RULE_PRIORITY_MAIN, RT_TABLE_MAIN, fwmark.intValue, mask.intValue, NULL); 254 255// TODO: Uncomment once we are sure everything works. 256#if 0 257 // Add a rule to preempt the pre-defined "from all lookup main" rule. This ensures that packets 258 // that are already marked with a specific NetId don't fall-through to the main table. 259 runIpRuleCommand(ADD, RULE_PRIORITY_UNREACHABLE, 0, 0, 0, NULL); 260#endif 261} 262 263bool RouteController::addInterfaceToNetwork(unsigned netId, const char* interface, 264 Permission permission) { 265 return modifyPerNetworkRules(netId, interface, permission, true, true); 266} 267 268bool RouteController::removeInterfaceFromNetwork(unsigned netId, const char* interface, 269 Permission permission) { 270 return modifyPerNetworkRules(netId, interface, permission, false, true) && 271 flushRoutes(interface); 272} 273 274bool RouteController::modifyNetworkPermission(unsigned netId, const char* interface, 275 Permission oldPermission, Permission newPermission) { 276 // Add the new rules before deleting the old ones, to avoid race conditions. 277 return modifyPerNetworkRules(netId, interface, newPermission, true, false) && 278 modifyPerNetworkRules(netId, interface, oldPermission, false, false); 279} 280 281bool RouteController::addDefaultNetwork(const char* interface, Permission permission) { 282 return modifyDefaultNetworkRules(interface, permission, ADD); 283} 284 285bool RouteController::removeDefaultNetwork(const char* interface, Permission permission) { 286 return modifyDefaultNetworkRules(interface, permission, DEL); 287} 288 289bool RouteController::addRoute(const char* interface, const char* destination, 290 const char* nexthop) { 291 return modifyRoute(interface, destination, nexthop, ADD); 292} 293 294bool RouteController::removeRoute(const char* interface, const char* destination, 295 const char* nexthop) { 296 return modifyRoute(interface, destination, nexthop, DEL); 297} 298