blob: 737dd9f7d6d278e1fedf77f01bb9333020e3f1ac [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
Lorenzo Colittia10ac322014-04-11 18:26:17 +090027const uint32_t RULE_PRIORITY_PER_NETWORK_EXPLICIT = 300;
28const uint32_t RULE_PRIORITY_PER_NETWORK_OIF = 400;
29const uint32_t RULE_PRIORITY_PER_NETWORK_NORMAL = 700;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070030
31const bool FWMARK_USE_NET_ID = true;
32const bool FWMARK_USE_EXPLICIT = true;
33const bool FWMARK_USE_PROTECT = true;
34
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070035uint32_t getRouteTableForInterface(const char* interface) {
Sreeram Ramachandrana4811802014-04-10 12:10:24 -070036 uint32_t index = if_nametoindex(interface);
37 return index ? index + RouteController::ROUTE_TABLE_OFFSET_FROM_INDEX : 0;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070038}
39
Lorenzo Colittia10ac322014-04-11 18:26:17 +090040bool runIpRuleCommand(const char* action, uint32_t priority, uint32_t table,
41 uint32_t fwmark, uint32_t mask, const char* oif) {
42
43 char priorityString[UINT32_STRLEN];
44 char tableString[UINT32_STRLEN];
45 snprintf(priorityString, sizeof(priorityString), "%u", priority);
46 snprintf(tableString, sizeof(tableString), "%u", table);
47
48 char fwmarkString[sizeof("0x12345678/0x12345678")];
49 snprintf(fwmarkString, sizeof(fwmarkString), "0x%x/0x%x", fwmark, mask);
50
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070051 const char* version[] = {"-4", "-6"};
52 for (size_t i = 0; i < ARRAY_SIZE(version); ++i) {
53 int argc = 0;
54 const char* argv[16];
55
56 argv[argc++] = IP_PATH;
57 argv[argc++] = version[i];
58 argv[argc++] = "rule";
59 argv[argc++] = action;
60 argv[argc++] = "priority";
Lorenzo Colittia10ac322014-04-11 18:26:17 +090061 argv[argc++] = priorityString;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070062 argv[argc++] = "table";
Lorenzo Colittia10ac322014-04-11 18:26:17 +090063 argv[argc++] = tableString;
64 if (mask) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070065 argv[argc++] = "fwmark";
Lorenzo Colittia10ac322014-04-11 18:26:17 +090066 argv[argc++] = fwmarkString;
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070067 }
68 if (oif) {
69 argv[argc++] = "oif";
70 argv[argc++] = oif;
71 }
72 if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
73 return false;
74 }
75 }
76
77 return true;
78}
79
Sreeram Ramachandran379bd332014-04-10 19:58:06 -070080bool modifyRules(unsigned netId, const char* interface, Permission permission, bool add,
81 bool modifyIptables) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070082 uint32_t table = getRouteTableForInterface(interface);
83 if (!table) {
84 return false;
85 }
86
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070087 const char* action = add ? ADD : DEL;
88
89 // A rule to route traffic based on an explicitly chosen network.
90 //
91 // Supports apps that use the multinetwork APIs to restrict their traffic to a network.
92 //
93 // We don't really need to check the permission bits of the fwmark here, as they would've been
94 // checked at the time the netId was set into the fwmark, but we do so to be consistent.
95 uint32_t fwmark = getFwmark(netId, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
96 uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
97 permission);
Lorenzo Colittia10ac322014-04-11 18:26:17 +090098 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table, fwmark, mask, NULL)) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -070099 return false;
100 }
101
102 // A rule to route traffic based on a chosen outgoing interface.
103 //
104 // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already
105 // knows the outgoing interface (typically for link-local communications).
106 fwmark = getFwmark(0, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
107 mask = getFwmark(!FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
Lorenzo Colittia10ac322014-04-11 18:26:17 +0900108 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_OIF, table, fwmark, mask, interface)) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700109 return false;
110 }
111
112 // A rule to route traffic based on the chosen network.
113 //
114 // This is for sockets that have not explicitly requested a particular network, but have been
115 // bound to one when they called connect(). This ensures that sockets connected on a particular
116 // network stay on that network even if the default network changes.
117 fwmark = getFwmark(netId, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
118 mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
Lorenzo Colittia10ac322014-04-11 18:26:17 +0900119 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table, fwmark, mask, NULL)) {
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700120 return false;
121 }
122
123 // An iptables rule to mark incoming packets on a network with the netId of the network.
124 //
125 // This is so that the kernel can:
126 // + Use the right fwmark for (and thus correctly route) replies (e.g.: TCP RST, ICMP errors,
Sreeram Ramachandrana4811802014-04-10 12:10:24 -0700127 // ping replies).
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700128 // + Mark sockets that accept connections from this interface so that the connection stays on
129 // the same interface.
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700130 if (modifyIptables) {
131 action = add ? "-A" : "-D";
132 char markString[UINT32_HEX_STRLEN];
133 snprintf(markString, sizeof(markString), "0x%x", netId);
134 if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK",
135 "--set-mark", markString, NULL)) {
136 return false;
137 }
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700138 }
139
140 return true;
141}
142
143} // namespace
144
145bool RouteController::createNetwork(unsigned netId, const char* interface, Permission permission) {
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700146 return modifyRules(netId, interface, permission, true, true);
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700147}
148
149bool RouteController::destroyNetwork(unsigned netId, const char* interface, Permission permission) {
Sreeram Ramachandran379bd332014-04-10 19:58:06 -0700150 return modifyRules(netId, interface, permission, false, true);
151 // TODO: Flush the routing table.
152}
153
154bool RouteController::modifyNetworkPermission(unsigned netId, const char* interface,
155 Permission oldPermission, Permission newPermission) {
156 // Add the new rules before deleting the old ones, to avoid race conditions.
157 return modifyRules(netId, interface, newPermission, true, false) &&
158 modifyRules(netId, interface, oldPermission, false, false);
Sreeram Ramachandran5c181bf2014-04-07 14:10:04 -0700159}