blob: 36701ad3711b96062b1407c0ff50384e8fa155da [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
Garrick Evans47c19272019-11-21 10:58:21 +090036} // namespace
37
Garrick Evans69b85872020-02-04 11:40:26 +090038CrostiniService::CrostiniService(ShillClient* shill_client,
Garrick Evans1b1f67c2020-02-04 16:21:25 +090039 AddressManager* addr_mgr,
40 Datapath* datapath,
41 TrafficForwarder* forwarder)
42 : shill_client_(shill_client),
43 addr_mgr_(addr_mgr),
44 datapath_(datapath),
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000045 forwarder_(forwarder),
46 adb_sideloading_enabled_(false) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +090047 DCHECK(shill_client_);
48 DCHECK(addr_mgr_);
Garrick Evansf29f5a32019-12-06 11:34:25 +090049 DCHECK(datapath_);
Garrick Evans1b1f67c2020-02-04 16:21:25 +090050 DCHECK(forwarder_);
Garrick Evansf29f5a32019-12-06 11:34:25 +090051
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000052 // Setup for ADB sideloading.
53 if (!SetupFirewallClient()) {
54 LOG(ERROR) << "Failed to setup firewall client for ADB sideloading";
55 } else {
56 CheckAdbSideloadingStatus();
57 }
58
Garrick Evans69b85872020-02-04 11:40:26 +090059 shill_client_->RegisterDefaultInterfaceChangedHandler(base::Bind(
60 &CrostiniService::OnDefaultInterfaceChanged, weak_factory_.GetWeakPtr()));
Garrick Evansf29f5a32019-12-06 11:34:25 +090061}
Garrick Evans47c19272019-11-21 10:58:21 +090062
Garrick Evansc1ac5c42020-03-31 15:31:22 +090063CrostiniService::~CrostiniService() {
64 if (bus_)
65 bus_->ShutdownAndBlock();
66}
67
Garrick Evans51d5b552020-01-30 10:42:06 +090068bool CrostiniService::Start(uint64_t vm_id, bool is_termina, int subnet_index) {
69 if (vm_id == kInvalidID) {
70 LOG(ERROR) << "Invalid VM id";
Garrick Evansb1c93712020-01-22 09:28:25 +090071 return false;
72 }
Garrick Evans47c19272019-11-21 10:58:21 +090073
Garrick Evans51d5b552020-01-30 10:42:06 +090074 const auto key = MakeKey(vm_id, is_termina);
75 if (taps_.find(key) != taps_.end()) {
76 LOG(WARNING) << "Already started for {id: " << vm_id << "}";
Garrick Evansb1c93712020-01-22 09:28:25 +090077 return false;
Garrick Evans51d5b552020-01-30 10:42:06 +090078 }
Garrick Evansb1c93712020-01-22 09:28:25 +090079
Garrick Evans51d5b552020-01-30 10:42:06 +090080 auto tap = AddTAP(is_termina, subnet_index);
81 if (!tap) {
82 LOG(ERROR) << "Cannot start for {id: " << vm_id << "}";
83 return false;
84 }
Jason Jeremy Imanbb8787e2019-12-13 14:08:20 +090085
Garrick Evans51d5b552020-01-30 10:42:06 +090086 LOG(INFO) << "Crostini network service started for {id: " << vm_id << "}";
Garrick Evans6c7dcb82020-03-16 15:21:05 +090087 StartForwarding(shill_client_->default_interface(), tap->host_ifname());
88
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000089 if (adb_sideloading_enabled_)
Garrick Evans6c7dcb82020-03-16 15:21:05 +090090 StartAdbPortForwarding(tap->phys_ifname());
91
Garrick Evans51d5b552020-01-30 10:42:06 +090092 taps_.emplace(key, std::move(tap));
Garrick Evans47c19272019-11-21 10:58:21 +090093 return true;
94}
95
Garrick Evans51d5b552020-01-30 10:42:06 +090096void CrostiniService::Stop(uint64_t vm_id, bool is_termina) {
97 const auto key = MakeKey(vm_id, is_termina);
98 const auto it = taps_.find(key);
Garrick Evansb1c93712020-01-22 09:28:25 +090099 if (it == taps_.end()) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900100 LOG(WARNING) << "Unknown {id: " << vm_id << "}";
Garrick Evans47c19272019-11-21 10:58:21 +0900101 return;
102 }
103
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900104 const auto& ifname = it->second->host_ifname();
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900105 StopForwarding(shill_client_->default_interface(), ifname);
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000106 if (adb_sideloading_enabled_)
107 StopAdbPortForwarding(ifname);
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900108 datapath_->RemoveInterface(ifname);
Garrick Evans51d5b552020-01-30 10:42:06 +0900109 taps_.erase(key);
Garrick Evans47c19272019-11-21 10:58:21 +0900110
Garrick Evans51d5b552020-01-30 10:42:06 +0900111 LOG(INFO) << "Crostini network service stopped for {id: " << vm_id << "}";
Garrick Evans47c19272019-11-21 10:58:21 +0900112}
113
Garrick Evans51d5b552020-01-30 10:42:06 +0900114const Device* const CrostiniService::TAP(uint64_t vm_id,
115 bool is_termina) const {
116 const auto it = taps_.find(MakeKey(vm_id, is_termina));
Garrick Evansb1c93712020-01-22 09:28:25 +0900117 if (it == taps_.end()) {
118 return nullptr;
119 }
120 return it->second.get();
121}
Garrick Evans47c19272019-11-21 10:58:21 +0900122
Garrick Evans51d5b552020-01-30 10:42:06 +0900123std::unique_ptr<Device> CrostiniService::AddTAP(bool is_termina,
124 int subnet_index) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900125 auto ipv4_subnet = addr_mgr_->AllocateIPv4Subnet(
Garrick Evans51d5b552020-01-30 10:42:06 +0900126 is_termina ? AddressManager::Guest::VM_TERMINA
Garrick Evans1fa4e642020-03-13 11:43:41 +0900127 : AddressManager::Guest::VM_PLUGIN,
Garrick Evans51d5b552020-01-30 10:42:06 +0900128 subnet_index);
Garrick Evansb1c93712020-01-22 09:28:25 +0900129 if (!ipv4_subnet) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900130 LOG(ERROR) << "Subnet already in use or unavailable.";
131 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900132 }
133 auto host_ipv4_addr = ipv4_subnet->AllocateAtOffset(0);
134 if (!host_ipv4_addr) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900135 LOG(ERROR) << "Host address already in use or unavailable.";
136 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900137 }
138 auto guest_ipv4_addr = ipv4_subnet->AllocateAtOffset(1);
139 if (!guest_ipv4_addr) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900140 LOG(ERROR) << "VM address already in use or unavailable.";
141 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900142 }
Garrick Evans51d5b552020-01-30 10:42:06 +0900143 std::unique_ptr<Subnet> lxd_subnet;
144 if (is_termina) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900145 lxd_subnet =
146 addr_mgr_->AllocateIPv4Subnet(AddressManager::Guest::CONTAINER);
Garrick Evans51d5b552020-01-30 10:42:06 +0900147 if (!lxd_subnet) {
148 LOG(ERROR) << "lxd subnet already in use or unavailable.";
149 return nullptr;
150 }
Garrick Evans47c19272019-11-21 10:58:21 +0900151 }
152
Garrick Evans7d9a2322020-04-02 11:59:56 +0900153 const auto mac_addr = addr_mgr_->GenerateMacAddress(subnet_index);
Garrick Evansb1c93712020-01-22 09:28:25 +0900154 const std::string tap =
Garrick Evans47c19272019-11-21 10:58:21 +0900155 datapath_->AddTAP("" /* auto-generate name */, &mac_addr,
Garrick Evansb1c93712020-01-22 09:28:25 +0900156 host_ipv4_addr.get(), vm_tools::kCrosVmUser);
Garrick Evans47c19272019-11-21 10:58:21 +0900157 if (tap.empty()) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900158 LOG(ERROR) << "Failed to create TAP device.";
159 return nullptr;
Garrick Evans47c19272019-11-21 10:58:21 +0900160 }
161
Garrick Evans3d97a392020-02-21 15:24:37 +0900162 if (lxd_subnet) {
163 // Setup lxd route for the container using the VM as a gateway.
164 if (!datapath_->AddIPv4Route(ipv4_subnet->AddressAtOffset(1),
165 lxd_subnet->AddressAtOffset(0),
166 lxd_subnet->Netmask())) {
167 LOG(ERROR) << "Failed to setup lxd route";
168 return nullptr;
169 }
170 }
171
Garrick Evansb1c93712020-01-22 09:28:25 +0900172 auto config = std::make_unique<Device::Config>(
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900173 mac_addr, std::move(ipv4_subnet), std::move(host_ipv4_addr),
Garrick Evansb1c93712020-01-22 09:28:25 +0900174 std::move(guest_ipv4_addr), std::move(lxd_subnet));
Garrick Evans47c19272019-11-21 10:58:21 +0900175
Garrick Evansb1c93712020-01-22 09:28:25 +0900176 Device::Options opts{
177 .fwd_multicast = true,
178 .ipv6_enabled = true,
Garrick Evansb1c93712020-01-22 09:28:25 +0900179 };
Garrick Evans47c19272019-11-21 10:58:21 +0900180
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900181 return std::make_unique<Device>(tap, tap, "", std::move(config), opts);
Garrick Evans47c19272019-11-21 10:58:21 +0900182}
183
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900184void CrostiniService::OnDefaultInterfaceChanged(
185 const std::string& new_ifname, const std::string& prev_ifname) {
186 for (const auto& t : taps_) {
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900187 StopForwarding(prev_ifname, t.second->host_ifname());
188 StartForwarding(new_ifname, t.second->host_ifname());
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900189 }
190}
Garrick Evans47c19272019-11-21 10:58:21 +0900191
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900192void CrostiniService::StartForwarding(const std::string& phys_ifname,
Jason Jeremy Iman0e9f8262020-03-06 14:50:49 +0900193 const std::string& virt_ifname) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900194 if (!phys_ifname.empty())
Jason Jeremy Iman0e9f8262020-03-06 14:50:49 +0900195 forwarder_->StartForwarding(phys_ifname, virt_ifname, true /*ipv6*/,
196 true /*multicast*/);
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900197}
Garrick Evans47c19272019-11-21 10:58:21 +0900198
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900199void CrostiniService::StopForwarding(const std::string& phys_ifname,
200 const std::string& virt_ifname) {
201 if (!phys_ifname.empty())
202 forwarder_->StopForwarding(phys_ifname, virt_ifname, true /*ipv6*/,
203 true /*multicast*/);
Garrick Evans47c19272019-11-21 10:58:21 +0900204}
205
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000206bool CrostiniService::SetupFirewallClient() {
207 dbus::Bus::Options options;
208 options.bus_type = dbus::Bus::SYSTEM;
209
210 bus_ = new dbus::Bus(options);
211 if (!bus_->Connect()) {
212 LOG(ERROR) << "Failed to connect to system bus";
213 return false;
214 }
215
216 permission_broker_proxy_.reset(
217 new org::chromium::PermissionBrokerProxy(bus_));
218
219 return true;
220}
221
222void CrostiniService::StartAdbPortForwarding(const std::string& ifname) {
223 if (!permission_broker_proxy_)
224 return;
225
226 DCHECK(lifeline_fds_.find(ifname) == lifeline_fds_.end());
227 // Setup lifeline pipe.
228 int lifeline_fds[2];
229 if (pipe(lifeline_fds) != 0) {
230 PLOG(ERROR) << "Failed to create lifeline pipe";
231 return;
232 }
233 base::ScopedFD lifeline_read_fd(lifeline_fds[0]);
234 base::ScopedFD lifeline_write_fd(lifeline_fds[1]);
235
236 bool allowed = false;
237 brillo::ErrorPtr error;
238 permission_broker_proxy_->RequestAdbPortForward(ifname, lifeline_fds[0],
239 &allowed, &error);
240 if (error) {
241 LOG(ERROR) << "Error calling D-Bus proxy call to interface "
242 << "'" << permission_broker_proxy_->GetObjectPath().value()
243 << "': " << error->GetMessage();
244 return;
245 }
246 if (!allowed) {
247 LOG(ERROR) << "ADB port forwarding on " << ifname << " not allowed";
248 return;
249 }
250
251 permission_broker_proxy_->RequestTcpPortAccess(
252 kAdbProxyTcpListenPort, ifname, lifeline_fds[0], &allowed, &error);
253 if (error) {
254 LOG(ERROR) << "Error calling D-Bus proxy call to interface "
255 << "'" << permission_broker_proxy_->GetObjectPath().value()
256 << "': " << error->GetMessage();
257 return;
258 }
259 if (!allowed) {
260 LOG(ERROR) << "ADB port access on " << ifname << " not allowed";
261 return;
262 }
263
264 if (datapath_->runner().sysctl_w(
265 "net.ipv4.conf." + ifname + ".route_localnet", "1") != 0) {
266 LOG(ERROR) << "Failed to set up route localnet for " << ifname;
267 return;
268 }
269
270 lifeline_fds_.emplace(ifname, std::move(lifeline_write_fd));
271}
272
273void CrostiniService::StopAdbPortForwarding(const std::string& ifname) {
274 lifeline_fds_.erase(ifname);
275}
276
277void CrostiniService::CheckAdbSideloadingStatus() {
Jason Jeremy Iman10314a82020-05-12 00:09:53 +0900278 static int num_try = 0;
279 if (num_try >= kAdbSideloadMaxTry)
280 return;
281
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000282 dbus::ObjectProxy* proxy = bus_->GetObjectProxy(
283 login_manager::kSessionManagerServiceName,
284 dbus::ObjectPath(login_manager::kSessionManagerServicePath));
285 dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
286 login_manager::kSessionManagerQueryAdbSideload);
287 std::unique_ptr<dbus::Response> dbus_response =
288 proxy->CallMethodAndBlock(&method_call, kDbusTimeoutMs);
289
290 if (!dbus_response) {
291 LOG(WARNING) << "Failed to get ADB sideloading status";
292 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
293 FROM_HERE,
294 base::BindOnce(&CrostiniService::CheckAdbSideloadingStatus,
295 weak_factory_.GetWeakPtr()),
296 kAdbSideloadUpdateDelay);
Jason Jeremy Iman10314a82020-05-12 00:09:53 +0900297 num_try++;
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000298 return;
299 }
300
301 dbus::MessageReader reader(dbus_response.get());
302 reader.PopBool(&adb_sideloading_enabled_);
303 if (!adb_sideloading_enabled_)
304 return;
305
306 // If ADB sideloading is enabled, start ADB forwarding on all configured
307 // Crostini's TAP interfaces.
308 for (const auto& tap : taps_) {
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900309 StartAdbPortForwarding(tap.second->phys_ifname());
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000310 }
311}
312
Garrick Evans3388a032020-03-24 11:25:55 +0900313} // namespace patchpanel