blob: 3f2c5f43ef13030c5e6a351deb9c72c79907a0a2 [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 Evansc7ae82c2019-09-04 16:25:10 +090035} // namespace
Garrick Evans54861622019-07-19 09:05:09 +090036
Garrick Evans2f581a02020-05-11 10:43:35 +090037std::string ArcVethHostName(const std::string& ifname) {
38 std::string n = "veth" + ifname;
39 if (n.length() < IFNAMSIZ)
40 return n;
Garrick Evans54861622019-07-19 09:05:09 +090041
Garrick Evans2f581a02020-05-11 10:43:35 +090042 // Best effort attempt to preserve the interface number, assuming it's the
43 // last char in the name.
44 auto c = ifname[ifname.length() - 1];
45 n.resize(IFNAMSIZ - 1);
46 n[n.length() - 1] = c;
47 return n;
Garrick Evans54861622019-07-19 09:05:09 +090048}
Garrick Evansf0ab7132019-06-18 14:50:42 +090049
50Datapath::Datapath(MinijailedProcessRunner* process_runner)
Garrick Evansc7ae82c2019-09-04 16:25:10 +090051 : Datapath(process_runner, ioctl) {}
52
53Datapath::Datapath(MinijailedProcessRunner* process_runner, ioctl_t ioctl_hook)
54 : process_runner_(process_runner), ioctl_(ioctl_hook) {
Garrick Evansf0ab7132019-06-18 14:50:42 +090055 CHECK(process_runner_);
56}
57
Garrick Evans260ff302019-07-25 11:22:50 +090058MinijailedProcessRunner& Datapath::runner() const {
59 return *process_runner_;
60}
61
Garrick Evans8a949dc2019-07-18 16:17:53 +090062bool Datapath::AddBridge(const std::string& ifname,
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090063 uint32_t ipv4_addr,
64 uint32_t ipv4_prefix_len) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090065 // Configure the persistent Chrome OS bridge interface with static IP.
Garrick Evans8e8e3472020-01-23 14:03:50 +090066 if (process_runner_->brctl("addbr", {ifname}) != 0) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090067 return false;
68 }
69
Garrick Evans6f4fa3a2020-02-10 16:15:09 +090070 if (process_runner_->ip(
71 "addr", "add",
72 {IPv4AddressToCidrString(ipv4_addr, ipv4_prefix_len), "brd",
73 IPv4AddressToString(Ipv4BroadcastAddr(ipv4_addr, ipv4_prefix_len)),
74 "dev", ifname}) != 0) {
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090075 RemoveBridge(ifname);
76 return false;
77 }
78
79 if (process_runner_->ip("link", "set", {ifname, "up"}) != 0) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090080 RemoveBridge(ifname);
81 return false;
82 }
83
84 // See nat.conf in chromeos-nat-init for the rest of the NAT setup rules.
Hugo Benichie8758b52020-04-03 14:49:01 +090085 if (!AddOutboundIPv4SNATMark(ifname)) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090086 RemoveBridge(ifname);
87 return false;
88 }
89
90 return true;
91}
92
93void Datapath::RemoveBridge(const std::string& ifname) {
Hugo Benichie8758b52020-04-03 14:49:01 +090094 RemoveOutboundIPv4SNATMark(ifname);
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090095 process_runner_->ip("link", "set", {ifname, "down"});
Garrick Evans8e8e3472020-01-23 14:03:50 +090096 process_runner_->brctl("delbr", {ifname});
Garrick Evans8a949dc2019-07-18 16:17:53 +090097}
98
Garrick Evans621ed262019-11-13 12:28:43 +090099bool Datapath::AddToBridge(const std::string& br_ifname,
100 const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900101 return (process_runner_->brctl("addif", {br_ifname, ifname}) == 0);
Garrick Evans621ed262019-11-13 12:28:43 +0900102}
103
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900104std::string Datapath::AddTAP(const std::string& name,
Garrick Evans621ed262019-11-13 12:28:43 +0900105 const MacAddress* mac_addr,
106 const SubnetAddress* ipv4_addr,
Garrick Evans4f9f5572019-11-26 10:25:16 +0900107 const std::string& user) {
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900108 base::ScopedFD dev(open(kTunDev, O_RDWR | O_NONBLOCK));
109 if (!dev.is_valid()) {
110 PLOG(ERROR) << "Failed to open " << kTunDev;
111 return "";
112 }
113
114 struct ifreq ifr;
115 memset(&ifr, 0, sizeof(ifr));
116 strncpy(ifr.ifr_name, name.empty() ? kDefaultIfname : name.c_str(),
117 sizeof(ifr.ifr_name));
118 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
119
120 // If a template was given as the name, ifr_name will be updated with the
121 // actual interface name.
122 if ((*ioctl_)(dev.get(), TUNSETIFF, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900123 PLOG(ERROR) << "Failed to create tap interface " << name;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900124 return "";
125 }
126 const char* ifname = ifr.ifr_name;
127
128 if ((*ioctl_)(dev.get(), TUNSETPERSIST, 1) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900129 PLOG(ERROR) << "Failed to persist the interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900130 return "";
131 }
132
Garrick Evans4f9f5572019-11-26 10:25:16 +0900133 if (!user.empty()) {
134 uid_t uid = -1;
135 if (!brillo::userdb::GetUserInfo(user, &uid, nullptr)) {
136 PLOG(ERROR) << "Unable to look up UID for " << user;
137 RemoveTAP(ifname);
138 return "";
139 }
140 if ((*ioctl_)(dev.get(), TUNSETOWNER, uid) != 0) {
141 PLOG(ERROR) << "Failed to set owner " << uid << " of tap interface "
142 << ifname;
143 RemoveTAP(ifname);
144 return "";
145 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900146 }
147
Hugo Benichib9b93fe2019-10-25 23:36:01 +0900148 // Create control socket for configuring the interface.
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900149 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
150 if (!sock.is_valid()) {
151 PLOG(ERROR) << "Failed to create control socket for tap interface "
Garrick Evans621ed262019-11-13 12:28:43 +0900152 << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900153 RemoveTAP(ifname);
154 return "";
155 }
156
Garrick Evans621ed262019-11-13 12:28:43 +0900157 if (ipv4_addr) {
158 struct sockaddr_in* addr =
159 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
160 addr->sin_family = AF_INET;
161 addr->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr->Address());
162 if ((*ioctl_)(sock.get(), SIOCSIFADDR, &ifr) != 0) {
163 PLOG(ERROR) << "Failed to set ip address for vmtap interface " << ifname
164 << " {" << ipv4_addr->ToCidrString() << "}";
165 RemoveTAP(ifname);
166 return "";
167 }
168
169 struct sockaddr_in* netmask =
170 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_netmask);
171 netmask->sin_family = AF_INET;
172 netmask->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr->Netmask());
173 if ((*ioctl_)(sock.get(), SIOCSIFNETMASK, &ifr) != 0) {
174 PLOG(ERROR) << "Failed to set netmask for vmtap interface " << ifname
175 << " {" << ipv4_addr->ToCidrString() << "}";
176 RemoveTAP(ifname);
177 return "";
178 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900179 }
180
Garrick Evans621ed262019-11-13 12:28:43 +0900181 if (mac_addr) {
182 struct sockaddr* hwaddr = &ifr.ifr_hwaddr;
183 hwaddr->sa_family = ARPHRD_ETHER;
184 memcpy(&hwaddr->sa_data, mac_addr, sizeof(*mac_addr));
185 if ((*ioctl_)(sock.get(), SIOCSIFHWADDR, &ifr) != 0) {
186 PLOG(ERROR) << "Failed to set mac address for vmtap interface " << ifname
187 << " {" << MacAddressToString(*mac_addr) << "}";
188 RemoveTAP(ifname);
189 return "";
190 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900191 }
192
193 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900194 PLOG(ERROR) << "Failed to get flags for tap interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900195 RemoveTAP(ifname);
196 return "";
197 }
198
199 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
200 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900201 PLOG(ERROR) << "Failed to enable tap interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900202 RemoveTAP(ifname);
203 return "";
204 }
205
206 return ifname;
207}
208
209void Datapath::RemoveTAP(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900210 process_runner_->ip("tuntap", "del", {ifname, "mode", "tap"});
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900211}
212
Hugo Benichi76675592020-04-08 14:29:57 +0900213bool Datapath::ConnectVethPair(pid_t pid,
214 const std::string& veth_ifname,
215 const std::string& peer_ifname,
216 const MacAddress& remote_mac_addr,
217 uint32_t remote_ipv4_addr,
218 uint32_t remote_ipv4_prefix_len,
219 bool remote_multicast_flag) {
220 // Set up the virtual pair inside the remote namespace.
221 {
222 ScopedNS ns(pid);
223 if (!ns.IsValid() && pid != kTestPID) {
224 LOG(ERROR)
225 << "Cannot create virtual link -- invalid container namespace?";
226 return false;
227 }
228
229 if (!AddVirtualInterfacePair(veth_ifname, peer_ifname)) {
230 LOG(ERROR) << "Failed to create veth pair " << veth_ifname << ","
231 << peer_ifname;
232 return false;
233 }
234
235 if (!ConfigureInterface(peer_ifname, remote_mac_addr, remote_ipv4_addr,
236 remote_ipv4_prefix_len, true /* link up */,
237 remote_multicast_flag)) {
238 LOG(ERROR) << "Failed to configure interface " << peer_ifname;
239 RemoveInterface(peer_ifname);
240 return false;
241 }
242 }
243
244 // Now pull the local end out into the local namespace.
245 if (runner().RestoreDefaultNamespace(veth_ifname, pid) != 0) {
246 LOG(ERROR) << "Failed to prepare interface " << veth_ifname;
247 {
248 ScopedNS ns(pid);
249 if (ns.IsValid()) {
250 RemoveInterface(peer_ifname);
251 } else {
252 LOG(ERROR) << "Failed to re-enter container namespace pid " << pid;
253 }
254 }
255 return false;
256 }
257 if (!ToggleInterface(veth_ifname, true /*up*/)) {
258 LOG(ERROR) << "Failed to bring up interface " << veth_ifname;
259 RemoveInterface(veth_ifname);
260 return false;
261 }
262 return true;
263}
264
Garrick Evans2470caa2020-03-04 14:15:41 +0900265bool Datapath::AddVirtualInterfacePair(const std::string& veth_ifname,
266 const std::string& peer_ifname) {
267 return process_runner_->ip(
268 "link", "add",
269 {veth_ifname, "type", "veth", "peer", "name", peer_ifname}) == 0;
270}
Garrick Evans54861622019-07-19 09:05:09 +0900271
Garrick Evans2470caa2020-03-04 14:15:41 +0900272bool Datapath::ToggleInterface(const std::string& ifname, bool up) {
273 const std::string link = up ? "up" : "down";
274 return process_runner_->ip("link", "set", {ifname, link}) == 0;
275}
Garrick Evans54861622019-07-19 09:05:09 +0900276
Garrick Evans2470caa2020-03-04 14:15:41 +0900277bool Datapath::ConfigureInterface(const std::string& ifname,
278 const MacAddress& mac_addr,
279 uint32_t ipv4_addr,
280 uint32_t ipv4_prefix_len,
281 bool up,
282 bool enable_multicast) {
283 const std::string link = up ? "up" : "down";
284 const std::string multicast = enable_multicast ? "on" : "off";
285 return (process_runner_->ip(
286 "addr", "add",
287 {IPv4AddressToCidrString(ipv4_addr, ipv4_prefix_len), "brd",
288 IPv4AddressToString(
289 Ipv4BroadcastAddr(ipv4_addr, ipv4_prefix_len)),
290 "dev", ifname}) == 0) &&
291 (process_runner_->ip("link", "set",
292 {
293 "dev",
294 ifname,
295 link,
296 "addr",
297 MacAddressToString(mac_addr),
298 "multicast",
299 multicast,
300 }) == 0);
Garrick Evans54861622019-07-19 09:05:09 +0900301}
302
303void Datapath::RemoveInterface(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900304 process_runner_->ip("link", "delete", {ifname}, false /*log_failures*/);
Garrick Evans54861622019-07-19 09:05:09 +0900305}
306
Garrick Evansf0ab7132019-06-18 14:50:42 +0900307bool Datapath::AddLegacyIPv4DNAT(const std::string& ipv4_addr) {
308 // Forward "unclaimed" packets to Android to allow inbound connections
309 // from devices on the LAN.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900310 if (process_runner_->iptables("nat", {"-N", "dnat_arc", "-w"}) != 0)
Garrick Evansf0ab7132019-06-18 14:50:42 +0900311 return false;
312
Garrick Evans8e8e3472020-01-23 14:03:50 +0900313 if (process_runner_->iptables("nat", {"-A", "dnat_arc", "-j", "DNAT",
314 "--to-destination", ipv4_addr, "-w"}) !=
Garrick Evansf0ab7132019-06-18 14:50:42 +0900315 0) {
Garrick Evans54861622019-07-19 09:05:09 +0900316 RemoveLegacyIPv4DNAT();
Garrick Evansf0ab7132019-06-18 14:50:42 +0900317 return false;
318 }
319
320 // This chain is dynamically updated whenever the default interface
321 // changes.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900322 if (process_runner_->iptables("nat", {"-N", "try_arc", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900323 RemoveLegacyIPv4DNAT();
324 return false;
325 }
326
Garrick Evans8e8e3472020-01-23 14:03:50 +0900327 if (process_runner_->iptables(
328 "nat", {"-A", "PREROUTING", "-m", "socket", "--nowildcard", "-j",
329 "ACCEPT", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900330 RemoveLegacyIPv4DNAT();
331 return false;
332 }
333
Garrick Evans8e8e3472020-01-23 14:03:50 +0900334 if (process_runner_->iptables("nat", {"-A", "PREROUTING", "-p", "tcp", "-j",
335 "try_arc", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900336 RemoveLegacyIPv4DNAT();
337 return false;
338 }
339
Garrick Evans8e8e3472020-01-23 14:03:50 +0900340 if (process_runner_->iptables("nat", {"-A", "PREROUTING", "-p", "udp", "-j",
341 "try_arc", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900342 RemoveLegacyIPv4DNAT();
343 return false;
344 }
345
346 return true;
347}
348
349void Datapath::RemoveLegacyIPv4DNAT() {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900350 process_runner_->iptables(
351 "nat", {"-D", "PREROUTING", "-p", "udp", "-j", "try_arc", "-w"});
352 process_runner_->iptables(
353 "nat", {"-D", "PREROUTING", "-p", "tcp", "-j", "try_arc", "-w"});
354 process_runner_->iptables("nat", {"-D", "PREROUTING", "-m", "socket",
355 "--nowildcard", "-j", "ACCEPT", "-w"});
356 process_runner_->iptables("nat", {"-F", "try_arc", "-w"});
357 process_runner_->iptables("nat", {"-X", "try_arc", "-w"});
358 process_runner_->iptables("nat", {"-F", "dnat_arc", "-w"});
359 process_runner_->iptables("nat", {"-X", "dnat_arc", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900360}
361
Garrick Evans54861622019-07-19 09:05:09 +0900362bool Datapath::AddLegacyIPv4InboundDNAT(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900363 return (process_runner_->iptables("nat", {"-A", "try_arc", "-i", ifname, "-j",
364 "dnat_arc", "-w"}) != 0);
Garrick Evans54861622019-07-19 09:05:09 +0900365}
366
367void Datapath::RemoveLegacyIPv4InboundDNAT() {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900368 process_runner_->iptables("nat", {"-F", "try_arc", "-w"});
Garrick Evans54861622019-07-19 09:05:09 +0900369}
370
Garrick Evansf0ab7132019-06-18 14:50:42 +0900371bool Datapath::AddInboundIPv4DNAT(const std::string& ifname,
372 const std::string& ipv4_addr) {
373 // Direct ingress IP traffic to existing sockets.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900374 if (process_runner_->iptables(
375 "nat", {"-A", "PREROUTING", "-i", ifname, "-m", "socket",
376 "--nowildcard", "-j", "ACCEPT", "-w"}) != 0)
Garrick Evansf0ab7132019-06-18 14:50:42 +0900377 return false;
378
379 // Direct ingress TCP & UDP traffic to ARC interface for new connections.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900380 if (process_runner_->iptables(
381 "nat", {"-A", "PREROUTING", "-i", ifname, "-p", "tcp", "-j", "DNAT",
382 "--to-destination", ipv4_addr, "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900383 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
384 return false;
385 }
Garrick Evans8e8e3472020-01-23 14:03:50 +0900386 if (process_runner_->iptables(
387 "nat", {"-A", "PREROUTING", "-i", ifname, "-p", "udp", "-j", "DNAT",
388 "--to-destination", ipv4_addr, "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900389 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
390 return false;
391 }
392
393 return true;
394}
395
396void Datapath::RemoveInboundIPv4DNAT(const std::string& ifname,
397 const std::string& ipv4_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900398 process_runner_->iptables(
399 "nat", {"-D", "PREROUTING", "-i", ifname, "-p", "udp", "-j", "DNAT",
400 "--to-destination", ipv4_addr, "-w"});
401 process_runner_->iptables(
402 "nat", {"-D", "PREROUTING", "-i", ifname, "-p", "tcp", "-j", "DNAT",
403 "--to-destination", ipv4_addr, "-w"});
404 process_runner_->iptables(
405 "nat", {"-D", "PREROUTING", "-i", ifname, "-m", "socket", "--nowildcard",
406 "-j", "ACCEPT", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900407}
408
409bool Datapath::AddOutboundIPv4(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900410 return process_runner_->iptables("filter", {"-A", "FORWARD", "-o", ifname,
411 "-j", "ACCEPT", "-w"}) == 0;
Garrick Evansf0ab7132019-06-18 14:50:42 +0900412}
413
414void Datapath::RemoveOutboundIPv4(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900415 process_runner_->iptables(
416 "filter", {"-D", "FORWARD", "-o", ifname, "-j", "ACCEPT", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900417}
418
Hugo Benichie8758b52020-04-03 14:49:01 +0900419bool Datapath::AddOutboundIPv4SNATMark(const std::string& ifname) {
420 return process_runner_->iptables(
421 "mangle", {"-A", "PREROUTING", "-i", ifname, "-j", "MARK",
422 "--set-mark", "1", "-w"}) == 0;
423}
424
425void Datapath::RemoveOutboundIPv4SNATMark(const std::string& ifname) {
426 process_runner_->iptables("mangle", {"-D", "PREROUTING", "-i", ifname, "-j",
427 "MARK", "--set-mark", "1", "-w"});
428}
429
Garrick Evans664a82f2019-12-17 12:18:05 +0900430bool Datapath::MaskInterfaceFlags(const std::string& ifname,
431 uint16_t on,
432 uint16_t off) {
Taoyu Li90c13912019-11-26 17:56:54 +0900433 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
434 if (!sock.is_valid()) {
435 PLOG(ERROR) << "Failed to create control socket";
436 return false;
437 }
438 ifreq ifr;
439 snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname.c_str());
440 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) < 0) {
441 PLOG(WARNING) << "ioctl() failed to get interface flag on " << ifname;
442 return false;
443 }
Garrick Evans664a82f2019-12-17 12:18:05 +0900444 ifr.ifr_flags |= on;
445 ifr.ifr_flags &= ~off;
Taoyu Li90c13912019-11-26 17:56:54 +0900446 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) < 0) {
Garrick Evans664a82f2019-12-17 12:18:05 +0900447 PLOG(WARNING) << "ioctl() failed to set flag 0x" << std::hex << on
448 << " unset flag 0x" << std::hex << off << " on " << ifname;
Taoyu Li90c13912019-11-26 17:56:54 +0900449 return false;
450 }
451 return true;
452}
453
Garrick Evans260ff302019-07-25 11:22:50 +0900454bool Datapath::AddIPv6HostRoute(const std::string& ifname,
455 const std::string& ipv6_addr,
456 int ipv6_prefix_len) {
457 std::string ipv6_addr_cidr =
458 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
459
Garrick Evans8e8e3472020-01-23 14:03:50 +0900460 return process_runner_->ip6("route", "replace",
461 {ipv6_addr_cidr, "dev", ifname}) == 0;
Garrick Evans260ff302019-07-25 11:22:50 +0900462}
463
464void Datapath::RemoveIPv6HostRoute(const std::string& ifname,
465 const std::string& ipv6_addr,
466 int ipv6_prefix_len) {
467 std::string ipv6_addr_cidr =
468 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
469
Garrick Evans8e8e3472020-01-23 14:03:50 +0900470 process_runner_->ip6("route", "del", {ipv6_addr_cidr, "dev", ifname});
Garrick Evans260ff302019-07-25 11:22:50 +0900471}
472
473bool Datapath::AddIPv6Neighbor(const std::string& ifname,
474 const std::string& ipv6_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900475 return process_runner_->ip6("neigh", "add",
476 {"proxy", ipv6_addr, "dev", ifname}) == 0;
Garrick Evans260ff302019-07-25 11:22:50 +0900477}
478
479void Datapath::RemoveIPv6Neighbor(const std::string& ifname,
480 const std::string& ipv6_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900481 process_runner_->ip6("neigh", "del", {"proxy", ipv6_addr, "dev", ifname});
Garrick Evans260ff302019-07-25 11:22:50 +0900482}
483
484bool Datapath::AddIPv6Forwarding(const std::string& ifname1,
485 const std::string& ifname2) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900486 if (process_runner_->ip6tables(
487 "filter",
488 {"-C", "FORWARD", "-i", ifname1, "-o", ifname2, "-j", "ACCEPT", "-w"},
489 false /*log_failures*/) != 0 &&
490 process_runner_->ip6tables(
491 "filter", {"-A", "FORWARD", "-i", ifname1, "-o", ifname2, "-j",
492 "ACCEPT", "-w"}) != 0) {
Garrick Evans260ff302019-07-25 11:22:50 +0900493 return false;
494 }
495
Garrick Evans8e8e3472020-01-23 14:03:50 +0900496 if (process_runner_->ip6tables(
497 "filter",
498 {"-C", "FORWARD", "-i", ifname2, "-o", ifname1, "-j", "ACCEPT", "-w"},
499 false /*log_failures*/) != 0 &&
500 process_runner_->ip6tables(
501 "filter", {"-A", "FORWARD", "-i", ifname2, "-o", ifname1, "-j",
502 "ACCEPT", "-w"}) != 0) {
Garrick Evans260ff302019-07-25 11:22:50 +0900503 RemoveIPv6Forwarding(ifname1, ifname2);
504 return false;
505 }
506
507 return true;
508}
509
510void Datapath::RemoveIPv6Forwarding(const std::string& ifname1,
511 const std::string& ifname2) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900512 process_runner_->ip6tables("filter", {"-D", "FORWARD", "-i", ifname1, "-o",
513 ifname2, "-j", "ACCEPT", "-w"});
Garrick Evans260ff302019-07-25 11:22:50 +0900514
Garrick Evans8e8e3472020-01-23 14:03:50 +0900515 process_runner_->ip6tables("filter", {"-D", "FORWARD", "-i", ifname2, "-o",
516 ifname1, "-j", "ACCEPT", "-w"});
Garrick Evans260ff302019-07-25 11:22:50 +0900517}
518
Garrick Evans3d97a392020-02-21 15:24:37 +0900519bool Datapath::AddIPv4Route(uint32_t gateway_addr,
520 uint32_t addr,
521 uint32_t netmask) {
522 struct rtentry route;
523 memset(&route, 0, sizeof(route));
Hugo Benichie8758b52020-04-03 14:49:01 +0900524 SetSockaddrIn(&route.rt_gateway, gateway_addr);
525 SetSockaddrIn(&route.rt_dst, addr & netmask);
526 SetSockaddrIn(&route.rt_genmask, netmask);
Garrick Evans3d97a392020-02-21 15:24:37 +0900527 route.rt_flags = RTF_UP | RTF_GATEWAY;
Hugo Benichie8758b52020-04-03 14:49:01 +0900528 return ModifyRtentry(SIOCADDRT, &route);
529}
Garrick Evans3d97a392020-02-21 15:24:37 +0900530
Hugo Benichie8758b52020-04-03 14:49:01 +0900531bool Datapath::DeleteIPv4Route(uint32_t gateway_addr,
532 uint32_t addr,
533 uint32_t netmask) {
534 struct rtentry route;
535 memset(&route, 0, sizeof(route));
536 SetSockaddrIn(&route.rt_gateway, gateway_addr);
537 SetSockaddrIn(&route.rt_dst, addr & netmask);
538 SetSockaddrIn(&route.rt_genmask, netmask);
539 route.rt_flags = RTF_UP | RTF_GATEWAY;
540 return ModifyRtentry(SIOCDELRT, &route);
541}
542
543bool Datapath::AddIPv4Route(const std::string& ifname,
544 uint32_t addr,
545 uint32_t netmask) {
546 struct rtentry route;
547 memset(&route, 0, sizeof(route));
548 SetSockaddrIn(&route.rt_dst, addr & netmask);
549 SetSockaddrIn(&route.rt_genmask, netmask);
550 char rt_dev[IFNAMSIZ];
551 strncpy(rt_dev, ifname.c_str(), IFNAMSIZ);
552 rt_dev[IFNAMSIZ - 1] = '\0';
553 route.rt_dev = rt_dev;
554 route.rt_flags = RTF_UP | RTF_GATEWAY;
555 return ModifyRtentry(SIOCADDRT, &route);
556}
557
558bool Datapath::DeleteIPv4Route(const std::string& ifname,
559 uint32_t addr,
560 uint32_t netmask) {
561 struct rtentry route;
562 memset(&route, 0, sizeof(route));
563 SetSockaddrIn(&route.rt_dst, addr & netmask);
564 SetSockaddrIn(&route.rt_genmask, netmask);
565 char rt_dev[IFNAMSIZ];
566 strncpy(rt_dev, ifname.c_str(), IFNAMSIZ);
567 rt_dev[IFNAMSIZ - 1] = '\0';
568 route.rt_dev = rt_dev;
569 route.rt_flags = RTF_UP | RTF_GATEWAY;
570 return ModifyRtentry(SIOCDELRT, &route);
571}
572
573bool Datapath::ModifyRtentry(unsigned long op, struct rtentry* route) {
574 DCHECK(route);
575 if (op != SIOCADDRT && op != SIOCDELRT) {
576 LOG(ERROR) << "Invalid operation " << op << " for rtentry " << route;
Garrick Evans3d97a392020-02-21 15:24:37 +0900577 return false;
578 }
Hugo Benichie8758b52020-04-03 14:49:01 +0900579 base::ScopedFD fd(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
580 if (!fd.is_valid()) {
581 PLOG(ERROR) << "Failed to create socket for adding rtentry " << route;
582 return false;
583 }
584 if (HANDLE_EINTR(ioctl_(fd.get(), op, route)) != 0) {
585 std::string opname = op == SIOCADDRT ? "add" : "delete";
586 PLOG(ERROR) << "Failed to " << opname << " rtentry " << route;
Garrick Evans3d97a392020-02-21 15:24:37 +0900587 return false;
588 }
589 return true;
590}
591
Garrick Evans3388a032020-03-24 11:25:55 +0900592} // namespace patchpanel