blob: 092d41c4d73acf97f8a9175d37c1f229109fa1bd [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;
26constexpr base::TimeDelta kAdbSideloadUpdateDelay =
27 base::TimeDelta::FromMilliseconds(5000);
Jason Jeremy Imanbb8787e2019-12-13 14:08:20 +090028
Garrick Evans51d5b552020-01-30 10:42:06 +090029std::string MakeKey(uint64_t vm_id, bool is_termina) {
30 return base::StringPrintf("%s:%s", is_termina ? "t" : "p",
31 base::NumberToString(vm_id).c_str());
32}
Garrick Evans1b1f67c2020-02-04 16:21:25 +090033
Garrick Evans47c19272019-11-21 10:58:21 +090034} // namespace
35
Garrick Evans69b85872020-02-04 11:40:26 +090036CrostiniService::CrostiniService(ShillClient* shill_client,
Garrick Evans1b1f67c2020-02-04 16:21:25 +090037 AddressManager* addr_mgr,
38 Datapath* datapath,
39 TrafficForwarder* forwarder)
40 : shill_client_(shill_client),
41 addr_mgr_(addr_mgr),
42 datapath_(datapath),
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000043 forwarder_(forwarder),
44 adb_sideloading_enabled_(false) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +090045 DCHECK(shill_client_);
46 DCHECK(addr_mgr_);
Garrick Evansf29f5a32019-12-06 11:34:25 +090047 DCHECK(datapath_);
Garrick Evans1b1f67c2020-02-04 16:21:25 +090048 DCHECK(forwarder_);
Garrick Evansf29f5a32019-12-06 11:34:25 +090049
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000050 // Setup for ADB sideloading.
51 if (!SetupFirewallClient()) {
52 LOG(ERROR) << "Failed to setup firewall client for ADB sideloading";
53 } else {
54 CheckAdbSideloadingStatus();
55 }
56
Garrick Evans69b85872020-02-04 11:40:26 +090057 shill_client_->RegisterDefaultInterfaceChangedHandler(base::Bind(
58 &CrostiniService::OnDefaultInterfaceChanged, weak_factory_.GetWeakPtr()));
Garrick Evansf29f5a32019-12-06 11:34:25 +090059}
Garrick Evans47c19272019-11-21 10:58:21 +090060
Garrick Evansc1ac5c42020-03-31 15:31:22 +090061CrostiniService::~CrostiniService() {
62 if (bus_)
63 bus_->ShutdownAndBlock();
64}
65
Garrick Evans51d5b552020-01-30 10:42:06 +090066bool CrostiniService::Start(uint64_t vm_id, bool is_termina, int subnet_index) {
67 if (vm_id == kInvalidID) {
68 LOG(ERROR) << "Invalid VM id";
Garrick Evansb1c93712020-01-22 09:28:25 +090069 return false;
70 }
Garrick Evans47c19272019-11-21 10:58:21 +090071
Garrick Evans51d5b552020-01-30 10:42:06 +090072 const auto key = MakeKey(vm_id, is_termina);
73 if (taps_.find(key) != taps_.end()) {
74 LOG(WARNING) << "Already started for {id: " << vm_id << "}";
Garrick Evansb1c93712020-01-22 09:28:25 +090075 return false;
Garrick Evans51d5b552020-01-30 10:42:06 +090076 }
Garrick Evansb1c93712020-01-22 09:28:25 +090077
Garrick Evans51d5b552020-01-30 10:42:06 +090078 auto tap = AddTAP(is_termina, subnet_index);
79 if (!tap) {
80 LOG(ERROR) << "Cannot start for {id: " << vm_id << "}";
81 return false;
82 }
Jason Jeremy Imanbb8787e2019-12-13 14:08:20 +090083
Garrick Evans51d5b552020-01-30 10:42:06 +090084 LOG(INFO) << "Crostini network service started for {id: " << vm_id << "}";
Garrick Evans6c7dcb82020-03-16 15:21:05 +090085 StartForwarding(shill_client_->default_interface(), tap->host_ifname());
86
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +000087 if (adb_sideloading_enabled_)
Garrick Evans6c7dcb82020-03-16 15:21:05 +090088 StartAdbPortForwarding(tap->phys_ifname());
89
Garrick Evans51d5b552020-01-30 10:42:06 +090090 taps_.emplace(key, std::move(tap));
Garrick Evans47c19272019-11-21 10:58:21 +090091 return true;
92}
93
Garrick Evans51d5b552020-01-30 10:42:06 +090094void CrostiniService::Stop(uint64_t vm_id, bool is_termina) {
95 const auto key = MakeKey(vm_id, is_termina);
96 const auto it = taps_.find(key);
Garrick Evansb1c93712020-01-22 09:28:25 +090097 if (it == taps_.end()) {
Garrick Evans51d5b552020-01-30 10:42:06 +090098 LOG(WARNING) << "Unknown {id: " << vm_id << "}";
Garrick Evans47c19272019-11-21 10:58:21 +090099 return;
100 }
101
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900102 const auto& ifname = it->second->host_ifname();
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900103 StopForwarding(shill_client_->default_interface(), ifname);
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000104 if (adb_sideloading_enabled_)
105 StopAdbPortForwarding(ifname);
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900106 datapath_->RemoveInterface(ifname);
Garrick Evans51d5b552020-01-30 10:42:06 +0900107 taps_.erase(key);
Garrick Evans47c19272019-11-21 10:58:21 +0900108
Garrick Evans51d5b552020-01-30 10:42:06 +0900109 LOG(INFO) << "Crostini network service stopped for {id: " << vm_id << "}";
Garrick Evans47c19272019-11-21 10:58:21 +0900110}
111
Garrick Evans51d5b552020-01-30 10:42:06 +0900112const Device* const CrostiniService::TAP(uint64_t vm_id,
113 bool is_termina) const {
114 const auto it = taps_.find(MakeKey(vm_id, is_termina));
Garrick Evansb1c93712020-01-22 09:28:25 +0900115 if (it == taps_.end()) {
116 return nullptr;
117 }
118 return it->second.get();
119}
Garrick Evans47c19272019-11-21 10:58:21 +0900120
Garrick Evans51d5b552020-01-30 10:42:06 +0900121std::unique_ptr<Device> CrostiniService::AddTAP(bool is_termina,
122 int subnet_index) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900123 auto ipv4_subnet = addr_mgr_->AllocateIPv4Subnet(
Garrick Evans51d5b552020-01-30 10:42:06 +0900124 is_termina ? AddressManager::Guest::VM_TERMINA
Garrick Evans1fa4e642020-03-13 11:43:41 +0900125 : AddressManager::Guest::VM_PLUGIN,
Garrick Evans51d5b552020-01-30 10:42:06 +0900126 subnet_index);
Garrick Evansb1c93712020-01-22 09:28:25 +0900127 if (!ipv4_subnet) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900128 LOG(ERROR) << "Subnet already in use or unavailable.";
129 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900130 }
131 auto host_ipv4_addr = ipv4_subnet->AllocateAtOffset(0);
132 if (!host_ipv4_addr) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900133 LOG(ERROR) << "Host address already in use or unavailable.";
134 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900135 }
136 auto guest_ipv4_addr = ipv4_subnet->AllocateAtOffset(1);
137 if (!guest_ipv4_addr) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900138 LOG(ERROR) << "VM address already in use or unavailable.";
139 return nullptr;
Garrick Evansb1c93712020-01-22 09:28:25 +0900140 }
Garrick Evans51d5b552020-01-30 10:42:06 +0900141 std::unique_ptr<Subnet> lxd_subnet;
142 if (is_termina) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900143 lxd_subnet =
144 addr_mgr_->AllocateIPv4Subnet(AddressManager::Guest::CONTAINER);
Garrick Evans51d5b552020-01-30 10:42:06 +0900145 if (!lxd_subnet) {
146 LOG(ERROR) << "lxd subnet already in use or unavailable.";
147 return nullptr;
148 }
Garrick Evans47c19272019-11-21 10:58:21 +0900149 }
150
Garrick Evans7d9a2322020-04-02 11:59:56 +0900151 const auto mac_addr = addr_mgr_->GenerateMacAddress(subnet_index);
Garrick Evansb1c93712020-01-22 09:28:25 +0900152 const std::string tap =
Garrick Evans47c19272019-11-21 10:58:21 +0900153 datapath_->AddTAP("" /* auto-generate name */, &mac_addr,
Garrick Evansb1c93712020-01-22 09:28:25 +0900154 host_ipv4_addr.get(), vm_tools::kCrosVmUser);
Garrick Evans47c19272019-11-21 10:58:21 +0900155 if (tap.empty()) {
Garrick Evans51d5b552020-01-30 10:42:06 +0900156 LOG(ERROR) << "Failed to create TAP device.";
157 return nullptr;
Garrick Evans47c19272019-11-21 10:58:21 +0900158 }
159
Garrick Evans3d97a392020-02-21 15:24:37 +0900160 if (lxd_subnet) {
161 // Setup lxd route for the container using the VM as a gateway.
162 if (!datapath_->AddIPv4Route(ipv4_subnet->AddressAtOffset(1),
163 lxd_subnet->AddressAtOffset(0),
164 lxd_subnet->Netmask())) {
165 LOG(ERROR) << "Failed to setup lxd route";
166 return nullptr;
167 }
168 }
169
Garrick Evansb1c93712020-01-22 09:28:25 +0900170 auto config = std::make_unique<Device::Config>(
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900171 mac_addr, std::move(ipv4_subnet), std::move(host_ipv4_addr),
Garrick Evansb1c93712020-01-22 09:28:25 +0900172 std::move(guest_ipv4_addr), std::move(lxd_subnet));
Garrick Evans47c19272019-11-21 10:58:21 +0900173
Garrick Evansb1c93712020-01-22 09:28:25 +0900174 Device::Options opts{
175 .fwd_multicast = true,
176 .ipv6_enabled = true,
Garrick Evansb1c93712020-01-22 09:28:25 +0900177 };
Garrick Evans47c19272019-11-21 10:58:21 +0900178
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900179 return std::make_unique<Device>(tap, tap, "", std::move(config), opts);
Garrick Evans47c19272019-11-21 10:58:21 +0900180}
181
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900182void CrostiniService::OnDefaultInterfaceChanged(
183 const std::string& new_ifname, const std::string& prev_ifname) {
184 for (const auto& t : taps_) {
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900185 StopForwarding(prev_ifname, t.second->host_ifname());
186 StartForwarding(new_ifname, t.second->host_ifname());
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900187 }
188}
Garrick Evans47c19272019-11-21 10:58:21 +0900189
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900190void CrostiniService::StartForwarding(const std::string& phys_ifname,
Jason Jeremy Iman0e9f8262020-03-06 14:50:49 +0900191 const std::string& virt_ifname) {
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900192 if (!phys_ifname.empty())
Jason Jeremy Iman0e9f8262020-03-06 14:50:49 +0900193 forwarder_->StartForwarding(phys_ifname, virt_ifname, true /*ipv6*/,
194 true /*multicast*/);
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900195}
Garrick Evans47c19272019-11-21 10:58:21 +0900196
Garrick Evans1b1f67c2020-02-04 16:21:25 +0900197void CrostiniService::StopForwarding(const std::string& phys_ifname,
198 const std::string& virt_ifname) {
199 if (!phys_ifname.empty())
200 forwarder_->StopForwarding(phys_ifname, virt_ifname, true /*ipv6*/,
201 true /*multicast*/);
Garrick Evans47c19272019-11-21 10:58:21 +0900202}
203
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000204bool CrostiniService::SetupFirewallClient() {
205 dbus::Bus::Options options;
206 options.bus_type = dbus::Bus::SYSTEM;
207
208 bus_ = new dbus::Bus(options);
209 if (!bus_->Connect()) {
210 LOG(ERROR) << "Failed to connect to system bus";
211 return false;
212 }
213
214 permission_broker_proxy_.reset(
215 new org::chromium::PermissionBrokerProxy(bus_));
216
217 return true;
218}
219
220void CrostiniService::StartAdbPortForwarding(const std::string& ifname) {
221 if (!permission_broker_proxy_)
222 return;
223
224 DCHECK(lifeline_fds_.find(ifname) == lifeline_fds_.end());
225 // Setup lifeline pipe.
226 int lifeline_fds[2];
227 if (pipe(lifeline_fds) != 0) {
228 PLOG(ERROR) << "Failed to create lifeline pipe";
229 return;
230 }
231 base::ScopedFD lifeline_read_fd(lifeline_fds[0]);
232 base::ScopedFD lifeline_write_fd(lifeline_fds[1]);
233
234 bool allowed = false;
235 brillo::ErrorPtr error;
236 permission_broker_proxy_->RequestAdbPortForward(ifname, lifeline_fds[0],
237 &allowed, &error);
238 if (error) {
239 LOG(ERROR) << "Error calling D-Bus proxy call to interface "
240 << "'" << permission_broker_proxy_->GetObjectPath().value()
241 << "': " << error->GetMessage();
242 return;
243 }
244 if (!allowed) {
245 LOG(ERROR) << "ADB port forwarding on " << ifname << " not allowed";
246 return;
247 }
248
249 permission_broker_proxy_->RequestTcpPortAccess(
250 kAdbProxyTcpListenPort, ifname, lifeline_fds[0], &allowed, &error);
251 if (error) {
252 LOG(ERROR) << "Error calling D-Bus proxy call to interface "
253 << "'" << permission_broker_proxy_->GetObjectPath().value()
254 << "': " << error->GetMessage();
255 return;
256 }
257 if (!allowed) {
258 LOG(ERROR) << "ADB port access on " << ifname << " not allowed";
259 return;
260 }
261
262 if (datapath_->runner().sysctl_w(
263 "net.ipv4.conf." + ifname + ".route_localnet", "1") != 0) {
264 LOG(ERROR) << "Failed to set up route localnet for " << ifname;
265 return;
266 }
267
268 lifeline_fds_.emplace(ifname, std::move(lifeline_write_fd));
269}
270
271void CrostiniService::StopAdbPortForwarding(const std::string& ifname) {
272 lifeline_fds_.erase(ifname);
273}
274
275void CrostiniService::CheckAdbSideloadingStatus() {
276 dbus::ObjectProxy* proxy = bus_->GetObjectProxy(
277 login_manager::kSessionManagerServiceName,
278 dbus::ObjectPath(login_manager::kSessionManagerServicePath));
279 dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
280 login_manager::kSessionManagerQueryAdbSideload);
281 std::unique_ptr<dbus::Response> dbus_response =
282 proxy->CallMethodAndBlock(&method_call, kDbusTimeoutMs);
283
284 if (!dbus_response) {
285 LOG(WARNING) << "Failed to get ADB sideloading status";
286 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
287 FROM_HERE,
288 base::BindOnce(&CrostiniService::CheckAdbSideloadingStatus,
289 weak_factory_.GetWeakPtr()),
290 kAdbSideloadUpdateDelay);
291 return;
292 }
293
294 dbus::MessageReader reader(dbus_response.get());
295 reader.PopBool(&adb_sideloading_enabled_);
296 if (!adb_sideloading_enabled_)
297 return;
298
299 // If ADB sideloading is enabled, start ADB forwarding on all configured
300 // Crostini's TAP interfaces.
301 for (const auto& tap : taps_) {
Garrick Evans6c7dcb82020-03-16 15:21:05 +0900302 StartAdbPortForwarding(tap.second->phys_ifname());
Jason Jeremy Imanfa8b6d22020-02-20 03:44:21 +0000303 }
304}
305
Garrick Evans3388a032020-03-24 11:25:55 +0900306} // namespace patchpanel