blob: a8f6700123d82b296a6d26a3a9a1e99db0ff3668 [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
22#include <logwrap/logwrap.h>
23#include <net/if.h>
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070024
25namespace {
26
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070027const uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT = 300;
28const uint32_t RULE_PRIORITY_PER_NETWORK_INTERFACE = 400;
29const uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL = 700;
30const uint32_t RULE_PRIORITY_DEFAULT_NETWORK = 900;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070031
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070032const bool FWMARK_USE_NET_ID = true;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070033const bool FWMARK_USE_EXPLICIT = true;
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070034const bool FWMARK_USE_PROTECT = true;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070035
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070036uint32_t getRouteTableForInterface(const char* interface) {
Sreeram Ramachandrana4811802014-04-10 12:10:24 -070037 uint32_t index = if_nametoindex(interface);
38 return index ? index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX : 0;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070039}
40
Lorenzo Colittia10ac322014-04-11 18:26:17 +090041bool runIpRuleCommand(const char* action, uint32_t priority, uint32_t table,
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070042 uint32_t fwmark, uint32_t mask, const char* interface) {
Lorenzo Colittia10ac322014-04-11 18:26:17 +090043
44 char priorityString[UINT32_STRLEN];
45 char tableString[UINT32_STRLEN];
46 snprintf(priorityString, sizeof(priorityString), "%u", priority);
47 snprintf(tableString, sizeof(tableString), "%u", table);
48
49 char fwmarkString[sizeof("0x12345678/0x12345678")];
50 snprintf(fwmarkString, sizeof(fwmarkString), "0x%x/0x%x", fwmark, mask);
51
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070052 const char* version[] = {"-4", "-6"};
53 for (size_t i = 0; i < ARRAY_SIZE(version); ++i) {
54 int argc = 0;
55 const char* argv[16];
56
57 argv[argc++] = IP_PATH;
58 argv[argc++] = version[i];
59 argv[argc++] = "rule";
60 argv[argc++] = action;
61 argv[argc++] = "priority";
Lorenzo Colittia10ac322014-04-11 18:26:17 +090062 argv[argc++] = priorityString;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070063 argv[argc++] = "table";
Lorenzo Colittia10ac322014-04-11 18:26:17 +090064 argv[argc++] = tableString;
65 if (mask) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070066 argv[argc++] = "fwmark";
Lorenzo Colittia10ac322014-04-11 18:26:17 +090067 argv[argc++] = fwmarkString;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070068 }
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070069 if (interface) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070070 argv[argc++] = "oif";
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070071 argv[argc++] = interface;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070072 }
73 if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
74 return false;
75 }
76 }
77
78 return true;
79}
80
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -070081bool modifyPerNetworkRules(unsigned netId, const char* interface, Permission permission, bool add,
82 bool modifyIptables) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070083 uint32_t table = getRouteTableForInterface(interface);
84 if (!table) {
85 return false;
86 }
87
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070088 const char* action = add ? ADD : DEL;
89
90 // A rule to route traffic based on an explicitly chosen network.
91 //
92 // Supports apps that use the multinetwork APIs to restrict their traffic to a network.
93 //
94 // We don't really need to check the permission bits of the fwmark here, as they would've been
95 // checked at the time the netId was set into the fwmark, but we do so to be consistent.
96 uint32_t fwmark = getFwmark(netId, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
97 uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
98 permission);
Lorenzo Colittia10ac322014-04-11 18:26:17 +090099 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table, fwmark, mask, NULL)) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700100 return false;
101 }
102
103 // A rule to route traffic based on a chosen outgoing interface.
104 //
105 // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already
106 // knows the outgoing interface (typically for link-local communications).
107 fwmark = getFwmark(0, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
108 mask = getFwmark(!FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700109 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_INTERFACE, table, fwmark, mask,
110 interface)) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700111 return false;
112 }
113
114 // A rule to route traffic based on the chosen network.
115 //
116 // This is for sockets that have not explicitly requested a particular network, but have been
117 // bound to one when they called connect(). This ensures that sockets connected on a particular
118 // network stay on that network even if the default network changes.
119 fwmark = getFwmark(netId, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
120 mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
Lorenzo Colittia10ac322014-04-11 18:26:17 +0900121 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table, fwmark, mask, NULL)) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700122 return false;
123 }
124
125 // An iptables rule to mark incoming packets on a network with the netId of the network.
126 //
127 // This is so that the kernel can:
128 // + Use the right fwmark for (and thus correctly route) replies (e.g.: TCP RST, ICMP errors,
Sreeram Ramachandrana4811802014-04-10 12:10:24 -0700129 // ping replies).
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700130 // + Mark sockets that accept connections from this interface so that the connection stays on
131 // the same interface.
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700132 if (modifyIptables) {
133 action = add ? "-A" : "-D";
134 char markString[UINT32_HEX_STRLEN];
135 snprintf(markString, sizeof(markString), "0x%x", netId);
136 if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK",
137 "--set-mark", markString, NULL)) {
138 return false;
139 }
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700140 }
141
142 return true;
143}
144
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700145bool modifyDefaultNetworkRules(const char* interface, Permission permission, const char* action) {
146 uint32_t table = getRouteTableForInterface(interface);
147 if (!table) {
148 return false;
149 }
150
151 uint32_t fwmark = getFwmark(0, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
152 uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
153 permission);
154
155 if (!runIpRuleCommand(action, RULE_PRIORITY_DEFAULT_NETWORK, table, fwmark, mask, NULL)) {
156 return false;
157 }
158
159 return true;
160}
161
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700162} // namespace
163
164bool RouteController::createNetwork(unsigned netId, const char* interface, Permission permission) {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700165 return modifyPerNetworkRules(netId, interface, permission, true, true);
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700166}
167
168bool RouteController::destroyNetwork(unsigned netId, const char* interface, Permission permission) {
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700169 return modifyPerNetworkRules(netId, interface, permission, false, true);
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700170 // TODO: Flush the routing table.
171}
172
173bool RouteController::modifyNetworkPermission(unsigned netId, const char* interface,
174 Permission oldPermission, Permission newPermission) {
175 // Add the new rules before deleting the old ones, to avoid race conditions.
Sreeram Ramachandran9c0d3132014-04-10 20:35:04 -0700176 return modifyPerNetworkRules(netId, interface, newPermission, true, false) &&
177 modifyPerNetworkRules(netId, interface, oldPermission, false, false);
178}
179
180bool RouteController::addDefaultNetwork(const char* interface, Permission permission) {
181 return modifyDefaultNetworkRules(interface, permission, ADD);
182}
183
184bool RouteController::removeDefaultNetwork(const char* interface, Permission permission) {
185 return modifyDefaultNetworkRules(interface, permission, DEL);
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700186}