arc: Move platform2/arc/network/ to platform2/patchpanel

Next step in the arc-networkd -> patchpanel rename, this patch moves the
location of the code.

BUG=b:151879931
TEST=units,flashed image to atlas
TEST=tasts arc.PlayStore, crostini.LaunchTerminal.download

Change-Id: I1b5cf8d670e1631d46f6449b725395157bf88dde
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2115863
Tested-by: Garrick Evans <garrick@chromium.org>
Commit-Queue: Garrick Evans <garrick@chromium.org>
Reviewed-by: Hidehiko Abe <hidehiko@chromium.org>
Reviewed-by: Eric Caruso <ejcaruso@chromium.org>
Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
Reviewed-by: Hugo Benichi <hugobenichi@google.com>
diff --git a/patchpanel/client.cc b/patchpanel/client.cc
new file mode 100644
index 0000000..d6de7d5
--- /dev/null
+++ b/patchpanel/client.cc
@@ -0,0 +1,406 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "patchpanel/client.h"
+
+#include <fcntl.h>
+
+#include <base/logging.h>
+#include <chromeos/dbus/service_constants.h>
+#include <dbus/message.h>
+#include <dbus/object_path.h>
+
+#include "patchpanel/net_util.h"
+
+namespace patchpanel {
+
+// static
+std::unique_ptr<Client> Client::New() {
+  dbus::Bus::Options opts;
+  opts.bus_type = dbus::Bus::SYSTEM;
+  scoped_refptr<dbus::Bus> bus(new dbus::Bus(std::move(opts)));
+
+  if (!bus->Connect()) {
+    LOG(ERROR) << "Failed to connect to system bus";
+    return nullptr;
+  }
+
+  dbus::ObjectProxy* proxy = bus->GetObjectProxy(
+      kPatchPanelServiceName, dbus::ObjectPath(kPatchPanelServicePath));
+  if (!proxy) {
+    LOG(ERROR) << "Unable to get dbus proxy for " << kPatchPanelServiceName;
+    return nullptr;
+  }
+
+  return std::make_unique<Client>(std::move(bus), proxy);
+}
+
+Client::~Client() {
+  if (bus_)
+    bus_->ShutdownAndBlock();
+}
+
+bool Client::NotifyArcStartup(pid_t pid) {
+  dbus::MethodCall method_call(kPatchPanelInterface, kArcStartupMethod);
+  dbus::MessageWriter writer(&method_call);
+
+  ArcStartupRequest request;
+  request.set_pid(static_cast<uint32_t>(pid));
+
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode ArcStartupRequest proto";
+    return false;
+  }
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR) << "Failed to send dbus message to patchpanel service";
+    return false;
+  }
+
+  dbus::MessageReader reader(dbus_response.get());
+  ArcStartupResponse response;
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse response proto";
+    return false;
+  }
+
+  return true;
+}
+
+bool Client::NotifyArcShutdown() {
+  dbus::MethodCall method_call(kPatchPanelInterface, kArcShutdownMethod);
+  dbus::MessageWriter writer(&method_call);
+
+  ArcShutdownRequest request;
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode ArcShutdownRequest proto";
+    return false;
+  }
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR) << "Failed to send dbus message to patchpanel service";
+    return false;
+  }
+
+  dbus::MessageReader reader(dbus_response.get());
+  ArcShutdownResponse response;
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse response proto";
+    return false;
+  }
+
+  return true;
+}
+
+std::vector<NetworkDevice> Client::NotifyArcVmStartup(uint32_t cid) {
+  dbus::MethodCall method_call(kPatchPanelInterface, kArcVmStartupMethod);
+  dbus::MessageWriter writer(&method_call);
+
+  ArcVmStartupRequest request;
+  request.set_cid(cid);
+
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode ArcVmStartupRequest proto";
+    return {};
+  }
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR) << "Failed to send dbus message to patchpanel service";
+    return {};
+  }
+
+  dbus::MessageReader reader(dbus_response.get());
+  ArcVmStartupResponse response;
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse response proto";
+    return {};
+  }
+
+  std::vector<NetworkDevice> devices;
+  for (const auto& d : response.devices()) {
+    devices.emplace_back(d);
+  }
+  return devices;
+}
+
+bool Client::NotifyArcVmShutdown(uint32_t cid) {
+  dbus::MethodCall method_call(kPatchPanelInterface, kArcVmShutdownMethod);
+  dbus::MessageWriter writer(&method_call);
+
+  ArcVmShutdownRequest request;
+  request.set_cid(cid);
+
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode ArcVmShutdownRequest proto";
+    return false;
+  }
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR) << "Failed to send dbus message to patchpanel service";
+    return false;
+  }
+
+  dbus::MessageReader reader(dbus_response.get());
+  ArcVmShutdownResponse response;
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse response proto";
+    return false;
+  }
+
+  return true;
+}
+
+bool Client::NotifyTerminaVmStartup(uint32_t cid,
+                                    NetworkDevice* device,
+                                    IPv4Subnet* container_subnet) {
+  dbus::MethodCall method_call(kPatchPanelInterface, kTerminaVmStartupMethod);
+  dbus::MessageWriter writer(&method_call);
+
+  TerminaVmStartupRequest request;
+  request.set_cid(cid);
+
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode TerminaVmStartupRequest proto";
+    return false;
+  }
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR) << "Failed to send dbus message to patchpanel service";
+    return false;
+  }
+
+  dbus::MessageReader reader(dbus_response.get());
+  TerminaVmStartupResponse response;
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse response proto";
+    return false;
+  }
+
+  if (!response.has_device()) {
+    LOG(ERROR) << "No device found";
+    return false;
+  }
+  *device = response.device();
+
+  if (response.has_container_subnet()) {
+    *container_subnet = response.container_subnet();
+  } else {
+    LOG(WARNING) << "No container subnet found";
+  }
+
+  return true;
+}
+
+bool Client::NotifyTerminaVmShutdown(uint32_t cid) {
+  dbus::MethodCall method_call(kPatchPanelInterface, kTerminaVmShutdownMethod);
+  dbus::MessageWriter writer(&method_call);
+
+  TerminaVmShutdownRequest request;
+  request.set_cid(cid);
+
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode TerminaVmShutdownRequest proto";
+    return false;
+  }
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR) << "Failed to send dbus message to patchpanel service";
+    return false;
+  }
+
+  dbus::MessageReader reader(dbus_response.get());
+  TerminaVmShutdownResponse response;
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse response proto";
+    return false;
+  }
+
+  return true;
+}
+
+bool Client::NotifyPluginVmStartup(uint64_t vm_id,
+                                   int subnet_index,
+                                   NetworkDevice* device) {
+  dbus::MethodCall method_call(kPatchPanelInterface, kPluginVmStartupMethod);
+  dbus::MessageWriter writer(&method_call);
+
+  PluginVmStartupRequest request;
+  request.set_id(vm_id);
+  request.set_subnet_index(subnet_index);
+
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode PluginVmStartupRequest proto";
+    return false;
+  }
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR) << "Failed to send dbus message to patchpanel service";
+    return false;
+  }
+
+  dbus::MessageReader reader(dbus_response.get());
+  PluginVmStartupResponse response;
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse response proto";
+    return false;
+  }
+
+  if (!response.has_device()) {
+    LOG(ERROR) << "No device found";
+    return false;
+  }
+  *device = response.device();
+
+  return true;
+}
+
+bool Client::NotifyPluginVmShutdown(uint64_t vm_id) {
+  dbus::MethodCall method_call(kPatchPanelInterface, kPluginVmShutdownMethod);
+  dbus::MessageWriter writer(&method_call);
+
+  PluginVmShutdownRequest request;
+  request.set_id(vm_id);
+
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode PluginVmShutdownRequest proto";
+    return false;
+  }
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR) << "Failed to send dbus message to patchpanel service";
+    return false;
+  }
+
+  dbus::MessageReader reader(dbus_response.get());
+  PluginVmShutdownResponse response;
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse response proto";
+    return false;
+  }
+
+  return true;
+}
+
+bool Client::DefaultVpnRouting(int socket) {
+  return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::DEFAULT_ROUTING);
+}
+
+bool Client::RouteOnVpn(int socket) {
+  return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::ROUTE_ON_VPN);
+}
+
+bool Client::BypassVpn(int socket) {
+  return SendSetVpnIntentRequest(socket, SetVpnIntentRequest::BYPASS_VPN);
+}
+
+bool Client::SendSetVpnIntentRequest(
+    int socket, SetVpnIntentRequest::VpnRoutingPolicy policy) {
+  dbus::MethodCall method_call(kPatchPanelInterface, kSetVpnIntentMethod);
+  dbus::MessageWriter writer(&method_call);
+
+  SetVpnIntentRequest request;
+  SetVpnIntentResponse response;
+  request.set_policy(policy);
+
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode SetVpnIntentRequest proto";
+    return false;
+  }
+  writer.AppendFileDescriptor(socket);
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR)
+        << "Failed to send SetVpnIntentRequest message to patchpanel service";
+    return false;
+  }
+
+  dbus::MessageReader reader(dbus_response.get());
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse SetVpnIntentResponse proto";
+    return false;
+  }
+
+  if (!response.success()) {
+    LOG(ERROR) << "SetVpnIntentRequest failed";
+    return false;
+  }
+  return true;
+}
+
+std::pair<base::ScopedFD, patchpanel::ConnectNamespaceResponse>
+Client::ConnectNamespace(pid_t pid,
+                         const std::string& outbound_ifname,
+                         bool forward_user_traffic) {
+  // Prepare and serialize the request proto.
+  ConnectNamespaceRequest request;
+  request.set_pid(static_cast<int32_t>(pid));
+  request.set_outbound_physical_device(outbound_ifname);
+  request.set_allow_user_traffic(forward_user_traffic);
+
+  dbus::MethodCall method_call(kPatchPanelInterface, kConnectNamespaceMethod);
+  dbus::MessageWriter writer(&method_call);
+  if (!writer.AppendProtoAsArrayOfBytes(request)) {
+    LOG(ERROR) << "Failed to encode ConnectNamespaceRequest proto";
+    return {};
+  }
+
+  // Prepare an fd pair and append one fd directly after the serialized request.
+  int pipe_fds[2] = {-1, -1};
+  if (pipe2(pipe_fds, O_CLOEXEC) < 0) {
+    PLOG(ERROR) << "Failed to create a pair of fds with pipe2()";
+    return {};
+  }
+  base::ScopedFD fd_local(pipe_fds[0]);
+  // MessageWriter::AppendFileDescriptor duplicates the fd, so use ScopeFD to
+  // make sure the original fd is closed eventually.
+  base::ScopedFD fd_remote(pipe_fds[1]);
+  writer.AppendFileDescriptor(pipe_fds[1]);
+
+  std::unique_ptr<dbus::Response> dbus_response = proxy_->CallMethodAndBlock(
+      &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
+  if (!dbus_response) {
+    LOG(ERROR) << "Failed to send ConnectNamespace message to patchpanel";
+    return {};
+  }
+
+  dbus::MessageReader reader(dbus_response.get());
+  ConnectNamespaceResponse response;
+  if (!reader.PopArrayOfBytesAsProto(&response)) {
+    LOG(ERROR) << "Failed to parse ConnectNamespaceResponse proto";
+    return {};
+  }
+
+  if (response.ifname().empty()) {
+    LOG(ERROR) << "ConnectNamespace for netns pid " << pid << " failed";
+    return {};
+  }
+
+  std::string subnet_info = IPv4AddressToCidrString(
+      response.ipv4_subnet().base_addr(), response.ipv4_subnet().prefix_len());
+  LOG(INFO) << "ConnectNamespace for netns pid " << pid
+            << " succeeded: veth=" << response.ifname()
+            << " subnet=" << subnet_info;
+
+  return std::make_pair(std::move(fd_local), std::move(response));
+}
+
+}  // namespace patchpanel