blob: 5acd2efb4dbdc42692fbaad2e1f3d536f3c363eb [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
Garrick Evans1f5a3612019-11-08 12:59:03 +090081
82 brillo::KeyValueStore store;
83 if (!store.Load(base::FilePath(kLsbReleasePath))) {
84 LOG(ERROR) << "Could not read lsb-release";
85 return false;
86 }
87
88 std::string value;
89 if (!store.GetString("CHROMEOS_ARC_ANDROID_SDK_VERSION", &value)) {
90 LOG(ERROR) << "ARC multi-networking disabled - cannot determine Android "
91 "SDK version";
92 return false;
93 }
94 int ver = 0;
95 if (!base::StringToInt(value.c_str(), &ver)) {
96 LOG(ERROR) << "ARC multi-networking disabled - invalid Android SDK version";
97 return false;
98 }
99 if (ver < kMinAndroidSdkVersion) {
100 LOG(INFO) << "ARC multi-networking disabled for Android SDK " << value;
101 return false;
102 }
Garrick Evans1f5a3612019-11-08 12:59:03 +0900103 return true;
104}
105
Garrick Evansa34b5862019-11-20 09:34:01 +0900106// Load networking modules needed by Android that are not compiled in the
107// kernel. Android does not allow auto-loading of kernel modules.
108void LoadModules(const Datapath& datapath) {
109 static bool done = false;
110 if (done)
111 return;
112
113 // These must succeed.
114 if (datapath.runner().ModprobeAll({
115 // The netfilter modules needed by netd for iptables commands.
116 "ip6table_filter",
117 "ip6t_ipv6header",
118 "ip6t_REJECT",
119 // The xfrm modules needed for Android's ipsec APIs.
120 "xfrm4_mode_transport",
121 "xfrm4_mode_tunnel",
122 "xfrm6_mode_transport",
123 "xfrm6_mode_tunnel",
124 // The ipsec modules for AH and ESP encryption for ipv6.
125 "ah6",
126 "esp6",
127 }) != 0) {
128 LOG(ERROR) << "One or more required kernel modules failed to load."
129 << " Some Android functionality may be broken.";
130 }
131 // Optional modules.
132 if (datapath.runner().ModprobeAll({
133 // This module is not available in kernels < 3.18
134 "nf_reject_ipv6",
135 // These modules are needed for supporting Chrome traffic on Android
136 // VPN which uses Android's NAT feature. Android NAT sets up
137 // iptables
138 // rules that use these conntrack modules for FTP/TFTP.
139 "nf_nat_ftp",
140 "nf_nat_tftp",
141 }) != 0) {
142 LOG(WARNING) << "One or more optional kernel modules failed to load.";
143 }
144
145 done = true;
146}
147
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900148// TODO(garrick): Remove this workaround ASAP.
149int GetContainerPID() {
150 const base::FilePath path("/run/containers/android-run_oci/container.pid");
151 std::string pid_str;
152 if (!base::ReadFileToStringWithMaxSize(path, &pid_str, 16 /* max size */)) {
153 LOG(ERROR) << "Failed to read pid file";
Garrick Evans54861622019-07-19 09:05:09 +0900154 return kInvalidPID;
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900155 }
156 int pid;
157 if (!base::StringToInt(base::TrimWhitespaceASCII(pid_str, base::TRIM_ALL),
158 &pid)) {
159 LOG(ERROR) << "Failed to convert container pid string";
Garrick Evans54861622019-07-19 09:05:09 +0900160 return kInvalidPID;
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900161 }
162 LOG(INFO) << "Read container pid as " << pid;
163 return pid;
164}
165
Garrick Evans508a4bc2019-11-14 08:45:52 +0900166bool IsArcVm() {
167 const base::FilePath path("/run/chrome/is_arcvm");
168 std::string contents;
169 if (!base::ReadFileToString(path, &contents)) {
170 PLOG(ERROR) << "Could not read " << path.value();
171 }
172 return contents == "1";
173}
174
175GuestMessage::GuestType ArcGuest(bool* is_legacy_override_for_testing) {
176 if (is_legacy_override_for_testing)
177 return *is_legacy_override_for_testing ? GuestMessage::ARC_LEGACY
178 : GuestMessage::ARC;
179
180 return IsArcVm() ? GuestMessage::ARC_VM
181 : ShouldEnableMultinet() ? GuestMessage::ARC
182 : GuestMessage::ARC_LEGACY;
183}
184
Garrick Evanscfd42822019-11-15 12:38:23 +0900185bool IsLegacy(GuestMessage::GuestType guest) {
186 return guest == GuestMessage::ARC_LEGACY || guest == GuestMessage::ARC_VM;
187}
188
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900189} // namespace
190
Garrick Evans54861622019-07-19 09:05:09 +0900191ArcService::ArcService(DeviceManagerBase* dev_mgr,
Taoyu Li179dcc62019-10-17 11:21:08 +0900192 Datapath* datapath,
Garrick Evans1f5a3612019-11-08 12:59:03 +0900193 bool* is_legacy)
Garrick Evans508a4bc2019-11-14 08:45:52 +0900194 : GuestService(ArcGuest(is_legacy), dev_mgr), datapath_(datapath) {
Taoyu Li179dcc62019-10-17 11:21:08 +0900195 DCHECK(datapath_);
Garrick Evans3915af32019-07-25 15:44:34 +0900196
Garrick Evans508a4bc2019-11-14 08:45:52 +0900197 if (guest_ == GuestMessage::ARC_VM)
198 impl_ = std::make_unique<VmImpl>(dev_mgr, datapath);
199 else
200 impl_ = std::make_unique<ContainerImpl>(dev_mgr, datapath, guest_);
Garrick Evans54861622019-07-19 09:05:09 +0900201}
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900202
Garrick Evans508a4bc2019-11-14 08:45:52 +0900203bool ArcService::Start(int32_t id) {
204 if (!impl_->Start(id))
205 return false;
Garrick Evanscb791e72019-11-11 15:44:34 +0900206
Garrick Evans508a4bc2019-11-14 08:45:52 +0900207 OnStart();
208 return true;
209}
210
211void ArcService::OnStart() {
Garrick Evanscb791e72019-11-11 15:44:34 +0900212 // Start known host devices, any new ones will be setup in the process.
213 dev_mgr_->ProcessDevices(
214 base::Bind(&ArcService::StartDevice, weak_factory_.GetWeakPtr()));
215
216 // If this is the first time the service is starting this will create the
217 // Android bridge device; otherwise it does nothing (this is a workaround for
218 // the bug in Shill that casues a Bus crash when it sees the ARC bridge a
219 // second time). Do this after processing the existing devices so it doesn't
220 // get started twice.
Garrick Evanscfd42822019-11-15 12:38:23 +0900221 std::string arc;
222 switch (const auto guest = impl_->guest()) {
223 case GuestMessage::ARC:
224 arc = kAndroidDevice;
225 break;
226 case GuestMessage::ARC_LEGACY:
227 arc = kAndroidLegacyDevice;
228 break;
229 case GuestMessage::ARC_VM:
230 arc = kAndroidVmDevice;
231 break;
232 default:
233 LOG(DFATAL) << "Unexpected guest: " << guest;
234 return;
235 }
236 dev_mgr_->Add(arc);
Garrick Evanscb791e72019-11-11 15:44:34 +0900237
238 // Finally, call the base implementation.
239 GuestService::OnStart();
240}
241
Garrick Evans508a4bc2019-11-14 08:45:52 +0900242void ArcService::Stop() {
243 OnStop();
244 impl_->Stop();
245}
246
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900247void ArcService::OnStop() {
Garrick Evans54861622019-07-19 09:05:09 +0900248 // Call the base implementation.
249 GuestService::OnStop();
250
251 // Stop known host devices. Note that this does not teardown any existing
252 // devices.
253 dev_mgr_->ProcessDevices(
254 base::Bind(&ArcService::StopDevice, weak_factory_.GetWeakPtr()));
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900255}
256
Garrick Evans54861622019-07-19 09:05:09 +0900257void ArcService::OnDeviceAdded(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900258 // ARC N uses legacy single networking and only requires the arcbr0/arc0
259 // configuration. Any other device can be safely ignored.
Garrick Evanscfd42822019-11-15 12:38:23 +0900260 if (IsLegacy(guest_) && !device->IsLegacyAndroid())
Garrick Evans54861622019-07-19 09:05:09 +0900261 return;
Garrick Evansba575742019-07-17 15:48:08 +0900262
Garrick Evans54861622019-07-19 09:05:09 +0900263 const auto& config = device->config();
264
265 LOG(INFO) << "Adding device " << device->ifname()
266 << " bridge: " << config.host_ifname()
Garrick Evans310ab552019-10-08 11:07:53 +0900267 << " guest_iface: " << config.guest_ifname();
Garrick Evans54861622019-07-19 09:05:09 +0900268
269 // Create the bridge.
270 if (!datapath_->AddBridge(config.host_ifname(),
271 IPv4AddressToString(config.host_ipv4_addr()))) {
272 LOG(ERROR) << "Failed to setup arc bridge: " << config.host_ifname();
273 return;
274 }
275
276 // Setup the iptables.
277 if (device->IsLegacyAndroid()) {
278 if (!datapath_->AddLegacyIPv4DNAT(
279 IPv4AddressToString(config.guest_ipv4_addr())))
280 LOG(ERROR) << "Failed to configure ARC traffic rules";
281
282 if (!datapath_->AddOutboundIPv4(config.host_ifname()))
283 LOG(ERROR) << "Failed to configure egress traffic rules";
284 } else if (!device->IsAndroid()) {
285 if (!datapath_->AddInboundIPv4DNAT(
286 device->ifname(), IPv4AddressToString(config.guest_ipv4_addr())))
287 LOG(ERROR) << "Failed to configure ingress traffic rules for "
288 << device->ifname();
289
290 if (!datapath_->AddOutboundIPv4(config.host_ifname()))
291 LOG(ERROR) << "Failed to configure egress traffic rules";
292 }
293
Garrick Evans2c263102019-07-26 16:07:18 +0900294 device->set_context(guest_, std::make_unique<Context>());
Garrick Evans54861622019-07-19 09:05:09 +0900295
296 StartDevice(device);
297}
298
299void ArcService::StartDevice(Device* device) {
Garrick Evanscfd42822019-11-15 12:38:23 +0900300 // ARC N uses legacy single networking and only requires the arcbr0/arc0
301 // configuration. Any other device can be safely ignored.
302 if (IsLegacy(guest_) && !device->IsLegacyAndroid())
303 return;
304
Garrick Evans310ab552019-10-08 11:07:53 +0900305 // This can happen if OnDeviceAdded is invoked when the container is down.
Garrick Evansd90a3822019-11-12 17:53:08 +0900306 if (!impl_->IsStarted())
Garrick Evans54861622019-07-19 09:05:09 +0900307 return;
308
Garrick Evans2c263102019-07-26 16:07:18 +0900309 // If there is no context, then this is a new device and it needs to run
310 // through the full setup process.
311 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
312 if (!ctx)
Garrick Evans54861622019-07-19 09:05:09 +0900313 return OnDeviceAdded(device);
314
Garrick Evans2c263102019-07-26 16:07:18 +0900315 if (ctx->IsStarted()) {
316 LOG(ERROR) << "Attempt to restart device " << device->ifname();
317 return;
318 }
319
Garrick Evansd90a3822019-11-12 17:53:08 +0900320 if (!impl_->OnStartDevice(device)) {
Garrick Evanscb791e72019-11-11 15:44:34 +0900321 LOG(ERROR) << "Failed to start device " << device->ifname();
322 return;
323 }
324
Garrick Evansb4eb3892019-11-13 12:07:07 +0900325 ctx->Start();
Garrick Evans54861622019-07-19 09:05:09 +0900326}
327
328void ArcService::OnDeviceRemoved(Device* device) {
Garrick Evans310ab552019-10-08 11:07:53 +0900329 // ARC N uses legacy single networking and only requires the arcbr0/arc0
330 // configuration. Any other device can be safely ignored.
Garrick Evanscfd42822019-11-15 12:38:23 +0900331 if (IsLegacy(guest_) && !device->IsLegacyAndroid())
Garrick Evans54861622019-07-19 09:05:09 +0900332 return;
333
Garrick Evans310ab552019-10-08 11:07:53 +0900334 // If the container is down, this call does nothing.
Garrick Evans54861622019-07-19 09:05:09 +0900335 StopDevice(device);
336
337 const auto& config = device->config();
338
339 LOG(INFO) << "Removing device " << device->ifname()
340 << " bridge: " << config.host_ifname()
341 << " guest_iface: " << config.guest_ifname();
342
Garrick Evans260ff302019-07-25 11:22:50 +0900343 device->Disable();
Garrick Evans54861622019-07-19 09:05:09 +0900344 if (device->IsLegacyAndroid()) {
345 datapath_->RemoveOutboundIPv4(config.host_ifname());
346 datapath_->RemoveLegacyIPv4DNAT();
347 } else if (!device->IsAndroid()) {
348 datapath_->RemoveOutboundIPv4(config.host_ifname());
349 datapath_->RemoveInboundIPv4DNAT(
350 device->ifname(), IPv4AddressToString(config.guest_ipv4_addr()));
351 }
352
353 datapath_->RemoveBridge(config.host_ifname());
354
Garrick Evans2c263102019-07-26 16:07:18 +0900355 device->set_context(guest_, nullptr);
Garrick Evans54861622019-07-19 09:05:09 +0900356}
357
358void ArcService::StopDevice(Device* device) {
Garrick Evanscfd42822019-11-15 12:38:23 +0900359 // ARC N uses legacy single networking and only requires the arcbr0/arc0
360 // configuration. Any other device can be safely ignored.
361 if (IsLegacy(guest_) && !device->IsLegacyAndroid())
362 return;
363
Garrick Evans310ab552019-10-08 11:07:53 +0900364 // This can happen if the device if OnDeviceRemoved is invoked when the
365 // container is down.
Garrick Evansd90a3822019-11-12 17:53:08 +0900366 if (!impl_->IsStarted())
Garrick Evans54861622019-07-19 09:05:09 +0900367 return;
368
Garrick Evans2c263102019-07-26 16:07:18 +0900369 Context* ctx = dynamic_cast<Context*>(device->context(guest_));
370 if (!ctx) {
371 LOG(ERROR) << "Attempt to stop removed device " << device->ifname();
372 return;
373 }
374
375 if (!ctx->IsStarted()) {
376 LOG(ERROR) << "Attempt to re-stop device " << device->ifname();
377 return;
378 }
379
Garrick Evansd90a3822019-11-12 17:53:08 +0900380 impl_->OnStopDevice(device);
Garrick Evanscb791e72019-11-11 15:44:34 +0900381
382 ctx->Stop();
383}
384
Garrick Evans54861622019-07-19 09:05:09 +0900385void ArcService::OnDefaultInterfaceChanged(const std::string& ifname) {
Garrick Evansd90a3822019-11-12 17:53:08 +0900386 impl_->OnDefaultInterfaceChanged(ifname);
Garrick Evans54861622019-07-19 09:05:09 +0900387}
Garrick Evansba575742019-07-17 15:48:08 +0900388
Garrick Evans2c263102019-07-26 16:07:18 +0900389// Context
390
391ArcService::Context::Context() : Device::Context() {
392 Stop();
393}
394
395void ArcService::Context::Start() {
396 Stop();
397 started_ = true;
398}
399
400void ArcService::Context::Stop() {
401 started_ = false;
402 link_up_ = false;
403 routing_table_id_ = kInvalidTableID;
404 routing_table_attempts_ = 0;
405}
406
407bool ArcService::Context::IsStarted() const {
408 return started_;
409}
410
411bool ArcService::Context::IsLinkUp() const {
412 return link_up_;
413}
414
415bool ArcService::Context::SetLinkUp(bool link_up) {
416 if (link_up == link_up_)
417 return false;
418
419 link_up_ = link_up;
420 return true;
421}
422
423bool ArcService::Context::HasIPv6() const {
424 return routing_table_id_ != kInvalidTableID;
425}
426
427bool ArcService::Context::SetHasIPv6(int routing_table_id) {
428 if (routing_table_id <= kRouteControllerRouteTableOffsetFromIndex)
429 return false;
430
431 routing_table_id_ = routing_table_id;
432 return true;
433}
434
Garrick Evansdc3fc452019-09-06 14:15:04 +0900435void ArcService::Context::ClearIPv6() {
436 routing_table_id_ = kInvalidTableID;
437 routing_table_attempts_ = 0;
438}
439
Garrick Evans2c263102019-07-26 16:07:18 +0900440int ArcService::Context::RoutingTableID() const {
441 return routing_table_id_;
442}
443
444int ArcService::Context::RoutingTableAttempts() {
445 return routing_table_attempts_++;
446}
447
Garrick Evansb4eb3892019-11-13 12:07:07 +0900448const std::string& ArcService::Context::TAP() const {
449 return tap_;
450}
451
452void ArcService::Context::SetTAP(const std::string& tap) {
453 tap_ = tap;
454}
455
Garrick Evansd90a3822019-11-12 17:53:08 +0900456// ARC++ specific functions.
457
458ArcService::ContainerImpl::ContainerImpl(DeviceManagerBase* dev_mgr,
459 Datapath* datapath,
460 GuestMessage::GuestType guest)
Garrick Evansa34b5862019-11-20 09:34:01 +0900461 : pid_(kInvalidPID), dev_mgr_(dev_mgr), datapath_(datapath), guest_(guest) {
462 LoadModules(*datapath_);
463}
Garrick Evansd90a3822019-11-12 17:53:08 +0900464
Garrick Evansb4eb3892019-11-13 12:07:07 +0900465GuestMessage::GuestType ArcService::ContainerImpl::guest() const {
466 return guest_;
467}
468
Garrick Evans508a4bc2019-11-14 08:45:52 +0900469bool ArcService::ContainerImpl::Start(int32_t pid) {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900470 // TODO(garrick): Remove this workaround.
471 pid_ = (pid == kTestPID) ? pid : GetContainerPID();
Garrick Evansd90a3822019-11-12 17:53:08 +0900472 if (pid_ == kInvalidPID) {
473 LOG(ERROR) << "Cannot start service - invalid container PID";
474 return false;
475 }
476
477 // Start listening for RTNetlink messages in the container's net namespace
478 // to be notified whenever it brings up an interface.
479 {
480 ScopedNS ns(pid_);
481 if (ns.IsValid()) {
482 rtnl_handler_ = std::make_unique<RTNetlinkHandler>();
483 rtnl_handler_->Start(RTMGRP_LINK);
484 link_listener_ = std::make_unique<shill::RTNLListener>(
485 shill::RTNLHandler::kRequestLink,
486 Bind(&ArcService::ContainerImpl::LinkMsgHandler,
487 weak_factory_.GetWeakPtr()),
488 rtnl_handler_.get());
489 } else {
490 // This is bad - it means we won't ever be able to tell when the container
491 // brings up an interface.
492 LOG(ERROR)
493 << "Cannot start netlink listener - invalid container namespace?";
494 return false;
495 }
496 }
497
498 dev_mgr_->RegisterDeviceIPv6AddressFoundHandler(
Garrick Evansb4eb3892019-11-13 12:07:07 +0900499 guest(), base::Bind(&ArcService::ContainerImpl::SetupIPv6,
500 weak_factory_.GetWeakPtr()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900501
502 LOG(INFO) << "ARC++ network service started {pid: " << pid_ << "}";
503 return true;
504}
505
Garrick Evans508a4bc2019-11-14 08:45:52 +0900506void ArcService::ContainerImpl::Stop() {
Garrick Evansd90a3822019-11-12 17:53:08 +0900507 rtnl_handler_->RemoveListener(link_listener_.get());
508 link_listener_.reset();
509 rtnl_handler_.reset();
510
511 LOG(INFO) << "ARC++ network service stopped {pid: " << pid_ << "}";
512 pid_ = kInvalidPID;
513}
514
515bool ArcService::ContainerImpl::IsStarted() const {
516 return pid_ != kInvalidPID;
517}
518
519bool ArcService::ContainerImpl::OnStartDevice(Device* device) {
520 const auto& config = device->config();
521
522 LOG(INFO) << "Starting device " << device->ifname()
523 << " bridge: " << config.host_ifname()
Garrick Evansb4eb3892019-11-13 12:07:07 +0900524 << " guest_iface: " << config.guest_ifname() << " pid: " << pid_;
Garrick Evansd90a3822019-11-12 17:53:08 +0900525
526 std::string veth_ifname = datapath_->AddVirtualBridgedInterface(
527 device->ifname(), MacAddressToString(config.guest_mac_addr()),
528 config.host_ifname());
529 if (veth_ifname.empty()) {
530 LOG(ERROR) << "Failed to create virtual interface for container";
531 return false;
532 }
533
534 if (!datapath_->AddInterfaceToContainer(
535 pid_, veth_ifname, config.guest_ifname(),
536 IPv4AddressToString(config.guest_ipv4_addr()),
537 device->options().fwd_multicast)) {
538 LOG(ERROR) << "Failed to create container interface.";
539 datapath_->RemoveInterface(veth_ifname);
540 datapath_->RemoveBridge(config.host_ifname());
541 return false;
542 }
543
544 // Signal the container that the network device is ready.
545 // This is only applicable for arc0.
546 if (device->IsAndroid() || device->IsLegacyAndroid()) {
547 datapath_->runner().WriteSentinelToContainer(base::IntToString(pid_));
548 }
549
550 return true;
551}
552
553void ArcService::ContainerImpl::OnStopDevice(Device* device) {
554 const auto& config = device->config();
555
556 LOG(INFO) << "Stopping device " << device->ifname()
557 << " bridge: " << config.host_ifname()
Garrick Evansb4eb3892019-11-13 12:07:07 +0900558 << " guest_iface: " << config.guest_ifname() << " pid: " << pid_;
Garrick Evansd90a3822019-11-12 17:53:08 +0900559
560 device->Disable();
561 if (!device->IsAndroid()) {
562 datapath_->RemoveInterface(ArcVethHostName(device->ifname()));
563 }
564}
565
566void ArcService::ContainerImpl::OnDefaultInterfaceChanged(
567 const std::string& ifname) {
568 if (!IsStarted())
569 return;
570
571 // For ARC N, we must always be able to find the arc0 device and, at a
572 // minimum, disable it.
Garrick Evansb4eb3892019-11-13 12:07:07 +0900573 if (guest() == GuestMessage::ARC_LEGACY) {
Garrick Evansd90a3822019-11-12 17:53:08 +0900574 datapath_->RemoveLegacyIPv4InboundDNAT();
575 auto* device = dev_mgr_->FindByGuestInterface("arc0");
576 if (!device) {
577 LOG(DFATAL) << "Expected legacy Android device missing";
578 return;
579 }
580 device->Disable();
581
582 // If a new default interface was given, then re-enable with that.
583 if (!ifname.empty()) {
584 datapath_->AddLegacyIPv4InboundDNAT(ifname);
585 device->Enable(ifname);
586 }
587 return;
588 }
589
590 // For ARC P and later, we're only concerned with resetting the device when it
591 // becomes the default (again) in order to ensure any previous configuration.
592 // is cleared.
593 if (ifname.empty())
594 return;
595
596 auto* device = dev_mgr_->FindByGuestInterface(ifname);
597 if (!device) {
598 LOG(ERROR) << "Expected default device missing: " << ifname;
599 return;
600 }
601 device->StopIPv6RoutingLegacy();
602 device->StartIPv6RoutingLegacy(ifname);
603}
604
605void ArcService::ContainerImpl::LinkMsgHandler(const shill::RTNLMessage& msg) {
606 if (!msg.HasAttribute(IFLA_IFNAME)) {
607 LOG(ERROR) << "Link event message does not have IFLA_IFNAME";
608 return;
609 }
610 bool link_up = msg.link_status().flags & IFF_UP;
611 shill::ByteString b(msg.GetAttribute(IFLA_IFNAME));
612 std::string ifname(reinterpret_cast<const char*>(
613 b.GetSubstring(0, IFNAMSIZ).GetConstData()));
614
615 auto* device = dev_mgr_->FindByGuestInterface(ifname);
616 if (!device)
617 return;
618
Garrick Evansb4eb3892019-11-13 12:07:07 +0900619 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900620 if (!ctx) {
621 LOG(DFATAL) << "Context missing";
622 return;
623 }
624
625 // If the link status is unchanged, there is nothing to do.
626 if (!ctx->SetLinkUp(link_up))
627 return;
628
629 if (!link_up) {
630 LOG(INFO) << ifname << " is now down";
631 return;
632 }
633 LOG(INFO) << ifname << " is now up";
634
635 if (device->IsAndroid())
636 return;
637
638 if (device->IsLegacyAndroid()) {
639 OnDefaultInterfaceChanged(dev_mgr_->DefaultInterface());
640 return;
641 }
642
643 device->Enable(ifname);
644}
645
646void ArcService::ContainerImpl::SetupIPv6(Device* device) {
647 device->RegisterIPv6TeardownHandler(base::Bind(
648 &ArcService::ContainerImpl::TeardownIPv6, weak_factory_.GetWeakPtr()));
649
650 auto& ipv6_config = device->ipv6_config();
651 if (ipv6_config.ifname.empty())
652 return;
653
Garrick Evansb4eb3892019-11-13 12:07:07 +0900654 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900655 if (!ctx) {
656 LOG(DFATAL) << "Context missing";
657 return;
658 }
659 if (ctx->HasIPv6())
660 return;
661
662 LOG(INFO) << "Setting up IPv6 for " << ipv6_config.ifname;
663
664 int table_id =
665 GetAndroidRoutingTableId(device->config().guest_ifname(), pid_);
666 if (table_id == kInvalidTableID) {
667 if (ctx->RoutingTableAttempts() < kMaxTableRetries) {
668 LOG(INFO) << "Could not look up routing table ID for container interface "
669 << device->config().guest_ifname() << " - trying again...";
670 base::MessageLoop::current()->task_runner()->PostDelayedTask(
671 FROM_HERE,
672 base::Bind(&ArcService::ContainerImpl::SetupIPv6,
673 weak_factory_.GetWeakPtr(), device),
674 kTableRetryDelay);
675 } else {
676 LOG(DFATAL)
677 << "Could not look up routing table ID for container interface "
678 << device->config().guest_ifname();
679 }
680 return;
681 }
682
683 LOG(INFO) << "Setting IPv6 address " << ipv6_config.addr
684 << "/128, gateway=" << ipv6_config.router << " on "
685 << ipv6_config.ifname;
686
687 char buf[INET6_ADDRSTRLEN] = {0};
688 if (!inet_ntop(AF_INET6, &ipv6_config.addr, buf, sizeof(buf))) {
689 LOG(DFATAL) << "Invalid address: " << ipv6_config.addr;
690 return;
691 }
692 std::string addr = buf;
693
694 if (!inet_ntop(AF_INET6, &ipv6_config.router, buf, sizeof(buf))) {
695 LOG(DFATAL) << "Invalid router address: " << ipv6_config.router;
696 return;
697 }
698 std::string router = buf;
699
700 const auto& config = device->config();
701 {
702 ScopedNS ns(pid_);
703 if (!ns.IsValid()) {
704 LOG(ERROR) << "Invalid container namespace (" << pid_
705 << ") - cannot configure IPv6.";
706 return;
707 }
Taoyu Li90c13912019-11-26 17:56:54 +0900708 // Tag the interface so that ARC can detect this manual configuration and
709 // skip disabling and re-enabling IPv6 (b/144545910).
710 if (!datapath_->SetInterfaceFlag(config.guest_ifname(), IFF_DEBUG)) {
711 LOG(ERROR) << "Failed to mark IPv6 manual config flag on interface";
712 }
Garrick Evansd90a3822019-11-12 17:53:08 +0900713 if (!datapath_->AddIPv6GatewayRoutes(config.guest_ifname(), addr, router,
714 ipv6_config.prefix_len, table_id)) {
715 LOG(ERROR) << "Failed to setup IPv6 routes in the container";
716 return;
717 }
718 }
719
720 if (!datapath_->AddIPv6HostRoute(config.host_ifname(), addr,
721 ipv6_config.prefix_len)) {
722 LOG(ERROR) << "Failed to setup the IPv6 route for interface "
723 << config.host_ifname();
724 return;
725 }
726
727 if (!datapath_->AddIPv6Neighbor(ipv6_config.ifname, addr)) {
728 LOG(ERROR) << "Failed to setup the IPv6 neighbor proxy";
729 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
730 ipv6_config.prefix_len);
731 return;
732 }
733
Taoyu Li2980ee92019-11-18 17:49:32 +0900734 if (!datapath_->AddIPv6Forwarding(ipv6_config.ifname,
735 device->config().host_ifname())) {
736 LOG(ERROR) << "Failed to setup iptables for IPv6";
737 datapath_->RemoveIPv6Neighbor(ipv6_config.ifname, addr);
738 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
739 ipv6_config.prefix_len);
740 return;
741 }
742
Garrick Evansd90a3822019-11-12 17:53:08 +0900743 ctx->SetHasIPv6(table_id);
744}
745
746void ArcService::ContainerImpl::TeardownIPv6(Device* device) {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900747 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
Garrick Evansd90a3822019-11-12 17:53:08 +0900748 if (!ctx || !ctx->HasIPv6())
749 return;
750
751 auto& ipv6_config = device->ipv6_config();
752 LOG(INFO) << "Clearing IPv6 for " << ipv6_config.ifname;
753 int table_id = ctx->RoutingTableID();
754 ctx->ClearIPv6();
755
756 char buf[INET6_ADDRSTRLEN] = {0};
757 if (!inet_ntop(AF_INET6, &ipv6_config.addr, buf, sizeof(buf))) {
758 LOG(DFATAL) << "Invalid address: " << ipv6_config.addr;
759 return;
760 }
761 std::string addr = buf;
762
763 if (!inet_ntop(AF_INET6, &ipv6_config.router, buf, sizeof(buf))) {
764 LOG(DFATAL) << "Invalid router address: " << ipv6_config.router;
765 return;
766 }
767 std::string router = buf;
768
769 const auto& config = device->config();
Taoyu Li2980ee92019-11-18 17:49:32 +0900770 datapath_->RemoveIPv6Forwarding(ipv6_config.ifname, config.host_ifname());
Garrick Evansd90a3822019-11-12 17:53:08 +0900771 datapath_->RemoveIPv6Neighbor(ipv6_config.ifname, addr);
772 datapath_->RemoveIPv6HostRoute(config.host_ifname(), addr,
773 ipv6_config.prefix_len);
774
775 ScopedNS ns(pid_);
776 if (ns.IsValid()) {
777 datapath_->RemoveIPv6GatewayRoutes(config.guest_ifname(), addr, router,
778 ipv6_config.prefix_len, table_id);
779 }
780}
781
Garrick Evansb4eb3892019-11-13 12:07:07 +0900782// VM specific functions
783
784ArcService::VmImpl::VmImpl(DeviceManagerBase* dev_mgr, Datapath* datapath)
785 : cid_(kInvalidCID), dev_mgr_(dev_mgr), datapath_(datapath) {}
786
787GuestMessage::GuestType ArcService::VmImpl::guest() const {
788 return GuestMessage::ARC_VM;
789}
790
Garrick Evans508a4bc2019-11-14 08:45:52 +0900791bool ArcService::VmImpl::Start(int32_t cid) {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900792 if (cid <= kInvalidCID) {
793 LOG(ERROR) << "Invalid VM cid " << cid;
794 return false;
795 }
796
797 cid_ = cid;
798 LOG(INFO) << "ARCVM network service started {cid: " << cid_ << "}";
799
800 return true;
801}
802
Garrick Evans508a4bc2019-11-14 08:45:52 +0900803void ArcService::VmImpl::Stop() {
Garrick Evansb4eb3892019-11-13 12:07:07 +0900804 LOG(INFO) << "ARCVM network service stopped {cid: " << cid_ << "}";
805 cid_ = kInvalidCID;
806}
807
808bool ArcService::VmImpl::IsStarted() const {
809 return cid_ > kInvalidCID;
810}
811
812bool ArcService::VmImpl::OnStartDevice(Device* device) {
813 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
814 // configurations; but for now ARCVM needs to be treated like ARC++ N.
815 if (!device->IsLegacyAndroid())
816 return false;
817
818 const auto& config = device->config();
819
820 LOG(INFO) << "Starting device " << device->ifname()
821 << " bridge: " << config.host_ifname()
822 << " guest_iface: " << config.guest_ifname() << " cid: " << cid_;
823
824 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
825 if (!ctx) {
826 LOG(ERROR) << "Context missing";
827 return false;
828 }
829
830 // Since the interface will be added to the bridge, no address configuration
831 // should be provided here.
832 std::string tap =
833 datapath_->AddTAP("" /* auto-generate name */, nullptr /* no mac addr */,
834 nullptr /* no ipv4 subnet */, vm_tools::kCrosVmUser);
835 if (tap.empty()) {
836 LOG(ERROR) << "Failed to create TAP device for VM";
837 return false;
838 }
839
840 if (!datapath_->AddToBridge(config.host_ifname(), tap)) {
841 LOG(ERROR) << "Failed to bridge TAP device " << tap;
842 datapath_->RemoveInterface(tap);
843 return false;
844 }
845
846 ctx->SetTAP(tap);
847 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
848 // configurations; but for now ARCVM needs to be treated like ARC++ N.
849 OnDefaultInterfaceChanged(dev_mgr_->DefaultInterface());
850 return true;
851}
852
853void ArcService::VmImpl::OnStopDevice(Device* device) {
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 if (!device->IsLegacyAndroid())
857 return;
858
859 const auto& config = device->config();
860
861 LOG(INFO) << "Stopping " << device->ifname()
862 << " bridge: " << config.host_ifname()
863 << " guest_iface: " << config.guest_ifname() << " cid: " << cid_;
864
865 Context* ctx = dynamic_cast<Context*>(device->context(guest()));
866 if (!ctx) {
867 LOG(ERROR) << "Context missing";
868 return;
869 }
870
871 device->Disable();
872 datapath_->RemoveInterface(ctx->TAP());
873}
874
875void ArcService::VmImpl::OnDefaultInterfaceChanged(const std::string& ifname) {
876 if (!IsStarted())
877 return;
878
879 // TODO(garrick): Remove this once ARCVM supports ad hoc interface
880 // configurations; but for now ARCVM needs to be treated like ARC++ N.
881 datapath_->RemoveLegacyIPv4InboundDNAT();
882 auto* device = dev_mgr_->FindByGuestInterface("arc0");
883 if (!device) {
884 LOG(DFATAL) << "Expected Android device missing";
885 return;
886 }
887 device->Disable();
888
889 // If a new default interface was given, then re-enable with that.
890 if (!ifname.empty()) {
891 datapath_->AddLegacyIPv4InboundDNAT(ifname);
892 device->Enable(ifname);
893 }
894}
895
Garrick Evans5d55f5e2019-07-17 15:28:10 +0900896} // namespace arc_networkd