blob: 099a8f1292caf16e6d32c4248213dd7757b0636a [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 Evanscfd42822019-11-15 12:38:23 +0900153bool IsLegacy(GuestMessage::GuestType guest) {
154 return guest == GuestMessage::ARC_LEGACY || guest == GuestMessage::ARC_VM;
155}
156
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900157} // namespace
158
Garrick Evans54861622019-07-19 09:05:09 +0900159ArcService::ArcService(DeviceManagerBase* dev_mgr,
Taoyu Li179dcc62019-10-17 11:21:08 +0900160 Datapath* datapath,
Garrick Evans1f5a3612019-11-08 12:59:03 +0900161 bool* is_legacy)
Garrick Evans508a4bc2019-11-14 08:45:52 +0900162 : GuestService(ArcGuest(is_legacy), dev_mgr), datapath_(datapath) {
Taoyu Li179dcc62019-10-17 11:21:08 +0900163 DCHECK(datapath_);
Garrick Evans3915af32019-07-25 15:44:34 +0900164
Garrick Evans508a4bc2019-11-14 08:45:52 +0900165 if (guest_ == GuestMessage::ARC_VM)
166 impl_ = std::make_unique<VmImpl>(dev_mgr, datapath);
167 else
168 impl_ = std::make_unique<ContainerImpl>(dev_mgr, datapath, guest_);
169
Garrick Evans3915af32019-07-25 15:44:34 +0900170 // Load networking modules needed by Android that are not compiled in the
171 // kernel. Android does not allow auto-loading of kernel modules.
Garrick Evans3915af32019-07-25 15:44:34 +0900172
173 // These must succeed.
Taoyu Lia0c43a72019-10-01 10:29:57 +0900174 if (datapath_->runner().ModprobeAll({
Garrick Evans3915af32019-07-25 15:44:34 +0900175 // The netfilter modules needed by netd for iptables commands.
176 "ip6table_filter",
177 "ip6t_ipv6header",
178 "ip6t_REJECT",
179 // The xfrm modules needed for Android's ipsec APIs.
180 "xfrm4_mode_transport",
181 "xfrm4_mode_tunnel",
182 "xfrm6_mode_transport",
183 "xfrm6_mode_tunnel",
184 // The ipsec modules for AH and ESP encryption for ipv6.
185 "ah6",
186 "esp6",
187 }) != 0) {
188 LOG(ERROR) << "One or more required kernel modules failed to load.";
189 }
190
191 // Optional modules.
Taoyu Lia0c43a72019-10-01 10:29:57 +0900192 if (datapath_->runner().ModprobeAll({
Garrick Evans3915af32019-07-25 15:44:34 +0900193 // This module is not available in kernels < 3.18
194 "nf_reject_ipv6",
195 // These modules are needed for supporting Chrome traffic on Android
196 // VPN which uses Android's NAT feature. Android NAT sets up iptables
197 // rules that use these conntrack modules for FTP/TFTP.
198 "nf_nat_ftp",
199 "nf_nat_tftp",
200 }) != 0) {
201 LOG(WARNING) << "One or more optional kernel modules failed to load.";
202 }
Garrick Evans54861622019-07-19 09:05:09 +0900203}
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900204
Garrick Evans508a4bc2019-11-14 08:45:52 +0900205bool ArcService::Start(int32_t id) {
206 if (!impl_->Start(id))
207 return false;
Garrick Evanscb791e72019-11-11 15:44:34 +0900208
Garrick Evans508a4bc2019-11-14 08:45:52 +0900209 OnStart();
210 return true;
211}
212
213void ArcService::OnStart() {
Garrick Evanscb791e72019-11-11 15:44:34 +0900214 // Start known host devices, any new ones will be setup in the process.
215 dev_mgr_->ProcessDevices(
216 base::Bind(&ArcService::StartDevice, weak_factory_.GetWeakPtr()));
217
218 // If this is the first time the service is starting this will create the
219 // Android bridge device; otherwise it does nothing (this is a workaround for
220 // the bug in Shill that casues a Bus crash when it sees the ARC bridge a
221 // second time). Do this after processing the existing devices so it doesn't
222 // get started twice.
Garrick Evanscfd42822019-11-15 12:38:23 +0900223 std::string arc;
224 switch (const auto guest = impl_->guest()) {
225 case GuestMessage::ARC:
226 arc = kAndroidDevice;
227 break;
228 case GuestMessage::ARC_LEGACY:
229 arc = kAndroidLegacyDevice;
230 break;
231 case GuestMessage::ARC_VM:
232 arc = kAndroidVmDevice;
233 break;
234 default:
235 LOG(DFATAL) << "Unexpected guest: " << guest;
236 return;
237 }
238 dev_mgr_->Add(arc);
Garrick Evanscb791e72019-11-11 15:44:34 +0900239
240 // Finally, call the base implementation.
241 GuestService::OnStart();
242}
243
Garrick Evans508a4bc2019-11-14 08:45:52 +0900244void ArcService::Stop() {
245 OnStop();
246 impl_->Stop();
247}
248
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900249void ArcService::OnStop() {
Garrick Evans54861622019-07-19 09:05:09 +0900250 // Call the base implementation.
251 GuestService::OnStop();
252
253 // Stop known host devices. Note that this does not teardown any existing
254 // devices.
255 dev_mgr_->ProcessDevices(
256 base::Bind(&ArcService::StopDevice, weak_factory_.GetWeakPtr()));
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900257}
258
Garrick Evans54861622019-07-19 09:05:09 +0900259void ArcService::OnDeviceAdded(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900260 // ARC N uses legacy single networking and only requires the arcbr0/arc0
261 // configuration. Any other device can be safely ignored.
Garrick Evanscfd42822019-11-15 12:38:23 +0900262 if (IsLegacy(guest_) && !device->IsLegacyAndroid())
Garrick Evans54861622019-07-19 09:05:09 +0900263 return;
Garrick Evansba575742019-07-17 15:48:08 +0900264
Garrick Evans54861622019-07-19 09:05:09 +0900265 const auto& config = device->config();
266
267 LOG(INFO) << "Adding device " << device->ifname()
268 << " bridge: " << config.host_ifname()
Garrick Evans310ab552019-10-08 11:07:53 +0900269 << " guest_iface: " << config.guest_ifname();
Garrick Evans54861622019-07-19 09:05:09 +0900270
271 // Create the bridge.
272 if (!datapath_->AddBridge(config.host_ifname(),
273 IPv4AddressToString(config.host_ipv4_addr()))) {
274 LOG(ERROR) << "Failed to setup arc bridge: " << config.host_ifname();
275 return;
276 }
277
278 // Setup the iptables.
279 if (device->IsLegacyAndroid()) {
280 if (!datapath_->AddLegacyIPv4DNAT(
281 IPv4AddressToString(config.guest_ipv4_addr())))
282 LOG(ERROR) << "Failed to configure ARC traffic rules";
283
284 if (!datapath_->AddOutboundIPv4(config.host_ifname()))
285 LOG(ERROR) << "Failed to configure egress traffic rules";
286 } else if (!device->IsAndroid()) {
287 if (!datapath_->AddInboundIPv4DNAT(
288 device->ifname(), IPv4AddressToString(config.guest_ipv4_addr())))
289 LOG(ERROR) << "Failed to configure ingress traffic rules for "
290 << device->ifname();
291
292 if (!datapath_->AddOutboundIPv4(config.host_ifname()))
293 LOG(ERROR) << "Failed to configure egress traffic rules";
294 }
295
Garrick Evans2c263102019-07-26 16:07:18 +0900296 device->set_context(guest_, std::make_unique<Context>());
Garrick Evans54861622019-07-19 09:05:09 +0900297
298 StartDevice(device);
299}
300
301void ArcService::StartDevice(Device* device) {
Garrick Evanscfd42822019-11-15 12:38:23 +0900302 // ARC N uses legacy single networking and only requires the arcbr0/arc0
303 // configuration. Any other device can be safely ignored.
304 if (IsLegacy(guest_) && !device->IsLegacyAndroid())
305 return;
306
Garrick Evans310ab552019-10-08 11:07:53 +0900307 // This can happen if OnDeviceAdded is invoked when the container is down.
Garrick Evansd90a3822019-11-12 17:53:08 +0900308 if (!impl_->IsStarted())
Garrick Evans54861622019-07-19 09:05:09 +0900309 return;
310
Garrick Evans2c263102019-07-26 16:07:18 +0900311 // If there is no context, then this is a new device and it needs to run
312 // through the full setup process.
313 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
314 if (!ctx)
Garrick Evans54861622019-07-19 09:05:09 +0900315 return OnDeviceAdded(device);
316
Garrick Evans2c263102019-07-26 16:07:18 +0900317 if (ctx->IsStarted()) {
318 LOG(ERROR) << "Attempt to restart device " << device->ifname();
319 return;
320 }
321
Garrick Evansd90a3822019-11-12 17:53:08 +0900322 if (!impl_->OnStartDevice(device)) {
Garrick Evanscb791e72019-11-11 15:44:34 +0900323 LOG(ERROR) << "Failed to start device " << device->ifname();
324 return;
325 }
326
Garrick Evansb4eb3892019-11-13 12:07:07 +0900327 ctx->Start();
Garrick Evans54861622019-07-19 09:05:09 +0900328}
329
330void ArcService::OnDeviceRemoved(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900331 // ARC N uses legacy single networking and only requires the arcbr0/arc0
332 // configuration. Any other device can be safely ignored.
Garrick Evanscfd42822019-11-15 12:38:23 +0900333 if (IsLegacy(guest_) && !device->IsLegacyAndroid())
Garrick Evans54861622019-07-19 09:05:09 +0900334 return;
335
Garrick Evans310ab552019-10-08 11:07:53 +0900336 // If the container is down, this call does nothing.
Garrick Evans54861622019-07-19 09:05:09 +0900337 StopDevice(device);
338
339 const auto& config = device->config();
340
341 LOG(INFO) << "Removing device " << device->ifname()
342 << " bridge: " << config.host_ifname()
343 << " guest_iface: " << config.guest_ifname();
344
Garrick Evans260ff302019-07-25 11:22:50 +0900345 device->Disable();
Garrick Evans54861622019-07-19 09:05:09 +0900346 if (device->IsLegacyAndroid()) {
347 datapath_->RemoveOutboundIPv4(config.host_ifname());
348 datapath_->RemoveLegacyIPv4DNAT();
349 } else if (!device->IsAndroid()) {
350 datapath_->RemoveOutboundIPv4(config.host_ifname());
351 datapath_->RemoveInboundIPv4DNAT(
352 device->ifname(), IPv4AddressToString(config.guest_ipv4_addr()));
353 }
354
355 datapath_->RemoveBridge(config.host_ifname());
356
Garrick Evans2c263102019-07-26 16:07:18 +0900357 device->set_context(guest_, nullptr);
Garrick Evans54861622019-07-19 09:05:09 +0900358}
359
360void ArcService::StopDevice(Device* device) {
Garrick Evanscfd42822019-11-15 12:38:23 +0900361 // ARC N uses legacy single networking and only requires the arcbr0/arc0
362 // configuration. Any other device can be safely ignored.
363 if (IsLegacy(guest_) && !device->IsLegacyAndroid())
364 return;
365
Garrick Evans310ab552019-10-08 11:07:53 +0900366 // This can happen if the device if OnDeviceRemoved is invoked when the
367 // container is down.
Garrick Evansd90a3822019-11-12 17:53:08 +0900368 if (!impl_->IsStarted())
Garrick Evans54861622019-07-19 09:05:09 +0900369 return;
370
Garrick Evans2c263102019-07-26 16:07:18 +0900371 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
372 if (!ctx) {
373 LOG(ERROR) << "Attempt to stop removed device " << device->ifname();
374 return;
375 }
376
377 if (!ctx->IsStarted()) {
378 LOG(ERROR) << "Attempt to re-stop device " << device->ifname();
379 return;
380 }
381
Garrick Evansd90a3822019-11-12 17:53:08 +0900382 impl_->OnStopDevice(device);
Garrick Evanscb791e72019-11-11 15:44:34 +0900383
384 ctx->Stop();
385}
386
Garrick Evans54861622019-07-19 09:05:09 +0900387void ArcService::OnDefaultInterfaceChanged(const std::string& ifname) {
Garrick Evansd90a3822019-11-12 17:53:08 +0900388 impl_->OnDefaultInterfaceChanged(ifname);
Garrick Evans54861622019-07-19 09:05:09 +0900389}
Garrick Evansba575742019-07-17 15:48:08 +0900390
Garrick Evans2c263102019-07-26 16:07:18 +0900391// Context
392
393ArcService::Context::Context() : Device::Context() {
394 Stop();
395}
396
397void ArcService::Context::Start() {
398 Stop();
399 started_ = true;
400}
401
402void ArcService::Context::Stop() {
403 started_ = false;
404 link_up_ = false;
405 routing_table_id_ = kInvalidTableID;
406 routing_table_attempts_ = 0;
407}
408
409bool ArcService::Context::IsStarted() const {
410 return started_;
411}
412
413bool ArcService::Context::IsLinkUp() const {
414 return link_up_;
415}
416
417bool ArcService::Context::SetLinkUp(bool link_up) {
418 if (link_up == link_up_)
419 return false;
420
421 link_up_ = link_up;
422 return true;
423}
424
425bool ArcService::Context::HasIPv6() const {
426 return routing_table_id_ != kInvalidTableID;
427}
428
429bool ArcService::Context::SetHasIPv6(int routing_table_id) {
430 if (routing_table_id <= kRouteControllerRouteTableOffsetFromIndex)
431 return false;
432
433 routing_table_id_ = routing_table_id;
434 return true;
435}
436
Garrick Evansdc3fc452019-09-06 14:15:04 +0900437void ArcService::Context::ClearIPv6() {
438 routing_table_id_ = kInvalidTableID;
439 routing_table_attempts_ = 0;
440}
441
Garrick Evans2c263102019-07-26 16:07:18 +0900442int ArcService::Context::RoutingTableID() const {
443 return routing_table_id_;
444}
445
446int ArcService::Context::RoutingTableAttempts() {
447 return routing_table_attempts_++;
448}
449
Garrick Evansb4eb3892019-11-13 12:07:07 +0900450const std::string& ArcService::Context::TAP() const {
451 return tap_;
452}
453
454void ArcService::Context::SetTAP(const std::string& tap) {
455 tap_ = tap;
456}
457
Garrick Evansd90a3822019-11-12 17:53:08 +0900458// ARC++ specific functions.
459
460ArcService::ContainerImpl::ContainerImpl(DeviceManagerBase* dev_mgr,
461 Datapath* datapath,
462 GuestMessage::GuestType guest)
463 : pid_(kInvalidPID),
464 dev_mgr_(dev_mgr),
465 datapath_(datapath),
466 guest_(guest) {}
467
Garrick Evansb4eb3892019-11-13 12:07:07 +0900468GuestMessage::GuestType ArcService::ContainerImpl::guest() const {
469 return guest_;
470}
471
Garrick Evans508a4bc2019-11-14 08:45:52 +0900472bool ArcService::ContainerImpl::Start(int32_t pid) {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900473 // TODO(garrick): Remove this workaround.
474 pid_ = (pid == kTestPID) ? pid : GetContainerPID();
Garrick Evansd90a3822019-11-12 17:53:08 +0900475 if (pid_ == kInvalidPID) {
476 LOG(ERROR) << "Cannot start service - invalid container PID";
477 return false;
478 }
479
480 // Start listening for RTNetlink messages in the container's net namespace
481 // to be notified whenever it brings up an interface.
482 {
483 ScopedNS ns(pid_);
484 if (ns.IsValid()) {
485 rtnl_handler_ = std::make_unique<RTNetlinkHandler>();
486 rtnl_handler_->Start(RTMGRP_LINK);
487 link_listener_ = std::make_unique<shill::RTNLListener>(
488 shill::RTNLHandler::kRequestLink,
489 Bind(&ArcService::ContainerImpl::LinkMsgHandler,
490 weak_factory_.GetWeakPtr()),
491 rtnl_handler_.get());
492 } else {
493 // This is bad - it means we won't ever be able to tell when the container
494 // brings up an interface.
495 LOG(ERROR)
496 << "Cannot start netlink listener - invalid container namespace?";
497 return false;
498 }
499 }
500
501 dev_mgr_->RegisterDeviceIPv6AddressFoundHandler(
Garrick Evansb4eb3892019-11-13 12:07:07 +0900502 guest(), base::Bind(&ArcService::ContainerImpl::SetupIPv6,
503 weak_factory_.GetWeakPtr()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900504
505 LOG(INFO) << "ARC++ network service started {pid: " << pid_ << "}";
506 return true;
507}
508
Garrick Evans508a4bc2019-11-14 08:45:52 +0900509void ArcService::ContainerImpl::Stop() {
Garrick Evansd90a3822019-11-12 17:53:08 +0900510 rtnl_handler_->RemoveListener(link_listener_.get());
511 link_listener_.reset();
512 rtnl_handler_.reset();
513
514 LOG(INFO) << "ARC++ network service stopped {pid: " << pid_ << "}";
515 pid_ = kInvalidPID;
516}
517
518bool ArcService::ContainerImpl::IsStarted() const {
519 return pid_ != kInvalidPID;
520}
521
522bool ArcService::ContainerImpl::OnStartDevice(Device* device) {
523 const auto& config = device->config();
524
525 LOG(INFO) << "Starting device " << device->ifname()
526 << " bridge: " << config.host_ifname()
Garrick Evansb4eb3892019-11-13 12:07:07 +0900527 << " guest_iface: " << config.guest_ifname() << " pid: " << pid_;
Garrick Evansd90a3822019-11-12 17:53:08 +0900528
529 std::string veth_ifname = datapath_->AddVirtualBridgedInterface(
530 device->ifname(), MacAddressToString(config.guest_mac_addr()),
531 config.host_ifname());
532 if (veth_ifname.empty()) {
533 LOG(ERROR) << "Failed to create virtual interface for container";
534 return false;
535 }
536
537 if (!datapath_->AddInterfaceToContainer(
538 pid_, veth_ifname, config.guest_ifname(),
539 IPv4AddressToString(config.guest_ipv4_addr()),
540 device->options().fwd_multicast)) {
541 LOG(ERROR) << "Failed to create container interface.";
542 datapath_->RemoveInterface(veth_ifname);
543 datapath_->RemoveBridge(config.host_ifname());
544 return false;
545 }
546
547 // Signal the container that the network device is ready.
548 // This is only applicable for arc0.
549 if (device->IsAndroid() || device->IsLegacyAndroid()) {
550 datapath_->runner().WriteSentinelToContainer(base::IntToString(pid_));
551 }
552
553 return true;
554}
555
556void ArcService::ContainerImpl::OnStopDevice(Device* device) {
557 const auto& config = device->config();
558
559 LOG(INFO) << "Stopping device " << device->ifname()
560 << " bridge: " << config.host_ifname()
Garrick Evansb4eb3892019-11-13 12:07:07 +0900561 << " guest_iface: " << config.guest_ifname() << " pid: " << pid_;
Garrick Evansd90a3822019-11-12 17:53:08 +0900562
563 device->Disable();
564 if (!device->IsAndroid()) {
565 datapath_->RemoveInterface(ArcVethHostName(device->ifname()));
566 }
567}
568
569void ArcService::ContainerImpl::OnDefaultInterfaceChanged(
570 const std::string& ifname) {
571 if (!IsStarted())
572 return;
573
574 // For ARC N, we must always be able to find the arc0 device and, at a
575 // minimum, disable it.
Garrick Evansb4eb3892019-11-13 12:07:07 +0900576 if (guest() == GuestMessage::ARC_LEGACY) {
Garrick Evansd90a3822019-11-12 17:53:08 +0900577 datapath_->RemoveLegacyIPv4InboundDNAT();
578 auto* device = dev_mgr_->FindByGuestInterface("arc0");
579 if (!device) {
580 LOG(DFATAL) << "Expected legacy Android device missing";
581 return;
582 }
583 device->Disable();
584
585 // If a new default interface was given, then re-enable with that.
586 if (!ifname.empty()) {
587 datapath_->AddLegacyIPv4InboundDNAT(ifname);
588 device->Enable(ifname);
589 }
590 return;
591 }
592
593 // For ARC P and later, we're only concerned with resetting the device when it
594 // becomes the default (again) in order to ensure any previous configuration.
595 // is cleared.
596 if (ifname.empty())
597 return;
598
599 auto* device = dev_mgr_->FindByGuestInterface(ifname);
600 if (!device) {
601 LOG(ERROR) << "Expected default device missing: " << ifname;
602 return;
603 }
604 device->StopIPv6RoutingLegacy();
605 device->StartIPv6RoutingLegacy(ifname);
606}
607
608void ArcService::ContainerImpl::LinkMsgHandler(const shill::RTNLMessage& msg) {
609 if (!msg.HasAttribute(IFLA_IFNAME)) {
610 LOG(ERROR) << "Link event message does not have IFLA_IFNAME";
611 return;
612 }
613 bool link_up = msg.link_status().flags & IFF_UP;
614 shill::ByteString b(msg.GetAttribute(IFLA_IFNAME));
615 std::string ifname(reinterpret_cast<const char*>(
616 b.GetSubstring(0, IFNAMSIZ).GetConstData()));
617
618 auto* device = dev_mgr_->FindByGuestInterface(ifname);
619 if (!device)
620 return;
621
Garrick Evansb4eb3892019-11-13 12:07:07 +0900622 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900623 if (!ctx) {
624 LOG(DFATAL) << "Context missing";
625 return;
626 }
627
628 // If the link status is unchanged, there is nothing to do.
629 if (!ctx->SetLinkUp(link_up))
630 return;
631
632 if (!link_up) {
633 LOG(INFO) << ifname << " is now down";
634 return;
635 }
636 LOG(INFO) << ifname << " is now up";
637
638 if (device->IsAndroid())
639 return;
640
641 if (device->IsLegacyAndroid()) {
642 OnDefaultInterfaceChanged(dev_mgr_->DefaultInterface());
643 return;
644 }
645
646 device->Enable(ifname);
647}
648
649void ArcService::ContainerImpl::SetupIPv6(Device* device) {
650 device->RegisterIPv6TeardownHandler(base::Bind(
651 &ArcService::ContainerImpl::TeardownIPv6, weak_factory_.GetWeakPtr()));
652
653 auto& ipv6_config = device->ipv6_config();
654 if (ipv6_config.ifname.empty())
655 return;
656
Garrick Evansb4eb3892019-11-13 12:07:07 +0900657 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900658 if (!ctx) {
659 LOG(DFATAL) << "Context missing";
660 return;
661 }
662 if (ctx->HasIPv6())
663 return;
664
665 LOG(INFO) << "Setting up IPv6 for " << ipv6_config.ifname;
666
667 int table_id =
668 GetAndroidRoutingTableId(device->config().guest_ifname(), pid_);
669 if (table_id == kInvalidTableID) {
670 if (ctx->RoutingTableAttempts() < kMaxTableRetries) {
671 LOG(INFO) << "Could not look up routing table ID for container interface "
672 << device->config().guest_ifname() << " - trying again...";
673 base::MessageLoop::current()->task_runner()->PostDelayedTask(
674 FROM_HERE,
675 base::Bind(&ArcService::ContainerImpl::SetupIPv6,
676 weak_factory_.GetWeakPtr(), device),
677 kTableRetryDelay);
678 } else {
679 LOG(DFATAL)
680 << "Could not look up routing table ID for container interface "
681 << device->config().guest_ifname();
682 }
683 return;
684 }
685
686 LOG(INFO) << "Setting IPv6 address " << ipv6_config.addr
687 << "/128, gateway=" << ipv6_config.router << " on "
688 << ipv6_config.ifname;
689
690 char buf[INET6_ADDRSTRLEN] = {0};
691 if (!inet_ntop(AF_INET6, &ipv6_config.addr, buf, sizeof(buf))) {
692 LOG(DFATAL) << "Invalid address: " << ipv6_config.addr;
693 return;
694 }
695 std::string addr = buf;
696
697 if (!inet_ntop(AF_INET6, &ipv6_config.router, buf, sizeof(buf))) {
698 LOG(DFATAL) << "Invalid router address: " << ipv6_config.router;
699 return;
700 }
701 std::string router = buf;
702
703 const auto& config = device->config();
704 {
705 ScopedNS ns(pid_);
706 if (!ns.IsValid()) {
707 LOG(ERROR) << "Invalid container namespace (" << pid_
708 << ") - cannot configure IPv6.";
709 return;
710 }
Taoyu Li90c13912019-11-26 17:56:54 +0900711 // Tag the interface so that ARC can detect this manual configuration and
712 // skip disabling and re-enabling IPv6 (b/144545910).
713 if (!datapath_->SetInterfaceFlag(config.guest_ifname(), IFF_DEBUG)) {
714 LOG(ERROR) << "Failed to mark IPv6 manual config flag on interface";
715 }
Garrick Evansd90a3822019-11-12 17:53:08 +0900716 if (!datapath_->AddIPv6GatewayRoutes(config.guest_ifname(), addr, router,
717 ipv6_config.prefix_len, table_id)) {
718 LOG(ERROR) << "Failed to setup IPv6 routes in the container";
719 return;
720 }
721 }
722
723 if (!datapath_->AddIPv6HostRoute(config.host_ifname(), addr,
724 ipv6_config.prefix_len)) {
725 LOG(ERROR) << "Failed to setup the IPv6 route for interface "
726 << config.host_ifname();
727 return;
728 }
729
730 if (!datapath_->AddIPv6Neighbor(ipv6_config.ifname, addr)) {
731 LOG(ERROR) << "Failed to setup the IPv6 neighbor proxy";
732 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
733 ipv6_config.prefix_len);
734 return;
735 }
736
Taoyu Li2980ee92019-11-18 17:49:32 +0900737 if (!datapath_->AddIPv6Forwarding(ipv6_config.ifname,
738 device->config().host_ifname())) {
739 LOG(ERROR) << "Failed to setup iptables for IPv6";
740 datapath_->RemoveIPv6Neighbor(ipv6_config.ifname, addr);
741 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
742 ipv6_config.prefix_len);
743 return;
744 }
745
Garrick Evansd90a3822019-11-12 17:53:08 +0900746 ctx->SetHasIPv6(table_id);
747}
748
749void ArcService::ContainerImpl::TeardownIPv6(Device* device) {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900750 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900751 if (!ctx || !ctx->HasIPv6())
752 return;
753
754 auto& ipv6_config = device->ipv6_config();
755 LOG(INFO) << "Clearing IPv6 for " << ipv6_config.ifname;
756 int table_id = ctx->RoutingTableID();
757 ctx->ClearIPv6();
758
759 char buf[INET6_ADDRSTRLEN] = {0};
760 if (!inet_ntop(AF_INET6, &ipv6_config.addr, buf, sizeof(buf))) {
761 LOG(DFATAL) << "Invalid address: " << ipv6_config.addr;
762 return;
763 }
764 std::string addr = buf;
765
766 if (!inet_ntop(AF_INET6, &ipv6_config.router, buf, sizeof(buf))) {
767 LOG(DFATAL) << "Invalid router address: " << ipv6_config.router;
768 return;
769 }
770 std::string router = buf;
771
772 const auto& config = device->config();
Taoyu Li2980ee92019-11-18 17:49:32 +0900773 datapath_->RemoveIPv6Forwarding(ipv6_config.ifname, config.host_ifname());
Garrick Evansd90a3822019-11-12 17:53:08 +0900774 datapath_->RemoveIPv6Neighbor(ipv6_config.ifname, addr);
775 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
776 ipv6_config.prefix_len);
777
778 ScopedNS ns(pid_);
779 if (ns.IsValid()) {
780 datapath_->RemoveIPv6GatewayRoutes(config.guest_ifname(), addr, router,
781 ipv6_config.prefix_len, table_id);
782 }
783}
784
Garrick Evansb4eb3892019-11-13 12:07:07 +0900785// VM specific functions
786
787ArcService::VmImpl::VmImpl(DeviceManagerBase* dev_mgr, Datapath* datapath)
788 : cid_(kInvalidCID), dev_mgr_(dev_mgr), datapath_(datapath) {}
789
790GuestMessage::GuestType ArcService::VmImpl::guest() const {
791 return GuestMessage::ARC_VM;
792}
793
Garrick Evans508a4bc2019-11-14 08:45:52 +0900794bool ArcService::VmImpl::Start(int32_t cid) {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900795 if (cid <= kInvalidCID) {
796 LOG(ERROR) << "Invalid VM cid " << cid;
797 return false;
798 }
799
800 cid_ = cid;
801 LOG(INFO) << "ARCVM network service started {cid: " << cid_ << "}";
802
803 return true;
804}
805
Garrick Evans508a4bc2019-11-14 08:45:52 +0900806void ArcService::VmImpl::Stop() {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900807 LOG(INFO) << "ARCVM network service stopped {cid: " << cid_ << "}";
808 cid_ = kInvalidCID;
809}
810
811bool ArcService::VmImpl::IsStarted() const {
812 return cid_ > kInvalidCID;
813}
814
815bool ArcService::VmImpl::OnStartDevice(Device* device) {
816 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
817 // configurations; but for now ARCVM needs to be treated like ARC++ N.
818 if (!device->IsLegacyAndroid())
819 return false;
820
821 const auto& config = device->config();
822
823 LOG(INFO) << "Starting device " << device->ifname()
824 << " bridge: " << config.host_ifname()
825 << " guest_iface: " << config.guest_ifname() << " cid: " << cid_;
826
827 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
828 if (!ctx) {
829 LOG(ERROR) << "Context missing";
830 return false;
831 }
832
833 // Since the interface will be added to the bridge, no address configuration
834 // should be provided here.
835 std::string tap =
836 datapath_->AddTAP("" /* auto-generate name */, nullptr /* no mac addr */,
837 nullptr /* no ipv4 subnet */, vm_tools::kCrosVmUser);
838 if (tap.empty()) {
839 LOG(ERROR) << "Failed to create TAP device for VM";
840 return false;
841 }
842
843 if (!datapath_->AddToBridge(config.host_ifname(), tap)) {
844 LOG(ERROR) << "Failed to bridge TAP device " << tap;
845 datapath_->RemoveInterface(tap);
846 return false;
847 }
848
849 ctx->SetTAP(tap);
850 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
851 // configurations; but for now ARCVM needs to be treated like ARC++ N.
852 OnDefaultInterfaceChanged(dev_mgr_->DefaultInterface());
853 return true;
854}
855
856void ArcService::VmImpl::OnStopDevice(Device* device) {
857 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
858 // configurations; but for now ARCVM needs to be treated like ARC++ N.
859 if (!device->IsLegacyAndroid())
860 return;
861
862 const auto& config = device->config();
863
864 LOG(INFO) << "Stopping " << device->ifname()
865 << " bridge: " << config.host_ifname()
866 << " guest_iface: " << config.guest_ifname() << " cid: " << cid_;
867
868 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
869 if (!ctx) {
870 LOG(ERROR) << "Context missing";
871 return;
872 }
873
874 device->Disable();
875 datapath_->RemoveInterface(ctx->TAP());
876}
877
878void ArcService::VmImpl::OnDefaultInterfaceChanged(const std::string& ifname) {
879 if (!IsStarted())
880 return;
881
882 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
883 // configurations; but for now ARCVM needs to be treated like ARC++ N.
884 datapath_->RemoveLegacyIPv4InboundDNAT();
885 auto* device = dev_mgr_->FindByGuestInterface("arc0");
886 if (!device) {
887 LOG(DFATAL) << "Expected Android device missing";
888 return;
889 }
890 device->Disable();
891
892 // If a new default interface was given, then re-enable with that.
893 if (!ifname.empty()) {
894 datapath_->AddLegacyIPv4InboundDNAT(ifname);
895 device->Enable(ifname);
896 }
897}
898
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900899} // namespace arc_networkd