unplugged-system/packages/modules/Bluetooth/system/gd/hal/snoop_logger_socket.cc

293 lines
8.1 KiB
C++

/******************************************************************************
*
* Copyright (C) 2022 Google, Inc.
*
* 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 "hal/snoop_logger_socket.h"
#include <arpa/inet.h>
#include <base/logging.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <mutex>
#include "common/init_flags.h"
#include "hal/snoop_logger_common.h"
#include "os/handler.h"
#include "os/log.h"
#include "os/thread.h"
#include "os/utils.h"
namespace bluetooth {
namespace hal {
using bluetooth::hal::SnoopLoggerCommon;
static constexpr int INVALID_FD = -1;
constexpr int INCOMING_SOCKET_CONNECTIONS_QUEUE_SIZE_ = 10;
SnoopLoggerSocket::SnoopLoggerSocket(SyscallWrapperInterface* syscall_if, int socket_address, int socket_port)
: syscall_if_(syscall_if),
socket_address_(socket_address),
socket_port_(socket_port),
notification_listen_fd_(-1),
notification_write_fd_(-1),
listen_socket_(-1),
fd_max_(-1),
client_socket_(-1) {
LOG_INFO("address %d port %d", socket_address, socket_port);
}
SnoopLoggerSocket::~SnoopLoggerSocket() {
Cleanup();
}
void SnoopLoggerSocket::Write(int& client_socket, const void* data, size_t length) {
if (client_socket == -1) {
return;
}
ssize_t ret;
RUN_NO_INTR(ret = syscall_if_->Send(client_socket, data, length, MSG_DONTWAIT));
if (ret == -1 && syscall_if_->GetErrno() == ECONNRESET) {
SafeCloseSocket(client_socket);
} else if (ret == -1 && syscall_if_->GetErrno() == EAGAIN) {
LOG_ERROR("Dropping snoop pkts because of congestion");
}
}
void SnoopLoggerSocket::Write(const void* data, size_t length) {
std::lock_guard<std::mutex> lock(client_socket_mutex_);
Write(client_socket_, data, length);
}
int SnoopLoggerSocket::InitializeCommunications() {
int self_pipe_fds[2];
int ret;
fd_max_ = -1;
syscall_if_->FDZero(&save_sock_fds_);
// Set up the communication channel
ret = syscall_if_->Pipe2(self_pipe_fds, O_NONBLOCK | O_CLOEXEC);
if (ret < 0) {
LOG_ERROR("Unable to establish a communication channel to the listen thread.");
return ret;
}
notification_listen_fd_ = self_pipe_fds[0];
notification_write_fd_ = self_pipe_fds[1];
syscall_if_->FDSet(notification_listen_fd_, &save_sock_fds_);
fd_max_ = notification_listen_fd_;
listen_socket_ = CreateSocket();
if (listen_socket_ == INVALID_FD) {
LOG_ERROR("Unable to create a listen socket.");
SafeCloseSocket(notification_listen_fd_);
SafeCloseSocket(notification_write_fd_);
return -1;
}
return 0;
}
bool SnoopLoggerSocket::ProcessIncomingRequest() {
int ret;
fd_set sock_fds = save_sock_fds_;
if ((syscall_if_->Select(fd_max_ + 1, &sock_fds, NULL, NULL, NULL)) == -1) {
LOG_ERROR("%s select failed %s", __func__, strerror(syscall_if_->GetErrno()));
if (syscall_if_->GetErrno() == EINTR) return true;
return false;
}
if ((listen_socket_ != -1) && syscall_if_->FDIsSet(listen_socket_, &sock_fds)) {
int client_socket = -1;
ret = AcceptIncomingConnection(listen_socket_, client_socket);
if (ret != 0) {
// Unrecoverable error, stop the thread.
return false;
}
if (client_socket < 0) {
return true;
}
InitializeClientSocket(client_socket);
ClientSocketConnected(client_socket);
} else if ((notification_listen_fd_ != -1) && syscall_if_->FDIsSet(notification_listen_fd_, &sock_fds)) {
LOG_WARN("exting from listen_fn_ thread ");
return false;
}
return true;
}
void SnoopLoggerSocket::Cleanup() {
SafeCloseSocket(notification_listen_fd_);
SafeCloseSocket(notification_write_fd_);
SafeCloseSocket(client_socket_);
SafeCloseSocket(listen_socket_);
}
int SnoopLoggerSocket::AcceptIncomingConnection(int listen_socket, int& client_socket) {
socklen_t clen;
struct sockaddr_in client_addr;
RUN_NO_INTR(client_socket = syscall_if_->Accept(listen_socket, (struct sockaddr*)&client_addr, &clen, SOCK_CLOEXEC));
if (client_socket == -1) {
int errno_ = syscall_if_->GetErrno();
LOG_WARN("error accepting socket: %s", strerror(errno_));
if (errno_ == EINVAL || errno_ == EBADF) {
return errno_;
}
return 0;
}
LOG_INFO(
"Client socket fd: %d, IP address: %s, port: %d",
client_socket,
inet_ntoa(client_addr.sin_addr),
(int)ntohs(client_addr.sin_port));
return 0;
}
void SnoopLoggerSocket::InitializeClientSocket(int client_socket) {
/* When a new client connects, we have to send the btsnoop file header. This
* allows a decoder to treat the session as a new, valid btsnoop file. */
Write(
client_socket,
reinterpret_cast<const char*>(&SnoopLoggerCommon::kBtSnoopFileHeader),
sizeof(SnoopLoggerCommon::FileHeaderType));
}
void SnoopLoggerSocket::ClientSocketConnected(int client_socket) {
std::lock_guard<std::mutex> lock(client_socket_mutex_);
SafeCloseSocket(client_socket_);
client_socket_ = client_socket;
client_socket_cv_.notify_one();
}
bool SnoopLoggerSocket::WaitForClientSocketConnected() {
std::unique_lock<std::mutex> lk(client_socket_mutex_);
client_socket_cv_.wait(lk, [this] { return IsClientSocketConnected(); });
return IsClientSocketConnected();
}
bool SnoopLoggerSocket::IsClientSocketConnected() const {
return client_socket_ != INVALID_FD;
}
int SnoopLoggerSocket::CreateSocket() {
LOG_DEBUG("");
int ret;
// Create a TCP socket file descriptor
int socket_fd = syscall_if_->Socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
if (socket_fd < 0) {
LOG_ERROR("can't create socket: %s", strerror(syscall_if_->GetErrno()));
return INVALID_FD;
}
syscall_if_->FDSet(socket_fd, &save_sock_fds_);
if (socket_fd > fd_max_) {
fd_max_ = socket_fd;
}
// Enable REUSEADDR
int enable = 1;
ret = syscall_if_->Setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
if (ret < 0) {
LOG_ERROR("unable to set SO_REUSEADDR: %s", strerror(syscall_if_->GetErrno()));
SafeCloseSocket(socket_fd);
return INVALID_FD;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(socket_address_);
addr.sin_port = htons(socket_port_);
// Bind socket to an address
ret = syscall_if_->Bind(socket_fd, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0) {
LOG_ERROR("unable to bind snoop socket to address: %s", strerror(syscall_if_->GetErrno()));
SafeCloseSocket(socket_fd);
return INVALID_FD;
}
// Mark this socket as a socket that will accept connections.
ret = syscall_if_->Listen(socket_fd, INCOMING_SOCKET_CONNECTIONS_QUEUE_SIZE_);
if (ret < 0) {
LOG_ERROR("unable to listen: %s", strerror(syscall_if_->GetErrno()));
SafeCloseSocket(socket_fd);
return INVALID_FD;
}
return socket_fd;
}
int SnoopLoggerSocket::NotifySocketListener() {
LOG_DEBUG("");
char buffer = '0';
int ret = -1;
if (notification_write_fd_ == -1) {
return 0;
}
RUN_NO_INTR(ret = syscall_if_->Write(notification_write_fd_, &buffer, 1));
if (ret < 0) {
LOG_ERROR("Error in notifying the listen thread to exit (%d)", ret);
return -1;
}
return 0;
}
void SnoopLoggerSocket::SafeCloseSocket(int& fd) {
LOG_DEBUG("%d", (fd));
if (fd != -1) {
syscall_if_->Close(fd);
syscall_if_->FDClr(fd, &save_sock_fds_);
fd = -1;
}
}
SyscallWrapperInterface* SnoopLoggerSocket::GetSyscallWrapperInterface() const {
return syscall_if_;
}
} // namespace hal
} // namespace bluetooth