blob: eed18dede54ecffd8c738112f8deb9d9a562d9f2 [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
Garrick Evansf04f0442020-12-01 12:36:44 +090068void OnNetworkDeviceChangedSignal(
69 const Client::NetworkDeviceChangedSignalHandler& handler,
70 dbus::Signal* signal) {
71 dbus::MessageReader reader(signal);
72 NetworkDeviceChangedSignal proto;
73 if (!reader.PopArrayOfBytesAsProto(&proto)) {
74 LOG(ERROR) << "Failed to parse NetworkDeviceChangedSignal proto";
75 return;
76 }
77
78 handler.Run(proto);
79}
80
Jie Jiang25c1b972020-11-12 15:42:53 +090081void OnNeighborReachabilityEventSignal(
82 const Client::NeighborReachabilityEventHandler& handler,
Jie Jiange2e4c0b2020-09-16 18:48:43 +090083 dbus::Signal* signal) {
84 dbus::MessageReader reader(signal);
Jie Jiang25c1b972020-11-12 15:42:53 +090085 NeighborReachabilityEventSignal proto;
Jie Jiange2e4c0b2020-09-16 18:48:43 +090086 if (!reader.PopArrayOfBytesAsProto(&proto)) {
87 LOG(ERROR) << "Failed to parse NeighborConnectedStateChangedSignal proto";
88 return;
89 }
90
91 handler.Run(proto);
92}
93
94void OnSignalConnectedCallback(const std::string& interface_name,
95 const std::string& signal_name,
96 bool success) {
97 if (!success)
98 LOG(ERROR) << "Failed to connect to " << signal_name;
99}
100
Jie Jiang81c84db2020-09-29 17:40:16 +0900101class ClientImpl : public Client {
102 public:
103 ClientImpl(const scoped_refptr<dbus::Bus>& bus, dbus::ObjectProxy* proxy)
104 : bus_(std::move(bus)), proxy_(proxy) {}
Qijiang Fan6bc59e12020-11-11 02:51:06 +0900105 ClientImpl(const ClientImpl&) = delete;
106 ClientImpl& operator=(const ClientImpl&) = delete;
107
Jie Jiang81c84db2020-09-29 17:40:16 +0900108 ~ClientImpl();
Jason Jeremy Iman6f1f3e72020-07-06 13:04:03 +0900109
Garrick Evansd0a646e2020-11-25 21:08:32 +0900110 void RegisterOnAvailableCallback(
111 base::RepeatingCallback<void(bool)> callback) override;
112
Jie Jiang81c84db2020-09-29 17:40:16 +0900113 bool NotifyArcStartup(pid_t pid) override;
114 bool NotifyArcShutdown() override;
Garrick Evans08843932019-09-17 14:41:08 +0900115
Jie Jiang81c84db2020-09-29 17:40:16 +0900116 std::vector<NetworkDevice> NotifyArcVmStartup(uint32_t cid) override;
117 bool NotifyArcVmShutdown(uint32_t cid) override;
Garrick Evans08843932019-09-17 14:41:08 +0900118
Jie Jiang81c84db2020-09-29 17:40:16 +0900119 bool NotifyTerminaVmStartup(uint32_t cid,
120 NetworkDevice* device,
121 IPv4Subnet* container_subnet) override;
122 bool NotifyTerminaVmShutdown(uint32_t cid) override;
Garrick Evans08843932019-09-17 14:41:08 +0900123
Jie Jiang81c84db2020-09-29 17:40:16 +0900124 bool NotifyPluginVmStartup(uint64_t vm_id,
125 int subnet_index,
126 NetworkDevice* device) override;
127 bool NotifyPluginVmShutdown(uint64_t vm_id) override;
Garrick Evans93a83fc2020-03-31 15:16:55 +0900128
Jie Jiang81c84db2020-09-29 17:40:16 +0900129 bool DefaultVpnRouting(int socket) override;
130
131 bool RouteOnVpn(int socket) override;
132
133 bool BypassVpn(int socket) override;
134
135 std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse>
136 ConnectNamespace(pid_t pid,
137 const std::string& outbound_ifname,
Garrick Evans58697022020-12-03 12:41:13 +0900138 bool forward_user_traffic,
139 bool route_on_vpn,
140 TrafficCounter::Source traffic_source) override;
Jie Jiang81c84db2020-09-29 17:40:16 +0900141
142 void GetTrafficCounters(const std::set<std::string>& devices,
143 GetTrafficCountersCallback callback) override;
144
145 bool ModifyPortRule(patchpanel::ModifyPortRuleRequest::Operation op,
146 patchpanel::ModifyPortRuleRequest::RuleType type,
147 patchpanel::ModifyPortRuleRequest::Protocol proto,
148 const std::string& input_ifname,
149 const std::string& input_dst_ip,
150 uint32_t input_dst_port,
151 const std::string& dst_ip,
152 uint32_t dst_port) override;
153
Garrick Evans9e637982020-11-30 11:59:27 +0900154 std::vector<NetworkDevice> GetDevices() override;
155
Garrick Evansf04f0442020-12-01 12:36:44 +0900156 void RegisterNetworkDeviceChangedSignalHandler(
157 NetworkDeviceChangedSignalHandler handler) override;
158
Jie Jiang25c1b972020-11-12 15:42:53 +0900159 void RegisterNeighborReachabilityEventHandler(
160 NeighborReachabilityEventHandler handler) override;
Jie Jiang81c84db2020-09-29 17:40:16 +0900161
162 private:
163 scoped_refptr<dbus::Bus> bus_;
164 dbus::ObjectProxy* proxy_ = nullptr; // owned by bus_
165
166 bool SendSetVpnIntentRequest(int socket,
167 SetVpnIntentRequest::VpnRoutingPolicy policy);
Jie Jiang81c84db2020-09-29 17:40:16 +0900168};
169
170ClientImpl::~ClientImpl() {
Garrick Evans93a83fc2020-03-31 15:16:55 +0900171 if (bus_)
172 bus_->ShutdownAndBlock();
Garrick Evans08843932019-09-17 14:41:08 +0900173}
174
Garrick Evansd0a646e2020-11-25 21:08:32 +0900175void ClientImpl::RegisterOnAvailableCallback(
176 base::RepeatingCallback<void(bool)> callback) {
177 if (!proxy_) {
178 LOG(ERROR) << "Cannot register callback - no proxy";
179 return;
180 }
181 proxy_->WaitForServiceToBeAvailable(callback);
182}
183
Jie Jiang81c84db2020-09-29 17:40:16 +0900184bool ClientImpl::NotifyArcStartup(pid_t pid) {
Garrick Evans08843932019-09-17 14:41:08 +0900185 dbus::MethodCall method_call(kPatchPanelInterface, kArcStartupMethod);
186 dbus::MessageWriter writer(&method_call);
187
188 ArcStartupRequest request;
189 request.set_pid(static_cast<uint32_t>(pid));
190
191 if (!writer.AppendProtoAsArrayOfBytes(request)) {
192 LOG(ERROR) << "Failed to encode ArcStartupRequest proto";
193 return false;
194 }
195
196 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
197 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
198 if (!dbus_response) {
199 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
200 return false;
201 }
202
203 dbus::MessageReader reader(dbus_response.get());
204 ArcStartupResponse response;
205 if (!reader.PopArrayOfBytesAsProto(&response)) {
206 LOG(ERROR) << "Failed to parse response proto";
207 return false;
208 }
209
210 return true;
211}
212
Jie Jiang81c84db2020-09-29 17:40:16 +0900213bool ClientImpl::NotifyArcShutdown() {
Garrick Evans08843932019-09-17 14:41:08 +0900214 dbus::MethodCall method_call(kPatchPanelInterface, kArcShutdownMethod);
215 dbus::MessageWriter writer(&method_call);
216
217 ArcShutdownRequest request;
Garrick Evans08843932019-09-17 14:41:08 +0900218 if (!writer.AppendProtoAsArrayOfBytes(request)) {
219 LOG(ERROR) << "Failed to encode ArcShutdownRequest proto";
220 return false;
221 }
222
223 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
224 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
225 if (!dbus_response) {
226 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
227 return false;
228 }
229
230 dbus::MessageReader reader(dbus_response.get());
231 ArcShutdownResponse response;
232 if (!reader.PopArrayOfBytesAsProto(&response)) {
233 LOG(ERROR) << "Failed to parse response proto";
234 return false;
235 }
236
237 return true;
238}
239
Jie Jiang81c84db2020-09-29 17:40:16 +0900240std::vector<NetworkDevice> ClientImpl::NotifyArcVmStartup(uint32_t cid) {
Garrick Evans08843932019-09-17 14:41:08 +0900241 dbus::MethodCall method_call(kPatchPanelInterface, kArcVmStartupMethod);
242 dbus::MessageWriter writer(&method_call);
243
244 ArcVmStartupRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900245 request.set_cid(cid);
Garrick Evans08843932019-09-17 14:41:08 +0900246
247 if (!writer.AppendProtoAsArrayOfBytes(request)) {
248 LOG(ERROR) << "Failed to encode ArcVmStartupRequest proto";
249 return {};
250 }
251
252 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
253 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
254 if (!dbus_response) {
255 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
256 return {};
257 }
258
259 dbus::MessageReader reader(dbus_response.get());
260 ArcVmStartupResponse response;
261 if (!reader.PopArrayOfBytesAsProto(&response)) {
262 LOG(ERROR) << "Failed to parse response proto";
263 return {};
264 }
265
Garrick Evans3388a032020-03-24 11:25:55 +0900266 std::vector<NetworkDevice> devices;
Garrick Evans08843932019-09-17 14:41:08 +0900267 for (const auto& d : response.devices()) {
268 devices.emplace_back(d);
269 }
270 return devices;
271}
272
Jie Jiang81c84db2020-09-29 17:40:16 +0900273bool ClientImpl::NotifyArcVmShutdown(uint32_t cid) {
Garrick Evans08843932019-09-17 14:41:08 +0900274 dbus::MethodCall method_call(kPatchPanelInterface, kArcVmShutdownMethod);
275 dbus::MessageWriter writer(&method_call);
276
277 ArcVmShutdownRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900278 request.set_cid(cid);
Garrick Evans08843932019-09-17 14:41:08 +0900279
280 if (!writer.AppendProtoAsArrayOfBytes(request)) {
281 LOG(ERROR) << "Failed to encode ArcVmShutdownRequest proto";
282 return false;
283 }
284
285 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
286 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
287 if (!dbus_response) {
288 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
289 return false;
290 }
291
292 dbus::MessageReader reader(dbus_response.get());
293 ArcVmShutdownResponse response;
294 if (!reader.PopArrayOfBytesAsProto(&response)) {
295 LOG(ERROR) << "Failed to parse response proto";
296 return false;
297 }
298
299 return true;
300}
301
Jie Jiang81c84db2020-09-29 17:40:16 +0900302bool ClientImpl::NotifyTerminaVmStartup(uint32_t cid,
303 NetworkDevice* device,
304 IPv4Subnet* container_subnet) {
Garrick Evans27b74032019-11-19 13:33:47 +0900305 dbus::MethodCall method_call(kPatchPanelInterface, kTerminaVmStartupMethod);
306 dbus::MessageWriter writer(&method_call);
307
308 TerminaVmStartupRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900309 request.set_cid(cid);
Garrick Evans27b74032019-11-19 13:33:47 +0900310
311 if (!writer.AppendProtoAsArrayOfBytes(request)) {
312 LOG(ERROR) << "Failed to encode TerminaVmStartupRequest proto";
313 return false;
314 }
315
316 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
317 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
318 if (!dbus_response) {
319 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
320 return false;
321 }
322
323 dbus::MessageReader reader(dbus_response.get());
324 TerminaVmStartupResponse response;
325 if (!reader.PopArrayOfBytesAsProto(&response)) {
326 LOG(ERROR) << "Failed to parse response proto";
327 return false;
328 }
329
330 if (!response.has_device()) {
331 LOG(ERROR) << "No device found";
332 return false;
333 }
334 *device = response.device();
335
336 if (response.has_container_subnet()) {
337 *container_subnet = response.container_subnet();
338 } else {
339 LOG(WARNING) << "No container subnet found";
340 }
341
342 return true;
343}
344
Jie Jiang81c84db2020-09-29 17:40:16 +0900345bool ClientImpl::NotifyTerminaVmShutdown(uint32_t cid) {
Garrick Evans27b74032019-11-19 13:33:47 +0900346 dbus::MethodCall method_call(kPatchPanelInterface, kTerminaVmShutdownMethod);
347 dbus::MessageWriter writer(&method_call);
348
349 TerminaVmShutdownRequest request;
Garrick Evans0a189372020-02-07 08:55:27 +0900350 request.set_cid(cid);
Garrick Evans27b74032019-11-19 13:33:47 +0900351
352 if (!writer.AppendProtoAsArrayOfBytes(request)) {
353 LOG(ERROR) << "Failed to encode TerminaVmShutdownRequest proto";
354 return false;
355 }
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) << "Failed to send dbus message to patchpanel service";
361 return false;
362 }
363
364 dbus::MessageReader reader(dbus_response.get());
365 TerminaVmShutdownResponse response;
366 if (!reader.PopArrayOfBytesAsProto(&response)) {
367 LOG(ERROR) << "Failed to parse response proto";
368 return false;
369 }
370
371 return true;
372}
373
Jie Jiang81c84db2020-09-29 17:40:16 +0900374bool ClientImpl::NotifyPluginVmStartup(uint64_t vm_id,
375 int subnet_index,
376 NetworkDevice* device) {
Garrick Evans376f0672020-01-07 15:31:50 +0900377 dbus::MethodCall method_call(kPatchPanelInterface, kPluginVmStartupMethod);
378 dbus::MessageWriter writer(&method_call);
379
380 PluginVmStartupRequest request;
381 request.set_id(vm_id);
382 request.set_subnet_index(subnet_index);
383
384 if (!writer.AppendProtoAsArrayOfBytes(request)) {
385 LOG(ERROR) << "Failed to encode PluginVmStartupRequest proto";
386 return false;
387 }
388
389 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
390 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
391 if (!dbus_response) {
392 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
393 return false;
394 }
395
396 dbus::MessageReader reader(dbus_response.get());
397 PluginVmStartupResponse response;
398 if (!reader.PopArrayOfBytesAsProto(&response)) {
399 LOG(ERROR) << "Failed to parse response proto";
400 return false;
401 }
402
403 if (!response.has_device()) {
404 LOG(ERROR) << "No device found";
405 return false;
406 }
407 *device = response.device();
408
409 return true;
410}
411
Jie Jiang81c84db2020-09-29 17:40:16 +0900412bool ClientImpl::NotifyPluginVmShutdown(uint64_t vm_id) {
Garrick Evans376f0672020-01-07 15:31:50 +0900413 dbus::MethodCall method_call(kPatchPanelInterface, kPluginVmShutdownMethod);
414 dbus::MessageWriter writer(&method_call);
415
416 PluginVmShutdownRequest request;
417 request.set_id(vm_id);
418
419 if (!writer.AppendProtoAsArrayOfBytes(request)) {
420 LOG(ERROR) << "Failed to encode PluginVmShutdownRequest proto";
421 return false;
422 }
423
424 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
425 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
426 if (!dbus_response) {
427 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
428 return false;
429 }
430
431 dbus::MessageReader reader(dbus_response.get());
Garrick Evans9751a1e2020-02-20 11:02:10 +0900432 PluginVmShutdownResponse response;
Garrick Evans376f0672020-01-07 15:31:50 +0900433 if (!reader.PopArrayOfBytesAsProto(&response)) {
434 LOG(ERROR) << "Failed to parse response proto";
435 return false;
436 }
437
438 return true;
439}
440
Jie Jiang81c84db2020-09-29 17:40:16 +0900441bool ClientImpl::DefaultVpnRouting(int socket) {
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900442 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::DEFAULT_ROUTING);
443}
444
Jie Jiang81c84db2020-09-29 17:40:16 +0900445bool ClientImpl::RouteOnVpn(int socket) {
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900446 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::ROUTE_ON_VPN);
447}
448
Jie Jiang81c84db2020-09-29 17:40:16 +0900449bool ClientImpl::BypassVpn(int socket) {
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900450 return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::BYPASS_VPN);
451}
452
Jie Jiang81c84db2020-09-29 17:40:16 +0900453bool ClientImpl::SendSetVpnIntentRequest(
Hugo Benichi7d9d8db2020-03-30 15:56:56 +0900454 int socket, SetVpnIntentRequest::VpnRoutingPolicy policy) {
455 dbus::MethodCall method_call(kPatchPanelInterface, kSetVpnIntentMethod);
456 dbus::MessageWriter writer(&method_call);
457
458 SetVpnIntentRequest request;
459 SetVpnIntentResponse response;
460 request.set_policy(policy);
461
462 if (!writer.AppendProtoAsArrayOfBytes(request)) {
463 LOG(ERROR) << "Failed to encode SetVpnIntentRequest proto";
464 return false;
465 }
466 writer.AppendFileDescriptor(socket);
467
468 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
469 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
470 if (!dbus_response) {
471 LOG(ERROR)
472 << "Failed to send SetVpnIntentRequest message to patchpanel service";
473 return false;
474 }
475
476 dbus::MessageReader reader(dbus_response.get());
477 if (!reader.PopArrayOfBytesAsProto(&response)) {
478 LOG(ERROR) << "Failed to parse SetVpnIntentResponse proto";
479 return false;
480 }
481
482 if (!response.success()) {
483 LOG(ERROR) << "SetVpnIntentRequest failed";
484 return false;
485 }
486 return true;
487}
488
Hugo Benichicc6850f2020-01-17 13:26:06 +0900489std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse>
Jie Jiang81c84db2020-09-29 17:40:16 +0900490ClientImpl::ConnectNamespace(pid_t pid,
491 const std::string& outbound_ifname,
Garrick Evans58697022020-12-03 12:41:13 +0900492 bool forward_user_traffic,
493 bool route_on_vpn,
494 TrafficCounter::Source traffic_source) {
Hugo Benichicc6850f2020-01-17 13:26:06 +0900495 // Prepare and serialize the request proto.
496 ConnectNamespaceRequest request;
497 request.set_pid(static_cast<int32_t>(pid));
498 request.set_outbound_physical_device(outbound_ifname);
499 request.set_allow_user_traffic(forward_user_traffic);
Garrick Evans58697022020-12-03 12:41:13 +0900500 request.set_route_on_vpn(route_on_vpn);
501 request.set_traffic_source(traffic_source);
Hugo Benichicc6850f2020-01-17 13:26:06 +0900502
503 dbus::MethodCall method_call(kPatchPanelInterface, kConnectNamespaceMethod);
504 dbus::MessageWriter writer(&method_call);
505 if (!writer.AppendProtoAsArrayOfBytes(request)) {
506 LOG(ERROR) << "Failed to encode ConnectNamespaceRequest proto";
507 return {};
508 }
509
510 // Prepare an fd pair and append one fd directly after the serialized request.
511 int pipe_fds[2] = {-1, -1};
512 if (pipe2(pipe_fds, O_CLOEXEC) < 0) {
513 PLOG(ERROR) << "Failed to create a pair of fds with pipe2()";
514 return {};
515 }
516 base::ScopedFD fd_local(pipe_fds[0]);
517 // MessageWriter::AppendFileDescriptor duplicates the fd, so use ScopeFD to
518 // make sure the original fd is closed eventually.
519 base::ScopedFD fd_remote(pipe_fds[1]);
520 writer.AppendFileDescriptor(pipe_fds[1]);
521
522 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
523 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
524 if (!dbus_response) {
525 LOG(ERROR) << "Failed to send ConnectNamespace message to patchpanel";
526 return {};
527 }
528
529 dbus::MessageReader reader(dbus_response.get());
530 ConnectNamespaceResponse response;
531 if (!reader.PopArrayOfBytesAsProto(&response)) {
532 LOG(ERROR) << "Failed to parse ConnectNamespaceResponse proto";
533 return {};
534 }
535
Hugo Benichi2fd0c6e2020-04-17 16:12:05 +0900536 if (response.peer_ifname().empty() || response.host_ifname().empty()) {
Hugo Benichicc6850f2020-01-17 13:26:06 +0900537 LOG(ERROR) << "ConnectNamespace for netns pid " << pid << " failed";
538 return {};
539 }
540
Garrick Evans3388a032020-03-24 11:25:55 +0900541 std::string subnet_info = IPv4AddressToCidrString(
Hugo Benichicc6850f2020-01-17 13:26:06 +0900542 response.ipv4_subnet().base_addr(), response.ipv4_subnet().prefix_len());
543 LOG(INFO) << "ConnectNamespace for netns pid " << pid
Hugo Benichi2fd0c6e2020-04-17 16:12:05 +0900544 << " succeeded: peer_ifname=" << response.peer_ifname()
545 << " peer_ipv4_address="
546 << IPv4AddressToString(response.peer_ipv4_address())
547 << " host_ifname=" << response.host_ifname()
548 << " host_ipv4_address="
549 << IPv4AddressToString(response.host_ipv4_address())
Hugo Benichicc6850f2020-01-17 13:26:06 +0900550 << " subnet=" << subnet_info;
551
552 return std::make_pair(std::move(fd_local), std::move(response));
553}
554
Jie Jiang81c84db2020-09-29 17:40:16 +0900555void ClientImpl::GetTrafficCounters(const std::set<std::string>& devices,
556 GetTrafficCountersCallback callback) {
Jie Jiange02d1202020-07-27 16:57:04 +0900557 dbus::MethodCall method_call(kPatchPanelInterface, kGetTrafficCountersMethod);
558 dbus::MessageWriter writer(&method_call);
559
560 TrafficCountersRequest request;
561 for (const auto& device : devices) {
562 request.add_devices(device);
563 }
564
565 if (!writer.AppendProtoAsArrayOfBytes(request)) {
566 LOG(ERROR) << "Failed to encode TrafficCountersRequest proto";
Jie Jiang0a70acf2020-10-02 11:57:32 +0900567 std::move(callback).Run({});
568 return;
Jie Jiange02d1202020-07-27 16:57:04 +0900569 }
570
Jie Jiang0a70acf2020-10-02 11:57:32 +0900571 proxy_->CallMethod(
572 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
573 base::BindOnce(&OnGetTrafficCountersDBusResponse, std::move(callback)));
Jie Jiange02d1202020-07-27 16:57:04 +0900574}
575
Jie Jiang81c84db2020-09-29 17:40:16 +0900576bool ClientImpl::ModifyPortRule(ModifyPortRuleRequest::Operation op,
577 ModifyPortRuleRequest::RuleType type,
578 ModifyPortRuleRequest::Protocol proto,
579 const std::string& input_ifname,
580 const std::string& input_dst_ip,
581 uint32_t input_dst_port,
582 const std::string& dst_ip,
583 uint32_t dst_port) {
Jason Jeremy Iman6f1f3e72020-07-06 13:04:03 +0900584 dbus::MethodCall method_call(kPatchPanelInterface, kModifyPortRuleMethod);
585 dbus::MessageWriter writer(&method_call);
586
587 ModifyPortRuleRequest request;
588 ModifyPortRuleResponse response;
589
590 request.set_op(op);
591 request.set_type(type);
592 request.set_proto(proto);
593 request.set_input_ifname(input_ifname);
594 request.set_input_dst_ip(input_dst_ip);
595 request.set_input_dst_port(input_dst_port);
596 request.set_dst_ip(dst_ip);
597 request.set_dst_port(dst_port);
598
599 if (!writer.AppendProtoAsArrayOfBytes(request)) {
600 LOG(ERROR) << "Failed to encode ModifyPortRuleRequest proto " << request;
601 return false;
602 }
603
604 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
605 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
606 if (!dbus_response) {
607 LOG(ERROR)
608 << "Failed to send ModifyPortRuleRequest message to patchpanel service "
609 << request;
610 return false;
611 }
612
613 dbus::MessageReader reader(dbus_response.get());
614 if (!reader.PopArrayOfBytesAsProto(&response)) {
615 LOG(ERROR) << "Failed to parse ModifyPortRuleResponse proto " << request;
616 return false;
617 }
618
619 if (!response.success()) {
620 LOG(ERROR) << "ModifyPortRuleRequest failed " << request;
621 return false;
622 }
623 return true;
624}
625
Garrick Evans9e637982020-11-30 11:59:27 +0900626std::vector<NetworkDevice> ClientImpl::GetDevices() {
627 dbus::MethodCall method_call(kPatchPanelInterface, kGetDevicesMethod);
628 dbus::MessageWriter writer(&method_call);
629
630 GetDevicesRequest request;
631 if (!writer.AppendProtoAsArrayOfBytes(request)) {
632 LOG(ERROR) << "Failed to encode GetDevicesRequest proto";
633 return {};
634 }
635
636 std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
637 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
638 if (!dbus_response) {
639 LOG(ERROR) << "Failed to send dbus message to patchpanel service";
640 return {};
641 }
642
643 dbus::MessageReader reader(dbus_response.get());
644 GetDevicesResponse response;
645 if (!reader.PopArrayOfBytesAsProto(&response)) {
646 LOG(ERROR) << "Failed to parse response proto";
647 return {};
648 }
649
650 std::vector<NetworkDevice> devices;
651 for (const auto& d : response.devices()) {
652 devices.emplace_back(d);
653 }
654 return devices;
655}
656
Garrick Evansf04f0442020-12-01 12:36:44 +0900657void ClientImpl::RegisterNetworkDeviceChangedSignalHandler(
658 NetworkDeviceChangedSignalHandler handler) {
659 proxy_->ConnectToSignal(
660 kPatchPanelInterface, kNetworkDeviceChangedSignal,
661 base::BindRepeating(OnNetworkDeviceChangedSignal, handler),
662 base::BindOnce(OnSignalConnectedCallback));
663}
664
Jie Jiang25c1b972020-11-12 15:42:53 +0900665void ClientImpl::RegisterNeighborReachabilityEventHandler(
666 NeighborReachabilityEventHandler handler) {
Jie Jiange2e4c0b2020-09-16 18:48:43 +0900667 proxy_->ConnectToSignal(
Jie Jiang25c1b972020-11-12 15:42:53 +0900668 kPatchPanelInterface, kNeighborReachabilityEventSignal,
669 base::BindRepeating(OnNeighborReachabilityEventSignal, handler),
Jie Jiange2e4c0b2020-09-16 18:48:43 +0900670 base::BindOnce(OnSignalConnectedCallback));
671}
672
Jie Jiang81c84db2020-09-29 17:40:16 +0900673} // namespace
674
675// static
676std::unique_ptr<Client> Client::New() {
677 dbus::Bus::Options opts;
678 opts.bus_type = dbus::Bus::SYSTEM;
679 scoped_refptr<dbus::Bus> bus(new dbus::Bus(std::move(opts)));
680
681 if (!bus->Connect()) {
682 LOG(ERROR) << "Failed to connect to system bus";
683 return nullptr;
684 }
685
686 dbus::ObjectProxy* proxy = bus->GetObjectProxy(
687 kPatchPanelServiceName, dbus::ObjectPath(kPatchPanelServicePath));
688 if (!proxy) {
689 LOG(ERROR) << "Unable to get dbus proxy for " << kPatchPanelServiceName;
690 return nullptr;
691 }
692
693 return std::make_unique<ClientImpl>(std::move(bus), proxy);
694}
695
696std::unique_ptr<Client> Client::New(const scoped_refptr<dbus::Bus>& bus,
697 dbus::ObjectProxy* proxy) {
698 return std::make_unique<ClientImpl>(std::move(bus), proxy);
699}
700
Garrick Evans08843932019-09-17 14:41:08 +0900701} // namespace patchpanel