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