UnixSocket: support the vsock address family
This change adds the support of vsock family to UnixSocket. A vsock
socket name is in the format of vsock://$CID:$PORT.
For example:
`PERFETTO_PRODUCER_SOCK_NAME=vsock://-1:10000 traced` starts traced with
the producer socket listening on the "ANY" CID and port 10000.
See vsock(7) for special CID values that may be used for vsock.
Bug: 284258446
Change-Id: Ie6f8e4a9f55c0550dba48818e6e26642763fad8d
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 7dfce6b..e8db554 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -144,6 +144,9 @@
if (is_win && perfetto_build_standalone) {
libs = [ "Ws2_32.lib" ]
}
+ if (is_linux || is_android) {
+ sources += [ "vm_sockets.h" ]
+ }
}
}
diff --git a/src/base/unix_socket.cc b/src/base/unix_socket.cc
index 6968e36..4092d79 100644
--- a/src/base/unix_socket.cc
+++ b/src/base/unix_socket.cc
@@ -23,6 +23,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include "perfetto/base/compiler.h"
+#include "perfetto/ext/base/string_utils.h"
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// The include order matters on these three Windows header groups.
@@ -56,6 +57,12 @@
#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/base/utils.h"
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+// Use a local stripped copy of vm_sockets.h from UAPI.
+#include "src/base/vm_sockets.h"
+#endif
+
namespace perfetto {
namespace base {
@@ -75,6 +82,11 @@
using CBufLenType = socklen_t;
#endif
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+constexpr char kVsockNamePrefix[] = "vsock://";
+#endif
+
// A wrapper around variable-size sockaddr structs.
// This is solving the following problem: when calling connect() or bind(), the
// caller needs to take care to allocate the right struct (sockaddr_un for
@@ -103,6 +115,12 @@
return AF_INET;
case SockFamily::kInet6:
return AF_INET6;
+ case SockFamily::kVsock:
+#ifdef AF_VSOCK
+ return AF_VSOCK;
+#else
+ return AF_UNSPEC; // Return AF_UNSPEC on unsupported platforms.
+#endif
case SockFamily::kUnspec:
return AF_UNSPEC;
}
@@ -191,6 +209,25 @@
freeaddrinfo(addr_info);
return res;
}
+ case SockFamily::kVsock: {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ PERFETTO_CHECK(StartsWith(socket_name, kVsockNamePrefix));
+ auto address_port = StripPrefix(socket_name, kVsockNamePrefix);
+ auto parts = SplitString(address_port, ":");
+ PERFETTO_CHECK(parts.size() == 2);
+ sockaddr_vm addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.svm_family = AF_VSOCK;
+ addr.svm_cid = *base::StringToUInt32(parts[0]);
+ addr.svm_port = *base::StringToUInt32(parts[1]);
+ SockaddrAny res(&addr, sizeof(addr));
+ return res;
+#else
+ errno = ENOTSOCK;
+ return SockAddrAny{};
+#endif
+ }
case SockFamily::kUnspec:
errno = ENOTSOCK;
return SockaddrAny();
@@ -224,6 +261,13 @@
if (addr[0] == '@')
return SockFamily::kUnix; // Abstract AF_UNIX sockets.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // Vsock address starts with vsock://.
+ if (strncmp(addr, kVsockNamePrefix, strlen(kVsockNamePrefix)) == 0)
+ return SockFamily::kVsock;
+#endif
+
// If `addr` ends in :NNNN it's either a kInet or kInet6 socket.
const char* col = strrchr(addr, ':');
if (col && CStringToInt32(col + 1).has_value()) {
@@ -296,14 +340,18 @@
setsockopt(*fd_, SOL_SOCKET, SO_NOSIGPIPE, &no_sigpipe, sizeof(no_sigpipe));
#endif
- if (family == SockFamily::kInet || family == SockFamily::kInet6) {
+ if (family == SockFamily::kInet || family == SockFamily::kInet6 ||
+ family == SockFamily::kVsock) {
int flag = 1;
// The reinterpret_cast<const char*> is needed for Windows, where the 4th
// arg is a const char* (on other POSIX system is a const void*).
PERFETTO_CHECK(!setsockopt(*fd_, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char*>(&flag),
sizeof(flag)));
- flag = 1;
+ }
+
+ if (family == SockFamily::kInet || family == SockFamily::kInet6) {
+ int flag = 1;
// Disable Nagle's algorithm, optimize for low-latency.
// See https://github.com/google/perfetto/issues/70.
setsockopt(*fd_, IPPROTO_TCP, TCP_NODELAY,
diff --git a/src/base/unix_socket_unittest.cc b/src/base/unix_socket_unittest.cc
index 3210c49..6ed65d5 100644
--- a/src/base/unix_socket_unittest.cc
+++ b/src/base/unix_socket_unittest.cc
@@ -1014,6 +1014,57 @@
ASSERT_EQ(0, connect(cli.fd(), reinterpret_cast<struct sockaddr*>(&addr),
addr_len));
}
+
+bool VsockLoopBackAddrSupported() {
+ auto sock =
+ UnixSocketRaw::CreateMayFail(SockFamily::kVsock, SockType::kStream);
+ return sock.Bind("vsock://1:10000");
+}
+
+TEST_F(UnixSocketTest, VSockStream) {
+ if (!VsockLoopBackAddrSupported())
+ GTEST_SKIP() << "vsock testing skipped: loopback address unsupported";
+
+ // Use the loopback CID (1) and a random unprileged port number.
+ StackString<128> sock_name("vsock://1:%d", 1024 + rand() % 10000);
+
+ // Set up the server.
+ auto srv =
+ UnixSocket::Listen(sock_name.ToStdString(), &event_listener_,
+ &task_runner_, SockFamily::kVsock, SockType::kStream);
+ ASSERT_TRUE(srv && srv->is_listening());
+
+ std::unique_ptr<UnixSocket> prod;
+ EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
+ .WillOnce(Invoke([&](UnixSocket*, UnixSocket* s) {
+ // OnDisconnect() might spuriously happen depending on the dtor order.
+ EXPECT_CALL(event_listener_, OnDisconnect(s)).Times(AtLeast(0));
+ EXPECT_CALL(event_listener_, OnDataAvailable(s))
+ .WillRepeatedly(Invoke([](UnixSocket* prod_sock) {
+ prod_sock->ReceiveString(); // Read connection EOF;
+ }));
+ ASSERT_TRUE(s->SendStr("welcome"));
+ }));
+
+ // Set up the client.
+ prod =
+ UnixSocket::Connect(sock_name.ToStdString(), &event_listener_,
+ &task_runner_, SockFamily::kVsock, SockType::kStream);
+ auto checkpoint = task_runner_.CreateCheckpoint("prod_connected");
+ EXPECT_CALL(event_listener_, OnDisconnect(prod.get())).Times(AtLeast(0));
+ EXPECT_CALL(event_listener_, OnConnect(prod.get(), true));
+ EXPECT_CALL(event_listener_, OnDataAvailable(prod.get()))
+ .WillRepeatedly(Invoke([checkpoint](UnixSocket* s) {
+ auto str = s->ReceiveString();
+ if (str == "")
+ return; // Connection EOF.
+ ASSERT_EQ("welcome", str);
+ checkpoint();
+ }));
+
+ task_runner_.RunUntilCheckpoint("prod_connected");
+ ASSERT_TRUE(Mock::VerifyAndClearExpectations(prod.get()));
+}
#endif // OS_LINUX || OS_ANDROID
TEST_F(UnixSocketTest, GetSockFamily) {
@@ -1025,6 +1076,10 @@
ASSERT_EQ(GetSockFamily("127.0.0.1:80"), SockFamily::kInet);
ASSERT_EQ(GetSockFamily("[effe::acca]:1234"), SockFamily::kInet6);
ASSERT_EQ(GetSockFamily("[::1]:123456"), SockFamily::kInet6);
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+ PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ ASSERT_EQ(GetSockFamily("vsock://-1:10000"), SockFamily::kVsock);
+#endif
}
} // namespace
diff --git a/src/base/vm_sockets.h b/src/base/vm_sockets.h
new file mode 100644
index 0000000..af5b917
--- /dev/null
+++ b/src/base/vm_sockets.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#ifndef SRC_BASE_VM_SOCKETS_H_
+#define SRC_BASE_VM_SOCKETS_H_
+
+#include <sys/socket.h>
+
+#ifdef AF_VSOCK
+// Use system vm_socket.h if avaialbe.
+#include <linux/vm_sockets.h>
+#else
+// Fallback and use the stripped copy from the UAPI vm_sockets.h.
+
+#include <stdint.h> // For uint8_t.
+
+#define AF_VSOCK 40
+
+struct sockaddr_vm {
+ sa_family_t svm_family;
+ unsigned short svm_reserved1;
+ unsigned int svm_port;
+ unsigned int svm_cid;
+ uint8_t svm_flags;
+ unsigned char svm_zero[sizeof(struct sockaddr) - sizeof(sa_family_t) -
+ sizeof(unsigned short) - sizeof(unsigned int) -
+ sizeof(unsigned int) - sizeof(uint8_t)];
+};
+
+#endif
+
+#endif // SRC_BASE_VM_SOCKETS_H_