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
next prev parent reply other threads:[~2026-10-12 23:55 UTC|newest]
Thread overview: 5+ 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-13 0:06 ` sashiko-bot
2026-10-12 23:55 ` David Carlier [this message]
2026-10-13 0:06 ` [PATCH net-next 2/2] vsock/test: cover SO_INQ sashiko-bot
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.