blob: b44a31ace4a3d31e43384629f3843bbfb1e056df [file] [log] [blame]
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -07001/*
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
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -070022#include <linux/rtnetlink.h>
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070023#include <logwrap/logwrap.h>
Paul Jensena561e122014-06-12 16:46:37 -040024#include <map>
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070025#include <net/if.h>
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070026
27namespace {
28
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -070029const uint32_t RULE_PRIORITY_PRIVILEGED_LEGACY = 11000;
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -070030const uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT = 13000;
31const uint32_t RULE_PRIORITY_PER_NETWORK_INTERFACE = 14000;
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -070032const uint32_t RULE_PRIORITY_LEGACY = 16000;
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -070033const uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL = 17000;
34const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 19000;
35const uint32_t RULE_PRIORITY_MAIN = 20000;
Sreeram Ramachandran56afacf2014-05-28 15:07:00 -070036// TODO: Uncomment once we are sure everything works.
37#if 0
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -070038const uint32_t RULE_PRIORITY_UNREACHABLE = 21000;
Sreeram Ramachandran56afacf2014-05-28 15:07:00 -070039#endif
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070040
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -070041// 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
Paul Jensena561e122014-06-12 16:46:37 -040045std::map<std::string, uint32_t> interfaceToIndex;
46
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070047uint32_t getRouteTableForInterface(const char* interface) {
Sreeram Ramachandrana4811802014-04-10 12:10:24 -070048 uint32_t index = if_nametoindex(interface);
Paul Jensena561e122014-06-12 16:46:37 -040049 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 }
Sreeram Ramachandrana4811802014-04-10 12:10:24 -070058 return index ? index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX : 0;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070059}
60
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -070061// 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) {
Lorenzo Colittia10ac322014-04-11 18:26:17 +090070 char priorityString[UINT32_STRLEN];
Lorenzo Colittia10ac322014-04-11 18:26:17 +090071 snprintf(priorityString, sizeof(priorityString), "%u", priority);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -070072
73 char tableString[UINT32_STRLEN];
Lorenzo Colittia10ac322014-04-11 18:26:17 +090074 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
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070079 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";
Lorenzo Colittia10ac322014-04-11 18:26:17 +090089 argv[argc++] = priorityString;
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -070090 if (table) {
91 argv[argc++] = "table";
92 argv[argc++] = tableString;
93 } else {
94 argv[argc++] = "unreachable";
95 }
Lorenzo Colittia10ac322014-04-11 18:26:17 +090096 if (mask) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070097 argv[argc++] = "fwmark";
Lorenzo Colittia10ac322014-04-11 18:26:17 +090098 argv[argc++] = fwmarkString;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070099 }
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700100 if (interface) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700101 argv[argc++] = "oif";
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700102 argv[argc++] = interface;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700103 }
104 if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
105 return false;
106 }
107 }
108
109 return true;
110}
111
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700112bool 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
Sreeram Ramachandrana79f6182014-04-24 15:55:26 -0700135 return !android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700136}
137
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700138bool modifyPerNetworkRules(unsigned netId, const char* interface, Permission permission, bool add,
139 bool modifyIptables) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700140 uint32_t table = getRouteTableForInterface(interface);
141 if (!table) {
142 return false;
143 }
144
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700145 const char* action = add ? ADD : DEL;
146
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700147 Fwmark fwmark;
148 fwmark.permission = permission;
149
150 Fwmark mask;
151 mask.permission = permission;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700152
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).
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700157 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_INTERFACE, table, fwmark.intValue,
158 mask.intValue, interface)) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700159 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.
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700167 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)) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700184 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,
Sreeram Ramachandrana4811802014-04-10 12:10:24 -0700191 // ping replies).
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700192 // + Mark sockets that accept connections from this interface so that the connection stays on
193 // the same interface.
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700194 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 }
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700202 }
203
204 return true;
205}
206
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700207bool modifyDefaultNetworkRules(const char* interface, Permission permission, const char* action) {
208 uint32_t table = getRouteTableForInterface(interface);
209 if (!table) {
210 return false;
211 }
212
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700213 Fwmark fwmark;
214 fwmark.netId = 0;
215 fwmark.permission = permission;
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700216
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700217 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);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700223}
224
Sreeram Ramachandranc9213372014-04-16 12:32:18 -0700225bool modifyRoute(const char* interface, const char* destination, const char* nexthop,
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700226 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 }
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700244 if (!table) {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700245 return false;
246 }
247
Sreeram Ramachandranc9213372014-04-16 12:32:18 -0700248 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.
Sreeram Ramachandranfd6424f2014-04-24 16:23:35 -0700256 // 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);
Sreeram Ramachandranc9213372014-04-16 12:32:18 -0700262 }
263
264 return true;
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700265}
266
Sreeram Ramachandran92b66c42014-04-15 14:28:55 -0700267bool flushRoutes(const char* interface) {
268 uint32_t table = getRouteTableForInterface(interface);
269 if (!table) {
270 return false;
271 }
Paul Jensena561e122014-06-12 16:46:37 -0400272 interfaceToIndex.erase(interface);
Sreeram Ramachandran92b66c42014-04-15 14:28:55 -0700273
Lorenzo Colitti357e5622014-06-17 16:14:17 +0900274 char tableString[UINT32_STRLEN];
275 snprintf(tableString, sizeof(tableString), "%u", table);
276
277 const char* version[] = {"-4", "-6"};
278 for (size_t i = 0; i < ARRAY_SIZE(version); ++i) {
279 const char* argv[] = {
280 IP_PATH,
281 version[i],
282 "route"
283 "flush",
284 "table",
285 tableString,
286 };
287 int argc = ARRAY_SIZE(argv);
288
289 if (!android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
290 return false;
291 }
292 }
293
294 return true;
Sreeram Ramachandran92b66c42014-04-15 14:28:55 -0700295}
296
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700297} // namespace
298
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -0700299void RouteController::Init() {
300 // Add a new rule to look up the 'main' table, with the same selectors as the "default network"
301 // rule, but with a lower priority. Since the default network rule points to a table with a
302 // default route, the rule we're adding will never be used for normal routing lookups. However,
303 // the kernel may fall-through to it to find directly-connected routes when it validates that a
304 // nexthop (in a route being added) is reachable.
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700305 Fwmark fwmark;
306 fwmark.netId = 0;
307
308 Fwmark mask;
309 mask.netId = FWMARK_NET_ID_MASK;
310
311 runIpRuleCommand(ADD, RULE_PRIORITY_MAIN, RT_TABLE_MAIN, fwmark.intValue, mask.intValue, NULL);
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -0700312
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700313 // Add rules to allow lookup of legacy routes.
314 //
315 // TODO: Remove these once the kernel supports UID-based routing. Instead, add them on demand
316 // when routes are added.
317 fwmark.netId = 0;
318 mask.netId = 0;
319
320 fwmark.explicitlySelected = false;
321 mask.explicitlySelected = true;
322
323 runIpRuleCommand(ADD, RULE_PRIORITY_LEGACY, ROUTE_TABLE_LEGACY, fwmark.intValue, mask.intValue,
324 NULL);
325
326 fwmark.permission = PERMISSION_CONNECTIVITY_INTERNAL;
327 mask.permission = PERMISSION_CONNECTIVITY_INTERNAL;
328
329 runIpRuleCommand(ADD, RULE_PRIORITY_PRIVILEGED_LEGACY, ROUTE_TABLE_PRIVILEGED_LEGACY,
330 fwmark.intValue, mask.intValue, NULL);
331
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -0700332// TODO: Uncomment once we are sure everything works.
333#if 0
334 // Add a rule to preempt the pre-defined "from all lookup main" rule. This ensures that packets
335 // that are already marked with a specific NetId don't fall-through to the main table.
336 runIpRuleCommand(ADD, RULE_PRIORITY_UNREACHABLE, 0, 0, 0, NULL);
337#endif
338}
339
Paul Jensenae37e8a2014-04-28 10:35:51 -0400340bool RouteController::addInterfaceToNetwork(unsigned netId, const char* interface,
341 Permission permission) {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700342 return modifyPerNetworkRules(netId, interface, permission, true, true);
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700343}
344
Paul Jensenae37e8a2014-04-28 10:35:51 -0400345bool RouteController::removeInterfaceFromNetwork(unsigned netId, const char* interface,
346 Permission permission) {
Sreeram Ramachandran92b66c42014-04-15 14:28:55 -0700347 return modifyPerNetworkRules(netId, interface, permission, false, true) &&
348 flushRoutes(interface);
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700349}
350
351bool RouteController::modifyNetworkPermission(unsigned netId, const char* interface,
352 Permission oldPermission, Permission newPermission) {
353 // Add the new rules before deleting the old ones, to avoid race conditions.
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700354 return modifyPerNetworkRules(netId, interface, newPermission, true, false) &&
355 modifyPerNetworkRules(netId, interface, oldPermission, false, false);
356}
357
Sreeram Ramachandran72604072014-05-21 13:19:43 -0700358bool RouteController::addToDefaultNetwork(const char* interface, Permission permission) {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700359 return modifyDefaultNetworkRules(interface, permission, ADD);
360}
361
Sreeram Ramachandran72604072014-05-21 13:19:43 -0700362bool RouteController::removeFromDefaultNetwork(const char* interface, Permission permission) {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700363 return modifyDefaultNetworkRules(interface, permission, DEL);
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700364}
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700365
366bool RouteController::addRoute(const char* interface, const char* destination,
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700367 const char* nexthop, TableType tableType, unsigned uid) {
368 return modifyRoute(interface, destination, nexthop, ADD, tableType, uid);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700369}
370
371bool RouteController::removeRoute(const char* interface, const char* destination,
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700372 const char* nexthop, TableType tableType, unsigned uid) {
373 return modifyRoute(interface, destination, nexthop, DEL, tableType, uid);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700374}