blob: 6a57df6140e9f8fac69d884929f23f54b91753d3 [file] [log] [blame]
Garrick Evansf0ab7132019-06-18 14:50:42 +09001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Garrick Evans3388a032020-03-24 11:25:55 +09005#include "patchpanel/datapath.h"
Garrick Evansf0ab7132019-06-18 14:50:42 +09006
Garrick Evans3d97a392020-02-21 15:24:37 +09007#include <arpa/inet.h>
Garrick Evansc7ae82c2019-09-04 16:25:10 +09008#include <fcntl.h>
9#include <linux/if_tun.h>
10#include <linux/sockios.h>
11#include <net/if.h>
12#include <net/if_arp.h>
13#include <netinet/in.h>
14#include <string.h>
15#include <sys/ioctl.h>
16#include <sys/socket.h>
17
Hugo Benichi2a940542020-10-26 18:50:49 +090018#include <algorithm>
Hugo Benichid82d8832020-08-14 10:05:03 +090019
Garrick Evansc7ae82c2019-09-04 16:25:10 +090020#include <base/files/scoped_file.h>
21#include <base/logging.h>
Taoyu Li79871c92020-07-02 16:09:39 +090022#include <base/posix/eintr_wrapper.h>
Garrick Evans54861622019-07-19 09:05:09 +090023#include <base/strings/string_number_conversions.h>
Hugo Benichi7e3b1fc2020-11-19 15:47:05 +090024#include <base/strings/string_util.h>
25#include <base/strings/stringprintf.h>
Garrick Evans4f9f5572019-11-26 10:25:16 +090026#include <brillo/userdb_utils.h>
Garrick Evans54861622019-07-19 09:05:09 +090027
Jason Jeremy Imana7273a32020-08-04 11:25:31 +090028#include "patchpanel/adb_proxy.h"
Hugo Benichibfc49112020-12-14 12:54:44 +090029#include "patchpanel/arc_service.h"
Garrick Evans3388a032020-03-24 11:25:55 +090030#include "patchpanel/net_util.h"
31#include "patchpanel/scoped_ns.h"
Garrick Evansc7ae82c2019-09-04 16:25:10 +090032
Garrick Evans3388a032020-03-24 11:25:55 +090033namespace patchpanel {
Garrick Evans54861622019-07-19 09:05:09 +090034
Garrick Evansc7ae82c2019-09-04 16:25:10 +090035namespace {
Hugo Benichi76675592020-04-08 14:29:57 +090036// TODO(hugobenichi) Consolidate this constant definition in a single place.
37constexpr pid_t kTestPID = -2;
Garrick Evansc7ae82c2019-09-04 16:25:10 +090038constexpr char kDefaultIfname[] = "vmtap%d";
39constexpr char kTunDev[] = "/dev/net/tun";
Jason Jeremy Imana7273a32020-08-04 11:25:31 +090040constexpr char kArcAddr[] = "100.115.92.2";
41constexpr char kLocalhostAddr[] = "127.0.0.1";
42constexpr uint16_t kAdbServerPort = 5555;
Hugo Benichie8758b52020-04-03 14:49:01 +090043
Hugo Benichibf811c62020-09-07 17:30:45 +090044// Constants used for dropping locally originated traffic bound to an incorrect
45// source IPv4 address.
46constexpr char kGuestIPv4Subnet[] = "100.115.92.0/23";
47constexpr std::array<const char*, 6> kPhysicalIfnamePrefixes{
48 {"eth+", "wlan+", "mlan+", "usb+", "wwan+", "rmnet+"}};
49
Hugo Benichi3a9162b2020-09-09 15:47:40 +090050constexpr char kApplyLocalSourceMarkChain[] = "apply_local_source_mark";
Hugo Benichi3ef370b2020-11-16 19:07:17 +090051constexpr char kApplyVpnMarkChain[] = "apply_vpn_mark";
Hugo Benichi155de002021-01-19 16:45:46 +090052constexpr char kCheckRoutingMarkChain[] = "check_routing_mark";
Hugo Benichi91ee09f2020-12-03 22:24:22 +090053constexpr char kDropGuestIpv4Prefix[] = "drop_guest_ipv4_prefix";
Hugo Benichi3ef370b2020-11-16 19:07:17 +090054
Hugo Benichi2a940542020-10-26 18:50:49 +090055// Constant fwmark mask for matching local socket traffic that should be routed
56// through a VPN connection. The traffic must not be part of an existing
57// connection and must match exactly the VPN routing intent policy bit.
58const Fwmark kFwmarkVpnMatchingMask = kFwmarkRoutingMask | kFwmarkVpnMask;
59
Garrick Evans8a067562020-05-11 12:47:30 +090060std::string PrefixIfname(const std::string& prefix, const std::string& ifname) {
61 std::string n = prefix + ifname;
Garrick Evans2f581a02020-05-11 10:43:35 +090062 if (n.length() < IFNAMSIZ)
63 return n;
Garrick Evans54861622019-07-19 09:05:09 +090064
Garrick Evans2f581a02020-05-11 10:43:35 +090065 // Best effort attempt to preserve the interface number, assuming it's the
66 // last char in the name.
67 auto c = ifname[ifname.length() - 1];
68 n.resize(IFNAMSIZ - 1);
69 n[n.length() - 1] = c;
70 return n;
Garrick Evans54861622019-07-19 09:05:09 +090071}
Garrick Evansf0ab7132019-06-18 14:50:42 +090072
Garrick Evans8a067562020-05-11 12:47:30 +090073} // namespace
74
75std::string ArcVethHostName(const std::string& ifname) {
76 return PrefixIfname("veth", ifname);
77}
78
79std::string ArcBridgeName(const std::string& ifname) {
80 return PrefixIfname("arc_", ifname);
81}
82
Jason Jeremy Imana7273a32020-08-04 11:25:31 +090083Datapath::Datapath(MinijailedProcessRunner* process_runner, Firewall* firewall)
84 : Datapath(process_runner, firewall, ioctl) {}
Garrick Evansc7ae82c2019-09-04 16:25:10 +090085
Jason Jeremy Imana7273a32020-08-04 11:25:31 +090086Datapath::Datapath(MinijailedProcessRunner* process_runner,
87 Firewall* firewall,
88 ioctl_t ioctl_hook)
89 : process_runner_(process_runner), firewall_(firewall), ioctl_(ioctl_hook) {
Garrick Evansf0ab7132019-06-18 14:50:42 +090090 CHECK(process_runner_);
91}
92
Garrick Evans260ff302019-07-25 11:22:50 +090093MinijailedProcessRunner& Datapath::runner() const {
94 return *process_runner_;
95}
96
Hugo Benichibf811c62020-09-07 17:30:45 +090097void Datapath::Start() {
Hugo Benichi91ee09f2020-12-03 22:24:22 +090098 // Restart from a clean iptables state in case of an unordered shutdown.
99 ResetIptables();
100
Hugo Benichibf811c62020-09-07 17:30:45 +0900101 // Enable IPv4 packet forwarding
102 if (process_runner_->sysctl_w("net.ipv4.ip_forward", "1") != 0)
103 LOG(ERROR) << "Failed to update net.ipv4.ip_forward."
104 << " Guest connectivity will not work correctly.";
105
106 // Limit local port range: Android owns 47104-61000.
107 // TODO(garrick): The original history behind this tweak is gone. Some
108 // investigation is needed to see if it is still applicable.
109 if (process_runner_->sysctl_w("net.ipv4.ip_local_port_range",
110 "32768 47103") != 0)
111 LOG(ERROR) << "Failed to limit local port range. Some Android features or"
112 << " apps may not work correctly.";
113
114 // Enable IPv6 packet forwarding
115 if (process_runner_->sysctl_w("net.ipv6.conf.all.forwarding", "1") != 0)
116 LOG(ERROR) << "Failed to update net.ipv6.conf.all.forwarding."
117 << " IPv6 functionality may be broken.";
118
Hugo Benichi58125d32020-09-09 11:25:45 +0900119 // Create a FORWARD ACCEPT rule for connections already established.
120 if (process_runner_->iptables(
121 "filter", {"-A", "FORWARD", "-m", "state", "--state",
122 "ESTABLISHED,RELATED", "-j", "ACCEPT", "-w"}) != 0)
Hugo Benichibf811c62020-09-07 17:30:45 +0900123 LOG(ERROR) << "Failed to install forwarding rule for established"
124 << " connections.";
125
126 // chromium:898210: Drop any locally originated traffic that would exit a
127 // physical interface with a source IPv4 address from the subnet of IPs used
128 // for VMs, containers, and connected namespaces This is needed to prevent
129 // packets leaking with an incorrect src IP when a local process binds to the
130 // wrong interface.
Hugo Benichi91ee09f2020-12-03 22:24:22 +0900131 if (!ModifyChain(IpFamily::IPv4, "filter", "-N", kDropGuestIpv4Prefix))
132 LOG(ERROR) << "Failed to create " << kDropGuestIpv4Prefix
133 << " filter chain";
134 if (!ModifyIptables(IpFamily::IPv4, "filter",
135 {"-I", "OUTPUT", "-j", kDropGuestIpv4Prefix, "-w"}))
136 LOG(ERROR) << "Failed to set up jump rule from filter OUTPUT to "
137 << kDropGuestIpv4Prefix;
Hugo Benichibf811c62020-09-07 17:30:45 +0900138 for (const auto& oif : kPhysicalIfnamePrefixes) {
139 if (!AddSourceIPv4DropRule(oif, kGuestIPv4Subnet))
140 LOG(WARNING) << "Failed to set up IPv4 drop rule for src ip "
141 << kGuestIPv4Subnet << " exiting " << oif;
142 }
143
Hugo Benichi561fae42021-01-22 15:28:40 +0900144 // Set static SNAT rules for any IPv4 traffic originated from a guest (ARC,
145 // Crostini, ...) or a connected namespace.
146 // chromium:1050579: INVALID packets cannot be tracked by conntrack therefore
147 // need to be explicitly dropped as SNAT cannot be applied to them.
148 if (process_runner_->iptables(
149 "filter", {"-A", "FORWARD", "-m", "mark", "--mark", "1/1", "-m",
150 "state", "--state", "INVALID", "-j", "DROP", "-w"}) != 0)
151 LOG(ERROR) << "Failed to install SNAT mark rules.";
152 if (process_runner_->iptables(
153 "nat", {"-A", "POSTROUTING", "-m", "mark", "--mark", "1/1", "-j",
154 "MASQUERADE", "-w"}) != 0)
155 LOG(ERROR) << "Failed to install SNAT mark rules.";
Hugo Benichibf811c62020-09-07 17:30:45 +0900156 if (!AddOutboundIPv4SNATMark("vmtap+"))
Hugo Benichi561fae42021-01-22 15:28:40 +0900157 LOG(ERROR) << "Failed to set up NAT for TAP devices.";
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900158
Hugo Benichi2a940542020-10-26 18:50:49 +0900159 // Applies the routing tag saved in conntrack for any established connection
160 // for sockets created in the host network namespace.
Hugo Benichi1af52392020-11-27 18:09:32 +0900161 if (!ModifyConnmarkRestore(IpFamily::Dual, "OUTPUT", "-A", "" /*iif*/,
162 kFwmarkRoutingMask))
Hugo Benichi2a940542020-10-26 18:50:49 +0900163 LOG(ERROR) << "Failed to add OUTPUT CONNMARK restore rule";
Hugo Benichi155de002021-01-19 16:45:46 +0900164 // b/177787823 Also restore the routing tag after routing has taken place so
165 // that packets pre-tagged with the VPN routing tag are in sync with their
166 // associated CONNMARK routing tag. This is necessary to correctly identify
167 // packets exiting through the wrong interface.
168 if (!ModifyConnmarkRestore(IpFamily::Dual, "POSTROUTING", "-A", "" /*iif*/,
169 kFwmarkRoutingMask))
170 LOG(ERROR) << "Failed to add POSTROUTING CONNMARK restore rule";
Hugo Benichi2a940542020-10-26 18:50:49 +0900171
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900172 // Set up a mangle chain used in OUTPUT for applying the fwmark TrafficSource
173 // tag and tagging the local traffic that should be routed through a VPN.
174 if (!ModifyChain(IpFamily::Dual, "mangle", "-N", kApplyLocalSourceMarkChain))
175 LOG(ERROR) << "Failed to set up " << kApplyLocalSourceMarkChain
176 << " mangle chain";
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900177 if (!ModifyIptables(IpFamily::Dual, "mangle",
178 {"-A", "OUTPUT", "-j", kApplyLocalSourceMarkChain, "-w"}))
179 LOG(ERROR) << "Failed to attach " << kApplyLocalSourceMarkChain
180 << " to mangle OUTPUT";
181 // Create rules for tagging local sources with the source tag and the vpn
182 // policy tag.
183 for (const auto& source : kLocalSourceTypes) {
Hugo Benichi620202f2020-11-27 10:14:38 +0900184 if (!ModifyFwmarkLocalSourceTag("-A", source))
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900185 LOG(ERROR) << "Failed to create fwmark tagging rule for uid " << source
186 << " in " << kApplyLocalSourceMarkChain;
187 }
188 // Finally add a catch-all rule for tagging any remaining local sources with
189 // the SYSTEM source tag
190 if (!ModifyFwmarkDefaultLocalSourceTag("-A", TrafficSource::SYSTEM))
191 LOG(ERROR) << "Failed to set up rule tagging traffic with default source";
192
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900193 // Sets up a mangle chain used in OUTPUT and PREROUTING for tagging "user"
194 // traffic that should be routed through a VPN.
195 if (!ModifyChain(IpFamily::Dual, "mangle", "-N", kApplyVpnMarkChain))
196 LOG(ERROR) << "Failed to set up " << kApplyVpnMarkChain << " mangle chain";
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900197 // All local outgoing traffic eligible to VPN routing should traverse the VPN
198 // marking chain.
199 if (!ModifyFwmarkVpnJumpRule("OUTPUT", "-A", "" /*iif*/, kFwmarkRouteOnVpn,
200 kFwmarkVpnMask))
201 LOG(ERROR) << "Failed to add jump rule to VPN chain in mangle OUTPUT chain";
202 // Any traffic that already has a routing tag applied is accepted.
203 if (!ModifyIptables(
204 IpFamily::Dual, "mangle",
205 {"-A", kApplyVpnMarkChain, "-m", "mark", "!", "--mark",
206 "0x0/" + kFwmarkRoutingMask.ToString(), "-j", "ACCEPT", "-w"}))
207 LOG(ERROR) << "Failed to add ACCEPT rule to VPN tagging chain for marked "
208 "connections";
Hugo Benichi155de002021-01-19 16:45:46 +0900209
210 // Sets up a mangle chain used in POSTROUTING for checking consistency between
211 // the routing tag and the output interface.
212 if (!ModifyChain(IpFamily::Dual, "mangle", "-N", kCheckRoutingMarkChain))
213 LOG(ERROR) << "Failed to set up " << kCheckRoutingMarkChain
214 << " mangle chain";
Hugo Benichi155de002021-01-19 16:45:46 +0900215 // b/177787823 If it already exists, the routing tag of any traffic exiting an
216 // interface (physical or VPN) must match the routing tag of that interface.
217 if (!ModifyIptables(IpFamily::Dual, "mangle",
218 {"-A", "POSTROUTING", "-m", "mark", "!", "--mark",
219 "0x0/" + kFwmarkRoutingMask.ToString(), "-j",
220 kCheckRoutingMarkChain, "-w"}))
221 LOG(ERROR) << "Failed to add POSTROUTING jump rule to "
222 << kCheckRoutingMarkChain;
Hugo Benichi52a64992021-01-28 17:47:33 +0900223
224 // b/176260499: on 4.4 kernel, the following connmark rules are observed to
225 // incorrectly cause neighbor discovery icmpv6 packets to be dropped. Add
226 // these rules to bypass connmark rule for those packets.
227 for (const auto& type : kNeighborDiscoveryTypes) {
228 if (!ModifyIptables(IpFamily::IPv6, "mangle",
229 {"-I", "OUTPUT", "-p", "icmpv6", "--icmpv6-type", type,
230 "-j", "ACCEPT", "-w"}))
231 LOG(ERROR) << "Failed to set up connmark bypass rule for " << type
232 << " packets in OUTPUT";
233 if (!ModifyIptables(IpFamily::IPv6, "mangle",
234 {"-I", kCheckRoutingMarkChain, "-p", "icmpv6",
235 "--icmpv6-type", type, "-j", "RETURN", "-w"}))
236 LOG(ERROR) << "Failed to set up connmark bypass rule for " << type
237 << " packets in " << kCheckRoutingMarkChain;
238 }
Hugo Benichibf811c62020-09-07 17:30:45 +0900239}
240
241void Datapath::Stop() {
Hugo Benichibf811c62020-09-07 17:30:45 +0900242 // Restore original local port range.
243 // TODO(garrick): The original history behind this tweak is gone. Some
244 // investigation is needed to see if it is still applicable.
245 if (process_runner_->sysctl_w("net.ipv4.ip_local_port_range",
246 "32768 61000") != 0)
247 LOG(ERROR) << "Failed to restore local port range";
248
249 // Disable packet forwarding
250 if (process_runner_->sysctl_w("net.ipv6.conf.all.forwarding", "0") != 0)
251 LOG(ERROR) << "Failed to restore net.ipv6.conf.all.forwarding.";
252
253 if (process_runner_->sysctl_w("net.ipv4.ip_forward", "0") != 0)
254 LOG(ERROR) << "Failed to restore net.ipv4.ip_forward.";
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900255
Hugo Benichi91ee09f2020-12-03 22:24:22 +0900256 ResetIptables();
257}
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900258
Hugo Benichi91ee09f2020-12-03 22:24:22 +0900259void Datapath::ResetIptables() {
260 // If it exists, remove jump rules from a built-in chain to a custom routing
261 // or tagging chain.
262 ModifyIptables(IpFamily::IPv4, "filter",
263 {"-D", "OUTPUT", "-j", kDropGuestIpv4Prefix, "-w"},
264 false /*log_failures*/);
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900265
Hugo Benichi91ee09f2020-12-03 22:24:22 +0900266 // Flush chains used for routing and fwmark tagging. Also delete additional
267 // chains made by patchpanel. Chains used by permission broker (nat
268 // PREROUTING, filter INPUT) and chains used for traffic counters (mangle
269 // {rx,tx}_{<iface>, vpn}) are not flushed.
270 static struct {
271 IpFamily family;
272 std::string table;
273 std::string chain;
274 bool should_delete;
275 } resetOps[] = {
276 {IpFamily::Dual, "filter", "FORWARD", false},
277 {IpFamily::Dual, "mangle", "FORWARD", false},
278 {IpFamily::Dual, "mangle", "INPUT", false},
279 {IpFamily::Dual, "mangle", "OUTPUT", false},
280 {IpFamily::Dual, "mangle", "POSTROUTING", false},
281 {IpFamily::Dual, "mangle", "PREROUTING", false},
282 {IpFamily::Dual, "mangle", kApplyLocalSourceMarkChain, true},
283 {IpFamily::Dual, "mangle", kApplyVpnMarkChain, true},
284 {IpFamily::Dual, "mangle", kCheckRoutingMarkChain, true},
285 {IpFamily::IPv4, "filter", kDropGuestIpv4Prefix, true},
286 {IpFamily::IPv4, "nat", "POSTROUTING", false},
287 };
288 for (const auto& op : resetOps) {
289 // Chains to delete are custom chains and will not exist the first time
290 // patchpanel starts after boot. Skip flushing and delete these chains if
291 // they do not exist to avoid logging spurious error messages.
292 if (op.should_delete && !ModifyChain(op.family, op.table, "-L", op.chain,
293 false /*log_failures*/))
294 continue;
Hugo Benichi155de002021-01-19 16:45:46 +0900295
Hugo Benichi91ee09f2020-12-03 22:24:22 +0900296 if (!ModifyChain(op.family, op.table, "-F", op.chain))
297 LOG(ERROR) << "Failed to flush " << op.chain << " chain in table "
298 << op.table;
Hugo Benichi2a940542020-10-26 18:50:49 +0900299
Hugo Benichi91ee09f2020-12-03 22:24:22 +0900300 if (op.should_delete && !ModifyChain(op.family, op.table, "-X", op.chain))
301 LOG(ERROR) << "Failed to delete " << op.chain << " chain in table "
302 << op.table;
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900303 }
Hugo Benichibf811c62020-09-07 17:30:45 +0900304}
305
Hugo Benichi33860d72020-07-09 16:34:01 +0900306bool Datapath::NetnsAttachName(const std::string& netns_name, pid_t netns_pid) {
307 // Try first to delete any netns with name |netns_name| in case patchpanel
308 // did not exit cleanly.
309 if (process_runner_->ip_netns_delete(netns_name, false /*log_failures*/) == 0)
310 LOG(INFO) << "Deleted left over network namespace name " << netns_name;
311 return process_runner_->ip_netns_attach(netns_name, netns_pid) == 0;
312}
313
314bool Datapath::NetnsDeleteName(const std::string& netns_name) {
315 return process_runner_->ip_netns_delete(netns_name) == 0;
316}
317
Garrick Evans8a949dc2019-07-18 16:17:53 +0900318bool Datapath::AddBridge(const std::string& ifname,
Garrick Evans7a1a9ee2020-01-28 11:03:57 +0900319 uint32_t ipv4_addr,
320 uint32_t ipv4_prefix_len) {
Garrick Evans8a949dc2019-07-18 16:17:53 +0900321 // Configure the persistent Chrome OS bridge interface with static IP.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900322 if (process_runner_->brctl("addbr", {ifname}) != 0) {
Garrick Evans8a949dc2019-07-18 16:17:53 +0900323 return false;
324 }
325
Garrick Evans6f4fa3a2020-02-10 16:15:09 +0900326 if (process_runner_->ip(
327 "addr", "add",
328 {IPv4AddressToCidrString(ipv4_addr, ipv4_prefix_len), "brd",
329 IPv4AddressToString(Ipv4BroadcastAddr(ipv4_addr, ipv4_prefix_len)),
330 "dev", ifname}) != 0) {
Garrick Evans7a1a9ee2020-01-28 11:03:57 +0900331 RemoveBridge(ifname);
332 return false;
333 }
334
335 if (process_runner_->ip("link", "set", {ifname, "up"}) != 0) {
Garrick Evans8a949dc2019-07-18 16:17:53 +0900336 RemoveBridge(ifname);
337 return false;
338 }
339
340 // See nat.conf in chromeos-nat-init for the rest of the NAT setup rules.
Hugo Benichie8758b52020-04-03 14:49:01 +0900341 if (!AddOutboundIPv4SNATMark(ifname)) {
Garrick Evans8a949dc2019-07-18 16:17:53 +0900342 RemoveBridge(ifname);
343 return false;
344 }
345
346 return true;
347}
348
349void Datapath::RemoveBridge(const std::string& ifname) {
Hugo Benichie8758b52020-04-03 14:49:01 +0900350 RemoveOutboundIPv4SNATMark(ifname);
Garrick Evans7a1a9ee2020-01-28 11:03:57 +0900351 process_runner_->ip("link", "set", {ifname, "down"});
Garrick Evans8e8e3472020-01-23 14:03:50 +0900352 process_runner_->brctl("delbr", {ifname});
Garrick Evans8a949dc2019-07-18 16:17:53 +0900353}
354
Garrick Evans621ed262019-11-13 12:28:43 +0900355bool Datapath::AddToBridge(const std::string& br_ifname,
356 const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900357 return (process_runner_->brctl("addif", {br_ifname, ifname}) == 0);
Garrick Evans621ed262019-11-13 12:28:43 +0900358}
359
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900360std::string Datapath::AddTAP(const std::string& name,
Garrick Evans621ed262019-11-13 12:28:43 +0900361 const MacAddress* mac_addr,
362 const SubnetAddress* ipv4_addr,
Garrick Evans4f9f5572019-11-26 10:25:16 +0900363 const std::string& user) {
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900364 base::ScopedFD dev(open(kTunDev, O_RDWR | O_NONBLOCK));
365 if (!dev.is_valid()) {
366 PLOG(ERROR) << "Failed to open " << kTunDev;
367 return "";
368 }
369
370 struct ifreq ifr;
371 memset(&ifr, 0, sizeof(ifr));
372 strncpy(ifr.ifr_name, name.empty() ? kDefaultIfname : name.c_str(),
373 sizeof(ifr.ifr_name));
374 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
375
376 // If a template was given as the name, ifr_name will be updated with the
377 // actual interface name.
378 if ((*ioctl_)(dev.get(), TUNSETIFF, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900379 PLOG(ERROR) << "Failed to create tap interface " << name;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900380 return "";
381 }
382 const char* ifname = ifr.ifr_name;
383
384 if ((*ioctl_)(dev.get(), TUNSETPERSIST, 1) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900385 PLOG(ERROR) << "Failed to persist the interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900386 return "";
387 }
388
Garrick Evans4f9f5572019-11-26 10:25:16 +0900389 if (!user.empty()) {
390 uid_t uid = -1;
391 if (!brillo::userdb::GetUserInfo(user, &uid, nullptr)) {
392 PLOG(ERROR) << "Unable to look up UID for " << user;
393 RemoveTAP(ifname);
394 return "";
395 }
396 if ((*ioctl_)(dev.get(), TUNSETOWNER, uid) != 0) {
397 PLOG(ERROR) << "Failed to set owner " << uid << " of tap interface "
398 << ifname;
399 RemoveTAP(ifname);
400 return "";
401 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900402 }
403
Hugo Benichib9b93fe2019-10-25 23:36:01 +0900404 // Create control socket for configuring the interface.
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900405 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
406 if (!sock.is_valid()) {
407 PLOG(ERROR) << "Failed to create control socket for tap interface "
Garrick Evans621ed262019-11-13 12:28:43 +0900408 << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900409 RemoveTAP(ifname);
410 return "";
411 }
412
Garrick Evans621ed262019-11-13 12:28:43 +0900413 if (ipv4_addr) {
414 struct sockaddr_in* addr =
415 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
416 addr->sin_family = AF_INET;
417 addr->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr->Address());
418 if ((*ioctl_)(sock.get(), SIOCSIFADDR, &ifr) != 0) {
419 PLOG(ERROR) << "Failed to set ip address for vmtap interface " << ifname
420 << " {" << ipv4_addr->ToCidrString() << "}";
421 RemoveTAP(ifname);
422 return "";
423 }
424
425 struct sockaddr_in* netmask =
426 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_netmask);
427 netmask->sin_family = AF_INET;
428 netmask->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr->Netmask());
429 if ((*ioctl_)(sock.get(), SIOCSIFNETMASK, &ifr) != 0) {
430 PLOG(ERROR) << "Failed to set netmask for vmtap interface " << ifname
431 << " {" << ipv4_addr->ToCidrString() << "}";
432 RemoveTAP(ifname);
433 return "";
434 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900435 }
436
Garrick Evans621ed262019-11-13 12:28:43 +0900437 if (mac_addr) {
438 struct sockaddr* hwaddr = &ifr.ifr_hwaddr;
439 hwaddr->sa_family = ARPHRD_ETHER;
440 memcpy(&hwaddr->sa_data, mac_addr, sizeof(*mac_addr));
441 if ((*ioctl_)(sock.get(), SIOCSIFHWADDR, &ifr) != 0) {
442 PLOG(ERROR) << "Failed to set mac address for vmtap interface " << ifname
443 << " {" << MacAddressToString(*mac_addr) << "}";
444 RemoveTAP(ifname);
445 return "";
446 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900447 }
448
449 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900450 PLOG(ERROR) << "Failed to get flags for tap interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900451 RemoveTAP(ifname);
452 return "";
453 }
454
455 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
456 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900457 PLOG(ERROR) << "Failed to enable tap interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900458 RemoveTAP(ifname);
459 return "";
460 }
461
462 return ifname;
463}
464
465void Datapath::RemoveTAP(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900466 process_runner_->ip("tuntap", "del", {ifname, "mode", "tap"});
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900467}
468
Hugo Benichi33860d72020-07-09 16:34:01 +0900469bool Datapath::ConnectVethPair(pid_t netns_pid,
470 const std::string& netns_name,
Hugo Benichi76675592020-04-08 14:29:57 +0900471 const std::string& veth_ifname,
472 const std::string& peer_ifname,
473 const MacAddress& remote_mac_addr,
474 uint32_t remote_ipv4_addr,
475 uint32_t remote_ipv4_prefix_len,
476 bool remote_multicast_flag) {
Hugo Benichi33860d72020-07-09 16:34:01 +0900477 // Set up the virtual pair across the current namespace and |netns_name|.
478 if (!AddVirtualInterfacePair(netns_name, veth_ifname, peer_ifname)) {
479 LOG(ERROR) << "Failed to create veth pair " << veth_ifname << ","
480 << peer_ifname;
481 return false;
482 }
483
484 // Configure the remote veth in namespace |netns_name|.
Hugo Benichi76675592020-04-08 14:29:57 +0900485 {
Hugo Benichi33860d72020-07-09 16:34:01 +0900486 ScopedNS ns(netns_pid);
487 if (!ns.IsValid() && netns_pid != kTestPID) {
Hugo Benichi76675592020-04-08 14:29:57 +0900488 LOG(ERROR)
489 << "Cannot create virtual link -- invalid container namespace?";
490 return false;
491 }
492
Hugo Benichi76675592020-04-08 14:29:57 +0900493 if (!ConfigureInterface(peer_ifname, remote_mac_addr, remote_ipv4_addr,
494 remote_ipv4_prefix_len, true /* link up */,
495 remote_multicast_flag)) {
496 LOG(ERROR) << "Failed to configure interface " << peer_ifname;
497 RemoveInterface(peer_ifname);
498 return false;
499 }
500 }
501
Hugo Benichi76675592020-04-08 14:29:57 +0900502 if (!ToggleInterface(veth_ifname, true /*up*/)) {
503 LOG(ERROR) << "Failed to bring up interface " << veth_ifname;
504 RemoveInterface(veth_ifname);
505 return false;
506 }
Hugo Benichi33860d72020-07-09 16:34:01 +0900507
Hugo Benichi76675592020-04-08 14:29:57 +0900508 return true;
509}
510
Hugo Benichi33860d72020-07-09 16:34:01 +0900511bool Datapath::AddVirtualInterfacePair(const std::string& netns_name,
512 const std::string& veth_ifname,
Garrick Evans2470caa2020-03-04 14:15:41 +0900513 const std::string& peer_ifname) {
Hugo Benichi33860d72020-07-09 16:34:01 +0900514 return process_runner_->ip("link", "add",
515 {veth_ifname, "type", "veth", "peer", "name",
516 peer_ifname, "netns", netns_name}) == 0;
Garrick Evans2470caa2020-03-04 14:15:41 +0900517}
Garrick Evans54861622019-07-19 09:05:09 +0900518
Garrick Evans2470caa2020-03-04 14:15:41 +0900519bool Datapath::ToggleInterface(const std::string& ifname, bool up) {
520 const std::string link = up ? "up" : "down";
521 return process_runner_->ip("link", "set", {ifname, link}) == 0;
522}
Garrick Evans54861622019-07-19 09:05:09 +0900523
Garrick Evans2470caa2020-03-04 14:15:41 +0900524bool Datapath::ConfigureInterface(const std::string& ifname,
525 const MacAddress& mac_addr,
526 uint32_t ipv4_addr,
527 uint32_t ipv4_prefix_len,
528 bool up,
529 bool enable_multicast) {
530 const std::string link = up ? "up" : "down";
531 const std::string multicast = enable_multicast ? "on" : "off";
532 return (process_runner_->ip(
533 "addr", "add",
534 {IPv4AddressToCidrString(ipv4_addr, ipv4_prefix_len), "brd",
535 IPv4AddressToString(
536 Ipv4BroadcastAddr(ipv4_addr, ipv4_prefix_len)),
537 "dev", ifname}) == 0) &&
538 (process_runner_->ip("link", "set",
539 {
540 "dev",
541 ifname,
542 link,
543 "addr",
544 MacAddressToString(mac_addr),
545 "multicast",
546 multicast,
547 }) == 0);
Garrick Evans54861622019-07-19 09:05:09 +0900548}
549
550void Datapath::RemoveInterface(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900551 process_runner_->ip("link", "delete", {ifname}, false /*log_failures*/);
Garrick Evans54861622019-07-19 09:05:09 +0900552}
553
Hugo Benichi321f23b2020-09-25 15:42:05 +0900554bool Datapath::AddSourceIPv4DropRule(const std::string& oif,
555 const std::string& src_ip) {
Hugo Benichi91ee09f2020-12-03 22:24:22 +0900556 return process_runner_->iptables(
557 "filter", {"-I", kDropGuestIpv4Prefix, "-o", oif, "-s", src_ip,
558 "-j", "DROP", "-w"}) == 0;
Hugo Benichi321f23b2020-09-25 15:42:05 +0900559}
560
561bool Datapath::RemoveSourceIPv4DropRule(const std::string& oif,
562 const std::string& src_ip) {
Hugo Benichi91ee09f2020-12-03 22:24:22 +0900563 return process_runner_->iptables(
564 "filter", {"-D", kDropGuestIpv4Prefix, "-o", oif, "-s", src_ip,
565 "-j", "DROP", "-w"}) == 0;
Hugo Benichi321f23b2020-09-25 15:42:05 +0900566}
567
Hugo Benichifcf81022020-12-04 11:01:37 +0900568bool Datapath::StartRoutingNamespace(const ConnectedNamespace& nsinfo) {
Hugo Benichi7c342672020-09-08 09:18:14 +0900569 // Veth interface configuration and client routing configuration:
570 // - attach a name to the client namespace.
571 // - create veth pair across the current namespace and the client namespace.
572 // - configure IPv4 address on remote veth inside client namespace.
573 // - configure IPv4 address on local veth inside host namespace.
574 // - add a default IPv4 /0 route sending traffic to that remote veth.
Hugo Benichifcf81022020-12-04 11:01:37 +0900575 if (!NetnsAttachName(nsinfo.netns_name, nsinfo.pid)) {
576 LOG(ERROR) << "Failed to attach name " << nsinfo.netns_name
577 << " to namespace pid " << nsinfo.pid;
Hugo Benichi7c342672020-09-08 09:18:14 +0900578 return false;
579 }
580
Hugo Benichifcf81022020-12-04 11:01:37 +0900581 if (!ConnectVethPair(
582 nsinfo.pid, nsinfo.netns_name, nsinfo.host_ifname, nsinfo.peer_ifname,
583 nsinfo.peer_mac_addr, nsinfo.peer_subnet->AddressAtOffset(1),
584 nsinfo.peer_subnet->PrefixLength(), false /* enable_multicast */)) {
Hugo Benichi7c342672020-09-08 09:18:14 +0900585 LOG(ERROR) << "Failed to create veth pair for"
586 " namespace pid "
Hugo Benichifcf81022020-12-04 11:01:37 +0900587 << nsinfo.pid;
588 NetnsDeleteName(nsinfo.netns_name);
Hugo Benichi7c342672020-09-08 09:18:14 +0900589 return false;
590 }
591
Hugo Benichifcf81022020-12-04 11:01:37 +0900592 if (!ConfigureInterface(nsinfo.host_ifname, nsinfo.peer_mac_addr,
593 nsinfo.peer_subnet->AddressAtOffset(0),
594 nsinfo.peer_subnet->PrefixLength(),
595 true /* link up */, false /* enable_multicast */)) {
596 LOG(ERROR) << "Cannot configure host interface " << nsinfo.host_ifname;
597 RemoveInterface(nsinfo.host_ifname);
598 NetnsDeleteName(nsinfo.netns_name);
Hugo Benichi7c342672020-09-08 09:18:14 +0900599 return false;
600 }
601
602 {
Hugo Benichifcf81022020-12-04 11:01:37 +0900603 ScopedNS ns(nsinfo.pid);
604 if (!ns.IsValid() && nsinfo.pid != kTestPID) {
605 LOG(ERROR) << "Invalid namespace pid " << nsinfo.pid;
606 RemoveInterface(nsinfo.host_ifname);
607 NetnsDeleteName(nsinfo.netns_name);
Hugo Benichi7c342672020-09-08 09:18:14 +0900608 return false;
609 }
610
Hugo Benichifcf81022020-12-04 11:01:37 +0900611 if (!AddIPv4Route(nsinfo.peer_subnet->AddressAtOffset(0), INADDR_ANY,
612 INADDR_ANY)) {
613 LOG(ERROR) << "Failed to add default /0 route to " << nsinfo.host_ifname
614 << " inside namespace pid " << nsinfo.pid;
615 RemoveInterface(nsinfo.host_ifname);
616 NetnsDeleteName(nsinfo.netns_name);
Hugo Benichi7c342672020-09-08 09:18:14 +0900617 return false;
618 }
619 }
620
621 // Host namespace routing configuration
622 // - ingress: add route to client subnet via |host_ifname|.
623 // - egress: - allow forwarding for traffic outgoing |host_ifname|.
624 // - add SNAT mark 0x1/0x1 for traffic outgoing |host_ifname|.
625 // Note that by default unsolicited ingress traffic is not forwarded to the
626 // client namespace unless the client specifically set port forwarding
627 // through permission_broker DBus APIs.
628 // TODO(hugobenichi) If allow_user_traffic is false, then prevent forwarding
629 // both ways between client namespace and other guest containers and VMs.
Hugo Benichifcf81022020-12-04 11:01:37 +0900630 uint32_t netmask = Ipv4Netmask(nsinfo.peer_subnet->PrefixLength());
631 if (!AddIPv4Route(nsinfo.peer_subnet->AddressAtOffset(0),
632 nsinfo.peer_subnet->BaseAddress(), netmask)) {
Hugo Benichi7c342672020-09-08 09:18:14 +0900633 LOG(ERROR) << "Failed to set route to client namespace";
Hugo Benichifcf81022020-12-04 11:01:37 +0900634 RemoveInterface(nsinfo.host_ifname);
635 NetnsDeleteName(nsinfo.netns_name);
Hugo Benichi7c342672020-09-08 09:18:14 +0900636 return false;
637 }
638
Hugo Benichi7c342672020-09-08 09:18:14 +0900639 // TODO(b/161508179) Do not rely on legacy fwmark 1 for SNAT.
Hugo Benichifcf81022020-12-04 11:01:37 +0900640 if (!AddOutboundIPv4SNATMark(nsinfo.host_ifname)) {
Hugo Benichi7c342672020-09-08 09:18:14 +0900641 LOG(ERROR) << "Failed to set SNAT for traffic"
642 " outgoing from "
Hugo Benichifcf81022020-12-04 11:01:37 +0900643 << nsinfo.host_ifname;
644 RemoveInterface(nsinfo.host_ifname);
645 DeleteIPv4Route(nsinfo.peer_subnet->AddressAtOffset(0),
646 nsinfo.peer_subnet->BaseAddress(), netmask);
647 StopIpForwarding(IpFamily::IPv4, "", nsinfo.host_ifname);
648 NetnsDeleteName(nsinfo.netns_name);
Hugo Benichi7c342672020-09-08 09:18:14 +0900649 return false;
650 }
651
Hugo Benichi93306e52020-12-04 16:08:00 +0900652 StartRoutingDevice(nsinfo.outbound_ifname, nsinfo.host_ifname,
653 nsinfo.peer_subnet->AddressAtOffset(0), nsinfo.source,
654 nsinfo.route_on_vpn);
655
Hugo Benichi7c342672020-09-08 09:18:14 +0900656 return true;
657}
658
Hugo Benichifcf81022020-12-04 11:01:37 +0900659void Datapath::StopRoutingNamespace(const ConnectedNamespace& nsinfo) {
Hugo Benichi93306e52020-12-04 16:08:00 +0900660 StopRoutingDevice(nsinfo.outbound_ifname, nsinfo.host_ifname,
661 nsinfo.peer_subnet->AddressAtOffset(0), nsinfo.source,
662 nsinfo.route_on_vpn);
Hugo Benichifcf81022020-12-04 11:01:37 +0900663 RemoveInterface(nsinfo.host_ifname);
Hugo Benichifcf81022020-12-04 11:01:37 +0900664 RemoveOutboundIPv4SNATMark(nsinfo.host_ifname);
665 DeleteIPv4Route(nsinfo.peer_subnet->AddressAtOffset(0),
666 nsinfo.peer_subnet->BaseAddress(),
667 Ipv4Netmask(nsinfo.peer_subnet->PrefixLength()));
668 NetnsDeleteName(nsinfo.netns_name);
Hugo Benichi7c342672020-09-08 09:18:14 +0900669}
670
Hugo Benichi8d622b52020-08-13 15:24:12 +0900671void Datapath::StartRoutingDevice(const std::string& ext_ifname,
672 const std::string& int_ifname,
673 uint32_t int_ipv4_addr,
Hugo Benichi93306e52020-12-04 16:08:00 +0900674 TrafficSource source,
675 bool route_on_vpn) {
676 if (source == TrafficSource::ARC && !ext_ifname.empty() &&
Hugo Benichibfc49112020-12-14 12:54:44 +0900677 int_ipv4_addr != 0 &&
Hugo Benichi8d622b52020-08-13 15:24:12 +0900678 !AddInboundIPv4DNAT(ext_ifname, IPv4AddressToString(int_ipv4_addr)))
679 LOG(ERROR) << "Failed to configure ingress traffic rules for " << ext_ifname
680 << "->" << int_ifname;
681
Hugo Benichifa97b3b2020-10-06 22:45:26 +0900682 if (!StartIpForwarding(IpFamily::IPv4, ext_ifname, int_ifname))
Hugo Benichic6ae67c2020-08-14 15:02:13 +0900683 LOG(ERROR) << "Failed to enable IP forwarding for " << ext_ifname << "->"
684 << int_ifname;
Hugo Benichi8d622b52020-08-13 15:24:12 +0900685
Hugo Benichifa97b3b2020-10-06 22:45:26 +0900686 if (!StartIpForwarding(IpFamily::IPv4, int_ifname, ext_ifname))
Hugo Benichic6ae67c2020-08-14 15:02:13 +0900687 LOG(ERROR) << "Failed to enable IP forwarding for " << ext_ifname << "<-"
688 << int_ifname;
Hugo Benichi8d622b52020-08-13 15:24:12 +0900689
Hugo Benichi2a940542020-10-26 18:50:49 +0900690 if (!ModifyFwmarkSourceTag("-A", int_ifname, source))
691 LOG(ERROR) << "Failed to add PREROUTING fwmark tagging rule for source "
692 << source << " for " << int_ifname;
693
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900694 if (!ext_ifname.empty()) {
695 // If |ext_ifname| is not null, mark egress traffic with the
696 // fwmark routing tag corresponding to |ext_ifname|.
Hugo Benichi2a940542020-10-26 18:50:49 +0900697 if (!ModifyFwmarkRoutingTag("PREROUTING", "-A", ext_ifname, int_ifname))
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900698 LOG(ERROR) << "Failed to add PREROUTING fwmark routing tag for "
699 << ext_ifname << "<-" << int_ifname;
700 } else {
701 // Otherwise if ext_ifname is null, set up a CONNMARK restore rule in
702 // PREROUTING to apply any fwmark routing tag saved for the current
703 // connection, and rely on implicit routing to the default logical network
704 // otherwise.
Hugo Benichi1af52392020-11-27 18:09:32 +0900705 if (!ModifyConnmarkRestore(IpFamily::Dual, "PREROUTING", "-A", int_ifname,
706 kFwmarkRoutingMask))
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900707 LOG(ERROR) << "Failed to add PREROUTING CONNMARK restore rule for "
708 << int_ifname;
Hugo Benichi8d622b52020-08-13 15:24:12 +0900709
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900710 // Forwarded traffic from downstream virtual devices routed to the system
Hugo Benichi93306e52020-12-04 16:08:00 +0900711 // default network is eligible to be routed through a VPN if |route_on_vpn|
712 // is true.
713 if (route_on_vpn &&
714 !ModifyFwmarkVpnJumpRule("PREROUTING", "-A", int_ifname, {}, {}))
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900715 LOG(ERROR) << "Failed to add jump rule to VPN chain for " << int_ifname;
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900716 }
Hugo Benichi8d622b52020-08-13 15:24:12 +0900717}
718
719void Datapath::StopRoutingDevice(const std::string& ext_ifname,
720 const std::string& int_ifname,
721 uint32_t int_ipv4_addr,
Hugo Benichi93306e52020-12-04 16:08:00 +0900722 TrafficSource source,
723 bool route_on_vpn) {
Hugo Benichibfc49112020-12-14 12:54:44 +0900724 if (source == TrafficSource::ARC && !ext_ifname.empty() && int_ipv4_addr != 0)
Hugo Benichi8d622b52020-08-13 15:24:12 +0900725 RemoveInboundIPv4DNAT(ext_ifname, IPv4AddressToString(int_ipv4_addr));
Hugo Benichic6ae67c2020-08-14 15:02:13 +0900726 StopIpForwarding(IpFamily::IPv4, ext_ifname, int_ifname);
727 StopIpForwarding(IpFamily::IPv4, int_ifname, ext_ifname);
Hugo Benichi9be19b12020-08-14 15:33:40 +0900728 ModifyFwmarkSourceTag("-D", int_ifname, source);
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900729 if (!ext_ifname.empty()) {
Hugo Benichi2a940542020-10-26 18:50:49 +0900730 ModifyFwmarkRoutingTag("PREROUTING", "-D", ext_ifname, int_ifname);
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900731 } else {
Hugo Benichi1af52392020-11-27 18:09:32 +0900732 ModifyConnmarkRestore(IpFamily::Dual, "PREROUTING", "-D", int_ifname,
733 kFwmarkRoutingMask);
Hugo Benichi93306e52020-12-04 16:08:00 +0900734 if (route_on_vpn)
735 ModifyFwmarkVpnJumpRule("PREROUTING", "-D", int_ifname, {}, {});
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900736 }
Hugo Benichi8d622b52020-08-13 15:24:12 +0900737}
738
Garrick Evansf0ab7132019-06-18 14:50:42 +0900739bool Datapath::AddInboundIPv4DNAT(const std::string& ifname,
740 const std::string& ipv4_addr) {
741 // Direct ingress IP traffic to existing sockets.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900742 if (process_runner_->iptables(
743 "nat", {"-A", "PREROUTING", "-i", ifname, "-m", "socket",
744 "--nowildcard", "-j", "ACCEPT", "-w"}) != 0)
Garrick Evansf0ab7132019-06-18 14:50:42 +0900745 return false;
746
747 // Direct ingress TCP & UDP traffic to ARC interface for new connections.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900748 if (process_runner_->iptables(
749 "nat", {"-A", "PREROUTING", "-i", ifname, "-p", "tcp", "-j", "DNAT",
750 "--to-destination", ipv4_addr, "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900751 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
752 return false;
753 }
Garrick Evans8e8e3472020-01-23 14:03:50 +0900754 if (process_runner_->iptables(
755 "nat", {"-A", "PREROUTING", "-i", ifname, "-p", "udp", "-j", "DNAT",
756 "--to-destination", ipv4_addr, "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900757 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
758 return false;
759 }
760
761 return true;
762}
763
764void Datapath::RemoveInboundIPv4DNAT(const std::string& ifname,
765 const std::string& ipv4_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900766 process_runner_->iptables(
767 "nat", {"-D", "PREROUTING", "-i", ifname, "-p", "udp", "-j", "DNAT",
768 "--to-destination", ipv4_addr, "-w"});
769 process_runner_->iptables(
770 "nat", {"-D", "PREROUTING", "-i", ifname, "-p", "tcp", "-j", "DNAT",
771 "--to-destination", ipv4_addr, "-w"});
772 process_runner_->iptables(
773 "nat", {"-D", "PREROUTING", "-i", ifname, "-m", "socket", "--nowildcard",
774 "-j", "ACCEPT", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900775}
776
Hugo Benichie8758b52020-04-03 14:49:01 +0900777bool Datapath::AddOutboundIPv4SNATMark(const std::string& ifname) {
778 return process_runner_->iptables(
779 "mangle", {"-A", "PREROUTING", "-i", ifname, "-j", "MARK",
Hugo Benichi6c445322020-08-12 16:46:19 +0900780 "--set-mark", "1/1", "-w"}) == 0;
Hugo Benichie8758b52020-04-03 14:49:01 +0900781}
782
783void Datapath::RemoveOutboundIPv4SNATMark(const std::string& ifname) {
784 process_runner_->iptables("mangle", {"-D", "PREROUTING", "-i", ifname, "-j",
Hugo Benichi6c445322020-08-12 16:46:19 +0900785 "MARK", "--set-mark", "1/1", "-w"});
Hugo Benichie8758b52020-04-03 14:49:01 +0900786}
787
Garrick Evans664a82f2019-12-17 12:18:05 +0900788bool Datapath::MaskInterfaceFlags(const std::string& ifname,
789 uint16_t on,
790 uint16_t off) {
Taoyu Li90c13912019-11-26 17:56:54 +0900791 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
792 if (!sock.is_valid()) {
793 PLOG(ERROR) << "Failed to create control socket";
794 return false;
795 }
796 ifreq ifr;
797 snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname.c_str());
798 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) < 0) {
799 PLOG(WARNING) << "ioctl() failed to get interface flag on " << ifname;
800 return false;
801 }
Garrick Evans664a82f2019-12-17 12:18:05 +0900802 ifr.ifr_flags |= on;
803 ifr.ifr_flags &= ~off;
Taoyu Li90c13912019-11-26 17:56:54 +0900804 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) < 0) {
Garrick Evans664a82f2019-12-17 12:18:05 +0900805 PLOG(WARNING) << "ioctl() failed to set flag 0x" << std::hex << on
806 << " unset flag 0x" << std::hex << off << " on " << ifname;
Taoyu Li90c13912019-11-26 17:56:54 +0900807 return false;
808 }
809 return true;
810}
811
Garrick Evans260ff302019-07-25 11:22:50 +0900812bool Datapath::AddIPv6HostRoute(const std::string& ifname,
813 const std::string& ipv6_addr,
814 int ipv6_prefix_len) {
815 std::string ipv6_addr_cidr =
816 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
817
Garrick Evans8e8e3472020-01-23 14:03:50 +0900818 return process_runner_->ip6("route", "replace",
819 {ipv6_addr_cidr, "dev", ifname}) == 0;
Garrick Evans260ff302019-07-25 11:22:50 +0900820}
821
822void Datapath::RemoveIPv6HostRoute(const std::string& ifname,
823 const std::string& ipv6_addr,
824 int ipv6_prefix_len) {
825 std::string ipv6_addr_cidr =
826 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
827
Garrick Evans8e8e3472020-01-23 14:03:50 +0900828 process_runner_->ip6("route", "del", {ipv6_addr_cidr, "dev", ifname});
Garrick Evans260ff302019-07-25 11:22:50 +0900829}
830
Taoyu Lia0727dc2020-09-24 19:54:59 +0900831bool Datapath::AddIPv6Address(const std::string& ifname,
832 const std::string& ipv6_addr) {
833 return process_runner_->ip6("addr", "add", {ipv6_addr, "dev", ifname}) == 0;
Garrick Evans260ff302019-07-25 11:22:50 +0900834}
835
Taoyu Lia0727dc2020-09-24 19:54:59 +0900836void Datapath::RemoveIPv6Address(const std::string& ifname,
837 const std::string& ipv6_addr) {
838 process_runner_->ip6("addr", "del", {ipv6_addr, "dev", ifname});
Garrick Evans260ff302019-07-25 11:22:50 +0900839}
840
Hugo Benichi76be34a2020-08-26 22:35:54 +0900841void Datapath::StartConnectionPinning(const std::string& ext_ifname) {
Hugo Benichi155de002021-01-19 16:45:46 +0900842 int ifindex = FindIfIndex(ext_ifname);
843 if (ifindex != 0 && !ModifyIptables(IpFamily::Dual, "mangle",
844 {"-A", kCheckRoutingMarkChain, "-o",
845 ext_ifname, "-m", "mark", "!", "--mark",
846 Fwmark::FromIfIndex(ifindex).ToString() +
847 "/" + kFwmarkRoutingMask.ToString(),
848 "-j", "DROP", "-w"}))
849 LOG(ERROR) << "Could not set fwmark routing filter rule for " << ext_ifname;
850
Hugo Benichi1af52392020-11-27 18:09:32 +0900851 // Set in CONNMARK the routing tag associated with |ext_ifname|.
Hugo Benichi76be34a2020-08-26 22:35:54 +0900852 if (!ModifyConnmarkSetPostrouting(IpFamily::Dual, "-A", ext_ifname))
853 LOG(ERROR) << "Could not start connection pinning on " << ext_ifname;
Hugo Benichi1af52392020-11-27 18:09:32 +0900854 // Save in CONNMARK the source tag for egress traffic of this connection.
855 if (!ModifyConnmarkSave(IpFamily::Dual, "POSTROUTING", "-A", ext_ifname,
856 kFwmarkAllSourcesMask))
857 LOG(ERROR) << "Failed to add POSTROUTING CONNMARK rule for saving fwmark "
858 "source tag on "
859 << ext_ifname;
860 // Restore from CONNMARK the source tag for ingress traffic of this connection
861 // (returned traffic).
862 if (!ModifyConnmarkRestore(IpFamily::Dual, "PREROUTING", "-A", ext_ifname,
863 kFwmarkAllSourcesMask))
864 LOG(ERROR) << "Could not setup fwmark source tagging rule for return "
865 "traffic received on "
866 << ext_ifname;
Hugo Benichi76be34a2020-08-26 22:35:54 +0900867}
868
869void Datapath::StopConnectionPinning(const std::string& ext_ifname) {
Hugo Benichi155de002021-01-19 16:45:46 +0900870 int ifindex = FindIfIndex(ext_ifname);
871 if (ifindex != 0 && !ModifyIptables(IpFamily::Dual, "mangle",
872 {"-D", kCheckRoutingMarkChain, "-o",
873 ext_ifname, "-m", "mark", "!", "--mark",
874 Fwmark::FromIfIndex(ifindex).ToString() +
875 "/" + kFwmarkRoutingMask.ToString(),
876 "-j", "DROP", "-w"}))
877 LOG(ERROR) << "Could not remove fwmark routing filter rule for "
878 << ext_ifname;
879
Hugo Benichi76be34a2020-08-26 22:35:54 +0900880 if (!ModifyConnmarkSetPostrouting(IpFamily::Dual, "-D", ext_ifname))
881 LOG(ERROR) << "Could not stop connection pinning on " << ext_ifname;
Hugo Benichi1af52392020-11-27 18:09:32 +0900882 if (!ModifyConnmarkSave(IpFamily::Dual, "POSTROUTING", "-D", ext_ifname,
883 kFwmarkAllSourcesMask))
884 LOG(ERROR) << "Could not remove POSTROUTING CONNMARK rule for saving "
885 "fwmark source tag on "
886 << ext_ifname;
887 if (!ModifyConnmarkRestore(IpFamily::Dual, "PREROUTING", "-D", ext_ifname,
888 kFwmarkAllSourcesMask))
889 LOG(ERROR) << "Could not remove fwmark source tagging rule for return "
890 "traffic received on "
891 << ext_ifname;
Hugo Benichi76be34a2020-08-26 22:35:54 +0900892}
893
Hugo Benichi2a940542020-10-26 18:50:49 +0900894void Datapath::StartVpnRouting(const std::string& vpn_ifname) {
Hugo Benichi891275e2020-12-16 10:35:34 +0900895 if (process_runner_->iptables("nat", {"-A", "POSTROUTING", "-o", vpn_ifname,
896 "-j", "MASQUERADE", "-w"}) != 0)
897 LOG(ERROR) << "Could not set up SNAT for traffic outgoing " << vpn_ifname;
Hugo Benichi2a940542020-10-26 18:50:49 +0900898 StartConnectionPinning(vpn_ifname);
899 if (!ModifyFwmarkRoutingTag(kApplyVpnMarkChain, "-A", vpn_ifname, ""))
900 LOG(ERROR) << "Failed to set up VPN set-mark rule for " << vpn_ifname;
Hugo Benichibfc49112020-12-14 12:54:44 +0900901 if (vpn_ifname != kArcBridge)
902 StartRoutingDevice(vpn_ifname, kArcBridge, 0 /*no inbound DNAT */,
903 TrafficSource::ARC, true /* route_on_vpn */);
Hugo Benichi2a940542020-10-26 18:50:49 +0900904}
905
906void Datapath::StopVpnRouting(const std::string& vpn_ifname) {
Hugo Benichibfc49112020-12-14 12:54:44 +0900907 if (vpn_ifname != kArcBridge)
908 StopRoutingDevice(vpn_ifname, kArcBridge, 0 /* no inbound DNAT */,
909 TrafficSource::ARC, false /* route_on_vpn */);
Hugo Benichi2a940542020-10-26 18:50:49 +0900910 if (!ModifyFwmarkRoutingTag(kApplyVpnMarkChain, "-D", vpn_ifname, ""))
911 LOG(ERROR) << "Failed to remove VPN set-mark rule for " << vpn_ifname;
912 StopConnectionPinning(vpn_ifname);
Hugo Benichi891275e2020-12-16 10:35:34 +0900913 if (process_runner_->iptables("nat", {"-D", "POSTROUTING", "-o", vpn_ifname,
914 "-j", "MASQUERADE", "-w"}) != 0)
915 LOG(ERROR) << "Could not stop SNAT for traffic outgoing " << vpn_ifname;
Hugo Benichi2a940542020-10-26 18:50:49 +0900916}
917
Hugo Benichi76be34a2020-08-26 22:35:54 +0900918bool Datapath::ModifyConnmarkSetPostrouting(IpFamily family,
919 const std::string& op,
920 const std::string& oif) {
Hugo Benichi76be34a2020-08-26 22:35:54 +0900921 int ifindex = FindIfIndex(oif);
922 if (ifindex == 0) {
923 PLOG(ERROR) << "if_nametoindex(" << oif << ") failed";
924 return false;
925 }
926
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900927 return ModifyConnmarkSet(family, "POSTROUTING", op, oif,
928 Fwmark::FromIfIndex(ifindex), kFwmarkRoutingMask);
929}
930
931bool Datapath::ModifyConnmarkSet(IpFamily family,
932 const std::string& chain,
933 const std::string& op,
934 const std::string& oif,
935 Fwmark mark,
936 Fwmark mask) {
937 if (chain != kApplyVpnMarkChain && (chain != "POSTROUTING" || oif.empty())) {
938 LOG(ERROR) << "Invalid arguments chain=" << chain << " oif=" << oif;
939 return false;
940 }
941
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900942 std::vector<std::string> args = {op, chain};
943 if (!oif.empty()) {
944 args.push_back("-o");
945 args.push_back(oif);
946 }
947 args.push_back("-j");
948 args.push_back("CONNMARK");
949 args.push_back("--set-mark");
950 args.push_back(mark.ToString() + "/" + mask.ToString());
951 args.push_back("-w");
Hugo Benichi76be34a2020-08-26 22:35:54 +0900952
Hugo Benichi58f264a2020-10-16 18:16:05 +0900953 return ModifyIptables(family, "mangle", args);
Hugo Benichi76be34a2020-08-26 22:35:54 +0900954}
955
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900956bool Datapath::ModifyConnmarkRestore(IpFamily family,
957 const std::string& chain,
958 const std::string& op,
Hugo Benichi1af52392020-11-27 18:09:32 +0900959 const std::string& iif,
960 Fwmark mask) {
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900961 std::vector<std::string> args = {op, chain};
962 if (!iif.empty()) {
963 args.push_back("-i");
964 args.push_back(iif);
965 }
966 args.insert(args.end(), {"-j", "CONNMARK", "--restore-mark", "--mask",
Hugo Benichi1af52392020-11-27 18:09:32 +0900967 mask.ToString(), "-w"});
968 return ModifyIptables(family, "mangle", args);
969}
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900970
Hugo Benichi1af52392020-11-27 18:09:32 +0900971bool Datapath::ModifyConnmarkSave(IpFamily family,
972 const std::string& chain,
973 const std::string& op,
974 const std::string& oif,
975 Fwmark mask) {
976 std::vector<std::string> args = {op, chain};
977 if (!oif.empty()) {
978 args.push_back("-o");
979 args.push_back(oif);
980 }
981 args.insert(args.end(), {"-j", "CONNMARK", "--save-mark", "--mask",
982 mask.ToString(), "-w"});
Hugo Benichi58f264a2020-10-16 18:16:05 +0900983 return ModifyIptables(family, "mangle", args);
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900984}
985
Hugo Benichi2a940542020-10-26 18:50:49 +0900986bool Datapath::ModifyFwmarkRoutingTag(const std::string& chain,
987 const std::string& op,
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900988 const std::string& ext_ifname,
989 const std::string& int_ifname) {
990 int ifindex = FindIfIndex(ext_ifname);
991 if (ifindex == 0) {
992 PLOG(ERROR) << "if_nametoindex(" << ext_ifname << ") failed";
993 return false;
994 }
995
Hugo Benichi2a940542020-10-26 18:50:49 +0900996 return ModifyFwmark(IpFamily::Dual, chain, op, int_ifname, "" /*uid_name*/,
Hugo Benichi7e3b1fc2020-11-19 15:47:05 +0900997 0 /*classid*/, Fwmark::FromIfIndex(ifindex),
998 kFwmarkRoutingMask);
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900999}
1000
Hugo Benichi9be19b12020-08-14 15:33:40 +09001001bool Datapath::ModifyFwmarkSourceTag(const std::string& op,
1002 const std::string& iif,
1003 TrafficSource source) {
Hugo Benichi3a9162b2020-09-09 15:47:40 +09001004 return ModifyFwmark(IpFamily::Dual, "PREROUTING", op, iif, "" /*uid_name*/,
Hugo Benichi7e3b1fc2020-11-19 15:47:05 +09001005 0 /*classid*/, Fwmark::FromSource(source),
1006 kFwmarkAllSourcesMask);
Hugo Benichi9be19b12020-08-14 15:33:40 +09001007}
1008
Hugo Benichi3a9162b2020-09-09 15:47:40 +09001009bool Datapath::ModifyFwmarkDefaultLocalSourceTag(const std::string& op,
1010 TrafficSource source) {
1011 std::vector<std::string> args = {"-A",
1012 kApplyLocalSourceMarkChain,
1013 "-m",
1014 "mark",
1015 "--mark",
1016 "0x0/" + kFwmarkAllSourcesMask.ToString(),
1017 "-j",
1018 "MARK",
1019 "--set-mark",
1020 Fwmark::FromSource(source).ToString() + "/" +
1021 kFwmarkAllSourcesMask.ToString(),
1022 "-w"};
1023 return ModifyIptables(IpFamily::Dual, "mangle", args);
1024}
Hugo Benichi9be19b12020-08-14 15:33:40 +09001025
Hugo Benichi3a9162b2020-09-09 15:47:40 +09001026bool Datapath::ModifyFwmarkLocalSourceTag(const std::string& op,
1027 const LocalSourceSpecs& source) {
Hugo Benichi7e3b1fc2020-11-19 15:47:05 +09001028 if (std::string(source.uid_name).empty() && source.classid == 0)
1029 return false;
1030
Hugo Benichi3a9162b2020-09-09 15:47:40 +09001031 Fwmark mark = Fwmark::FromSource(source.source_type);
1032 if (source.is_on_vpn)
1033 mark = mark | kFwmarkRouteOnVpn;
1034
Hugo Benichi7e3b1fc2020-11-19 15:47:05 +09001035 return ModifyFwmark(IpFamily::Dual, kApplyLocalSourceMarkChain, op,
1036 "" /*iif*/, source.uid_name, source.classid, mark,
1037 kFwmarkPolicyMask);
Hugo Benichi3a9162b2020-09-09 15:47:40 +09001038}
1039
1040bool Datapath::ModifyFwmark(IpFamily family,
1041 const std::string& chain,
1042 const std::string& op,
1043 const std::string& iif,
1044 const std::string& uid_name,
Hugo Benichi7e3b1fc2020-11-19 15:47:05 +09001045 uint32_t classid,
Hugo Benichi3a9162b2020-09-09 15:47:40 +09001046 Fwmark mark,
1047 Fwmark mask,
1048 bool log_failures) {
Hugo Benichi3a9162b2020-09-09 15:47:40 +09001049 std::vector<std::string> args = {op, chain};
1050 if (!iif.empty()) {
1051 args.push_back("-i");
1052 args.push_back(iif);
1053 }
1054 if (!uid_name.empty()) {
1055 args.push_back("-m");
1056 args.push_back("owner");
1057 args.push_back("--uid-owner");
1058 args.push_back(uid_name);
1059 }
Hugo Benichi7e3b1fc2020-11-19 15:47:05 +09001060 if (classid != 0) {
1061 args.push_back("-m");
1062 args.push_back("cgroup");
1063 args.push_back("--cgroup");
1064 args.push_back(base::StringPrintf("0x%08x", classid));
1065 }
Hugo Benichi3a9162b2020-09-09 15:47:40 +09001066 args.push_back("-j");
1067 args.push_back("MARK");
1068 args.push_back("--set-mark");
1069 args.push_back(mark.ToString() + "/" + mask.ToString());
1070 args.push_back("-w");
Hugo Benichi9be19b12020-08-14 15:33:40 +09001071
Hugo Benichi58f264a2020-10-16 18:16:05 +09001072 return ModifyIptables(family, "mangle", args, log_failures);
Hugo Benichi9be19b12020-08-14 15:33:40 +09001073}
1074
Hugo Benichid82d8832020-08-14 10:05:03 +09001075bool Datapath::ModifyIpForwarding(IpFamily family,
1076 const std::string& op,
1077 const std::string& iif,
1078 const std::string& oif,
1079 bool log_failures) {
1080 if (iif.empty() && oif.empty()) {
1081 LOG(ERROR) << "Cannot change IP forwarding with no input or output "
1082 "interface specified";
Garrick Evans260ff302019-07-25 11:22:50 +09001083 return false;
1084 }
1085
Hugo Benichid82d8832020-08-14 10:05:03 +09001086 std::vector<std::string> args = {op, "FORWARD"};
1087 if (!iif.empty()) {
1088 args.push_back("-i");
1089 args.push_back(iif);
1090 }
1091 if (!oif.empty()) {
1092 args.push_back("-o");
1093 args.push_back(oif);
1094 }
1095 args.push_back("-j");
1096 args.push_back("ACCEPT");
1097 args.push_back("-w");
1098
Hugo Benichi58f264a2020-10-16 18:16:05 +09001099 return ModifyIptables(family, "filter", args, log_failures);
Hugo Benichid82d8832020-08-14 10:05:03 +09001100}
1101
Hugo Benichi3ef370b2020-11-16 19:07:17 +09001102bool Datapath::ModifyFwmarkVpnJumpRule(const std::string& chain,
1103 const std::string& op,
1104 const std::string& iif,
1105 Fwmark mark,
1106 Fwmark mask) {
1107 std::vector<std::string> args = {op, chain};
1108 if (!iif.empty()) {
1109 args.push_back("-i");
1110 args.push_back(iif);
1111 }
1112 if (mark.Value() != 0 && mask.Value() != 0) {
1113 args.push_back("-m");
1114 args.push_back("mark");
1115 args.push_back("--mark");
1116 args.push_back(mark.ToString() + "/" + mask.ToString());
1117 }
1118 args.insert(args.end(), {"-j", kApplyVpnMarkChain, "-w"});
1119 return ModifyIptables(IpFamily::Dual, "mangle", args);
1120}
1121
1122bool Datapath::ModifyChain(IpFamily family,
1123 const std::string& table,
1124 const std::string& op,
Hugo Benichi58f264a2020-10-16 18:16:05 +09001125 const std::string& chain,
1126 bool log_failures) {
1127 return ModifyIptables(family, table, {op, chain, "-w"}, log_failures);
Hugo Benichi3ef370b2020-11-16 19:07:17 +09001128}
1129
1130bool Datapath::ModifyIptables(IpFamily family,
1131 const std::string& table,
Hugo Benichi58f264a2020-10-16 18:16:05 +09001132 const std::vector<std::string>& argv,
1133 bool log_failures) {
1134 switch (family) {
1135 case IPv4:
1136 case IPv6:
1137 case Dual:
1138 break;
1139 default:
1140 LOG(ERROR) << "Could not execute iptables command " << table
1141 << base::JoinString(argv, " ") << ": incorrect IP family "
1142 << family;
1143 return false;
Hugo Benichi3ef370b2020-11-16 19:07:17 +09001144 }
1145
1146 bool success = true;
1147 if (family & IpFamily::IPv4)
Hugo Benichi58f264a2020-10-16 18:16:05 +09001148 success &= process_runner_->iptables(table, argv, log_failures) == 0;
Hugo Benichi3ef370b2020-11-16 19:07:17 +09001149 if (family & IpFamily::IPv6)
Hugo Benichi58f264a2020-10-16 18:16:05 +09001150 success &= process_runner_->ip6tables(table, argv, log_failures) == 0;
Hugo Benichi3ef370b2020-11-16 19:07:17 +09001151 return success;
1152}
1153
Hugo Benichid82d8832020-08-14 10:05:03 +09001154bool Datapath::StartIpForwarding(IpFamily family,
1155 const std::string& iif,
1156 const std::string& oif) {
1157 return ModifyIpForwarding(family, "-A", iif, oif);
1158}
1159
1160bool Datapath::StopIpForwarding(IpFamily family,
1161 const std::string& iif,
1162 const std::string& oif) {
1163 return ModifyIpForwarding(family, "-D", iif, oif);
1164}
1165
1166bool Datapath::AddIPv6Forwarding(const std::string& ifname1,
1167 const std::string& ifname2) {
1168 // Only start Ipv6 forwarding if -C returns false and it had not been
1169 // started yet.
1170 if (!ModifyIpForwarding(IpFamily::IPv6, "-C", ifname1, ifname2,
1171 false /*log_failures*/) &&
1172 !StartIpForwarding(IpFamily::IPv6, ifname1, ifname2)) {
1173 return false;
1174 }
1175
1176 if (!ModifyIpForwarding(IpFamily::IPv6, "-C", ifname2, ifname1,
1177 false /*log_failures*/) &&
1178 !StartIpForwarding(IpFamily::IPv6, ifname2, ifname1)) {
Garrick Evans260ff302019-07-25 11:22:50 +09001179 RemoveIPv6Forwarding(ifname1, ifname2);
1180 return false;
1181 }
1182
1183 return true;
1184}
1185
1186void Datapath::RemoveIPv6Forwarding(const std::string& ifname1,
1187 const std::string& ifname2) {
Hugo Benichid82d8832020-08-14 10:05:03 +09001188 StopIpForwarding(IpFamily::IPv6, ifname1, ifname2);
1189 StopIpForwarding(IpFamily::IPv6, ifname2, ifname1);
Garrick Evans260ff302019-07-25 11:22:50 +09001190}
1191
Garrick Evans3d97a392020-02-21 15:24:37 +09001192bool Datapath::AddIPv4Route(uint32_t gateway_addr,
1193 uint32_t addr,
1194 uint32_t netmask) {
1195 struct rtentry route;
1196 memset(&route, 0, sizeof(route));
Hugo Benichie8758b52020-04-03 14:49:01 +09001197 SetSockaddrIn(&route.rt_gateway, gateway_addr);
1198 SetSockaddrIn(&route.rt_dst, addr & netmask);
1199 SetSockaddrIn(&route.rt_genmask, netmask);
Garrick Evans3d97a392020-02-21 15:24:37 +09001200 route.rt_flags = RTF_UP | RTF_GATEWAY;
Hugo Benichie8758b52020-04-03 14:49:01 +09001201 return ModifyRtentry(SIOCADDRT, &route);
1202}
Garrick Evans3d97a392020-02-21 15:24:37 +09001203
Hugo Benichie8758b52020-04-03 14:49:01 +09001204bool Datapath::DeleteIPv4Route(uint32_t gateway_addr,
1205 uint32_t addr,
1206 uint32_t netmask) {
1207 struct rtentry route;
1208 memset(&route, 0, sizeof(route));
1209 SetSockaddrIn(&route.rt_gateway, gateway_addr);
1210 SetSockaddrIn(&route.rt_dst, addr & netmask);
1211 SetSockaddrIn(&route.rt_genmask, netmask);
1212 route.rt_flags = RTF_UP | RTF_GATEWAY;
1213 return ModifyRtentry(SIOCDELRT, &route);
1214}
1215
1216bool Datapath::AddIPv4Route(const std::string& ifname,
1217 uint32_t addr,
1218 uint32_t netmask) {
1219 struct rtentry route;
1220 memset(&route, 0, sizeof(route));
1221 SetSockaddrIn(&route.rt_dst, addr & netmask);
1222 SetSockaddrIn(&route.rt_genmask, netmask);
1223 char rt_dev[IFNAMSIZ];
1224 strncpy(rt_dev, ifname.c_str(), IFNAMSIZ);
1225 rt_dev[IFNAMSIZ - 1] = '\0';
1226 route.rt_dev = rt_dev;
1227 route.rt_flags = RTF_UP | RTF_GATEWAY;
1228 return ModifyRtentry(SIOCADDRT, &route);
1229}
1230
1231bool Datapath::DeleteIPv4Route(const std::string& ifname,
1232 uint32_t addr,
1233 uint32_t netmask) {
1234 struct rtentry route;
1235 memset(&route, 0, sizeof(route));
1236 SetSockaddrIn(&route.rt_dst, addr & netmask);
1237 SetSockaddrIn(&route.rt_genmask, netmask);
1238 char rt_dev[IFNAMSIZ];
1239 strncpy(rt_dev, ifname.c_str(), IFNAMSIZ);
1240 rt_dev[IFNAMSIZ - 1] = '\0';
1241 route.rt_dev = rt_dev;
1242 route.rt_flags = RTF_UP | RTF_GATEWAY;
1243 return ModifyRtentry(SIOCDELRT, &route);
1244}
1245
Taoyu Lia0727dc2020-09-24 19:54:59 +09001246bool Datapath::ModifyRtentry(ioctl_req_t op, struct rtentry* route) {
Hugo Benichie8758b52020-04-03 14:49:01 +09001247 DCHECK(route);
1248 if (op != SIOCADDRT && op != SIOCDELRT) {
Andreea Costinas34aa7a92020-08-04 10:36:10 +02001249 LOG(ERROR) << "Invalid operation " << op << " for rtentry " << *route;
Garrick Evans3d97a392020-02-21 15:24:37 +09001250 return false;
1251 }
Hugo Benichie8758b52020-04-03 14:49:01 +09001252 base::ScopedFD fd(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
1253 if (!fd.is_valid()) {
Andreea Costinas34aa7a92020-08-04 10:36:10 +02001254 PLOG(ERROR) << "Failed to create socket for adding rtentry " << *route;
Hugo Benichie8758b52020-04-03 14:49:01 +09001255 return false;
1256 }
1257 if (HANDLE_EINTR(ioctl_(fd.get(), op, route)) != 0) {
1258 std::string opname = op == SIOCADDRT ? "add" : "delete";
Andreea Costinas34aa7a92020-08-04 10:36:10 +02001259 PLOG(ERROR) << "Failed to " << opname << " rtentry " << *route;
Garrick Evans3d97a392020-02-21 15:24:37 +09001260 return false;
1261 }
1262 return true;
1263}
1264
Jason Jeremy Imana7273a32020-08-04 11:25:31 +09001265bool Datapath::AddAdbPortForwardRule(const std::string& ifname) {
1266 return firewall_->AddIpv4ForwardRule(patchpanel::ModifyPortRuleRequest::TCP,
1267 kArcAddr, kAdbServerPort, ifname,
1268 kLocalhostAddr, kAdbProxyTcpListenPort);
1269}
1270
1271void Datapath::DeleteAdbPortForwardRule(const std::string& ifname) {
1272 firewall_->DeleteIpv4ForwardRule(patchpanel::ModifyPortRuleRequest::TCP,
1273 kArcAddr, kAdbServerPort, ifname,
1274 kLocalhostAddr, kAdbProxyTcpListenPort);
1275}
1276
1277bool Datapath::AddAdbPortAccessRule(const std::string& ifname) {
1278 return firewall_->AddAcceptRules(patchpanel::ModifyPortRuleRequest::TCP,
1279 kAdbProxyTcpListenPort, ifname);
1280}
1281
1282void Datapath::DeleteAdbPortAccessRule(const std::string& ifname) {
1283 firewall_->DeleteAcceptRules(patchpanel::ModifyPortRuleRequest::TCP,
1284 kAdbProxyTcpListenPort, ifname);
1285}
1286
Hugo Benichiaf9d8a72020-08-26 13:28:13 +09001287void Datapath::SetIfnameIndex(const std::string& ifname, int ifindex) {
1288 if_nametoindex_[ifname] = ifindex;
1289}
1290
1291int Datapath::FindIfIndex(const std::string& ifname) {
1292 uint32_t ifindex = if_nametoindex(ifname.c_str());
1293 if (ifindex > 0) {
1294 if_nametoindex_[ifname] = ifindex;
1295 return ifindex;
1296 }
1297
1298 const auto it = if_nametoindex_.find(ifname);
1299 if (it != if_nametoindex_.end())
1300 return it->second;
1301
1302 return 0;
1303}
1304
Hugo Benichifcf81022020-12-04 11:01:37 +09001305std::ostream& operator<<(std::ostream& stream,
1306 const ConnectedNamespace& nsinfo) {
Hugo Benichi93306e52020-12-04 16:08:00 +09001307 stream << "{ pid: " << nsinfo.pid
1308 << ", source: " << TrafficSourceName(nsinfo.source);
Hugo Benichifcf81022020-12-04 11:01:37 +09001309 if (!nsinfo.outbound_ifname.empty()) {
1310 stream << ", outbound_ifname: " << nsinfo.outbound_ifname;
1311 }
Hugo Benichi93306e52020-12-04 16:08:00 +09001312 stream << ", route_on_vpn: " << nsinfo.route_on_vpn
1313 << ", host_ifname: " << nsinfo.host_ifname
Hugo Benichifcf81022020-12-04 11:01:37 +09001314 << ", peer_ifname: " << nsinfo.peer_ifname
1315 << ", peer_subnet: " << nsinfo.peer_subnet->ToCidrString() << '}';
1316 return stream;
1317}
1318
Garrick Evans3388a032020-03-24 11:25:55 +09001319} // namespace patchpanel