blob: 88997018668295c5804e42d7ef16b588c8ba6462 [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
134} // namespace
135
Garrick Evans54861622019-07-19 09:05:09 +0900136ArcService::ArcService(DeviceManagerBase* dev_mgr,
Taoyu Li179dcc62019-10-17 11:21:08 +0900137 Datapath* datapath,
Garrick Evans1f5a3612019-11-08 12:59:03 +0900138 bool* is_legacy)
139 : GuestService((is_legacy ? !*is_legacy : ShouldEnableMultinet())
140 ? GuestMessage::ARC
141 : GuestMessage::ARC_LEGACY,
Garrick Evans54861622019-07-19 09:05:09 +0900142 dev_mgr),
Taoyu Li179dcc62019-10-17 11:21:08 +0900143 datapath_(datapath),
Garrick Evansd90a3822019-11-12 17:53:08 +0900144 impl_(std::make_unique<ContainerImpl>(dev_mgr, datapath, guest_)) {
Taoyu Li179dcc62019-10-17 11:21:08 +0900145 DCHECK(datapath_);
Garrick Evans3915af32019-07-25 15:44:34 +0900146
147 // Load networking modules needed by Android that are not compiled in the
148 // kernel. Android does not allow auto-loading of kernel modules.
Garrick Evans3915af32019-07-25 15:44:34 +0900149
150 // These must succeed.
Taoyu Lia0c43a72019-10-01 10:29:57 +0900151 if (datapath_->runner().ModprobeAll({
Garrick Evans3915af32019-07-25 15:44:34 +0900152 // The netfilter modules needed by netd for iptables commands.
153 "ip6table_filter",
154 "ip6t_ipv6header",
155 "ip6t_REJECT",
156 // The xfrm modules needed for Android's ipsec APIs.
157 "xfrm4_mode_transport",
158 "xfrm4_mode_tunnel",
159 "xfrm6_mode_transport",
160 "xfrm6_mode_tunnel",
161 // The ipsec modules for AH and ESP encryption for ipv6.
162 "ah6",
163 "esp6",
164 }) != 0) {
165 LOG(ERROR) << "One or more required kernel modules failed to load.";
166 }
167
168 // Optional modules.
Taoyu Lia0c43a72019-10-01 10:29:57 +0900169 if (datapath_->runner().ModprobeAll({
Garrick Evans3915af32019-07-25 15:44:34 +0900170 // This module is not available in kernels < 3.18
171 "nf_reject_ipv6",
172 // These modules are needed for supporting Chrome traffic on Android
173 // VPN which uses Android's NAT feature. Android NAT sets up iptables
174 // rules that use these conntrack modules for FTP/TFTP.
175 "nf_nat_ftp",
176 "nf_nat_tftp",
177 }) != 0) {
178 LOG(WARNING) << "One or more optional kernel modules failed to load.";
179 }
Garrick Evans54861622019-07-19 09:05:09 +0900180}
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900181
182void ArcService::OnStart() {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900183 // TODO(garrick): Plumb PID from DBus message into here.
184 if (!impl_->OnStart(0 /* unused */)) {
Garrick Evanscb791e72019-11-11 15:44:34 +0900185 LOG(ERROR) << "Failed to start ARC++ network service";
186 return;
187 }
188
189 // Start known host devices, any new ones will be setup in the process.
190 dev_mgr_->ProcessDevices(
191 base::Bind(&ArcService::StartDevice, weak_factory_.GetWeakPtr()));
192
193 // If this is the first time the service is starting this will create the
194 // Android bridge device; otherwise it does nothing (this is a workaround for
195 // the bug in Shill that casues a Bus crash when it sees the ARC bridge a
196 // second time). Do this after processing the existing devices so it doesn't
197 // get started twice.
198 dev_mgr_->Add(guest_ == GuestMessage::ARC_LEGACY ? kAndroidLegacyDevice
199 : kAndroidDevice);
200
201 // Finally, call the base implementation.
202 GuestService::OnStart();
203}
204
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900205void ArcService::OnStop() {
Garrick Evans54861622019-07-19 09:05:09 +0900206 // Call the base implementation.
207 GuestService::OnStop();
208
209 // Stop known host devices. Note that this does not teardown any existing
210 // devices.
211 dev_mgr_->ProcessDevices(
212 base::Bind(&ArcService::StopDevice, weak_factory_.GetWeakPtr()));
213
Garrick Evansd90a3822019-11-12 17:53:08 +0900214 impl_->OnStop();
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900215}
216
Garrick Evans54861622019-07-19 09:05:09 +0900217void ArcService::OnDeviceAdded(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900218 // ARC N uses legacy single networking and only requires the arcbr0/arc0
219 // configuration. Any other device can be safely ignored.
220 if (guest_ == GuestMessage::ARC_LEGACY && !device->IsLegacyAndroid())
Garrick Evans54861622019-07-19 09:05:09 +0900221 return;
Garrick Evansba575742019-07-17 15:48:08 +0900222
Garrick Evans54861622019-07-19 09:05:09 +0900223 const auto& config = device->config();
224
225 LOG(INFO) << "Adding device " << device->ifname()
226 << " bridge: " << config.host_ifname()
Garrick Evans310ab552019-10-08 11:07:53 +0900227 << " guest_iface: " << config.guest_ifname();
Garrick Evans54861622019-07-19 09:05:09 +0900228
229 // Create the bridge.
230 if (!datapath_->AddBridge(config.host_ifname(),
231 IPv4AddressToString(config.host_ipv4_addr()))) {
232 LOG(ERROR) << "Failed to setup arc bridge: " << config.host_ifname();
233 return;
234 }
235
236 // Setup the iptables.
237 if (device->IsLegacyAndroid()) {
238 if (!datapath_->AddLegacyIPv4DNAT(
239 IPv4AddressToString(config.guest_ipv4_addr())))
240 LOG(ERROR) << "Failed to configure ARC traffic rules";
241
242 if (!datapath_->AddOutboundIPv4(config.host_ifname()))
243 LOG(ERROR) << "Failed to configure egress traffic rules";
244 } else if (!device->IsAndroid()) {
245 if (!datapath_->AddInboundIPv4DNAT(
246 device->ifname(), IPv4AddressToString(config.guest_ipv4_addr())))
247 LOG(ERROR) << "Failed to configure ingress traffic rules for "
248 << device->ifname();
249
250 if (!datapath_->AddOutboundIPv4(config.host_ifname()))
251 LOG(ERROR) << "Failed to configure egress traffic rules";
252 }
253
Garrick Evans2c263102019-07-26 16:07:18 +0900254 device->set_context(guest_, std::make_unique<Context>());
Garrick Evans54861622019-07-19 09:05:09 +0900255
256 StartDevice(device);
257}
258
259void ArcService::StartDevice(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900260 // This can happen if OnDeviceAdded is invoked when the container is down.
Garrick Evansd90a3822019-11-12 17:53:08 +0900261 if (!impl_->IsStarted())
Garrick Evans54861622019-07-19 09:05:09 +0900262 return;
263
Garrick Evans2c263102019-07-26 16:07:18 +0900264 // If there is no context, then this is a new device and it needs to run
265 // through the full setup process.
266 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
267 if (!ctx)
Garrick Evans54861622019-07-19 09:05:09 +0900268 return OnDeviceAdded(device);
269
Garrick Evans2c263102019-07-26 16:07:18 +0900270 if (ctx->IsStarted()) {
271 LOG(ERROR) << "Attempt to restart device " << device->ifname();
272 return;
273 }
274
Garrick Evansd90a3822019-11-12 17:53:08 +0900275 if (!impl_->OnStartDevice(device)) {
Garrick Evanscb791e72019-11-11 15:44:34 +0900276 LOG(ERROR) << "Failed to start device " << device->ifname();
277 return;
278 }
279
Garrick Evansb4eb3892019-11-13 12:07:07 +0900280 ctx->Start();
Garrick Evans54861622019-07-19 09:05:09 +0900281}
282
283void ArcService::OnDeviceRemoved(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900284 // ARC N uses legacy single networking and only requires the arcbr0/arc0
285 // configuration. Any other device can be safely ignored.
286 if (guest_ == GuestMessage::ARC_LEGACY && !device->IsLegacyAndroid())
Garrick Evans54861622019-07-19 09:05:09 +0900287 return;
288
Garrick Evans310ab552019-10-08 11:07:53 +0900289 // If the container is down, this call does nothing.
Garrick Evans54861622019-07-19 09:05:09 +0900290 StopDevice(device);
291
292 const auto& config = device->config();
293
294 LOG(INFO) << "Removing device " << device->ifname()
295 << " bridge: " << config.host_ifname()
296 << " guest_iface: " << config.guest_ifname();
297
Garrick Evans260ff302019-07-25 11:22:50 +0900298 device->Disable();
Garrick Evans54861622019-07-19 09:05:09 +0900299 if (device->IsLegacyAndroid()) {
300 datapath_->RemoveOutboundIPv4(config.host_ifname());
301 datapath_->RemoveLegacyIPv4DNAT();
302 } else if (!device->IsAndroid()) {
303 datapath_->RemoveOutboundIPv4(config.host_ifname());
304 datapath_->RemoveInboundIPv4DNAT(
305 device->ifname(), IPv4AddressToString(config.guest_ipv4_addr()));
306 }
307
308 datapath_->RemoveBridge(config.host_ifname());
309
Garrick Evans2c263102019-07-26 16:07:18 +0900310 device->set_context(guest_, nullptr);
Garrick Evans54861622019-07-19 09:05:09 +0900311}
312
313void ArcService::StopDevice(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900314 // This can happen if the device if OnDeviceRemoved is invoked when the
315 // container is down.
Garrick Evansd90a3822019-11-12 17:53:08 +0900316 if (!impl_->IsStarted())
Garrick Evans54861622019-07-19 09:05:09 +0900317 return;
318
Garrick Evans2c263102019-07-26 16:07:18 +0900319 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
320 if (!ctx) {
321 LOG(ERROR) << "Attempt to stop removed device " << device->ifname();
322 return;
323 }
324
325 if (!ctx->IsStarted()) {
326 LOG(ERROR) << "Attempt to re-stop device " << device->ifname();
327 return;
328 }
329
Garrick Evansd90a3822019-11-12 17:53:08 +0900330 impl_->OnStopDevice(device);
Garrick Evanscb791e72019-11-11 15:44:34 +0900331
332 ctx->Stop();
333}
334
Garrick Evans54861622019-07-19 09:05:09 +0900335void ArcService::OnDefaultInterfaceChanged(const std::string& ifname) {
Garrick Evansd90a3822019-11-12 17:53:08 +0900336 impl_->OnDefaultInterfaceChanged(ifname);
Garrick Evans54861622019-07-19 09:05:09 +0900337}
Garrick Evansba575742019-07-17 15:48:08 +0900338
Garrick Evans2c263102019-07-26 16:07:18 +0900339// Context
340
341ArcService::Context::Context() : Device::Context() {
342 Stop();
343}
344
345void ArcService::Context::Start() {
346 Stop();
347 started_ = true;
348}
349
350void ArcService::Context::Stop() {
351 started_ = false;
352 link_up_ = false;
353 routing_table_id_ = kInvalidTableID;
354 routing_table_attempts_ = 0;
355}
356
357bool ArcService::Context::IsStarted() const {
358 return started_;
359}
360
361bool ArcService::Context::IsLinkUp() const {
362 return link_up_;
363}
364
365bool ArcService::Context::SetLinkUp(bool link_up) {
366 if (link_up == link_up_)
367 return false;
368
369 link_up_ = link_up;
370 return true;
371}
372
373bool ArcService::Context::HasIPv6() const {
374 return routing_table_id_ != kInvalidTableID;
375}
376
377bool ArcService::Context::SetHasIPv6(int routing_table_id) {
378 if (routing_table_id <= kRouteControllerRouteTableOffsetFromIndex)
379 return false;
380
381 routing_table_id_ = routing_table_id;
382 return true;
383}
384
Garrick Evansdc3fc452019-09-06 14:15:04 +0900385void ArcService::Context::ClearIPv6() {
386 routing_table_id_ = kInvalidTableID;
387 routing_table_attempts_ = 0;
388}
389
Garrick Evans2c263102019-07-26 16:07:18 +0900390int ArcService::Context::RoutingTableID() const {
391 return routing_table_id_;
392}
393
394int ArcService::Context::RoutingTableAttempts() {
395 return routing_table_attempts_++;
396}
397
Garrick Evansb4eb3892019-11-13 12:07:07 +0900398const std::string& ArcService::Context::TAP() const {
399 return tap_;
400}
401
402void ArcService::Context::SetTAP(const std::string& tap) {
403 tap_ = tap;
404}
405
Garrick Evansd90a3822019-11-12 17:53:08 +0900406// ARC++ specific functions.
407
408ArcService::ContainerImpl::ContainerImpl(DeviceManagerBase* dev_mgr,
409 Datapath* datapath,
410 GuestMessage::GuestType guest)
411 : pid_(kInvalidPID),
412 dev_mgr_(dev_mgr),
413 datapath_(datapath),
414 guest_(guest) {}
415
Garrick Evansb4eb3892019-11-13 12:07:07 +0900416GuestMessage::GuestType ArcService::ContainerImpl::guest() const {
417 return guest_;
418}
419
420bool ArcService::ContainerImpl::OnStart(int32_t pid) {
421 // TODO(garrick): Remove this workaround.
422 pid_ = (pid == kTestPID) ? pid : GetContainerPID();
Garrick Evansd90a3822019-11-12 17:53:08 +0900423 if (pid_ == kInvalidPID) {
424 LOG(ERROR) << "Cannot start service - invalid container PID";
425 return false;
426 }
427
428 // Start listening for RTNetlink messages in the container's net namespace
429 // to be notified whenever it brings up an interface.
430 {
431 ScopedNS ns(pid_);
432 if (ns.IsValid()) {
433 rtnl_handler_ = std::make_unique<RTNetlinkHandler>();
434 rtnl_handler_->Start(RTMGRP_LINK);
435 link_listener_ = std::make_unique<shill::RTNLListener>(
436 shill::RTNLHandler::kRequestLink,
437 Bind(&ArcService::ContainerImpl::LinkMsgHandler,
438 weak_factory_.GetWeakPtr()),
439 rtnl_handler_.get());
440 } else {
441 // This is bad - it means we won't ever be able to tell when the container
442 // brings up an interface.
443 LOG(ERROR)
444 << "Cannot start netlink listener - invalid container namespace?";
445 return false;
446 }
447 }
448
449 dev_mgr_->RegisterDeviceIPv6AddressFoundHandler(
Garrick Evansb4eb3892019-11-13 12:07:07 +0900450 guest(), base::Bind(&ArcService::ContainerImpl::SetupIPv6,
451 weak_factory_.GetWeakPtr()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900452
453 LOG(INFO) << "ARC++ network service started {pid: " << pid_ << "}";
454 return true;
455}
456
457void ArcService::ContainerImpl::OnStop() {
458 rtnl_handler_->RemoveListener(link_listener_.get());
459 link_listener_.reset();
460 rtnl_handler_.reset();
461
462 LOG(INFO) << "ARC++ network service stopped {pid: " << pid_ << "}";
463 pid_ = kInvalidPID;
464}
465
466bool ArcService::ContainerImpl::IsStarted() const {
467 return pid_ != kInvalidPID;
468}
469
470bool ArcService::ContainerImpl::OnStartDevice(Device* device) {
471 const auto& config = device->config();
472
473 LOG(INFO) << "Starting device " << device->ifname()
474 << " bridge: " << config.host_ifname()
Garrick Evansb4eb3892019-11-13 12:07:07 +0900475 << " guest_iface: " << config.guest_ifname() << " pid: " << pid_;
Garrick Evansd90a3822019-11-12 17:53:08 +0900476
477 std::string veth_ifname = datapath_->AddVirtualBridgedInterface(
478 device->ifname(), MacAddressToString(config.guest_mac_addr()),
479 config.host_ifname());
480 if (veth_ifname.empty()) {
481 LOG(ERROR) << "Failed to create virtual interface for container";
482 return false;
483 }
484
485 if (!datapath_->AddInterfaceToContainer(
486 pid_, veth_ifname, config.guest_ifname(),
487 IPv4AddressToString(config.guest_ipv4_addr()),
488 device->options().fwd_multicast)) {
489 LOG(ERROR) << "Failed to create container interface.";
490 datapath_->RemoveInterface(veth_ifname);
491 datapath_->RemoveBridge(config.host_ifname());
492 return false;
493 }
494
495 // Signal the container that the network device is ready.
496 // This is only applicable for arc0.
497 if (device->IsAndroid() || device->IsLegacyAndroid()) {
498 datapath_->runner().WriteSentinelToContainer(base::IntToString(pid_));
499 }
500
501 return true;
502}
503
504void ArcService::ContainerImpl::OnStopDevice(Device* device) {
505 const auto& config = device->config();
506
507 LOG(INFO) << "Stopping device " << device->ifname()
508 << " bridge: " << config.host_ifname()
Garrick Evansb4eb3892019-11-13 12:07:07 +0900509 << " guest_iface: " << config.guest_ifname() << " pid: " << pid_;
Garrick Evansd90a3822019-11-12 17:53:08 +0900510
511 device->Disable();
512 if (!device->IsAndroid()) {
513 datapath_->RemoveInterface(ArcVethHostName(device->ifname()));
514 }
515}
516
517void ArcService::ContainerImpl::OnDefaultInterfaceChanged(
518 const std::string& ifname) {
519 if (!IsStarted())
520 return;
521
522 // For ARC N, we must always be able to find the arc0 device and, at a
523 // minimum, disable it.
Garrick Evansb4eb3892019-11-13 12:07:07 +0900524 if (guest() == GuestMessage::ARC_LEGACY) {
Garrick Evansd90a3822019-11-12 17:53:08 +0900525 datapath_->RemoveLegacyIPv4InboundDNAT();
526 auto* device = dev_mgr_->FindByGuestInterface("arc0");
527 if (!device) {
528 LOG(DFATAL) << "Expected legacy Android device missing";
529 return;
530 }
531 device->Disable();
532
533 // If a new default interface was given, then re-enable with that.
534 if (!ifname.empty()) {
535 datapath_->AddLegacyIPv4InboundDNAT(ifname);
536 device->Enable(ifname);
537 }
538 return;
539 }
540
541 // For ARC P and later, we're only concerned with resetting the device when it
542 // becomes the default (again) in order to ensure any previous configuration.
543 // is cleared.
544 if (ifname.empty())
545 return;
546
547 auto* device = dev_mgr_->FindByGuestInterface(ifname);
548 if (!device) {
549 LOG(ERROR) << "Expected default device missing: " << ifname;
550 return;
551 }
552 device->StopIPv6RoutingLegacy();
553 device->StartIPv6RoutingLegacy(ifname);
554}
555
556void ArcService::ContainerImpl::LinkMsgHandler(const shill::RTNLMessage& msg) {
557 if (!msg.HasAttribute(IFLA_IFNAME)) {
558 LOG(ERROR) << "Link event message does not have IFLA_IFNAME";
559 return;
560 }
561 bool link_up = msg.link_status().flags & IFF_UP;
562 shill::ByteString b(msg.GetAttribute(IFLA_IFNAME));
563 std::string ifname(reinterpret_cast<const char*>(
564 b.GetSubstring(0, IFNAMSIZ).GetConstData()));
565
566 auto* device = dev_mgr_->FindByGuestInterface(ifname);
567 if (!device)
568 return;
569
Garrick Evansb4eb3892019-11-13 12:07:07 +0900570 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900571 if (!ctx) {
572 LOG(DFATAL) << "Context missing";
573 return;
574 }
575
576 // If the link status is unchanged, there is nothing to do.
577 if (!ctx->SetLinkUp(link_up))
578 return;
579
580 if (!link_up) {
581 LOG(INFO) << ifname << " is now down";
582 return;
583 }
584 LOG(INFO) << ifname << " is now up";
585
586 if (device->IsAndroid())
587 return;
588
589 if (device->IsLegacyAndroid()) {
590 OnDefaultInterfaceChanged(dev_mgr_->DefaultInterface());
591 return;
592 }
593
594 device->Enable(ifname);
595}
596
597void ArcService::ContainerImpl::SetupIPv6(Device* device) {
598 device->RegisterIPv6TeardownHandler(base::Bind(
599 &ArcService::ContainerImpl::TeardownIPv6, weak_factory_.GetWeakPtr()));
600
601 auto& ipv6_config = device->ipv6_config();
602 if (ipv6_config.ifname.empty())
603 return;
604
Garrick Evansb4eb3892019-11-13 12:07:07 +0900605 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900606 if (!ctx) {
607 LOG(DFATAL) << "Context missing";
608 return;
609 }
610 if (ctx->HasIPv6())
611 return;
612
613 LOG(INFO) << "Setting up IPv6 for " << ipv6_config.ifname;
614
615 int table_id =
616 GetAndroidRoutingTableId(device->config().guest_ifname(), pid_);
617 if (table_id == kInvalidTableID) {
618 if (ctx->RoutingTableAttempts() < kMaxTableRetries) {
619 LOG(INFO) << "Could not look up routing table ID for container interface "
620 << device->config().guest_ifname() << " - trying again...";
621 base::MessageLoop::current()->task_runner()->PostDelayedTask(
622 FROM_HERE,
623 base::Bind(&ArcService::ContainerImpl::SetupIPv6,
624 weak_factory_.GetWeakPtr(), device),
625 kTableRetryDelay);
626 } else {
627 LOG(DFATAL)
628 << "Could not look up routing table ID for container interface "
629 << device->config().guest_ifname();
630 }
631 return;
632 }
633
634 LOG(INFO) << "Setting IPv6 address " << ipv6_config.addr
635 << "/128, gateway=" << ipv6_config.router << " on "
636 << ipv6_config.ifname;
637
638 char buf[INET6_ADDRSTRLEN] = {0};
639 if (!inet_ntop(AF_INET6, &ipv6_config.addr, buf, sizeof(buf))) {
640 LOG(DFATAL) << "Invalid address: " << ipv6_config.addr;
641 return;
642 }
643 std::string addr = buf;
644
645 if (!inet_ntop(AF_INET6, &ipv6_config.router, buf, sizeof(buf))) {
646 LOG(DFATAL) << "Invalid router address: " << ipv6_config.router;
647 return;
648 }
649 std::string router = buf;
650
651 const auto& config = device->config();
652 {
653 ScopedNS ns(pid_);
654 if (!ns.IsValid()) {
655 LOG(ERROR) << "Invalid container namespace (" << pid_
656 << ") - cannot configure IPv6.";
657 return;
658 }
659 if (!datapath_->AddIPv6GatewayRoutes(config.guest_ifname(), addr, router,
660 ipv6_config.prefix_len, table_id)) {
661 LOG(ERROR) << "Failed to setup IPv6 routes in the container";
662 return;
663 }
664 }
665
666 if (!datapath_->AddIPv6HostRoute(config.host_ifname(), addr,
667 ipv6_config.prefix_len)) {
668 LOG(ERROR) << "Failed to setup the IPv6 route for interface "
669 << config.host_ifname();
670 return;
671 }
672
673 if (!datapath_->AddIPv6Neighbor(ipv6_config.ifname, addr)) {
674 LOG(ERROR) << "Failed to setup the IPv6 neighbor proxy";
675 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
676 ipv6_config.prefix_len);
677 return;
678 }
679
Taoyu Li2980ee92019-11-18 17:49:32 +0900680 if (!datapath_->AddIPv6Forwarding(ipv6_config.ifname,
681 device->config().host_ifname())) {
682 LOG(ERROR) << "Failed to setup iptables for IPv6";
683 datapath_->RemoveIPv6Neighbor(ipv6_config.ifname, addr);
684 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
685 ipv6_config.prefix_len);
686 return;
687 }
688
Garrick Evansd90a3822019-11-12 17:53:08 +0900689 ctx->SetHasIPv6(table_id);
690}
691
692void ArcService::ContainerImpl::TeardownIPv6(Device* device) {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900693 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900694 if (!ctx || !ctx->HasIPv6())
695 return;
696
697 auto& ipv6_config = device->ipv6_config();
698 LOG(INFO) << "Clearing IPv6 for " << ipv6_config.ifname;
699 int table_id = ctx->RoutingTableID();
700 ctx->ClearIPv6();
701
702 char buf[INET6_ADDRSTRLEN] = {0};
703 if (!inet_ntop(AF_INET6, &ipv6_config.addr, buf, sizeof(buf))) {
704 LOG(DFATAL) << "Invalid address: " << ipv6_config.addr;
705 return;
706 }
707 std::string addr = buf;
708
709 if (!inet_ntop(AF_INET6, &ipv6_config.router, buf, sizeof(buf))) {
710 LOG(DFATAL) << "Invalid router address: " << ipv6_config.router;
711 return;
712 }
713 std::string router = buf;
714
715 const auto& config = device->config();
Taoyu Li2980ee92019-11-18 17:49:32 +0900716 datapath_->RemoveIPv6Forwarding(ipv6_config.ifname, config.host_ifname());
Garrick Evansd90a3822019-11-12 17:53:08 +0900717 datapath_->RemoveIPv6Neighbor(ipv6_config.ifname, addr);
718 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
719 ipv6_config.prefix_len);
720
721 ScopedNS ns(pid_);
722 if (ns.IsValid()) {
723 datapath_->RemoveIPv6GatewayRoutes(config.guest_ifname(), addr, router,
724 ipv6_config.prefix_len, table_id);
725 }
726}
727
Garrick Evansb4eb3892019-11-13 12:07:07 +0900728// VM specific functions
729
730ArcService::VmImpl::VmImpl(DeviceManagerBase* dev_mgr, Datapath* datapath)
731 : cid_(kInvalidCID), dev_mgr_(dev_mgr), datapath_(datapath) {}
732
733GuestMessage::GuestType ArcService::VmImpl::guest() const {
734 return GuestMessage::ARC_VM;
735}
736
737bool ArcService::VmImpl::OnStart(int32_t cid) {
738 if (cid <= kInvalidCID) {
739 LOG(ERROR) << "Invalid VM cid " << cid;
740 return false;
741 }
742
743 cid_ = cid;
744 LOG(INFO) << "ARCVM network service started {cid: " << cid_ << "}";
745
746 return true;
747}
748
749void ArcService::VmImpl::OnStop() {
750 LOG(INFO) << "ARCVM network service stopped {cid: " << cid_ << "}";
751 cid_ = kInvalidCID;
752}
753
754bool ArcService::VmImpl::IsStarted() const {
755 return cid_ > kInvalidCID;
756}
757
758bool ArcService::VmImpl::OnStartDevice(Device* device) {
759 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
760 // configurations; but for now ARCVM needs to be treated like ARC++ N.
761 if (!device->IsLegacyAndroid())
762 return false;
763
764 const auto& config = device->config();
765
766 LOG(INFO) << "Starting device " << device->ifname()
767 << " bridge: " << config.host_ifname()
768 << " guest_iface: " << config.guest_ifname() << " cid: " << cid_;
769
770 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
771 if (!ctx) {
772 LOG(ERROR) << "Context missing";
773 return false;
774 }
775
776 // Since the interface will be added to the bridge, no address configuration
777 // should be provided here.
778 std::string tap =
779 datapath_->AddTAP("" /* auto-generate name */, nullptr /* no mac addr */,
780 nullptr /* no ipv4 subnet */, vm_tools::kCrosVmUser);
781 if (tap.empty()) {
782 LOG(ERROR) << "Failed to create TAP device for VM";
783 return false;
784 }
785
786 if (!datapath_->AddToBridge(config.host_ifname(), tap)) {
787 LOG(ERROR) << "Failed to bridge TAP device " << tap;
788 datapath_->RemoveInterface(tap);
789 return false;
790 }
791
792 ctx->SetTAP(tap);
793 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
794 // configurations; but for now ARCVM needs to be treated like ARC++ N.
795 OnDefaultInterfaceChanged(dev_mgr_->DefaultInterface());
796 return true;
797}
798
799void ArcService::VmImpl::OnStopDevice(Device* device) {
800 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
801 // configurations; but for now ARCVM needs to be treated like ARC++ N.
802 if (!device->IsLegacyAndroid())
803 return;
804
805 const auto& config = device->config();
806
807 LOG(INFO) << "Stopping " << device->ifname()
808 << " bridge: " << config.host_ifname()
809 << " guest_iface: " << config.guest_ifname() << " cid: " << cid_;
810
811 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
812 if (!ctx) {
813 LOG(ERROR) << "Context missing";
814 return;
815 }
816
817 device->Disable();
818 datapath_->RemoveInterface(ctx->TAP());
819}
820
821void ArcService::VmImpl::OnDefaultInterfaceChanged(const std::string& ifname) {
822 if (!IsStarted())
823 return;
824
825 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
826 // configurations; but for now ARCVM needs to be treated like ARC++ N.
827 datapath_->RemoveLegacyIPv4InboundDNAT();
828 auto* device = dev_mgr_->FindByGuestInterface("arc0");
829 if (!device) {
830 LOG(DFATAL) << "Expected Android device missing";
831 return;
832 }
833 device->Disable();
834
835 // If a new default interface was given, then re-enable with that.
836 if (!ifname.empty()) {
837 datapath_->AddLegacyIPv4InboundDNAT(ifname);
838 device->Enable(ifname);
839 }
840}
841
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900842} // namespace arc_networkd