blob: b094399d7aa641985c039c6afc105f3a6391d6fb [file] [log] [blame]
// Copyright 2018 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 "arc/vm/vsock_proxy/client_proxy.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
// Needs to be included after sys/socket.h
#include <linux/vm_sockets.h>
#include <utility>
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/logging.h>
#include <base/posix/eintr_wrapper.h>
#include "arc/vm/vsock_proxy/file_descriptor_util.h"
#include "arc/vm/vsock_proxy/message.pb.h"
#include "arc/vm/vsock_proxy/vsock_proxy.h"
namespace arc {
namespace {
// Path to the socket file for ArcBridgeService.
constexpr char kGuestSocketPath[] = "/var/run/chrome/arc_bridge.sock";
// Port for VSOCK.
constexpr unsigned int kVSockPort = 9999;
base::ScopedFD ConnectVSock() {
LOG(INFO) << "Creating VSOCK...";
struct sockaddr_vm sa = {};
sa.svm_family = AF_VSOCK;
sa.svm_cid = VMADDR_CID_HOST;
sa.svm_port = kVSockPort;
// TODO(hidehiko): Consider to time out.
while (true) {
base::ScopedFD fd(
socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0 /* protocol */));
if (!fd.is_valid()) {
PLOG(ERROR) << "Failed to create VSOCK";
return {};
}
LOG(INFO) << "Connecting VSOCK";
if (HANDLE_EINTR(connect(fd.get(),
reinterpret_cast<const struct sockaddr*>(&sa),
sizeof(sa))) == -1) {
fd.reset();
PLOG(ERROR) << "Failed to connect. Waiting and then retry...";
sleep(1); // Arbitrary wait.
continue;
}
LOG(INFO) << "VSOCK created.";
return fd;
}
}
} // namespace
ClientProxy::ClientProxy() = default;
ClientProxy::~ClientProxy() = default;
void ClientProxy::Initialize() {
// TODO(hidehiko): Rework on the initial socket creation protocol.
// At the moment:
// 1) Chrome creates a socket at /run/chrome/arc_bridge.sock (in host).
// 2) Starts ARCVM, then start arcbridgeservice in the guest OS.
// 3) ClientProxy in arcbridgeservice creates yet another socket at
// /var/run/chrome/arc_bridge.sock (in guest).
// 4) ArcBridgeService in arcbridgeservice connects to
// /var/run/chrome/arc_bridge.sock, so
// ClientProxy::onLocalSocketReadReady() is called.
// 5) Asynchronously, host side proxy should start, and listens VSOCK.
// 6) ClientProxy connects to VSOCK (this is blocking operation.
// Waiting for 5)).
// 7) Accepts the /var/run/chrome/arc_bridge.sock connection request
// (in guest). The handle is reserved with "1".
// 8) In parallel with 6), host proxy connects to
// /run/chrome/arc_bridge.sock.
//
// Plan:
// The main change is the connection order. Current protocol uses VSOCK
// connection as a kind of a message to notify host that there is incoming
// connection request to /var/run/chrome/arc_bridge.sock. Instead, in new
// protocol, establish VSOCK at the beginning, and send an explicit
// connection message when needed.
// 1) Chrome creates a socket at /run/chrome/arc_bridge.sock (in host).
// 2) Start ARCVM, then starts host proxy in host OS.
// 3) Host proxy prepares VSOCK and listens it.
// 4) ClientProxy (this class) in arcbridgeservice connects to VSOCK,
// and initializes VSockProxy.
// 5) Creates /var/run/chrome/arc_bridge.sock in guest.
// 6) ArcBridgeService in arcbridgeservice connects to the guest
// arc_bridge.sock.
// 7) VSockProxy is notified, so send a message that a connection comes
// to host via VSOCK.
// 8) Host proxy connects to /run/chrome/arc_bridge.sock, then
// ArcBridge connection between ARCVM and host is established.
arc_bridge_socket_ = CreateUnixDomainSocket(base::FilePath(kGuestSocketPath));
LOG(INFO) << "Start observing " << kGuestSocketPath;
arc_bridge_socket_controller_ = base::FileDescriptorWatcher::WatchReadable(
arc_bridge_socket_.get(),
base::BindRepeating(&ClientProxy::OnLocalSocketReadReady,
weak_factory_.GetWeakPtr()));
}
void ClientProxy::OnLocalSocketReadReady() {
LOG(INFO) << "Initial socket connection comes";
arc_bridge_socket_controller_.reset();
vsock_proxy_ =
std::make_unique<VSockProxy>(VSockProxy::Type::CLIENT, ConnectVSock());
// 1 is reserved for the initial socket handle.
constexpr uint64_t kInitialSocketHandle = 1;
vsock_proxy_->RegisterFileDescriptor(AcceptSocket(arc_bridge_socket_.get()),
arc_proxy::FileDescriptor::SOCKET,
kInitialSocketHandle);
LOG(INFO) << "ClientProxy has started to work.";
}
} // namespace arc