blob: ad3ddc0a9ad3c1a1ac139739aededa7befd3d547 [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
Jie Jiang0a70acf2020-10-02 11:57:32 +09009#include <base/bind.h>
Garrick Evans08843932019-09-17 14:41:08 +090010#include <base/logging.h>
11#include <chromeos/dbus/service_constants.h>
12#include <dbus/message.h>
13#include <dbus/object_path.h>
14
Garrick Evans3388a032020-03-24 11:25:55 +090015#include "patchpanel/net_util.h"
Hugo Benichicc6850f2020-01-17 13:26:06 +090016
Garrick Evans08843932019-09-17 14:41:08 +090017namespace patchpanel {
18
Jason Jeremy Iman6f1f3e72020-07-06 13:04:03 +090019namespace {
20
21std::ostream& operator<<(std::ostream& stream,
22 const ModifyPortRuleRequest& request) {
23 stream << "{ operation: "
24 << ModifyPortRuleRequest::Operation_Name(request.op())
25 << ", rule type: "
26 << ModifyPortRuleRequest::RuleType_Name(request.type())
27 << ", protocol: "
28 << ModifyPortRuleRequest::Protocol_Name(request.proto());
29 if (!request.input_ifname().empty()) {
30 stream << ", input interface name: " << request.input_ifname();
31 }
32 if (!request.input_dst_ip().empty()) {
33 stream << ", input destination IP: " << request.input_dst_ip();
34 }
35 stream << ", input destination port: " << request.input_dst_port();
36 if (!request.dst_ip().empty()) {
37 stream << ", destination IP: " << request.dst_ip();
38 }
39 if (request.dst_port() != 0) {
40 stream << ", destination port: " << request.dst_port();
41 }
42 stream << " }";
43 return stream;
44}
45
Jie Jiang0a70acf2020-10-02 11:57:32 +090046void OnGetTrafficCountersDBusResponse(
47 Client::GetTrafficCountersCallback callback,
48 dbus::Response* dbus_response) {
49 if (!dbus_response) {
50 LOG(ERROR) << "Failed to send TrafficCountersRequest message to patchpanel "
51 "service";
52 std::move(callback).Run({});
53 return;
54 }
55
56 TrafficCountersResponse response;
57 dbus::MessageReader reader(dbus_response);
58 if (!reader.PopArrayOfBytesAsProto(&response)) {
59 LOG(ERROR) << "Failed to parse TrafficCountersResponse proto";
60 std::move(callback).Run({});
61 return;
62 }
63
64 std::move(callback).Run(
65 {response.counters().begin(), response.counters().end()});
66}
67
Jason Jeremy Iman6f1f3e72020-07-06 13:04:03 +090068} // namespace
69
Garrick Evans08843932019-09-17 14:41:08 +090070// static
71std::unique_ptr<Client> Client::New() {
72 dbus::Bus::Options opts;
73 opts.bus_type = dbus::Bus::SYSTEM;
74 scoped_refptr<dbus::Bus> bus(new dbus::Bus(std::move(opts)));
75
76 if (!bus->Connect()) {
77 LOG(ERROR) << "Failed to connect to system bus";
78 return nullptr;
79 }
80
81 dbus::ObjectProxy* proxy = bus->GetObjectProxy(
82 kPatchPanelServiceName, dbus::ObjectPath(kPatchPanelServicePath));
83 if (!proxy) {
84 LOG(ERROR) << "Unable to get dbus proxy for " << kPatchPanelServiceName;
85 return nullptr;
86 }
87
Garrick Evans93a83fc2020-03-31 15:16:55 +090088 return std::make_unique<Client>(std::move(bus), proxy);
89}
90
91Client::~Client() {
92 if (bus_)
93 bus_->ShutdownAndBlock();
Garrick Evans08843932019-09-17 14:41:08 +090094}
95
96bool Client::NotifyArcStartup(pid_t pid) {
97 dbus::MethodCall method_call(kPatchPanelInterface, kArcStartupMethod);
98 dbus::MessageWriter writer(&method_call);
99
100 ArcStartupRequest request;
101 request.set_pid(static_cast<uint32_t>(pid));
102
103 if (!writer.AppendProtoAsArrayOfBytes(request)) {
104 LOG(ERROR) << "Failed to encode ArcStartupRequest proto";
105 return false;
106 }
107
108 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
109 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
110 if (!dbus_response) {
111 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
112 return false;
113 }
114
115 dbus::MessageReader reader(dbus_response.get());
116 ArcStartupResponse response;
117 if (!reader.PopArrayOfBytesAsProto(&response)) {
118 LOG(ERROR) << "Failed to parse response proto";
119 return false;
120 }
121
122 return true;
123}
124
Garrick Evansca2b41b2019-12-02 09:06:11 +0900125bool Client::NotifyArcShutdown() {
Garrick Evans08843932019-09-17 14:41:08 +0900126 dbus::MethodCall method_call(kPatchPanelInterface, kArcShutdownMethod);
127 dbus::MessageWriter writer(&method_call);
128
129 ArcShutdownRequest request;
Garrick Evans08843932019-09-17 14:41:08 +0900130 if (!writer.AppendProtoAsArrayOfBytes(request)) {
131 LOG(ERROR) << "Failed to encode ArcShutdownRequest proto";
132 return false;
133 }
134
135 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
136 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
137 if (!dbus_response) {
138 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
139 return false;
140 }
141
142 dbus::MessageReader reader(dbus_response.get());
143 ArcShutdownResponse response;
144 if (!reader.PopArrayOfBytesAsProto(&response)) {
145 LOG(ERROR) << "Failed to parse response proto";
146 return false;
147 }
148
149 return true;
150}
151
Garrick Evans3388a032020-03-24 11:25:55 +0900152std::vector<NetworkDevice> Client::NotifyArcVmStartup(uint32_t cid) {
Garrick Evans08843932019-09-17 14:41:08 +0900153 dbus::MethodCall method_call(kPatchPanelInterface, kArcVmStartupMethod);
154 dbus::MessageWriter writer(&method_call);
155
156 ArcVmStartupRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900157 request.set_cid(cid);
Garrick Evans08843932019-09-17 14:41:08 +0900158
159 if (!writer.AppendProtoAsArrayOfBytes(request)) {
160 LOG(ERROR) << "Failed to encode ArcVmStartupRequest proto";
161 return {};
162 }
163
164 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
165 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
166 if (!dbus_response) {
167 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
168 return {};
169 }
170
171 dbus::MessageReader reader(dbus_response.get());
172 ArcVmStartupResponse response;
173 if (!reader.PopArrayOfBytesAsProto(&response)) {
174 LOG(ERROR) << "Failed to parse response proto";
175 return {};
176 }
177
Garrick Evans3388a032020-03-24 11:25:55 +0900178 std::vector<NetworkDevice> devices;
Garrick Evans08843932019-09-17 14:41:08 +0900179 for (const auto& d : response.devices()) {
180 devices.emplace_back(d);
181 }
182 return devices;
183}
184
Garrick Evans0a189372020-02-07 08:55:27 +0900185bool Client::NotifyArcVmShutdown(uint32_t cid) {
Garrick Evans08843932019-09-17 14:41:08 +0900186 dbus::MethodCall method_call(kPatchPanelInterface, kArcVmShutdownMethod);
187 dbus::MessageWriter writer(&method_call);
188
189 ArcVmShutdownRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900190 request.set_cid(cid);
Garrick Evans08843932019-09-17 14:41:08 +0900191
192 if (!writer.AppendProtoAsArrayOfBytes(request)) {
193 LOG(ERROR) << "Failed to encode ArcVmShutdownRequest proto";
194 return false;
195 }
196
197 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
198 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
199 if (!dbus_response) {
200 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
201 return false;
202 }
203
204 dbus::MessageReader reader(dbus_response.get());
205 ArcVmShutdownResponse response;
206 if (!reader.PopArrayOfBytesAsProto(&response)) {
207 LOG(ERROR) << "Failed to parse response proto";
208 return false;
209 }
210
211 return true;
212}
213
Garrick Evans0a189372020-02-07 08:55:27 +0900214bool Client::NotifyTerminaVmStartup(uint32_t cid,
Garrick Evans3388a032020-03-24 11:25:55 +0900215 NetworkDevice* device,
216 IPv4Subnet* container_subnet) {
Garrick Evans27b74032019-11-19 13:33:47 +0900217 dbus::MethodCall method_call(kPatchPanelInterface, kTerminaVmStartupMethod);
218 dbus::MessageWriter writer(&method_call);
219
220 TerminaVmStartupRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900221 request.set_cid(cid);
Garrick Evans27b74032019-11-19 13:33:47 +0900222
223 if (!writer.AppendProtoAsArrayOfBytes(request)) {
224 LOG(ERROR) << "Failed to encode TerminaVmStartupRequest proto";
225 return false;
226 }
227
228 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
229 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
230 if (!dbus_response) {
231 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
232 return false;
233 }
234
235 dbus::MessageReader reader(dbus_response.get());
236 TerminaVmStartupResponse response;
237 if (!reader.PopArrayOfBytesAsProto(&response)) {
238 LOG(ERROR) << "Failed to parse response proto";
239 return false;
240 }
241
242 if (!response.has_device()) {
243 LOG(ERROR) << "No device found";
244 return false;
245 }
246 *device = response.device();
247
248 if (response.has_container_subnet()) {
249 *container_subnet = response.container_subnet();
250 } else {
251 LOG(WARNING) << "No container subnet found";
252 }
253
254 return true;
255}
256
Garrick Evans0a189372020-02-07 08:55:27 +0900257bool Client::NotifyTerminaVmShutdown(uint32_t cid) {
Garrick Evans27b74032019-11-19 13:33:47 +0900258 dbus::MethodCall method_call(kPatchPanelInterface, kTerminaVmShutdownMethod);
259 dbus::MessageWriter writer(&method_call);
260
261 TerminaVmShutdownRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900262 request.set_cid(cid);
Garrick Evans27b74032019-11-19 13:33:47 +0900263
264 if (!writer.AppendProtoAsArrayOfBytes(request)) {
265 LOG(ERROR) << "Failed to encode TerminaVmShutdownRequest proto";
266 return false;
267 }
268
269 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
270 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
271 if (!dbus_response) {
272 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
273 return false;
274 }
275
276 dbus::MessageReader reader(dbus_response.get());
277 TerminaVmShutdownResponse response;
278 if (!reader.PopArrayOfBytesAsProto(&response)) {
279 LOG(ERROR) << "Failed to parse response proto";
280 return false;
281 }
282
283 return true;
284}
285
Garrick Evans376f0672020-01-07 15:31:50 +0900286bool Client::NotifyPluginVmStartup(uint64_t vm_id,
287 int subnet_index,
Garrick Evans3388a032020-03-24 11:25:55 +0900288 NetworkDevice* device) {
Garrick Evans376f0672020-01-07 15:31:50 +0900289 dbus::MethodCall method_call(kPatchPanelInterface, kPluginVmStartupMethod);
290 dbus::MessageWriter writer(&method_call);
291
292 PluginVmStartupRequest request;
293 request.set_id(vm_id);
294 request.set_subnet_index(subnet_index);
295
296 if (!writer.AppendProtoAsArrayOfBytes(request)) {
297 LOG(ERROR) << "Failed to encode PluginVmStartupRequest proto";
298 return false;
299 }
300
301 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
302 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
303 if (!dbus_response) {
304 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
305 return false;
306 }
307
308 dbus::MessageReader reader(dbus_response.get());
309 PluginVmStartupResponse response;
310 if (!reader.PopArrayOfBytesAsProto(&response)) {
311 LOG(ERROR) << "Failed to parse response proto";
312 return false;
313 }
314
315 if (!response.has_device()) {
316 LOG(ERROR) << "No device found";
317 return false;
318 }
319 *device = response.device();
320
321 return true;
322}
323
324bool Client::NotifyPluginVmShutdown(uint64_t vm_id) {
325 dbus::MethodCall method_call(kPatchPanelInterface, kPluginVmShutdownMethod);
326 dbus::MessageWriter writer(&method_call);
327
328 PluginVmShutdownRequest request;
329 request.set_id(vm_id);
330
331 if (!writer.AppendProtoAsArrayOfBytes(request)) {
332 LOG(ERROR) << "Failed to encode PluginVmShutdownRequest proto";
333 return false;
334 }
335
336 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
337 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
338 if (!dbus_response) {
339 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
340 return false;
341 }
342
343 dbus::MessageReader reader(dbus_response.get());
Garrick Evans9751a1e2020-02-20 11:02:10 +0900344 PluginVmShutdownResponse response;
Garrick Evans376f0672020-01-07 15:31:50 +0900345 if (!reader.PopArrayOfBytesAsProto(&response)) {
346 LOG(ERROR) << "Failed to parse response proto";
347 return false;
348 }
349
350 return true;
351}
352
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900353bool Client::DefaultVpnRouting(int socket) {
354 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::DEFAULT_ROUTING);
355}
356
357bool Client::RouteOnVpn(int socket) {
358 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::ROUTE_ON_VPN);
359}
360
361bool Client::BypassVpn(int socket) {
362 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::BYPASS_VPN);
363}
364
365bool Client::SendSetVpnIntentRequest(
366 int socket, SetVpnIntentRequest::VpnRoutingPolicy policy) {
367 dbus::MethodCall method_call(kPatchPanelInterface, kSetVpnIntentMethod);
368 dbus::MessageWriter writer(&method_call);
369
370 SetVpnIntentRequest request;
371 SetVpnIntentResponse response;
372 request.set_policy(policy);
373
374 if (!writer.AppendProtoAsArrayOfBytes(request)) {
375 LOG(ERROR) << "Failed to encode SetVpnIntentRequest proto";
376 return false;
377 }
378 writer.AppendFileDescriptor(socket);
379
380 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
381 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
382 if (!dbus_response) {
383 LOG(ERROR)
384 << "Failed to send SetVpnIntentRequest message to patchpanel service";
385 return false;
386 }
387
388 dbus::MessageReader reader(dbus_response.get());
389 if (!reader.PopArrayOfBytesAsProto(&response)) {
390 LOG(ERROR) << "Failed to parse SetVpnIntentResponse proto";
391 return false;
392 }
393
394 if (!response.success()) {
395 LOG(ERROR) << "SetVpnIntentRequest failed";
396 return false;
397 }
398 return true;
399}
400
Hugo Benichicc6850f2020-01-17 13:26:06 +0900401std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse>
402Client::ConnectNamespace(pid_t pid,
403 const std::string& outbound_ifname,
404 bool forward_user_traffic) {
405 // Prepare and serialize the request proto.
406 ConnectNamespaceRequest request;
407 request.set_pid(static_cast<int32_t>(pid));
408 request.set_outbound_physical_device(outbound_ifname);
409 request.set_allow_user_traffic(forward_user_traffic);
410
411 dbus::MethodCall method_call(kPatchPanelInterface, kConnectNamespaceMethod);
412 dbus::MessageWriter writer(&method_call);
413 if (!writer.AppendProtoAsArrayOfBytes(request)) {
414 LOG(ERROR) << "Failed to encode ConnectNamespaceRequest proto";
415 return {};
416 }
417
418 // Prepare an fd pair and append one fd directly after the serialized request.
419 int pipe_fds[2] = {-1, -1};
420 if (pipe2(pipe_fds, O_CLOEXEC) < 0) {
421 PLOG(ERROR) << "Failed to create a pair of fds with pipe2()";
422 return {};
423 }
424 base::ScopedFD fd_local(pipe_fds[0]);
425 // MessageWriter::AppendFileDescriptor duplicates the fd, so use ScopeFD to
426 // make sure the original fd is closed eventually.
427 base::ScopedFD fd_remote(pipe_fds[1]);
428 writer.AppendFileDescriptor(pipe_fds[1]);
429
430 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
431 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
432 if (!dbus_response) {
433 LOG(ERROR) << "Failed to send ConnectNamespace message to patchpanel";
434 return {};
435 }
436
437 dbus::MessageReader reader(dbus_response.get());
438 ConnectNamespaceResponse response;
439 if (!reader.PopArrayOfBytesAsProto(&response)) {
440 LOG(ERROR) << "Failed to parse ConnectNamespaceResponse proto";
441 return {};
442 }
443
Hugo Benichi2fd0c6e2020-04-17 16:12:05 +0900444 if (response.peer_ifname().empty() || response.host_ifname().empty()) {
Hugo Benichicc6850f2020-01-17 13:26:06 +0900445 LOG(ERROR) << "ConnectNamespace for netns pid " << pid << " failed";
446 return {};
447 }
448
Garrick Evans3388a032020-03-24 11:25:55 +0900449 std::string subnet_info = IPv4AddressToCidrString(
Hugo Benichicc6850f2020-01-17 13:26:06 +0900450 response.ipv4_subnet().base_addr(), response.ipv4_subnet().prefix_len());
451 LOG(INFO) << "ConnectNamespace for netns pid " << pid
Hugo Benichi2fd0c6e2020-04-17 16:12:05 +0900452 << " succeeded: peer_ifname=" << response.peer_ifname()
453 << " peer_ipv4_address="
454 << IPv4AddressToString(response.peer_ipv4_address())
455 << " host_ifname=" << response.host_ifname()
456 << " host_ipv4_address="
457 << IPv4AddressToString(response.host_ipv4_address())
Hugo Benichicc6850f2020-01-17 13:26:06 +0900458 << " subnet=" << subnet_info;
459
460 return std::make_pair(std::move(fd_local), std::move(response));
461}
462
Jie Jiang0a70acf2020-10-02 11:57:32 +0900463void Client::GetTrafficCounters(const std::set<std::string>& devices,
464 GetTrafficCountersCallback callback) {
Jie Jiange02d1202020-07-27 16:57:04 +0900465 dbus::MethodCall method_call(kPatchPanelInterface, kGetTrafficCountersMethod);
466 dbus::MessageWriter writer(&method_call);
467
468 TrafficCountersRequest request;
469 for (const auto& device : devices) {
470 request.add_devices(device);
471 }
472
473 if (!writer.AppendProtoAsArrayOfBytes(request)) {
474 LOG(ERROR) << "Failed to encode TrafficCountersRequest proto";
Jie Jiang0a70acf2020-10-02 11:57:32 +0900475 std::move(callback).Run({});
476 return;
Jie Jiange02d1202020-07-27 16:57:04 +0900477 }
478
Jie Jiang0a70acf2020-10-02 11:57:32 +0900479 proxy_->CallMethod(
480 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
481 base::BindOnce(&OnGetTrafficCountersDBusResponse, std::move(callback)));
Jie Jiange02d1202020-07-27 16:57:04 +0900482}
483
Jason Jeremy Iman6f1f3e72020-07-06 13:04:03 +0900484bool Client::ModifyPortRule(ModifyPortRuleRequest::Operation op,
485 ModifyPortRuleRequest::RuleType type,
486 ModifyPortRuleRequest::Protocol proto,
487 const std::string& input_ifname,
488 const std::string& input_dst_ip,
489 uint32_t input_dst_port,
490 const std::string& dst_ip,
491 uint32_t dst_port) {
492 dbus::MethodCall method_call(kPatchPanelInterface, kModifyPortRuleMethod);
493 dbus::MessageWriter writer(&method_call);
494
495 ModifyPortRuleRequest request;
496 ModifyPortRuleResponse response;
497
498 request.set_op(op);
499 request.set_type(type);
500 request.set_proto(proto);
501 request.set_input_ifname(input_ifname);
502 request.set_input_dst_ip(input_dst_ip);
503 request.set_input_dst_port(input_dst_port);
504 request.set_dst_ip(dst_ip);
505 request.set_dst_port(dst_port);
506
507 if (!writer.AppendProtoAsArrayOfBytes(request)) {
508 LOG(ERROR) << "Failed to encode ModifyPortRuleRequest proto " << request;
509 return false;
510 }
511
512 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
513 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
514 if (!dbus_response) {
515 LOG(ERROR)
516 << "Failed to send ModifyPortRuleRequest message to patchpanel service "
517 << request;
518 return false;
519 }
520
521 dbus::MessageReader reader(dbus_response.get());
522 if (!reader.PopArrayOfBytesAsProto(&response)) {
523 LOG(ERROR) << "Failed to parse ModifyPortRuleResponse proto " << request;
524 return false;
525 }
526
527 if (!response.success()) {
528 LOG(ERROR) << "ModifyPortRuleRequest failed " << request;
529 return false;
530 }
531 return true;
532}
533
Garrick Evans08843932019-09-17 14:41:08 +0900534} // namespace patchpanel