blob: fb1cd62826c16e671652315a87fc69831dfbfd91 [file] [log] [blame]
Garrick Evans5d55f5e2019-07-17 15:28:10 +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/arc_service.h"
6
Garrick Evans54861622019-07-19 09:05:09 +09007#include <linux/rtnetlink.h>
8#include <net/if.h>
9
Garrick Evans5d55f5e2019-07-17 15:28:10 +090010#include <utility>
11
Garrick Evans54861622019-07-19 09:05:09 +090012#include <base/bind.h>
Garrick Evans5d55f5e2019-07-17 15:28:10 +090013#include <base/files/file_path.h>
14#include <base/files/file_util.h>
15#include <base/logging.h>
16#include <base/strings/string_number_conversions.h>
17#include <base/strings/string_util.h>
Garrick Evans54861622019-07-19 09:05:09 +090018#include <base/strings/stringprintf.h>
19#include <shill/net/rtnl_message.h>
20
21#include "arc/network/datapath.h"
22#include "arc/network/ipc.pb.h"
23#include "arc/network/mac_address_generator.h"
Garrick Evans3915af32019-07-25 15:44:34 +090024#include "arc/network/minijailed_process_runner.h"
Garrick Evans54861622019-07-19 09:05:09 +090025#include "arc/network/net_util.h"
26#include "arc/network/scoped_ns.h"
Garrick Evans5d55f5e2019-07-17 15:28:10 +090027
28namespace arc_networkd {
29namespace {
Garrick Evans54861622019-07-19 09:05:09 +090030constexpr pid_t kInvalidPID = -1;
31constexpr pid_t kTestPID = -2;
Garrick Evans260ff302019-07-25 11:22:50 +090032constexpr int kInvalidTableID = -1;
33constexpr int kMaxTableRetries = 10; // Based on 1 second delay.
34constexpr base::TimeDelta kTableRetryDelay = base::TimeDelta::FromSeconds(1);
35// Android adds a constant to the interface index to derive the table id.
36// This is defined in system/netd/server/RouteController.h
37constexpr int kRouteControllerRouteTableOffsetFromIndex = 1000;
Garrick Evans54861622019-07-19 09:05:09 +090038
39// This wrapper is required since the base class is a singleton that hides its
40// constructor. It is necessary here because the message loop thread has to be
41// reassociated to the container's network namespace; and since the container
42// can be repeatedly created and destroyed, the handler must be as well.
43class RTNetlinkHandler : public shill::RTNLHandler {
44 public:
45 RTNetlinkHandler() = default;
46 ~RTNetlinkHandler() = default;
47
48 private:
49 DISALLOW_COPY_AND_ASSIGN(RTNetlinkHandler);
50};
Garrick Evans5d55f5e2019-07-17 15:28:10 +090051
Garrick Evans260ff302019-07-25 11:22:50 +090052int GetAndroidRoutingTableId(const std::string& ifname, pid_t pid) {
53 base::FilePath ifindex_path(base::StringPrintf(
54 "/proc/%d/root/sys/class/net/%s/ifindex", pid, ifname.c_str()));
55 std::string contents;
56 if (!base::ReadFileToString(ifindex_path, &contents)) {
57 PLOG(WARNING) << "Could not read " << ifindex_path.value();
58 return kInvalidTableID;
59 }
60
61 base::TrimWhitespaceASCII(contents, base::TRIM_TRAILING, &contents);
62 int table_id = kInvalidTableID;
63 if (!base::StringToInt(contents, &table_id)) {
64 LOG(ERROR) << "Could not parse ifindex from " << ifindex_path.value()
65 << ": " << contents;
66 return kInvalidTableID;
67 }
68 table_id += kRouteControllerRouteTableOffsetFromIndex;
69
70 LOG(INFO) << "Found table id " << table_id << " for container interface "
71 << ifname;
72 return table_id;
73}
74
Garrick Evans5d55f5e2019-07-17 15:28:10 +090075// TODO(garrick): Remove this workaround ASAP.
76int GetContainerPID() {
77 const base::FilePath path("/run/containers/android-run_oci/container.pid");
78 std::string pid_str;
79 if (!base::ReadFileToStringWithMaxSize(path, &pid_str, 16 /* max size */)) {
80 LOG(ERROR) << "Failed to read pid file";
Garrick Evans54861622019-07-19 09:05:09 +090081 return kInvalidPID;
Garrick Evans5d55f5e2019-07-17 15:28:10 +090082 }
83 int pid;
84 if (!base::StringToInt(base::TrimWhitespaceASCII(pid_str, base::TRIM_ALL),
85 &pid)) {
86 LOG(ERROR) << "Failed to convert container pid string";
Garrick Evans54861622019-07-19 09:05:09 +090087 return kInvalidPID;
Garrick Evans5d55f5e2019-07-17 15:28:10 +090088 }
89 LOG(INFO) << "Read container pid as " << pid;
90 return pid;
91}
92
93} // namespace
94
Garrick Evans54861622019-07-19 09:05:09 +090095ArcService::ArcService(DeviceManagerBase* dev_mgr,
96 bool is_legacy,
97 std::unique_ptr<Datapath> datapath)
Garrick Evans5d55f5e2019-07-17 15:28:10 +090098 : GuestService(is_legacy ? GuestMessage::ARC_LEGACY : GuestMessage::ARC,
Garrick Evans54861622019-07-19 09:05:09 +090099 dev_mgr),
100 pid_(kInvalidPID) {
Garrick Evans260ff302019-07-25 11:22:50 +0900101 if (!datapath) {
102 runner_ = std::make_unique<MinijailedProcessRunner>();
103 datapath = std::make_unique<Datapath>(runner_.get());
104 }
Garrick Evans54861622019-07-19 09:05:09 +0900105
106 datapath_ = std::move(datapath);
Garrick Evans260ff302019-07-25 11:22:50 +0900107 dev_mgr_->RegisterDeviceIPv6AddressFoundHandler(
108 base::Bind(&ArcService::SetupIPv6, weak_factory_.GetWeakPtr()));
Garrick Evans3915af32019-07-25 15:44:34 +0900109
110 // Load networking modules needed by Android that are not compiled in the
111 // kernel. Android does not allow auto-loading of kernel modules.
Garrick Evans3915af32019-07-25 15:44:34 +0900112
113 // These must succeed.
Taoyu Lia0c43a72019-10-01 10:29:57 +0900114 if (datapath_->runner().ModprobeAll({
Garrick Evans3915af32019-07-25 15:44:34 +0900115 // The netfilter modules needed by netd for iptables commands.
116 "ip6table_filter",
117 "ip6t_ipv6header",
118 "ip6t_REJECT",
119 // The xfrm modules needed for Android's ipsec APIs.
120 "xfrm4_mode_transport",
121 "xfrm4_mode_tunnel",
122 "xfrm6_mode_transport",
123 "xfrm6_mode_tunnel",
124 // The ipsec modules for AH and ESP encryption for ipv6.
125 "ah6",
126 "esp6",
127 }) != 0) {
128 LOG(ERROR) << "One or more required kernel modules failed to load.";
129 }
130
131 // Optional modules.
Taoyu Lia0c43a72019-10-01 10:29:57 +0900132 if (datapath_->runner().ModprobeAll({
Garrick Evans3915af32019-07-25 15:44:34 +0900133 // This module is not available in kernels < 3.18
134 "nf_reject_ipv6",
135 // These modules are needed for supporting Chrome traffic on Android
136 // VPN which uses Android's NAT feature. Android NAT sets up iptables
137 // rules that use these conntrack modules for FTP/TFTP.
138 "nf_nat_ftp",
139 "nf_nat_tftp",
140 }) != 0) {
141 LOG(WARNING) << "One or more optional kernel modules failed to load.";
142 }
Garrick Evans54861622019-07-19 09:05:09 +0900143}
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900144
145void ArcService::OnStart() {
Garrick Evans54861622019-07-19 09:05:09 +0900146 LOG(INFO) << "ARC++ network service starting";
147 pid_ = GetContainerPID();
148 if (pid_ == kInvalidPID) {
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900149 LOG(ERROR) << "Cannot start service - invalid container PID";
150 return;
151 }
152
Garrick Evans54861622019-07-19 09:05:09 +0900153 // Start listening for RTNetlink messages in the container's net namespace
154 // to be notified whenever it brings up an interface.
155 {
156 ScopedNS ns(pid_);
157 if (ns.IsValid()) {
158 rtnl_handler_ = std::make_unique<RTNetlinkHandler>();
159 rtnl_handler_->Start(RTMGRP_LINK);
160 link_listener_ = std::make_unique<shill::RTNLListener>(
161 shill::RTNLHandler::kRequestLink,
162 Bind(&ArcService::LinkMsgHandler, weak_factory_.GetWeakPtr()),
163 rtnl_handler_.get());
164 } else {
165 // This is bad - it means we won't ever be able to tell when the container
166 // brings up an interface.
167 LOG(ERROR)
168 << "Cannot start netlink listener - invalid container namespace?";
169 }
170 }
171
172 // Start known host devices, any new ones will be setup in the process.
173 dev_mgr_->ProcessDevices(
174 base::Bind(&ArcService::StartDevice, weak_factory_.GetWeakPtr()));
175
176 // If this is the first time the service is starting this will create the
177 // Android bridge device; otherwise it does nothing. Do this after processing
178 // the existing devices so it doesn't get started twice.
179 dev_mgr_->Add(guest_ == GuestMessage::ARC_LEGACY ? kAndroidLegacyDevice
180 : kAndroidDevice);
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900181
182 GuestMessage msg;
183 msg.set_event(GuestMessage::START);
Garrick Evans54861622019-07-19 09:05:09 +0900184 msg.set_arc_pid(pid_);
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900185 msg.set_type(guest_);
186 DispatchMessage(msg);
Garrick Evans54861622019-07-19 09:05:09 +0900187
188 // Finally, call the base implementation.
189 GuestService::OnStart();
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900190}
191
192void ArcService::OnStop() {
Garrick Evans54861622019-07-19 09:05:09 +0900193 LOG(INFO) << "ARC++ network service stopping";
194 // Call the base implementation.
195 GuestService::OnStop();
196
197 // Stop known host devices. Note that this does not teardown any existing
198 // devices.
199 dev_mgr_->ProcessDevices(
200 base::Bind(&ArcService::StopDevice, weak_factory_.GetWeakPtr()));
201
202 rtnl_handler_->RemoveListener(link_listener_.get());
203 link_listener_.reset();
204 rtnl_handler_.reset();
205
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900206 GuestMessage msg;
207 msg.set_event(GuestMessage::STOP);
208 msg.set_type(guest_);
209 DispatchMessage(msg);
210
Garrick Evans54861622019-07-19 09:05:09 +0900211 pid_ = kInvalidPID;
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900212}
213
Garrick Evans54861622019-07-19 09:05:09 +0900214void ArcService::OnDeviceAdded(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900215 // ARC N uses legacy single networking and only requires the arcbr0/arc0
216 // configuration. Any other device can be safely ignored.
217 if (guest_ == GuestMessage::ARC_LEGACY && !device->IsLegacyAndroid())
Garrick Evans54861622019-07-19 09:05:09 +0900218 return;
Garrick Evansba575742019-07-17 15:48:08 +0900219
Garrick Evans54861622019-07-19 09:05:09 +0900220 const auto& config = device->config();
221
222 LOG(INFO) << "Adding device " << device->ifname()
223 << " bridge: " << config.host_ifname()
Garrick Evans310ab552019-10-08 11:07:53 +0900224 << " guest_iface: " << config.guest_ifname();
Garrick Evans54861622019-07-19 09:05:09 +0900225
226 // Create the bridge.
227 if (!datapath_->AddBridge(config.host_ifname(),
228 IPv4AddressToString(config.host_ipv4_addr()))) {
229 LOG(ERROR) << "Failed to setup arc bridge: " << config.host_ifname();
230 return;
231 }
232
233 // Setup the iptables.
234 if (device->IsLegacyAndroid()) {
235 if (!datapath_->AddLegacyIPv4DNAT(
236 IPv4AddressToString(config.guest_ipv4_addr())))
237 LOG(ERROR) << "Failed to configure ARC traffic rules";
238
239 if (!datapath_->AddOutboundIPv4(config.host_ifname()))
240 LOG(ERROR) << "Failed to configure egress traffic rules";
241 } else if (!device->IsAndroid()) {
242 if (!datapath_->AddInboundIPv4DNAT(
243 device->ifname(), IPv4AddressToString(config.guest_ipv4_addr())))
244 LOG(ERROR) << "Failed to configure ingress traffic rules for "
245 << device->ifname();
246
247 if (!datapath_->AddOutboundIPv4(config.host_ifname()))
248 LOG(ERROR) << "Failed to configure egress traffic rules";
249 }
250
Garrick Evans2c263102019-07-26 16:07:18 +0900251 device->set_context(guest_, std::make_unique<Context>());
Garrick Evans54861622019-07-19 09:05:09 +0900252
253 StartDevice(device);
254}
255
256void ArcService::StartDevice(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900257 // This can happen if OnDeviceAdded is invoked when the container is down.
258 if (pid_ == kInvalidPID)
Garrick Evans54861622019-07-19 09:05:09 +0900259 return;
260
Garrick Evans2c263102019-07-26 16:07:18 +0900261 // If there is no context, then this is a new device and it needs to run
262 // through the full setup process.
263 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
264 if (!ctx)
Garrick Evans54861622019-07-19 09:05:09 +0900265 return OnDeviceAdded(device);
266
Garrick Evans2c263102019-07-26 16:07:18 +0900267 if (ctx->IsStarted()) {
268 LOG(ERROR) << "Attempt to restart device " << device->ifname();
269 return;
270 }
271
272 const auto& config = device->config();
273
Garrick Evans54861622019-07-19 09:05:09 +0900274 LOG(INFO) << "Starting device " << device->ifname()
275 << " bridge: " << config.host_ifname()
276 << " guest_iface: " << config.guest_ifname()
277 << " for container pid " << pid_;
278
279 std::string veth_ifname = datapath_->AddVirtualBridgedInterface(
280 device->ifname(), MacAddressToString(config.guest_mac_addr()),
281 config.host_ifname());
282 if (veth_ifname.empty()) {
283 LOG(ERROR) << "Failed to create virtual interface for container";
284 return;
285 }
286
287 if (!datapath_->AddInterfaceToContainer(
288 pid_, veth_ifname, config.guest_ifname(),
289 IPv4AddressToString(config.guest_ipv4_addr()),
290 device->options().fwd_multicast)) {
291 LOG(ERROR) << "Failed to create container interface.";
292 datapath_->RemoveInterface(veth_ifname);
293 datapath_->RemoveBridge(config.host_ifname());
294 return;
295 }
296
297 // Signal the container that the network device is ready.
298 // This is only applicable for arc0.
299 if (device->IsAndroid() || device->IsLegacyAndroid()) {
Garrick Evans260ff302019-07-25 11:22:50 +0900300 datapath_->runner().WriteSentinelToContainer(base::IntToString(pid_));
Garrick Evans54861622019-07-19 09:05:09 +0900301 }
Garrick Evans2c263102019-07-26 16:07:18 +0900302
303 ctx->Start();
Garrick Evans54861622019-07-19 09:05:09 +0900304}
305
306void ArcService::OnDeviceRemoved(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900307 // ARC N uses legacy single networking and only requires the arcbr0/arc0
308 // configuration. Any other device can be safely ignored.
309 if (guest_ == GuestMessage::ARC_LEGACY && !device->IsLegacyAndroid())
Garrick Evans54861622019-07-19 09:05:09 +0900310 return;
311
Garrick Evans310ab552019-10-08 11:07:53 +0900312 // If the container is down, this call does nothing.
Garrick Evans54861622019-07-19 09:05:09 +0900313 StopDevice(device);
314
315 const auto& config = device->config();
316
317 LOG(INFO) << "Removing device " << device->ifname()
318 << " bridge: " << config.host_ifname()
319 << " guest_iface: " << config.guest_ifname();
320
Garrick Evans260ff302019-07-25 11:22:50 +0900321 device->Disable();
Garrick Evans54861622019-07-19 09:05:09 +0900322 if (device->IsLegacyAndroid()) {
323 datapath_->RemoveOutboundIPv4(config.host_ifname());
324 datapath_->RemoveLegacyIPv4DNAT();
325 } else if (!device->IsAndroid()) {
326 datapath_->RemoveOutboundIPv4(config.host_ifname());
327 datapath_->RemoveInboundIPv4DNAT(
328 device->ifname(), IPv4AddressToString(config.guest_ipv4_addr()));
329 }
330
331 datapath_->RemoveBridge(config.host_ifname());
332
Garrick Evans2c263102019-07-26 16:07:18 +0900333 device->set_context(guest_, nullptr);
Garrick Evans54861622019-07-19 09:05:09 +0900334}
335
336void ArcService::StopDevice(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900337 // This can happen if the device if OnDeviceRemoved is invoked when the
338 // container is down.
339 if (pid_ == kInvalidPID)
Garrick Evans54861622019-07-19 09:05:09 +0900340 return;
341
Garrick Evans2c263102019-07-26 16:07:18 +0900342 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
343 if (!ctx) {
344 LOG(ERROR) << "Attempt to stop removed device " << device->ifname();
345 return;
346 }
347
348 if (!ctx->IsStarted()) {
349 LOG(ERROR) << "Attempt to re-stop device " << device->ifname();
350 return;
351 }
352
353 const auto& config = device->config();
354
Garrick Evans54861622019-07-19 09:05:09 +0900355 LOG(INFO) << "Stopping device " << device->ifname()
356 << " bridge: " << config.host_ifname()
Garrick Evans310ab552019-10-08 11:07:53 +0900357 << " guest_iface: " << config.guest_ifname()
358 << " for container pid " << pid_;
Garrick Evans54861622019-07-19 09:05:09 +0900359
360 device->Disable();
361 if (!device->IsAndroid()) {
362 datapath_->RemoveInterface(ArcVethHostName(device->ifname()));
363 }
Garrick Evans2c263102019-07-26 16:07:18 +0900364
365 ctx->Stop();
Garrick Evans54861622019-07-19 09:05:09 +0900366}
367
Garrick Evans54861622019-07-19 09:05:09 +0900368void ArcService::OnDefaultInterfaceChanged(const std::string& ifname) {
Garrick Evansdc3fc452019-09-06 14:15:04 +0900369 if (pid_ == kInvalidPID)
Garrick Evans54861622019-07-19 09:05:09 +0900370 return;
371
Garrick Evansdc3fc452019-09-06 14:15:04 +0900372 // For ARC N, we must always be able to find the arc0 device and, at a
373 // minimum, disable it.
374 if (guest_ == GuestMessage::ARC_LEGACY) {
375 datapath_->RemoveLegacyIPv4InboundDNAT();
376 auto* device = dev_mgr_->FindByGuestInterface("arc0");
377 if (!device) {
378 LOG(DFATAL) << "Expected legacy Android device missing";
379 return;
380 }
381 device->Disable();
Garrick Evans54861622019-07-19 09:05:09 +0900382
Garrick Evansdc3fc452019-09-06 14:15:04 +0900383 // If a new default interface was given, then re-enable with that.
384 if (!ifname.empty()) {
385 datapath_->AddLegacyIPv4InboundDNAT(ifname);
386 device->Enable(ifname);
387 }
388 return;
389 }
390
391 // For ARC P and later, we're only concerned with resetting the device when it
392 // becomes the default (again) in order to ensure any previous configuration.
393 // is cleared.
394 if (ifname.empty())
395 return;
396
397 auto* device = dev_mgr_->FindByGuestInterface(ifname);
Garrick Evans54861622019-07-19 09:05:09 +0900398 if (!device) {
Garrick Evansdc3fc452019-09-06 14:15:04 +0900399 LOG(ERROR) << "Expected default device missing: " << ifname;
Garrick Evans54861622019-07-19 09:05:09 +0900400 return;
401 }
Garrick Evansdc3fc452019-09-06 14:15:04 +0900402 device->StopIPv6Routing();
403 device->StartIPv6Routing(ifname);
Garrick Evans54861622019-07-19 09:05:09 +0900404}
405
406void ArcService::LinkMsgHandler(const shill::RTNLMessage& msg) {
407 if (!msg.HasAttribute(IFLA_IFNAME)) {
408 LOG(ERROR) << "Link event message does not have IFLA_IFNAME";
409 return;
410 }
411 bool link_up = msg.link_status().flags & IFF_UP;
412 shill::ByteString b(msg.GetAttribute(IFLA_IFNAME));
413 std::string ifname(reinterpret_cast<const char*>(
414 b.GetSubstring(0, IFNAMSIZ).GetConstData()));
415
416 auto* device = dev_mgr_->FindByGuestInterface(ifname);
Garrick Evans2c263102019-07-26 16:07:18 +0900417 if (!device)
418 return;
419
420 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
421 if (!ctx) {
422 LOG(DFATAL) << "Context missing";
423 return;
424 }
425
426 // If the link status is unchanged, there is nothing to do.
427 if (!ctx->SetLinkUp(link_up))
Garrick Evans54861622019-07-19 09:05:09 +0900428 return;
429
430 if (!link_up) {
431 LOG(INFO) << ifname << " is now down";
432 return;
433 }
434 LOG(INFO) << ifname << " is now up";
435
436 if (device->IsAndroid())
437 return;
438
439 if (device->IsLegacyAndroid()) {
440 OnDefaultInterfaceChanged(dev_mgr_->DefaultInterface());
441 return;
442 }
443
444 device->Enable(ifname);
445}
446
Garrick Evans260ff302019-07-25 11:22:50 +0900447void ArcService::SetupIPv6(Device* device) {
448 device->RegisterIPv6TeardownHandler(
449 base::Bind(&ArcService::TeardownIPv6, weak_factory_.GetWeakPtr()));
450
451 auto& ipv6_config = device->ipv6_config();
452 if (ipv6_config.ifname.empty())
453 return;
454
Garrick Evans2c263102019-07-26 16:07:18 +0900455 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
456 if (!ctx) {
457 LOG(DFATAL) << "Context missing";
458 return;
459 }
460 if (ctx->HasIPv6())
461 return;
462
Garrick Evans260ff302019-07-25 11:22:50 +0900463 LOG(INFO) << "Setting up IPv6 for " << ipv6_config.ifname;
464
Garrick Evans2c263102019-07-26 16:07:18 +0900465 int table_id =
Garrick Evans260ff302019-07-25 11:22:50 +0900466 GetAndroidRoutingTableId(device->config().guest_ifname(), pid_);
Garrick Evans2c263102019-07-26 16:07:18 +0900467 if (table_id == kInvalidTableID) {
468 if (ctx->RoutingTableAttempts() < kMaxTableRetries) {
Garrick Evans260ff302019-07-25 11:22:50 +0900469 LOG(INFO) << "Could not look up routing table ID for container interface "
470 << device->config().guest_ifname() << " - trying again...";
471 base::MessageLoop::current()->task_runner()->PostDelayedTask(
472 FROM_HERE,
473 base::Bind(&ArcService::SetupIPv6, weak_factory_.GetWeakPtr(),
474 device),
475 kTableRetryDelay);
476 } else {
477 LOG(DFATAL)
478 << "Could not look up routing table ID for container interface "
479 << device->config().guest_ifname();
480 }
481 return;
482 }
483
484 LOG(INFO) << "Setting IPv6 address " << ipv6_config.addr
485 << "/128, gateway=" << ipv6_config.router << " on "
486 << ipv6_config.ifname;
487
488 char buf[INET6_ADDRSTRLEN] = {0};
489 if (!inet_ntop(AF_INET6, &ipv6_config.addr, buf, sizeof(buf))) {
490 LOG(DFATAL) << "Invalid address: " << ipv6_config.addr;
491 return;
492 }
493 std::string addr = buf;
494
495 if (!inet_ntop(AF_INET6, &ipv6_config.router, buf, sizeof(buf))) {
496 LOG(DFATAL) << "Invalid router address: " << ipv6_config.router;
497 return;
498 }
499 std::string router = buf;
500
501 const auto& config = device->config();
502 {
503 ScopedNS ns(pid_);
504 if (!ns.IsValid()) {
505 LOG(ERROR) << "Invalid container namespace (" << pid_
506 << ") - cannot configure IPv6.";
507 return;
508 }
509 if (!datapath_->AddIPv6GatewayRoutes(config.guest_ifname(), addr, router,
Garrick Evans2c263102019-07-26 16:07:18 +0900510 ipv6_config.prefix_len, table_id)) {
Garrick Evans260ff302019-07-25 11:22:50 +0900511 LOG(ERROR) << "Failed to setup IPv6 routes in the container";
512 return;
513 }
514 }
515
516 if (!datapath_->AddIPv6HostRoute(config.host_ifname(), addr,
517 ipv6_config.prefix_len)) {
518 LOG(ERROR) << "Failed to setup the IPv6 route for interface "
519 << config.host_ifname();
520 return;
521 }
522
523 if (!datapath_->AddIPv6Neighbor(ipv6_config.ifname, addr)) {
524 LOG(ERROR) << "Failed to setup the IPv6 neighbor proxy";
525 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
526 ipv6_config.prefix_len);
527 return;
528 }
529
530 if (!datapath_->AddIPv6Forwarding(ipv6_config.ifname,
531 device->config().host_ifname())) {
532 LOG(ERROR) << "Failed to setup iptables for IPv6";
533 datapath_->RemoveIPv6Neighbor(ipv6_config.ifname, addr);
534 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
535 ipv6_config.prefix_len);
536 return;
537 }
538
Garrick Evans2c263102019-07-26 16:07:18 +0900539 ctx->SetHasIPv6(table_id);
Garrick Evans260ff302019-07-25 11:22:50 +0900540}
541
542void ArcService::TeardownIPv6(Device* device) {
Garrick Evans2c263102019-07-26 16:07:18 +0900543 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
544 if (!ctx || !ctx->HasIPv6())
Garrick Evans260ff302019-07-25 11:22:50 +0900545 return;
546
Garrick Evans2c263102019-07-26 16:07:18 +0900547 auto& ipv6_config = device->ipv6_config();
Garrick Evans260ff302019-07-25 11:22:50 +0900548 LOG(INFO) << "Clearing IPv6 for " << ipv6_config.ifname;
Garrick Evans2c263102019-07-26 16:07:18 +0900549 int table_id = ctx->RoutingTableID();
Garrick Evansdc3fc452019-09-06 14:15:04 +0900550 ctx->ClearIPv6();
Garrick Evans260ff302019-07-25 11:22:50 +0900551
552 char buf[INET6_ADDRSTRLEN] = {0};
553 if (!inet_ntop(AF_INET6, &ipv6_config.addr, buf, sizeof(buf))) {
554 LOG(DFATAL) << "Invalid address: " << ipv6_config.addr;
555 return;
556 }
557 std::string addr = buf;
558
559 if (!inet_ntop(AF_INET6, &ipv6_config.router, buf, sizeof(buf))) {
560 LOG(DFATAL) << "Invalid router address: " << ipv6_config.router;
561 return;
562 }
563 std::string router = buf;
564
565 const auto& config = device->config();
566 datapath_->RemoveIPv6Forwarding(ipv6_config.ifname, config.host_ifname());
567 datapath_->RemoveIPv6Neighbor(ipv6_config.ifname, addr);
568 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
569 ipv6_config.prefix_len);
570
571 ScopedNS ns(pid_);
572 if (ns.IsValid()) {
573 datapath_->RemoveIPv6GatewayRoutes(config.guest_ifname(), addr, router,
Garrick Evans2c263102019-07-26 16:07:18 +0900574 ipv6_config.prefix_len, table_id);
Garrick Evans260ff302019-07-25 11:22:50 +0900575 }
576}
577
Garrick Evans54861622019-07-19 09:05:09 +0900578void ArcService::SetPIDForTestingOnly() {
579 pid_ = kTestPID;
580}
Garrick Evansba575742019-07-17 15:48:08 +0900581
Garrick Evans2c263102019-07-26 16:07:18 +0900582// Context
583
584ArcService::Context::Context() : Device::Context() {
585 Stop();
586}
587
588void ArcService::Context::Start() {
589 Stop();
590 started_ = true;
591}
592
593void ArcService::Context::Stop() {
594 started_ = false;
595 link_up_ = false;
596 routing_table_id_ = kInvalidTableID;
597 routing_table_attempts_ = 0;
598}
599
600bool ArcService::Context::IsStarted() const {
601 return started_;
602}
603
604bool ArcService::Context::IsLinkUp() const {
605 return link_up_;
606}
607
608bool ArcService::Context::SetLinkUp(bool link_up) {
609 if (link_up == link_up_)
610 return false;
611
612 link_up_ = link_up;
613 return true;
614}
615
616bool ArcService::Context::HasIPv6() const {
617 return routing_table_id_ != kInvalidTableID;
618}
619
620bool ArcService::Context::SetHasIPv6(int routing_table_id) {
621 if (routing_table_id <= kRouteControllerRouteTableOffsetFromIndex)
622 return false;
623
624 routing_table_id_ = routing_table_id;
625 return true;
626}
627
Garrick Evansdc3fc452019-09-06 14:15:04 +0900628void ArcService::Context::ClearIPv6() {
629 routing_table_id_ = kInvalidTableID;
630 routing_table_attempts_ = 0;
631}
632
Garrick Evans2c263102019-07-26 16:07:18 +0900633int ArcService::Context::RoutingTableID() const {
634 return routing_table_id_;
635}
636
637int ArcService::Context::RoutingTableAttempts() {
638 return routing_table_attempts_++;
639}
640
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900641} // namespace arc_networkd