RouteController.cpp revision a561e121c724e9163b2e256e15eef660e3a326da
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 <map> 25#include <net/if.h> 26 27namespace { 28 29const uint32_t RULE_PRIORITY_PRIVILEGED_LEGACY = 11000; 30const uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT = 13000; 31const uint32_t RULE_PRIORITY_PER_NETWORK_INTERFACE = 14000; 32const uint32_t RULE_PRIORITY_LEGACY = 16000; 33const uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL = 17000; 34const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 19000; 35const uint32_t RULE_PRIORITY_MAIN = 20000; 36// TODO: Uncomment once we are sure everything works. 37#if 0 38const uint32_t RULE_PRIORITY_UNREACHABLE = 21000; 39#endif 40 41// TODO: These should be turned into per-UID tables once the kernel supports UID-based routing. 42const int ROUTE_TABLE_PRIVILEGED_LEGACY = RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX - 901; 43const int ROUTE_TABLE_LEGACY = RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX - 902; 44 45std::map<std::string, uint32_t> interfaceToIndex; 46 47uint32_t getRouteTableForInterface(const char* interface) { 48 uint32_t index = if_nametoindex(interface); 49 if (index) { 50 interfaceToIndex[interface] = index; 51 } else { 52 // If the interface goes away if_nametoindex() will return 0 but we still need to know 53 // the index so we can remove the rules and routes. 54 std::map<std::string, uint32_t>::iterator it = interfaceToIndex.find(interface); 55 if (it != interfaceToIndex.end()) 56 index = it->second; 57 } 58 return index ? index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX : 0; 59} 60 61// Adds or removes a routing rule for IPv4 and IPv6. 62// 63// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the rule 64// returns ENETUNREACH. 65// + If |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is 66// ignored. 67// + If |interface| is non-NULL, the rule matches the specified outgoing interface. 68bool runIpRuleCommand(const char* action, uint32_t priority, uint32_t table, uint32_t fwmark, 69 uint32_t mask, const char* interface) { 70 char priorityString[UINT32_STRLEN]; 71 snprintf(priorityString, sizeof(priorityString), "%u", priority); 72 73 char tableString[UINT32_STRLEN]; 74 snprintf(tableString, sizeof(tableString), "%u", table); 75 76 char fwmarkString[sizeof("0x12345678/0x12345678")]; 77 snprintf(fwmarkString, sizeof(fwmarkString), "0x%x/0x%x", fwmark, mask); 78 79 const char* version[] = {"-4", "-6"}; 80 for (size_t i = 0; i < ARRAY_SIZE(version); ++i) { 81 int argc = 0; 82 const char* argv[16]; 83 84 argv[argc++] = IP_PATH; 85 argv[argc++] = version[i]; 86 argv[argc++] = "rule"; 87 argv[argc++] = action; 88 argv[argc++] = "priority"; 89 argv[argc++] = priorityString; 90 if (table) { 91 argv[argc++] = "table"; 92 argv[argc++] = tableString; 93 } else { 94 argv[argc++] = "unreachable"; 95 } 96 if (mask) { 97 argv[argc++] = "fwmark"; 98 argv[argc++] = fwmarkString; 99 } 100 if (interface) { 101 argv[argc++] = "oif"; 102 argv[argc++] = interface; 103 } 104 if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) { 105 return false; 106 } 107 } 108 109 return true; 110} 111 112bool runIpRouteCommand(const char* action, uint32_t table, const char* interface, 113 const char* destination, const char* nexthop) { 114 char tableString[UINT32_STRLEN]; 115 snprintf(tableString, sizeof(tableString), "%u", table); 116 117 int argc = 0; 118 const char* argv[16]; 119 120 argv[argc++] = IP_PATH; 121 argv[argc++] = "route"; 122 argv[argc++] = action; 123 argv[argc++] = "table"; 124 argv[argc++] = tableString; 125 if (destination) { 126 argv[argc++] = destination; 127 argv[argc++] = "dev"; 128 argv[argc++] = interface; 129 if (nexthop) { 130 argv[argc++] = "via"; 131 argv[argc++] = nexthop; 132 } 133 } 134 135 return !android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false); 136} 137 138bool modifyPerNetworkRules(unsigned netId, const char* interface, Permission permission, bool add, 139 bool modifyIptables) { 140 uint32_t table = getRouteTableForInterface(interface); 141 if (!table) { 142 return false; 143 } 144 145 const char* action = add ? ADD : DEL; 146 147 Fwmark fwmark; 148 fwmark.permission = permission; 149 150 Fwmark mask; 151 mask.permission = permission; 152 153 // A rule to route traffic based on a chosen outgoing interface. 154 // 155 // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already 156 // knows the outgoing interface (typically for link-local communications). 157 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_INTERFACE, table, fwmark.intValue, 158 mask.intValue, interface)) { 159 return false; 160 } 161 162 // A rule to route traffic based on the chosen network. 163 // 164 // This is for sockets that have not explicitly requested a particular network, but have been 165 // bound to one when they called connect(). This ensures that sockets connected on a particular 166 // network stay on that network even if the default network changes. 167 fwmark.netId = netId; 168 mask.netId = FWMARK_NET_ID_MASK; 169 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table, fwmark.intValue, 170 mask.intValue, NULL)) { 171 return false; 172 } 173 174 // A rule to route traffic based on an explicitly chosen network. 175 // 176 // Supports apps that use the multinetwork APIs to restrict their traffic to a network. 177 // 178 // We don't really need to check the permission bits of the fwmark here, as they would've been 179 // checked at the time the netId was set into the fwmark, but we do so to be consistent. 180 fwmark.explicitlySelected = true; 181 mask.explicitlySelected = true; 182 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table, fwmark.intValue, 183 mask.intValue, NULL)) { 184 return false; 185 } 186 187 // An iptables rule to mark incoming packets on a network with the netId of the network. 188 // 189 // This is so that the kernel can: 190 // + Use the right fwmark for (and thus correctly route) replies (e.g.: TCP RST, ICMP errors, 191 // ping replies). 192 // + Mark sockets that accept connections from this interface so that the connection stays on 193 // the same interface. 194 if (modifyIptables) { 195 action = add ? "-A" : "-D"; 196 char markString[UINT32_HEX_STRLEN]; 197 snprintf(markString, sizeof(markString), "0x%x", netId); 198 if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK", 199 "--set-mark", markString, NULL)) { 200 return false; 201 } 202 } 203 204 return true; 205} 206 207bool modifyDefaultNetworkRules(const char* interface, Permission permission, const char* action) { 208 uint32_t table = getRouteTableForInterface(interface); 209 if (!table) { 210 return false; 211 } 212 213 Fwmark fwmark; 214 fwmark.netId = 0; 215 fwmark.permission = permission; 216 217 Fwmark mask; 218 mask.netId = FWMARK_NET_ID_MASK; 219 mask.permission = permission; 220 221 return runIpRuleCommand(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark.intValue, 222 mask.intValue, NULL); 223} 224 225bool modifyRoute(const char* interface, const char* destination, const char* nexthop, 226 const char* action, RouteController::TableType tableType, unsigned /* uid */) { 227 uint32_t table = 0; 228 switch (tableType) { 229 case RouteController::INTERFACE: { 230 table = getRouteTableForInterface(interface); 231 break; 232 } 233 case RouteController::LEGACY: { 234 // TODO: Use the UID to assign a unique table per UID instead of this fixed table. 235 table = ROUTE_TABLE_LEGACY; 236 break; 237 } 238 case RouteController::PRIVILEGED_LEGACY: { 239 // TODO: Use the UID to assign a unique table per UID instead of this fixed table. 240 table = ROUTE_TABLE_PRIVILEGED_LEGACY; 241 break; 242 } 243 } 244 if (!table) { 245 return false; 246 } 247 248 if (!runIpRouteCommand(action, table, interface, destination, nexthop)) { 249 return false; 250 } 251 252 // If there's no nexthop, this is a directly connected route. Add it to the main table also, to 253 // let the kernel find it when validating nexthops when global routes are added. Don't do this 254 // for IPv6, since all directly-connected routes in v6 are link-local and should already be in 255 // the main table. 256 // TODO: A failure here typically means that the route already exists in the main table, so we 257 // ignore it. It's wrong to ignore other kinds of failures, but we have no way to distinguish 258 // them based on the return status of the 'ip' command. Fix this situation by ignoring errors 259 // only when action == ADD && error == EEXIST. 260 if (!nexthop && !strchr(destination, ':')) { 261 runIpRouteCommand(action, RT_TABLE_MAIN, interface, destination, NULL); 262 } 263 264 return true; 265} 266 267bool flushRoutes(const char* interface) { 268 uint32_t table = getRouteTableForInterface(interface); 269 if (!table) { 270 return false; 271 } 272 interfaceToIndex.erase(interface); 273 274 return runIpRouteCommand("flush", table, NULL, NULL, NULL); 275} 276 277} // namespace 278 279void RouteController::Init() { 280 // Add a new rule to look up the 'main' table, with the same selectors as the "default network" 281 // rule, but with a lower priority. Since the default network rule points to a table with a 282 // default route, the rule we're adding will never be used for normal routing lookups. However, 283 // the kernel may fall-through to it to find directly-connected routes when it validates that a 284 // nexthop (in a route being added) is reachable. 285 Fwmark fwmark; 286 fwmark.netId = 0; 287 288 Fwmark mask; 289 mask.netId = FWMARK_NET_ID_MASK; 290 291 runIpRuleCommand(ADD, RULE_PRIORITY_MAIN, RT_TABLE_MAIN, fwmark.intValue, mask.intValue, NULL); 292 293 // Add rules to allow lookup of legacy routes. 294 // 295 // TODO: Remove these once the kernel supports UID-based routing. Instead, add them on demand 296 // when routes are added. 297 fwmark.netId = 0; 298 mask.netId = 0; 299 300 fwmark.explicitlySelected = false; 301 mask.explicitlySelected = true; 302 303 runIpRuleCommand(ADD, RULE_PRIORITY_LEGACY, ROUTE_TABLE_LEGACY, fwmark.intValue, mask.intValue, 304 NULL); 305 306 fwmark.permission = PERMISSION_CONNECTIVITY_INTERNAL; 307 mask.permission = PERMISSION_CONNECTIVITY_INTERNAL; 308 309 runIpRuleCommand(ADD, RULE_PRIORITY_PRIVILEGED_LEGACY, ROUTE_TABLE_PRIVILEGED_LEGACY, 310 fwmark.intValue, mask.intValue, NULL); 311 312// TODO: Uncomment once we are sure everything works. 313#if 0 314 // Add a rule to preempt the pre-defined "from all lookup main" rule. This ensures that packets 315 // that are already marked with a specific NetId don't fall-through to the main table. 316 runIpRuleCommand(ADD, RULE_PRIORITY_UNREACHABLE, 0, 0, 0, NULL); 317#endif 318} 319 320bool RouteController::addInterfaceToNetwork(unsigned netId, const char* interface, 321 Permission permission) { 322 return modifyPerNetworkRules(netId, interface, permission, true, true); 323} 324 325bool RouteController::removeInterfaceFromNetwork(unsigned netId, const char* interface, 326 Permission permission) { 327 return modifyPerNetworkRules(netId, interface, permission, false, true) && 328 flushRoutes(interface); 329} 330 331bool RouteController::modifyNetworkPermission(unsigned netId, const char* interface, 332 Permission oldPermission, Permission newPermission) { 333 // Add the new rules before deleting the old ones, to avoid race conditions. 334 return modifyPerNetworkRules(netId, interface, newPermission, true, false) && 335 modifyPerNetworkRules(netId, interface, oldPermission, false, false); 336} 337 338bool RouteController::addToDefaultNetwork(const char* interface, Permission permission) { 339 return modifyDefaultNetworkRules(interface, permission, ADD); 340} 341 342bool RouteController::removeFromDefaultNetwork(const char* interface, Permission permission) { 343 return modifyDefaultNetworkRules(interface, permission, DEL); 344} 345 346bool RouteController::addRoute(const char* interface, const char* destination, 347 const char* nexthop, TableType tableType, unsigned uid) { 348 return modifyRoute(interface, destination, nexthop, ADD, tableType, uid); 349} 350 351bool RouteController::removeRoute(const char* interface, const char* destination, 352 const char* nexthop, TableType tableType, unsigned uid) { 353 return modifyRoute(interface, destination, nexthop, DEL, tableType, uid); 354} 355