blob: 77d7ea1a3c637a80dff6d3b1891efa77bb950cb2 [file] [log] [blame]
Garrick Evans47c19272019-11-21 10:58:21 +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
Garrick Evans3388a032020-03-24 11:25:55 +09005#include "patchpanel/crostini_service.h"
Garrick Evans47c19272019-11-21 10:58:21 +09006
7#include <memory>
8#include <utility>
9
10#include <base/strings/string_number_conversions.h>
11#include <base/strings/string_util.h>
12#include <base/strings/stringprintf.h>
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000013#include "base/threading/thread_task_runner_handle.h"
Garrick Evans47c19272019-11-21 10:58:21 +090014#include <chromeos/constants/vm_tools.h>
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000015#include <chromeos/dbus/service_constants.h>
16#include <dbus/message.h>
17#include <dbus/object_path.h>
Garrick Evans47c19272019-11-21 10:58:21 +090018
Garrick Evans3388a032020-03-24 11:25:55 +090019#include "patchpanel/adb_proxy.h"
20#include "patchpanel/device.h"
Garrick Evans47c19272019-11-21 10:58:21 +090021
Garrick Evans3388a032020-03-24 11:25:55 +090022namespace patchpanel {
Garrick Evans47c19272019-11-21 10:58:21 +090023namespace {
Garrick Evans51d5b552020-01-30 10:42:06 +090024constexpr int32_t kInvalidID = 0;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000025constexpr int kDbusTimeoutMs = 200;
Jason Jeremy Iman10314a82020-05-12 00:09:53 +090026// The maximum number of ADB sideloading query failures before stopping.
27constexpr int kAdbSideloadMaxTry = 5;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000028constexpr base::TimeDelta kAdbSideloadUpdateDelay =
29 base::TimeDelta::FromMilliseconds(5000);
Jason Jeremy Imanbb8787e2019-12-13 14:08:20 +090030
Garrick Evans51d5b552020-01-30 10:42:06 +090031std::string MakeKey(uint64_t vm_id, bool is_termina) {
32 return base::StringPrintf("%s:%s", is_termina ? "t" : "p",
33 base::NumberToString(vm_id).c_str());
34}
Garrick Evans1b1f67c2020-02-04 16:21:25 +090035
Hugo Benichi78148a02020-10-30 18:37:00 +090036bool IsEthernetOrWifiDevice(const ShillClient::Device& device) {
37 return device.type == ShillClient::Device::Type::kEthernet ||
38 device.type == ShillClient::Device::Type::kWifi;
39}
40
Garrick Evans47c19272019-11-21 10:58:21 +090041} // namespace
42
Garrick Evans69b85872020-02-04 11:40:26 +090043CrostiniService::CrostiniService(ShillClient* shill_client,
Garrick Evans1b1f67c2020-02-04 16:21:25 +090044 AddressManager* addr_mgr,
45 Datapath* datapath,
46 TrafficForwarder* forwarder)
47 : shill_client_(shill_client),
48 addr_mgr_(addr_mgr),
49 datapath_(datapath),
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000050 forwarder_(forwarder),
51 adb_sideloading_enabled_(false) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +090052 DCHECK(shill_client_);
53 DCHECK(addr_mgr_);
Garrick Evansf29f5a32019-12-06 11:34:25 +090054 DCHECK(datapath_);
Garrick Evans1b1f67c2020-02-04 16:21:25 +090055 DCHECK(forwarder_);
Garrick Evansf29f5a32019-12-06 11:34:25 +090056
Jason Jeremy Imana7273a32020-08-04 11:25:31 +090057 dbus::Bus::Options options;
58 options.bus_type = dbus::Bus::SYSTEM;
59
60 bus_ = new dbus::Bus(options);
61 if (!bus_->Connect()) {
62 LOG(ERROR) << "Failed to connect to system bus";
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000063 } else {
64 CheckAdbSideloadingStatus();
65 }
66
Hugo Benichi78148a02020-10-30 18:37:00 +090067 shill_client_->RegisterDefaultDeviceChangedHandler(base::Bind(
68 &CrostiniService::OnDefaultDeviceChanged, weak_factory_.GetWeakPtr()));
Garrick Evansf29f5a32019-12-06 11:34:25 +090069}
Garrick Evans47c19272019-11-21 10:58:21 +090070
Garrick Evansc1ac5c42020-03-31 15:31:22 +090071CrostiniService::~CrostiniService() {
72 if (bus_)
73 bus_->ShutdownAndBlock();
74}
75
Garrick Evans51d5b552020-01-30 10:42:06 +090076bool CrostiniService::Start(uint64_t vm_id, bool is_termina, int subnet_index) {
77 if (vm_id == kInvalidID) {
78 LOG(ERROR) << "Invalid VM id";
Garrick Evansb1c93712020-01-22 09:28:25 +090079 return false;
80 }
Garrick Evans47c19272019-11-21 10:58:21 +090081
Garrick Evans51d5b552020-01-30 10:42:06 +090082 const auto key = MakeKey(vm_id, is_termina);
83 if (taps_.find(key) != taps_.end()) {
84 LOG(WARNING) << "Already started for {id: " << vm_id << "}";
Garrick Evansb1c93712020-01-22 09:28:25 +090085 return false;
Garrick Evans51d5b552020-01-30 10:42:06 +090086 }
Garrick Evansb1c93712020-01-22 09:28:25 +090087
Garrick Evans51d5b552020-01-30 10:42:06 +090088 auto tap = AddTAP(is_termina, subnet_index);
89 if (!tap) {
90 LOG(ERROR) << "Cannot start for {id: " << vm_id << "}";
91 return false;
92 }
Jason Jeremy Imanbb8787e2019-12-13 14:08:20 +090093
Garrick Evans51d5b552020-01-30 10:42:06 +090094 LOG(INFO) << "Crostini network service started for {id: " << vm_id << "}";
Garrick Evans6c7dcb82020-03-16 15:21:05 +090095 StartForwarding(shill_client_->default_interface(), tap->host_ifname());
Hugo Benichic6ae67c2020-08-14 15:02:13 +090096 auto source = is_termina ? TrafficSource::CROSVM : TrafficSource::PLUGINVM;
97 datapath_->StartRoutingDevice("", tap->host_ifname(),
98 tap->config().host_ipv4_addr(), source);
Garrick Evans6c7dcb82020-03-16 15:21:05 +090099
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000100 if (adb_sideloading_enabled_)
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900101 StartAdbPortForwarding(tap->phys_ifname());
102
Garrick Evans51d5b552020-01-30 10:42:06 +0900103 taps_.emplace(key, std::move(tap));
Garrick Evans47c19272019-11-21 10:58:21 +0900104 return true;
105}
106
Garrick Evans51d5b552020-01-30 10:42:06 +0900107void CrostiniService::Stop(uint64_t vm_id, bool is_termina) {
108 const auto key = MakeKey(vm_id, is_termina);
109 const auto it = taps_.find(key);
Garrick Evansb1c93712020-01-22 09:28:25 +0900110 if (it == taps_.end()) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900111 LOG(WARNING) << "Unknown {id: " << vm_id << "}";
Garrick Evans47c19272019-11-21 10:58:21 +0900112 return;
113 }
114
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900115 const auto& ifname = it->second->host_ifname();
Hugo Benichic6ae67c2020-08-14 15:02:13 +0900116 auto source = is_termina ? TrafficSource::CROSVM : TrafficSource::PLUGINVM;
117 datapath_->StopRoutingDevice("", ifname,
118 it->second->config().host_ipv4_addr(), source);
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900119 StopForwarding(shill_client_->default_interface(), ifname);
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000120 if (adb_sideloading_enabled_)
121 StopAdbPortForwarding(ifname);
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900122 datapath_->RemoveInterface(ifname);
Garrick Evans51d5b552020-01-30 10:42:06 +0900123 taps_.erase(key);
Garrick Evans47c19272019-11-21 10:58:21 +0900124
Garrick Evans51d5b552020-01-30 10:42:06 +0900125 LOG(INFO) << "Crostini network service stopped for {id: " << vm_id << "}";
Garrick Evans47c19272019-11-21 10:58:21 +0900126}
127
Garrick Evans51d5b552020-01-30 10:42:06 +0900128const Device* const CrostiniService::TAP(uint64_t vm_id,
129 bool is_termina) const {
130 const auto it = taps_.find(MakeKey(vm_id, is_termina));
Garrick Evansb1c93712020-01-22 09:28:25 +0900131 if (it == taps_.end()) {
132 return nullptr;
133 }
134 return it->second.get();
135}
Garrick Evans47c19272019-11-21 10:58:21 +0900136
Garrick Evans51d5b552020-01-30 10:42:06 +0900137std::unique_ptr<Device> CrostiniService::AddTAP(bool is_termina,
138 int subnet_index) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900139 auto ipv4_subnet = addr_mgr_->AllocateIPv4Subnet(
Garrick Evans51d5b552020-01-30 10:42:06 +0900140 is_termina ? AddressManager::Guest::VM_TERMINA
Garrick Evans1fa4e642020-03-13 11:43:41 +0900141 : AddressManager::Guest::VM_PLUGIN,
Garrick Evans51d5b552020-01-30 10:42:06 +0900142 subnet_index);
Garrick Evansb1c93712020-01-22 09:28:25 +0900143 if (!ipv4_subnet) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900144 LOG(ERROR) << "Subnet already in use or unavailable.";
145 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900146 }
147 auto host_ipv4_addr = ipv4_subnet->AllocateAtOffset(0);
148 if (!host_ipv4_addr) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900149 LOG(ERROR) << "Host address already in use or unavailable.";
150 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900151 }
152 auto guest_ipv4_addr = ipv4_subnet->AllocateAtOffset(1);
153 if (!guest_ipv4_addr) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900154 LOG(ERROR) << "VM address already in use or unavailable.";
155 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900156 }
Garrick Evans51d5b552020-01-30 10:42:06 +0900157 std::unique_ptr<Subnet> lxd_subnet;
158 if (is_termina) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900159 lxd_subnet =
160 addr_mgr_->AllocateIPv4Subnet(AddressManager::Guest::CONTAINER);
Garrick Evans51d5b552020-01-30 10:42:06 +0900161 if (!lxd_subnet) {
162 LOG(ERROR) << "lxd subnet already in use or unavailable.";
163 return nullptr;
164 }
Garrick Evans47c19272019-11-21 10:58:21 +0900165 }
166
Garrick Evans7d9a2322020-04-02 11:59:56 +0900167 const auto mac_addr = addr_mgr_->GenerateMacAddress(subnet_index);
Garrick Evansb1c93712020-01-22 09:28:25 +0900168 const std::string tap =
Garrick Evans47c19272019-11-21 10:58:21 +0900169 datapath_->AddTAP("" /* auto-generate name */, &mac_addr,
Garrick Evansb1c93712020-01-22 09:28:25 +0900170 host_ipv4_addr.get(), vm_tools::kCrosVmUser);
Garrick Evans47c19272019-11-21 10:58:21 +0900171 if (tap.empty()) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900172 LOG(ERROR) << "Failed to create TAP device.";
173 return nullptr;
Garrick Evans47c19272019-11-21 10:58:21 +0900174 }
175
Garrick Evans3d97a392020-02-21 15:24:37 +0900176 if (lxd_subnet) {
177 // Setup lxd route for the container using the VM as a gateway.
178 if (!datapath_->AddIPv4Route(ipv4_subnet->AddressAtOffset(1),
179 lxd_subnet->AddressAtOffset(0),
180 lxd_subnet->Netmask())) {
181 LOG(ERROR) << "Failed to setup lxd route";
182 return nullptr;
183 }
184 }
185
Garrick Evansb1c93712020-01-22 09:28:25 +0900186 auto config = std::make_unique<Device::Config>(
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900187 mac_addr, std::move(ipv4_subnet), std::move(host_ipv4_addr),
Garrick Evansb1c93712020-01-22 09:28:25 +0900188 std::move(guest_ipv4_addr), std::move(lxd_subnet));
Garrick Evans47c19272019-11-21 10:58:21 +0900189
Garrick Evansb1c93712020-01-22 09:28:25 +0900190 Device::Options opts{
191 .fwd_multicast = true,
192 .ipv6_enabled = true,
Garrick Evansb1c93712020-01-22 09:28:25 +0900193 };
Garrick Evans47c19272019-11-21 10:58:21 +0900194
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900195 return std::make_unique<Device>(tap, tap, "", std::move(config), opts);
Garrick Evans47c19272019-11-21 10:58:21 +0900196}
197
Hugo Benichi78148a02020-10-30 18:37:00 +0900198void CrostiniService::OnDefaultDeviceChanged(
199 const ShillClient::Device& new_device,
200 const ShillClient::Device& prev_device) {
201 // Only take into account interface switches and ignore layer 3 property
202 // changes.
203 if (prev_device.ifname == new_device.ifname)
204 return;
205
206 if (IsEthernetOrWifiDevice(prev_device)) {
207 for (const auto& t : taps_)
208 StopForwarding(prev_device.ifname, t.second->host_ifname());
209 }
210
211 if (IsEthernetOrWifiDevice(new_device)) {
212 for (const auto& t : taps_)
213 StartForwarding(new_device.ifname, t.second->host_ifname());
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900214 }
215}
Garrick Evans47c19272019-11-21 10:58:21 +0900216
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900217void CrostiniService::StartForwarding(const std::string& phys_ifname,
Jason Jeremy Iman0e9f8262020-03-06 14:50:49 +0900218 const std::string& virt_ifname) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900219 if (!phys_ifname.empty())
Jason Jeremy Iman0e9f8262020-03-06 14:50:49 +0900220 forwarder_->StartForwarding(phys_ifname, virt_ifname, true /*ipv6*/,
221 true /*multicast*/);
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900222}
Garrick Evans47c19272019-11-21 10:58:21 +0900223
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900224void CrostiniService::StopForwarding(const std::string& phys_ifname,
225 const std::string& virt_ifname) {
226 if (!phys_ifname.empty())
227 forwarder_->StopForwarding(phys_ifname, virt_ifname, true /*ipv6*/,
228 true /*multicast*/);
Garrick Evans47c19272019-11-21 10:58:21 +0900229}
230
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000231void CrostiniService::StartAdbPortForwarding(const std::string& ifname) {
Jason Jeremy Imana7273a32020-08-04 11:25:31 +0900232 if (!datapath_->AddAdbPortForwardRule(ifname)) {
233 LOG(ERROR) << "Error adding ADB port forwarding rule for " << ifname;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000234 return;
235 }
236
Jason Jeremy Imana7273a32020-08-04 11:25:31 +0900237 if (!datapath_->AddAdbPortAccessRule(ifname)) {
238 datapath_->DeleteAdbPortForwardRule(ifname);
239 LOG(ERROR) << "Error adding ADB port access rule for " << ifname;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000240 return;
241 }
242
243 if (datapath_->runner().sysctl_w(
244 "net.ipv4.conf." + ifname + ".route_localnet", "1") != 0) {
245 LOG(ERROR) << "Failed to set up route localnet for " << ifname;
246 return;
247 }
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000248}
249
250void CrostiniService::StopAdbPortForwarding(const std::string& ifname) {
Jason Jeremy Imana7273a32020-08-04 11:25:31 +0900251 datapath_->DeleteAdbPortForwardRule(ifname);
252 datapath_->DeleteAdbPortAccessRule(ifname);
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000253}
254
255void CrostiniService::CheckAdbSideloadingStatus() {
Jason Jeremy Iman10314a82020-05-12 00:09:53 +0900256 static int num_try = 0;
Jason Jeremy Imandd372fd2020-08-11 00:07:58 +0900257 if (num_try >= kAdbSideloadMaxTry) {
258 LOG(WARNING) << "Failed to get ADB sideloading status after " << num_try
259 << " tries. ADB sideloading will not work";
Jason Jeremy Iman10314a82020-05-12 00:09:53 +0900260 return;
Jason Jeremy Imandd372fd2020-08-11 00:07:58 +0900261 }
Jason Jeremy Iman10314a82020-05-12 00:09:53 +0900262
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000263 dbus::ObjectProxy* proxy = bus_->GetObjectProxy(
264 login_manager::kSessionManagerServiceName,
265 dbus::ObjectPath(login_manager::kSessionManagerServicePath));
266 dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
267 login_manager::kSessionManagerQueryAdbSideload);
268 std::unique_ptr<dbus::Response> dbus_response =
269 proxy->CallMethodAndBlock(&method_call, kDbusTimeoutMs);
270
271 if (!dbus_response) {
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000272 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
273 FROM_HERE,
274 base::BindOnce(&CrostiniService::CheckAdbSideloadingStatus,
275 weak_factory_.GetWeakPtr()),
276 kAdbSideloadUpdateDelay);
Jason Jeremy Iman10314a82020-05-12 00:09:53 +0900277 num_try++;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000278 return;
279 }
280
281 dbus::MessageReader reader(dbus_response.get());
282 reader.PopBool(&adb_sideloading_enabled_);
283 if (!adb_sideloading_enabled_)
284 return;
285
286 // If ADB sideloading is enabled, start ADB forwarding on all configured
287 // Crostini's TAP interfaces.
288 for (const auto& tap : taps_) {
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900289 StartAdbPortForwarding(tap.second->phys_ifname());
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000290 }
291}
292
Garrick Evans3388a032020-03-24 11:25:55 +0900293} // namespace patchpanel