blob: b686a4690dc6d2fd236906fabc9f7720dae350dc [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>
24#include <stdio.h>
25
26namespace {
27
28// TODO: Keep this in sync with the kernel.
29const uint32_t ROUTE_TABLE_OFFSET_FROM_INDEX = 255;
30
31const char* const RULE_PRIORITY_PER_NETWORK_EXPLICIT = "300";
32const char* const RULE_PRIORITY_PER_NETWORK_OIF = "400";
33const char* const RULE_PRIORITY_PER_NETWORK_NORMAL = "700";
34
35const bool FWMARK_USE_NET_ID = true;
36const bool FWMARK_USE_EXPLICIT = true;
37const bool FWMARK_USE_PROTECT = true;
38
39// TODO: Tell the kernel about the offset using sysctls during init.
40uint32_t getRouteTableForInterface(const char* interface) {
41 uint32_t index = static_cast<uint32_t>(if_nametoindex(interface));
42 return index ? index + ROUTE_TABLE_OFFSET_FROM_INDEX : 0;
43}
44
45bool runIpRuleCommand(const char* action, const char* priority, const char* table,
46 const char* fwmark, const char* oif) {
47 const char* version[] = {"-4", "-6"};
48 for (size_t i = 0; i < ARRAY_SIZE(version); ++i) {
49 int argc = 0;
50 const char* argv[16];
51
52 argv[argc++] = IP_PATH;
53 argv[argc++] = version[i];
54 argv[argc++] = "rule";
55 argv[argc++] = action;
56 argv[argc++] = "priority";
57 argv[argc++] = priority;
58 argv[argc++] = "table";
59 argv[argc++] = table;
60 if (fwmark) {
61 argv[argc++] = "fwmark";
62 argv[argc++] = fwmark;
63 }
64 if (oif) {
65 argv[argc++] = "oif";
66 argv[argc++] = oif;
67 }
68 if (android_fork_execvp(argc, const_cast<char**>(argv), NULL, false, false)) {
69 return false;
70 }
71 }
72
73 return true;
74}
75
76bool modifyNetwork(unsigned netId, const char* interface, Permission permission, bool add) {
77 uint32_t table = getRouteTableForInterface(interface);
78 if (!table) {
79 return false;
80 }
81
82 char table_string[sizeof("0x12345678")];
83 snprintf(table_string, sizeof(table_string), "0x%x", table);
84
85 char mark_string[sizeof("0x12345678/0x12345678")];
86 const char* action = add ? ADD : DEL;
87
88 // A rule to route traffic based on an explicitly chosen network.
89 //
90 // Supports apps that use the multinetwork APIs to restrict their traffic to a network.
91 //
92 // We don't really need to check the permission bits of the fwmark here, as they would've been
93 // checked at the time the netId was set into the fwmark, but we do so to be consistent.
94 uint32_t fwmark = getFwmark(netId, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
95 uint32_t mask = getFwmarkMask(FWMARK_USE_NET_ID, FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT,
96 permission);
97 snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
98 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_EXPLICIT, table_string, mark_string,
99 NULL)) {
100 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);
109 snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
110 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_OIF, table_string, mark_string,
111 interface)) {
112 return false;
113 }
114
115 // A rule to route traffic based on the chosen network.
116 //
117 // This is for sockets that have not explicitly requested a particular network, but have been
118 // bound to one when they called connect(). This ensures that sockets connected on a particular
119 // network stay on that network even if the default network changes.
120 fwmark = getFwmark(netId, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
121 mask = getFwmarkMask(FWMARK_USE_NET_ID, !FWMARK_USE_EXPLICIT, !FWMARK_USE_PROTECT, permission);
122 snprintf(mark_string, sizeof(mark_string), "0x%x/0x%x", fwmark, mask);
123 if (!runIpRuleCommand(action, RULE_PRIORITY_PER_NETWORK_NORMAL, table_string, mark_string,
124 NULL)) {
125 return false;
126 }
127
128 // An iptables rule to mark incoming packets on a network with the netId of the network.
129 //
130 // This is so that the kernel can:
131 // + Use the right fwmark for (and thus correctly route) replies (e.g.: TCP RST, ICMP errors,
132 // ping replies, etc).
133 // + Mark sockets that accept connections from this interface so that the connection stays on
134 // the same interface.
135 action = add ? "-A" : "-D";
136 snprintf(mark_string, sizeof(mark_string), "0x%x", netId);
137 if (execIptables(V4V6, "-t", "mangle", action, "INPUT", "-i", interface, "-j", "MARK",
138 "--set-mark", mark_string, NULL)) {
139 return false;
140 }
141
142 return true;
143}
144
145} // namespace
146
147bool RouteController::createNetwork(unsigned netId, const char* interface, Permission permission) {
148 return modifyNetwork(netId, interface, permission, true);
149}
150
151bool RouteController::destroyNetwork(unsigned netId, const char* interface, Permission permission) {
152 return modifyNetwork(netId, interface, permission, false);
153}