blob: aadd1df82e62e8465aa05ee3be64fb59335778f8 [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
18#include <base/files/scoped_file.h>
19#include <base/logging.h>
Garrick Evans54861622019-07-19 09:05:09 +090020#include <base/strings/string_number_conversions.h>
Garrick Evans3d97a392020-02-21 15:24:37 +090021#include "base/posix/eintr_wrapper.h"
Garrick Evans4f9f5572019-11-26 10:25:16 +090022#include <brillo/userdb_utils.h>
Garrick Evans54861622019-07-19 09:05:09 +090023
Garrick Evans3388a032020-03-24 11:25:55 +090024#include "patchpanel/net_util.h"
25#include "patchpanel/scoped_ns.h"
Garrick Evansc7ae82c2019-09-04 16:25:10 +090026
Garrick Evans3388a032020-03-24 11:25:55 +090027namespace patchpanel {
Garrick Evans54861622019-07-19 09:05:09 +090028
Garrick Evansc7ae82c2019-09-04 16:25:10 +090029namespace {
Hugo Benichi76675592020-04-08 14:29:57 +090030// TODO(hugobenichi) Consolidate this constant definition in a single place.
31constexpr pid_t kTestPID = -2;
Garrick Evansc7ae82c2019-09-04 16:25:10 +090032constexpr char kDefaultIfname[] = "vmtap%d";
33constexpr char kTunDev[] = "/dev/net/tun";
Hugo Benichie8758b52020-04-03 14:49:01 +090034
Garrick Evans8a067562020-05-11 12:47:30 +090035std::string PrefixIfname(const std::string& prefix, const std::string& ifname) {
36 std::string n = prefix + ifname;
Garrick Evans2f581a02020-05-11 10:43:35 +090037 if (n.length() < IFNAMSIZ)
38 return n;
Garrick Evans54861622019-07-19 09:05:09 +090039
Garrick Evans2f581a02020-05-11 10:43:35 +090040 // Best effort attempt to preserve the interface number, assuming it's the
41 // last char in the name.
42 auto c = ifname[ifname.length() - 1];
43 n.resize(IFNAMSIZ - 1);
44 n[n.length() - 1] = c;
45 return n;
Garrick Evans54861622019-07-19 09:05:09 +090046}
Garrick Evansf0ab7132019-06-18 14:50:42 +090047
Garrick Evans8a067562020-05-11 12:47:30 +090048} // namespace
49
50std::string ArcVethHostName(const std::string& ifname) {
51 return PrefixIfname("veth", ifname);
52}
53
54std::string ArcBridgeName(const std::string& ifname) {
55 return PrefixIfname("arc_", ifname);
56}
57
Garrick Evansf0ab7132019-06-18 14:50:42 +090058Datapath::Datapath(MinijailedProcessRunner* process_runner)
Garrick Evansc7ae82c2019-09-04 16:25:10 +090059 : Datapath(process_runner, ioctl) {}
60
61Datapath::Datapath(MinijailedProcessRunner* process_runner, ioctl_t ioctl_hook)
62 : process_runner_(process_runner), ioctl_(ioctl_hook) {
Garrick Evansf0ab7132019-06-18 14:50:42 +090063 CHECK(process_runner_);
64}
65
Garrick Evans260ff302019-07-25 11:22:50 +090066MinijailedProcessRunner& Datapath::runner() const {
67 return *process_runner_;
68}
69
Garrick Evans8a949dc2019-07-18 16:17:53 +090070bool Datapath::AddBridge(const std::string& ifname,
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090071 uint32_t ipv4_addr,
72 uint32_t ipv4_prefix_len) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090073 // Configure the persistent Chrome OS bridge interface with static IP.
Garrick Evans8e8e3472020-01-23 14:03:50 +090074 if (process_runner_->brctl("addbr", {ifname}) != 0) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090075 return false;
76 }
77
Garrick Evans6f4fa3a2020-02-10 16:15:09 +090078 if (process_runner_->ip(
79 "addr", "add",
80 {IPv4AddressToCidrString(ipv4_addr, ipv4_prefix_len), "brd",
81 IPv4AddressToString(Ipv4BroadcastAddr(ipv4_addr, ipv4_prefix_len)),
82 "dev", ifname}) != 0) {
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090083 RemoveBridge(ifname);
84 return false;
85 }
86
87 if (process_runner_->ip("link", "set", {ifname, "up"}) != 0) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090088 RemoveBridge(ifname);
89 return false;
90 }
91
92 // See nat.conf in chromeos-nat-init for the rest of the NAT setup rules.
Hugo Benichie8758b52020-04-03 14:49:01 +090093 if (!AddOutboundIPv4SNATMark(ifname)) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090094 RemoveBridge(ifname);
95 return false;
96 }
97
98 return true;
99}
100
101void Datapath::RemoveBridge(const std::string& ifname) {
Hugo Benichie8758b52020-04-03 14:49:01 +0900102 RemoveOutboundIPv4SNATMark(ifname);
Garrick Evans7a1a9ee2020-01-28 11:03:57 +0900103 process_runner_->ip("link", "set", {ifname, "down"});
Garrick Evans8e8e3472020-01-23 14:03:50 +0900104 process_runner_->brctl("delbr", {ifname});
Garrick Evans8a949dc2019-07-18 16:17:53 +0900105}
106
Garrick Evans621ed262019-11-13 12:28:43 +0900107bool Datapath::AddToBridge(const std::string& br_ifname,
108 const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900109 return (process_runner_->brctl("addif", {br_ifname, ifname}) == 0);
Garrick Evans621ed262019-11-13 12:28:43 +0900110}
111
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900112std::string Datapath::AddTAP(const std::string& name,
Garrick Evans621ed262019-11-13 12:28:43 +0900113 const MacAddress* mac_addr,
114 const SubnetAddress* ipv4_addr,
Garrick Evans4f9f5572019-11-26 10:25:16 +0900115 const std::string& user) {
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900116 base::ScopedFD dev(open(kTunDev, O_RDWR | O_NONBLOCK));
117 if (!dev.is_valid()) {
118 PLOG(ERROR) << "Failed to open " << kTunDev;
119 return "";
120 }
121
122 struct ifreq ifr;
123 memset(&ifr, 0, sizeof(ifr));
124 strncpy(ifr.ifr_name, name.empty() ? kDefaultIfname : name.c_str(),
125 sizeof(ifr.ifr_name));
126 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
127
128 // If a template was given as the name, ifr_name will be updated with the
129 // actual interface name.
130 if ((*ioctl_)(dev.get(), TUNSETIFF, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900131 PLOG(ERROR) << "Failed to create tap interface " << name;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900132 return "";
133 }
134 const char* ifname = ifr.ifr_name;
135
136 if ((*ioctl_)(dev.get(), TUNSETPERSIST, 1) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900137 PLOG(ERROR) << "Failed to persist the interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900138 return "";
139 }
140
Garrick Evans4f9f5572019-11-26 10:25:16 +0900141 if (!user.empty()) {
142 uid_t uid = -1;
143 if (!brillo::userdb::GetUserInfo(user, &uid, nullptr)) {
144 PLOG(ERROR) << "Unable to look up UID for " << user;
145 RemoveTAP(ifname);
146 return "";
147 }
148 if ((*ioctl_)(dev.get(), TUNSETOWNER, uid) != 0) {
149 PLOG(ERROR) << "Failed to set owner " << uid << " of tap interface "
150 << ifname;
151 RemoveTAP(ifname);
152 return "";
153 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900154 }
155
Hugo Benichib9b93fe2019-10-25 23:36:01 +0900156 // Create control socket for configuring the interface.
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900157 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
158 if (!sock.is_valid()) {
159 PLOG(ERROR) << "Failed to create control socket for tap interface "
Garrick Evans621ed262019-11-13 12:28:43 +0900160 << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900161 RemoveTAP(ifname);
162 return "";
163 }
164
Garrick Evans621ed262019-11-13 12:28:43 +0900165 if (ipv4_addr) {
166 struct sockaddr_in* addr =
167 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
168 addr->sin_family = AF_INET;
169 addr->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr->Address());
170 if ((*ioctl_)(sock.get(), SIOCSIFADDR, &ifr) != 0) {
171 PLOG(ERROR) << "Failed to set ip address for vmtap interface " << ifname
172 << " {" << ipv4_addr->ToCidrString() << "}";
173 RemoveTAP(ifname);
174 return "";
175 }
176
177 struct sockaddr_in* netmask =
178 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_netmask);
179 netmask->sin_family = AF_INET;
180 netmask->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr->Netmask());
181 if ((*ioctl_)(sock.get(), SIOCSIFNETMASK, &ifr) != 0) {
182 PLOG(ERROR) << "Failed to set netmask for vmtap interface " << ifname
183 << " {" << ipv4_addr->ToCidrString() << "}";
184 RemoveTAP(ifname);
185 return "";
186 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900187 }
188
Garrick Evans621ed262019-11-13 12:28:43 +0900189 if (mac_addr) {
190 struct sockaddr* hwaddr = &ifr.ifr_hwaddr;
191 hwaddr->sa_family = ARPHRD_ETHER;
192 memcpy(&hwaddr->sa_data, mac_addr, sizeof(*mac_addr));
193 if ((*ioctl_)(sock.get(), SIOCSIFHWADDR, &ifr) != 0) {
194 PLOG(ERROR) << "Failed to set mac address for vmtap interface " << ifname
195 << " {" << MacAddressToString(*mac_addr) << "}";
196 RemoveTAP(ifname);
197 return "";
198 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900199 }
200
201 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900202 PLOG(ERROR) << "Failed to get flags for tap interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900203 RemoveTAP(ifname);
204 return "";
205 }
206
207 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
208 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900209 PLOG(ERROR) << "Failed to enable tap interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900210 RemoveTAP(ifname);
211 return "";
212 }
213
214 return ifname;
215}
216
217void Datapath::RemoveTAP(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900218 process_runner_->ip("tuntap", "del", {ifname, "mode", "tap"});
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900219}
220
Hugo Benichi76675592020-04-08 14:29:57 +0900221bool Datapath::ConnectVethPair(pid_t pid,
222 const std::string& veth_ifname,
223 const std::string& peer_ifname,
224 const MacAddress& remote_mac_addr,
225 uint32_t remote_ipv4_addr,
226 uint32_t remote_ipv4_prefix_len,
227 bool remote_multicast_flag) {
228 // Set up the virtual pair inside the remote namespace.
229 {
230 ScopedNS ns(pid);
231 if (!ns.IsValid() && pid != kTestPID) {
232 LOG(ERROR)
233 << "Cannot create virtual link -- invalid container namespace?";
234 return false;
235 }
236
237 if (!AddVirtualInterfacePair(veth_ifname, peer_ifname)) {
238 LOG(ERROR) << "Failed to create veth pair " << veth_ifname << ","
239 << peer_ifname;
240 return false;
241 }
242
243 if (!ConfigureInterface(peer_ifname, remote_mac_addr, remote_ipv4_addr,
244 remote_ipv4_prefix_len, true /* link up */,
245 remote_multicast_flag)) {
246 LOG(ERROR) << "Failed to configure interface " << peer_ifname;
247 RemoveInterface(peer_ifname);
248 return false;
249 }
250 }
251
252 // Now pull the local end out into the local namespace.
253 if (runner().RestoreDefaultNamespace(veth_ifname, pid) != 0) {
254 LOG(ERROR) << "Failed to prepare interface " << veth_ifname;
255 {
256 ScopedNS ns(pid);
257 if (ns.IsValid()) {
258 RemoveInterface(peer_ifname);
259 } else {
260 LOG(ERROR) << "Failed to re-enter container namespace pid " << pid;
261 }
262 }
263 return false;
264 }
265 if (!ToggleInterface(veth_ifname, true /*up*/)) {
266 LOG(ERROR) << "Failed to bring up interface " << veth_ifname;
267 RemoveInterface(veth_ifname);
268 return false;
269 }
270 return true;
271}
272
Garrick Evans2470caa2020-03-04 14:15:41 +0900273bool Datapath::AddVirtualInterfacePair(const std::string& veth_ifname,
274 const std::string& peer_ifname) {
275 return process_runner_->ip(
276 "link", "add",
277 {veth_ifname, "type", "veth", "peer", "name", peer_ifname}) == 0;
278}
Garrick Evans54861622019-07-19 09:05:09 +0900279
Garrick Evans2470caa2020-03-04 14:15:41 +0900280bool Datapath::ToggleInterface(const std::string& ifname, bool up) {
281 const std::string link = up ? "up" : "down";
282 return process_runner_->ip("link", "set", {ifname, link}) == 0;
283}
Garrick Evans54861622019-07-19 09:05:09 +0900284
Garrick Evans2470caa2020-03-04 14:15:41 +0900285bool Datapath::ConfigureInterface(const std::string& ifname,
286 const MacAddress& mac_addr,
287 uint32_t ipv4_addr,
288 uint32_t ipv4_prefix_len,
289 bool up,
290 bool enable_multicast) {
291 const std::string link = up ? "up" : "down";
292 const std::string multicast = enable_multicast ? "on" : "off";
293 return (process_runner_->ip(
294 "addr", "add",
295 {IPv4AddressToCidrString(ipv4_addr, ipv4_prefix_len), "brd",
296 IPv4AddressToString(
297 Ipv4BroadcastAddr(ipv4_addr, ipv4_prefix_len)),
298 "dev", ifname}) == 0) &&
299 (process_runner_->ip("link", "set",
300 {
301 "dev",
302 ifname,
303 link,
304 "addr",
305 MacAddressToString(mac_addr),
306 "multicast",
307 multicast,
308 }) == 0);
Garrick Evans54861622019-07-19 09:05:09 +0900309}
310
311void Datapath::RemoveInterface(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900312 process_runner_->ip("link", "delete", {ifname}, false /*log_failures*/);
Garrick Evans54861622019-07-19 09:05:09 +0900313}
314
Garrick Evansf0ab7132019-06-18 14:50:42 +0900315bool Datapath::AddLegacyIPv4DNAT(const std::string& ipv4_addr) {
316 // Forward "unclaimed" packets to Android to allow inbound connections
317 // from devices on the LAN.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900318 if (process_runner_->iptables("nat", {"-N", "dnat_arc", "-w"}) != 0)
Garrick Evansf0ab7132019-06-18 14:50:42 +0900319 return false;
320
Garrick Evans8e8e3472020-01-23 14:03:50 +0900321 if (process_runner_->iptables("nat", {"-A", "dnat_arc", "-j", "DNAT",
322 "--to-destination", ipv4_addr, "-w"}) !=
Garrick Evansf0ab7132019-06-18 14:50:42 +0900323 0) {
Garrick Evans54861622019-07-19 09:05:09 +0900324 RemoveLegacyIPv4DNAT();
Garrick Evansf0ab7132019-06-18 14:50:42 +0900325 return false;
326 }
327
328 // This chain is dynamically updated whenever the default interface
329 // changes.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900330 if (process_runner_->iptables("nat", {"-N", "try_arc", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900331 RemoveLegacyIPv4DNAT();
332 return false;
333 }
334
Garrick Evans8e8e3472020-01-23 14:03:50 +0900335 if (process_runner_->iptables(
336 "nat", {"-A", "PREROUTING", "-m", "socket", "--nowildcard", "-j",
337 "ACCEPT", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900338 RemoveLegacyIPv4DNAT();
339 return false;
340 }
341
Garrick Evans8e8e3472020-01-23 14:03:50 +0900342 if (process_runner_->iptables("nat", {"-A", "PREROUTING", "-p", "tcp", "-j",
343 "try_arc", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900344 RemoveLegacyIPv4DNAT();
345 return false;
346 }
347
Garrick Evans8e8e3472020-01-23 14:03:50 +0900348 if (process_runner_->iptables("nat", {"-A", "PREROUTING", "-p", "udp", "-j",
349 "try_arc", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900350 RemoveLegacyIPv4DNAT();
351 return false;
352 }
353
354 return true;
355}
356
357void Datapath::RemoveLegacyIPv4DNAT() {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900358 process_runner_->iptables(
359 "nat", {"-D", "PREROUTING", "-p", "udp", "-j", "try_arc", "-w"});
360 process_runner_->iptables(
361 "nat", {"-D", "PREROUTING", "-p", "tcp", "-j", "try_arc", "-w"});
362 process_runner_->iptables("nat", {"-D", "PREROUTING", "-m", "socket",
363 "--nowildcard", "-j", "ACCEPT", "-w"});
364 process_runner_->iptables("nat", {"-F", "try_arc", "-w"});
365 process_runner_->iptables("nat", {"-X", "try_arc", "-w"});
366 process_runner_->iptables("nat", {"-F", "dnat_arc", "-w"});
367 process_runner_->iptables("nat", {"-X", "dnat_arc", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900368}
369
Garrick Evans54861622019-07-19 09:05:09 +0900370bool Datapath::AddLegacyIPv4InboundDNAT(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900371 return (process_runner_->iptables("nat", {"-A", "try_arc", "-i", ifname, "-j",
372 "dnat_arc", "-w"}) != 0);
Garrick Evans54861622019-07-19 09:05:09 +0900373}
374
375void Datapath::RemoveLegacyIPv4InboundDNAT() {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900376 process_runner_->iptables("nat", {"-F", "try_arc", "-w"});
Garrick Evans54861622019-07-19 09:05:09 +0900377}
378
Garrick Evansf0ab7132019-06-18 14:50:42 +0900379bool Datapath::AddInboundIPv4DNAT(const std::string& ifname,
380 const std::string& ipv4_addr) {
381 // Direct ingress IP traffic to existing sockets.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900382 if (process_runner_->iptables(
383 "nat", {"-A", "PREROUTING", "-i", ifname, "-m", "socket",
384 "--nowildcard", "-j", "ACCEPT", "-w"}) != 0)
Garrick Evansf0ab7132019-06-18 14:50:42 +0900385 return false;
386
387 // Direct ingress TCP & UDP traffic to ARC interface for new connections.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900388 if (process_runner_->iptables(
389 "nat", {"-A", "PREROUTING", "-i", ifname, "-p", "tcp", "-j", "DNAT",
390 "--to-destination", ipv4_addr, "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900391 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
392 return false;
393 }
Garrick Evans8e8e3472020-01-23 14:03:50 +0900394 if (process_runner_->iptables(
395 "nat", {"-A", "PREROUTING", "-i", ifname, "-p", "udp", "-j", "DNAT",
396 "--to-destination", ipv4_addr, "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900397 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
398 return false;
399 }
400
401 return true;
402}
403
404void Datapath::RemoveInboundIPv4DNAT(const std::string& ifname,
405 const std::string& ipv4_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900406 process_runner_->iptables(
407 "nat", {"-D", "PREROUTING", "-i", ifname, "-p", "udp", "-j", "DNAT",
408 "--to-destination", ipv4_addr, "-w"});
409 process_runner_->iptables(
410 "nat", {"-D", "PREROUTING", "-i", ifname, "-p", "tcp", "-j", "DNAT",
411 "--to-destination", ipv4_addr, "-w"});
412 process_runner_->iptables(
413 "nat", {"-D", "PREROUTING", "-i", ifname, "-m", "socket", "--nowildcard",
414 "-j", "ACCEPT", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900415}
416
417bool Datapath::AddOutboundIPv4(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900418 return process_runner_->iptables("filter", {"-A", "FORWARD", "-o", ifname,
419 "-j", "ACCEPT", "-w"}) == 0;
Garrick Evansf0ab7132019-06-18 14:50:42 +0900420}
421
422void Datapath::RemoveOutboundIPv4(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900423 process_runner_->iptables(
424 "filter", {"-D", "FORWARD", "-o", ifname, "-j", "ACCEPT", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900425}
426
Garrick Evansd291af62020-05-25 10:39:06 +0900427bool Datapath::AddSNATMarkRules() {
428 if (process_runner_->iptables(
429 "filter", {"-A", "FORWARD", "-m", "mark", "--mark", "1", "-j",
430 "ACCEPT", "-w"}) != 0) {
431 return false;
432 }
433 if (process_runner_->iptables(
434 "nat", {"-A", "POSTROUTING", "-m", "mark", "--mark", "1", "-j",
435 "MASQUERADE", "-w"}) != 0) {
436 RemoveSNATMarkRules();
437 return false;
438 }
439 return true;
440}
441
442void Datapath::RemoveSNATMarkRules() {
443 process_runner_->iptables("nat", {"-D", "POSTROUTING", "-m", "mark", "--mark",
444 "1", "-j", "MASQUERADE", "-w"});
445 process_runner_->iptables("filter", {"-D", "FORWARD", "-m", "mark", "--mark",
446 "1", "-j", "ACCEPT", "-w"});
447}
448
Garrick Evansff6e37f2020-05-25 10:54:47 +0900449bool Datapath::AddInterfaceSNAT(const std::string& ifname) {
450 return process_runner_->iptables("nat", {"-A", "POSTROUTING", "-o", ifname,
451 "-j", "MASQUERADE", "-w"}) == 0;
452}
453
454void Datapath::RemoveInterfaceSNAT(const std::string& ifname) {
455 process_runner_->iptables(
456 "nat", {"-D", "POSTROUTING", "-o", ifname, "-j", "MASQUERADE", "-w"});
457}
458
Hugo Benichie8758b52020-04-03 14:49:01 +0900459bool Datapath::AddOutboundIPv4SNATMark(const std::string& ifname) {
460 return process_runner_->iptables(
461 "mangle", {"-A", "PREROUTING", "-i", ifname, "-j", "MARK",
462 "--set-mark", "1", "-w"}) == 0;
463}
464
465void Datapath::RemoveOutboundIPv4SNATMark(const std::string& ifname) {
466 process_runner_->iptables("mangle", {"-D", "PREROUTING", "-i", ifname, "-j",
467 "MARK", "--set-mark", "1", "-w"});
468}
469
Garrick Evansd291af62020-05-25 10:39:06 +0900470bool Datapath::AddForwardEstablishedRule() {
471 return process_runner_->iptables(
472 "filter", {"-A", "FORWARD", "-m", "state", "--state",
473 "ESTABLISHED,RELATED", "-j", "ACCEPT", "-w"}) == 0;
474}
475
476void Datapath::RemoveForwardEstablishedRule() {
477 process_runner_->iptables("filter",
478 {"-D", "FORWARD", "-m", "state", "--state",
479 "ESTABLISHED,RELATED", "-j", "ACCEPT", "-w"});
480}
481
Garrick Evans664a82f2019-12-17 12:18:05 +0900482bool Datapath::MaskInterfaceFlags(const std::string& ifname,
483 uint16_t on,
484 uint16_t off) {
Taoyu Li90c13912019-11-26 17:56:54 +0900485 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
486 if (!sock.is_valid()) {
487 PLOG(ERROR) << "Failed to create control socket";
488 return false;
489 }
490 ifreq ifr;
491 snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname.c_str());
492 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) < 0) {
493 PLOG(WARNING) << "ioctl() failed to get interface flag on " << ifname;
494 return false;
495 }
Garrick Evans664a82f2019-12-17 12:18:05 +0900496 ifr.ifr_flags |= on;
497 ifr.ifr_flags &= ~off;
Taoyu Li90c13912019-11-26 17:56:54 +0900498 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) < 0) {
Garrick Evans664a82f2019-12-17 12:18:05 +0900499 PLOG(WARNING) << "ioctl() failed to set flag 0x" << std::hex << on
500 << " unset flag 0x" << std::hex << off << " on " << ifname;
Taoyu Li90c13912019-11-26 17:56:54 +0900501 return false;
502 }
503 return true;
504}
505
Garrick Evans260ff302019-07-25 11:22:50 +0900506bool Datapath::AddIPv6HostRoute(const std::string& ifname,
507 const std::string& ipv6_addr,
508 int ipv6_prefix_len) {
509 std::string ipv6_addr_cidr =
510 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
511
Garrick Evans8e8e3472020-01-23 14:03:50 +0900512 return process_runner_->ip6("route", "replace",
513 {ipv6_addr_cidr, "dev", ifname}) == 0;
Garrick Evans260ff302019-07-25 11:22:50 +0900514}
515
516void Datapath::RemoveIPv6HostRoute(const std::string& ifname,
517 const std::string& ipv6_addr,
518 int ipv6_prefix_len) {
519 std::string ipv6_addr_cidr =
520 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
521
Garrick Evans8e8e3472020-01-23 14:03:50 +0900522 process_runner_->ip6("route", "del", {ipv6_addr_cidr, "dev", ifname});
Garrick Evans260ff302019-07-25 11:22:50 +0900523}
524
525bool Datapath::AddIPv6Neighbor(const std::string& ifname,
526 const std::string& ipv6_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900527 return process_runner_->ip6("neigh", "add",
528 {"proxy", ipv6_addr, "dev", ifname}) == 0;
Garrick Evans260ff302019-07-25 11:22:50 +0900529}
530
531void Datapath::RemoveIPv6Neighbor(const std::string& ifname,
532 const std::string& ipv6_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900533 process_runner_->ip6("neigh", "del", {"proxy", ipv6_addr, "dev", ifname});
Garrick Evans260ff302019-07-25 11:22:50 +0900534}
535
536bool Datapath::AddIPv6Forwarding(const std::string& ifname1,
537 const std::string& ifname2) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900538 if (process_runner_->ip6tables(
539 "filter",
540 {"-C", "FORWARD", "-i", ifname1, "-o", ifname2, "-j", "ACCEPT", "-w"},
541 false /*log_failures*/) != 0 &&
542 process_runner_->ip6tables(
543 "filter", {"-A", "FORWARD", "-i", ifname1, "-o", ifname2, "-j",
544 "ACCEPT", "-w"}) != 0) {
Garrick Evans260ff302019-07-25 11:22:50 +0900545 return false;
546 }
547
Garrick Evans8e8e3472020-01-23 14:03:50 +0900548 if (process_runner_->ip6tables(
549 "filter",
550 {"-C", "FORWARD", "-i", ifname2, "-o", ifname1, "-j", "ACCEPT", "-w"},
551 false /*log_failures*/) != 0 &&
552 process_runner_->ip6tables(
553 "filter", {"-A", "FORWARD", "-i", ifname2, "-o", ifname1, "-j",
554 "ACCEPT", "-w"}) != 0) {
Garrick Evans260ff302019-07-25 11:22:50 +0900555 RemoveIPv6Forwarding(ifname1, ifname2);
556 return false;
557 }
558
559 return true;
560}
561
562void Datapath::RemoveIPv6Forwarding(const std::string& ifname1,
563 const std::string& ifname2) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900564 process_runner_->ip6tables("filter", {"-D", "FORWARD", "-i", ifname1, "-o",
565 ifname2, "-j", "ACCEPT", "-w"});
Garrick Evans260ff302019-07-25 11:22:50 +0900566
Garrick Evans8e8e3472020-01-23 14:03:50 +0900567 process_runner_->ip6tables("filter", {"-D", "FORWARD", "-i", ifname2, "-o",
568 ifname1, "-j", "ACCEPT", "-w"});
Garrick Evans260ff302019-07-25 11:22:50 +0900569}
570
Garrick Evans3d97a392020-02-21 15:24:37 +0900571bool Datapath::AddIPv4Route(uint32_t gateway_addr,
572 uint32_t addr,
573 uint32_t netmask) {
574 struct rtentry route;
575 memset(&route, 0, sizeof(route));
Hugo Benichie8758b52020-04-03 14:49:01 +0900576 SetSockaddrIn(&route.rt_gateway, gateway_addr);
577 SetSockaddrIn(&route.rt_dst, addr & netmask);
578 SetSockaddrIn(&route.rt_genmask, netmask);
Garrick Evans3d97a392020-02-21 15:24:37 +0900579 route.rt_flags = RTF_UP | RTF_GATEWAY;
Hugo Benichie8758b52020-04-03 14:49:01 +0900580 return ModifyRtentry(SIOCADDRT, &route);
581}
Garrick Evans3d97a392020-02-21 15:24:37 +0900582
Hugo Benichie8758b52020-04-03 14:49:01 +0900583bool Datapath::DeleteIPv4Route(uint32_t gateway_addr,
584 uint32_t addr,
585 uint32_t netmask) {
586 struct rtentry route;
587 memset(&route, 0, sizeof(route));
588 SetSockaddrIn(&route.rt_gateway, gateway_addr);
589 SetSockaddrIn(&route.rt_dst, addr & netmask);
590 SetSockaddrIn(&route.rt_genmask, netmask);
591 route.rt_flags = RTF_UP | RTF_GATEWAY;
592 return ModifyRtentry(SIOCDELRT, &route);
593}
594
595bool Datapath::AddIPv4Route(const std::string& ifname,
596 uint32_t addr,
597 uint32_t netmask) {
598 struct rtentry route;
599 memset(&route, 0, sizeof(route));
600 SetSockaddrIn(&route.rt_dst, addr & netmask);
601 SetSockaddrIn(&route.rt_genmask, netmask);
602 char rt_dev[IFNAMSIZ];
603 strncpy(rt_dev, ifname.c_str(), IFNAMSIZ);
604 rt_dev[IFNAMSIZ - 1] = '\0';
605 route.rt_dev = rt_dev;
606 route.rt_flags = RTF_UP | RTF_GATEWAY;
607 return ModifyRtentry(SIOCADDRT, &route);
608}
609
610bool Datapath::DeleteIPv4Route(const std::string& ifname,
611 uint32_t addr,
612 uint32_t netmask) {
613 struct rtentry route;
614 memset(&route, 0, sizeof(route));
615 SetSockaddrIn(&route.rt_dst, addr & netmask);
616 SetSockaddrIn(&route.rt_genmask, netmask);
617 char rt_dev[IFNAMSIZ];
618 strncpy(rt_dev, ifname.c_str(), IFNAMSIZ);
619 rt_dev[IFNAMSIZ - 1] = '\0';
620 route.rt_dev = rt_dev;
621 route.rt_flags = RTF_UP | RTF_GATEWAY;
622 return ModifyRtentry(SIOCDELRT, &route);
623}
624
625bool Datapath::ModifyRtentry(unsigned long op, struct rtentry* route) {
626 DCHECK(route);
627 if (op != SIOCADDRT && op != SIOCDELRT) {
628 LOG(ERROR) << "Invalid operation " << op << " for rtentry " << route;
Garrick Evans3d97a392020-02-21 15:24:37 +0900629 return false;
630 }
Hugo Benichie8758b52020-04-03 14:49:01 +0900631 base::ScopedFD fd(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
632 if (!fd.is_valid()) {
633 PLOG(ERROR) << "Failed to create socket for adding rtentry " << route;
634 return false;
635 }
636 if (HANDLE_EINTR(ioctl_(fd.get(), op, route)) != 0) {
637 std::string opname = op == SIOCADDRT ? "add" : "delete";
638 PLOG(ERROR) << "Failed to " << opname << " rtentry " << route;
Garrick Evans3d97a392020-02-21 15:24:37 +0900639 return false;
640 }
641 return true;
642}
643
Garrick Evans3388a032020-03-24 11:25:55 +0900644} // namespace patchpanel