blob: 8f55543c35b873d2aadbd605f5ee15b9642722ba [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
Jason Jeremy Imanadffbcb2020-08-31 13:21:36 +09005#include "patchpanel/dbus/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
Jason Jeremy Iman6f1f3e72020-07-06 13:04:03 +090018namespace {
19
20std::ostream& operator<<(std::ostream& stream,
21 const ModifyPortRuleRequest& request) {
22 stream << "{ operation: "
23 << ModifyPortRuleRequest::Operation_Name(request.op())
24 << ", rule type: "
25 << ModifyPortRuleRequest::RuleType_Name(request.type())
26 << ", protocol: "
27 << ModifyPortRuleRequest::Protocol_Name(request.proto());
28 if (!request.input_ifname().empty()) {
29 stream << ", input interface name: " << request.input_ifname();
30 }
31 if (!request.input_dst_ip().empty()) {
32 stream << ", input destination IP: " << request.input_dst_ip();
33 }
34 stream << ", input destination port: " << request.input_dst_port();
35 if (!request.dst_ip().empty()) {
36 stream << ", destination IP: " << request.dst_ip();
37 }
38 if (request.dst_port() != 0) {
39 stream << ", destination port: " << request.dst_port();
40 }
41 stream << " }";
42 return stream;
43}
44
45} // namespace
46
Garrick Evans08843932019-09-17 14:41:08 +090047// static
48std::unique_ptr<Client> Client::New() {
49 dbus::Bus::Options opts;
50 opts.bus_type = dbus::Bus::SYSTEM;
51 scoped_refptr<dbus::Bus> bus(new dbus::Bus(std::move(opts)));
52
53 if (!bus->Connect()) {
54 LOG(ERROR) << "Failed to connect to system bus";
55 return nullptr;
56 }
57
58 dbus::ObjectProxy* proxy = bus->GetObjectProxy(
59 kPatchPanelServiceName, dbus::ObjectPath(kPatchPanelServicePath));
60 if (!proxy) {
61 LOG(ERROR) << "Unable to get dbus proxy for " << kPatchPanelServiceName;
62 return nullptr;
63 }
64
Garrick Evans93a83fc2020-03-31 15:16:55 +090065 return std::make_unique<Client>(std::move(bus), proxy);
66}
67
68Client::~Client() {
69 if (bus_)
70 bus_->ShutdownAndBlock();
Garrick Evans08843932019-09-17 14:41:08 +090071}
72
73bool Client::NotifyArcStartup(pid_t pid) {
74 dbus::MethodCall method_call(kPatchPanelInterface, kArcStartupMethod);
75 dbus::MessageWriter writer(&method_call);
76
77 ArcStartupRequest request;
78 request.set_pid(static_cast<uint32_t>(pid));
79
80 if (!writer.AppendProtoAsArrayOfBytes(request)) {
81 LOG(ERROR) << "Failed to encode ArcStartupRequest proto";
82 return false;
83 }
84
85 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
86 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
87 if (!dbus_response) {
88 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
89 return false;
90 }
91
92 dbus::MessageReader reader(dbus_response.get());
93 ArcStartupResponse response;
94 if (!reader.PopArrayOfBytesAsProto(&response)) {
95 LOG(ERROR) << "Failed to parse response proto";
96 return false;
97 }
98
99 return true;
100}
101
Garrick Evansca2b41b2019-12-02 09:06:11 +0900102bool Client::NotifyArcShutdown() {
Garrick Evans08843932019-09-17 14:41:08 +0900103 dbus::MethodCall method_call(kPatchPanelInterface, kArcShutdownMethod);
104 dbus::MessageWriter writer(&method_call);
105
106 ArcShutdownRequest request;
Garrick Evans08843932019-09-17 14:41:08 +0900107 if (!writer.AppendProtoAsArrayOfBytes(request)) {
108 LOG(ERROR) << "Failed to encode ArcShutdownRequest proto";
109 return false;
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 false;
117 }
118
119 dbus::MessageReader reader(dbus_response.get());
120 ArcShutdownResponse response;
121 if (!reader.PopArrayOfBytesAsProto(&response)) {
122 LOG(ERROR) << "Failed to parse response proto";
123 return false;
124 }
125
126 return true;
127}
128
Garrick Evans3388a032020-03-24 11:25:55 +0900129std::vector<NetworkDevice> Client::NotifyArcVmStartup(uint32_t cid) {
Garrick Evans08843932019-09-17 14:41:08 +0900130 dbus::MethodCall method_call(kPatchPanelInterface, kArcVmStartupMethod);
131 dbus::MessageWriter writer(&method_call);
132
133 ArcVmStartupRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900134 request.set_cid(cid);
Garrick Evans08843932019-09-17 14:41:08 +0900135
136 if (!writer.AppendProtoAsArrayOfBytes(request)) {
137 LOG(ERROR) << "Failed to encode ArcVmStartupRequest proto";
138 return {};
139 }
140
141 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
142 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
143 if (!dbus_response) {
144 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
145 return {};
146 }
147
148 dbus::MessageReader reader(dbus_response.get());
149 ArcVmStartupResponse response;
150 if (!reader.PopArrayOfBytesAsProto(&response)) {
151 LOG(ERROR) << "Failed to parse response proto";
152 return {};
153 }
154
Garrick Evans3388a032020-03-24 11:25:55 +0900155 std::vector<NetworkDevice> devices;
Garrick Evans08843932019-09-17 14:41:08 +0900156 for (const auto& d : response.devices()) {
157 devices.emplace_back(d);
158 }
159 return devices;
160}
161
Garrick Evans0a189372020-02-07 08:55:27 +0900162bool Client::NotifyArcVmShutdown(uint32_t cid) {
Garrick Evans08843932019-09-17 14:41:08 +0900163 dbus::MethodCall method_call(kPatchPanelInterface, kArcVmShutdownMethod);
164 dbus::MessageWriter writer(&method_call);
165
166 ArcVmShutdownRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900167 request.set_cid(cid);
Garrick Evans08843932019-09-17 14:41:08 +0900168
169 if (!writer.AppendProtoAsArrayOfBytes(request)) {
170 LOG(ERROR) << "Failed to encode ArcVmShutdownRequest proto";
171 return false;
172 }
173
174 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
175 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
176 if (!dbus_response) {
177 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
178 return false;
179 }
180
181 dbus::MessageReader reader(dbus_response.get());
182 ArcVmShutdownResponse response;
183 if (!reader.PopArrayOfBytesAsProto(&response)) {
184 LOG(ERROR) << "Failed to parse response proto";
185 return false;
186 }
187
188 return true;
189}
190
Garrick Evans0a189372020-02-07 08:55:27 +0900191bool Client::NotifyTerminaVmStartup(uint32_t cid,
Garrick Evans3388a032020-03-24 11:25:55 +0900192 NetworkDevice* device,
193 IPv4Subnet* container_subnet) {
Garrick Evans27b74032019-11-19 13:33:47 +0900194 dbus::MethodCall method_call(kPatchPanelInterface, kTerminaVmStartupMethod);
195 dbus::MessageWriter writer(&method_call);
196
197 TerminaVmStartupRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900198 request.set_cid(cid);
Garrick Evans27b74032019-11-19 13:33:47 +0900199
200 if (!writer.AppendProtoAsArrayOfBytes(request)) {
201 LOG(ERROR) << "Failed to encode TerminaVmStartupRequest proto";
202 return false;
203 }
204
205 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
206 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
207 if (!dbus_response) {
208 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
209 return false;
210 }
211
212 dbus::MessageReader reader(dbus_response.get());
213 TerminaVmStartupResponse response;
214 if (!reader.PopArrayOfBytesAsProto(&response)) {
215 LOG(ERROR) << "Failed to parse response proto";
216 return false;
217 }
218
219 if (!response.has_device()) {
220 LOG(ERROR) << "No device found";
221 return false;
222 }
223 *device = response.device();
224
225 if (response.has_container_subnet()) {
226 *container_subnet = response.container_subnet();
227 } else {
228 LOG(WARNING) << "No container subnet found";
229 }
230
231 return true;
232}
233
Garrick Evans0a189372020-02-07 08:55:27 +0900234bool Client::NotifyTerminaVmShutdown(uint32_t cid) {
Garrick Evans27b74032019-11-19 13:33:47 +0900235 dbus::MethodCall method_call(kPatchPanelInterface, kTerminaVmShutdownMethod);
236 dbus::MessageWriter writer(&method_call);
237
238 TerminaVmShutdownRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900239 request.set_cid(cid);
Garrick Evans27b74032019-11-19 13:33:47 +0900240
241 if (!writer.AppendProtoAsArrayOfBytes(request)) {
242 LOG(ERROR) << "Failed to encode TerminaVmShutdownRequest proto";
243 return false;
244 }
245
246 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
247 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
248 if (!dbus_response) {
249 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
250 return false;
251 }
252
253 dbus::MessageReader reader(dbus_response.get());
254 TerminaVmShutdownResponse response;
255 if (!reader.PopArrayOfBytesAsProto(&response)) {
256 LOG(ERROR) << "Failed to parse response proto";
257 return false;
258 }
259
260 return true;
261}
262
Garrick Evans376f0672020-01-07 15:31:50 +0900263bool Client::NotifyPluginVmStartup(uint64_t vm_id,
264 int subnet_index,
Garrick Evans3388a032020-03-24 11:25:55 +0900265 NetworkDevice* device) {
Garrick Evans376f0672020-01-07 15:31:50 +0900266 dbus::MethodCall method_call(kPatchPanelInterface, kPluginVmStartupMethod);
267 dbus::MessageWriter writer(&method_call);
268
269 PluginVmStartupRequest request;
270 request.set_id(vm_id);
271 request.set_subnet_index(subnet_index);
272
273 if (!writer.AppendProtoAsArrayOfBytes(request)) {
274 LOG(ERROR) << "Failed to encode PluginVmStartupRequest proto";
275 return false;
276 }
277
278 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
279 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
280 if (!dbus_response) {
281 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
282 return false;
283 }
284
285 dbus::MessageReader reader(dbus_response.get());
286 PluginVmStartupResponse response;
287 if (!reader.PopArrayOfBytesAsProto(&response)) {
288 LOG(ERROR) << "Failed to parse response proto";
289 return false;
290 }
291
292 if (!response.has_device()) {
293 LOG(ERROR) << "No device found";
294 return false;
295 }
296 *device = response.device();
297
298 return true;
299}
300
301bool Client::NotifyPluginVmShutdown(uint64_t vm_id) {
302 dbus::MethodCall method_call(kPatchPanelInterface, kPluginVmShutdownMethod);
303 dbus::MessageWriter writer(&method_call);
304
305 PluginVmShutdownRequest request;
306 request.set_id(vm_id);
307
308 if (!writer.AppendProtoAsArrayOfBytes(request)) {
309 LOG(ERROR) << "Failed to encode PluginVmShutdownRequest proto";
310 return false;
311 }
312
313 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
314 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
315 if (!dbus_response) {
316 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
317 return false;
318 }
319
320 dbus::MessageReader reader(dbus_response.get());
Garrick Evans9751a1e2020-02-20 11:02:10 +0900321 PluginVmShutdownResponse response;
Garrick Evans376f0672020-01-07 15:31:50 +0900322 if (!reader.PopArrayOfBytesAsProto(&response)) {
323 LOG(ERROR) << "Failed to parse response proto";
324 return false;
325 }
326
327 return true;
328}
329
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900330bool Client::DefaultVpnRouting(int socket) {
331 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::DEFAULT_ROUTING);
332}
333
334bool Client::RouteOnVpn(int socket) {
335 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::ROUTE_ON_VPN);
336}
337
338bool Client::BypassVpn(int socket) {
339 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::BYPASS_VPN);
340}
341
342bool Client::SendSetVpnIntentRequest(
343 int socket, SetVpnIntentRequest::VpnRoutingPolicy policy) {
344 dbus::MethodCall method_call(kPatchPanelInterface, kSetVpnIntentMethod);
345 dbus::MessageWriter writer(&method_call);
346
347 SetVpnIntentRequest request;
348 SetVpnIntentResponse response;
349 request.set_policy(policy);
350
351 if (!writer.AppendProtoAsArrayOfBytes(request)) {
352 LOG(ERROR) << "Failed to encode SetVpnIntentRequest proto";
353 return false;
354 }
355 writer.AppendFileDescriptor(socket);
356
357 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
358 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
359 if (!dbus_response) {
360 LOG(ERROR)
361 << "Failed to send SetVpnIntentRequest message to patchpanel service";
362 return false;
363 }
364
365 dbus::MessageReader reader(dbus_response.get());
366 if (!reader.PopArrayOfBytesAsProto(&response)) {
367 LOG(ERROR) << "Failed to parse SetVpnIntentResponse proto";
368 return false;
369 }
370
371 if (!response.success()) {
372 LOG(ERROR) << "SetVpnIntentRequest failed";
373 return false;
374 }
375 return true;
376}
377
Hugo Benichicc6850f2020-01-17 13:26:06 +0900378std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse>
379Client::ConnectNamespace(pid_t pid,
380 const std::string& outbound_ifname,
381 bool forward_user_traffic) {
382 // Prepare and serialize the request proto.
383 ConnectNamespaceRequest request;
384 request.set_pid(static_cast<int32_t>(pid));
385 request.set_outbound_physical_device(outbound_ifname);
386 request.set_allow_user_traffic(forward_user_traffic);
387
388 dbus::MethodCall method_call(kPatchPanelInterface, kConnectNamespaceMethod);
389 dbus::MessageWriter writer(&method_call);
390 if (!writer.AppendProtoAsArrayOfBytes(request)) {
391 LOG(ERROR) << "Failed to encode ConnectNamespaceRequest proto";
392 return {};
393 }
394
395 // Prepare an fd pair and append one fd directly after the serialized request.
396 int pipe_fds[2] = {-1, -1};
397 if (pipe2(pipe_fds, O_CLOEXEC) < 0) {
398 PLOG(ERROR) << "Failed to create a pair of fds with pipe2()";
399 return {};
400 }
401 base::ScopedFD fd_local(pipe_fds[0]);
402 // MessageWriter::AppendFileDescriptor duplicates the fd, so use ScopeFD to
403 // make sure the original fd is closed eventually.
404 base::ScopedFD fd_remote(pipe_fds[1]);
405 writer.AppendFileDescriptor(pipe_fds[1]);
406
407 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
408 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
409 if (!dbus_response) {
410 LOG(ERROR) << "Failed to send ConnectNamespace message to patchpanel";
411 return {};
412 }
413
414 dbus::MessageReader reader(dbus_response.get());
415 ConnectNamespaceResponse response;
416 if (!reader.PopArrayOfBytesAsProto(&response)) {
417 LOG(ERROR) << "Failed to parse ConnectNamespaceResponse proto";
418 return {};
419 }
420
Hugo Benichi2fd0c6e2020-04-17 16:12:05 +0900421 if (response.peer_ifname().empty() || response.host_ifname().empty()) {
Hugo Benichicc6850f2020-01-17 13:26:06 +0900422 LOG(ERROR) << "ConnectNamespace for netns pid " << pid << " failed";
423 return {};
424 }
425
Garrick Evans3388a032020-03-24 11:25:55 +0900426 std::string subnet_info = IPv4AddressToCidrString(
Hugo Benichicc6850f2020-01-17 13:26:06 +0900427 response.ipv4_subnet().base_addr(), response.ipv4_subnet().prefix_len());
428 LOG(INFO) << "ConnectNamespace for netns pid " << pid
Hugo Benichi2fd0c6e2020-04-17 16:12:05 +0900429 << " succeeded: peer_ifname=" << response.peer_ifname()
430 << " peer_ipv4_address="
431 << IPv4AddressToString(response.peer_ipv4_address())
432 << " host_ifname=" << response.host_ifname()
433 << " host_ipv4_address="
434 << IPv4AddressToString(response.host_ipv4_address())
Hugo Benichicc6850f2020-01-17 13:26:06 +0900435 << " subnet=" << subnet_info;
436
437 return std::make_pair(std::move(fd_local), std::move(response));
438}
439
Jie Jiange02d1202020-07-27 16:57:04 +0900440std::vector<TrafficCounter> Client::GetTrafficCounters(
441 const std::set<std::string>& devices) {
442 dbus::MethodCall method_call(kPatchPanelInterface, kGetTrafficCountersMethod);
443 dbus::MessageWriter writer(&method_call);
444
445 TrafficCountersRequest request;
446 for (const auto& device : devices) {
447 request.add_devices(device);
448 }
449
450 if (!writer.AppendProtoAsArrayOfBytes(request)) {
451 LOG(ERROR) << "Failed to encode TrafficCountersRequest proto";
452 return {};
453 }
454
455 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
456 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
457 if (!dbus_response) {
458 LOG(ERROR) << "Failed to send TrafficCountersRequest message to patchpanel "
459 "service";
460 return {};
461 }
462
463 TrafficCountersResponse response;
464 dbus::MessageReader reader(dbus_response.get());
465 if (!reader.PopArrayOfBytesAsProto(&response)) {
466 LOG(ERROR) << "Failed to parse TrafficCountersResponse proto";
467 return {};
468 }
469
470 return {response.counters().begin(), response.counters().end()};
471}
472
Jason Jeremy Iman6f1f3e72020-07-06 13:04:03 +0900473bool Client::ModifyPortRule(ModifyPortRuleRequest::Operation op,
474 ModifyPortRuleRequest::RuleType type,
475 ModifyPortRuleRequest::Protocol proto,
476 const std::string& input_ifname,
477 const std::string& input_dst_ip,
478 uint32_t input_dst_port,
479 const std::string& dst_ip,
480 uint32_t dst_port) {
481 dbus::MethodCall method_call(kPatchPanelInterface, kModifyPortRuleMethod);
482 dbus::MessageWriter writer(&method_call);
483
484 ModifyPortRuleRequest request;
485 ModifyPortRuleResponse response;
486
487 request.set_op(op);
488 request.set_type(type);
489 request.set_proto(proto);
490 request.set_input_ifname(input_ifname);
491 request.set_input_dst_ip(input_dst_ip);
492 request.set_input_dst_port(input_dst_port);
493 request.set_dst_ip(dst_ip);
494 request.set_dst_port(dst_port);
495
496 if (!writer.AppendProtoAsArrayOfBytes(request)) {
497 LOG(ERROR) << "Failed to encode ModifyPortRuleRequest proto " << request;
498 return false;
499 }
500
501 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
502 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
503 if (!dbus_response) {
504 LOG(ERROR)
505 << "Failed to send ModifyPortRuleRequest message to patchpanel service "
506 << request;
507 return false;
508 }
509
510 dbus::MessageReader reader(dbus_response.get());
511 if (!reader.PopArrayOfBytesAsProto(&response)) {
512 LOG(ERROR) << "Failed to parse ModifyPortRuleResponse proto " << request;
513 return false;
514 }
515
516 if (!response.success()) {
517 LOG(ERROR) << "ModifyPortRuleRequest failed " << request;
518 return false;
519 }
520 return true;
521}
522
Garrick Evans08843932019-09-17 14:41:08 +0900523} // namespace patchpanel