blob: c9400ba4ae257562e18f0b64a972f0f7909719c5 [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>
Garrick Evans1f5a3612019-11-08 12:59:03 +090019#include <brillo/key_value_store.h>
Garrick Evansb4eb3892019-11-13 12:07:07 +090020#include <chromeos/constants/vm_tools.h>
Garrick Evans54861622019-07-19 09:05:09 +090021#include <shill/net/rtnl_message.h>
22
23#include "arc/network/datapath.h"
24#include "arc/network/ipc.pb.h"
25#include "arc/network/mac_address_generator.h"
Garrick Evans3915af32019-07-25 15:44:34 +090026#include "arc/network/minijailed_process_runner.h"
Garrick Evans54861622019-07-19 09:05:09 +090027#include "arc/network/net_util.h"
28#include "arc/network/scoped_ns.h"
Garrick Evans5d55f5e2019-07-17 15:28:10 +090029
30namespace arc_networkd {
31namespace {
Garrick Evans54861622019-07-19 09:05:09 +090032constexpr pid_t kInvalidPID = -1;
Garrick Evansb4eb3892019-11-13 12:07:07 +090033constexpr pid_t kTestPID = -2;
34constexpr int32_t kInvalidCID = -1;
Garrick Evans260ff302019-07-25 11:22:50 +090035constexpr int kInvalidTableID = -1;
36constexpr int kMaxTableRetries = 10; // Based on 1 second delay.
37constexpr base::TimeDelta kTableRetryDelay = base::TimeDelta::FromSeconds(1);
38// Android adds a constant to the interface index to derive the table id.
39// This is defined in system/netd/server/RouteController.h
40constexpr int kRouteControllerRouteTableOffsetFromIndex = 1000;
Garrick Evans54861622019-07-19 09:05:09 +090041
42// This wrapper is required since the base class is a singleton that hides its
43// constructor. It is necessary here because the message loop thread has to be
44// reassociated to the container's network namespace; and since the container
45// can be repeatedly created and destroyed, the handler must be as well.
46class RTNetlinkHandler : public shill::RTNLHandler {
47 public:
48 RTNetlinkHandler() = default;
49 ~RTNetlinkHandler() = default;
50
51 private:
52 DISALLOW_COPY_AND_ASSIGN(RTNetlinkHandler);
53};
Garrick Evans5d55f5e2019-07-17 15:28:10 +090054
Garrick Evans260ff302019-07-25 11:22:50 +090055int GetAndroidRoutingTableId(const std::string& ifname, pid_t pid) {
56 base::FilePath ifindex_path(base::StringPrintf(
57 "/proc/%d/root/sys/class/net/%s/ifindex", pid, ifname.c_str()));
58 std::string contents;
59 if (!base::ReadFileToString(ifindex_path, &contents)) {
60 PLOG(WARNING) << "Could not read " << ifindex_path.value();
61 return kInvalidTableID;
62 }
63
64 base::TrimWhitespaceASCII(contents, base::TRIM_TRAILING, &contents);
65 int table_id = kInvalidTableID;
66 if (!base::StringToInt(contents, &table_id)) {
67 LOG(ERROR) << "Could not parse ifindex from " << ifindex_path.value()
68 << ": " << contents;
69 return kInvalidTableID;
70 }
71 table_id += kRouteControllerRouteTableOffsetFromIndex;
72
73 LOG(INFO) << "Found table id " << table_id << " for container interface "
74 << ifname;
75 return table_id;
76}
77
Garrick Evans1f5a3612019-11-08 12:59:03 +090078bool ShouldEnableMultinet() {
79 static const char kLsbReleasePath[] = "/etc/lsb-release";
80 static int kMinAndroidSdkVersion = 28; // P
81 static int kMinChromeMilestone = 76;
82
83 brillo::KeyValueStore store;
84 if (!store.Load(base::FilePath(kLsbReleasePath))) {
85 LOG(ERROR) << "Could not read lsb-release";
86 return false;
87 }
88
89 std::string value;
90 if (!store.GetString("CHROMEOS_ARC_ANDROID_SDK_VERSION", &value)) {
91 LOG(ERROR) << "ARC multi-networking disabled - cannot determine Android "
92 "SDK version";
93 return false;
94 }
95 int ver = 0;
96 if (!base::StringToInt(value.c_str(), &ver)) {
97 LOG(ERROR) << "ARC multi-networking disabled - invalid Android SDK version";
98 return false;
99 }
100 if (ver < kMinAndroidSdkVersion) {
101 LOG(INFO) << "ARC multi-networking disabled for Android SDK " << value;
102 return false;
103 }
104 if (!store.GetString("CHROMEOS_RELEASE_CHROME_MILESTONE", &value)) {
105 LOG(ERROR)
106 << "ARC multi-networking disabled - cannot determine Chrome milestone";
107 return false;
108 }
109 if (atoi(value.c_str()) < kMinChromeMilestone) {
110 LOG(INFO) << "ARC multi-networking disabled for Chrome M" << value;
111 return false;
112 }
113 return true;
114}
115
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900116// TODO(garrick): Remove this workaround ASAP.
117int GetContainerPID() {
118 const base::FilePath path("/run/containers/android-run_oci/container.pid");
119 std::string pid_str;
120 if (!base::ReadFileToStringWithMaxSize(path, &pid_str, 16 /* max size */)) {
121 LOG(ERROR) << "Failed to read pid file";
Garrick Evans54861622019-07-19 09:05:09 +0900122 return kInvalidPID;
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900123 }
124 int pid;
125 if (!base::StringToInt(base::TrimWhitespaceASCII(pid_str, base::TRIM_ALL),
126 &pid)) {
127 LOG(ERROR) << "Failed to convert container pid string";
Garrick Evans54861622019-07-19 09:05:09 +0900128 return kInvalidPID;
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900129 }
130 LOG(INFO) << "Read container pid as " << pid;
131 return pid;
132}
133
Garrick Evans508a4bc2019-11-14 08:45:52 +0900134bool IsArcVm() {
135 const base::FilePath path("/run/chrome/is_arcvm");
136 std::string contents;
137 if (!base::ReadFileToString(path, &contents)) {
138 PLOG(ERROR) << "Could not read " << path.value();
139 }
140 return contents == "1";
141}
142
143GuestMessage::GuestType ArcGuest(bool* is_legacy_override_for_testing) {
144 if (is_legacy_override_for_testing)
145 return *is_legacy_override_for_testing ? GuestMessage::ARC_LEGACY
146 : GuestMessage::ARC;
147
148 return IsArcVm() ? GuestMessage::ARC_VM
149 : ShouldEnableMultinet() ? GuestMessage::ARC
150 : GuestMessage::ARC_LEGACY;
151}
152
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900153} // namespace
154
Garrick Evans54861622019-07-19 09:05:09 +0900155ArcService::ArcService(DeviceManagerBase* dev_mgr,
Taoyu Li179dcc62019-10-17 11:21:08 +0900156 Datapath* datapath,
Garrick Evans1f5a3612019-11-08 12:59:03 +0900157 bool* is_legacy)
Garrick Evans508a4bc2019-11-14 08:45:52 +0900158 : GuestService(ArcGuest(is_legacy), dev_mgr), datapath_(datapath) {
Taoyu Li179dcc62019-10-17 11:21:08 +0900159 DCHECK(datapath_);
Garrick Evans3915af32019-07-25 15:44:34 +0900160
Garrick Evans508a4bc2019-11-14 08:45:52 +0900161 if (guest_ == GuestMessage::ARC_VM)
162 impl_ = std::make_unique<VmImpl>(dev_mgr, datapath);
163 else
164 impl_ = std::make_unique<ContainerImpl>(dev_mgr, datapath, guest_);
165
Garrick Evans3915af32019-07-25 15:44:34 +0900166 // Load networking modules needed by Android that are not compiled in the
167 // kernel. Android does not allow auto-loading of kernel modules.
Garrick Evans3915af32019-07-25 15:44:34 +0900168
169 // These must succeed.
Taoyu Lia0c43a72019-10-01 10:29:57 +0900170 if (datapath_->runner().ModprobeAll({
Garrick Evans3915af32019-07-25 15:44:34 +0900171 // The netfilter modules needed by netd for iptables commands.
172 "ip6table_filter",
173 "ip6t_ipv6header",
174 "ip6t_REJECT",
175 // The xfrm modules needed for Android's ipsec APIs.
176 "xfrm4_mode_transport",
177 "xfrm4_mode_tunnel",
178 "xfrm6_mode_transport",
179 "xfrm6_mode_tunnel",
180 // The ipsec modules for AH and ESP encryption for ipv6.
181 "ah6",
182 "esp6",
183 }) != 0) {
184 LOG(ERROR) << "One or more required kernel modules failed to load.";
185 }
186
187 // Optional modules.
Taoyu Lia0c43a72019-10-01 10:29:57 +0900188 if (datapath_->runner().ModprobeAll({
Garrick Evans3915af32019-07-25 15:44:34 +0900189 // This module is not available in kernels < 3.18
190 "nf_reject_ipv6",
191 // These modules are needed for supporting Chrome traffic on Android
192 // VPN which uses Android's NAT feature. Android NAT sets up iptables
193 // rules that use these conntrack modules for FTP/TFTP.
194 "nf_nat_ftp",
195 "nf_nat_tftp",
196 }) != 0) {
197 LOG(WARNING) << "One or more optional kernel modules failed to load.";
198 }
Garrick Evans54861622019-07-19 09:05:09 +0900199}
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900200
Garrick Evans508a4bc2019-11-14 08:45:52 +0900201bool ArcService::Start(int32_t id) {
202 if (!impl_->Start(id))
203 return false;
Garrick Evanscb791e72019-11-11 15:44:34 +0900204
Garrick Evans508a4bc2019-11-14 08:45:52 +0900205 OnStart();
206 return true;
207}
208
209void ArcService::OnStart() {
Garrick Evanscb791e72019-11-11 15:44:34 +0900210 // Start known host devices, any new ones will be setup in the process.
211 dev_mgr_->ProcessDevices(
212 base::Bind(&ArcService::StartDevice, weak_factory_.GetWeakPtr()));
213
214 // If this is the first time the service is starting this will create the
215 // Android bridge device; otherwise it does nothing (this is a workaround for
216 // the bug in Shill that casues a Bus crash when it sees the ARC bridge a
217 // second time). Do this after processing the existing devices so it doesn't
218 // get started twice.
219 dev_mgr_->Add(guest_ == GuestMessage::ARC_LEGACY ? kAndroidLegacyDevice
220 : kAndroidDevice);
221
222 // Finally, call the base implementation.
223 GuestService::OnStart();
224}
225
Garrick Evans508a4bc2019-11-14 08:45:52 +0900226void ArcService::Stop() {
227 OnStop();
228 impl_->Stop();
229}
230
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900231void ArcService::OnStop() {
Garrick Evans54861622019-07-19 09:05:09 +0900232 // Call the base implementation.
233 GuestService::OnStop();
234
235 // Stop known host devices. Note that this does not teardown any existing
236 // devices.
237 dev_mgr_->ProcessDevices(
238 base::Bind(&ArcService::StopDevice, weak_factory_.GetWeakPtr()));
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900239}
240
Garrick Evans54861622019-07-19 09:05:09 +0900241void ArcService::OnDeviceAdded(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900242 // ARC N uses legacy single networking and only requires the arcbr0/arc0
243 // configuration. Any other device can be safely ignored.
244 if (guest_ == GuestMessage::ARC_LEGACY && !device->IsLegacyAndroid())
Garrick Evans54861622019-07-19 09:05:09 +0900245 return;
Garrick Evansba575742019-07-17 15:48:08 +0900246
Garrick Evans54861622019-07-19 09:05:09 +0900247 const auto& config = device->config();
248
249 LOG(INFO) << "Adding device " << device->ifname()
250 << " bridge: " << config.host_ifname()
Garrick Evans310ab552019-10-08 11:07:53 +0900251 << " guest_iface: " << config.guest_ifname();
Garrick Evans54861622019-07-19 09:05:09 +0900252
253 // Create the bridge.
254 if (!datapath_->AddBridge(config.host_ifname(),
255 IPv4AddressToString(config.host_ipv4_addr()))) {
256 LOG(ERROR) << "Failed to setup arc bridge: " << config.host_ifname();
257 return;
258 }
259
260 // Setup the iptables.
261 if (device->IsLegacyAndroid()) {
262 if (!datapath_->AddLegacyIPv4DNAT(
263 IPv4AddressToString(config.guest_ipv4_addr())))
264 LOG(ERROR) << "Failed to configure ARC traffic rules";
265
266 if (!datapath_->AddOutboundIPv4(config.host_ifname()))
267 LOG(ERROR) << "Failed to configure egress traffic rules";
268 } else if (!device->IsAndroid()) {
269 if (!datapath_->AddInboundIPv4DNAT(
270 device->ifname(), IPv4AddressToString(config.guest_ipv4_addr())))
271 LOG(ERROR) << "Failed to configure ingress traffic rules for "
272 << device->ifname();
273
274 if (!datapath_->AddOutboundIPv4(config.host_ifname()))
275 LOG(ERROR) << "Failed to configure egress traffic rules";
276 }
277
Garrick Evans2c263102019-07-26 16:07:18 +0900278 device->set_context(guest_, std::make_unique<Context>());
Garrick Evans54861622019-07-19 09:05:09 +0900279
280 StartDevice(device);
281}
282
283void ArcService::StartDevice(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900284 // This can happen if OnDeviceAdded is invoked when the container is down.
Garrick Evansd90a3822019-11-12 17:53:08 +0900285 if (!impl_->IsStarted())
Garrick Evans54861622019-07-19 09:05:09 +0900286 return;
287
Garrick Evans2c263102019-07-26 16:07:18 +0900288 // If there is no context, then this is a new device and it needs to run
289 // through the full setup process.
290 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
291 if (!ctx)
Garrick Evans54861622019-07-19 09:05:09 +0900292 return OnDeviceAdded(device);
293
Garrick Evans2c263102019-07-26 16:07:18 +0900294 if (ctx->IsStarted()) {
295 LOG(ERROR) << "Attempt to restart device " << device->ifname();
296 return;
297 }
298
Garrick Evansd90a3822019-11-12 17:53:08 +0900299 if (!impl_->OnStartDevice(device)) {
Garrick Evanscb791e72019-11-11 15:44:34 +0900300 LOG(ERROR) << "Failed to start device " << device->ifname();
301 return;
302 }
303
Garrick Evansb4eb3892019-11-13 12:07:07 +0900304 ctx->Start();
Garrick Evans54861622019-07-19 09:05:09 +0900305}
306
307void ArcService::OnDeviceRemoved(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900308 // ARC N uses legacy single networking and only requires the arcbr0/arc0
309 // configuration. Any other device can be safely ignored.
310 if (guest_ == GuestMessage::ARC_LEGACY && !device->IsLegacyAndroid())
Garrick Evans54861622019-07-19 09:05:09 +0900311 return;
312
Garrick Evans310ab552019-10-08 11:07:53 +0900313 // If the container is down, this call does nothing.
Garrick Evans54861622019-07-19 09:05:09 +0900314 StopDevice(device);
315
316 const auto& config = device->config();
317
318 LOG(INFO) << "Removing device " << device->ifname()
319 << " bridge: " << config.host_ifname()
320 << " guest_iface: " << config.guest_ifname();
321
Garrick Evans260ff302019-07-25 11:22:50 +0900322 device->Disable();
Garrick Evans54861622019-07-19 09:05:09 +0900323 if (device->IsLegacyAndroid()) {
324 datapath_->RemoveOutboundIPv4(config.host_ifname());
325 datapath_->RemoveLegacyIPv4DNAT();
326 } else if (!device->IsAndroid()) {
327 datapath_->RemoveOutboundIPv4(config.host_ifname());
328 datapath_->RemoveInboundIPv4DNAT(
329 device->ifname(), IPv4AddressToString(config.guest_ipv4_addr()));
330 }
331
332 datapath_->RemoveBridge(config.host_ifname());
333
Garrick Evans2c263102019-07-26 16:07:18 +0900334 device->set_context(guest_, nullptr);
Garrick Evans54861622019-07-19 09:05:09 +0900335}
336
337void ArcService::StopDevice(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900338 // This can happen if the device if OnDeviceRemoved is invoked when the
339 // container is down.
Garrick Evansd90a3822019-11-12 17:53:08 +0900340 if (!impl_->IsStarted())
Garrick Evans54861622019-07-19 09:05:09 +0900341 return;
342
Garrick Evans2c263102019-07-26 16:07:18 +0900343 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
344 if (!ctx) {
345 LOG(ERROR) << "Attempt to stop removed device " << device->ifname();
346 return;
347 }
348
349 if (!ctx->IsStarted()) {
350 LOG(ERROR) << "Attempt to re-stop device " << device->ifname();
351 return;
352 }
353
Garrick Evansd90a3822019-11-12 17:53:08 +0900354 impl_->OnStopDevice(device);
Garrick Evanscb791e72019-11-11 15:44:34 +0900355
356 ctx->Stop();
357}
358
Garrick Evans54861622019-07-19 09:05:09 +0900359void ArcService::OnDefaultInterfaceChanged(const std::string& ifname) {
Garrick Evansd90a3822019-11-12 17:53:08 +0900360 impl_->OnDefaultInterfaceChanged(ifname);
Garrick Evans54861622019-07-19 09:05:09 +0900361}
Garrick Evansba575742019-07-17 15:48:08 +0900362
Garrick Evans2c263102019-07-26 16:07:18 +0900363// Context
364
365ArcService::Context::Context() : Device::Context() {
366 Stop();
367}
368
369void ArcService::Context::Start() {
370 Stop();
371 started_ = true;
372}
373
374void ArcService::Context::Stop() {
375 started_ = false;
376 link_up_ = false;
377 routing_table_id_ = kInvalidTableID;
378 routing_table_attempts_ = 0;
379}
380
381bool ArcService::Context::IsStarted() const {
382 return started_;
383}
384
385bool ArcService::Context::IsLinkUp() const {
386 return link_up_;
387}
388
389bool ArcService::Context::SetLinkUp(bool link_up) {
390 if (link_up == link_up_)
391 return false;
392
393 link_up_ = link_up;
394 return true;
395}
396
397bool ArcService::Context::HasIPv6() const {
398 return routing_table_id_ != kInvalidTableID;
399}
400
401bool ArcService::Context::SetHasIPv6(int routing_table_id) {
402 if (routing_table_id <= kRouteControllerRouteTableOffsetFromIndex)
403 return false;
404
405 routing_table_id_ = routing_table_id;
406 return true;
407}
408
Garrick Evansdc3fc452019-09-06 14:15:04 +0900409void ArcService::Context::ClearIPv6() {
410 routing_table_id_ = kInvalidTableID;
411 routing_table_attempts_ = 0;
412}
413
Garrick Evans2c263102019-07-26 16:07:18 +0900414int ArcService::Context::RoutingTableID() const {
415 return routing_table_id_;
416}
417
418int ArcService::Context::RoutingTableAttempts() {
419 return routing_table_attempts_++;
420}
421
Garrick Evansb4eb3892019-11-13 12:07:07 +0900422const std::string& ArcService::Context::TAP() const {
423 return tap_;
424}
425
426void ArcService::Context::SetTAP(const std::string& tap) {
427 tap_ = tap;
428}
429
Garrick Evansd90a3822019-11-12 17:53:08 +0900430// ARC++ specific functions.
431
432ArcService::ContainerImpl::ContainerImpl(DeviceManagerBase* dev_mgr,
433 Datapath* datapath,
434 GuestMessage::GuestType guest)
435 : pid_(kInvalidPID),
436 dev_mgr_(dev_mgr),
437 datapath_(datapath),
438 guest_(guest) {}
439
Garrick Evansb4eb3892019-11-13 12:07:07 +0900440GuestMessage::GuestType ArcService::ContainerImpl::guest() const {
441 return guest_;
442}
443
Garrick Evans508a4bc2019-11-14 08:45:52 +0900444bool ArcService::ContainerImpl::Start(int32_t pid) {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900445 // TODO(garrick): Remove this workaround.
446 pid_ = (pid == kTestPID) ? pid : GetContainerPID();
Garrick Evansd90a3822019-11-12 17:53:08 +0900447 if (pid_ == kInvalidPID) {
448 LOG(ERROR) << "Cannot start service - invalid container PID";
449 return false;
450 }
451
452 // Start listening for RTNetlink messages in the container's net namespace
453 // to be notified whenever it brings up an interface.
454 {
455 ScopedNS ns(pid_);
456 if (ns.IsValid()) {
457 rtnl_handler_ = std::make_unique<RTNetlinkHandler>();
458 rtnl_handler_->Start(RTMGRP_LINK);
459 link_listener_ = std::make_unique<shill::RTNLListener>(
460 shill::RTNLHandler::kRequestLink,
461 Bind(&ArcService::ContainerImpl::LinkMsgHandler,
462 weak_factory_.GetWeakPtr()),
463 rtnl_handler_.get());
464 } else {
465 // This is bad - it means we won't ever be able to tell when the container
466 // brings up an interface.
467 LOG(ERROR)
468 << "Cannot start netlink listener - invalid container namespace?";
469 return false;
470 }
471 }
472
473 dev_mgr_->RegisterDeviceIPv6AddressFoundHandler(
Garrick Evansb4eb3892019-11-13 12:07:07 +0900474 guest(), base::Bind(&ArcService::ContainerImpl::SetupIPv6,
475 weak_factory_.GetWeakPtr()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900476
477 LOG(INFO) << "ARC++ network service started {pid: " << pid_ << "}";
478 return true;
479}
480
Garrick Evans508a4bc2019-11-14 08:45:52 +0900481void ArcService::ContainerImpl::Stop() {
Garrick Evansd90a3822019-11-12 17:53:08 +0900482 rtnl_handler_->RemoveListener(link_listener_.get());
483 link_listener_.reset();
484 rtnl_handler_.reset();
485
486 LOG(INFO) << "ARC++ network service stopped {pid: " << pid_ << "}";
487 pid_ = kInvalidPID;
488}
489
490bool ArcService::ContainerImpl::IsStarted() const {
491 return pid_ != kInvalidPID;
492}
493
494bool ArcService::ContainerImpl::OnStartDevice(Device* device) {
495 const auto& config = device->config();
496
497 LOG(INFO) << "Starting device " << device->ifname()
498 << " bridge: " << config.host_ifname()
Garrick Evansb4eb3892019-11-13 12:07:07 +0900499 << " guest_iface: " << config.guest_ifname() << " pid: " << pid_;
Garrick Evansd90a3822019-11-12 17:53:08 +0900500
501 std::string veth_ifname = datapath_->AddVirtualBridgedInterface(
502 device->ifname(), MacAddressToString(config.guest_mac_addr()),
503 config.host_ifname());
504 if (veth_ifname.empty()) {
505 LOG(ERROR) << "Failed to create virtual interface for container";
506 return false;
507 }
508
509 if (!datapath_->AddInterfaceToContainer(
510 pid_, veth_ifname, config.guest_ifname(),
511 IPv4AddressToString(config.guest_ipv4_addr()),
512 device->options().fwd_multicast)) {
513 LOG(ERROR) << "Failed to create container interface.";
514 datapath_->RemoveInterface(veth_ifname);
515 datapath_->RemoveBridge(config.host_ifname());
516 return false;
517 }
518
519 // Signal the container that the network device is ready.
520 // This is only applicable for arc0.
521 if (device->IsAndroid() || device->IsLegacyAndroid()) {
522 datapath_->runner().WriteSentinelToContainer(base::IntToString(pid_));
523 }
524
525 return true;
526}
527
528void ArcService::ContainerImpl::OnStopDevice(Device* device) {
529 const auto& config = device->config();
530
531 LOG(INFO) << "Stopping device " << device->ifname()
532 << " bridge: " << config.host_ifname()
Garrick Evansb4eb3892019-11-13 12:07:07 +0900533 << " guest_iface: " << config.guest_ifname() << " pid: " << pid_;
Garrick Evansd90a3822019-11-12 17:53:08 +0900534
535 device->Disable();
536 if (!device->IsAndroid()) {
537 datapath_->RemoveInterface(ArcVethHostName(device->ifname()));
538 }
539}
540
541void ArcService::ContainerImpl::OnDefaultInterfaceChanged(
542 const std::string& ifname) {
543 if (!IsStarted())
544 return;
545
546 // For ARC N, we must always be able to find the arc0 device and, at a
547 // minimum, disable it.
Garrick Evansb4eb3892019-11-13 12:07:07 +0900548 if (guest() == GuestMessage::ARC_LEGACY) {
Garrick Evansd90a3822019-11-12 17:53:08 +0900549 datapath_->RemoveLegacyIPv4InboundDNAT();
550 auto* device = dev_mgr_->FindByGuestInterface("arc0");
551 if (!device) {
552 LOG(DFATAL) << "Expected legacy Android device missing";
553 return;
554 }
555 device->Disable();
556
557 // If a new default interface was given, then re-enable with that.
558 if (!ifname.empty()) {
559 datapath_->AddLegacyIPv4InboundDNAT(ifname);
560 device->Enable(ifname);
561 }
562 return;
563 }
564
565 // For ARC P and later, we're only concerned with resetting the device when it
566 // becomes the default (again) in order to ensure any previous configuration.
567 // is cleared.
568 if (ifname.empty())
569 return;
570
571 auto* device = dev_mgr_->FindByGuestInterface(ifname);
572 if (!device) {
573 LOG(ERROR) << "Expected default device missing: " << ifname;
574 return;
575 }
576 device->StopIPv6RoutingLegacy();
577 device->StartIPv6RoutingLegacy(ifname);
578}
579
580void ArcService::ContainerImpl::LinkMsgHandler(const shill::RTNLMessage& msg) {
581 if (!msg.HasAttribute(IFLA_IFNAME)) {
582 LOG(ERROR) << "Link event message does not have IFLA_IFNAME";
583 return;
584 }
585 bool link_up = msg.link_status().flags & IFF_UP;
586 shill::ByteString b(msg.GetAttribute(IFLA_IFNAME));
587 std::string ifname(reinterpret_cast<const char*>(
588 b.GetSubstring(0, IFNAMSIZ).GetConstData()));
589
590 auto* device = dev_mgr_->FindByGuestInterface(ifname);
591 if (!device)
592 return;
593
Garrick Evansb4eb3892019-11-13 12:07:07 +0900594 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900595 if (!ctx) {
596 LOG(DFATAL) << "Context missing";
597 return;
598 }
599
600 // If the link status is unchanged, there is nothing to do.
601 if (!ctx->SetLinkUp(link_up))
602 return;
603
604 if (!link_up) {
605 LOG(INFO) << ifname << " is now down";
606 return;
607 }
608 LOG(INFO) << ifname << " is now up";
609
610 if (device->IsAndroid())
611 return;
612
613 if (device->IsLegacyAndroid()) {
614 OnDefaultInterfaceChanged(dev_mgr_->DefaultInterface());
615 return;
616 }
617
618 device->Enable(ifname);
619}
620
621void ArcService::ContainerImpl::SetupIPv6(Device* device) {
622 device->RegisterIPv6TeardownHandler(base::Bind(
623 &ArcService::ContainerImpl::TeardownIPv6, weak_factory_.GetWeakPtr()));
624
625 auto& ipv6_config = device->ipv6_config();
626 if (ipv6_config.ifname.empty())
627 return;
628
Garrick Evansb4eb3892019-11-13 12:07:07 +0900629 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900630 if (!ctx) {
631 LOG(DFATAL) << "Context missing";
632 return;
633 }
634 if (ctx->HasIPv6())
635 return;
636
637 LOG(INFO) << "Setting up IPv6 for " << ipv6_config.ifname;
638
639 int table_id =
640 GetAndroidRoutingTableId(device->config().guest_ifname(), pid_);
641 if (table_id == kInvalidTableID) {
642 if (ctx->RoutingTableAttempts() < kMaxTableRetries) {
643 LOG(INFO) << "Could not look up routing table ID for container interface "
644 << device->config().guest_ifname() << " - trying again...";
645 base::MessageLoop::current()->task_runner()->PostDelayedTask(
646 FROM_HERE,
647 base::Bind(&ArcService::ContainerImpl::SetupIPv6,
648 weak_factory_.GetWeakPtr(), device),
649 kTableRetryDelay);
650 } else {
651 LOG(DFATAL)
652 << "Could not look up routing table ID for container interface "
653 << device->config().guest_ifname();
654 }
655 return;
656 }
657
658 LOG(INFO) << "Setting IPv6 address " << ipv6_config.addr
659 << "/128, gateway=" << ipv6_config.router << " on "
660 << ipv6_config.ifname;
661
662 char buf[INET6_ADDRSTRLEN] = {0};
663 if (!inet_ntop(AF_INET6, &ipv6_config.addr, buf, sizeof(buf))) {
664 LOG(DFATAL) << "Invalid address: " << ipv6_config.addr;
665 return;
666 }
667 std::string addr = buf;
668
669 if (!inet_ntop(AF_INET6, &ipv6_config.router, buf, sizeof(buf))) {
670 LOG(DFATAL) << "Invalid router address: " << ipv6_config.router;
671 return;
672 }
673 std::string router = buf;
674
675 const auto& config = device->config();
676 {
677 ScopedNS ns(pid_);
678 if (!ns.IsValid()) {
679 LOG(ERROR) << "Invalid container namespace (" << pid_
680 << ") - cannot configure IPv6.";
681 return;
682 }
Taoyu Li90c13912019-11-26 17:56:54 +0900683 // Tag the interface so that ARC can detect this manual configuration and
684 // skip disabling and re-enabling IPv6 (b/144545910).
685 if (!datapath_->SetInterfaceFlag(config.guest_ifname(), IFF_DEBUG)) {
686 LOG(ERROR) << "Failed to mark IPv6 manual config flag on interface";
687 }
Garrick Evansd90a3822019-11-12 17:53:08 +0900688 if (!datapath_->AddIPv6GatewayRoutes(config.guest_ifname(), addr, router,
689 ipv6_config.prefix_len, table_id)) {
690 LOG(ERROR) << "Failed to setup IPv6 routes in the container";
691 return;
692 }
693 }
694
695 if (!datapath_->AddIPv6HostRoute(config.host_ifname(), addr,
696 ipv6_config.prefix_len)) {
697 LOG(ERROR) << "Failed to setup the IPv6 route for interface "
698 << config.host_ifname();
699 return;
700 }
701
702 if (!datapath_->AddIPv6Neighbor(ipv6_config.ifname, addr)) {
703 LOG(ERROR) << "Failed to setup the IPv6 neighbor proxy";
704 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
705 ipv6_config.prefix_len);
706 return;
707 }
708
Taoyu Li2980ee92019-11-18 17:49:32 +0900709 if (!datapath_->AddIPv6Forwarding(ipv6_config.ifname,
710 device->config().host_ifname())) {
711 LOG(ERROR) << "Failed to setup iptables for IPv6";
712 datapath_->RemoveIPv6Neighbor(ipv6_config.ifname, addr);
713 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
714 ipv6_config.prefix_len);
715 return;
716 }
717
Garrick Evansd90a3822019-11-12 17:53:08 +0900718 ctx->SetHasIPv6(table_id);
719}
720
721void ArcService::ContainerImpl::TeardownIPv6(Device* device) {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900722 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900723 if (!ctx || !ctx->HasIPv6())
724 return;
725
726 auto& ipv6_config = device->ipv6_config();
727 LOG(INFO) << "Clearing IPv6 for " << ipv6_config.ifname;
728 int table_id = ctx->RoutingTableID();
729 ctx->ClearIPv6();
730
731 char buf[INET6_ADDRSTRLEN] = {0};
732 if (!inet_ntop(AF_INET6, &ipv6_config.addr, buf, sizeof(buf))) {
733 LOG(DFATAL) << "Invalid address: " << ipv6_config.addr;
734 return;
735 }
736 std::string addr = buf;
737
738 if (!inet_ntop(AF_INET6, &ipv6_config.router, buf, sizeof(buf))) {
739 LOG(DFATAL) << "Invalid router address: " << ipv6_config.router;
740 return;
741 }
742 std::string router = buf;
743
744 const auto& config = device->config();
Taoyu Li2980ee92019-11-18 17:49:32 +0900745 datapath_->RemoveIPv6Forwarding(ipv6_config.ifname, config.host_ifname());
Garrick Evansd90a3822019-11-12 17:53:08 +0900746 datapath_->RemoveIPv6Neighbor(ipv6_config.ifname, addr);
747 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
748 ipv6_config.prefix_len);
749
750 ScopedNS ns(pid_);
751 if (ns.IsValid()) {
752 datapath_->RemoveIPv6GatewayRoutes(config.guest_ifname(), addr, router,
753 ipv6_config.prefix_len, table_id);
754 }
755}
756
Garrick Evansb4eb3892019-11-13 12:07:07 +0900757// VM specific functions
758
759ArcService::VmImpl::VmImpl(DeviceManagerBase* dev_mgr, Datapath* datapath)
760 : cid_(kInvalidCID), dev_mgr_(dev_mgr), datapath_(datapath) {}
761
762GuestMessage::GuestType ArcService::VmImpl::guest() const {
763 return GuestMessage::ARC_VM;
764}
765
Garrick Evans508a4bc2019-11-14 08:45:52 +0900766bool ArcService::VmImpl::Start(int32_t cid) {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900767 if (cid <= kInvalidCID) {
768 LOG(ERROR) << "Invalid VM cid " << cid;
769 return false;
770 }
771
772 cid_ = cid;
773 LOG(INFO) << "ARCVM network service started {cid: " << cid_ << "}";
774
775 return true;
776}
777
Garrick Evans508a4bc2019-11-14 08:45:52 +0900778void ArcService::VmImpl::Stop() {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900779 LOG(INFO) << "ARCVM network service stopped {cid: " << cid_ << "}";
780 cid_ = kInvalidCID;
781}
782
783bool ArcService::VmImpl::IsStarted() const {
784 return cid_ > kInvalidCID;
785}
786
787bool ArcService::VmImpl::OnStartDevice(Device* device) {
788 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
789 // configurations; but for now ARCVM needs to be treated like ARC++ N.
790 if (!device->IsLegacyAndroid())
791 return false;
792
793 const auto& config = device->config();
794
795 LOG(INFO) << "Starting device " << device->ifname()
796 << " bridge: " << config.host_ifname()
797 << " guest_iface: " << config.guest_ifname() << " cid: " << cid_;
798
799 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
800 if (!ctx) {
801 LOG(ERROR) << "Context missing";
802 return false;
803 }
804
805 // Since the interface will be added to the bridge, no address configuration
806 // should be provided here.
807 std::string tap =
808 datapath_->AddTAP("" /* auto-generate name */, nullptr /* no mac addr */,
809 nullptr /* no ipv4 subnet */, vm_tools::kCrosVmUser);
810 if (tap.empty()) {
811 LOG(ERROR) << "Failed to create TAP device for VM";
812 return false;
813 }
814
815 if (!datapath_->AddToBridge(config.host_ifname(), tap)) {
816 LOG(ERROR) << "Failed to bridge TAP device " << tap;
817 datapath_->RemoveInterface(tap);
818 return false;
819 }
820
821 ctx->SetTAP(tap);
822 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
823 // configurations; but for now ARCVM needs to be treated like ARC++ N.
824 OnDefaultInterfaceChanged(dev_mgr_->DefaultInterface());
825 return true;
826}
827
828void ArcService::VmImpl::OnStopDevice(Device* device) {
829 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
830 // configurations; but for now ARCVM needs to be treated like ARC++ N.
831 if (!device->IsLegacyAndroid())
832 return;
833
834 const auto& config = device->config();
835
836 LOG(INFO) << "Stopping " << device->ifname()
837 << " bridge: " << config.host_ifname()
838 << " guest_iface: " << config.guest_ifname() << " cid: " << cid_;
839
840 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
841 if (!ctx) {
842 LOG(ERROR) << "Context missing";
843 return;
844 }
845
846 device->Disable();
847 datapath_->RemoveInterface(ctx->TAP());
848}
849
850void ArcService::VmImpl::OnDefaultInterfaceChanged(const std::string& ifname) {
851 if (!IsStarted())
852 return;
853
854 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
855 // configurations; but for now ARCVM needs to be treated like ARC++ N.
856 datapath_->RemoveLegacyIPv4InboundDNAT();
857 auto* device = dev_mgr_->FindByGuestInterface("arc0");
858 if (!device) {
859 LOG(DFATAL) << "Expected Android device missing";
860 return;
861 }
862 device->Disable();
863
864 // If a new default interface was given, then re-enable with that.
865 if (!ifname.empty()) {
866 datapath_->AddLegacyIPv4InboundDNAT(ifname);
867 device->Enable(ifname);
868 }
869}
870
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900871} // namespace arc_networkd