libbinder: vsock support for RPC
This adds support for binder RPC to be used with virtual sockets.
One decision made here - should RpcConnection.h take a sockaddr object,
or not? I've chosen not to expose sockaddr directly because it is a
low-level type (specifically for unix domain sockets, where the path may
not be null-terminated).
Fixes: 182938272
Test: binderRpcTest
Change-Id: I29bbbd2745f71d8ed7d4b8f8273fce28de0a9779
diff --git a/libs/binder/RpcConnection.cpp b/libs/binder/RpcConnection.cpp
index 83a1618..dab3246 100644
--- a/libs/binder/RpcConnection.cpp
+++ b/libs/binder/RpcConnection.cpp
@@ -20,6 +20,7 @@
#include <binder/Parcel.h>
#include <binder/Stability.h>
+#include <utils/String8.h>
#include "RpcState.h"
#include "RpcWireFormat.h"
@@ -29,14 +30,20 @@
#include <sys/un.h>
#include <unistd.h>
-#if defined(__GLIBC__)
+#ifdef __GLIBC__
extern "C" pid_t gettid();
#endif
+#ifdef __BIONIC__
+#include <linux/vm_sockets.h>
+#endif
+
namespace android {
using base::unique_fd;
+RpcConnection::SocketAddress::~SocketAddress() {}
+
RpcConnection::RpcConnection() {
LOG_RPC_DETAIL("RpcConnection created %p", this);
@@ -50,65 +57,68 @@
return new RpcConnection;
}
+class UnixSocketAddress : public RpcConnection::SocketAddress {
+public:
+ explicit UnixSocketAddress(const char* path) : mAddr({.sun_family = AF_UNIX}) {
+ unsigned int pathLen = strlen(path) + 1;
+ LOG_ALWAYS_FATAL_IF(pathLen > sizeof(mAddr.sun_path), "%u %s", pathLen, path);
+ memcpy(mAddr.sun_path, path, pathLen);
+ }
+ virtual ~UnixSocketAddress() {}
+ std::string toString() const override {
+ return String8::format("path '%.*s'", static_cast<int>(sizeof(mAddr.sun_path)),
+ mAddr.sun_path)
+ .c_str();
+ }
+ const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+ size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+ sockaddr_un mAddr;
+};
+
bool RpcConnection::setupUnixDomainServer(const char* path) {
- LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Only supports one server now");
-
- unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
- if (serverFd == -1) {
- ALOGE("Could not create socket at %s: %s", path, strerror(errno));
- return false;
- }
-
- struct sockaddr_un addr = {
- .sun_family = AF_UNIX,
- };
-
- unsigned int pathLen = strlen(path) + 1;
- LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
- memcpy(addr.sun_path, path, pathLen);
-
- if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
- ALOGE("Could not bind socket at %s: %s", path, strerror(errno));
- return false;
- }
-
- if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
- ALOGE("Could not listen socket at %s: %s", path, strerror(errno));
- return false;
- }
-
- mServer = std::move(serverFd);
- return true;
+ return addServer(UnixSocketAddress(path));
}
bool RpcConnection::addUnixDomainClient(const char* path) {
- LOG_RPC_DETAIL("Connecting on path: %s", path);
-
- unique_fd serverFd(TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)));
- if (serverFd == -1) {
- ALOGE("Could not create socket at %s: %s", path, strerror(errno));
- return false;
- }
-
- struct sockaddr_un addr = {
- .sun_family = AF_UNIX,
- };
-
- unsigned int pathLen = strlen(path) + 1;
- LOG_ALWAYS_FATAL_IF(pathLen > sizeof(addr.sun_path), "%u", pathLen);
- memcpy(addr.sun_path, path, pathLen);
-
- if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), (struct sockaddr*)&addr, sizeof(addr)))) {
- ALOGE("Could not connect socket at %s: %s", path, strerror(errno));
- return false;
- }
-
- LOG_RPC_DETAIL("Unix domain client with fd %d", serverFd.get());
-
- addClient(std::move(serverFd));
- return true;
+ return addClient(UnixSocketAddress(path));
}
+#ifdef __BIONIC__
+
+class VsockSocketAddress : public RpcConnection::SocketAddress {
+public:
+ VsockSocketAddress(unsigned int cid, unsigned int port)
+ : mAddr({
+ .svm_family = AF_VSOCK,
+ .svm_port = port,
+ .svm_cid = cid,
+ }) {}
+ virtual ~VsockSocketAddress() {}
+ std::string toString() const override {
+ return String8::format("cid %du port %du", mAddr.svm_cid, mAddr.svm_port).c_str();
+ }
+ const sockaddr* addr() const override { return reinterpret_cast<const sockaddr*>(&mAddr); }
+ size_t addrSize() const override { return sizeof(mAddr); }
+
+private:
+ sockaddr_vm mAddr;
+};
+
+bool RpcConnection::setupVsockServer(unsigned int port) {
+ // realizing value w/ this type at compile time to avoid ubsan abort
+ constexpr unsigned int kAnyCid = VMADDR_CID_ANY;
+
+ return addServer(VsockSocketAddress(kAnyCid, port));
+}
+
+bool RpcConnection::addVsockClient(unsigned int cid, unsigned int port) {
+ return addClient(VsockSocketAddress(cid, port));
+}
+
+#endif // __BIONIC__
+
sp<IBinder> RpcConnection::getRootObject() {
ExclusiveSocket socket(this, SocketUse::CLIENT);
return state()->getRootObject(socket.fd(), this);
@@ -130,11 +140,8 @@
void RpcConnection::join() {
// establish a connection
{
- struct sockaddr_un clientSa;
- socklen_t clientSaLen = sizeof(clientSa);
-
- unique_fd clientFd(TEMP_FAILURE_RETRY(
- accept4(mServer.get(), (struct sockaddr*)&clientSa, &clientSaLen, SOCK_CLOEXEC)));
+ unique_fd clientFd(
+ TEMP_FAILURE_RETRY(accept4(mServer.get(), nullptr, 0 /*length*/, SOCK_CLOEXEC)));
if (clientFd < 0) {
// If this log becomes confusing, should save more state from setupUnixDomainServer
// in order to output here.
@@ -144,7 +151,7 @@
LOG_RPC_DETAIL("accept4 on fd %d yields fd %d", mServer.get(), clientFd.get());
- addServer(std::move(clientFd));
+ assignServerToThisThread(std::move(clientFd));
}
// We may not use the connection we just established (two threads might
@@ -170,14 +177,57 @@
return mForServer;
}
-void RpcConnection::addClient(base::unique_fd&& fd) {
- std::lock_guard<std::mutex> _l(mSocketMutex);
- sp<ConnectionSocket> connection = new ConnectionSocket();
- connection->fd = std::move(fd);
- mClients.push_back(connection);
+bool RpcConnection::addServer(const SocketAddress& addr) {
+ LOG_ALWAYS_FATAL_IF(mServer.get() != -1, "Each RpcConnection can only have one server.");
+
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ ALOGE("Could not create socket: %s", strerror(errno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(bind(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ int savedErrno = errno;
+ ALOGE("Could not bind socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(listen(serverFd.get(), 1 /*backlog*/))) {
+ int savedErrno = errno;
+ ALOGE("Could not listen socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ mServer = std::move(serverFd);
+ return true;
}
-void RpcConnection::addServer(base::unique_fd&& fd) {
+bool RpcConnection::addClient(const SocketAddress& addr) {
+ unique_fd serverFd(
+ TEMP_FAILURE_RETRY(socket(addr.addr()->sa_family, SOCK_STREAM | SOCK_CLOEXEC, 0)));
+ if (serverFd == -1) {
+ int savedErrno = errno;
+ ALOGE("Could not create socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ if (0 != TEMP_FAILURE_RETRY(connect(serverFd.get(), addr.addr(), addr.addrSize()))) {
+ int savedErrno = errno;
+ ALOGE("Could not connect socket at %s: %s", addr.toString().c_str(), strerror(savedErrno));
+ return false;
+ }
+
+ LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(), serverFd.get());
+
+ std::lock_guard<std::mutex> _l(mSocketMutex);
+ sp<ConnectionSocket> connection = new ConnectionSocket();
+ connection->fd = std::move(serverFd);
+ mClients.push_back(connection);
+ return true;
+}
+
+void RpcConnection::assignServerToThisThread(base::unique_fd&& fd) {
std::lock_guard<std::mutex> _l(mSocketMutex);
sp<ConnectionSocket> connection = new ConnectionSocket();
connection->fd = std::move(fd);