blob: d6de7d519ac408f0ffdd97070914008e7b994f26 [file] [log] [blame]
Garrick Evans08843932019-09-17 14:41:08 +09001// Copyright 2016 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/client.h"
Garrick Evans08843932019-09-17 14:41:08 +09006
Hugo Benichicc6850f2020-01-17 13:26:06 +09007#include <fcntl.h>
8
Garrick Evans08843932019-09-17 14:41:08 +09009#include <base/logging.h>
10#include <chromeos/dbus/service_constants.h>
11#include <dbus/message.h>
12#include <dbus/object_path.h>
13
Garrick Evans3388a032020-03-24 11:25:55 +090014#include "patchpanel/net_util.h"
Hugo Benichicc6850f2020-01-17 13:26:06 +090015
Garrick Evans08843932019-09-17 14:41:08 +090016namespace patchpanel {
17
18// static
19std::unique_ptr<Client> Client::New() {
20 dbus::Bus::Options opts;
21 opts.bus_type = dbus::Bus::SYSTEM;
22 scoped_refptr<dbus::Bus> bus(new dbus::Bus(std::move(opts)));
23
24 if (!bus->Connect()) {
25 LOG(ERROR) << "Failed to connect to system bus";
26 return nullptr;
27 }
28
29 dbus::ObjectProxy* proxy = bus->GetObjectProxy(
30 kPatchPanelServiceName, dbus::ObjectPath(kPatchPanelServicePath));
31 if (!proxy) {
32 LOG(ERROR) << "Unable to get dbus proxy for " << kPatchPanelServiceName;
33 return nullptr;
34 }
35
Garrick Evans93a83fc2020-03-31 15:16:55 +090036 return std::make_unique<Client>(std::move(bus), proxy);
37}
38
39Client::~Client() {
40 if (bus_)
41 bus_->ShutdownAndBlock();
Garrick Evans08843932019-09-17 14:41:08 +090042}
43
44bool Client::NotifyArcStartup(pid_t pid) {
45 dbus::MethodCall method_call(kPatchPanelInterface, kArcStartupMethod);
46 dbus::MessageWriter writer(&method_call);
47
48 ArcStartupRequest request;
49 request.set_pid(static_cast<uint32_t>(pid));
50
51 if (!writer.AppendProtoAsArrayOfBytes(request)) {
52 LOG(ERROR) << "Failed to encode ArcStartupRequest proto";
53 return false;
54 }
55
56 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
57 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
58 if (!dbus_response) {
59 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
60 return false;
61 }
62
63 dbus::MessageReader reader(dbus_response.get());
64 ArcStartupResponse response;
65 if (!reader.PopArrayOfBytesAsProto(&response)) {
66 LOG(ERROR) << "Failed to parse response proto";
67 return false;
68 }
69
70 return true;
71}
72
Garrick Evansca2b41b2019-12-02 09:06:11 +090073bool Client::NotifyArcShutdown() {
Garrick Evans08843932019-09-17 14:41:08 +090074 dbus::MethodCall method_call(kPatchPanelInterface, kArcShutdownMethod);
75 dbus::MessageWriter writer(&method_call);
76
77 ArcShutdownRequest request;
Garrick Evans08843932019-09-17 14:41:08 +090078 if (!writer.AppendProtoAsArrayOfBytes(request)) {
79 LOG(ERROR) << "Failed to encode ArcShutdownRequest proto";
80 return false;
81 }
82
83 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
84 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
85 if (!dbus_response) {
86 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
87 return false;
88 }
89
90 dbus::MessageReader reader(dbus_response.get());
91 ArcShutdownResponse response;
92 if (!reader.PopArrayOfBytesAsProto(&response)) {
93 LOG(ERROR) << "Failed to parse response proto";
94 return false;
95 }
96
97 return true;
98}
99
Garrick Evans3388a032020-03-24 11:25:55 +0900100std::vector<NetworkDevice> Client::NotifyArcVmStartup(uint32_t cid) {
Garrick Evans08843932019-09-17 14:41:08 +0900101 dbus::MethodCall method_call(kPatchPanelInterface, kArcVmStartupMethod);
102 dbus::MessageWriter writer(&method_call);
103
104 ArcVmStartupRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900105 request.set_cid(cid);
Garrick Evans08843932019-09-17 14:41:08 +0900106
107 if (!writer.AppendProtoAsArrayOfBytes(request)) {
108 LOG(ERROR) << "Failed to encode ArcVmStartupRequest proto";
109 return {};
110 }
111
112 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
113 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
114 if (!dbus_response) {
115 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
116 return {};
117 }
118
119 dbus::MessageReader reader(dbus_response.get());
120 ArcVmStartupResponse response;
121 if (!reader.PopArrayOfBytesAsProto(&response)) {
122 LOG(ERROR) << "Failed to parse response proto";
123 return {};
124 }
125
Garrick Evans3388a032020-03-24 11:25:55 +0900126 std::vector<NetworkDevice> devices;
Garrick Evans08843932019-09-17 14:41:08 +0900127 for (const auto& d : response.devices()) {
128 devices.emplace_back(d);
129 }
130 return devices;
131}
132
Garrick Evans0a189372020-02-07 08:55:27 +0900133bool Client::NotifyArcVmShutdown(uint32_t cid) {
Garrick Evans08843932019-09-17 14:41:08 +0900134 dbus::MethodCall method_call(kPatchPanelInterface, kArcVmShutdownMethod);
135 dbus::MessageWriter writer(&method_call);
136
137 ArcVmShutdownRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900138 request.set_cid(cid);
Garrick Evans08843932019-09-17 14:41:08 +0900139
140 if (!writer.AppendProtoAsArrayOfBytes(request)) {
141 LOG(ERROR) << "Failed to encode ArcVmShutdownRequest proto";
142 return false;
143 }
144
145 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
146 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
147 if (!dbus_response) {
148 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
149 return false;
150 }
151
152 dbus::MessageReader reader(dbus_response.get());
153 ArcVmShutdownResponse response;
154 if (!reader.PopArrayOfBytesAsProto(&response)) {
155 LOG(ERROR) << "Failed to parse response proto";
156 return false;
157 }
158
159 return true;
160}
161
Garrick Evans0a189372020-02-07 08:55:27 +0900162bool Client::NotifyTerminaVmStartup(uint32_t cid,
Garrick Evans3388a032020-03-24 11:25:55 +0900163 NetworkDevice* device,
164 IPv4Subnet* container_subnet) {
Garrick Evans27b74032019-11-19 13:33:47 +0900165 dbus::MethodCall method_call(kPatchPanelInterface, kTerminaVmStartupMethod);
166 dbus::MessageWriter writer(&method_call);
167
168 TerminaVmStartupRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900169 request.set_cid(cid);
Garrick Evans27b74032019-11-19 13:33:47 +0900170
171 if (!writer.AppendProtoAsArrayOfBytes(request)) {
172 LOG(ERROR) << "Failed to encode TerminaVmStartupRequest proto";
173 return false;
174 }
175
176 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
177 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
178 if (!dbus_response) {
179 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
180 return false;
181 }
182
183 dbus::MessageReader reader(dbus_response.get());
184 TerminaVmStartupResponse response;
185 if (!reader.PopArrayOfBytesAsProto(&response)) {
186 LOG(ERROR) << "Failed to parse response proto";
187 return false;
188 }
189
190 if (!response.has_device()) {
191 LOG(ERROR) << "No device found";
192 return false;
193 }
194 *device = response.device();
195
196 if (response.has_container_subnet()) {
197 *container_subnet = response.container_subnet();
198 } else {
199 LOG(WARNING) << "No container subnet found";
200 }
201
202 return true;
203}
204
Garrick Evans0a189372020-02-07 08:55:27 +0900205bool Client::NotifyTerminaVmShutdown(uint32_t cid) {
Garrick Evans27b74032019-11-19 13:33:47 +0900206 dbus::MethodCall method_call(kPatchPanelInterface, kTerminaVmShutdownMethod);
207 dbus::MessageWriter writer(&method_call);
208
209 TerminaVmShutdownRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900210 request.set_cid(cid);
Garrick Evans27b74032019-11-19 13:33:47 +0900211
212 if (!writer.AppendProtoAsArrayOfBytes(request)) {
213 LOG(ERROR) << "Failed to encode TerminaVmShutdownRequest proto";
214 return false;
215 }
216
217 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
218 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
219 if (!dbus_response) {
220 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
221 return false;
222 }
223
224 dbus::MessageReader reader(dbus_response.get());
225 TerminaVmShutdownResponse response;
226 if (!reader.PopArrayOfBytesAsProto(&response)) {
227 LOG(ERROR) << "Failed to parse response proto";
228 return false;
229 }
230
231 return true;
232}
233
Garrick Evans376f0672020-01-07 15:31:50 +0900234bool Client::NotifyPluginVmStartup(uint64_t vm_id,
235 int subnet_index,
Garrick Evans3388a032020-03-24 11:25:55 +0900236 NetworkDevice* device) {
Garrick Evans376f0672020-01-07 15:31:50 +0900237 dbus::MethodCall method_call(kPatchPanelInterface, kPluginVmStartupMethod);
238 dbus::MessageWriter writer(&method_call);
239
240 PluginVmStartupRequest request;
241 request.set_id(vm_id);
242 request.set_subnet_index(subnet_index);
243
244 if (!writer.AppendProtoAsArrayOfBytes(request)) {
245 LOG(ERROR) << "Failed to encode PluginVmStartupRequest proto";
246 return false;
247 }
248
249 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
250 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
251 if (!dbus_response) {
252 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
253 return false;
254 }
255
256 dbus::MessageReader reader(dbus_response.get());
257 PluginVmStartupResponse response;
258 if (!reader.PopArrayOfBytesAsProto(&response)) {
259 LOG(ERROR) << "Failed to parse response proto";
260 return false;
261 }
262
263 if (!response.has_device()) {
264 LOG(ERROR) << "No device found";
265 return false;
266 }
267 *device = response.device();
268
269 return true;
270}
271
272bool Client::NotifyPluginVmShutdown(uint64_t vm_id) {
273 dbus::MethodCall method_call(kPatchPanelInterface, kPluginVmShutdownMethod);
274 dbus::MessageWriter writer(&method_call);
275
276 PluginVmShutdownRequest request;
277 request.set_id(vm_id);
278
279 if (!writer.AppendProtoAsArrayOfBytes(request)) {
280 LOG(ERROR) << "Failed to encode PluginVmShutdownRequest proto";
281 return false;
282 }
283
284 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
285 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
286 if (!dbus_response) {
287 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
288 return false;
289 }
290
291 dbus::MessageReader reader(dbus_response.get());
Garrick Evans9751a1e2020-02-20 11:02:10 +0900292 PluginVmShutdownResponse response;
Garrick Evans376f0672020-01-07 15:31:50 +0900293 if (!reader.PopArrayOfBytesAsProto(&response)) {
294 LOG(ERROR) << "Failed to parse response proto";
295 return false;
296 }
297
298 return true;
299}
300
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900301bool Client::DefaultVpnRouting(int socket) {
302 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::DEFAULT_ROUTING);
303}
304
305bool Client::RouteOnVpn(int socket) {
306 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::ROUTE_ON_VPN);
307}
308
309bool Client::BypassVpn(int socket) {
310 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::BYPASS_VPN);
311}
312
313bool Client::SendSetVpnIntentRequest(
314 int socket, SetVpnIntentRequest::VpnRoutingPolicy policy) {
315 dbus::MethodCall method_call(kPatchPanelInterface, kSetVpnIntentMethod);
316 dbus::MessageWriter writer(&method_call);
317
318 SetVpnIntentRequest request;
319 SetVpnIntentResponse response;
320 request.set_policy(policy);
321
322 if (!writer.AppendProtoAsArrayOfBytes(request)) {
323 LOG(ERROR) << "Failed to encode SetVpnIntentRequest proto";
324 return false;
325 }
326 writer.AppendFileDescriptor(socket);
327
328 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
329 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
330 if (!dbus_response) {
331 LOG(ERROR)
332 << "Failed to send SetVpnIntentRequest message to patchpanel service";
333 return false;
334 }
335
336 dbus::MessageReader reader(dbus_response.get());
337 if (!reader.PopArrayOfBytesAsProto(&response)) {
338 LOG(ERROR) << "Failed to parse SetVpnIntentResponse proto";
339 return false;
340 }
341
342 if (!response.success()) {
343 LOG(ERROR) << "SetVpnIntentRequest failed";
344 return false;
345 }
346 return true;
347}
348
Hugo Benichicc6850f2020-01-17 13:26:06 +0900349std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse>
350Client::ConnectNamespace(pid_t pid,
351 const std::string& outbound_ifname,
352 bool forward_user_traffic) {
353 // Prepare and serialize the request proto.
354 ConnectNamespaceRequest request;
355 request.set_pid(static_cast<int32_t>(pid));
356 request.set_outbound_physical_device(outbound_ifname);
357 request.set_allow_user_traffic(forward_user_traffic);
358
359 dbus::MethodCall method_call(kPatchPanelInterface, kConnectNamespaceMethod);
360 dbus::MessageWriter writer(&method_call);
361 if (!writer.AppendProtoAsArrayOfBytes(request)) {
362 LOG(ERROR) << "Failed to encode ConnectNamespaceRequest proto";
363 return {};
364 }
365
366 // Prepare an fd pair and append one fd directly after the serialized request.
367 int pipe_fds[2] = {-1, -1};
368 if (pipe2(pipe_fds, O_CLOEXEC) < 0) {
369 PLOG(ERROR) << "Failed to create a pair of fds with pipe2()";
370 return {};
371 }
372 base::ScopedFD fd_local(pipe_fds[0]);
373 // MessageWriter::AppendFileDescriptor duplicates the fd, so use ScopeFD to
374 // make sure the original fd is closed eventually.
375 base::ScopedFD fd_remote(pipe_fds[1]);
376 writer.AppendFileDescriptor(pipe_fds[1]);
377
378 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
379 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
380 if (!dbus_response) {
381 LOG(ERROR) << "Failed to send ConnectNamespace message to patchpanel";
382 return {};
383 }
384
385 dbus::MessageReader reader(dbus_response.get());
386 ConnectNamespaceResponse response;
387 if (!reader.PopArrayOfBytesAsProto(&response)) {
388 LOG(ERROR) << "Failed to parse ConnectNamespaceResponse proto";
389 return {};
390 }
391
392 if (response.ifname().empty()) {
393 LOG(ERROR) << "ConnectNamespace for netns pid " << pid << " failed";
394 return {};
395 }
396
Garrick Evans3388a032020-03-24 11:25:55 +0900397 std::string subnet_info = IPv4AddressToCidrString(
Hugo Benichicc6850f2020-01-17 13:26:06 +0900398 response.ipv4_subnet().base_addr(), response.ipv4_subnet().prefix_len());
399 LOG(INFO) << "ConnectNamespace for netns pid " << pid
400 << " succeeded: veth=" << response.ifname()
401 << " subnet=" << subnet_info;
402
403 return std::make_pair(std::move(fd_local), std::move(response));
404}
405
Garrick Evans08843932019-09-17 14:41:08 +0900406} // namespace patchpanel