blob: 560cc086f3721505a05b2bab7f95b9f1163ca6a4 [file]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <signal.h>
#include <android-base/logging.h>
#include <gflags/gflags.h>
#include <chrono>
#include <memory>
#include <sstream>
#include "common/frontend/socket_vsock_proxy/client.h"
#include "common/frontend/socket_vsock_proxy/server.h"
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/result.h"
#include "common/libs/utils/socket2socket_proxy.h"
#include "common/libs/utils/tee_logging.h"
#include "host/commands/kernel_log_monitor/utils.h"
#ifdef CUTTLEFISH_HOST
#include "host/libs/config/logging.h"
#endif // CUTTLEFISH_HOST
constexpr int TCP_SERVER_START_RETRIES_COUNT = 10;
constexpr std::chrono::milliseconds TCP_SERVER_RETRIES_DELAY(1250);
constexpr std::chrono::seconds TCP_CLIENT_TIMEOUT(1);
constexpr char TRANSPORT_TCP[] = "tcp";
constexpr char TRANSPORT_VSOCK[] = "vsock";
DEFINE_string(label, "socket_vsock_proxy", "Label which is used only for logging. "
"Log messages will look like [label] message");
DEFINE_string(server_type, "", "The type of server to host, `vsock` or `tcp`.");
DEFINE_string(client_type, "", "The type of server to host, `vsock` or `tcp`.");
DEFINE_uint32(server_tcp_port, 0, "Server TCP port");
DEFINE_string(client_tcp_host, "localhost", "Client TCP host (default localhost)");
DEFINE_uint32(client_tcp_port, 0, "Client TCP port");
DEFINE_uint32(server_vsock_port, 0, "vsock port");
DEFINE_uint32(server_vsock_id, 0, "Vsock cid which server listens to");
DEFINE_uint32(client_vsock_id, 0, "Vsock cid to initiate connections to");
DEFINE_uint32(client_vsock_port, 0, "Vsock port to initiate connections to");
DEFINE_int32(server_fd, -1, "A file descriptor. If set the passed file descriptor will be used as "
"the server and the corresponding port flag will be ignored");
DEFINE_uint32(events_fd, -1, "A file descriptor. If set it will listen for the events "
"to start / stop proxying. This option can be used only "
"if start_event_id is provided (stop_event_id is optional)");
DEFINE_uint32(start_event_id, -1, "Kernel event id (cuttlefish::monitor::Event from "
"kernel_log_server.h) that we will listen to start proxy");
DEFINE_uint32(stop_event_id, -1, "Kernel event id (cuttlefish::monitor::Event from "
"kernel_log_server.h) that we will listen to stop proxy");
DEFINE_bool(start_immediately, false, "A flag to start proxying without waiting for "
"initial start event");
DEFINE_bool(vhost_user_vsock, false, "A flag to user vhost_user_vsock");
namespace cuttlefish {
namespace socket_proxy {
namespace {
static bool use_vhost_vsock() {
#ifdef CUTTLEFISH_HOST
return FLAGS_vhost_user_vsock;
#else
return false;
#endif
}
static std::unique_ptr<Server> BuildServer() {
if (FLAGS_server_fd >= 0) {
return std::make_unique<DupServer>(FLAGS_server_fd);
}
CHECK(FLAGS_server_type == TRANSPORT_TCP || FLAGS_server_type == TRANSPORT_VSOCK)
<< "Must specify -server_type with tcp or vsock values";
if (FLAGS_server_type == TRANSPORT_TCP) {
CHECK(FLAGS_server_tcp_port != 0)
<< "Must specify -server_tcp_port or -server_fd with -server_type=tcp flag";
}
if (FLAGS_server_type == TRANSPORT_VSOCK) {
CHECK(FLAGS_server_vsock_port != 0)
<< "Must specify -server_vsock_port or -server_fd with -server_type=vsock flag";
if (use_vhost_vsock()) {
CHECK(FLAGS_server_vsock_id > VMADDR_CID_HOST)
<< "Must specify --server_vsock_id with --vhost_user_vsock=true flag";
}
}
std::unique_ptr<Server> server = nullptr;
if (FLAGS_server_type == TRANSPORT_TCP) {
server = std::make_unique<TcpServer>(FLAGS_server_tcp_port, TCP_SERVER_START_RETRIES_COUNT,
TCP_SERVER_RETRIES_DELAY);
} else if (FLAGS_server_type == TRANSPORT_VSOCK) {
server = std::make_unique<VsockServer>(
FLAGS_server_vsock_port, use_vhost_vsock()
? std::make_optional(FLAGS_server_vsock_id)
: std::nullopt);
} else {
LOG(FATAL) << "Unknown server type: " << FLAGS_server_type;
}
return server;
}
static std::unique_ptr<Client> BuildClient() {
CHECK(FLAGS_client_type == TRANSPORT_TCP || FLAGS_client_type == TRANSPORT_VSOCK)
<< "Must specify -client_type with tcp or vsock values";
if (FLAGS_client_type == TRANSPORT_TCP) {
CHECK(FLAGS_client_tcp_port != 0)
<< "For -client_type=tcp you must specify -client_tcp_port flag";
}
if (FLAGS_client_type == TRANSPORT_VSOCK) {
CHECK(FLAGS_client_vsock_id >= 0 && FLAGS_client_vsock_port >= 0)
<< "For -client_type=vsock you must specify -client_vsock_id and -client_vsock_port flags";
}
std::unique_ptr<Client> client = nullptr;
if (FLAGS_client_type == TRANSPORT_TCP) {
client = std::make_unique<TcpClient>(FLAGS_client_tcp_host, FLAGS_client_tcp_port,
TCP_CLIENT_TIMEOUT);
} else if (FLAGS_client_type == TRANSPORT_VSOCK) {
client = std::make_unique<VsockClient>(
FLAGS_client_vsock_id, FLAGS_client_vsock_port, use_vhost_vsock());
} else {
LOG(FATAL) << "Unknown client type: " << FLAGS_client_type;
}
return client;
}
static Result<std::unique_ptr<ProxyServer>> StartProxyAsync(Server& server, Client& client) {
LOG(INFO) << "From: " << server.Describe();
LOG(INFO) << "To: " << client.Describe();
return ProxyAsync(CF_EXPECT(server.Start()), [&client] { return client.Start(); });
}
static Result<void> ListenEventsAndProxy(int events_fd,
const monitor::Event start, const monitor::Event stop,
Server& server, Client& client) {
auto events = SharedFD::Dup(events_fd);
close(events_fd);
std::unique_ptr<ProxyServer> proxy;
if (FLAGS_start_immediately) {
LOG(INFO) << "start_immediately was set to true, so starting proxying";
proxy = std::move(CF_EXPECT(StartProxyAsync(server, client)));
}
LOG(DEBUG) << "Start reading events to start/stop proxying";
while (events->IsOpen()) {
std::optional<monitor::ReadEventResult> received_event = monitor::ReadEvent(events);
if (!received_event) {
LOG(ERROR) << "Failed to read a complete kernel log event";
continue;
}
if (start != -1 && received_event->event == start) {
if (!proxy) {
LOG(INFO) << "Start event (" << start << ") received. Starting proxy";
proxy = std::move(CF_EXPECT(StartProxyAsync(server, client)));
}
continue;
}
if (stop != -1 && received_event->event == stop) {
LOG(INFO) << "Stop event (" << start << ") received. Stopping proxy";
proxy.reset();
continue;
}
}
return {};
}
Result<void> Main() {
auto server = BuildServer();
auto client = BuildClient();
if (FLAGS_events_fd != -1) {
CF_EXPECT(FLAGS_start_event_id != -1, "start_event_id is required if events_fd is provided");
const monitor::Event start_event = static_cast<monitor::Event>(FLAGS_start_event_id);
const monitor::Event stop_event = static_cast<monitor::Event>(FLAGS_stop_event_id);
CF_EXPECT(ListenEventsAndProxy(FLAGS_events_fd, start_event, stop_event,
*server, *client));
} else {
LOG(DEBUG) << "Starting proxy";
Proxy(CF_EXPECT(server->Start()), [&client] { return client->Start(); });
}
return {};
}
}
}
}
int main(int argc, char* argv[]) {
signal(SIGPIPE, SIG_IGN);
#ifdef CUTTLEFISH_HOST
cuttlefish::DefaultSubprocessLogging(argv, cuttlefish::MetadataLevel::TAG_AND_MESSAGE);
#else
::android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
#endif
google::ParseCommandLineFlags(&argc, &argv, true);
if (!FLAGS_label.empty()) {
android::base::SetDefaultTag("proxy_" + FLAGS_label);
}
auto result = cuttlefish::socket_proxy::Main();
if (!result.ok()) {
LOG(FATAL) << "Failed to proxy: " << result.error().FormatForEnv();
}
return 0;
}