blob: f4ed97040c71d1a891935a515866a31b9302b825 [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>
Hugo Benichi58f264a2020-10-16 18:16:05 +090023#include <base/strings/string_util.h>
Garrick Evans54861622019-07-19 09:05:09 +090024#include <base/strings/string_number_conversions.h>
Garrick Evans4f9f5572019-11-26 10:25:16 +090025#include <brillo/userdb_utils.h>
Garrick Evans54861622019-07-19 09:05:09 +090026
Jason Jeremy Imana7273a32020-08-04 11:25:31 +090027#include "patchpanel/adb_proxy.h"
Garrick Evans3388a032020-03-24 11:25:55 +090028#include "patchpanel/net_util.h"
29#include "patchpanel/scoped_ns.h"
Garrick Evansc7ae82c2019-09-04 16:25:10 +090030
Garrick Evans3388a032020-03-24 11:25:55 +090031namespace patchpanel {
Garrick Evans54861622019-07-19 09:05:09 +090032
Garrick Evansc7ae82c2019-09-04 16:25:10 +090033namespace {
Hugo Benichi76675592020-04-08 14:29:57 +090034// TODO(hugobenichi) Consolidate this constant definition in a single place.
35constexpr pid_t kTestPID = -2;
Garrick Evansc7ae82c2019-09-04 16:25:10 +090036constexpr char kDefaultIfname[] = "vmtap%d";
37constexpr char kTunDev[] = "/dev/net/tun";
Jason Jeremy Imana7273a32020-08-04 11:25:31 +090038constexpr char kArcAddr[] = "100.115.92.2";
39constexpr char kLocalhostAddr[] = "127.0.0.1";
40constexpr uint16_t kAdbServerPort = 5555;
Hugo Benichie8758b52020-04-03 14:49:01 +090041
Hugo Benichibf811c62020-09-07 17:30:45 +090042// Constants used for dropping locally originated traffic bound to an incorrect
43// source IPv4 address.
44constexpr char kGuestIPv4Subnet[] = "100.115.92.0/23";
45constexpr std::array<const char*, 6> kPhysicalIfnamePrefixes{
46 {"eth+", "wlan+", "mlan+", "usb+", "wwan+", "rmnet+"}};
47
Hugo Benichi3a9162b2020-09-09 15:47:40 +090048constexpr char kApplyLocalSourceMarkChain[] = "apply_local_source_mark";
Hugo Benichi3ef370b2020-11-16 19:07:17 +090049constexpr char kApplyVpnMarkChain[] = "apply_vpn_mark";
50
Hugo Benichi2a940542020-10-26 18:50:49 +090051// Constant fwmark mask for matching local socket traffic that should be routed
52// through a VPN connection. The traffic must not be part of an existing
53// connection and must match exactly the VPN routing intent policy bit.
54const Fwmark kFwmarkVpnMatchingMask = kFwmarkRoutingMask | kFwmarkVpnMask;
55
Garrick Evans8a067562020-05-11 12:47:30 +090056std::string PrefixIfname(const std::string& prefix, const std::string& ifname) {
57 std::string n = prefix + ifname;
Garrick Evans2f581a02020-05-11 10:43:35 +090058 if (n.length() < IFNAMSIZ)
59 return n;
Garrick Evans54861622019-07-19 09:05:09 +090060
Garrick Evans2f581a02020-05-11 10:43:35 +090061 // Best effort attempt to preserve the interface number, assuming it's the
62 // last char in the name.
63 auto c = ifname[ifname.length() - 1];
64 n.resize(IFNAMSIZ - 1);
65 n[n.length() - 1] = c;
66 return n;
Garrick Evans54861622019-07-19 09:05:09 +090067}
Garrick Evansf0ab7132019-06-18 14:50:42 +090068
Garrick Evans8a067562020-05-11 12:47:30 +090069} // namespace
70
71std::string ArcVethHostName(const std::string& ifname) {
72 return PrefixIfname("veth", ifname);
73}
74
75std::string ArcBridgeName(const std::string& ifname) {
76 return PrefixIfname("arc_", ifname);
77}
78
Jason Jeremy Imana7273a32020-08-04 11:25:31 +090079Datapath::Datapath(MinijailedProcessRunner* process_runner, Firewall* firewall)
80 : Datapath(process_runner, firewall, ioctl) {}
Garrick Evansc7ae82c2019-09-04 16:25:10 +090081
Jason Jeremy Imana7273a32020-08-04 11:25:31 +090082Datapath::Datapath(MinijailedProcessRunner* process_runner,
83 Firewall* firewall,
84 ioctl_t ioctl_hook)
85 : process_runner_(process_runner), firewall_(firewall), ioctl_(ioctl_hook) {
Garrick Evansf0ab7132019-06-18 14:50:42 +090086 CHECK(process_runner_);
87}
88
Garrick Evans260ff302019-07-25 11:22:50 +090089MinijailedProcessRunner& Datapath::runner() const {
90 return *process_runner_;
91}
92
Hugo Benichibf811c62020-09-07 17:30:45 +090093void Datapath::Start() {
94 // Enable IPv4 packet forwarding
95 if (process_runner_->sysctl_w("net.ipv4.ip_forward", "1") != 0)
96 LOG(ERROR) << "Failed to update net.ipv4.ip_forward."
97 << " Guest connectivity will not work correctly.";
98
99 // Limit local port range: Android owns 47104-61000.
100 // TODO(garrick): The original history behind this tweak is gone. Some
101 // investigation is needed to see if it is still applicable.
102 if (process_runner_->sysctl_w("net.ipv4.ip_local_port_range",
103 "32768 47103") != 0)
104 LOG(ERROR) << "Failed to limit local port range. Some Android features or"
105 << " apps may not work correctly.";
106
107 // Enable IPv6 packet forwarding
108 if (process_runner_->sysctl_w("net.ipv6.conf.all.forwarding", "1") != 0)
109 LOG(ERROR) << "Failed to update net.ipv6.conf.all.forwarding."
110 << " IPv6 functionality may be broken.";
111
112 if (!AddSNATMarkRules())
113 LOG(ERROR) << "Failed to install SNAT mark rules."
114 << " Guest connectivity may be broken.";
115
Hugo Benichi58125d32020-09-09 11:25:45 +0900116 // Create a FORWARD ACCEPT rule for connections already established.
117 if (process_runner_->iptables(
118 "filter", {"-A", "FORWARD", "-m", "state", "--state",
119 "ESTABLISHED,RELATED", "-j", "ACCEPT", "-w"}) != 0)
Hugo Benichibf811c62020-09-07 17:30:45 +0900120 LOG(ERROR) << "Failed to install forwarding rule for established"
121 << " connections.";
122
123 // chromium:898210: Drop any locally originated traffic that would exit a
124 // physical interface with a source IPv4 address from the subnet of IPs used
125 // for VMs, containers, and connected namespaces This is needed to prevent
126 // packets leaking with an incorrect src IP when a local process binds to the
127 // wrong interface.
128 for (const auto& oif : kPhysicalIfnamePrefixes) {
129 if (!AddSourceIPv4DropRule(oif, kGuestIPv4Subnet))
130 LOG(WARNING) << "Failed to set up IPv4 drop rule for src ip "
131 << kGuestIPv4Subnet << " exiting " << oif;
132 }
133
134 if (!AddOutboundIPv4SNATMark("vmtap+"))
135 LOG(ERROR) << "Failed to set up NAT for TAP devices."
136 << " Guest connectivity may be broken.";
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900137
Hugo Benichi2a940542020-10-26 18:50:49 +0900138 // Applies the routing tag saved in conntrack for any established connection
139 // for sockets created in the host network namespace.
Hugo Benichi1af52392020-11-27 18:09:32 +0900140 if (!ModifyConnmarkRestore(IpFamily::Dual, "OUTPUT", "-A", "" /*iif*/,
141 kFwmarkRoutingMask))
Hugo Benichi2a940542020-10-26 18:50:49 +0900142 LOG(ERROR) << "Failed to add OUTPUT CONNMARK restore rule";
143
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900144 // Set up a mangle chain used in OUTPUT for applying the fwmark TrafficSource
145 // tag and tagging the local traffic that should be routed through a VPN.
146 if (!ModifyChain(IpFamily::Dual, "mangle", "-N", kApplyLocalSourceMarkChain))
147 LOG(ERROR) << "Failed to set up " << kApplyLocalSourceMarkChain
148 << " mangle chain";
149 // Ensure that the chain is empty if patchpanel is restarting after a crash.
150 if (!ModifyChain(IpFamily::Dual, "mangle", "-F", kApplyLocalSourceMarkChain))
151 LOG(ERROR) << "Failed to flush " << kApplyLocalSourceMarkChain
152 << " mangle chain";
153 if (!ModifyIptables(IpFamily::Dual, "mangle",
154 {"-A", "OUTPUT", "-j", kApplyLocalSourceMarkChain, "-w"}))
155 LOG(ERROR) << "Failed to attach " << kApplyLocalSourceMarkChain
156 << " to mangle OUTPUT";
157 // Create rules for tagging local sources with the source tag and the vpn
158 // policy tag.
159 for (const auto& source : kLocalSourceTypes) {
Hugo Benichi620202f2020-11-27 10:14:38 +0900160 if (!ModifyFwmarkLocalSourceTag("-A", source))
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900161 LOG(ERROR) << "Failed to create fwmark tagging rule for uid " << source
162 << " in " << kApplyLocalSourceMarkChain;
163 }
164 // Finally add a catch-all rule for tagging any remaining local sources with
165 // the SYSTEM source tag
166 if (!ModifyFwmarkDefaultLocalSourceTag("-A", TrafficSource::SYSTEM))
167 LOG(ERROR) << "Failed to set up rule tagging traffic with default source";
168
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900169 // Sets up a mangle chain used in OUTPUT and PREROUTING for tagging "user"
170 // traffic that should be routed through a VPN.
171 if (!ModifyChain(IpFamily::Dual, "mangle", "-N", kApplyVpnMarkChain))
172 LOG(ERROR) << "Failed to set up " << kApplyVpnMarkChain << " mangle chain";
173 // Ensure that the chain is empty if patchpanel is restarting after a crash.
174 if (!ModifyChain(IpFamily::Dual, "mangle", "-F", kApplyVpnMarkChain))
175 LOG(ERROR) << "Failed to flush " << kApplyVpnMarkChain << " mangle chain";
176 // All local outgoing traffic eligible to VPN routing should traverse the VPN
177 // marking chain.
178 if (!ModifyFwmarkVpnJumpRule("OUTPUT", "-A", "" /*iif*/, kFwmarkRouteOnVpn,
179 kFwmarkVpnMask))
180 LOG(ERROR) << "Failed to add jump rule to VPN chain in mangle OUTPUT chain";
181 // Any traffic that already has a routing tag applied is accepted.
182 if (!ModifyIptables(
183 IpFamily::Dual, "mangle",
184 {"-A", kApplyVpnMarkChain, "-m", "mark", "!", "--mark",
185 "0x0/" + kFwmarkRoutingMask.ToString(), "-j", "ACCEPT", "-w"}))
186 LOG(ERROR) << "Failed to add ACCEPT rule to VPN tagging chain for marked "
187 "connections";
Hugo Benichibf811c62020-09-07 17:30:45 +0900188}
189
190void Datapath::Stop() {
191 RemoveOutboundIPv4SNATMark("vmtap+");
Hugo Benichi58125d32020-09-09 11:25:45 +0900192 process_runner_->iptables("filter",
193 {"-D", "FORWARD", "-m", "state", "--state",
194 "ESTABLISHED,RELATED", "-j", "ACCEPT", "-w"});
Hugo Benichibf811c62020-09-07 17:30:45 +0900195 RemoveSNATMarkRules();
196 for (const auto& oif : kPhysicalIfnamePrefixes)
197 RemoveSourceIPv4DropRule(oif, kGuestIPv4Subnet);
198
199 // Restore original local port range.
200 // TODO(garrick): The original history behind this tweak is gone. Some
201 // investigation is needed to see if it is still applicable.
202 if (process_runner_->sysctl_w("net.ipv4.ip_local_port_range",
203 "32768 61000") != 0)
204 LOG(ERROR) << "Failed to restore local port range";
205
206 // Disable packet forwarding
207 if (process_runner_->sysctl_w("net.ipv6.conf.all.forwarding", "0") != 0)
208 LOG(ERROR) << "Failed to restore net.ipv6.conf.all.forwarding.";
209
210 if (process_runner_->sysctl_w("net.ipv4.ip_forward", "0") != 0)
211 LOG(ERROR) << "Failed to restore net.ipv4.ip_forward.";
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900212
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900213 // Detach the VPN marking mangle chain
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900214 if (!ModifyFwmarkVpnJumpRule("OUTPUT", "-D", "" /*iif*/, kFwmarkRouteOnVpn,
215 kFwmarkVpnMask))
216 LOG(ERROR)
217 << "Failed to remove from mangle OUTPUT chain jump rule to VPN chain";
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900218
219 // Detach apply_local_source_mark from mangle PREROUTING
220 if (!ModifyIptables(IpFamily::Dual, "mangle",
221 {"-D", "OUTPUT", "-j", kApplyLocalSourceMarkChain, "-w"}))
222 LOG(ERROR) << "Failed to detach " << kApplyLocalSourceMarkChain
223 << " from mangle OUTPUT";
224
Hugo Benichi2a940542020-10-26 18:50:49 +0900225 // Stops applying routing tags saved in conntrack for sockets created in the
226 // host network namespace.
Hugo Benichi1af52392020-11-27 18:09:32 +0900227 if (!ModifyConnmarkRestore(IpFamily::Dual, "OUTPUT", "-D", "" /*iif*/,
228 kFwmarkRoutingMask))
Hugo Benichi2a940542020-10-26 18:50:49 +0900229 LOG(ERROR) << "Failed to remove OUTPUT CONNMARK restore rule";
230
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900231 // Delete the mangle chains
232 for (const auto* chain : {kApplyLocalSourceMarkChain, kApplyVpnMarkChain}) {
233 if (!ModifyChain(IpFamily::Dual, "mangle", "-F", chain))
234 LOG(ERROR) << "Failed to flush " << chain << " mangle chain";
235
236 if (!ModifyChain(IpFamily::Dual, "mangle", "-X", chain))
237 LOG(ERROR) << "Failed to delete " << chain << " mangle chain";
238 }
Hugo Benichibf811c62020-09-07 17:30:45 +0900239}
240
Hugo Benichi33860d72020-07-09 16:34:01 +0900241bool Datapath::NetnsAttachName(const std::string& netns_name, pid_t netns_pid) {
242 // Try first to delete any netns with name |netns_name| in case patchpanel
243 // did not exit cleanly.
244 if (process_runner_->ip_netns_delete(netns_name, false /*log_failures*/) == 0)
245 LOG(INFO) << "Deleted left over network namespace name " << netns_name;
246 return process_runner_->ip_netns_attach(netns_name, netns_pid) == 0;
247}
248
249bool Datapath::NetnsDeleteName(const std::string& netns_name) {
250 return process_runner_->ip_netns_delete(netns_name) == 0;
251}
252
Garrick Evans8a949dc2019-07-18 16:17:53 +0900253bool Datapath::AddBridge(const std::string& ifname,
Garrick Evans7a1a9ee2020-01-28 11:03:57 +0900254 uint32_t ipv4_addr,
255 uint32_t ipv4_prefix_len) {
Garrick Evans8a949dc2019-07-18 16:17:53 +0900256 // Configure the persistent Chrome OS bridge interface with static IP.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900257 if (process_runner_->brctl("addbr", {ifname}) != 0) {
Garrick Evans8a949dc2019-07-18 16:17:53 +0900258 return false;
259 }
260
Garrick Evans6f4fa3a2020-02-10 16:15:09 +0900261 if (process_runner_->ip(
262 "addr", "add",
263 {IPv4AddressToCidrString(ipv4_addr, ipv4_prefix_len), "brd",
264 IPv4AddressToString(Ipv4BroadcastAddr(ipv4_addr, ipv4_prefix_len)),
265 "dev", ifname}) != 0) {
Garrick Evans7a1a9ee2020-01-28 11:03:57 +0900266 RemoveBridge(ifname);
267 return false;
268 }
269
270 if (process_runner_->ip("link", "set", {ifname, "up"}) != 0) {
Garrick Evans8a949dc2019-07-18 16:17:53 +0900271 RemoveBridge(ifname);
272 return false;
273 }
274
275 // See nat.conf in chromeos-nat-init for the rest of the NAT setup rules.
Hugo Benichie8758b52020-04-03 14:49:01 +0900276 if (!AddOutboundIPv4SNATMark(ifname)) {
Garrick Evans8a949dc2019-07-18 16:17:53 +0900277 RemoveBridge(ifname);
278 return false;
279 }
280
281 return true;
282}
283
284void Datapath::RemoveBridge(const std::string& ifname) {
Hugo Benichie8758b52020-04-03 14:49:01 +0900285 RemoveOutboundIPv4SNATMark(ifname);
Garrick Evans7a1a9ee2020-01-28 11:03:57 +0900286 process_runner_->ip("link", "set", {ifname, "down"});
Garrick Evans8e8e3472020-01-23 14:03:50 +0900287 process_runner_->brctl("delbr", {ifname});
Garrick Evans8a949dc2019-07-18 16:17:53 +0900288}
289
Garrick Evans621ed262019-11-13 12:28:43 +0900290bool Datapath::AddToBridge(const std::string& br_ifname,
291 const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900292 return (process_runner_->brctl("addif", {br_ifname, ifname}) == 0);
Garrick Evans621ed262019-11-13 12:28:43 +0900293}
294
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900295std::string Datapath::AddTAP(const std::string& name,
Garrick Evans621ed262019-11-13 12:28:43 +0900296 const MacAddress* mac_addr,
297 const SubnetAddress* ipv4_addr,
Garrick Evans4f9f5572019-11-26 10:25:16 +0900298 const std::string& user) {
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900299 base::ScopedFD dev(open(kTunDev, O_RDWR | O_NONBLOCK));
300 if (!dev.is_valid()) {
301 PLOG(ERROR) << "Failed to open " << kTunDev;
302 return "";
303 }
304
305 struct ifreq ifr;
306 memset(&ifr, 0, sizeof(ifr));
307 strncpy(ifr.ifr_name, name.empty() ? kDefaultIfname : name.c_str(),
308 sizeof(ifr.ifr_name));
309 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
310
311 // If a template was given as the name, ifr_name will be updated with the
312 // actual interface name.
313 if ((*ioctl_)(dev.get(), TUNSETIFF, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900314 PLOG(ERROR) << "Failed to create tap interface " << name;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900315 return "";
316 }
317 const char* ifname = ifr.ifr_name;
318
319 if ((*ioctl_)(dev.get(), TUNSETPERSIST, 1) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900320 PLOG(ERROR) << "Failed to persist the interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900321 return "";
322 }
323
Garrick Evans4f9f5572019-11-26 10:25:16 +0900324 if (!user.empty()) {
325 uid_t uid = -1;
326 if (!brillo::userdb::GetUserInfo(user, &uid, nullptr)) {
327 PLOG(ERROR) << "Unable to look up UID for " << user;
328 RemoveTAP(ifname);
329 return "";
330 }
331 if ((*ioctl_)(dev.get(), TUNSETOWNER, uid) != 0) {
332 PLOG(ERROR) << "Failed to set owner " << uid << " of tap interface "
333 << ifname;
334 RemoveTAP(ifname);
335 return "";
336 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900337 }
338
Hugo Benichib9b93fe2019-10-25 23:36:01 +0900339 // Create control socket for configuring the interface.
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900340 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
341 if (!sock.is_valid()) {
342 PLOG(ERROR) << "Failed to create control socket for tap interface "
Garrick Evans621ed262019-11-13 12:28:43 +0900343 << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900344 RemoveTAP(ifname);
345 return "";
346 }
347
Garrick Evans621ed262019-11-13 12:28:43 +0900348 if (ipv4_addr) {
349 struct sockaddr_in* addr =
350 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
351 addr->sin_family = AF_INET;
352 addr->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr->Address());
353 if ((*ioctl_)(sock.get(), SIOCSIFADDR, &ifr) != 0) {
354 PLOG(ERROR) << "Failed to set ip address for vmtap interface " << ifname
355 << " {" << ipv4_addr->ToCidrString() << "}";
356 RemoveTAP(ifname);
357 return "";
358 }
359
360 struct sockaddr_in* netmask =
361 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_netmask);
362 netmask->sin_family = AF_INET;
363 netmask->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr->Netmask());
364 if ((*ioctl_)(sock.get(), SIOCSIFNETMASK, &ifr) != 0) {
365 PLOG(ERROR) << "Failed to set netmask for vmtap interface " << ifname
366 << " {" << ipv4_addr->ToCidrString() << "}";
367 RemoveTAP(ifname);
368 return "";
369 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900370 }
371
Garrick Evans621ed262019-11-13 12:28:43 +0900372 if (mac_addr) {
373 struct sockaddr* hwaddr = &ifr.ifr_hwaddr;
374 hwaddr->sa_family = ARPHRD_ETHER;
375 memcpy(&hwaddr->sa_data, mac_addr, sizeof(*mac_addr));
376 if ((*ioctl_)(sock.get(), SIOCSIFHWADDR, &ifr) != 0) {
377 PLOG(ERROR) << "Failed to set mac address for vmtap interface " << ifname
378 << " {" << MacAddressToString(*mac_addr) << "}";
379 RemoveTAP(ifname);
380 return "";
381 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900382 }
383
384 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900385 PLOG(ERROR) << "Failed to get flags for tap interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900386 RemoveTAP(ifname);
387 return "";
388 }
389
390 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
391 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900392 PLOG(ERROR) << "Failed to enable tap interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900393 RemoveTAP(ifname);
394 return "";
395 }
396
397 return ifname;
398}
399
400void Datapath::RemoveTAP(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900401 process_runner_->ip("tuntap", "del", {ifname, "mode", "tap"});
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900402}
403
Hugo Benichi33860d72020-07-09 16:34:01 +0900404bool Datapath::ConnectVethPair(pid_t netns_pid,
405 const std::string& netns_name,
Hugo Benichi76675592020-04-08 14:29:57 +0900406 const std::string& veth_ifname,
407 const std::string& peer_ifname,
408 const MacAddress& remote_mac_addr,
409 uint32_t remote_ipv4_addr,
410 uint32_t remote_ipv4_prefix_len,
411 bool remote_multicast_flag) {
Hugo Benichi33860d72020-07-09 16:34:01 +0900412 // Set up the virtual pair across the current namespace and |netns_name|.
413 if (!AddVirtualInterfacePair(netns_name, veth_ifname, peer_ifname)) {
414 LOG(ERROR) << "Failed to create veth pair " << veth_ifname << ","
415 << peer_ifname;
416 return false;
417 }
418
419 // Configure the remote veth in namespace |netns_name|.
Hugo Benichi76675592020-04-08 14:29:57 +0900420 {
Hugo Benichi33860d72020-07-09 16:34:01 +0900421 ScopedNS ns(netns_pid);
422 if (!ns.IsValid() && netns_pid != kTestPID) {
Hugo Benichi76675592020-04-08 14:29:57 +0900423 LOG(ERROR)
424 << "Cannot create virtual link -- invalid container namespace?";
425 return false;
426 }
427
Hugo Benichi76675592020-04-08 14:29:57 +0900428 if (!ConfigureInterface(peer_ifname, remote_mac_addr, remote_ipv4_addr,
429 remote_ipv4_prefix_len, true /* link up */,
430 remote_multicast_flag)) {
431 LOG(ERROR) << "Failed to configure interface " << peer_ifname;
432 RemoveInterface(peer_ifname);
433 return false;
434 }
435 }
436
Hugo Benichi76675592020-04-08 14:29:57 +0900437 if (!ToggleInterface(veth_ifname, true /*up*/)) {
438 LOG(ERROR) << "Failed to bring up interface " << veth_ifname;
439 RemoveInterface(veth_ifname);
440 return false;
441 }
Hugo Benichi33860d72020-07-09 16:34:01 +0900442
Hugo Benichi76675592020-04-08 14:29:57 +0900443 return true;
444}
445
Hugo Benichi33860d72020-07-09 16:34:01 +0900446bool Datapath::AddVirtualInterfacePair(const std::string& netns_name,
447 const std::string& veth_ifname,
Garrick Evans2470caa2020-03-04 14:15:41 +0900448 const std::string& peer_ifname) {
Hugo Benichi33860d72020-07-09 16:34:01 +0900449 return process_runner_->ip("link", "add",
450 {veth_ifname, "type", "veth", "peer", "name",
451 peer_ifname, "netns", netns_name}) == 0;
Garrick Evans2470caa2020-03-04 14:15:41 +0900452}
Garrick Evans54861622019-07-19 09:05:09 +0900453
Garrick Evans2470caa2020-03-04 14:15:41 +0900454bool Datapath::ToggleInterface(const std::string& ifname, bool up) {
455 const std::string link = up ? "up" : "down";
456 return process_runner_->ip("link", "set", {ifname, link}) == 0;
457}
Garrick Evans54861622019-07-19 09:05:09 +0900458
Garrick Evans2470caa2020-03-04 14:15:41 +0900459bool Datapath::ConfigureInterface(const std::string& ifname,
460 const MacAddress& mac_addr,
461 uint32_t ipv4_addr,
462 uint32_t ipv4_prefix_len,
463 bool up,
464 bool enable_multicast) {
465 const std::string link = up ? "up" : "down";
466 const std::string multicast = enable_multicast ? "on" : "off";
467 return (process_runner_->ip(
468 "addr", "add",
469 {IPv4AddressToCidrString(ipv4_addr, ipv4_prefix_len), "brd",
470 IPv4AddressToString(
471 Ipv4BroadcastAddr(ipv4_addr, ipv4_prefix_len)),
472 "dev", ifname}) == 0) &&
473 (process_runner_->ip("link", "set",
474 {
475 "dev",
476 ifname,
477 link,
478 "addr",
479 MacAddressToString(mac_addr),
480 "multicast",
481 multicast,
482 }) == 0);
Garrick Evans54861622019-07-19 09:05:09 +0900483}
484
485void Datapath::RemoveInterface(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900486 process_runner_->ip("link", "delete", {ifname}, false /*log_failures*/);
Garrick Evans54861622019-07-19 09:05:09 +0900487}
488
Hugo Benichi321f23b2020-09-25 15:42:05 +0900489bool Datapath::AddSourceIPv4DropRule(const std::string& oif,
490 const std::string& src_ip) {
491 return process_runner_->iptables("filter", {"-I", "OUTPUT", "-o", oif, "-s",
492 src_ip, "-j", "DROP", "-w"}) == 0;
493}
494
495bool Datapath::RemoveSourceIPv4DropRule(const std::string& oif,
496 const std::string& src_ip) {
497 return process_runner_->iptables("filter", {"-D", "OUTPUT", "-o", oif, "-s",
498 src_ip, "-j", "DROP", "-w"}) == 0;
499}
500
Hugo Benichi7c342672020-09-08 09:18:14 +0900501bool Datapath::StartRoutingNamespace(pid_t pid,
502 const std::string& netns_name,
503 const std::string& host_ifname,
504 const std::string& peer_ifname,
505 uint32_t subnet_ipv4_addr,
506 uint32_t subnet_prefixlen,
507 uint32_t host_ipv4_addr,
508 uint32_t peer_ipv4_addr,
509 const MacAddress& peer_mac_addr) {
510 // Veth interface configuration and client routing configuration:
511 // - attach a name to the client namespace.
512 // - create veth pair across the current namespace and the client namespace.
513 // - configure IPv4 address on remote veth inside client namespace.
514 // - configure IPv4 address on local veth inside host namespace.
515 // - add a default IPv4 /0 route sending traffic to that remote veth.
516 if (!NetnsAttachName(netns_name, pid)) {
517 LOG(ERROR) << "Failed to attach name " << netns_name << " to namespace pid "
518 << pid;
519 return false;
520 }
521
522 if (!ConnectVethPair(pid, netns_name, host_ifname, peer_ifname, peer_mac_addr,
523 peer_ipv4_addr, subnet_prefixlen,
524 false /* enable_multicast */)) {
525 LOG(ERROR) << "Failed to create veth pair for"
526 " namespace pid "
527 << pid;
528 NetnsDeleteName(netns_name);
529 return false;
530 }
531
532 if (!ConfigureInterface(host_ifname, peer_mac_addr, host_ipv4_addr,
533 subnet_prefixlen, true /* link up */,
534 false /* enable_multicast */)) {
535 LOG(ERROR) << "Cannot configure host interface " << host_ifname;
536 RemoveInterface(host_ifname);
537 NetnsDeleteName(netns_name);
538 return false;
539 }
540
541 {
542 ScopedNS ns(pid);
543 if (!ns.IsValid() && pid != kTestPID) {
544 LOG(ERROR) << "Invalid namespace pid " << pid;
545 RemoveInterface(host_ifname);
546 NetnsDeleteName(netns_name);
547 return false;
548 }
549
550 if (!AddIPv4Route(host_ipv4_addr, INADDR_ANY, INADDR_ANY)) {
551 LOG(ERROR) << "Failed to add default /0 route to " << host_ifname
552 << " inside namespace pid " << pid;
553 RemoveInterface(host_ifname);
554 NetnsDeleteName(netns_name);
555 return false;
556 }
557 }
558
559 // Host namespace routing configuration
560 // - ingress: add route to client subnet via |host_ifname|.
561 // - egress: - allow forwarding for traffic outgoing |host_ifname|.
562 // - add SNAT mark 0x1/0x1 for traffic outgoing |host_ifname|.
563 // Note that by default unsolicited ingress traffic is not forwarded to the
564 // client namespace unless the client specifically set port forwarding
565 // through permission_broker DBus APIs.
566 // TODO(hugobenichi) If allow_user_traffic is false, then prevent forwarding
567 // both ways between client namespace and other guest containers and VMs.
568 // TODO(b/161507671) If outbound_physical_device is defined, then set strong
569 // routing to that interface routing table.
570 uint32_t netmask = Ipv4Netmask(subnet_prefixlen);
571 if (!AddIPv4Route(host_ipv4_addr, subnet_ipv4_addr, netmask)) {
572 LOG(ERROR) << "Failed to set route to client namespace";
573 RemoveInterface(host_ifname);
574 NetnsDeleteName(netns_name);
575 return false;
576 }
577
Hugo Benichi58125d32020-09-09 11:25:45 +0900578 if (!StartIpForwarding(IpFamily::IPv4, "", host_ifname)) {
579 LOG(ERROR) << "Failed to allow FORWARD for ingress traffic into "
Hugo Benichi7c342672020-09-08 09:18:14 +0900580 << host_ifname;
581 RemoveInterface(host_ifname);
582 DeleteIPv4Route(host_ipv4_addr, subnet_ipv4_addr, netmask);
583 NetnsDeleteName(netns_name);
584 return false;
585 }
586
587 // TODO(b/161508179) Add fwmark source tagging based on client usage.
588 // TODO(b/161508179) Do not rely on legacy fwmark 1 for SNAT.
589 if (!AddOutboundIPv4SNATMark(host_ifname)) {
590 LOG(ERROR) << "Failed to set SNAT for traffic"
591 " outgoing from "
592 << host_ifname;
593 RemoveInterface(host_ifname);
594 DeleteIPv4Route(host_ipv4_addr, subnet_ipv4_addr, netmask);
Hugo Benichi58125d32020-09-09 11:25:45 +0900595 StopIpForwarding(IpFamily::IPv4, "", host_ifname);
Hugo Benichi7c342672020-09-08 09:18:14 +0900596 NetnsDeleteName(netns_name);
597 return false;
598 }
599
600 return true;
601}
602
603void Datapath::StopRoutingNamespace(const std::string& netns_name,
604 const std::string& host_ifname,
605 uint32_t subnet_ipv4_addr,
606 uint32_t subnet_prefixlen,
607 uint32_t host_ipv4_addr) {
608 RemoveInterface(host_ifname);
Hugo Benichi58125d32020-09-09 11:25:45 +0900609 StopIpForwarding(IpFamily::IPv4, "", host_ifname);
Hugo Benichi7c342672020-09-08 09:18:14 +0900610 RemoveOutboundIPv4SNATMark(host_ifname);
611 DeleteIPv4Route(host_ipv4_addr, subnet_ipv4_addr,
612 Ipv4Netmask(subnet_prefixlen));
613 NetnsDeleteName(netns_name);
614}
615
Hugo Benichi8d622b52020-08-13 15:24:12 +0900616void Datapath::StartRoutingDevice(const std::string& ext_ifname,
617 const std::string& int_ifname,
618 uint32_t int_ipv4_addr,
619 TrafficSource source) {
620 if (!ext_ifname.empty() &&
621 !AddInboundIPv4DNAT(ext_ifname, IPv4AddressToString(int_ipv4_addr)))
622 LOG(ERROR) << "Failed to configure ingress traffic rules for " << ext_ifname
623 << "->" << int_ifname;
624
Hugo Benichifa97b3b2020-10-06 22:45:26 +0900625 if (!StartIpForwarding(IpFamily::IPv4, ext_ifname, int_ifname))
Hugo Benichic6ae67c2020-08-14 15:02:13 +0900626 LOG(ERROR) << "Failed to enable IP forwarding for " << ext_ifname << "->"
627 << int_ifname;
Hugo Benichi8d622b52020-08-13 15:24:12 +0900628
Hugo Benichifa97b3b2020-10-06 22:45:26 +0900629 if (!StartIpForwarding(IpFamily::IPv4, int_ifname, ext_ifname))
Hugo Benichic6ae67c2020-08-14 15:02:13 +0900630 LOG(ERROR) << "Failed to enable IP forwarding for " << ext_ifname << "<-"
631 << int_ifname;
Hugo Benichi8d622b52020-08-13 15:24:12 +0900632
Hugo Benichi2a940542020-10-26 18:50:49 +0900633 if (!ModifyFwmarkSourceTag("-A", int_ifname, source))
634 LOG(ERROR) << "Failed to add PREROUTING fwmark tagging rule for source "
635 << source << " for " << int_ifname;
636
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900637 if (!ext_ifname.empty()) {
638 // If |ext_ifname| is not null, mark egress traffic with the
639 // fwmark routing tag corresponding to |ext_ifname|.
Hugo Benichi2a940542020-10-26 18:50:49 +0900640 if (!ModifyFwmarkRoutingTag("PREROUTING", "-A", ext_ifname, int_ifname))
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900641 LOG(ERROR) << "Failed to add PREROUTING fwmark routing tag for "
642 << ext_ifname << "<-" << int_ifname;
643 } else {
644 // Otherwise if ext_ifname is null, set up a CONNMARK restore rule in
645 // PREROUTING to apply any fwmark routing tag saved for the current
646 // connection, and rely on implicit routing to the default logical network
647 // otherwise.
Hugo Benichi1af52392020-11-27 18:09:32 +0900648 if (!ModifyConnmarkRestore(IpFamily::Dual, "PREROUTING", "-A", int_ifname,
649 kFwmarkRoutingMask))
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900650 LOG(ERROR) << "Failed to add PREROUTING CONNMARK restore rule for "
651 << int_ifname;
Hugo Benichi8d622b52020-08-13 15:24:12 +0900652
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900653 // Forwarded traffic from downstream virtual devices routed to the system
654 // logical default network is always eligible to be routed through a VPN.
655 if (!ModifyFwmarkVpnJumpRule("PREROUTING", "-A", int_ifname, {}, {}))
656 LOG(ERROR) << "Failed to add jump rule to VPN chain for " << int_ifname;
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900657 }
Hugo Benichi8d622b52020-08-13 15:24:12 +0900658}
659
660void Datapath::StopRoutingDevice(const std::string& ext_ifname,
661 const std::string& int_ifname,
662 uint32_t int_ipv4_addr,
663 TrafficSource source) {
664 if (!ext_ifname.empty())
665 RemoveInboundIPv4DNAT(ext_ifname, IPv4AddressToString(int_ipv4_addr));
Hugo Benichic6ae67c2020-08-14 15:02:13 +0900666 StopIpForwarding(IpFamily::IPv4, ext_ifname, int_ifname);
667 StopIpForwarding(IpFamily::IPv4, int_ifname, ext_ifname);
Hugo Benichi9be19b12020-08-14 15:33:40 +0900668 ModifyFwmarkSourceTag("-D", int_ifname, source);
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900669 if (!ext_ifname.empty()) {
Hugo Benichi2a940542020-10-26 18:50:49 +0900670 ModifyFwmarkRoutingTag("PREROUTING", "-D", ext_ifname, int_ifname);
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900671 } else {
Hugo Benichi1af52392020-11-27 18:09:32 +0900672 ModifyConnmarkRestore(IpFamily::Dual, "PREROUTING", "-D", int_ifname,
673 kFwmarkRoutingMask);
Hugo Benichi3ef370b2020-11-16 19:07:17 +0900674 ModifyFwmarkVpnJumpRule("PREROUTING", "-D", int_ifname, {}, {});
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900675 }
Hugo Benichi8d622b52020-08-13 15:24:12 +0900676}
677
Garrick Evansf0ab7132019-06-18 14:50:42 +0900678bool Datapath::AddInboundIPv4DNAT(const std::string& ifname,
679 const std::string& ipv4_addr) {
680 // Direct ingress IP traffic to existing sockets.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900681 if (process_runner_->iptables(
682 "nat", {"-A", "PREROUTING", "-i", ifname, "-m", "socket",
683 "--nowildcard", "-j", "ACCEPT", "-w"}) != 0)
Garrick Evansf0ab7132019-06-18 14:50:42 +0900684 return false;
685
686 // Direct ingress TCP & UDP traffic to ARC interface for new connections.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900687 if (process_runner_->iptables(
688 "nat", {"-A", "PREROUTING", "-i", ifname, "-p", "tcp", "-j", "DNAT",
689 "--to-destination", ipv4_addr, "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900690 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
691 return false;
692 }
Garrick Evans8e8e3472020-01-23 14:03:50 +0900693 if (process_runner_->iptables(
694 "nat", {"-A", "PREROUTING", "-i", ifname, "-p", "udp", "-j", "DNAT",
695 "--to-destination", ipv4_addr, "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900696 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
697 return false;
698 }
699
700 return true;
701}
702
703void Datapath::RemoveInboundIPv4DNAT(const std::string& ifname,
704 const std::string& ipv4_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900705 process_runner_->iptables(
706 "nat", {"-D", "PREROUTING", "-i", ifname, "-p", "udp", "-j", "DNAT",
707 "--to-destination", ipv4_addr, "-w"});
708 process_runner_->iptables(
709 "nat", {"-D", "PREROUTING", "-i", ifname, "-p", "tcp", "-j", "DNAT",
710 "--to-destination", ipv4_addr, "-w"});
711 process_runner_->iptables(
712 "nat", {"-D", "PREROUTING", "-i", ifname, "-m", "socket", "--nowildcard",
713 "-j", "ACCEPT", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900714}
715
Hugo Benichic6ae67c2020-08-14 15:02:13 +0900716// TODO(b/161507671) Stop relying on the traffic fwmark 1/1 once forwarded
717// egress traffic is routed through the fwmark routing tag.
Garrick Evansd291af62020-05-25 10:39:06 +0900718bool Datapath::AddSNATMarkRules() {
Taoyu Li79871c92020-07-02 16:09:39 +0900719 // chromium:1050579: INVALID packets cannot be tracked by conntrack therefore
720 // need to be explicitly dropped.
721 if (process_runner_->iptables(
Hugo Benichi6c445322020-08-12 16:46:19 +0900722 "filter", {"-A", "FORWARD", "-m", "mark", "--mark", "1/1", "-m",
Taoyu Li79871c92020-07-02 16:09:39 +0900723 "state", "--state", "INVALID", "-j", "DROP", "-w"}) != 0) {
724 return false;
725 }
Garrick Evansd291af62020-05-25 10:39:06 +0900726 if (process_runner_->iptables(
Hugo Benichi6c445322020-08-12 16:46:19 +0900727 "filter", {"-A", "FORWARD", "-m", "mark", "--mark", "1/1", "-j",
Garrick Evansd291af62020-05-25 10:39:06 +0900728 "ACCEPT", "-w"}) != 0) {
729 return false;
730 }
731 if (process_runner_->iptables(
Hugo Benichi6c445322020-08-12 16:46:19 +0900732 "nat", {"-A", "POSTROUTING", "-m", "mark", "--mark", "1/1", "-j",
Garrick Evansd291af62020-05-25 10:39:06 +0900733 "MASQUERADE", "-w"}) != 0) {
734 RemoveSNATMarkRules();
735 return false;
736 }
737 return true;
738}
739
740void Datapath::RemoveSNATMarkRules() {
741 process_runner_->iptables("nat", {"-D", "POSTROUTING", "-m", "mark", "--mark",
Hugo Benichi6c445322020-08-12 16:46:19 +0900742 "1/1", "-j", "MASQUERADE", "-w"});
Garrick Evansd291af62020-05-25 10:39:06 +0900743 process_runner_->iptables("filter", {"-D", "FORWARD", "-m", "mark", "--mark",
Hugo Benichi6c445322020-08-12 16:46:19 +0900744 "1/1", "-j", "ACCEPT", "-w"});
Taoyu Li79871c92020-07-02 16:09:39 +0900745 process_runner_->iptables(
Hugo Benichi6c445322020-08-12 16:46:19 +0900746 "filter", {"-D", "FORWARD", "-m", "mark", "--mark", "1/1", "-m", "state",
Taoyu Li79871c92020-07-02 16:09:39 +0900747 "--state", "INVALID", "-j", "DROP", "-w"});
Garrick Evansd291af62020-05-25 10:39:06 +0900748}
749
Hugo Benichie8758b52020-04-03 14:49:01 +0900750bool Datapath::AddOutboundIPv4SNATMark(const std::string& ifname) {
751 return process_runner_->iptables(
752 "mangle", {"-A", "PREROUTING", "-i", ifname, "-j", "MARK",
Hugo Benichi6c445322020-08-12 16:46:19 +0900753 "--set-mark", "1/1", "-w"}) == 0;
Hugo Benichie8758b52020-04-03 14:49:01 +0900754}
755
756void Datapath::RemoveOutboundIPv4SNATMark(const std::string& ifname) {
757 process_runner_->iptables("mangle", {"-D", "PREROUTING", "-i", ifname, "-j",
Hugo Benichi6c445322020-08-12 16:46:19 +0900758 "MARK", "--set-mark", "1/1", "-w"});
Hugo Benichie8758b52020-04-03 14:49:01 +0900759}
760
Garrick Evans664a82f2019-12-17 12:18:05 +0900761bool Datapath::MaskInterfaceFlags(const std::string& ifname,
762 uint16_t on,
763 uint16_t off) {
Taoyu Li90c13912019-11-26 17:56:54 +0900764 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
765 if (!sock.is_valid()) {
766 PLOG(ERROR) << "Failed to create control socket";
767 return false;
768 }
769 ifreq ifr;
770 snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname.c_str());
771 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) < 0) {
772 PLOG(WARNING) << "ioctl() failed to get interface flag on " << ifname;
773 return false;
774 }
Garrick Evans664a82f2019-12-17 12:18:05 +0900775 ifr.ifr_flags |= on;
776 ifr.ifr_flags &= ~off;
Taoyu Li90c13912019-11-26 17:56:54 +0900777 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) < 0) {
Garrick Evans664a82f2019-12-17 12:18:05 +0900778 PLOG(WARNING) << "ioctl() failed to set flag 0x" << std::hex << on
779 << " unset flag 0x" << std::hex << off << " on " << ifname;
Taoyu Li90c13912019-11-26 17:56:54 +0900780 return false;
781 }
782 return true;
783}
784
Garrick Evans260ff302019-07-25 11:22:50 +0900785bool Datapath::AddIPv6HostRoute(const std::string& ifname,
786 const std::string& ipv6_addr,
787 int ipv6_prefix_len) {
788 std::string ipv6_addr_cidr =
789 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
790
Garrick Evans8e8e3472020-01-23 14:03:50 +0900791 return process_runner_->ip6("route", "replace",
792 {ipv6_addr_cidr, "dev", ifname}) == 0;
Garrick Evans260ff302019-07-25 11:22:50 +0900793}
794
795void Datapath::RemoveIPv6HostRoute(const std::string& ifname,
796 const std::string& ipv6_addr,
797 int ipv6_prefix_len) {
798 std::string ipv6_addr_cidr =
799 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
800
Garrick Evans8e8e3472020-01-23 14:03:50 +0900801 process_runner_->ip6("route", "del", {ipv6_addr_cidr, "dev", ifname});
Garrick Evans260ff302019-07-25 11:22:50 +0900802}
803
Taoyu Lia0727dc2020-09-24 19:54:59 +0900804bool Datapath::AddIPv6Address(const std::string& ifname,
805 const std::string& ipv6_addr) {
806 return process_runner_->ip6("addr", "add", {ipv6_addr, "dev", ifname}) == 0;
Garrick Evans260ff302019-07-25 11:22:50 +0900807}
808
Taoyu Lia0727dc2020-09-24 19:54:59 +0900809void Datapath::RemoveIPv6Address(const std::string& ifname,
810 const std::string& ipv6_addr) {
811 process_runner_->ip6("addr", "del", {ipv6_addr, "dev", ifname});
Garrick Evans260ff302019-07-25 11:22:50 +0900812}
813
Hugo Benichi76be34a2020-08-26 22:35:54 +0900814void Datapath::StartConnectionPinning(const std::string& ext_ifname) {
Hugo Benichi1af52392020-11-27 18:09:32 +0900815 // Set in CONNMARK the routing tag associated with |ext_ifname|.
Hugo Benichi76be34a2020-08-26 22:35:54 +0900816 if (!ModifyConnmarkSetPostrouting(IpFamily::Dual, "-A", ext_ifname))
817 LOG(ERROR) << "Could not start connection pinning on " << ext_ifname;
Hugo Benichi1af52392020-11-27 18:09:32 +0900818 // Save in CONNMARK the source tag for egress traffic of this connection.
819 if (!ModifyConnmarkSave(IpFamily::Dual, "POSTROUTING", "-A", ext_ifname,
820 kFwmarkAllSourcesMask))
821 LOG(ERROR) << "Failed to add POSTROUTING CONNMARK rule for saving fwmark "
822 "source tag on "
823 << ext_ifname;
824 // Restore from CONNMARK the source tag for ingress traffic of this connection
825 // (returned traffic).
826 if (!ModifyConnmarkRestore(IpFamily::Dual, "PREROUTING", "-A", ext_ifname,
827 kFwmarkAllSourcesMask))
828 LOG(ERROR) << "Could not setup fwmark source tagging rule for return "
829 "traffic received on "
830 << ext_ifname;
Hugo Benichi76be34a2020-08-26 22:35:54 +0900831}
832
833void Datapath::StopConnectionPinning(const std::string& ext_ifname) {
834 if (!ModifyConnmarkSetPostrouting(IpFamily::Dual, "-D", ext_ifname))
835 LOG(ERROR) << "Could not stop connection pinning on " << ext_ifname;
Hugo Benichi1af52392020-11-27 18:09:32 +0900836 if (!ModifyConnmarkSave(IpFamily::Dual, "POSTROUTING", "-D", ext_ifname,
837 kFwmarkAllSourcesMask))
838 LOG(ERROR) << "Could not remove POSTROUTING CONNMARK rule for saving "
839 "fwmark source tag on "
840 << ext_ifname;
841 if (!ModifyConnmarkRestore(IpFamily::Dual, "PREROUTING", "-D", ext_ifname,
842 kFwmarkAllSourcesMask))
843 LOG(ERROR) << "Could not remove fwmark source tagging rule for return "
844 "traffic received on "
845 << ext_ifname;
Hugo Benichi76be34a2020-08-26 22:35:54 +0900846}
847
Hugo Benichi2a940542020-10-26 18:50:49 +0900848void Datapath::StartVpnRouting(const std::string& vpn_ifname) {
849 StartConnectionPinning(vpn_ifname);
850 if (!ModifyFwmarkRoutingTag(kApplyVpnMarkChain, "-A", vpn_ifname, ""))
851 LOG(ERROR) << "Failed to set up VPN set-mark rule for " << vpn_ifname;
852}
853
854void Datapath::StopVpnRouting(const std::string& vpn_ifname) {
855 if (!ModifyFwmarkRoutingTag(kApplyVpnMarkChain, "-D", vpn_ifname, ""))
856 LOG(ERROR) << "Failed to remove VPN set-mark rule for " << vpn_ifname;
857 StopConnectionPinning(vpn_ifname);
858}
859
Hugo Benichi76be34a2020-08-26 22:35:54 +0900860bool Datapath::ModifyConnmarkSetPostrouting(IpFamily family,
861 const std::string& op,
862 const std::string& oif) {
Hugo Benichi76be34a2020-08-26 22:35:54 +0900863 int ifindex = FindIfIndex(oif);
864 if (ifindex == 0) {
865 PLOG(ERROR) << "if_nametoindex(" << oif << ") failed";
866 return false;
867 }
868
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900869 return ModifyConnmarkSet(family, "POSTROUTING", op, oif,
870 Fwmark::FromIfIndex(ifindex), kFwmarkRoutingMask);
871}
872
873bool Datapath::ModifyConnmarkSet(IpFamily family,
874 const std::string& chain,
875 const std::string& op,
876 const std::string& oif,
877 Fwmark mark,
878 Fwmark mask) {
879 if (chain != kApplyVpnMarkChain && (chain != "POSTROUTING" || oif.empty())) {
880 LOG(ERROR) << "Invalid arguments chain=" << chain << " oif=" << oif;
881 return false;
882 }
883
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900884 std::vector<std::string> args = {op, chain};
885 if (!oif.empty()) {
886 args.push_back("-o");
887 args.push_back(oif);
888 }
889 args.push_back("-j");
890 args.push_back("CONNMARK");
891 args.push_back("--set-mark");
892 args.push_back(mark.ToString() + "/" + mask.ToString());
893 args.push_back("-w");
Hugo Benichi76be34a2020-08-26 22:35:54 +0900894
Hugo Benichi58f264a2020-10-16 18:16:05 +0900895 return ModifyIptables(family, "mangle", args);
Hugo Benichi76be34a2020-08-26 22:35:54 +0900896}
897
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900898bool Datapath::ModifyConnmarkRestore(IpFamily family,
899 const std::string& chain,
900 const std::string& op,
Hugo Benichi1af52392020-11-27 18:09:32 +0900901 const std::string& iif,
902 Fwmark mask) {
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900903 if (chain != "OUTPUT" && (chain != "PREROUTING" || iif.empty())) {
904 LOG(ERROR) << "Invalid arguments chain=" << chain << " iif=" << iif;
905 return false;
906 }
907
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900908 std::vector<std::string> args = {op, chain};
909 if (!iif.empty()) {
910 args.push_back("-i");
911 args.push_back(iif);
912 }
913 args.insert(args.end(), {"-j", "CONNMARK", "--restore-mark", "--mask",
Hugo Benichi1af52392020-11-27 18:09:32 +0900914 mask.ToString(), "-w"});
915 return ModifyIptables(family, "mangle", args);
916}
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900917
Hugo Benichi1af52392020-11-27 18:09:32 +0900918bool Datapath::ModifyConnmarkSave(IpFamily family,
919 const std::string& chain,
920 const std::string& op,
921 const std::string& oif,
922 Fwmark mask) {
923 std::vector<std::string> args = {op, chain};
924 if (!oif.empty()) {
925 args.push_back("-o");
926 args.push_back(oif);
927 }
928 args.insert(args.end(), {"-j", "CONNMARK", "--save-mark", "--mask",
929 mask.ToString(), "-w"});
Hugo Benichi58f264a2020-10-16 18:16:05 +0900930 return ModifyIptables(family, "mangle", args);
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900931}
932
Hugo Benichi2a940542020-10-26 18:50:49 +0900933bool Datapath::ModifyFwmarkRoutingTag(const std::string& chain,
934 const std::string& op,
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900935 const std::string& ext_ifname,
936 const std::string& int_ifname) {
937 int ifindex = FindIfIndex(ext_ifname);
938 if (ifindex == 0) {
939 PLOG(ERROR) << "if_nametoindex(" << ext_ifname << ") failed";
940 return false;
941 }
942
Hugo Benichi2a940542020-10-26 18:50:49 +0900943 return ModifyFwmark(IpFamily::Dual, chain, op, int_ifname, "" /*uid_name*/,
944 Fwmark::FromIfIndex(ifindex), kFwmarkRoutingMask);
Hugo Benichiaf9d8a72020-08-26 13:28:13 +0900945}
946
Hugo Benichi9be19b12020-08-14 15:33:40 +0900947bool Datapath::ModifyFwmarkSourceTag(const std::string& op,
948 const std::string& iif,
949 TrafficSource source) {
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900950 return ModifyFwmark(IpFamily::Dual, "PREROUTING", op, iif, "" /*uid_name*/,
951 Fwmark::FromSource(source), kFwmarkAllSourcesMask);
Hugo Benichi9be19b12020-08-14 15:33:40 +0900952}
953
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900954bool Datapath::ModifyFwmarkDefaultLocalSourceTag(const std::string& op,
955 TrafficSource source) {
956 std::vector<std::string> args = {"-A",
957 kApplyLocalSourceMarkChain,
958 "-m",
959 "mark",
960 "--mark",
961 "0x0/" + kFwmarkAllSourcesMask.ToString(),
962 "-j",
963 "MARK",
964 "--set-mark",
965 Fwmark::FromSource(source).ToString() + "/" +
966 kFwmarkAllSourcesMask.ToString(),
967 "-w"};
968 return ModifyIptables(IpFamily::Dual, "mangle", args);
969}
Hugo Benichi9be19b12020-08-14 15:33:40 +0900970
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900971bool Datapath::ModifyFwmarkLocalSourceTag(const std::string& op,
972 const LocalSourceSpecs& source) {
973 Fwmark mark = Fwmark::FromSource(source.source_type);
974 if (source.is_on_vpn)
975 mark = mark | kFwmarkRouteOnVpn;
976
977 const std::string& uid_name = source.uid_name;
978 if (!uid_name.empty())
979 return ModifyFwmark(IpFamily::Dual, kApplyLocalSourceMarkChain, op,
980 "" /*iif*/, uid_name, mark, kFwmarkPolicyMask);
981
982 return false;
983 // TODO(b/167479541) Supports entries specifying a cgroup classid value.
984}
985
986bool Datapath::ModifyFwmark(IpFamily family,
987 const std::string& chain,
988 const std::string& op,
989 const std::string& iif,
990 const std::string& uid_name,
991 Fwmark mark,
992 Fwmark mask,
993 bool log_failures) {
Hugo Benichi3a9162b2020-09-09 15:47:40 +0900994 std::vector<std::string> args = {op, chain};
995 if (!iif.empty()) {
996 args.push_back("-i");
997 args.push_back(iif);
998 }
999 if (!uid_name.empty()) {
1000 args.push_back("-m");
1001 args.push_back("owner");
1002 args.push_back("--uid-owner");
1003 args.push_back(uid_name);
1004 }
1005 args.push_back("-j");
1006 args.push_back("MARK");
1007 args.push_back("--set-mark");
1008 args.push_back(mark.ToString() + "/" + mask.ToString());
1009 args.push_back("-w");
Hugo Benichi9be19b12020-08-14 15:33:40 +09001010
Hugo Benichi58f264a2020-10-16 18:16:05 +09001011 return ModifyIptables(family, "mangle", args, log_failures);
Hugo Benichi9be19b12020-08-14 15:33:40 +09001012}
1013
Hugo Benichid82d8832020-08-14 10:05:03 +09001014bool Datapath::ModifyIpForwarding(IpFamily family,
1015 const std::string& op,
1016 const std::string& iif,
1017 const std::string& oif,
1018 bool log_failures) {
1019 if (iif.empty() && oif.empty()) {
1020 LOG(ERROR) << "Cannot change IP forwarding with no input or output "
1021 "interface specified";
Garrick Evans260ff302019-07-25 11:22:50 +09001022 return false;
1023 }
1024
Hugo Benichid82d8832020-08-14 10:05:03 +09001025 std::vector<std::string> args = {op, "FORWARD"};
1026 if (!iif.empty()) {
1027 args.push_back("-i");
1028 args.push_back(iif);
1029 }
1030 if (!oif.empty()) {
1031 args.push_back("-o");
1032 args.push_back(oif);
1033 }
1034 args.push_back("-j");
1035 args.push_back("ACCEPT");
1036 args.push_back("-w");
1037
Hugo Benichi58f264a2020-10-16 18:16:05 +09001038 return ModifyIptables(family, "filter", args, log_failures);
Hugo Benichid82d8832020-08-14 10:05:03 +09001039}
1040
Hugo Benichi3ef370b2020-11-16 19:07:17 +09001041bool Datapath::ModifyFwmarkVpnJumpRule(const std::string& chain,
1042 const std::string& op,
1043 const std::string& iif,
1044 Fwmark mark,
1045 Fwmark mask) {
1046 std::vector<std::string> args = {op, chain};
1047 if (!iif.empty()) {
1048 args.push_back("-i");
1049 args.push_back(iif);
1050 }
1051 if (mark.Value() != 0 && mask.Value() != 0) {
1052 args.push_back("-m");
1053 args.push_back("mark");
1054 args.push_back("--mark");
1055 args.push_back(mark.ToString() + "/" + mask.ToString());
1056 }
1057 args.insert(args.end(), {"-j", kApplyVpnMarkChain, "-w"});
1058 return ModifyIptables(IpFamily::Dual, "mangle", args);
1059}
1060
1061bool Datapath::ModifyChain(IpFamily family,
1062 const std::string& table,
1063 const std::string& op,
Hugo Benichi58f264a2020-10-16 18:16:05 +09001064 const std::string& chain,
1065 bool log_failures) {
1066 return ModifyIptables(family, table, {op, chain, "-w"}, log_failures);
Hugo Benichi3ef370b2020-11-16 19:07:17 +09001067}
1068
1069bool Datapath::ModifyIptables(IpFamily family,
1070 const std::string& table,
Hugo Benichi58f264a2020-10-16 18:16:05 +09001071 const std::vector<std::string>& argv,
1072 bool log_failures) {
1073 switch (family) {
1074 case IPv4:
1075 case IPv6:
1076 case Dual:
1077 break;
1078 default:
1079 LOG(ERROR) << "Could not execute iptables command " << table
1080 << base::JoinString(argv, " ") << ": incorrect IP family "
1081 << family;
1082 return false;
Hugo Benichi3ef370b2020-11-16 19:07:17 +09001083 }
1084
1085 bool success = true;
1086 if (family & IpFamily::IPv4)
Hugo Benichi58f264a2020-10-16 18:16:05 +09001087 success &= process_runner_->iptables(table, argv, log_failures) == 0;
Hugo Benichi3ef370b2020-11-16 19:07:17 +09001088 if (family & IpFamily::IPv6)
Hugo Benichi58f264a2020-10-16 18:16:05 +09001089 success &= process_runner_->ip6tables(table, argv, log_failures) == 0;
Hugo Benichi3ef370b2020-11-16 19:07:17 +09001090 return success;
1091}
1092
Hugo Benichid82d8832020-08-14 10:05:03 +09001093bool Datapath::StartIpForwarding(IpFamily family,
1094 const std::string& iif,
1095 const std::string& oif) {
1096 return ModifyIpForwarding(family, "-A", iif, oif);
1097}
1098
1099bool Datapath::StopIpForwarding(IpFamily family,
1100 const std::string& iif,
1101 const std::string& oif) {
1102 return ModifyIpForwarding(family, "-D", iif, oif);
1103}
1104
1105bool Datapath::AddIPv6Forwarding(const std::string& ifname1,
1106 const std::string& ifname2) {
1107 // Only start Ipv6 forwarding if -C returns false and it had not been
1108 // started yet.
1109 if (!ModifyIpForwarding(IpFamily::IPv6, "-C", ifname1, ifname2,
1110 false /*log_failures*/) &&
1111 !StartIpForwarding(IpFamily::IPv6, ifname1, ifname2)) {
1112 return false;
1113 }
1114
1115 if (!ModifyIpForwarding(IpFamily::IPv6, "-C", ifname2, ifname1,
1116 false /*log_failures*/) &&
1117 !StartIpForwarding(IpFamily::IPv6, ifname2, ifname1)) {
Garrick Evans260ff302019-07-25 11:22:50 +09001118 RemoveIPv6Forwarding(ifname1, ifname2);
1119 return false;
1120 }
1121
1122 return true;
1123}
1124
1125void Datapath::RemoveIPv6Forwarding(const std::string& ifname1,
1126 const std::string& ifname2) {
Hugo Benichid82d8832020-08-14 10:05:03 +09001127 StopIpForwarding(IpFamily::IPv6, ifname1, ifname2);
1128 StopIpForwarding(IpFamily::IPv6, ifname2, ifname1);
Garrick Evans260ff302019-07-25 11:22:50 +09001129}
1130
Garrick Evans3d97a392020-02-21 15:24:37 +09001131bool Datapath::AddIPv4Route(uint32_t gateway_addr,
1132 uint32_t addr,
1133 uint32_t netmask) {
1134 struct rtentry route;
1135 memset(&route, 0, sizeof(route));
Hugo Benichie8758b52020-04-03 14:49:01 +09001136 SetSockaddrIn(&route.rt_gateway, gateway_addr);
1137 SetSockaddrIn(&route.rt_dst, addr & netmask);
1138 SetSockaddrIn(&route.rt_genmask, netmask);
Garrick Evans3d97a392020-02-21 15:24:37 +09001139 route.rt_flags = RTF_UP | RTF_GATEWAY;
Hugo Benichie8758b52020-04-03 14:49:01 +09001140 return ModifyRtentry(SIOCADDRT, &route);
1141}
Garrick Evans3d97a392020-02-21 15:24:37 +09001142
Hugo Benichie8758b52020-04-03 14:49:01 +09001143bool Datapath::DeleteIPv4Route(uint32_t gateway_addr,
1144 uint32_t addr,
1145 uint32_t netmask) {
1146 struct rtentry route;
1147 memset(&route, 0, sizeof(route));
1148 SetSockaddrIn(&route.rt_gateway, gateway_addr);
1149 SetSockaddrIn(&route.rt_dst, addr & netmask);
1150 SetSockaddrIn(&route.rt_genmask, netmask);
1151 route.rt_flags = RTF_UP | RTF_GATEWAY;
1152 return ModifyRtentry(SIOCDELRT, &route);
1153}
1154
1155bool Datapath::AddIPv4Route(const std::string& ifname,
1156 uint32_t addr,
1157 uint32_t netmask) {
1158 struct rtentry route;
1159 memset(&route, 0, sizeof(route));
1160 SetSockaddrIn(&route.rt_dst, addr & netmask);
1161 SetSockaddrIn(&route.rt_genmask, netmask);
1162 char rt_dev[IFNAMSIZ];
1163 strncpy(rt_dev, ifname.c_str(), IFNAMSIZ);
1164 rt_dev[IFNAMSIZ - 1] = '\0';
1165 route.rt_dev = rt_dev;
1166 route.rt_flags = RTF_UP | RTF_GATEWAY;
1167 return ModifyRtentry(SIOCADDRT, &route);
1168}
1169
1170bool Datapath::DeleteIPv4Route(const std::string& ifname,
1171 uint32_t addr,
1172 uint32_t netmask) {
1173 struct rtentry route;
1174 memset(&route, 0, sizeof(route));
1175 SetSockaddrIn(&route.rt_dst, addr & netmask);
1176 SetSockaddrIn(&route.rt_genmask, netmask);
1177 char rt_dev[IFNAMSIZ];
1178 strncpy(rt_dev, ifname.c_str(), IFNAMSIZ);
1179 rt_dev[IFNAMSIZ - 1] = '\0';
1180 route.rt_dev = rt_dev;
1181 route.rt_flags = RTF_UP | RTF_GATEWAY;
1182 return ModifyRtentry(SIOCDELRT, &route);
1183}
1184
Taoyu Lia0727dc2020-09-24 19:54:59 +09001185bool Datapath::ModifyRtentry(ioctl_req_t op, struct rtentry* route) {
Hugo Benichie8758b52020-04-03 14:49:01 +09001186 DCHECK(route);
1187 if (op != SIOCADDRT && op != SIOCDELRT) {
Andreea Costinas34aa7a92020-08-04 10:36:10 +02001188 LOG(ERROR) << "Invalid operation " << op << " for rtentry " << *route;
Garrick Evans3d97a392020-02-21 15:24:37 +09001189 return false;
1190 }
Hugo Benichie8758b52020-04-03 14:49:01 +09001191 base::ScopedFD fd(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
1192 if (!fd.is_valid()) {
Andreea Costinas34aa7a92020-08-04 10:36:10 +02001193 PLOG(ERROR) << "Failed to create socket for adding rtentry " << *route;
Hugo Benichie8758b52020-04-03 14:49:01 +09001194 return false;
1195 }
1196 if (HANDLE_EINTR(ioctl_(fd.get(), op, route)) != 0) {
1197 std::string opname = op == SIOCADDRT ? "add" : "delete";
Andreea Costinas34aa7a92020-08-04 10:36:10 +02001198 PLOG(ERROR) << "Failed to " << opname << " rtentry " << *route;
Garrick Evans3d97a392020-02-21 15:24:37 +09001199 return false;
1200 }
1201 return true;
1202}
1203
Jason Jeremy Imana7273a32020-08-04 11:25:31 +09001204bool Datapath::AddAdbPortForwardRule(const std::string& ifname) {
1205 return firewall_->AddIpv4ForwardRule(patchpanel::ModifyPortRuleRequest::TCP,
1206 kArcAddr, kAdbServerPort, ifname,
1207 kLocalhostAddr, kAdbProxyTcpListenPort);
1208}
1209
1210void Datapath::DeleteAdbPortForwardRule(const std::string& ifname) {
1211 firewall_->DeleteIpv4ForwardRule(patchpanel::ModifyPortRuleRequest::TCP,
1212 kArcAddr, kAdbServerPort, ifname,
1213 kLocalhostAddr, kAdbProxyTcpListenPort);
1214}
1215
1216bool Datapath::AddAdbPortAccessRule(const std::string& ifname) {
1217 return firewall_->AddAcceptRules(patchpanel::ModifyPortRuleRequest::TCP,
1218 kAdbProxyTcpListenPort, ifname);
1219}
1220
1221void Datapath::DeleteAdbPortAccessRule(const std::string& ifname) {
1222 firewall_->DeleteAcceptRules(patchpanel::ModifyPortRuleRequest::TCP,
1223 kAdbProxyTcpListenPort, ifname);
1224}
1225
Hugo Benichiaf9d8a72020-08-26 13:28:13 +09001226void Datapath::SetIfnameIndex(const std::string& ifname, int ifindex) {
1227 if_nametoindex_[ifname] = ifindex;
1228}
1229
1230int Datapath::FindIfIndex(const std::string& ifname) {
1231 uint32_t ifindex = if_nametoindex(ifname.c_str());
1232 if (ifindex > 0) {
1233 if_nametoindex_[ifname] = ifindex;
1234 return ifindex;
1235 }
1236
1237 const auto it = if_nametoindex_.find(ifname);
1238 if (it != if_nametoindex_.end())
1239 return it->second;
1240
1241 return 0;
1242}
1243
Garrick Evans3388a032020-03-24 11:25:55 +09001244} // namespace patchpanel