blob: 488274cad85238046187deb807951893aa644d11 [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
Qijiang Fan713061e2021-03-08 15:45:12 +090010#include <base/check.h>
Garrick Evans47c19272019-11-21 10:58:21 +090011#include <base/strings/string_number_conversions.h>
Garrick Evans02e6e872020-11-30 11:53:13 +090012#include <base/strings/string_split.h>
Garrick Evans47c19272019-11-21 10:58:21 +090013#include <base/strings/string_util.h>
14#include <base/strings/stringprintf.h>
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000015#include "base/threading/thread_task_runner_handle.h"
Garrick Evans47c19272019-11-21 10:58:21 +090016#include <chromeos/constants/vm_tools.h>
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000017#include <chromeos/dbus/service_constants.h>
18#include <dbus/message.h>
19#include <dbus/object_path.h>
Hugo Benichi69c989d2021-03-01 00:23:39 +090020#include <dbus/object_proxy.h>
Garrick Evans47c19272019-11-21 10:58:21 +090021
Garrick Evans3388a032020-03-24 11:25:55 +090022#include "patchpanel/adb_proxy.h"
Garrick Evans47c19272019-11-21 10:58:21 +090023
Garrick Evans3388a032020-03-24 11:25:55 +090024namespace patchpanel {
Garrick Evans47c19272019-11-21 10:58:21 +090025namespace {
Garrick Evans51d5b552020-01-30 10:42:06 +090026constexpr int32_t kInvalidID = 0;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000027constexpr int kDbusTimeoutMs = 200;
Jason Jeremy Iman10314a82020-05-12 00:09:53 +090028// The maximum number of ADB sideloading query failures before stopping.
29constexpr int kAdbSideloadMaxTry = 5;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000030constexpr base::TimeDelta kAdbSideloadUpdateDelay =
31 base::TimeDelta::FromMilliseconds(5000);
Jason Jeremy Imanbb8787e2019-12-13 14:08:20 +090032
Garrick Evans51d5b552020-01-30 10:42:06 +090033std::string MakeKey(uint64_t vm_id, bool is_termina) {
34 return base::StringPrintf("%s:%s", is_termina ? "t" : "p",
35 base::NumberToString(vm_id).c_str());
36}
Garrick Evans1b1f67c2020-02-04 16:21:25 +090037
Garrick Evans02e6e872020-11-30 11:53:13 +090038bool ParseKey(const std::string& key, uint64_t* vm_id, bool* is_termina) {
39 auto s =
40 base::SplitString(key, ":", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
Jason Jeremy Imand6faa012021-04-05 09:15:38 +090041 if (s.size() != 2 || !base::StringToUint64(s[1], vm_id) ||
42 (s[0] != "t" && s[0] != "p")) {
Garrick Evans02e6e872020-11-30 11:53:13 +090043 LOG(DFATAL) << "Invalid key: " + key;
44 return false;
45 }
46 *is_termina = s[0] == "t";
47 return true;
48}
49
Garrick Evans47c19272019-11-21 10:58:21 +090050} // namespace
51
Garrick Evans209a80a2020-11-30 14:42:40 +090052CrostiniService::CrostiniService(
Garrick Evans209a80a2020-11-30 14:42:40 +090053 AddressManager* addr_mgr,
54 Datapath* datapath,
Garrick Evans209a80a2020-11-30 14:42:40 +090055 Device::ChangeEventHandler device_changed_handler)
Hugo Benichi69c989d2021-03-01 00:23:39 +090056 : addr_mgr_(addr_mgr),
Garrick Evans1b1f67c2020-02-04 16:21:25 +090057 datapath_(datapath),
Garrick Evans209a80a2020-11-30 14:42:40 +090058 device_changed_handler_(device_changed_handler),
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000059 adb_sideloading_enabled_(false) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +090060 DCHECK(addr_mgr_);
Garrick Evansf29f5a32019-12-06 11:34:25 +090061 DCHECK(datapath_);
62
Jason Jeremy Imana7273a32020-08-04 11:25:31 +090063 dbus::Bus::Options options;
64 options.bus_type = dbus::Bus::SYSTEM;
65
66 bus_ = new dbus::Bus(options);
67 if (!bus_->Connect()) {
68 LOG(ERROR) << "Failed to connect to system bus";
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000069 } else {
70 CheckAdbSideloadingStatus();
71 }
Garrick Evansf29f5a32019-12-06 11:34:25 +090072}
Garrick Evans47c19272019-11-21 10:58:21 +090073
Garrick Evansc1ac5c42020-03-31 15:31:22 +090074CrostiniService::~CrostiniService() {
75 if (bus_)
76 bus_->ShutdownAndBlock();
77}
78
Garrick Evans51d5b552020-01-30 10:42:06 +090079bool CrostiniService::Start(uint64_t vm_id, bool is_termina, int subnet_index) {
80 if (vm_id == kInvalidID) {
81 LOG(ERROR) << "Invalid VM id";
Garrick Evansb1c93712020-01-22 09:28:25 +090082 return false;
83 }
Garrick Evans47c19272019-11-21 10:58:21 +090084
Garrick Evans51d5b552020-01-30 10:42:06 +090085 const auto key = MakeKey(vm_id, is_termina);
86 if (taps_.find(key) != taps_.end()) {
87 LOG(WARNING) << "Already started for {id: " << vm_id << "}";
Garrick Evansb1c93712020-01-22 09:28:25 +090088 return false;
Garrick Evans51d5b552020-01-30 10:42:06 +090089 }
Garrick Evansb1c93712020-01-22 09:28:25 +090090
Garrick Evans51d5b552020-01-30 10:42:06 +090091 auto tap = AddTAP(is_termina, subnet_index);
92 if (!tap) {
93 LOG(ERROR) << "Cannot start for {id: " << vm_id << "}";
94 return false;
95 }
Jason Jeremy Imanbb8787e2019-12-13 14:08:20 +090096
Garrick Evans51d5b552020-01-30 10:42:06 +090097 LOG(INFO) << "Crostini network service started for {id: " << vm_id << "}";
Hugo Benichic6ae67c2020-08-14 15:02:13 +090098 auto source = is_termina ? TrafficSource::CROSVM : TrafficSource::PLUGINVM;
99 datapath_->StartRoutingDevice("", tap->host_ifname(),
Hugo Benichi93306e52020-12-04 16:08:00 +0900100 tap->config().host_ipv4_addr(), source,
101 true /*route_on_vpn*/);
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900102
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000103 if (adb_sideloading_enabled_)
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900104 StartAdbPortForwarding(tap->phys_ifname());
105
Garrick Evans209a80a2020-11-30 14:42:40 +0900106 device_changed_handler_.Run(
107 *tap, Device::ChangeEvent::ADDED,
108 is_termina ? GuestMessage::TERMINA_VM : GuestMessage::PLUGIN_VM);
109
Garrick Evans51d5b552020-01-30 10:42:06 +0900110 taps_.emplace(key, std::move(tap));
Garrick Evans47c19272019-11-21 10:58:21 +0900111 return true;
112}
113
Garrick Evans51d5b552020-01-30 10:42:06 +0900114void CrostiniService::Stop(uint64_t vm_id, bool is_termina) {
115 const auto key = MakeKey(vm_id, is_termina);
116 const auto it = taps_.find(key);
Garrick Evansb1c93712020-01-22 09:28:25 +0900117 if (it == taps_.end()) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900118 LOG(WARNING) << "Unknown {id: " << vm_id << "}";
Garrick Evans47c19272019-11-21 10:58:21 +0900119 return;
120 }
121
Garrick Evans209a80a2020-11-30 14:42:40 +0900122 device_changed_handler_.Run(
123 *it->second, Device::ChangeEvent::REMOVED,
124 is_termina ? GuestMessage::TERMINA_VM : GuestMessage::PLUGIN_VM);
125
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900126 const auto& ifname = it->second->host_ifname();
Hugo Benichic6ae67c2020-08-14 15:02:13 +0900127 auto source = is_termina ? TrafficSource::CROSVM : TrafficSource::PLUGINVM;
128 datapath_->StopRoutingDevice("", ifname,
Hugo Benichi93306e52020-12-04 16:08:00 +0900129 it->second->config().host_ipv4_addr(), source,
130 true /*route_on_vpn*/);
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000131 if (adb_sideloading_enabled_)
132 StopAdbPortForwarding(ifname);
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900133 datapath_->RemoveInterface(ifname);
Garrick Evans51d5b552020-01-30 10:42:06 +0900134 taps_.erase(key);
Garrick Evans47c19272019-11-21 10:58:21 +0900135
Garrick Evans51d5b552020-01-30 10:42:06 +0900136 LOG(INFO) << "Crostini network service stopped for {id: " << vm_id << "}";
Garrick Evans47c19272019-11-21 10:58:21 +0900137}
138
Garrick Evans51d5b552020-01-30 10:42:06 +0900139const Device* const CrostiniService::TAP(uint64_t vm_id,
140 bool is_termina) const {
141 const auto it = taps_.find(MakeKey(vm_id, is_termina));
Garrick Evansb1c93712020-01-22 09:28:25 +0900142 if (it == taps_.end()) {
143 return nullptr;
144 }
145 return it->second.get();
146}
Garrick Evans47c19272019-11-21 10:58:21 +0900147
Garrick Evans02e6e872020-11-30 11:53:13 +0900148void CrostiniService::ScanDevices(
149 base::RepeatingCallback<void(uint64_t, bool, const Device&)> callback)
150 const {
151 for (const auto& [key, dev] : taps_) {
152 uint64_t vm_id;
153 bool is_termina;
154 if (ParseKey(key, &vm_id, &is_termina))
155 callback.Run(vm_id, is_termina, *dev.get());
156 }
157}
158
Hugo Benichi69c989d2021-03-01 00:23:39 +0900159std::vector<const Device*> CrostiniService::GetDevices() const {
160 std::vector<const Device*> devices;
161 for (const auto& [_, dev] : taps_) {
162 devices.push_back(dev.get());
163 }
164 return devices;
165}
166
Garrick Evans51d5b552020-01-30 10:42:06 +0900167std::unique_ptr<Device> CrostiniService::AddTAP(bool is_termina,
168 int subnet_index) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900169 auto ipv4_subnet = addr_mgr_->AllocateIPv4Subnet(
Garrick Evans51d5b552020-01-30 10:42:06 +0900170 is_termina ? AddressManager::Guest::VM_TERMINA
Garrick Evans1fa4e642020-03-13 11:43:41 +0900171 : AddressManager::Guest::VM_PLUGIN,
Garrick Evans51d5b552020-01-30 10:42:06 +0900172 subnet_index);
Garrick Evansb1c93712020-01-22 09:28:25 +0900173 if (!ipv4_subnet) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900174 LOG(ERROR) << "Subnet already in use or unavailable.";
175 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900176 }
177 auto host_ipv4_addr = ipv4_subnet->AllocateAtOffset(0);
178 if (!host_ipv4_addr) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900179 LOG(ERROR) << "Host address already in use or unavailable.";
180 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900181 }
182 auto guest_ipv4_addr = ipv4_subnet->AllocateAtOffset(1);
183 if (!guest_ipv4_addr) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900184 LOG(ERROR) << "VM address already in use or unavailable.";
185 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900186 }
Garrick Evans51d5b552020-01-30 10:42:06 +0900187 std::unique_ptr<Subnet> lxd_subnet;
188 if (is_termina) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900189 lxd_subnet =
190 addr_mgr_->AllocateIPv4Subnet(AddressManager::Guest::CONTAINER);
Garrick Evans51d5b552020-01-30 10:42:06 +0900191 if (!lxd_subnet) {
192 LOG(ERROR) << "lxd subnet already in use or unavailable.";
193 return nullptr;
194 }
Garrick Evans47c19272019-11-21 10:58:21 +0900195 }
196
Garrick Evans7d9a2322020-04-02 11:59:56 +0900197 const auto mac_addr = addr_mgr_->GenerateMacAddress(subnet_index);
Garrick Evansb1c93712020-01-22 09:28:25 +0900198 const std::string tap =
Garrick Evans47c19272019-11-21 10:58:21 +0900199 datapath_->AddTAP("" /* auto-generate name */, &mac_addr,
Garrick Evansb1c93712020-01-22 09:28:25 +0900200 host_ipv4_addr.get(), vm_tools::kCrosVmUser);
Garrick Evans47c19272019-11-21 10:58:21 +0900201 if (tap.empty()) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900202 LOG(ERROR) << "Failed to create TAP device.";
203 return nullptr;
Garrick Evans47c19272019-11-21 10:58:21 +0900204 }
205
Garrick Evans3d97a392020-02-21 15:24:37 +0900206 if (lxd_subnet) {
207 // Setup lxd route for the container using the VM as a gateway.
208 if (!datapath_->AddIPv4Route(ipv4_subnet->AddressAtOffset(1),
209 lxd_subnet->AddressAtOffset(0),
210 lxd_subnet->Netmask())) {
211 LOG(ERROR) << "Failed to setup lxd route";
212 return nullptr;
213 }
214 }
215
Garrick Evansb1c93712020-01-22 09:28:25 +0900216 auto config = std::make_unique<Device::Config>(
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900217 mac_addr, std::move(ipv4_subnet), std::move(host_ipv4_addr),
Garrick Evansb1c93712020-01-22 09:28:25 +0900218 std::move(guest_ipv4_addr), std::move(lxd_subnet));
Garrick Evans47c19272019-11-21 10:58:21 +0900219
Hugo Benichi6f118a32021-03-01 12:28:14 +0900220 return std::make_unique<Device>(tap, tap, "", std::move(config));
Garrick Evans47c19272019-11-21 10:58:21 +0900221}
222
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000223void CrostiniService::StartAdbPortForwarding(const std::string& ifname) {
Jason Jeremy Imana7273a32020-08-04 11:25:31 +0900224 if (!datapath_->AddAdbPortForwardRule(ifname)) {
225 LOG(ERROR) << "Error adding ADB port forwarding rule for " << ifname;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000226 return;
227 }
228
Jason Jeremy Imana7273a32020-08-04 11:25:31 +0900229 if (!datapath_->AddAdbPortAccessRule(ifname)) {
230 datapath_->DeleteAdbPortForwardRule(ifname);
231 LOG(ERROR) << "Error adding ADB port access rule for " << ifname;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000232 return;
233 }
234
235 if (datapath_->runner().sysctl_w(
236 "net.ipv4.conf." + ifname + ".route_localnet", "1") != 0) {
237 LOG(ERROR) << "Failed to set up route localnet for " << ifname;
238 return;
239 }
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000240}
241
242void CrostiniService::StopAdbPortForwarding(const std::string& ifname) {
Jason Jeremy Imana7273a32020-08-04 11:25:31 +0900243 datapath_->DeleteAdbPortForwardRule(ifname);
244 datapath_->DeleteAdbPortAccessRule(ifname);
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000245}
246
247void CrostiniService::CheckAdbSideloadingStatus() {
Jason Jeremy Iman10314a82020-05-12 00:09:53 +0900248 static int num_try = 0;
Jason Jeremy Imandd372fd2020-08-11 00:07:58 +0900249 if (num_try >= kAdbSideloadMaxTry) {
250 LOG(WARNING) << "Failed to get ADB sideloading status after " << num_try
251 << " tries. ADB sideloading will not work";
Jason Jeremy Iman10314a82020-05-12 00:09:53 +0900252 return;
Jason Jeremy Imandd372fd2020-08-11 00:07:58 +0900253 }
Jason Jeremy Iman10314a82020-05-12 00:09:53 +0900254
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000255 dbus::ObjectProxy* proxy = bus_->GetObjectProxy(
256 login_manager::kSessionManagerServiceName,
257 dbus::ObjectPath(login_manager::kSessionManagerServicePath));
258 dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
259 login_manager::kSessionManagerQueryAdbSideload);
260 std::unique_ptr<dbus::Response> dbus_response =
261 proxy->CallMethodAndBlock(&method_call, kDbusTimeoutMs);
262
263 if (!dbus_response) {
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000264 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
265 FROM_HERE,
266 base::BindOnce(&CrostiniService::CheckAdbSideloadingStatus,
267 weak_factory_.GetWeakPtr()),
268 kAdbSideloadUpdateDelay);
Jason Jeremy Iman10314a82020-05-12 00:09:53 +0900269 num_try++;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000270 return;
271 }
272
273 dbus::MessageReader reader(dbus_response.get());
274 reader.PopBool(&adb_sideloading_enabled_);
275 if (!adb_sideloading_enabled_)
276 return;
277
278 // If ADB sideloading is enabled, start ADB forwarding on all configured
279 // Crostini's TAP interfaces.
280 for (const auto& tap : taps_) {
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900281 StartAdbPortForwarding(tap.second->phys_ifname());
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000282 }
283}
284
Garrick Evans3388a032020-03-24 11:25:55 +0900285} // namespace patchpanel