From: David Carlier <devnexen@gmail.com>
To: netdev@vger.kernel.org
Cc: sgarzare@redhat.com, bobbyeshleman@gmail.com,
davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org,
virtualization@lists.linux.dev, linux-kernel@vger.kernel.org,
David Carlier <devnexen@gmail.com>
Subject: [PATCH net-next 2/2] vsock/test: cover SO_INQ
Date: Tue, 13 Oct 2026 00:55:24 +0100 [thread overview]
Message-ID: <20261012235524.41468-3-devnexen@gmail.com> (raw)
In-Reply-To: <20261012235524.41468-1-devnexen@gmail.com>
Check the SCM_INQ value after a partial receive, after draining the
queue and at EOF, that no cmsg is sent while the option is off, and
that out of range values and SOCK_SEQPACKET are rejected.
Signed-off-by: David Carlier <devnexen@gmail.com>
---
tools/testing/vsock/vsock_test.c | 191 +++++++++++++++++++++++++++++++
1 file changed, 191 insertions(+)
diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c
index b4ff9f946565..00bb68c00c07 100644
--- a/tools/testing/vsock/vsock_test.c
+++ b/tools/testing/vsock/vsock_test.c
@@ -2429,6 +2429,188 @@ static void test_stream_collapse_server(const struct test_opts *opts)
close(fd);
}
+#define SO_INQ_BUF_LEN 64
+#define SO_INQ_CHUNK_LEN 16
+
+static int so_inq_set(int fd, int val)
+{
+ return setsockopt(fd, SOL_SOCKET, SO_INQ, &val, sizeof(val));
+}
+
+/* Receive @len bytes and return the SCM_INQ value, or -1 if the kernel did
+ * not attach one.
+ */
+static int so_inq_recv(int fd, size_t len, int expected_ret)
+{
+ char control[CMSG_SPACE(sizeof(int))] = { 0 };
+ unsigned char buf[SO_INQ_BUF_LEN];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = len,
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = control,
+ .msg_controllen = sizeof(control),
+ };
+ struct cmsghdr *cmsg;
+ ssize_t ret;
+ int inq;
+
+ ret = recvmsg(fd, &msg, 0);
+ if (ret < 0) {
+ perror("recvmsg");
+ exit(EXIT_FAILURE);
+ }
+
+ expect_res(ret, expected_ret, "recvmsg");
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET ||
+ cmsg->cmsg_type != SCM_INQ)
+ continue;
+
+ memcpy(&inq, CMSG_DATA(cmsg), sizeof(inq));
+ return inq;
+ }
+
+ return -1;
+}
+
+static void test_stream_so_inq_client(const struct test_opts *opts)
+{
+ int fd;
+
+ fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);
+ if (fd < 0) {
+ perror("connect");
+ exit(EXIT_FAILURE);
+ }
+
+ control_expectln("SENT");
+
+ /* Wait for the whole payload to be queued, so the values below do not
+ * depend on how the transport delivers it.
+ */
+ if (!vsock_ioctl_int(fd, SIOCINQ, SO_INQ_BUF_LEN)) {
+ fprintf(stderr, "Test skipped, SIOCINQ not supported.\n");
+ goto out;
+ }
+
+ /* No ancillary data unless the option is enabled. */
+ expect_res(so_inq_recv(fd, SO_INQ_CHUNK_LEN, SO_INQ_CHUNK_LEN), -1,
+ "SCM_INQ with SO_INQ disabled");
+
+ if (so_inq_set(fd, 1)) {
+ if (errno != ENOPROTOOPT) {
+ perror("setsockopt(SO_INQ)");
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(stderr, "Test skipped, SO_INQ not supported.\n");
+ goto out;
+ }
+
+ /* Partial read: the rest of the payload is still queued. */
+ expect_res(so_inq_recv(fd, SO_INQ_CHUNK_LEN, SO_INQ_CHUNK_LEN),
+ SO_INQ_BUF_LEN - 2 * SO_INQ_CHUNK_LEN,
+ "SCM_INQ after partial receive");
+
+ /* Draining read while the peer stays connected. */
+ expect_res(so_inq_recv(fd, SO_INQ_BUF_LEN - 2 * SO_INQ_CHUNK_LEN,
+ SO_INQ_BUF_LEN - 2 * SO_INQ_CHUNK_LEN), 0,
+ "SCM_INQ after draining receive");
+
+ control_writeln("DRAINED");
+ vsock_wait_remote_close(fd);
+
+ /* Nothing is queued once the peer is gone, but 1 is reported so the
+ * caller keeps reading until it observes EOF, as TCP_INQ does.
+ */
+ expect_res(so_inq_recv(fd, SO_INQ_CHUNK_LEN, 0), 1, "SCM_INQ at EOF");
+
+ if (so_inq_set(fd, 0)) {
+ perror("setsockopt(SO_INQ)");
+ exit(EXIT_FAILURE);
+ }
+
+ expect_res(so_inq_recv(fd, SO_INQ_CHUNK_LEN, 0), -1,
+ "SCM_INQ after disabling SO_INQ");
+
+ close(fd);
+ return;
+
+out:
+ control_writeln("DRAINED");
+ close(fd);
+}
+
+static void test_stream_so_inq_server(const struct test_opts *opts)
+{
+ unsigned char buf[SO_INQ_BUF_LEN];
+ int fd;
+
+ fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);
+ if (fd < 0) {
+ perror("accept");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(buf, 'A', sizeof(buf));
+ send_buf(fd, buf, sizeof(buf), 0, sizeof(buf));
+ control_writeln("SENT");
+
+ /* Keep the connection open until the peer has drained the queue: an
+ * early close would turn its last SCM_INQ value into the EOF one.
+ */
+ control_expectln("DRAINED");
+ close(fd);
+}
+
+static void test_so_inq_validation_client(const struct test_opts *opts)
+{
+ int fd;
+
+ fd = socket(AF_VSOCK, SOCK_STREAM, 0);
+ if (fd < 0) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ if (so_inq_set(fd, 1)) {
+ if (errno != ENOPROTOOPT) {
+ perror("setsockopt(SO_INQ)");
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(stderr, "Test skipped, SO_INQ not supported.\n");
+ close(fd);
+ return;
+ }
+
+ if (!so_inq_set(fd, 2) || errno != EINVAL) {
+ fprintf(stderr, "SO_INQ accepted an out of range value\n");
+ exit(EXIT_FAILURE);
+ }
+
+ close(fd);
+
+ /* SCM_INQ counts bytes, which a message based socket cannot report. */
+ fd = socket(AF_VSOCK, SOCK_SEQPACKET, 0);
+ if (fd < 0) {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!so_inq_set(fd, 1) || errno != ENOPROTOOPT) {
+ fprintf(stderr, "SO_INQ not rejected on SOCK_SEQPACKET\n");
+ exit(EXIT_FAILURE);
+ }
+
+ close(fd);
+}
+
static struct test_case test_cases[] = {
{
.name = "SOCK_STREAM connection reset",
@@ -2633,6 +2815,15 @@ static struct test_case test_cases[] = {
.run_client = test_stream_collapse_client,
.run_server = test_stream_collapse_server,
},
+ {
+ .name = "SOCK_STREAM SO_INQ functionality",
+ .run_client = test_stream_so_inq_client,
+ .run_server = test_stream_so_inq_server,
+ },
+ {
+ .name = "SO_INQ option validation",
+ .run_client = test_so_inq_validation_client,
+ },
{},
};
--
2.55.0
prev parent reply other threads:[~2026-10-12 23:55 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-12 23:55 [PATCH net-next 0/2] vsock: SO_INQ for stream sockets David Carlier
2026-10-12 23:55 ` [PATCH net-next 1/2] vsock: support SO_INQ on " David Carlier
2026-10-12 23:55 ` David Carlier [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261012235524.41468-3-devnexen@gmail.com \
--to=devnexen@gmail.com \
--cc=bobbyeshleman@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sgarzare@redhat.com \
--cc=virtualization@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox