blob: 16426385c85b149b2522ea0e10dc9a52ed66c635 [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
5#include "arc/network/datapath.h"
6
Garrick Evansc7ae82c2019-09-04 16:25:10 +09007#include <fcntl.h>
8#include <linux/if_tun.h>
9#include <linux/sockios.h>
10#include <net/if.h>
11#include <net/if_arp.h>
12#include <netinet/in.h>
13#include <string.h>
14#include <sys/ioctl.h>
15#include <sys/socket.h>
16
17#include <base/files/scoped_file.h>
18#include <base/logging.h>
Garrick Evans54861622019-07-19 09:05:09 +090019#include <base/strings/string_number_conversions.h>
20
Garrick Evansc7ae82c2019-09-04 16:25:10 +090021#include "arc/network/net_util.h"
22
Garrick Evansf0ab7132019-06-18 14:50:42 +090023namespace arc_networkd {
Garrick Evans54861622019-07-19 09:05:09 +090024
Garrick Evansc7ae82c2019-09-04 16:25:10 +090025namespace {
Garrick Evans8a949dc2019-07-18 16:17:53 +090026constexpr char kDefaultNetmask[] = "255.255.255.252";
Garrick Evansc7ae82c2019-09-04 16:25:10 +090027constexpr char kDefaultIfname[] = "vmtap%d";
28constexpr char kTunDev[] = "/dev/net/tun";
29} // namespace
Garrick Evans54861622019-07-19 09:05:09 +090030
31std::string ArcVethHostName(std::string ifname) {
32 return "veth_" + ifname;
33}
34
35std::string ArcVethPeerName(std::string ifname) {
36 return "peer_" + ifname;
37}
Garrick Evansf0ab7132019-06-18 14:50:42 +090038
39Datapath::Datapath(MinijailedProcessRunner* process_runner)
Garrick Evansc7ae82c2019-09-04 16:25:10 +090040 : Datapath(process_runner, ioctl) {}
41
42Datapath::Datapath(MinijailedProcessRunner* process_runner, ioctl_t ioctl_hook)
43 : process_runner_(process_runner), ioctl_(ioctl_hook) {
Garrick Evansf0ab7132019-06-18 14:50:42 +090044 CHECK(process_runner_);
45}
46
Garrick Evans260ff302019-07-25 11:22:50 +090047MinijailedProcessRunner& Datapath::runner() const {
48 return *process_runner_;
49}
50
Garrick Evans8a949dc2019-07-18 16:17:53 +090051bool Datapath::AddBridge(const std::string& ifname,
52 const std::string& ipv4_addr) {
53 // Configure the persistent Chrome OS bridge interface with static IP.
54 if (process_runner_->Run({kBrctlPath, "addbr", ifname}) != 0) {
55 return false;
56 }
57
58 if (process_runner_->Run({kIfConfigPath, ifname, ipv4_addr, "netmask",
59 kDefaultNetmask, "up"}) != 0) {
60 RemoveBridge(ifname);
61 return false;
62 }
63
64 // See nat.conf in chromeos-nat-init for the rest of the NAT setup rules.
65 if (process_runner_->Run({kIpTablesPath, "-t", "mangle", "-A", "PREROUTING",
66 "-i", ifname, "-j", "MARK", "--set-mark", "1",
67 "-w"}) != 0) {
68 RemoveBridge(ifname);
69 return false;
70 }
71
72 return true;
73}
74
75void Datapath::RemoveBridge(const std::string& ifname) {
76 process_runner_->Run({kIpTablesPath, "-t", "mangle", "-D", "PREROUTING", "-i",
77 ifname, "-j", "MARK", "--set-mark", "1", "-w"});
78 process_runner_->Run({kIfConfigPath, ifname, "down"});
79 process_runner_->Run({kBrctlPath, "delbr", ifname});
80}
81
Garrick Evansc7ae82c2019-09-04 16:25:10 +090082std::string Datapath::AddTAP(const std::string& name,
83 const MacAddress& mac_addr,
84 const SubnetAddress& ipv4_addr,
85 uid_t user_id) {
86 base::ScopedFD dev(open(kTunDev, O_RDWR | O_NONBLOCK));
87 if (!dev.is_valid()) {
88 PLOG(ERROR) << "Failed to open " << kTunDev;
89 return "";
90 }
91
92 struct ifreq ifr;
93 memset(&ifr, 0, sizeof(ifr));
94 strncpy(ifr.ifr_name, name.empty() ? kDefaultIfname : name.c_str(),
95 sizeof(ifr.ifr_name));
96 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
97
98 // If a template was given as the name, ifr_name will be updated with the
99 // actual interface name.
100 if ((*ioctl_)(dev.get(), TUNSETIFF, &ifr) != 0) {
101 PLOG(ERROR) << "Failed to create tap interface " << name << " {"
102 << ipv4_addr.ToCidrString() << "}";
103 return "";
104 }
105 const char* ifname = ifr.ifr_name;
106
107 if ((*ioctl_)(dev.get(), TUNSETPERSIST, 1) != 0) {
108 PLOG(ERROR) << "Failed to persist the interface " << ifname << " {"
109 << ipv4_addr.ToCidrString() << "}";
110 return "";
111 }
112
113 if (user_id != -1 && (*ioctl_)(dev.get(), TUNSETOWNER, user_id) != 0) {
114 PLOG(ERROR) << "Failed to set owner " << user_id << " of tap interface "
115 << ifname << " {" << ipv4_addr.ToCidrString() << "}";
116 RemoveTAP(ifname);
117 return "";
118 }
119
Hugo Benichib9b93fe2019-10-25 23:36:01 +0900120 // Create control socket for configuring the interface.
Garrick Evansc7ae82c2019-09-04 16:25:10 +0900121 base::ScopedFD sock(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
122 if (!sock.is_valid()) {
123 PLOG(ERROR) << "Failed to create control socket for tap interface "
124 << ifname << " {" << ipv4_addr.ToCidrString() << "}";
125 RemoveTAP(ifname);
126 return "";
127 }
128
129 struct sockaddr_in* addr =
130 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_addr);
131 addr->sin_family = AF_INET;
132 addr->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr.Address());
133 if ((*ioctl_)(sock.get(), SIOCSIFADDR, &ifr) != 0) {
134 PLOG(ERROR) << "Failed to set ip address for vmtap interface " << ifname
135 << " {" << ipv4_addr.ToCidrString() << "}";
136 RemoveTAP(ifname);
137 return "";
138 }
139
140 struct sockaddr_in* netmask =
141 reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_netmask);
142 netmask->sin_family = AF_INET;
143 netmask->sin_addr.s_addr = static_cast<in_addr_t>(ipv4_addr.Netmask());
144 if ((*ioctl_)(sock.get(), SIOCSIFNETMASK, &ifr) != 0) {
145 PLOG(ERROR) << "Failed to set netmask for vmtap interface " << ifname
146 << " {" << ipv4_addr.ToCidrString() << "}";
147 RemoveTAP(ifname);
148 return "";
149 }
150
151 struct sockaddr* hwaddr = &ifr.ifr_hwaddr;
152 hwaddr->sa_family = ARPHRD_ETHER;
153 memcpy(&hwaddr->sa_data, &mac_addr, sizeof(mac_addr));
154 if ((*ioctl_)(sock.get(), SIOCSIFHWADDR, &ifr) != 0) {
155 PLOG(ERROR) << "Failed to set mac address for vmtap interface " << ifname
156 << " {" << ipv4_addr.ToCidrString() << "}";
157 RemoveTAP(ifname);
158 return "";
159 }
160
161 if ((*ioctl_)(sock.get(), SIOCGIFFLAGS, &ifr) != 0) {
162 PLOG(ERROR) << "Failed to get flags for tap interface " << ifname << " {"
163 << ipv4_addr.ToCidrString() << "}";
164 RemoveTAP(ifname);
165 return "";
166 }
167
168 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
169 if ((*ioctl_)(sock.get(), SIOCSIFFLAGS, &ifr) != 0) {
170 PLOG(ERROR) << "Failed to enable tap interface " << ifname << " {"
171 << ipv4_addr.ToCidrString() << "}";
172 RemoveTAP(ifname);
173 return "";
174 }
175
176 return ifname;
177}
178
179void Datapath::RemoveTAP(const std::string& ifname) {
180 process_runner_->Run({kIpPath, "tuntap", "del", ifname, "mode", "tap"});
181}
182
Garrick Evans54861622019-07-19 09:05:09 +0900183std::string Datapath::AddVirtualBridgedInterface(const std::string& ifname,
184 const std::string& mac_addr,
185 const std::string& br_ifname) {
186 const std::string veth = ArcVethHostName(ifname);
187 const std::string peer = ArcVethPeerName(ifname);
188
189 RemoveInterface(veth);
190 if (process_runner_->Run({kIpPath, "link", "add", veth, "type", "veth",
191 "peer", "name", peer}) != 0) {
192 return "";
193 }
194
195 if (process_runner_->Run({kIfConfigPath, veth, "up"}) != 0) {
196 RemoveInterface(veth);
197 RemoveInterface(peer);
198 return "";
199 }
200
201 if (process_runner_->Run({kIpPath, "link", "set", "dev", peer, "addr",
202 mac_addr, "down"}) != 0) {
203 RemoveInterface(veth);
204 RemoveInterface(peer);
205 return "";
206 }
207
208 if (process_runner_->Run({kBrctlPath, "addif", br_ifname, veth}) != 0) {
209 RemoveInterface(veth);
210 RemoveInterface(peer);
211 return "";
212 }
213
214 return peer;
215}
216
217void Datapath::RemoveInterface(const std::string& ifname) {
218 process_runner_->Run({kIpPath, "link", "delete", ifname}, false /* log */);
219}
220
221bool Datapath::AddInterfaceToContainer(int ns,
222 const std::string& src_ifname,
223 const std::string& dst_ifname,
224 const std::string& dst_ipv4,
225 bool fwd_multicast) {
226 const std::string pid = base::IntToString(ns);
227 return (process_runner_->Run(
228 {kIpPath, "link", "set", src_ifname, "netns", pid}) == 0) &&
229 (process_runner_->AddInterfaceToContainer(src_ifname, dst_ifname,
230 dst_ipv4, kDefaultNetmask,
231 fwd_multicast, pid) == 0);
232}
233
Garrick Evansf0ab7132019-06-18 14:50:42 +0900234bool Datapath::AddLegacyIPv4DNAT(const std::string& ipv4_addr) {
235 // Forward "unclaimed" packets to Android to allow inbound connections
236 // from devices on the LAN.
237 if (process_runner_->Run(
238 {kIpTablesPath, "-t", "nat", "-N", "dnat_arc", "-w"}) != 0)
239 return false;
240
241 if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "dnat_arc", "-j",
242 "DNAT", "--to-destination", ipv4_addr, "-w"}) !=
243 0) {
Garrick Evans54861622019-07-19 09:05:09 +0900244 RemoveLegacyIPv4DNAT();
Garrick Evansf0ab7132019-06-18 14:50:42 +0900245 return false;
246 }
247
248 // This chain is dynamically updated whenever the default interface
249 // changes.
250 if (process_runner_->Run(
251 {kIpTablesPath, "-t", "nat", "-N", "try_arc", "-w"}) != 0) {
252 RemoveLegacyIPv4DNAT();
253 return false;
254 }
255
256 if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING",
257 "-m", "socket", "--nowildcard", "-j", "ACCEPT",
258 "-w"}) != 0) {
259 RemoveLegacyIPv4DNAT();
260 return false;
261 }
262
263 if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING",
264 "-p", "tcp", "-j", "try_arc", "-w"}) != 0) {
265 RemoveLegacyIPv4DNAT();
266 return false;
267 }
268
269 if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING",
270 "-p", "udp", "-j", "try_arc", "-w"}) != 0) {
271 RemoveLegacyIPv4DNAT();
272 return false;
273 }
274
275 return true;
276}
277
278void Datapath::RemoveLegacyIPv4DNAT() {
279 process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-p",
280 "udp", "-j", "try_arc", "-w"});
281 process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-p",
282 "tcp", "-j", "try_arc", "-w"});
283 process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-m",
284 "socket", "--nowildcard", "-j", "ACCEPT", "-w"});
285 process_runner_->Run({kIpTablesPath, "-t", "nat", "-F", "try_arc", "-w"});
286 process_runner_->Run({kIpTablesPath, "-t", "nat", "-X", "try_arc", "-w"});
287 process_runner_->Run({kIpTablesPath, "-t", "nat", "-F", "dnat_arc", "-w"});
288 process_runner_->Run({kIpTablesPath, "-t", "nat", "-X", "dnat_arc", "-w"});
289}
290
Garrick Evans54861622019-07-19 09:05:09 +0900291bool Datapath::AddLegacyIPv4InboundDNAT(const std::string& ifname) {
292 return (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "try_arc",
293 "-i", ifname, "-j", "dnat_arc", "-w"}) != 0);
294}
295
296void Datapath::RemoveLegacyIPv4InboundDNAT() {
297 process_runner_->Run({kIpTablesPath, "-t", "nat", "-F", "try_arc", "-w"});
298}
299
Garrick Evansf0ab7132019-06-18 14:50:42 +0900300bool Datapath::AddInboundIPv4DNAT(const std::string& ifname,
301 const std::string& ipv4_addr) {
302 // Direct ingress IP traffic to existing sockets.
303 if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING",
304 "-i", ifname, "-m", "socket", "--nowildcard", "-j",
305 "ACCEPT", "-w"}) != 0)
306 return false;
307
308 // Direct ingress TCP & UDP traffic to ARC interface for new connections.
309 if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING",
310 "-i", ifname, "-p", "tcp", "-j", "DNAT",
311 "--to-destination", ipv4_addr, "-w"}) != 0) {
312 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
313 return false;
314 }
315 if (process_runner_->Run({kIpTablesPath, "-t", "nat", "-A", "PREROUTING",
316 "-i", ifname, "-p", "udp", "-j", "DNAT",
317 "--to-destination", ipv4_addr, "-w"}) != 0) {
318 RemoveInboundIPv4DNAT(ifname, ipv4_addr);
319 return false;
320 }
321
322 return true;
323}
324
325void Datapath::RemoveInboundIPv4DNAT(const std::string& ifname,
326 const std::string& ipv4_addr) {
327 process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-i",
328 ifname, "-p", "udp", "-j", "DNAT", "--to-destination",
329 ipv4_addr, "-w"});
330 process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-i",
331 ifname, "-p", "tcp", "-j", "DNAT", "--to-destination",
332 ipv4_addr, "-w"});
333 process_runner_->Run({kIpTablesPath, "-t", "nat", "-D", "PREROUTING", "-i",
334 ifname, "-m", "socket", "--nowildcard", "-j", "ACCEPT",
335 "-w"});
336}
337
338bool Datapath::AddOutboundIPv4(const std::string& ifname) {
339 return process_runner_->Run({kIpTablesPath, "-t", "filter", "-A", "FORWARD",
340 "-o", ifname, "-j", "ACCEPT", "-w"}) == 0;
341}
342
343void Datapath::RemoveOutboundIPv4(const std::string& ifname) {
344 process_runner_->Run({kIpTablesPath, "-t", "filter", "-D", "FORWARD", "-o",
345 ifname, "-j", "ACCEPT", "-w"});
346}
347
Garrick Evans260ff302019-07-25 11:22:50 +0900348bool Datapath::AddIPv6GatewayRoutes(const std::string& ifname,
349 const std::string& ipv6_addr,
350 const std::string& ipv6_router,
351 int ipv6_prefix_len,
352 int routing_table) {
353 std::string ipv6_addr_cidr =
354 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
355
356 process_runner_->Run(
357 {kIpPath, "-6", "addr", "add", ipv6_addr_cidr, "dev", ifname});
358
359 process_runner_->Run({kIpPath, "-6", "route", "add", ipv6_router, "dev",
360 ifname, "table", std::to_string(routing_table)});
361
362 process_runner_->Run({kIpPath, "-6", "route", "add", "default", "via",
363 ipv6_router, "dev", ifname, "table",
364 std::to_string(routing_table)});
365 return true;
366}
367
368void Datapath::RemoveIPv6GatewayRoutes(const std::string& ifname,
369 const std::string& ipv6_addr,
370 const std::string& ipv6_router,
371 int ipv6_prefix_len,
372 int routing_table) {
373 std::string ipv6_addr_cidr =
374 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
375
376 process_runner_->Run({kIpPath, "-6", "route", "del", "default", "via",
377 ipv6_router, "dev", ifname, "table",
378 std::to_string(routing_table)});
379 process_runner_->Run({kIpPath, "-6", "route", "del", ipv6_router, "dev",
380 ifname, "table", std::to_string(routing_table)});
381 process_runner_->Run(
382 {kIpPath, "-6", "addr", "del", ipv6_addr_cidr, "dev", ifname}, false);
383}
384
385bool Datapath::AddIPv6HostRoute(const std::string& ifname,
386 const std::string& ipv6_addr,
387 int ipv6_prefix_len) {
388 std::string ipv6_addr_cidr =
389 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
390
391 return process_runner_->Run({kIpPath, "-6", "route", "add", ipv6_addr_cidr,
392 "dev", ifname}) == 0;
393}
394
395void Datapath::RemoveIPv6HostRoute(const std::string& ifname,
396 const std::string& ipv6_addr,
397 int ipv6_prefix_len) {
398 std::string ipv6_addr_cidr =
399 ipv6_addr + "/" + std::to_string(ipv6_prefix_len);
400
401 process_runner_->Run(
402 {kIpPath, "-6", "route", "del", ipv6_addr_cidr, "dev", ifname});
403}
404
405bool Datapath::AddIPv6Neighbor(const std::string& ifname,
406 const std::string& ipv6_addr) {
407 return process_runner_->Run({kIpPath, "-6", "neigh", "add", "proxy",
408 ipv6_addr, "dev", ifname}) == 0;
409}
410
411void Datapath::RemoveIPv6Neighbor(const std::string& ifname,
412 const std::string& ipv6_addr) {
413 process_runner_->Run(
414 {kIpPath, "-6", "neigh", "del", "proxy", ipv6_addr, "dev", ifname});
415}
416
417bool Datapath::AddIPv6Forwarding(const std::string& ifname1,
418 const std::string& ifname2) {
419 if (process_runner_->Run({kIp6TablesPath, "-A", "FORWARD", "-i", ifname1,
420 "-o", ifname2, "-j", "ACCEPT", "-w"}) != 0) {
421 return false;
422 }
423
424 if (process_runner_->Run({kIp6TablesPath, "-A", "FORWARD", "-i", ifname2,
425 "-o", ifname1, "-j", "ACCEPT", "-w"}) != 0) {
426 RemoveIPv6Forwarding(ifname1, ifname2);
427 return false;
428 }
429
430 return true;
431}
432
433void Datapath::RemoveIPv6Forwarding(const std::string& ifname1,
434 const std::string& ifname2) {
435 process_runner_->Run({kIp6TablesPath, "-D", "FORWARD", "-i", ifname1, "-o",
436 ifname2, "-j", "ACCEPT", "-w"});
437
438 process_runner_->Run({kIp6TablesPath, "-D", "FORWARD", "-i", ifname2, "-o",
439 ifname1, "-j", "ACCEPT", "-w"});
440}
441
Garrick Evansf0ab7132019-06-18 14:50:42 +0900442} // namespace arc_networkd