blob: 7670730d6077d9723c61f47d8bbec5641b875cdd [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
37std::string ArcVethHostName(std::string ifname) {
38 return "veth_" + ifname;
39}
40
41std::string ArcVethPeerName(std::string ifname) {
42 return "peer_" + ifname;
43}
Garrick Evansf0ab7132019-06-18 14:50:42 +090044
45Datapath::Datapath(MinijailedProcessRunner* process_runner)
Garrick Evansc7ae82c2019-09-04 16:25:10 +090046 : Datapath(process_runner, ioctl) {}
47
48Datapath::Datapath(MinijailedProcessRunner* process_runner, ioctl_t ioctl_hook)
49 : process_runner_(process_runner), ioctl_(ioctl_hook) {
Garrick Evansf0ab7132019-06-18 14:50:42 +090050 CHECK(process_runner_);
51}
52
Garrick Evans260ff302019-07-25 11:22:50 +090053MinijailedProcessRunner& Datapath::runner() const {
54 return *process_runner_;
55}
56
Garrick Evans8a949dc2019-07-18 16:17:53 +090057bool Datapath::AddBridge(const std::string& ifname,
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090058 uint32_t ipv4_addr,
59 uint32_t ipv4_prefix_len) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090060 // Configure the persistent Chrome OS bridge interface with static IP.
Garrick Evans8e8e3472020-01-23 14:03:50 +090061 if (process_runner_->brctl("addbr", {ifname}) != 0) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090062 return false;
63 }
64
Garrick Evans6f4fa3a2020-02-10 16:15:09 +090065 if (process_runner_->ip(
66 "addr", "add",
67 {IPv4AddressToCidrString(ipv4_addr, ipv4_prefix_len), "brd",
68 IPv4AddressToString(Ipv4BroadcastAddr(ipv4_addr, ipv4_prefix_len)),
69 "dev", ifname}) != 0) {
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090070 RemoveBridge(ifname);
71 return false;
72 }
73
74 if (process_runner_->ip("link", "set", {ifname, "up"}) != 0) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090075 RemoveBridge(ifname);
76 return false;
77 }
78
79 // See nat.conf in chromeos-nat-init for the rest of the NAT setup rules.
Hugo Benichie8758b52020-04-03 14:49:01 +090080 if (!AddOutboundIPv4SNATMark(ifname)) {
Garrick Evans8a949dc2019-07-18 16:17:53 +090081 RemoveBridge(ifname);
82 return false;
83 }
84
85 return true;
86}
87
88void Datapath::RemoveBridge(const std::string& ifname) {
Hugo Benichie8758b52020-04-03 14:49:01 +090089 RemoveOutboundIPv4SNATMark(ifname);
Garrick Evans7a1a9ee2020-01-28 11:03:57 +090090 process_runner_->ip("link", "set", {ifname, "down"});
Garrick Evans8e8e3472020-01-23 14:03:50 +090091 process_runner_->brctl("delbr", {ifname});
Garrick Evans8a949dc2019-07-18 16:17:53 +090092}
93
Garrick Evans621ed262019-11-13 12:28:43 +090094bool Datapath::AddToBridge(const std::string& br_ifname,
95 const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +090096 return (process_runner_->brctl("addif", {br_ifname, ifname}) == 0);
Garrick Evans621ed262019-11-13 12:28:43 +090097}
98
Garrick Evansc7ae82c2019-09-04 16:25:10 +090099std::string Datapath::AddTAP(const std::string& name,
Garrick Evans621ed262019-11-13 12:28:43 +0900100 const MacAddress* mac_addr,
101 const SubnetAddress* ipv4_addr,
Garrick Evans4f9f5572019-11-26 10:25:16 +0900102 const std::string& user) {
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900103 base::ScopedFD dev(open(kTunDev, O_RDWR | O_NONBLOCK));
104 if (!dev.is_valid()) {
105 PLOG(ERROR) << "Failed to open " << kTunDev;
106 return "";
107 }
108
109 struct ifreq ifr;
110 memset(&ifr, 0, sizeof(ifr));
111 strncpy(ifr.ifr_name, name.empty() ? kDefaultIfname : name.c_str(),
112 sizeof(ifr.ifr_name));
113 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
114
115 // If a template was given as the name, ifr_name will be updated with the
116 // actual interface name.
117 if ((*ioctl_)(dev.get(), TUNSETIFF, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900118 PLOG(ERROR) << "Failed to create tap interface " << name;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900119 return "";
120 }
121 const char* ifname = ifr.ifr_name;
122
123 if ((*ioctl_)(dev.get(), TUNSETPERSIST, 1) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900124 PLOG(ERROR) << "Failed to persist the interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900125 return "";
126 }
127
Garrick Evans4f9f5572019-11-26 10:25:16 +0900128 if (!user.empty()) {
129 uid_t uid = -1;
130 if (!brillo::userdb::GetUserInfo(user, &uid, nullptr)) {
131 PLOG(ERROR) << "Unable to look up UID for " << user;
132 RemoveTAP(ifname);
133 return "";
134 }
135 if ((*ioctl_)(dev.get(), TUNSETOWNER, uid) != 0) {
136 PLOG(ERROR) << "Failed to set owner " << uid << " of tap interface "
137 << ifname;
138 RemoveTAP(ifname);
139 return "";
140 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900141 }
142
Hugo Benichib9b93fe2019-10-25 23:36:01 +0900143 // Create control socket for configuring the interface.
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900144 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
145 if (!sock.is_valid()) {
146 PLOG(ERROR) << "Failed to create control socket for tap interface "
Garrick Evans621ed262019-11-13 12:28:43 +0900147 << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900148 RemoveTAP(ifname);
149 return "";
150 }
151
Garrick Evans621ed262019-11-13 12:28:43 +0900152 if (ipv4_addr) {
153 struct sockaddr_in* addr =
154 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
155 addr->sin_family = AF_INET;
156 addr->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr->Address());
157 if ((*ioctl_)(sock.get(), SIOCSIFADDR, &ifr) != 0) {
158 PLOG(ERROR) << "Failed to set ip address for vmtap interface " << ifname
159 << " {" << ipv4_addr->ToCidrString() << "}";
160 RemoveTAP(ifname);
161 return "";
162 }
163
164 struct sockaddr_in* netmask =
165 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_netmask);
166 netmask->sin_family = AF_INET;
167 netmask->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr->Netmask());
168 if ((*ioctl_)(sock.get(), SIOCSIFNETMASK, &ifr) != 0) {
169 PLOG(ERROR) << "Failed to set netmask for vmtap interface " << ifname
170 << " {" << ipv4_addr->ToCidrString() << "}";
171 RemoveTAP(ifname);
172 return "";
173 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900174 }
175
Garrick Evans621ed262019-11-13 12:28:43 +0900176 if (mac_addr) {
177 struct sockaddr* hwaddr = &ifr.ifr_hwaddr;
178 hwaddr->sa_family = ARPHRD_ETHER;
179 memcpy(&hwaddr->sa_data, mac_addr, sizeof(*mac_addr));
180 if ((*ioctl_)(sock.get(), SIOCSIFHWADDR, &ifr) != 0) {
181 PLOG(ERROR) << "Failed to set mac address for vmtap interface " << ifname
182 << " {" << MacAddressToString(*mac_addr) << "}";
183 RemoveTAP(ifname);
184 return "";
185 }
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900186 }
187
188 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900189 PLOG(ERROR) << "Failed to get flags for tap interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900190 RemoveTAP(ifname);
191 return "";
192 }
193
194 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
195 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) != 0) {
Garrick Evans621ed262019-11-13 12:28:43 +0900196 PLOG(ERROR) << "Failed to enable tap interface " << ifname;
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900197 RemoveTAP(ifname);
198 return "";
199 }
200
201 return ifname;
202}
203
204void Datapath::RemoveTAP(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900205 process_runner_->ip("tuntap", "del", {ifname, "mode", "tap"});
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900206}
207
Hugo Benichi76675592020-04-08 14:29:57 +0900208bool Datapath::ConnectVethPair(pid_t pid,
209 const std::string& veth_ifname,
210 const std::string& peer_ifname,
211 const MacAddress& remote_mac_addr,
212 uint32_t remote_ipv4_addr,
213 uint32_t remote_ipv4_prefix_len,
214 bool remote_multicast_flag) {
215 // Set up the virtual pair inside the remote namespace.
216 {
217 ScopedNS ns(pid);
218 if (!ns.IsValid() && pid != kTestPID) {
219 LOG(ERROR)
220 << "Cannot create virtual link -- invalid container namespace?";
221 return false;
222 }
223
224 if (!AddVirtualInterfacePair(veth_ifname, peer_ifname)) {
225 LOG(ERROR) << "Failed to create veth pair " << veth_ifname << ","
226 << peer_ifname;
227 return false;
228 }
229
230 if (!ConfigureInterface(peer_ifname, remote_mac_addr, remote_ipv4_addr,
231 remote_ipv4_prefix_len, true /* link up */,
232 remote_multicast_flag)) {
233 LOG(ERROR) << "Failed to configure interface " << peer_ifname;
234 RemoveInterface(peer_ifname);
235 return false;
236 }
237 }
238
239 // Now pull the local end out into the local namespace.
240 if (runner().RestoreDefaultNamespace(veth_ifname, pid) != 0) {
241 LOG(ERROR) << "Failed to prepare interface " << veth_ifname;
242 {
243 ScopedNS ns(pid);
244 if (ns.IsValid()) {
245 RemoveInterface(peer_ifname);
246 } else {
247 LOG(ERROR) << "Failed to re-enter container namespace pid " << pid;
248 }
249 }
250 return false;
251 }
252 if (!ToggleInterface(veth_ifname, true /*up*/)) {
253 LOG(ERROR) << "Failed to bring up interface " << veth_ifname;
254 RemoveInterface(veth_ifname);
255 return false;
256 }
257 return true;
258}
259
Garrick Evans2470caa2020-03-04 14:15:41 +0900260bool Datapath::AddVirtualInterfacePair(const std::string& veth_ifname,
261 const std::string& peer_ifname) {
262 return process_runner_->ip(
263 "link", "add",
264 {veth_ifname, "type", "veth", "peer", "name", peer_ifname}) == 0;
265}
Garrick Evans54861622019-07-19 09:05:09 +0900266
Garrick Evans2470caa2020-03-04 14:15:41 +0900267bool Datapath::ToggleInterface(const std::string& ifname, bool up) {
268 const std::string link = up ? "up" : "down";
269 return process_runner_->ip("link", "set", {ifname, link}) == 0;
270}
Garrick Evans54861622019-07-19 09:05:09 +0900271
Garrick Evans2470caa2020-03-04 14:15:41 +0900272bool Datapath::ConfigureInterface(const std::string& ifname,
273 const MacAddress& mac_addr,
274 uint32_t ipv4_addr,
275 uint32_t ipv4_prefix_len,
276 bool up,
277 bool enable_multicast) {
278 const std::string link = up ? "up" : "down";
279 const std::string multicast = enable_multicast ? "on" : "off";
280 return (process_runner_->ip(
281 "addr", "add",
282 {IPv4AddressToCidrString(ipv4_addr, ipv4_prefix_len), "brd",
283 IPv4AddressToString(
284 Ipv4BroadcastAddr(ipv4_addr, ipv4_prefix_len)),
285 "dev", ifname}) == 0) &&
286 (process_runner_->ip("link", "set",
287 {
288 "dev",
289 ifname,
290 link,
291 "addr",
292 MacAddressToString(mac_addr),
293 "multicast",
294 multicast,
295 }) == 0);
Garrick Evans54861622019-07-19 09:05:09 +0900296}
297
298void Datapath::RemoveInterface(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900299 process_runner_->ip("link", "delete", {ifname}, false /*log_failures*/);
Garrick Evans54861622019-07-19 09:05:09 +0900300}
301
Garrick Evansf0ab7132019-06-18 14:50:42 +0900302bool Datapath::AddLegacyIPv4DNAT(const std::string& ipv4_addr) {
303 // Forward "unclaimed" packets to Android to allow inbound connections
304 // from devices on the LAN.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900305 if (process_runner_->iptables("nat", {"-N", "dnat_arc", "-w"}) != 0)
Garrick Evansf0ab7132019-06-18 14:50:42 +0900306 return false;
307
Garrick Evans8e8e3472020-01-23 14:03:50 +0900308 if (process_runner_->iptables("nat", {"-A", "dnat_arc", "-j", "DNAT",
309 "--to-destination", ipv4_addr, "-w"}) !=
Garrick Evansf0ab7132019-06-18 14:50:42 +0900310 0) {
Garrick Evans54861622019-07-19 09:05:09 +0900311 RemoveLegacyIPv4DNAT();
Garrick Evansf0ab7132019-06-18 14:50:42 +0900312 return false;
313 }
314
315 // This chain is dynamically updated whenever the default interface
316 // changes.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900317 if (process_runner_->iptables("nat", {"-N", "try_arc", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900318 RemoveLegacyIPv4DNAT();
319 return false;
320 }
321
Garrick Evans8e8e3472020-01-23 14:03:50 +0900322 if (process_runner_->iptables(
323 "nat", {"-A", "PREROUTING", "-m", "socket", "--nowildcard", "-j",
324 "ACCEPT", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900325 RemoveLegacyIPv4DNAT();
326 return false;
327 }
328
Garrick Evans8e8e3472020-01-23 14:03:50 +0900329 if (process_runner_->iptables("nat", {"-A", "PREROUTING", "-p", "tcp", "-j",
330 "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("nat", {"-A", "PREROUTING", "-p", "udp", "-j",
336 "try_arc", "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900337 RemoveLegacyIPv4DNAT();
338 return false;
339 }
340
341 return true;
342}
343
344void Datapath::RemoveLegacyIPv4DNAT() {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900345 process_runner_->iptables(
346 "nat", {"-D", "PREROUTING", "-p", "udp", "-j", "try_arc", "-w"});
347 process_runner_->iptables(
348 "nat", {"-D", "PREROUTING", "-p", "tcp", "-j", "try_arc", "-w"});
349 process_runner_->iptables("nat", {"-D", "PREROUTING", "-m", "socket",
350 "--nowildcard", "-j", "ACCEPT", "-w"});
351 process_runner_->iptables("nat", {"-F", "try_arc", "-w"});
352 process_runner_->iptables("nat", {"-X", "try_arc", "-w"});
353 process_runner_->iptables("nat", {"-F", "dnat_arc", "-w"});
354 process_runner_->iptables("nat", {"-X", "dnat_arc", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900355}
356
Garrick Evans54861622019-07-19 09:05:09 +0900357bool Datapath::AddLegacyIPv4InboundDNAT(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900358 return (process_runner_->iptables("nat", {"-A", "try_arc", "-i", ifname, "-j",
359 "dnat_arc", "-w"}) != 0);
Garrick Evans54861622019-07-19 09:05:09 +0900360}
361
362void Datapath::RemoveLegacyIPv4InboundDNAT() {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900363 process_runner_->iptables("nat", {"-F", "try_arc", "-w"});
Garrick Evans54861622019-07-19 09:05:09 +0900364}
365
Garrick Evansf0ab7132019-06-18 14:50:42 +0900366bool Datapath::AddInboundIPv4DNAT(const std::string& ifname,
367 const std::string& ipv4_addr) {
368 // Direct ingress IP traffic to existing sockets.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900369 if (process_runner_->iptables(
370 "nat", {"-A", "PREROUTING", "-i", ifname, "-m", "socket",
371 "--nowildcard", "-j", "ACCEPT", "-w"}) != 0)
Garrick Evansf0ab7132019-06-18 14:50:42 +0900372 return false;
373
374 // Direct ingress TCP & UDP traffic to ARC interface for new connections.
Garrick Evans8e8e3472020-01-23 14:03:50 +0900375 if (process_runner_->iptables(
376 "nat", {"-A", "PREROUTING", "-i", ifname, "-p", "tcp", "-j", "DNAT",
377 "--to-destination", ipv4_addr, "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900378 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
379 return false;
380 }
Garrick Evans8e8e3472020-01-23 14:03:50 +0900381 if (process_runner_->iptables(
382 "nat", {"-A", "PREROUTING", "-i", ifname, "-p", "udp", "-j", "DNAT",
383 "--to-destination", ipv4_addr, "-w"}) != 0) {
Garrick Evansf0ab7132019-06-18 14:50:42 +0900384 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
385 return false;
386 }
387
388 return true;
389}
390
391void Datapath::RemoveInboundIPv4DNAT(const std::string& ifname,
392 const std::string& ipv4_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900393 process_runner_->iptables(
394 "nat", {"-D", "PREROUTING", "-i", ifname, "-p", "udp", "-j", "DNAT",
395 "--to-destination", ipv4_addr, "-w"});
396 process_runner_->iptables(
397 "nat", {"-D", "PREROUTING", "-i", ifname, "-p", "tcp", "-j", "DNAT",
398 "--to-destination", ipv4_addr, "-w"});
399 process_runner_->iptables(
400 "nat", {"-D", "PREROUTING", "-i", ifname, "-m", "socket", "--nowildcard",
401 "-j", "ACCEPT", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900402}
403
404bool Datapath::AddOutboundIPv4(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900405 return process_runner_->iptables("filter", {"-A", "FORWARD", "-o", ifname,
406 "-j", "ACCEPT", "-w"}) == 0;
Garrick Evansf0ab7132019-06-18 14:50:42 +0900407}
408
409void Datapath::RemoveOutboundIPv4(const std::string& ifname) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900410 process_runner_->iptables(
411 "filter", {"-D", "FORWARD", "-o", ifname, "-j", "ACCEPT", "-w"});
Garrick Evansf0ab7132019-06-18 14:50:42 +0900412}
413
Hugo Benichie8758b52020-04-03 14:49:01 +0900414bool Datapath::AddOutboundIPv4SNATMark(const std::string& ifname) {
415 return process_runner_->iptables(
416 "mangle", {"-A", "PREROUTING", "-i", ifname, "-j", "MARK",
417 "--set-mark", "1", "-w"}) == 0;
418}
419
420void Datapath::RemoveOutboundIPv4SNATMark(const std::string& ifname) {
421 process_runner_->iptables("mangle", {"-D", "PREROUTING", "-i", ifname, "-j",
422 "MARK", "--set-mark", "1", "-w"});
423}
424
Garrick Evans664a82f2019-12-17 12:18:05 +0900425bool Datapath::MaskInterfaceFlags(const std::string& ifname,
426 uint16_t on,
427 uint16_t off) {
Taoyu Li90c13912019-11-26 17:56:54 +0900428 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
429 if (!sock.is_valid()) {
430 PLOG(ERROR) << "Failed to create control socket";
431 return false;
432 }
433 ifreq ifr;
434 snprintf(ifr.ifr_name, IFNAMSIZ, "%s", ifname.c_str());
435 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) < 0) {
436 PLOG(WARNING) << "ioctl() failed to get interface flag on " << ifname;
437 return false;
438 }
Garrick Evans664a82f2019-12-17 12:18:05 +0900439 ifr.ifr_flags |= on;
440 ifr.ifr_flags &= ~off;
Taoyu Li90c13912019-11-26 17:56:54 +0900441 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) < 0) {
Garrick Evans664a82f2019-12-17 12:18:05 +0900442 PLOG(WARNING) << "ioctl() failed to set flag 0x" << std::hex << on
443 << " unset flag 0x" << std::hex << off << " on " << ifname;
Taoyu Li90c13912019-11-26 17:56:54 +0900444 return false;
445 }
446 return true;
447}
448
Garrick Evans260ff302019-07-25 11:22:50 +0900449bool Datapath::AddIPv6HostRoute(const std::string& ifname,
450 const std::string& ipv6_addr,
451 int ipv6_prefix_len) {
452 std::string ipv6_addr_cidr =
453 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
454
Garrick Evans8e8e3472020-01-23 14:03:50 +0900455 return process_runner_->ip6("route", "replace",
456 {ipv6_addr_cidr, "dev", ifname}) == 0;
Garrick Evans260ff302019-07-25 11:22:50 +0900457}
458
459void Datapath::RemoveIPv6HostRoute(const std::string& ifname,
460 const std::string& ipv6_addr,
461 int ipv6_prefix_len) {
462 std::string ipv6_addr_cidr =
463 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
464
Garrick Evans8e8e3472020-01-23 14:03:50 +0900465 process_runner_->ip6("route", "del", {ipv6_addr_cidr, "dev", ifname});
Garrick Evans260ff302019-07-25 11:22:50 +0900466}
467
468bool Datapath::AddIPv6Neighbor(const std::string& ifname,
469 const std::string& ipv6_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900470 return process_runner_->ip6("neigh", "add",
471 {"proxy", ipv6_addr, "dev", ifname}) == 0;
Garrick Evans260ff302019-07-25 11:22:50 +0900472}
473
474void Datapath::RemoveIPv6Neighbor(const std::string& ifname,
475 const std::string& ipv6_addr) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900476 process_runner_->ip6("neigh", "del", {"proxy", ipv6_addr, "dev", ifname});
Garrick Evans260ff302019-07-25 11:22:50 +0900477}
478
479bool Datapath::AddIPv6Forwarding(const std::string& ifname1,
480 const std::string& ifname2) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900481 if (process_runner_->ip6tables(
482 "filter",
483 {"-C", "FORWARD", "-i", ifname1, "-o", ifname2, "-j", "ACCEPT", "-w"},
484 false /*log_failures*/) != 0 &&
485 process_runner_->ip6tables(
486 "filter", {"-A", "FORWARD", "-i", ifname1, "-o", ifname2, "-j",
487 "ACCEPT", "-w"}) != 0) {
Garrick Evans260ff302019-07-25 11:22:50 +0900488 return false;
489 }
490
Garrick Evans8e8e3472020-01-23 14:03:50 +0900491 if (process_runner_->ip6tables(
492 "filter",
493 {"-C", "FORWARD", "-i", ifname2, "-o", ifname1, "-j", "ACCEPT", "-w"},
494 false /*log_failures*/) != 0 &&
495 process_runner_->ip6tables(
496 "filter", {"-A", "FORWARD", "-i", ifname2, "-o", ifname1, "-j",
497 "ACCEPT", "-w"}) != 0) {
Garrick Evans260ff302019-07-25 11:22:50 +0900498 RemoveIPv6Forwarding(ifname1, ifname2);
499 return false;
500 }
501
502 return true;
503}
504
505void Datapath::RemoveIPv6Forwarding(const std::string& ifname1,
506 const std::string& ifname2) {
Garrick Evans8e8e3472020-01-23 14:03:50 +0900507 process_runner_->ip6tables("filter", {"-D", "FORWARD", "-i", ifname1, "-o",
508 ifname2, "-j", "ACCEPT", "-w"});
Garrick Evans260ff302019-07-25 11:22:50 +0900509
Garrick Evans8e8e3472020-01-23 14:03:50 +0900510 process_runner_->ip6tables("filter", {"-D", "FORWARD", "-i", ifname2, "-o",
511 ifname1, "-j", "ACCEPT", "-w"});
Garrick Evans260ff302019-07-25 11:22:50 +0900512}
513
Garrick Evans3d97a392020-02-21 15:24:37 +0900514bool Datapath::AddIPv4Route(uint32_t gateway_addr,
515 uint32_t addr,
516 uint32_t netmask) {
517 struct rtentry route;
518 memset(&route, 0, sizeof(route));
Hugo Benichie8758b52020-04-03 14:49:01 +0900519 SetSockaddrIn(&route.rt_gateway, gateway_addr);
520 SetSockaddrIn(&route.rt_dst, addr & netmask);
521 SetSockaddrIn(&route.rt_genmask, netmask);
Garrick Evans3d97a392020-02-21 15:24:37 +0900522 route.rt_flags = RTF_UP | RTF_GATEWAY;
Hugo Benichie8758b52020-04-03 14:49:01 +0900523 return ModifyRtentry(SIOCADDRT, &route);
524}
Garrick Evans3d97a392020-02-21 15:24:37 +0900525
Hugo Benichie8758b52020-04-03 14:49:01 +0900526bool Datapath::DeleteIPv4Route(uint32_t gateway_addr,
527 uint32_t addr,
528 uint32_t netmask) {
529 struct rtentry route;
530 memset(&route, 0, sizeof(route));
531 SetSockaddrIn(&route.rt_gateway, gateway_addr);
532 SetSockaddrIn(&route.rt_dst, addr & netmask);
533 SetSockaddrIn(&route.rt_genmask, netmask);
534 route.rt_flags = RTF_UP | RTF_GATEWAY;
535 return ModifyRtentry(SIOCDELRT, &route);
536}
537
538bool Datapath::AddIPv4Route(const std::string& ifname,
539 uint32_t addr,
540 uint32_t netmask) {
541 struct rtentry route;
542 memset(&route, 0, sizeof(route));
543 SetSockaddrIn(&route.rt_dst, addr & netmask);
544 SetSockaddrIn(&route.rt_genmask, netmask);
545 char rt_dev[IFNAMSIZ];
546 strncpy(rt_dev, ifname.c_str(), IFNAMSIZ);
547 rt_dev[IFNAMSIZ - 1] = '\0';
548 route.rt_dev = rt_dev;
549 route.rt_flags = RTF_UP | RTF_GATEWAY;
550 return ModifyRtentry(SIOCADDRT, &route);
551}
552
553bool Datapath::DeleteIPv4Route(const std::string& ifname,
554 uint32_t addr,
555 uint32_t netmask) {
556 struct rtentry route;
557 memset(&route, 0, sizeof(route));
558 SetSockaddrIn(&route.rt_dst, addr & netmask);
559 SetSockaddrIn(&route.rt_genmask, netmask);
560 char rt_dev[IFNAMSIZ];
561 strncpy(rt_dev, ifname.c_str(), IFNAMSIZ);
562 rt_dev[IFNAMSIZ - 1] = '\0';
563 route.rt_dev = rt_dev;
564 route.rt_flags = RTF_UP | RTF_GATEWAY;
565 return ModifyRtentry(SIOCDELRT, &route);
566}
567
568bool Datapath::ModifyRtentry(unsigned long op, struct rtentry* route) {
569 DCHECK(route);
570 if (op != SIOCADDRT && op != SIOCDELRT) {
571 LOG(ERROR) << "Invalid operation " << op << " for rtentry " << route;
Garrick Evans3d97a392020-02-21 15:24:37 +0900572 return false;
573 }
Hugo Benichie8758b52020-04-03 14:49:01 +0900574 base::ScopedFD fd(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
575 if (!fd.is_valid()) {
576 PLOG(ERROR) << "Failed to create socket for adding rtentry " << route;
577 return false;
578 }
579 if (HANDLE_EINTR(ioctl_(fd.get(), op, route)) != 0) {
580 std::string opname = op == SIOCADDRT ? "add" : "delete";
581 PLOG(ERROR) << "Failed to " << opname << " rtentry " << route;
Garrick Evans3d97a392020-02-21 15:24:37 +0900582 return false;
583 }
584 return true;
585}
586
Garrick Evans3388a032020-03-24 11:25:55 +0900587} // namespace patchpanel