From: David Carlier <devnexen@gmail.com>
To: netdev@vger.kernel.org, mptcp@lists.linux.dev
Cc: matttbe@kernel.org, martineau@kernel.org, geliang@kernel.org,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org,
David Carlier <devnexen@gmail.com>
Subject: [PATCH 3/3] selftests: mptcp: cover RECVERR and MSG_ERRQUEUE
Date: Tue, 21 Apr 2026 16:22:11 +0100 [thread overview]
Message-ID: <20260421152216.38127-4-devnexen@gmail.com> (raw)
In-Reply-To: <20260421152216.38127-1-devnexen@gmail.com>
Add MPTCP selftest coverage for RECVERR sockopt round-trips and
parent-socket MSG_ERRQUEUE delivery.
Enable TX software timestamping, send data over an MPTCP socket, wait
for POLLERR, and verify that recvmsg(MSG_ERRQUEUE) returns timestamping
metadata on the MPTCP parent socket.
Signed-off-by: David Carlier <devnexen@gmail.com>
Assisted-by: Codex:gpt-5
---
.../selftests/net/mptcp/mptcp_sockopt.c | 152 ++++++++++++++++++
1 file changed, 152 insertions(+)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index b6e58d936ebe..b499e7585d38 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -26,9 +26,17 @@
#include <linux/tcp.h>
#include <linux/compiler.h>
+#include <linux/errqueue.h>
+#include <linux/net_tstamp.h>
+
+#include <poll.h>
static int pf = AF_INET;
+#ifndef SCM_TIMESTAMPING
+#define SCM_TIMESTAMPING SO_TIMESTAMPING
+#endif
+
#ifndef IPPROTO_MPTCP
#define IPPROTO_MPTCP 262
#endif
@@ -128,6 +136,9 @@ struct so_state {
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
+static void enable_tx_timestamping(int fd);
+static void test_msg_errqueue_timestamping(int fd);
+
static void __noreturn die_perror(const char *msg)
{
perror(msg);
@@ -598,6 +609,8 @@ static void connect_one_server(int fd, int pipefd)
assert(strncmp(buf2, "xmit", 4) == 0);
+ enable_tx_timestamping(fd);
+
ret = write(fd, buf, len);
if (ret < 0)
die_perror("write");
@@ -605,6 +618,8 @@ static void connect_one_server(int fd, int pipefd)
if (ret != (ssize_t)len)
xerror("short write");
+ test_msg_errqueue_timestamping(fd);
+
total = 0;
do {
ret = read(fd, buf2 + total, sizeof(buf2) - total);
@@ -769,6 +784,142 @@ static void test_ip_tos_sockopt(int fd)
xerror("expect socklen_t == -1");
}
+static void test_ip_recverr_sockopt(int fd)
+{
+ struct iovec iov = {
+ .iov_base = &(char){ 0 },
+ .iov_len = 1,
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ int one = 1, zero = 0, val = -1;
+ socklen_t s = sizeof(val);
+ int level, optname, r;
+
+ switch (pf) {
+ case AF_INET:
+ level = SOL_IP;
+ optname = IP_RECVERR;
+ break;
+ case AF_INET6:
+ level = SOL_IPV6;
+ optname = IPV6_RECVERR;
+ break;
+ default:
+ xerror("Unknown pf %d\n", pf);
+ }
+
+ r = setsockopt(fd, level, optname, &one, sizeof(one));
+ if (r)
+ die_perror("setsockopt recverr on");
+
+ r = getsockopt(fd, level, optname, &val, &s);
+ if (r)
+ die_perror("getsockopt recverr on");
+ if (s != sizeof(val) || val != one)
+ xerror("recverr on mismatch val=%d len=%u", val, s);
+
+ r = recvmsg(fd, &msg, MSG_ERRQUEUE | MSG_DONTWAIT);
+ if (r != -1 || errno != EAGAIN)
+ xerror("expected empty errqueue to return EAGAIN, ret=%d errno=%d", r, errno);
+
+ r = setsockopt(fd, level, optname, &zero, sizeof(zero));
+ if (r)
+ die_perror("setsockopt recverr off");
+
+ val = -1;
+ s = sizeof(val);
+ r = getsockopt(fd, level, optname, &val, &s);
+ if (r)
+ die_perror("getsockopt recverr off");
+ if (s != sizeof(val) || val != zero)
+ xerror("recverr off mismatch val=%d len=%u", val, s);
+}
+
+static void enable_tx_timestamping(int fd)
+{
+ int val = SOF_TIMESTAMPING_SOFTWARE |
+ SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_OPT_TSONLY;
+ int ret;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING_OLD,
+ &val, sizeof(val));
+ if (ret)
+ die_perror("setsockopt SO_TIMESTAMPING");
+}
+
+static void test_msg_errqueue_timestamping(int fd)
+{
+ char ctrl[512] = { 0 };
+ char data[32] = { 0 };
+ struct iovec iov = {
+ .iov_base = data,
+ .iov_len = sizeof(data),
+ };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ .msg_control = ctrl,
+ .msg_controllen = sizeof(ctrl),
+ };
+ struct pollfd pfd = {
+ .fd = fd,
+ .events = POLLERR,
+ };
+ struct cmsghdr *cm;
+ struct scm_timestamping *tss = NULL;
+ struct sock_extended_err *serr = NULL;
+ int ret, i;
+
+ for (i = 0; i < 10; i++) {
+ ret = poll(&pfd, 1, 1000);
+ if (ret < 0)
+ die_perror("poll errqueue");
+ if (ret == 0)
+ continue;
+ if (!(pfd.revents & POLLERR))
+ xerror("expected POLLERR, got revents %#x", pfd.revents);
+ break;
+ }
+
+ if (i == 10)
+ xerror("timed out waiting for MSG_ERRQUEUE event");
+
+ ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
+ if (ret < 0)
+ die_perror("recvmsg timestamping errqueue");
+ if (!(msg.msg_flags & MSG_ERRQUEUE))
+ xerror("expected MSG_ERRQUEUE in msg_flags, got %#x",
+ msg.msg_flags);
+
+ for (cm = CMSG_FIRSTHDR(&msg); cm; cm = CMSG_NXTHDR(&msg, cm)) {
+ if (cm->cmsg_level == SOL_SOCKET &&
+ cm->cmsg_type == SCM_TIMESTAMPING)
+ tss = (void *)CMSG_DATA(cm);
+ if ((cm->cmsg_level == SOL_IP &&
+ cm->cmsg_type == IP_RECVERR) ||
+ (cm->cmsg_level == SOL_IPV6 &&
+ cm->cmsg_type == IPV6_RECVERR))
+ serr = (void *)CMSG_DATA(cm);
+ }
+
+ if (!tss)
+ xerror("missing SCM_TIMESTAMPING cmsg");
+ if (!serr)
+ xerror("missing sock_extended_err cmsg");
+ if (serr->ee_errno != ENOMSG ||
+ serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING)
+ xerror("unexpected timestamping err ee_errno=%u ee_origin=%u",
+ serr->ee_errno, serr->ee_origin);
+ if (!tss->ts[0].tv_sec && !tss->ts[0].tv_nsec &&
+ !tss->ts[1].tv_sec && !tss->ts[1].tv_nsec &&
+ !tss->ts[2].tv_sec && !tss->ts[2].tv_nsec)
+ xerror("all timestamp slots are zero");
+}
+
static int client(int pipefd)
{
int fd = -1;
@@ -787,6 +938,7 @@ static int client(int pipefd)
}
test_ip_tos_sockopt(fd);
+ test_ip_recverr_sockopt(fd);
connect_one_server(fd, pipefd);
--
2.53.0
next prev parent reply other threads:[~2026-04-21 15:22 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-21 15:22 [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support David Carlier
2026-04-21 15:22 ` [PATCH 1/3] mptcp: propagate RECVERR sockopts to subflows David Carlier
2026-04-21 15:22 ` [PATCH 2/3] mptcp: support MSG_ERRQUEUE on the parent socket David Carlier
2026-04-21 15:22 ` David Carlier [this message]
2026-04-21 15:48 ` [PATCH 0/3] mptcp: add RECVERR and MSG_ERRQUEUE support MPTCP CI
2026-04-21 16:07 ` Matthieu Baerts
2026-04-21 17:16 ` David CARLIER
2026-04-21 16:50 ` MPTCP CI
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=20260421152216.38127-4-devnexen@gmail.com \
--to=devnexen@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=geliang@kernel.org \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=martineau@kernel.org \
--cc=matttbe@kernel.org \
--cc=mptcp@lists.linux.dev \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
/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.