blob: 36773d70a75b7b907ee06fd1b0d35e0696110205 [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>
24#include <net/if.h>
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070025
26namespace {
27
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -070028const uint32_t RULE_PRIORITY_PRIVILEGED_LEGACY = 11000;
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -070029const uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT = 13000;
30const uint32_t RULE_PRIORITY_PER_NETWORK_INTERFACE = 14000;
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -070031const uint32_t RULE_PRIORITY_LEGACY = 16000;
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -070032const uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL = 17000;
33const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 19000;
34const uint32_t RULE_PRIORITY_MAIN = 20000;
35const uint32_t RULE_PRIORITY_UNREACHABLE = 21000;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070036
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -070037// TODO: These should be turned into per-UID tables once the kernel supports UID-based routing.
38const int ROUTE_TABLE_PRIVILEGED_LEGACY = RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX - 901;
39const int ROUTE_TABLE_LEGACY = RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX - 902;
40
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070041uint32_t getRouteTableForInterface(const char* interface) {
Sreeram Ramachandrana4811802014-04-10 12:10:24 -070042 uint32_t index = if_nametoindex(interface);
43 return index ? index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX : 0;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070044}
45
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -070046// Adds or removes a routing rule for IPv4 and IPv6.
47//
48// + If |table| is non-zero, the rule points at the specified routing table. Otherwise, the rule
49// returns ENETUNREACH.
50// + If |mask| is non-zero, the rule matches the specified fwmark and mask. Otherwise, |fwmark| is
51// ignored.
52// + If |interface| is non-NULL, the rule matches the specified outgoing interface.
53bool runIpRuleCommand(const char* action, uint32_t priority, uint32_t table, uint32_t fwmark,
54 uint32_t mask, const char* interface) {
Lorenzo Colittia10ac322014-04-11 18:26:17 +090055 char priorityString[UINT32_STRLEN];
Lorenzo Colittia10ac322014-04-11 18:26:17 +090056 snprintf(priorityString, sizeof(priorityString), "%u", priority);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -070057
58 char tableString[UINT32_STRLEN];
Lorenzo Colittia10ac322014-04-11 18:26:17 +090059 snprintf(tableString, sizeof(tableString), "%u", table);
60
61 char fwmarkString[sizeof("0x12345678/0x12345678")];
62 snprintf(fwmarkString, sizeof(fwmarkString), "0x%x/0x%x", fwmark, mask);
63
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070064 const char* version[] = {"-4", "-6"};
65 for (size_t i = 0; i < ARRAY_SIZE(version); ++i) {
66 int argc = 0;
67 const char* argv[16];
68
69 argv[argc++] = IP_PATH;
70 argv[argc++] = version[i];
71 argv[argc++] = "rule";
72 argv[argc++] = action;
73 argv[argc++] = "priority";
Lorenzo Colittia10ac322014-04-11 18:26:17 +090074 argv[argc++] = priorityString;
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -070075 if (table) {
76 argv[argc++] = "table";
77 argv[argc++] = tableString;
78 } else {
79 argv[argc++] = "unreachable";
80 }
Lorenzo Colittia10ac322014-04-11 18:26:17 +090081 if (mask) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070082 argv[argc++] = "fwmark";
Lorenzo Colittia10ac322014-04-11 18:26:17 +090083 argv[argc++] = fwmarkString;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070084 }
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070085 if (interface) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070086 argv[argc++] = "oif";
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070087 argv[argc++] = interface;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070088 }
89 if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
90 return false;
91 }
92 }
93
94 return true;
95}
96
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -070097bool runIpRouteCommand(const char* action, uint32_t table, const char* interface,
98 const char* destination, const char* nexthop) {
99 char tableString[UINT32_STRLEN];
100 snprintf(tableString, sizeof(tableString), "%u", table);
101
102 int argc = 0;
103 const char* argv[16];
104
105 argv[argc++] = IP_PATH;
106 argv[argc++] = "route";
107 argv[argc++] = action;
108 argv[argc++] = "table";
109 argv[argc++] = tableString;
110 if (destination) {
111 argv[argc++] = destination;
112 argv[argc++] = "dev";
113 argv[argc++] = interface;
114 if (nexthop) {
115 argv[argc++] = "via";
116 argv[argc++] = nexthop;
117 }
118 }
119
Sreeram Ramachandrana79f6182014-04-24 15:55:26 -0700120 return !android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700121}
122
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700123bool modifyPerNetworkRules(unsigned netId, const char* interface, Permission permission, bool add,
124 bool modifyIptables) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700125 uint32_t table = getRouteTableForInterface(interface);
126 if (!table) {
127 return false;
128 }
129
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700130 const char* action = add ? ADD : DEL;
131
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700132 Fwmark fwmark;
133 fwmark.permission = permission;
134
135 Fwmark mask;
136 mask.permission = permission;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700137
138 // A rule to route traffic based on a chosen outgoing interface.
139 //
140 // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already
141 // knows the outgoing interface (typically for link-local communications).
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700142 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_INTERFACE, table, fwmark.intValue,
143 mask.intValue, interface)) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700144 return false;
145 }
146
147 // A rule to route traffic based on the chosen network.
148 //
149 // This is for sockets that have not explicitly requested a particular network, but have been
150 // bound to one when they called connect(). This ensures that sockets connected on a particular
151 // network stay on that network even if the default network changes.
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700152 fwmark.netId = netId;
153 mask.netId = FWMARK_NET_ID_MASK;
154 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table, fwmark.intValue,
155 mask.intValue, NULL)) {
156 return false;
157 }
158
159 // A rule to route traffic based on an explicitly chosen network.
160 //
161 // Supports apps that use the multinetwork APIs to restrict their traffic to a network.
162 //
163 // We don't really need to check the permission bits of the fwmark here, as they would've been
164 // checked at the time the netId was set into the fwmark, but we do so to be consistent.
165 fwmark.explicitlySelected = true;
166 mask.explicitlySelected = true;
167 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table, fwmark.intValue,
168 mask.intValue, NULL)) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700169 return false;
170 }
171
172 // An iptables rule to mark incoming packets on a network with the netId of the network.
173 //
174 // This is so that the kernel can:
175 // + Use the right fwmark for (and thus correctly route) replies (e.g.: TCP RST, ICMP errors,
Sreeram Ramachandrana4811802014-04-10 12:10:24 -0700176 // ping replies).
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700177 // + Mark sockets that accept connections from this interface so that the connection stays on
178 // the same interface.
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700179 if (modifyIptables) {
180 action = add ? "-A" : "-D";
181 char markString[UINT32_HEX_STRLEN];
182 snprintf(markString, sizeof(markString), "0x%x", netId);
183 if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK",
184 "--set-mark", markString, NULL)) {
185 return false;
186 }
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700187 }
188
189 return true;
190}
191
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700192bool modifyDefaultNetworkRules(const char* interface, Permission permission, const char* action) {
193 uint32_t table = getRouteTableForInterface(interface);
194 if (!table) {
195 return false;
196 }
197
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700198 Fwmark fwmark;
199 fwmark.netId = 0;
200 fwmark.permission = permission;
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700201
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700202 Fwmark mask;
203 mask.netId = FWMARK_NET_ID_MASK;
204 mask.permission = permission;
205
206 return runIpRuleCommand(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark.intValue,
207 mask.intValue, NULL);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700208}
209
Sreeram Ramachandranc9213372014-04-16 12:32:18 -0700210bool modifyRoute(const char* interface, const char* destination, const char* nexthop,
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700211 const char* action, RouteController::TableType tableType, unsigned /* uid */) {
212 uint32_t table = 0;
213 switch (tableType) {
214 case RouteController::INTERFACE: {
215 table = getRouteTableForInterface(interface);
216 break;
217 }
218 case RouteController::LEGACY: {
219 // TODO: Use the UID to assign a unique table per UID instead of this fixed table.
220 table = ROUTE_TABLE_LEGACY;
221 break;
222 }
223 case RouteController::PRIVILEGED_LEGACY: {
224 // TODO: Use the UID to assign a unique table per UID instead of this fixed table.
225 table = ROUTE_TABLE_PRIVILEGED_LEGACY;
226 break;
227 }
228 }
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700229 if (!table) {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700230 return false;
231 }
232
Sreeram Ramachandranc9213372014-04-16 12:32:18 -0700233 if (!runIpRouteCommand(action, table, interface, destination, nexthop)) {
234 return false;
235 }
236
237 // If there's no nexthop, this is a directly connected route. Add it to the main table also, to
238 // let the kernel find it when validating nexthops when global routes are added. Don't do this
239 // for IPv6, since all directly-connected routes in v6 are link-local and should already be in
240 // the main table.
Sreeram Ramachandranfd6424f2014-04-24 16:23:35 -0700241 // TODO: A failure here typically means that the route already exists in the main table, so we
242 // ignore it. It's wrong to ignore other kinds of failures, but we have no way to distinguish
243 // them based on the return status of the 'ip' command. Fix this situation by ignoring errors
244 // only when action == ADD && error == EEXIST.
245 if (!nexthop && !strchr(destination, ':')) {
246 runIpRouteCommand(action, RT_TABLE_MAIN, interface, destination, NULL);
Sreeram Ramachandranc9213372014-04-16 12:32:18 -0700247 }
248
249 return true;
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700250}
251
Sreeram Ramachandran92b66c42014-04-15 14:28:55 -0700252bool flushRoutes(const char* interface) {
253 uint32_t table = getRouteTableForInterface(interface);
254 if (!table) {
255 return false;
256 }
257
258 return runIpRouteCommand("flush", table, NULL, NULL, NULL);
259}
260
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700261} // namespace
262
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -0700263void RouteController::Init() {
264 // Add a new rule to look up the 'main' table, with the same selectors as the "default network"
265 // rule, but with a lower priority. Since the default network rule points to a table with a
266 // default route, the rule we're adding will never be used for normal routing lookups. However,
267 // the kernel may fall-through to it to find directly-connected routes when it validates that a
268 // nexthop (in a route being added) is reachable.
Sreeram Ramachandran122f5812014-05-11 20:29:49 -0700269 Fwmark fwmark;
270 fwmark.netId = 0;
271
272 Fwmark mask;
273 mask.netId = FWMARK_NET_ID_MASK;
274
275 runIpRuleCommand(ADD, RULE_PRIORITY_MAIN, RT_TABLE_MAIN, fwmark.intValue, mask.intValue, NULL);
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -0700276
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700277 // Add rules to allow lookup of legacy routes.
278 //
279 // TODO: Remove these once the kernel supports UID-based routing. Instead, add them on demand
280 // when routes are added.
281 fwmark.netId = 0;
282 mask.netId = 0;
283
284 fwmark.explicitlySelected = false;
285 mask.explicitlySelected = true;
286
287 runIpRuleCommand(ADD, RULE_PRIORITY_LEGACY, ROUTE_TABLE_LEGACY, fwmark.intValue, mask.intValue,
288 NULL);
289
290 fwmark.permission = PERMISSION_CONNECTIVITY_INTERNAL;
291 mask.permission = PERMISSION_CONNECTIVITY_INTERNAL;
292
293 runIpRuleCommand(ADD, RULE_PRIORITY_PRIVILEGED_LEGACY, ROUTE_TABLE_PRIVILEGED_LEGACY,
294 fwmark.intValue, mask.intValue, NULL);
295
Sreeram Ramachandran8fe9c8e2014-04-16 12:08:05 -0700296// TODO: Uncomment once we are sure everything works.
297#if 0
298 // Add a rule to preempt the pre-defined "from all lookup main" rule. This ensures that packets
299 // that are already marked with a specific NetId don't fall-through to the main table.
300 runIpRuleCommand(ADD, RULE_PRIORITY_UNREACHABLE, 0, 0, 0, NULL);
301#endif
302}
303
Paul Jensenae37e8a2014-04-28 10:35:51 -0400304bool RouteController::addInterfaceToNetwork(unsigned netId, const char* interface,
305 Permission permission) {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700306 return modifyPerNetworkRules(netId, interface, permission, true, true);
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700307}
308
Paul Jensenae37e8a2014-04-28 10:35:51 -0400309bool RouteController::removeInterfaceFromNetwork(unsigned netId, const char* interface,
310 Permission permission) {
Sreeram Ramachandran92b66c42014-04-15 14:28:55 -0700311 return modifyPerNetworkRules(netId, interface, permission, false, true) &&
312 flushRoutes(interface);
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700313}
314
315bool RouteController::modifyNetworkPermission(unsigned netId, const char* interface,
316 Permission oldPermission, Permission newPermission) {
317 // Add the new rules before deleting the old ones, to avoid race conditions.
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700318 return modifyPerNetworkRules(netId, interface, newPermission, true, false) &&
319 modifyPerNetworkRules(netId, interface, oldPermission, false, false);
320}
321
Sreeram Ramachandran72604072014-05-21 13:19:43 -0700322bool RouteController::addToDefaultNetwork(const char* interface, Permission permission) {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700323 return modifyDefaultNetworkRules(interface, permission, ADD);
324}
325
Sreeram Ramachandran72604072014-05-21 13:19:43 -0700326bool RouteController::removeFromDefaultNetwork(const char* interface, Permission permission) {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700327 return modifyDefaultNetworkRules(interface, permission, DEL);
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700328}
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700329
330bool RouteController::addRoute(const char* interface, const char* destination,
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700331 const char* nexthop, TableType tableType, unsigned uid) {
332 return modifyRoute(interface, destination, nexthop, ADD, tableType, uid);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700333}
334
335bool RouteController::removeRoute(const char* interface, const char* destination,
Sreeram Ramachandran38b7af12014-05-22 14:21:49 -0700336 const char* nexthop, TableType tableType, unsigned uid) {
337 return modifyRoute(interface, destination, nexthop, DEL, tableType, uid);
Sreeram Ramachandran7619e1b2014-04-15 14:23:08 -0700338}