* [PATCH mptcp-next v11 01/11] selftests: mptcp: sockopt: reseed RNG after fork
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 02/11] selftests: mptcp: sockopt: use exact length in connect_one_server Geliang Tang
` (10 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
xfork() does not reseed the RNG in the child, so server and client
processes use the same rand() sequence. Restore the per-fork reseed
from mptcp_inq.c by calling init_rng() in the child, ensuring
independent random values for buffer contents and transfer sizes.
These codes are from mptcp_inq.c.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
tools/testing/selftests/net/mptcp/mptcp_sockopt.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index b6e58d936ebe..05afe8dd1381 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -128,6 +128,8 @@ struct so_state {
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
+static void init_rng(void);
+
static void __noreturn die_perror(const char *msg)
{
perror(msg);
@@ -799,6 +801,8 @@ static pid_t xfork(void)
if (p < 0)
die_perror("fork");
+ else if (p == 0)
+ init_rng();
return p;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH mptcp-next v11 02/11] selftests: mptcp: sockopt: use exact length in connect_one_server
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 01/11] selftests: mptcp: sockopt: reseed RNG after fork Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 03/11] selftests: mptcp: sockopt: add check_stat_equal helper Geliang Tang
` (9 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Use the exact remaining length ('len - total') when reading the echoed data
from the socket, instead of the full buffer size.
This silences a false positive -Wstringop-overflow warning from GCC, as the
compiler can now prove that the destination buffer is large enough for the
specified read size.
connect_one_server() passes per-chunk 'ret' to do_getsockopts() instead of
cumulative 'total', causing mismatches when the echo is fragmented. Fix by
passing 'total' to match the semantics used in process_one_client().
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
tools/testing/selftests/net/mptcp/mptcp_sockopt.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index 05afe8dd1381..9588a25ac6b2 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -609,7 +609,7 @@ static void connect_one_server(int fd, int pipefd)
total = 0;
do {
- ret = read(fd, buf2 + total, sizeof(buf2) - total);
+ ret = read(fd, buf2 + total, len - total);
if (ret < 0)
die_perror("read");
if (ret == 0) {
@@ -629,7 +629,7 @@ static void connect_one_server(int fd, int pipefd)
if (s.tcpi_rcv_delta)
assert(s.tcpi_rcv_delta <= total);
- do_getsockopts(&s, fd, ret, ret);
+ do_getsockopts(&s, fd, total, total);
if (eof)
total += 1; /* sequence advances due to FIN */
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH mptcp-next v11 03/11] selftests: mptcp: sockopt: add check_stat_equal helper
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 01/11] selftests: mptcp: sockopt: reseed RNG after fork Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 02/11] selftests: mptcp: sockopt: use exact length in connect_one_server Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 04/11] selftests: mptcp: sockopt: use size_t for byte counters Geliang Tang
` (8 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Add check_stat_equal() helper to reduce repetitive error reporting code
when validating MPTCP statistics values in process_one_client().
Also fix bytes_acked validation: compare it against the amount of data
written (ret2) instead of the amount read (ret), as bytes_acked tracks
sent and acknowledged data.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
.../selftests/net/mptcp/mptcp_sockopt.c | 39 ++++++++++---------
1 file changed, 21 insertions(+), 18 deletions(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index 9588a25ac6b2..f124850181e7 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -638,6 +638,16 @@ static void connect_one_server(int fd, int pipefd)
close(fd);
}
+static void check_stat_equal(const char *name, uint64_t actual,
+ uint64_t expected)
+{
+ if (actual == expected)
+ return;
+
+ xerror("%s %" PRIu64 ", expect %" PRIu64 ", diff %" PRId64,
+ name, actual, expected, (int64_t)(actual - expected));
+}
+
static void process_one_client(int fd, int pipefd)
{
ssize_t ret, ret2, ret3;
@@ -669,27 +679,20 @@ static void process_one_client(int fd, int pipefd)
xerror("expected EOF, got %lu", ret3);
do_getsockopts(&s, fd, ret, ret2);
- if (s.mptcpi_rcv_delta != (uint64_t)ret + 1)
- xerror("mptcpi_rcv_delta %" PRIu64 ", expect %" PRIu64 ", diff %" PRId64,
- s.mptcpi_rcv_delta, ret + 1, s.mptcpi_rcv_delta - (ret + 1));
+ check_stat_equal("mptcpi_rcv_delta", s.mptcpi_rcv_delta,
+ (uint64_t)ret + 1); /* +1 for FIN */
/* be nice when running on top of older kernel */
if (s.pkt_stats_avail) {
- if (s.last_sample.mptcpi_bytes_sent != ret2)
- xerror("mptcpi_bytes_sent %" PRIu64 ", expect %" PRIu64
- ", diff %" PRId64,
- s.last_sample.mptcpi_bytes_sent, ret2,
- s.last_sample.mptcpi_bytes_sent - ret2);
- if (s.last_sample.mptcpi_bytes_received != ret)
- xerror("mptcpi_bytes_received %" PRIu64 ", expect %" PRIu64
- ", diff %" PRId64,
- s.last_sample.mptcpi_bytes_received, ret,
- s.last_sample.mptcpi_bytes_received - ret);
- if (s.last_sample.mptcpi_bytes_acked != ret)
- xerror("mptcpi_bytes_acked %" PRIu64 ", expect %" PRIu64
- ", diff %" PRId64,
- s.last_sample.mptcpi_bytes_acked, ret,
- s.last_sample.mptcpi_bytes_acked - ret);
+ check_stat_equal("mptcpi_bytes_sent",
+ s.last_sample.mptcpi_bytes_sent,
+ (uint64_t)ret2);
+ check_stat_equal("mptcpi_bytes_received",
+ s.last_sample.mptcpi_bytes_received,
+ (uint64_t)ret);
+ check_stat_equal("mptcpi_bytes_acked",
+ s.last_sample.mptcpi_bytes_acked,
+ (uint64_t)ret2);
}
close(fd);
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH mptcp-next v11 04/11] selftests: mptcp: sockopt: use size_t for byte counters
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
` (2 preceding siblings ...)
2026-07-15 2:48 ` [PATCH mptcp-next v11 03/11] selftests: mptcp: sockopt: add check_stat_equal helper Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 05/11] selftests: mptcp: sockopt: add tx/rx protocol options Geliang Tang
` (7 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Rename ret/ret2/ret3 to ret (ssize_t, for syscall return values) and
r/w (size_t, for byte counters) for better readability.
The byte counters were previously stored in ssize_t but never hold
negative values; size_t is the natural type and matches the parameter
types of do_getsockopts(s, fd, size_t r, size_t w). This also prepares
for future updates where r will be modified via '+='.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
.../selftests/net/mptcp/mptcp_sockopt.c | 30 +++++++++++--------
1 file changed, 17 insertions(+), 13 deletions(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index f124850181e7..9e1d84af5268 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -650,9 +650,10 @@ static void check_stat_equal(const char *name, uint64_t actual,
static void process_one_client(int fd, int pipefd)
{
- ssize_t ret, ret2, ret3;
struct so_state s;
char buf[4096];
+ ssize_t ret;
+ size_t r, w;
memset(&s, 0, sizeof(s));
do_getsockopts(&s, fd, 0, 0);
@@ -663,36 +664,39 @@ static void process_one_client(int fd, int pipefd)
ret = read(fd, buf, sizeof(buf));
if (ret < 0)
die_perror("read");
+ r = ret;
- assert(s.mptcpi_rcv_delta <= (uint64_t)ret);
+ assert(s.mptcpi_rcv_delta <= (uint64_t)r);
if (s.tcpi_rcv_delta)
- assert(s.tcpi_rcv_delta == (uint64_t)ret);
+ assert(s.tcpi_rcv_delta == (uint64_t)r);
- ret2 = write(fd, buf, ret);
- if (ret2 < 0)
+ ret = write(fd, buf, r);
+ if (ret < 0)
die_perror("write");
+ w = ret;
/* wait for hangup */
- ret3 = read(fd, buf, 1);
- if (ret3 != 0)
- xerror("expected EOF, got %lu", ret3);
+ ret = read(fd, buf, 1);
+ if (ret != 0)
+ xerror("expected EOF, got %zd", ret);
+ r += ret;
- do_getsockopts(&s, fd, ret, ret2);
+ do_getsockopts(&s, fd, r, w);
check_stat_equal("mptcpi_rcv_delta", s.mptcpi_rcv_delta,
- (uint64_t)ret + 1); /* +1 for FIN */
+ (uint64_t)r + 1); /* +1 for FIN */
/* be nice when running on top of older kernel */
if (s.pkt_stats_avail) {
check_stat_equal("mptcpi_bytes_sent",
s.last_sample.mptcpi_bytes_sent,
- (uint64_t)ret2);
+ (uint64_t)w);
check_stat_equal("mptcpi_bytes_received",
s.last_sample.mptcpi_bytes_received,
- (uint64_t)ret);
+ (uint64_t)r);
check_stat_equal("mptcpi_bytes_acked",
s.last_sample.mptcpi_bytes_acked,
- (uint64_t)ret2);
+ (uint64_t)w);
}
close(fd);
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH mptcp-next v11 05/11] selftests: mptcp: sockopt: add tx/rx protocol options
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
` (3 preceding siblings ...)
2026-07-15 2:48 ` [PATCH mptcp-next v11 04/11] selftests: mptcp: sockopt: use size_t for byte counters Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 06/11] selftests: mptcp: sockopt: add TCP_INQ test option Geliang Tang
` (6 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Add -t and -r options to specify tx/rx protocols (TCP/MPTCP). This
increases testing flexibility by allowing explicit protocol selection
for both transmission and reception paths.
These codes are from mptcp_inq.c.
Enhance compatibility with TCP tests by skipping MPTCP getsockopt checks
during TCP tests since MPTCP socket options are not available.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
.../selftests/net/mptcp/mptcp_sockopt.c | 39 +++++++++++++++----
1 file changed, 32 insertions(+), 7 deletions(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index 9e1d84af5268..71db07913d14 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -28,6 +28,8 @@
#include <linux/compiler.h>
static int pf = AF_INET;
+static int proto_tx = IPPROTO_MPTCP;
+static int proto_rx = IPPROTO_MPTCP;
#ifndef IPPROTO_MPTCP
#define IPPROTO_MPTCP 262
@@ -139,6 +141,7 @@ static void __noreturn die_perror(const char *msg)
static void die_usage(int r)
{
fprintf(stderr, "Usage: mptcp_sockopt [-6]\n");
+ fprintf(stderr, " [-t tcp|mptcp] [-r tcp|mptcp]\n");
exit(r);
}
@@ -204,7 +207,7 @@ static int sock_listen_mptcp(const char * const listenaddr,
hints.ai_family = pf;
for (a = addr; a; a = a->ai_next) {
- sock = socket(a->ai_family, a->ai_socktype, IPPROTO_MPTCP);
+ sock = socket(a->ai_family, a->ai_socktype, proto_rx);
if (sock < 0)
continue;
@@ -262,11 +265,22 @@ static int sock_connect_mptcp(const char * const remoteaddr,
return sock;
}
+static int protostr_to_num(const char *s)
+{
+ if (strcasecmp(s, "tcp") == 0)
+ return IPPROTO_TCP;
+ if (strcasecmp(s, "mptcp") == 0)
+ return IPPROTO_MPTCP;
+
+ die_usage(1);
+ return 0;
+}
+
static void parse_opts(int argc, char **argv)
{
int c;
- while ((c = getopt(argc, argv, "h6")) != -1) {
+ while ((c = getopt(argc, argv, "h6t:r:")) != -1) {
switch (c) {
case 'h':
die_usage(0);
@@ -274,6 +288,12 @@ static void parse_opts(int argc, char **argv)
case '6':
pf = AF_INET6;
break;
+ case 't':
+ proto_tx = protostr_to_num(optarg);
+ break;
+ case 'r':
+ proto_rx = protostr_to_num(optarg);
+ break;
default:
die_usage(1);
break;
@@ -559,6 +579,9 @@ static void do_getsockopt_mptcp_full_info(struct so_state *s, int fd)
static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w)
{
+ if (proto_tx != IPPROTO_MPTCP || proto_rx != IPPROTO_MPTCP)
+ return;
+
do_getsockopt_mptcp_info(s, fd, w);
do_getsockopt_tcp_info(s, fd, r, w);
@@ -634,7 +657,8 @@ static void connect_one_server(int fd, int pipefd)
if (eof)
total += 1; /* sequence advances due to FIN */
- assert(s.mptcpi_rcv_delta == (uint64_t)total);
+ if (proto_tx == IPPROTO_MPTCP && proto_rx == IPPROTO_MPTCP)
+ assert(s.mptcpi_rcv_delta == (uint64_t)total);
close(fd);
}
@@ -683,8 +707,9 @@ static void process_one_client(int fd, int pipefd)
r += ret;
do_getsockopts(&s, fd, r, w);
- check_stat_equal("mptcpi_rcv_delta", s.mptcpi_rcv_delta,
- (uint64_t)r + 1); /* +1 for FIN */
+ if (proto_tx == IPPROTO_MPTCP && proto_rx == IPPROTO_MPTCP)
+ check_stat_equal("mptcpi_rcv_delta", s.mptcpi_rcv_delta,
+ (uint64_t)r + 1); /* +1 for FIN */
/* be nice when running on top of older kernel */
if (s.pkt_stats_avail) {
@@ -786,10 +811,10 @@ static int client(int pipefd)
switch (pf) {
case AF_INET:
- fd = sock_connect_mptcp("127.0.0.1", "15432", IPPROTO_MPTCP);
+ fd = sock_connect_mptcp("127.0.0.1", "15432", proto_tx);
break;
case AF_INET6:
- fd = sock_connect_mptcp("::1", "15432", IPPROTO_MPTCP);
+ fd = sock_connect_mptcp("::1", "15432", proto_tx);
break;
default:
xerror("Unknown pf %d\n", pf);
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH mptcp-next v11 06/11] selftests: mptcp: sockopt: add TCP_INQ test option
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
` (4 preceding siblings ...)
2026-07-15 2:48 ` [PATCH mptcp-next v11 05/11] selftests: mptcp: sockopt: add tx/rx protocol options Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 07/11] selftests: mptcp: sockopt: use unix socket instead of pipe Geliang Tang
` (5 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Add -i option to enable TCP_INQ testing. This prepares for consolidating
TCP_INQ tests into a single binary by adding the necessary command-line
interface.
Add helper functions to conditionally set TCP_INQ sockopt on accepted
sockets when running in inq mode. This ensures proper testing of TCP_CM_INQ
functionality by enabling the required socket option on the incoming server
connection.
These codes are from mptcp_inq.c.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
.../selftests/net/mptcp/mptcp_sockopt.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index 71db07913d14..b40ec54cb134 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -30,6 +30,7 @@
static int pf = AF_INET;
static int proto_tx = IPPROTO_MPTCP;
static int proto_rx = IPPROTO_MPTCP;
+static bool inq;
#ifndef IPPROTO_MPTCP
#define IPPROTO_MPTCP 262
@@ -142,6 +143,7 @@ static void die_usage(int r)
{
fprintf(stderr, "Usage: mptcp_sockopt [-6]\n");
fprintf(stderr, " [-t tcp|mptcp] [-r tcp|mptcp]\n");
+ fprintf(stderr, " [-i]\n");
exit(r);
}
@@ -280,7 +282,7 @@ static void parse_opts(int argc, char **argv)
{
int c;
- while ((c = getopt(argc, argv, "h6t:r:")) != -1) {
+ while ((c = getopt(argc, argv, "h6t:r:i")) != -1) {
switch (c) {
case 'h':
die_usage(0);
@@ -294,6 +296,9 @@ static void parse_opts(int argc, char **argv)
case 'r':
proto_rx = protostr_to_num(optarg);
break;
+ case 'i':
+ inq = true;
+ break;
default:
die_usage(1);
break;
@@ -727,6 +732,14 @@ static void process_one_client(int fd, int pipefd)
close(fd);
}
+static void do_setsockopt_inq(int fd)
+{
+ int on = 1;
+
+ if (setsockopt(fd, IPPROTO_TCP, TCP_INQ, &on, sizeof(on)))
+ die_perror("setsockopt(TCP_INQ)");
+}
+
static int xaccept(int s)
{
int fd = accept(s, NULL, 0);
@@ -734,6 +747,9 @@ static int xaccept(int s)
if (fd < 0)
die_perror("accept");
+ if (inq)
+ do_setsockopt_inq(fd);
+
return fd;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH mptcp-next v11 07/11] selftests: mptcp: sockopt: use unix socket instead of pipe
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
` (5 preceding siblings ...)
2026-07-15 2:48 ` [PATCH mptcp-next v11 06/11] selftests: mptcp: sockopt: add TCP_INQ test option Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 08/11] selftests: mptcp: sockopt: use recvmsg instead of read Geliang Tang
` (4 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Replace the pipe() with socketpair(AF_UNIX, SOCK_STREAM) for
inter-process communication. This prepares for upcoming patches that
will require bidirectional communication and reliable EOF behavior,
which a pipe does not fully support.
Use SOCK_STREAM over SOCK_DGRAM (used in the original mptcp_inq.c)
to reliably detect peer closure via EOF, avoiding indefinite hangs.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
.../selftests/net/mptcp/mptcp_sockopt.c | 40 +++++++++----------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index b40ec54cb134..0d1426c11581 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -597,7 +597,7 @@ static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w)
do_getsockopt_mptcp_full_info(s, fd);
}
-static void connect_one_server(int fd, int pipefd)
+static void connect_one_server(int fd, int unixfd)
{
char buf[4096], buf2[4096];
size_t len, i, total;
@@ -622,9 +622,9 @@ static void connect_one_server(int fd, int pipefd)
do_getsockopts(&s, fd, 0, 0);
/* un-block server */
- ret = read(pipefd, buf2, 4);
+ ret = read(unixfd, buf2, 4);
assert(ret == 4);
- close(pipefd);
+ close(unixfd);
assert(strncmp(buf2, "xmit", 4) == 0);
@@ -677,7 +677,7 @@ static void check_stat_equal(const char *name, uint64_t actual,
name, actual, expected, (int64_t)(actual - expected));
}
-static void process_one_client(int fd, int pipefd)
+static void process_one_client(int fd, int unixfd)
{
struct so_state s;
char buf[4096];
@@ -687,7 +687,7 @@ static void process_one_client(int fd, int pipefd)
memset(&s, 0, sizeof(s));
do_getsockopts(&s, fd, 0, 0);
- ret = write(pipefd, "xmit", 4);
+ ret = write(unixfd, "xmit", 4);
assert(ret == 4);
ret = read(fd, buf, sizeof(buf));
@@ -753,7 +753,7 @@ static int xaccept(int s)
return fd;
}
-static int server(int pipefd)
+static int server(int unixfd)
{
int fd = -1, r;
@@ -769,13 +769,13 @@ static int server(int pipefd)
break;
}
- r = write(pipefd, "conn", 4);
+ r = write(unixfd, "conn", 4);
assert(r == 4);
alarm(15);
r = xaccept(fd);
- process_one_client(r, pipefd);
+ process_one_client(r, unixfd);
close(fd);
return 0;
@@ -819,7 +819,7 @@ static void test_ip_tos_sockopt(int fd)
xerror("expect socklen_t == -1");
}
-static int client(int pipefd)
+static int client(int unixfd)
{
int fd = -1;
@@ -838,7 +838,7 @@ static int client(int pipefd)
test_ip_tos_sockopt(fd);
- connect_one_server(fd, pipefd);
+ connect_one_server(fd, unixfd);
return 0;
}
@@ -894,35 +894,35 @@ int main(int argc, char *argv[])
{
int e1, e2, wstatus;
pid_t s, c, ret;
- int pipefds[2];
+ int unixfds[2];
parse_opts(argc, argv);
init_rng();
- e1 = pipe(pipefds);
+ e1 = socketpair(AF_UNIX, SOCK_STREAM, 0, unixfds);
if (e1 < 0)
- die_perror("pipe");
+ die_perror("socketpair");
s = xfork();
if (s == 0) {
- close(pipefds[0]);
- ret = server(pipefds[1]);
- close(pipefds[1]);
+ close(unixfds[0]);
+ ret = server(unixfds[1]);
+ close(unixfds[1]);
return ret;
}
- close(pipefds[1]);
+ close(unixfds[1]);
/* wait until server bound a socket */
- e1 = read(pipefds[0], &e1, 4);
+ e1 = read(unixfds[0], &e1, 4);
assert(e1 == 4);
c = xfork();
if (c == 0)
- return client(pipefds[0]);
+ return client(unixfds[0]);
- close(pipefds[0]);
+ close(unixfds[0]);
ret = waitpid(s, &wstatus, 0);
if (ret == -1)
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH mptcp-next v11 08/11] selftests: mptcp: sockopt: use recvmsg instead of read
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
` (6 preceding siblings ...)
2026-07-15 2:48 ` [PATCH mptcp-next v11 07/11] selftests: mptcp: sockopt: use unix socket instead of pipe Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 09/11] selftests: mptcp: sockopt: get and verify TCP_INQ Geliang Tang
` (3 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Replace read() with recvmsg() in process_one_client() to enable control
message (CMSG) handling. The original read(fd, buf, sizeof(buf)) is split
into two recvmsg() calls: the first reads exactly one byte, and the second
reads the remaining bytes. This allows the code to access CMSG data for
each received segment, facilitating the upcoming TCP_INQ validation.
Introduce inq_msg_init() and inq_msg_reset() helpers to manage the
struct inq_msg control buffer and iovec, enabling reuse of the same
msghdr across multiple recvmsg() calls with different payload buffers.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
.../selftests/net/mptcp/mptcp_sockopt.c | 48 +++++++++++++++++--
1 file changed, 43 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index 0d1426c11581..87a7090a68b4 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -677,36 +677,74 @@ static void check_stat_equal(const char *name, uint64_t actual,
name, actual, expected, (int64_t)(actual - expected));
}
+struct inq_msg {
+ char buf[4096];
+ union {
+ struct cmsghdr cmsg;
+ char msg_buf[4096];
+ } control;
+ struct iovec iov;
+ struct msghdr hdr;
+};
+
+static void inq_msg_init(struct inq_msg *m, size_t iov_len)
+{
+ memset(m, 0, sizeof(*m));
+ m->iov.iov_base = m->buf;
+ m->iov.iov_len = iov_len;
+ m->hdr.msg_iov = &m->iov;
+ m->hdr.msg_iovlen = 1;
+ m->hdr.msg_control = m->control.msg_buf;
+ m->hdr.msg_controllen = sizeof(m->control.msg_buf);
+}
+
+static void inq_msg_reset(struct inq_msg *m, size_t iov_len, void *base)
+{
+ m->iov.iov_base = base;
+ m->iov.iov_len = iov_len;
+ m->hdr.msg_controllen = sizeof(m->control.msg_buf);
+}
+
static void process_one_client(int fd, int unixfd)
{
struct so_state s;
- char buf[4096];
+ struct inq_msg m;
ssize_t ret;
size_t r, w;
+ inq_msg_init(&m, 1);
+
memset(&s, 0, sizeof(s));
do_getsockopts(&s, fd, 0, 0);
ret = write(unixfd, "xmit", 4);
assert(ret == 4);
- ret = read(fd, buf, sizeof(buf));
+ /* read one byte */
+ ret = recvmsg(fd, &m.hdr, 0);
if (ret < 0)
- die_perror("read");
+ die_perror("recvmsg");
r = ret;
+ inq_msg_reset(&m, sizeof(m.buf) - 1, m.buf + 1);
+ ret = recvmsg(fd, &m.hdr, 0);
+ if (ret < 0)
+ die_perror("recvmsg");
+ r += ret;
+
assert(s.mptcpi_rcv_delta <= (uint64_t)r);
if (s.tcpi_rcv_delta)
assert(s.tcpi_rcv_delta == (uint64_t)r);
- ret = write(fd, buf, r);
+ ret = write(fd, m.buf, r);
if (ret < 0)
die_perror("write");
w = ret;
/* wait for hangup */
- ret = read(fd, buf, 1);
+ inq_msg_reset(&m, 1, m.buf);
+ ret = recvmsg(fd, &m.hdr, 0);
if (ret != 0)
xerror("expected EOF, got %zd", ret);
r += ret;
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH mptcp-next v11 09/11] selftests: mptcp: sockopt: get and verify TCP_INQ
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
` (7 preceding siblings ...)
2026-07-15 2:48 ` [PATCH mptcp-next v11 08/11] selftests: mptcp: sockopt: use recvmsg instead of read Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 10/11] selftests: mptcp: sockopt: add huge data transfer tests Geliang Tang
` (2 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Implement FIONREAD ioctl checks to validate available data size, ensuring
synchronization between write and read operations with non-blocking waits.
Pass the payload length ('len') from the client to the server via the
unixfd control channel. This allows the server to know the exact amount
of data to expect, enabling precise FIONREAD loop and later TCP_CM_INQ
validation.
Add get_tcp_inq() helper to extract and verify TCP_CM_INQ values from
received control messages. Integrate it into the echo test to validate
in-queue byte counts at key stages, ensuring correct TCP_INQ behavior
with MPTCP.
Code adapted from mptcp_inq.c.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
.../selftests/net/mptcp/mptcp_sockopt.c | 72 ++++++++++++++++++-
1 file changed, 71 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index 87a7090a68b4..c754e6cdaa40 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -20,6 +20,7 @@
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/ioctl.h>
#include <netdb.h>
#include <netinet/in.h>
@@ -624,10 +625,12 @@ static void connect_one_server(int fd, int unixfd)
/* un-block server */
ret = read(unixfd, buf2, 4);
assert(ret == 4);
- close(unixfd);
assert(strncmp(buf2, "xmit", 4) == 0);
+ ret = write(unixfd, &len, sizeof(len));
+ assert(ret == (ssize_t)sizeof(len));
+
ret = write(fd, buf, len);
if (ret < 0)
die_perror("write");
@@ -665,6 +668,7 @@ static void connect_one_server(int fd, int unixfd)
if (proto_tx == IPPROTO_MPTCP && proto_rx == IPPROTO_MPTCP)
assert(s.mptcpi_rcv_delta == (uint64_t)total);
close(fd);
+ close(unixfd);
}
static void check_stat_equal(const char *name, uint64_t actual,
@@ -705,9 +709,30 @@ static void inq_msg_reset(struct inq_msg *m, size_t iov_len, void *base)
m->hdr.msg_controllen = sizeof(m->control.msg_buf);
}
+static void get_tcp_inq(struct msghdr *msgh, unsigned int *inqv)
+{
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(msgh); cmsg;
+ cmsg = CMSG_NXTHDR(msgh, cmsg)) {
+ if (cmsg->cmsg_level == IPPROTO_TCP &&
+ cmsg->cmsg_type == TCP_CM_INQ) {
+ if (cmsg->cmsg_len < CMSG_LEN(sizeof(*inqv)))
+ xerror("TCP_CM_INQ cmsg_len too small: %zu",
+ cmsg->cmsg_len);
+ memcpy(inqv, CMSG_DATA(cmsg), sizeof(*inqv));
+ return;
+ }
+ }
+
+ xerror("could not find TCP_CM_INQ cmsg type");
+}
+
static void process_one_client(int fd, int unixfd)
{
+ unsigned int tcp_inq;
struct so_state s;
+ size_t expect_len;
struct inq_msg m;
ssize_t ret;
size_t r, w;
@@ -720,16 +745,57 @@ static void process_one_client(int fd, int unixfd)
ret = write(unixfd, "xmit", 4);
assert(ret == 4);
+ ret = read(unixfd, &expect_len, sizeof(expect_len));
+ assert(ret == (ssize_t)sizeof(expect_len));
+
+ if (expect_len > sizeof(m.buf))
+ xerror("expect len %zu exceeds buffer size", expect_len);
+
+ for (;;) {
+ struct timespec req;
+ unsigned int queued;
+
+ ret = ioctl(fd, FIONREAD, &queued);
+ if (ret < 0)
+ die_perror("FIONREAD");
+ if (queued > expect_len)
+ xerror("FIONREAD returned %u, but only %zu expected\n",
+ queued, expect_len);
+ if (queued == expect_len)
+ break;
+
+ req.tv_sec = 0;
+ req.tv_nsec = 1000 * 1000ul;
+ nanosleep(&req, NULL);
+ }
+
/* read one byte */
ret = recvmsg(fd, &m.hdr, 0);
if (ret < 0)
die_perror("recvmsg");
+ if (inq) {
+ if (m.hdr.msg_controllen == 0)
+ xerror("msg_controllen is 0");
+
+ get_tcp_inq(&m.hdr, &tcp_inq);
+
+ /* expect cmsg to return expected - 1 */
+ assert((size_t)tcp_inq == (expect_len - 1));
+ }
r = ret;
inq_msg_reset(&m, sizeof(m.buf) - 1, m.buf + 1);
ret = recvmsg(fd, &m.hdr, 0);
if (ret < 0)
die_perror("recvmsg");
+ if (inq) {
+ /* should have gotten exact remainder of all pending data */
+ assert(ret == (ssize_t)tcp_inq);
+
+ /* should be 0, all drained */
+ get_tcp_inq(&m.hdr, &tcp_inq);
+ assert(tcp_inq == 0);
+ }
r += ret;
assert(s.mptcpi_rcv_delta <= (uint64_t)r);
@@ -747,6 +813,10 @@ static void process_one_client(int fd, int unixfd)
ret = recvmsg(fd, &m.hdr, 0);
if (ret != 0)
xerror("expected EOF, got %zd", ret);
+ if (inq) {
+ get_tcp_inq(&m.hdr, &tcp_inq);
+ assert(tcp_inq == 1);
+ }
r += ret;
do_getsockopts(&s, fd, r, w);
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH mptcp-next v11 10/11] selftests: mptcp: sockopt: add huge data transfer tests
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
` (8 preceding siblings ...)
2026-07-15 2:48 ` [PATCH mptcp-next v11 09/11] selftests: mptcp: sockopt: get and verify TCP_INQ Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 2:48 ` [PATCH mptcp-next v11 11/11] selftests: mptcp: sockopt: replace mptcp_inq with sockopt Geliang Tang
2026-07-15 3:52 ` [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt MPTCP CI
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Introduce huge data transfer tests (1-17 MB) to validate MPTCP socket
options under high-volume conditions, including proper EOF handling,
preparing for TCP_INQ support in subsequent patches.
Add wait_for_ack() helper to synchronize test steps by waiting until the
send queue is drained (all data ACKed). Used in server_huge_transfer()
to ensure reliable TCP_INQ validation.
These codes are from mptcp_inq.c, but here they have been placed into
two separate functions. And in client_huge_transfer(), do a blocking read
for EOF on unixfd instead of sleeping, and poll with POLLRDHUP to wait for
FIN arrival before reading the final byte, preventing flaky EOF detection.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
.../selftests/net/mptcp/mptcp_sockopt.c | 171 ++++++++++++++++++
1 file changed, 171 insertions(+)
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
index c754e6cdaa40..005779ebdabe 100644
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.c
@@ -16,6 +16,7 @@
#include <strings.h>
#include <time.h>
#include <unistd.h>
+#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
@@ -26,6 +27,7 @@
#include <netinet/in.h>
#include <linux/tcp.h>
+#include <linux/sockios.h>
#include <linux/compiler.h>
static int pf = AF_INET;
@@ -598,6 +600,95 @@ static void do_getsockopts(struct so_state *s, int fd, size_t r, size_t w)
do_getsockopt_mptcp_full_info(s, fd);
}
+/* wait up to timeout milliseconds */
+static void wait_for_ack(int fd, int timeout, size_t total)
+{
+ int i;
+
+ for (i = 0; i < timeout; i++) {
+ int nsd, ret, queued = -1;
+ struct timespec req;
+
+ ret = ioctl(fd, TIOCOUTQ, &queued);
+ if (ret < 0)
+ die_perror("TIOCOUTQ");
+
+ ret = ioctl(fd, SIOCOUTQNSD, &nsd);
+ if (ret < 0)
+ die_perror("SIOCOUTQNSD");
+
+ if ((size_t)queued > total)
+ xerror("TIOCOUTQ %u, but only %zu expected\n",
+ queued, total);
+ assert(nsd <= queued);
+
+ if (queued == 0)
+ return;
+
+ /* wait for peer to ack rx of all data */
+ req.tv_sec = 0;
+ req.tv_nsec = 1 * 1000 * 1000ul; /* 1ms */
+ nanosleep(&req, NULL);
+ }
+
+ xerror("still tx data queued after %u ms\n", timeout);
+}
+
+static void server_huge_transfer(int fd, int unixfd, size_t echo)
+{
+ char buf[4096], buf2[4];
+ size_t len, i, total;
+ size_t sent;
+ ssize_t ret;
+
+ for (i = 0; i < sizeof(buf) - 1; i++) {
+ buf[i] = rand() % 26;
+ buf[i] += 'A';
+ }
+
+ buf[i] = '\n';
+
+ ret = read(unixfd, buf2, 4);
+ assert(ret == 4);
+ assert(strncmp(buf2, "huge", 4) == 0);
+
+ total = rand() % (16 * 1024 * 1024);
+ total += (1 * 1024 * 1024);
+ sent = total;
+
+ ret = write(unixfd, &total, sizeof(total));
+ assert(ret == (ssize_t)sizeof(total));
+
+ wait_for_ack(fd, 5000, echo);
+
+ while (total > 0) {
+ if (total > sizeof(buf))
+ len = sizeof(buf);
+ else
+ len = total;
+
+ ret = write(fd, buf, len);
+ if (ret < 0)
+ die_perror("write");
+ total -= ret;
+
+ /* we don't have to care about buf content, only
+ * number of total bytes sent
+ */
+ }
+
+ ret = read(unixfd, buf2, 4);
+ assert(ret == 4);
+ assert(strncmp(buf2, "shut", 4) == 0);
+
+ wait_for_ack(fd, 5000, sent);
+
+ ret = write(fd, buf, 1);
+ assert(ret == 1);
+ ret = write(unixfd, "closed", 6);
+ assert(ret == 6);
+}
+
static void connect_one_server(int fd, int unixfd)
{
char buf[4096], buf2[4096];
@@ -667,6 +758,10 @@ static void connect_one_server(int fd, int unixfd)
if (proto_tx == IPPROTO_MPTCP && proto_rx == IPPROTO_MPTCP)
assert(s.mptcpi_rcv_delta == (uint64_t)total);
+
+ if (inq)
+ server_huge_transfer(fd, unixfd, total);
+
close(fd);
close(unixfd);
}
@@ -728,6 +823,79 @@ static void get_tcp_inq(struct msghdr *msgh, unsigned int *inqv)
xerror("could not find TCP_CM_INQ cmsg type");
}
+static size_t client_huge_transfer(int fd, int unixfd)
+{
+ struct pollfd pfd = { .fd = fd, .events = POLLRDHUP };
+ unsigned int tcp_inq;
+ size_t expect_len;
+ ssize_t ret, tot;
+ struct inq_msg m;
+ char tmp[16];
+
+ inq_msg_init(&m, 1);
+
+ /* request a large swath of data. */
+ ret = write(unixfd, "huge", 4);
+ assert(ret == 4);
+
+ ret = read(unixfd, &expect_len, sizeof(expect_len));
+ assert(ret == (ssize_t)sizeof(expect_len));
+
+ /* peer should send us a few mb of data */
+ if (expect_len <= sizeof(m.buf))
+ xerror("expect len %zu too small\n", expect_len);
+
+ tot = 0;
+ do {
+ inq_msg_reset(&m, sizeof(m.buf), m.buf);
+ ret = recvmsg(fd, &m.hdr, 0);
+ if (ret < 0)
+ die_perror("recvmsg");
+ if (ret == 0)
+ xerror("unexpected EOF in huge recv");
+
+ tot += ret;
+
+ get_tcp_inq(&m.hdr, &tcp_inq);
+
+ if (tcp_inq > expect_len - tot)
+ xerror("inq %d, remaining %zu total_len %d\n",
+ tcp_inq, expect_len - tot, (int)expect_len);
+
+ assert(tcp_inq <= expect_len - tot);
+ } while ((size_t)tot < expect_len);
+
+ ret = write(unixfd, "shut", 4);
+ assert(ret == 4);
+
+ /* wait for "closed"; server did final write on fd */
+ ret = read(unixfd, tmp, sizeof(tmp));
+ assert(ret == 6);
+ assert(strncmp(tmp, "closed", 6) == 0);
+
+ /* block on EOF: server close(fd) precedes close(unixfd) */
+ ret = read(unixfd, tmp, sizeof(tmp));
+ assert(ret == 0);
+
+ /* Wait for the TCP FIN to actually arrive. */
+ ret = poll(&pfd, 1, 5000);
+ assert(ret == 1);
+ assert(pfd.revents & POLLRDHUP);
+
+ inq_msg_reset(&m, 1, m.buf);
+ ret = recvmsg(fd, &m.hdr, 0);
+ if (ret < 0)
+ die_perror("recvmsg");
+ assert(ret == 1);
+
+ get_tcp_inq(&m.hdr, &tcp_inq);
+
+ /* tcp_inq should be 1 due to received fin. */
+ assert(tcp_inq == 1);
+
+ return tot + 1;
+}
+
static void process_one_client(int fd, int unixfd)
{
unsigned int tcp_inq;
@@ -808,6 +976,9 @@ static void process_one_client(int fd, int unixfd)
die_perror("write");
w = ret;
+ if (inq)
+ r += client_huge_transfer(fd, unixfd);
+
/* wait for hangup */
inq_msg_reset(&m, 1, m.buf);
ret = recvmsg(fd, &m.hdr, 0);
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH mptcp-next v11 11/11] selftests: mptcp: sockopt: replace mptcp_inq with sockopt
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
` (9 preceding siblings ...)
2026-07-15 2:48 ` [PATCH mptcp-next v11 10/11] selftests: mptcp: sockopt: add huge data transfer tests Geliang Tang
@ 2026-07-15 2:48 ` Geliang Tang
2026-07-15 3:52 ` [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt MPTCP CI
11 siblings, 0 replies; 13+ messages in thread
From: Geliang Tang @ 2026-07-15 2:48 UTC (permalink / raw)
To: mptcp; +Cc: Geliang Tang
From: Geliang Tang <tanggeliang@kylinos.cn>
Update mptcp_sockopt.sh to use mptcp_sockopt binary instead of
mptcp_inq. This consolidates TCP_INQ testing into a single test binary
while maintaining equivalent test coverage.
Delete mptcp_inq binary and references since its functionality has been
fully integrated into mptcp_sockopt. This simplifies the test suite
structure and reduces maintenance overhead.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
tools/testing/selftests/net/mptcp/.gitignore | 1 -
tools/testing/selftests/net/mptcp/Makefile | 1 -
tools/testing/selftests/net/mptcp/mptcp_inq.c | 614 ------------------
.../selftests/net/mptcp/mptcp_sockopt.sh | 8 +-
4 files changed, 4 insertions(+), 620 deletions(-)
delete mode 100644 tools/testing/selftests/net/mptcp/mptcp_inq.c
diff --git a/tools/testing/selftests/net/mptcp/.gitignore b/tools/testing/selftests/net/mptcp/.gitignore
index 833279fb34e2..c62e91ce385d 100644
--- a/tools/testing/selftests/net/mptcp/.gitignore
+++ b/tools/testing/selftests/net/mptcp/.gitignore
@@ -1,7 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
mptcp_connect
mptcp_diag
-mptcp_inq
mptcp_sockopt
pm_nl_ctl
*.pcap
diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile
index 9e47ecfe58e2..d5fab4d26de7 100644
--- a/tools/testing/selftests/net/mptcp/Makefile
+++ b/tools/testing/selftests/net/mptcp/Makefile
@@ -23,7 +23,6 @@ TEST_PROGS := \
TEST_GEN_FILES := \
mptcp_connect \
mptcp_diag \
- mptcp_inq \
mptcp_sockopt \
pm_nl_ctl \
# end of TEST_GEN_FILES
diff --git a/tools/testing/selftests/net/mptcp/mptcp_inq.c b/tools/testing/selftests/net/mptcp/mptcp_inq.c
deleted file mode 100644
index 5716998da192..000000000000
--- a/tools/testing/selftests/net/mptcp/mptcp_inq.c
+++ /dev/null
@@ -1,614 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#define _GNU_SOURCE
-
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <time.h>
-
-#include <sys/ioctl.h>
-#include <sys/random.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <netdb.h>
-#include <netinet/in.h>
-
-#include <linux/tcp.h>
-#include <linux/sockios.h>
-#include <linux/compiler.h>
-
-#ifndef IPPROTO_MPTCP
-#define IPPROTO_MPTCP 262
-#endif
-#ifndef SOL_MPTCP
-#define SOL_MPTCP 284
-#endif
-
-static int pf = AF_INET;
-static int proto_tx = IPPROTO_MPTCP;
-static int proto_rx = IPPROTO_MPTCP;
-
-static void __noreturn die_perror(const char *msg)
-{
- perror(msg);
- exit(1);
-}
-
-static void die_usage(int r)
-{
- fprintf(stderr, "Usage: mptcp_inq [-6] [ -t tcp|mptcp ] [ -r tcp|mptcp]\n");
- exit(r);
-}
-
-static void __noreturn xerror(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fputc('\n', stderr);
- exit(1);
-}
-
-static const char *getxinfo_strerr(int err)
-{
- if (err == EAI_SYSTEM)
- return strerror(errno);
-
- return gai_strerror(err);
-}
-
-static void xgetaddrinfo(const char *node, const char *service,
- struct addrinfo *hints,
- struct addrinfo **res)
-{
- int err;
-
-again:
- err = getaddrinfo(node, service, hints, res);
- if (err) {
- const char *errstr;
-
- if (err == EAI_SOCKTYPE) {
- hints->ai_protocol = IPPROTO_TCP;
- goto again;
- }
-
- errstr = getxinfo_strerr(err);
-
- fprintf(stderr, "Fatal: getaddrinfo(%s:%s): %s\n",
- node ? node : "", service ? service : "", errstr);
- exit(1);
- }
-}
-
-static int sock_listen_mptcp(const char * const listenaddr,
- const char * const port)
-{
- int sock = -1;
- struct addrinfo hints = {
- .ai_protocol = IPPROTO_MPTCP,
- .ai_socktype = SOCK_STREAM,
- .ai_flags = AI_PASSIVE | AI_NUMERICHOST
- };
-
- hints.ai_family = pf;
-
- struct addrinfo *a, *addr;
- int one = 1;
-
- xgetaddrinfo(listenaddr, port, &hints, &addr);
- hints.ai_family = pf;
-
- for (a = addr; a; a = a->ai_next) {
- sock = socket(a->ai_family, a->ai_socktype, proto_rx);
- if (sock < 0)
- continue;
-
- if (-1 == setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one,
- sizeof(one)))
- perror("setsockopt");
-
- if (bind(sock, a->ai_addr, a->ai_addrlen) == 0)
- break; /* success */
-
- perror("bind");
- close(sock);
- sock = -1;
- }
-
- freeaddrinfo(addr);
-
- if (sock < 0)
- xerror("could not create listen socket");
-
- if (listen(sock, 20))
- die_perror("listen");
-
- return sock;
-}
-
-static int sock_connect_mptcp(const char * const remoteaddr,
- const char * const port, int proto)
-{
- struct addrinfo hints = {
- .ai_protocol = IPPROTO_MPTCP,
- .ai_socktype = SOCK_STREAM,
- };
- struct addrinfo *a, *addr;
- int sock = -1;
-
- hints.ai_family = pf;
-
- xgetaddrinfo(remoteaddr, port, &hints, &addr);
- for (a = addr; a; a = a->ai_next) {
- sock = socket(a->ai_family, a->ai_socktype, proto);
- if (sock < 0)
- continue;
-
- if (connect(sock, a->ai_addr, a->ai_addrlen) == 0)
- break; /* success */
-
- die_perror("connect");
- }
-
- if (sock < 0)
- xerror("could not create connect socket");
-
- freeaddrinfo(addr);
- return sock;
-}
-
-static int protostr_to_num(const char *s)
-{
- if (strcasecmp(s, "tcp") == 0)
- return IPPROTO_TCP;
- if (strcasecmp(s, "mptcp") == 0)
- return IPPROTO_MPTCP;
-
- die_usage(1);
- return 0;
-}
-
-static void parse_opts(int argc, char **argv)
-{
- int c;
-
- while ((c = getopt(argc, argv, "h6t:r:")) != -1) {
- switch (c) {
- case 'h':
- die_usage(0);
- break;
- case '6':
- pf = AF_INET6;
- break;
- case 't':
- proto_tx = protostr_to_num(optarg);
- break;
- case 'r':
- proto_rx = protostr_to_num(optarg);
- break;
- default:
- die_usage(1);
- break;
- }
- }
-}
-
-/* wait up to timeout milliseconds */
-static void wait_for_ack(int fd, int timeout, size_t total)
-{
- int i;
-
- for (i = 0; i < timeout; i++) {
- int nsd, ret, queued = -1;
- struct timespec req;
-
- ret = ioctl(fd, TIOCOUTQ, &queued);
- if (ret < 0)
- die_perror("TIOCOUTQ");
-
- ret = ioctl(fd, SIOCOUTQNSD, &nsd);
- if (ret < 0)
- die_perror("SIOCOUTQNSD");
-
- if ((size_t)queued > total)
- xerror("TIOCOUTQ %u, but only %zu expected\n", queued, total);
- assert(nsd <= queued);
-
- if (queued == 0)
- return;
-
- /* wait for peer to ack rx of all data */
- req.tv_sec = 0;
- req.tv_nsec = 1 * 1000 * 1000ul; /* 1ms */
- nanosleep(&req, NULL);
- }
-
- xerror("still tx data queued after %u ms\n", timeout);
-}
-
-static void connect_one_server(int fd, int unixfd)
-{
- size_t len, i, total, sent;
- char buf[4096], buf2[4096];
- ssize_t ret;
-
- len = rand() % (sizeof(buf) - 1);
-
- if (len < 128)
- len = 128;
-
- for (i = 0; i < len ; i++) {
- buf[i] = rand() % 26;
- buf[i] += 'A';
- }
-
- buf[i] = '\n';
-
- /* un-block server */
- ret = read(unixfd, buf2, 4);
- assert(ret == 4);
-
- assert(strncmp(buf2, "xmit", 4) == 0);
-
- ret = write(unixfd, &len, sizeof(len));
- assert(ret == (ssize_t)sizeof(len));
-
- ret = write(fd, buf, len);
- if (ret < 0)
- die_perror("write");
-
- if (ret != (ssize_t)len)
- xerror("short write");
-
- ret = read(unixfd, buf2, 4);
- assert(strncmp(buf2, "huge", 4) == 0);
-
- total = rand() % (16 * 1024 * 1024);
- total += (1 * 1024 * 1024);
- sent = total;
-
- ret = write(unixfd, &total, sizeof(total));
- assert(ret == (ssize_t)sizeof(total));
-
- wait_for_ack(fd, 5000, len);
-
- while (total > 0) {
- if (total > sizeof(buf))
- len = sizeof(buf);
- else
- len = total;
-
- ret = write(fd, buf, len);
- if (ret < 0)
- die_perror("write");
- total -= ret;
-
- /* we don't have to care about buf content, only
- * number of total bytes sent
- */
- }
-
- ret = read(unixfd, buf2, 4);
- assert(ret == 4);
- assert(strncmp(buf2, "shut", 4) == 0);
-
- wait_for_ack(fd, 5000, sent);
-
- ret = write(fd, buf, 1);
- assert(ret == 1);
- close(fd);
- ret = write(unixfd, "closed", 6);
- assert(ret == 6);
-
- close(unixfd);
-}
-
-static void get_tcp_inq(struct msghdr *msgh, unsigned int *inqv)
-{
- struct cmsghdr *cmsg;
-
- for (cmsg = CMSG_FIRSTHDR(msgh); cmsg ; cmsg = CMSG_NXTHDR(msgh, cmsg)) {
- if (cmsg->cmsg_level == IPPROTO_TCP && cmsg->cmsg_type == TCP_CM_INQ) {
- memcpy(inqv, CMSG_DATA(cmsg), sizeof(*inqv));
- return;
- }
- }
-
- xerror("could not find TCP_CM_INQ cmsg type");
-}
-
-static void process_one_client(int fd, int unixfd)
-{
- unsigned int tcp_inq;
- size_t expect_len;
- char msg_buf[4096];
- char buf[4096];
- char tmp[16];
- struct iovec iov = {
- .iov_base = buf,
- .iov_len = 1,
- };
- struct msghdr msg = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = msg_buf,
- .msg_controllen = sizeof(msg_buf),
- };
- ssize_t ret, tot;
-
- ret = write(unixfd, "xmit", 4);
- assert(ret == 4);
-
- ret = read(unixfd, &expect_len, sizeof(expect_len));
- assert(ret == (ssize_t)sizeof(expect_len));
-
- if (expect_len > sizeof(buf))
- xerror("expect len %zu exceeds buffer size", expect_len);
-
- for (;;) {
- struct timespec req;
- unsigned int queued;
-
- ret = ioctl(fd, FIONREAD, &queued);
- if (ret < 0)
- die_perror("FIONREAD");
- if (queued > expect_len)
- xerror("FIONREAD returned %u, but only %zu expected\n",
- queued, expect_len);
- if (queued == expect_len)
- break;
-
- req.tv_sec = 0;
- req.tv_nsec = 1000 * 1000ul;
- nanosleep(&req, NULL);
- }
-
- /* read one byte, expect cmsg to return expected - 1 */
- ret = recvmsg(fd, &msg, 0);
- if (ret < 0)
- die_perror("recvmsg");
-
- if (msg.msg_controllen == 0)
- xerror("msg_controllen is 0");
-
- get_tcp_inq(&msg, &tcp_inq);
-
- assert((size_t)tcp_inq == (expect_len - 1));
-
- iov.iov_len = sizeof(buf);
- ret = recvmsg(fd, &msg, 0);
- if (ret < 0)
- die_perror("recvmsg");
-
- /* should have gotten exact remainder of all pending data */
- assert(ret == (ssize_t)tcp_inq);
-
- /* should be 0, all drained */
- get_tcp_inq(&msg, &tcp_inq);
- assert(tcp_inq == 0);
-
- /* request a large swath of data. */
- ret = write(unixfd, "huge", 4);
- assert(ret == 4);
-
- ret = read(unixfd, &expect_len, sizeof(expect_len));
- assert(ret == (ssize_t)sizeof(expect_len));
-
- /* peer should send us a few mb of data */
- if (expect_len <= sizeof(buf))
- xerror("expect len %zu too small\n", expect_len);
-
- tot = 0;
- do {
- iov.iov_len = sizeof(buf);
- ret = recvmsg(fd, &msg, 0);
- if (ret < 0)
- die_perror("recvmsg");
-
- tot += ret;
-
- get_tcp_inq(&msg, &tcp_inq);
-
- if (tcp_inq > expect_len - tot)
- xerror("inq %d, remaining %d total_len %d\n",
- tcp_inq, expect_len - tot, (int)expect_len);
-
- assert(tcp_inq <= expect_len - tot);
- } while ((size_t)tot < expect_len);
-
- ret = write(unixfd, "shut", 4);
- assert(ret == 4);
-
- /* wait for hangup. Should have received one more byte of data. */
- ret = read(unixfd, tmp, sizeof(tmp));
- assert(ret == 6);
- assert(strncmp(tmp, "closed", 6) == 0);
-
- sleep(1);
-
- iov.iov_len = 1;
- ret = recvmsg(fd, &msg, 0);
- if (ret < 0)
- die_perror("recvmsg");
- assert(ret == 1);
-
- get_tcp_inq(&msg, &tcp_inq);
-
- /* tcp_inq should be 1 due to received fin. */
- assert(tcp_inq == 1);
-
- iov.iov_len = 1;
- ret = recvmsg(fd, &msg, 0);
- if (ret < 0)
- die_perror("recvmsg");
-
- /* expect EOF */
- assert(ret == 0);
- get_tcp_inq(&msg, &tcp_inq);
- assert(tcp_inq == 1);
-
- close(fd);
-}
-
-static int xaccept(int s)
-{
- int fd = accept(s, NULL, 0);
-
- if (fd < 0)
- die_perror("accept");
-
- return fd;
-}
-
-static int server(int unixfd)
-{
- int fd = -1, r, on = 1;
-
- switch (pf) {
- case AF_INET:
- fd = sock_listen_mptcp("127.0.0.1", "15432");
- break;
- case AF_INET6:
- fd = sock_listen_mptcp("::1", "15432");
- break;
- default:
- xerror("Unknown pf %d\n", pf);
- break;
- }
-
- r = write(unixfd, "conn", 4);
- assert(r == 4);
-
- alarm(15);
- r = xaccept(fd);
-
- if (-1 == setsockopt(r, IPPROTO_TCP, TCP_INQ, &on, sizeof(on)))
- die_perror("setsockopt");
-
- process_one_client(r, unixfd);
-
- close(fd);
- return 0;
-}
-
-static int client(int unixfd)
-{
- int fd = -1;
-
- alarm(15);
-
- switch (pf) {
- case AF_INET:
- fd = sock_connect_mptcp("127.0.0.1", "15432", proto_tx);
- break;
- case AF_INET6:
- fd = sock_connect_mptcp("::1", "15432", proto_tx);
- break;
- default:
- xerror("Unknown pf %d\n", pf);
- }
-
- connect_one_server(fd, unixfd);
-
- return 0;
-}
-
-static void init_rng(void)
-{
- unsigned int foo;
-
- if (getrandom(&foo, sizeof(foo), 0) == -1) {
- perror("getrandom");
- exit(1);
- }
-
- srand(foo);
-}
-
-static pid_t xfork(void)
-{
- pid_t p = fork();
-
- if (p < 0)
- die_perror("fork");
- else if (p == 0)
- init_rng();
-
- return p;
-}
-
-static int rcheck(int wstatus, const char *what)
-{
- if (WIFEXITED(wstatus)) {
- if (WEXITSTATUS(wstatus) == 0)
- return 0;
- fprintf(stderr, "%s exited, status=%d\n", what, WEXITSTATUS(wstatus));
- return WEXITSTATUS(wstatus);
- } else if (WIFSIGNALED(wstatus)) {
- xerror("%s killed by signal %d\n", what, WTERMSIG(wstatus));
- } else if (WIFSTOPPED(wstatus)) {
- xerror("%s stopped by signal %d\n", what, WSTOPSIG(wstatus));
- }
-
- return 111;
-}
-
-int main(int argc, char *argv[])
-{
- int e1, e2, wstatus;
- pid_t s, c, ret;
- int unixfds[2];
-
- parse_opts(argc, argv);
-
- e1 = socketpair(AF_UNIX, SOCK_DGRAM, 0, unixfds);
- if (e1 < 0)
- die_perror("pipe");
-
- s = xfork();
- if (s == 0) {
- close(unixfds[0]);
- ret = server(unixfds[1]);
- close(unixfds[1]);
- return ret;
- }
-
- close(unixfds[1]);
-
- /* wait until server bound a socket */
- e1 = read(unixfds[0], &e1, 4);
- assert(e1 == 4);
-
- c = xfork();
- if (c == 0)
- return client(unixfds[0]);
-
- close(unixfds[0]);
-
- ret = waitpid(s, &wstatus, 0);
- if (ret == -1)
- die_perror("waitpid");
- e1 = rcheck(wstatus, "server");
- ret = waitpid(c, &wstatus, 0);
- if (ret == -1)
- die_perror("waitpid");
- e2 = rcheck(wstatus, "client");
-
- return e1 ? e1 : e2;
-}
diff --git a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh
index e850a87429b6..a39265555438 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_sockopt.sh
@@ -308,7 +308,7 @@ run_tests()
do_tcpinq_test()
{
print_title "TCP_INQ cmsg/ioctl $*"
- ip netns exec "$ns_sbox" ./mptcp_inq "$@"
+ ip netns exec "$ns_sbox" ./mptcp_sockopt "$@"
local lret=$?
if [ $lret -ne 0 ];then
ret=$lret
@@ -334,19 +334,19 @@ do_tcpinq_tests()
local args
for args in "-t tcp" "-r tcp"; do
- do_tcpinq_test $args
+ do_tcpinq_test $args -i
lret=$?
if [ $lret -ne 0 ] ; then
return $lret
fi
- do_tcpinq_test -6 $args
+ do_tcpinq_test -6 $args -i
lret=$?
if [ $lret -ne 0 ] ; then
return $lret
fi
done
- do_tcpinq_test -r tcp -t tcp
+ do_tcpinq_test -r tcp -t tcp -i
return $?
}
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt
2026-07-15 2:48 [PATCH mptcp-next v11 00/11] selftests: consolidate TCP_INQ testing into sockopt Geliang Tang
` (10 preceding siblings ...)
2026-07-15 2:48 ` [PATCH mptcp-next v11 11/11] selftests: mptcp: sockopt: replace mptcp_inq with sockopt Geliang Tang
@ 2026-07-15 3:52 ` MPTCP CI
11 siblings, 0 replies; 13+ messages in thread
From: MPTCP CI @ 2026-07-15 3:52 UTC (permalink / raw)
To: Geliang Tang; +Cc: mptcp
Hi Geliang,
Thank you for your modifications, that's great!
Our CI did some validations and here is its report:
- KVM Validation: normal (except selftest_mptcp_join): Success! ✅
- KVM Validation: normal (only selftest_mptcp_join): Success! ✅
- KVM Validation: debug (except selftest_mptcp_join): Success! ✅
- KVM Validation: debug (only selftest_mptcp_join): Success! ✅
- KVM Validation: btf-normal (only bpftest_all): Success! ✅
- KVM Validation: btf-debug (only bpftest_all): Success! ✅
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/29385676727
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/ecf3e45a76bb
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1127842
If there are some issues, you can reproduce them using the same environment as
the one used by the CI thanks to a docker image, e.g.:
$ cd [kernel source code]
$ docker run -v "${PWD}:${PWD}:rw" -w "${PWD}" --privileged --rm -it \
--pull always mptcp/mptcp-upstream-virtme-docker:latest \
auto-normal
For more details:
https://github.com/multipath-tcp/mptcp-upstream-virtme-docker
Please note that despite all the efforts that have been already done to have a
stable tests suite when executed on a public CI like here, it is possible some
reported issues are not due to your modifications. Still, do not hesitate to
help us improve that ;-)
Cheers,
MPTCP GH Action bot
Bot operated by Matthieu Baerts (NGI0 Core)
^ permalink raw reply [flat|nested] 13+ messages in thread