blob: 4c56226a37bbda5d3b76dd0e339af2be42113625 [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 Ramachandran5c181bf2014-04-07 14:10:04 -070027const char* const RULE_PRIORITY_PER_NETWORK_EXPLICIT = "300";
28const char* const RULE_PRIORITY_PER_NETWORK_OIF = "400";
29const char* const RULE_PRIORITY_PER_NETWORK_NORMAL = "700";
30
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
40bool runIpRuleCommand(const char* action, const char* priority, const char* table,
41 const char* fwmark, const char* oif) {
42 const char* version[] = {"-4", "-6"};
43 for (size_t i = 0; i < ARRAY_SIZE(version); ++i) {
44 int argc = 0;
45 const char* argv[16];
46
47 argv[argc++] = IP_PATH;
48 argv[argc++] = version[i];
49 argv[argc++] = "rule";
50 argv[argc++] = action;
51 argv[argc++] = "priority";
52 argv[argc++] = priority;
53 argv[argc++] = "table";
54 argv[argc++] = table;
55 if (fwmark) {
56 argv[argc++] = "fwmark";
57 argv[argc++] = fwmark;
58 }
59 if (oif) {
60 argv[argc++] = "oif";
61 argv[argc++] = oif;
62 }
63 if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
64 return false;
65 }
66 }
67
68 return true;
69}
70
71bool modifyNetwork(unsigned netId, const char* interface, Permission permission, bool add) {
72 uint32_t table = getRouteTableForInterface(interface);
73 if (!table) {
74 return false;
75 }
76
77 char table_string[sizeof("0x12345678")];
78 snprintf(table_string, sizeof(table_string), "0x%x", table);
79
80 char mark_string[sizeof("0x12345678/0x12345678")];
81 const char* action = add ? ADD : DEL;
82
83 // A rule to route traffic based on an explicitly chosen network.
84 //
85 // Supports apps that use the multinetwork APIs to restrict their traffic to a network.
86 //
87 // We don't really need to check the permission bits of the fwmark here, as they would've been
88 // checked at the time the netId was set into the fwmark, but we do so to be consistent.
89 uint32_t fwmark = getFwmark(netId, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
90 uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
91 permission);
92 snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
93 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table_string, mark_string,
94 NULL)) {
95 return false;
96 }
97
98 // A rule to route traffic based on a chosen outgoing interface.
99 //
100 // Supports apps that use SO_BINDTODEVICE or IP_PKTINFO options and the kernel that already
101 // knows the outgoing interface (typically for link-local communications).
102 fwmark = getFwmark(0, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
103 mask = getFwmark(!FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
104 snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
105 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_OIF, table_string, mark_string,
106 interface)) {
107 return false;
108 }
109
110 // A rule to route traffic based on the chosen network.
111 //
112 // This is for sockets that have not explicitly requested a particular network, but have been
113 // bound to one when they called connect(). This ensures that sockets connected on a particular
114 // network stay on that network even if the default network changes.
115 fwmark = getFwmark(netId, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
116 mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
117 snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
118 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table_string, mark_string,
119 NULL)) {
120 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.
130 action = add ? "-A" : "-D";
131 snprintf(mark_string, sizeof(mark_string), "0x%x", netId);
132 if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK",
133 "--set-mark", mark_string, NULL)) {
134 return false;
135 }
136
137 return true;
138}
139
140} // namespace
141
142bool RouteController::createNetwork(unsigned netId, const char* interface, Permission permission) {
143 return modifyNetwork(netId, interface, permission, true);
144}
145
146bool RouteController::destroyNetwork(unsigned netId, const char* interface, Permission permission) {
147 return modifyNetwork(netId, interface, permission, false);
148}