[Patch mptcp-net 1/3] mptcp: add backlog_list bug reproducer test

Gang Yan posted 3 patches 3 days, 15 hours ago
[Patch mptcp-net 1/3] mptcp: add backlog_list bug reproducer test
Posted by Gang Yan 3 days, 15 hours ago
From: Gang Yan <yangang@kylinos.cn>

This patch introduces a test program to reproduce bugs related to the
backlog_list in MPTCP. The test is derived from tls.c in the selftests
suite, but adapted to work without TLS configuration specifically for
MPTCP testing.

The program performs chunked sendfile operations with various payload
sizes to exercise different code paths and trigger backlog_list-related
issues.

It can be run in .virtme-exec-run:
        run_loop run_selftest_one ./multi_chunk.sh

'''
selftest Test: ./multi_chunk.sh
TAP version 13
1..1
[stalls for a while]
^C

'''

Co-developed-by: Geliang Tang <tanggeliang@kylinos.cn>
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
Signed-off-by: Gang Yan <yangang@kylinos.cn>
---
 tools/testing/selftests/net/mptcp/Makefile    |   1 +
 .../testing/selftests/net/mptcp/multi_chunk.c | 148 ++++++++++++++++++
 .../selftests/net/mptcp/multi_chunk.sh        |  37 +++++
 3 files changed, 186 insertions(+)
 create mode 100644 tools/testing/selftests/net/mptcp/multi_chunk.c
 create mode 100755 tools/testing/selftests/net/mptcp/multi_chunk.sh

diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile
index 22ba0da2adb8..087e6ae6f0b8 100644
--- a/tools/testing/selftests/net/mptcp/Makefile
+++ b/tools/testing/selftests/net/mptcp/Makefile
@@ -24,6 +24,7 @@ TEST_GEN_FILES := \
 	mptcp_diag \
 	mptcp_inq \
 	mptcp_sockopt \
+	multi_chunk \
 	pm_nl_ctl \
 # end of TEST_GEN_FILES
 
diff --git a/tools/testing/selftests/net/mptcp/multi_chunk.c b/tools/testing/selftests/net/mptcp/multi_chunk.c
new file mode 100644
index 000000000000..8c97db58a6db
--- /dev/null
+++ b/tools/testing/selftests/net/mptcp/multi_chunk.c
@@ -0,0 +1,148 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/sendfile.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <sys/wait.h>
+
+#ifndef IPPROTO_MPTCP
+#define IPPROTO_MPTCP 262
+#endif
+
+#define TLS_PAYLOAD_MAX_LEN 16384
+#define TEST_PORT 12345
+
+static void chunked_sendfile(int cfd, int sfd,
+			     size_t chunk_size,
+			     size_t extra_payload_size)
+{
+	char buf[TLS_PAYLOAD_MAX_LEN];
+	uint16_t test_payload_size;
+	size_t recved = 0;
+	size_t sent = 0;
+	int size = 0;
+	int ret;
+	char filename[] = "/tmp/mytemp.XXXXXX";
+	int fd = mkstemp(filename);
+	off_t offset = 0;
+
+	unlink(filename);
+	if (fd <= 0) {
+		perror("tempfile");
+		exit(1);
+	}
+	if (chunk_size < 1) {
+		perror("chunksize");
+		exit(1);
+	}
+
+	test_payload_size = chunk_size + extra_payload_size;
+	if (test_payload_size > TLS_PAYLOAD_MAX_LEN) {
+		perror("payload_size");
+		exit(1);
+	}
+	memset(buf, 1, test_payload_size);
+	size = write(fd, buf, test_payload_size);
+	if (size != test_payload_size) {
+		perror("file write");
+		exit(1);
+	}
+	fsync(fd);
+
+	while (size > 0) {
+		ret = sendfile(sfd, fd, &offset, chunk_size);
+		if (ret <= 0)
+			exit(1);
+		size -= ret;
+		sent += ret;
+	}
+	printf("[client] sent %zu bytes\n", sent);
+
+	recved = recv(cfd, buf, test_payload_size, MSG_WAITALL);
+	printf("[server] receieved %zu bytes\n", recved);
+
+	if (recved != test_payload_size)
+		exit(1);
+
+	close(fd);
+}
+
+int main()
+{
+	int sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
+	int cfd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
+	struct sockaddr_in addr = {0};
+	socklen_t addrlen = sizeof(addr);
+
+	printf("==== multi_chunk_sendfile MPTCP test ====\n");
+
+	if (sfd < 0 || cfd < 0) {
+		perror("socket");
+		exit(1);
+	}
+
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	addr.sin_port = htons(TEST_PORT);
+
+	if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		perror("bind");
+		exit(1);
+	}
+
+	if (listen(sfd, 1) < 0) {
+		perror("listen");
+		exit(1);
+	}
+
+	if (getsockname(sfd, (struct sockaddr *)&addr, &addrlen) < 0) {
+		perror("getsockname");
+		exit(1);
+	}
+
+	if (connect(cfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+		perror("connect");
+		exit(1);
+	}
+
+	int nfd = accept(sfd, NULL, NULL);
+	if (nfd < 0) {
+		perror("accept");
+		exit(1);
+	}
+
+	chunked_sendfile(cfd, nfd, 4096, 4096);
+	chunked_sendfile(cfd, nfd, 4096, 0);
+	chunked_sendfile(cfd, nfd, 4096, 1);
+	chunked_sendfile(cfd, nfd, 4096, 2048);
+	chunked_sendfile(cfd, nfd, 8192, 2048);
+	chunked_sendfile(cfd, nfd, 4096, 8192);
+	chunked_sendfile(cfd, nfd, 8192, 4096);
+	chunked_sendfile(cfd, nfd, 12288, 1024);
+	chunked_sendfile(cfd, nfd, 12288, 2000);
+	chunked_sendfile(cfd, nfd, 15360, 100);
+	chunked_sendfile(cfd, nfd, 15360, 300);
+	chunked_sendfile(cfd, nfd, 1, 4096);
+	chunked_sendfile(cfd, nfd, 2048, 4096);
+	chunked_sendfile(cfd, nfd, 2048, 8192);
+	chunked_sendfile(cfd, nfd, 4096, 8192);
+	chunked_sendfile(cfd, nfd, 1024, 12288);
+	chunked_sendfile(cfd, nfd, 2000, 12288);
+	chunked_sendfile(cfd, nfd, 100, 15360);
+	chunked_sendfile(cfd, nfd, 300, 15360);
+
+	close(cfd);
+	close(nfd);
+	close(sfd);
+
+	printf("==== test ends ====\n");
+	return 0;
+}
diff --git a/tools/testing/selftests/net/mptcp/multi_chunk.sh b/tools/testing/selftests/net/mptcp/multi_chunk.sh
new file mode 100755
index 000000000000..c0352c89087f
--- /dev/null
+++ b/tools/testing/selftests/net/mptcp/multi_chunk.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+. "$(dirname "${0}")/mptcp_lib.sh"
+
+cleanup()
+{
+	if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
+		kill "$pid" 2>/dev/null
+		wait "$pid" 2>/dev/null
+	fi
+
+	mptcp_lib_ns_exit "${ns1}"
+}
+
+init()
+{
+	mptcp_lib_ns_init ns1
+
+	local i
+	for i in $(seq 1 4); do
+		mptcp_lib_pm_nl_add_endpoint "${ns1}" "127.0.0.1" flags signal port 1000$i
+	done
+
+	mptcp_lib_pm_nl_set_limits "${ns1}" 8 8
+
+	ip netns exec ${ns1} ip mptcp endpoint show
+	ip netns exec ${ns1} ip mptcp limits
+}
+
+init
+trap cleanup EXIT
+
+ip netns exec $ns1 ./multi_chunk &
+
+pid=$!
+wait $pid
-- 
2.43.0
Re: [Patch mptcp-net 1/3] mptcp: add backlog_list bug reproducer test
Posted by Matthieu Baerts 3 days, 3 hours ago
Hi Gang,

Thank you for looking at this.

Small detail: I'm not sure what you are using to generate the patches
(b4 prep/send is great BTW), but it is strange to have 'Patch' instead
of PATCH in the subjects.

On 05/02/2026 07:41, Gang Yan wrote:
> From: Gang Yan <yangang@kylinos.cn>
> 
> This patch introduces a test program to reproduce bugs related to the
> backlog_list in MPTCP. The test is derived from tls.c in the selftests
> suite, but adapted to work without TLS configuration specifically for
> MPTCP testing.
> 
> The program performs chunked sendfile operations with various payload
> sizes to exercise different code paths and trigger backlog_list-related
> issues.

I understood it was just a tmp patch just to show how to reproduce the
issue, but I think it is good to make sure such regression is not
reintroduced. So if this test is quick to run, it should be kept. No?

Except if the error could be reproduced with packetdrill? Or eventually
if mptcp_connect.(c|sh) could be easily adapted to reproduce the issue
instead?

> 
> It can be run in .virtme-exec-run:
>         run_loop run_selftest_one ./multi_chunk.sh

(That's specific to the MPTCP docker image, no need to specify this here
to avoid confusions)

> 
> '''
> selftest Test: ./multi_chunk.sh
> TAP version 13
> 1..1
> [stalls for a while]
> ^C
> 
> '''
> 
> Co-developed-by: Geliang Tang <tanggeliang@kylinos.cn>
> Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
> Signed-off-by: Gang Yan <yangang@kylinos.cn>
> ---
>  tools/testing/selftests/net/mptcp/Makefile    |   1 +
>  .../testing/selftests/net/mptcp/multi_chunk.c | 148 ++++++++++++++++++
>  .../selftests/net/mptcp/multi_chunk.sh        |  37 +++++
>  3 files changed, 186 insertions(+)
>  create mode 100644 tools/testing/selftests/net/mptcp/multi_chunk.c
>  create mode 100755 tools/testing/selftests/net/mptcp/multi_chunk.sh
> 
> diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile
> index 22ba0da2adb8..087e6ae6f0b8 100644
> --- a/tools/testing/selftests/net/mptcp/Makefile
> +++ b/tools/testing/selftests/net/mptcp/Makefile
> @@ -24,6 +24,7 @@ TEST_GEN_FILES := \
>  	mptcp_diag \
>  	mptcp_inq \
>  	mptcp_sockopt \
> +	multi_chunk \
>  	pm_nl_ctl \
>  # end of TEST_GEN_FILES
>  
> diff --git a/tools/testing/selftests/net/mptcp/multi_chunk.c b/tools/testing/selftests/net/mptcp/multi_chunk.c
> new file mode 100644
> index 000000000000..8c97db58a6db
> --- /dev/null
> +++ b/tools/testing/selftests/net/mptcp/multi_chunk.c
> @@ -0,0 +1,148 @@
> +#define _GNU_SOURCE
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +#include <sys/sendfile.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <errno.h>
> +#include <arpa/inet.h>
> +#include <sys/wait.h>

Please sort the headers, see:

https://lore.kernel.org/mptcp/20260130-mptcp-sft-sort-includes-v1-1-ab022fad9741@kernel.org/

> +
> +#ifndef IPPROTO_MPTCP
> +#define IPPROTO_MPTCP 262
> +#endif
> +
> +#define TLS_PAYLOAD_MAX_LEN 16384
> +#define TEST_PORT 12345
> +
> +static void chunked_sendfile(int cfd, int sfd,
> +			     size_t chunk_size,
> +			     size_t extra_payload_size)
> +{
> +	char buf[TLS_PAYLOAD_MAX_LEN];
> +	uint16_t test_payload_size;
> +	size_t recved = 0;
> +	size_t sent = 0;
> +	int size = 0;
> +	int ret;
> +	char filename[] = "/tmp/mytemp.XXXXXX";
> +	int fd = mkstemp(filename);
> +	off_t offset = 0;

Ideally keep the reverse XMAS tree for the order.

> +
> +	unlink(filename);
> +	if (fd <= 0) {
> +		perror("tempfile");
> +		exit(1);
> +	}
> +	if (chunk_size < 1) {
> +		perror("chunksize");
> +		exit(1);
> +	}
> +
> +	test_payload_size = chunk_size + extra_payload_size;
> +	if (test_payload_size > TLS_PAYLOAD_MAX_LEN) {
> +		perror("payload_size");
> +		exit(1);
> +	}
> +	memset(buf, 1, test_payload_size);
> +	size = write(fd, buf, test_payload_size);
> +	if (size != test_payload_size) {
> +		perror("file write");
> +		exit(1);
> +	}
> +	fsync(fd);
> +
> +	while (size > 0) {
> +		ret = sendfile(sfd, fd, &offset, chunk_size);
> +		if (ret <= 0)

It would be good to print an error to stderr.

Also, maybe you will need to at least close(fd) to be sure the tmp file
will be removed, no?


> +			exit(1);
> +		size -= ret;
> +		sent += ret;
> +	}
> +	printf("[client] sent %zu bytes\n", sent);
> +
> +	recved = recv(cfd, buf, test_payload_size, MSG_WAITALL);
> +	printf("[server] receieved %zu bytes\n", recved);
> +
> +	if (recved != test_payload_size)

Same here.

> +		exit(1);
> +
> +	close(fd);
> +}
> +
> +int main()
> +{
> +	int sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
> +	int cfd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
> +	struct sockaddr_in addr = {0};
> +	socklen_t addrlen = sizeof(addr);
> +
> +	printf("==== multi_chunk_sendfile MPTCP test ====\n");

Maybe these printf() here and at the bottom are not really needed?

> +
> +	if (sfd < 0 || cfd < 0) {
> +		perror("socket");
> +		exit(1);
> +	}
> +
> +	addr.sin_family = AF_INET;
> +	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> +	addr.sin_port = htons(TEST_PORT);
> +
> +	if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> +		perror("bind");
> +		exit(1);
> +	}
> +
> +	if (listen(sfd, 1) < 0) {
> +		perror("listen");
> +		exit(1);
> +	}
> +
> +	if (getsockname(sfd, (struct sockaddr *)&addr, &addrlen) < 0) {
> +		perror("getsockname");
> +		exit(1);
> +	}
> +
> +	if (connect(cfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> +		perror("connect");
> +		exit(1);
> +	}
> +
> +	int nfd = accept(sfd, NULL, NULL);
> +	if (nfd < 0) {
> +		perror("accept");
> +		exit(1);
> +	}
> +
> +	chunked_sendfile(cfd, nfd, 4096, 4096);
> +	chunked_sendfile(cfd, nfd, 4096, 0);
> +	chunked_sendfile(cfd, nfd, 4096, 1);
> +	chunked_sendfile(cfd, nfd, 4096, 2048);
> +	chunked_sendfile(cfd, nfd, 8192, 2048);
> +	chunked_sendfile(cfd, nfd, 4096, 8192);
> +	chunked_sendfile(cfd, nfd, 8192, 4096);
> +	chunked_sendfile(cfd, nfd, 12288, 1024);
> +	chunked_sendfile(cfd, nfd, 12288, 2000);
> +	chunked_sendfile(cfd, nfd, 15360, 100);
> +	chunked_sendfile(cfd, nfd, 15360, 300);
> +	chunked_sendfile(cfd, nfd, 1, 4096);
> +	chunked_sendfile(cfd, nfd, 2048, 4096);
> +	chunked_sendfile(cfd, nfd, 2048, 8192);
> +	chunked_sendfile(cfd, nfd, 4096, 8192);
> +	chunked_sendfile(cfd, nfd, 1024, 12288);
> +	chunked_sendfile(cfd, nfd, 2000, 12288);
> +	chunked_sendfile(cfd, nfd, 100, 15360);
> +	chunked_sendfile(cfd, nfd, 300, 15360);
> +
> +	close(cfd);
> +	close(nfd);
> +	close(sfd);
> +
> +	printf("==== test ends ====\n");
> +	return 0;
> +}
> diff --git a/tools/testing/selftests/net/mptcp/multi_chunk.sh b/tools/testing/selftests/net/mptcp/multi_chunk.sh
> new file mode 100755
> index 000000000000..c0352c89087f
> --- /dev/null
> +++ b/tools/testing/selftests/net/mptcp/multi_chunk.sh
> @@ -0,0 +1,37 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +
> +. "$(dirname "${0}")/mptcp_lib.sh"
> +
> +cleanup()
> +{
> +	if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
> +		kill "$pid" 2>/dev/null
> +		wait "$pid" 2>/dev/null
> +	fi
> +
> +	mptcp_lib_ns_exit "${ns1}"
> +}
> +
> +init()
> +{
> +	mptcp_lib_ns_init ns1
> +
> +	local i
> +	for i in $(seq 1 4); do
> +		mptcp_lib_pm_nl_add_endpoint "${ns1}" "127.0.0.1" flags signal port 1000$i
> +	done
> +
> +	mptcp_lib_pm_nl_set_limits "${ns1}" 8 8
> +
> +	ip netns exec ${ns1} ip mptcp endpoint show
> +	ip netns exec ${ns1} ip mptcp limits

These two lines are not needed.

> +}
> +
> +init
> +trap cleanup EXIT
> +
> +ip netns exec $ns1 ./multi_chunk &
> +
> +pid=$!
> +wait $pid

Cheers,
Matt
-- 
Sponsored by the NGI0 Core fund.
Re: [Patch mptcp-net 1/3] mptcp: add backlog_list bug reproducer test
Posted by Paolo Abeni 3 days, 12 hours ago
On 2/5/26 7:41 AM, Gang Yan wrote:
> From: Gang Yan <yangang@kylinos.cn>
> 
> This patch introduces a test program to reproduce bugs related to the
> backlog_list in MPTCP. The test is derived from tls.c in the selftests
> suite, but adapted to work without TLS configuration specifically for
> MPTCP testing.
> 
> The program performs chunked sendfile operations with various payload
> sizes to exercise different code paths and trigger backlog_list-related
> issues.
> 
> It can be run in .virtme-exec-run:
>         run_loop run_selftest_one ./multi_chunk.sh
> 
> '''
> selftest Test: ./multi_chunk.sh
> TAP version 13
> 1..1
> [stalls for a while]
> ^C
> 
> '''
> 
> Co-developed-by: Geliang Tang <tanggeliang@kylinos.cn>
> Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
> Signed-off-by: Gang Yan <yangang@kylinos.cn>
> ---
>  tools/testing/selftests/net/mptcp/Makefile    |   1 +
>  .../testing/selftests/net/mptcp/multi_chunk.c | 148 ++++++++++++++++++
>  .../selftests/net/mptcp/multi_chunk.sh        |  37 +++++
>  3 files changed, 186 insertions(+)
>  create mode 100644 tools/testing/selftests/net/mptcp/multi_chunk.c
>  create mode 100755 tools/testing/selftests/net/mptcp/multi_chunk.sh
> 
> diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile
> index 22ba0da2adb8..087e6ae6f0b8 100644
> --- a/tools/testing/selftests/net/mptcp/Makefile
> +++ b/tools/testing/selftests/net/mptcp/Makefile
> @@ -24,6 +24,7 @@ TEST_GEN_FILES := \
>  	mptcp_diag \
>  	mptcp_inq \
>  	mptcp_sockopt \
> +	multi_chunk \
>  	pm_nl_ctl \
>  # end of TEST_GEN_FILES
>  
> diff --git a/tools/testing/selftests/net/mptcp/multi_chunk.c b/tools/testing/selftests/net/mptcp/multi_chunk.c
> new file mode 100644
> index 000000000000..8c97db58a6db
> --- /dev/null
> +++ b/tools/testing/selftests/net/mptcp/multi_chunk.c
> @@ -0,0 +1,148 @@
> +#define _GNU_SOURCE
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +#include <sys/sendfile.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <errno.h>
> +#include <arpa/inet.h>
> +#include <sys/wait.h>
> +
> +#ifndef IPPROTO_MPTCP
> +#define IPPROTO_MPTCP 262
> +#endif

Minor nit: I think we can avoid the compiler guards here; older test
program have them for historical reasons.

> +
> +#define TLS_PAYLOAD_MAX_LEN 16384

Possibly this macro should be renamed, i.e. to PAYLOAD_MAX_LEN and
likely the value increased to 64K


> +#define TEST_PORT 12345
> +
> +static void chunked_sendfile(int cfd, int sfd,
> +			     size_t chunk_size,
> +			     size_t extra_payload_size)
> +{
> +	char buf[TLS_PAYLOAD_MAX_LEN];
> +	uint16_t test_payload_size;
> +	size_t recved = 0;
> +	size_t sent = 0;
> +	int size = 0;
> +	int ret;
> +	char filename[] = "/tmp/mytemp.XXXXXX";
> +	int fd = mkstemp(filename);
> +	off_t offset = 0;
> +
> +	unlink(filename);
> +	if (fd <= 0) {
> +		perror("tempfile");
> +		exit(1);

Here and below you could use the error() libcall instead of the
perror/exit combo.

> +	}
> +	if (chunk_size < 1) {
> +		perror("chunksize");
> +		exit(1);
> +	}
> +
> +	test_payload_size = chunk_size + extra_payload_size;
> +	if (test_payload_size > TLS_PAYLOAD_MAX_LEN) {
> +		perror("payload_size");
> +		exit(1);
> +	}
> +	memset(buf, 1, test_payload_size);
> +	size = write(fd, buf, test_payload_size);
> +	if (size != test_payload_size) {
> +		perror("file write");
> +		exit(1);
> +	}
> +	fsync(fd);
> +
> +	while (size > 0) {
> +		ret = sendfile(sfd, fd, &offset, chunk_size);
> +		if (ret <= 0)
> +			exit(1);
> +		size -= ret;
> +		sent += ret;
> +	}
> +	printf("[client] sent %zu bytes\n", sent);
> +
> +	recved = recv(cfd, buf, test_payload_size, MSG_WAITALL);
> +	printf("[server] receieved %zu bytes\n", recved);

Possibly the above 2 print should be off by default and enabled via
command line switch.

> +
> +	if (recved != test_payload_size)
> +		exit(1);
> +
> +	close(fd);
> +}
> +
> +int main()
> +{
> +	int sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
> +	int cfd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
> +	struct sockaddr_in addr = {0};
> +	socklen_t addrlen = sizeof(addr);
> +
> +	printf("==== multi_chunk_sendfile MPTCP test ====\n");
> +
> +	if (sfd < 0 || cfd < 0) {
> +		perror("socket");
> +		exit(1);
> +	}
> +
> +	addr.sin_family = AF_INET;
> +	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> +	addr.sin_port = htons(TEST_PORT);
> +
> +	if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> +		perror("bind");
> +		exit(1);
> +	}
> +
> +	if (listen(sfd, 1) < 0) {
> +		perror("listen");
> +		exit(1);
> +	}
> +
> +	if (getsockname(sfd, (struct sockaddr *)&addr, &addrlen) < 0) {
> +		perror("getsockname");
> +		exit(1);
> +	}
> +
> +	if (connect(cfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> +		perror("connect");
> +		exit(1);
> +	}
> +
> +	int nfd = accept(sfd, NULL, NULL);
> +	if (nfd < 0) {
> +		perror("accept");
> +		exit(1);
> +	}
> +
> +	chunked_sendfile(cfd, nfd, 4096, 4096);
> +	chunked_sendfile(cfd, nfd, 4096, 0);
> +	chunked_sendfile(cfd, nfd, 4096, 1);
> +	chunked_sendfile(cfd, nfd, 4096, 2048);
> +	chunked_sendfile(cfd, nfd, 8192, 2048);
> +	chunked_sendfile(cfd, nfd, 4096, 8192);
> +	chunked_sendfile(cfd, nfd, 8192, 4096);
> +	chunked_sendfile(cfd, nfd, 12288, 1024);
> +	chunked_sendfile(cfd, nfd, 12288, 2000);
> +	chunked_sendfile(cfd, nfd, 15360, 100);
> +	chunked_sendfile(cfd, nfd, 15360, 300);
> +	chunked_sendfile(cfd, nfd, 1, 4096);
> +	chunked_sendfile(cfd, nfd, 2048, 4096);
> +	chunked_sendfile(cfd, nfd, 2048, 8192);
> +	chunked_sendfile(cfd, nfd, 4096, 8192);
> +	chunked_sendfile(cfd, nfd, 1024, 12288);
> +	chunked_sendfile(cfd, nfd, 2000, 12288);
> +	chunked_sendfile(cfd, nfd, 100, 15360);
> +	chunked_sendfile(cfd, nfd, 300, 15360);
> +
> +	close(cfd);
> +	close(nfd);
> +	close(sfd);
> +
> +	printf("==== test ends ====\n");
> +	return 0;
> +}
> diff --git a/tools/testing/selftests/net/mptcp/multi_chunk.sh b/tools/testing/selftests/net/mptcp/multi_chunk.sh
> new file mode 100755
> index 000000000000..c0352c89087f
> --- /dev/null
> +++ b/tools/testing/selftests/net/mptcp/multi_chunk.sh
> @@ -0,0 +1,37 @@
> +#!/bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +
> +. "$(dirname "${0}")/mptcp_lib.sh"
> +
> +cleanup()
> +{
> +	if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
> +		kill "$pid" 2>/dev/null
> +		wait "$pid" 2>/dev/null
> +	fi
> +
> +	mptcp_lib_ns_exit "${ns1}"
> +}
> +
> +init()
> +{
> +	mptcp_lib_ns_init ns1
> +
> +	local i
> +	for i in $(seq 1 4); do
> +		mptcp_lib_pm_nl_add_endpoint "${ns1}" "127.0.0.1" flags signal port 1000$i
> +	done
> +
> +	mptcp_lib_pm_nl_set_limits "${ns1}" 8 8
> +
> +	ip netns exec ${ns1} ip mptcp endpoint show
> +	ip netns exec ${ns1} ip mptcp limits
> +}
> +
> +init
> +trap cleanup EXIT
> +
> +ip netns exec $ns1 ./multi_chunk &
> +
> +pid=$!
> +wait $pid

I think it would be nice to emit an ok/fail message as other tests

/P
Re: [Patch mptcp-net 1/3] mptcp: add backlog_list bug reproducer test
Posted by gang.yan@linux.dev 3 days, 8 hours ago
>February 5, 2026 at 5:20 PM, "Paolo Abeni" <pabeni@redhat.com mailto:pabeni@redhat.com?to=%22Paolo%20Abeni%22%20%3Cpabeni%40redhat.com%3E > wrote:
Hi, Paolo:

Thanks for your detailed review comments.

This patch is for temporary testing purposes only to reproduce the
backlog_list-related issues, and it's not meant for merging. In the upcoming
v2 version, I'll incorporate all your suggestions and add the appropriate
"do-not-merge" tag.

Best regards,
Gang

> 
> On 2/5/26 7:41 AM, Gang Yan wrote:
> 
> > 
> > From: Gang Yan <yangang@kylinos.cn>
> >  
> >  This patch introduces a test program to reproduce bugs related to the
> >  backlog_list in MPTCP. The test is derived from tls.c in the selftests
> >  suite, but adapted to work without TLS configuration specifically for
> >  MPTCP testing.
> >  
> >  The program performs chunked sendfile operations with various payload
> >  sizes to exercise different code paths and trigger backlog_list-related
> >  issues.
> >  
> >  It can be run in .virtme-exec-run:
> >  run_loop run_selftest_one ./multi_chunk.sh
> >  
> >  '''
> >  selftest Test: ./multi_chunk.sh
> >  TAP version 13
> >  1..1
> >  [stalls for a while]
> >  ^C
> >  
> >  '''
> >  
> >  Co-developed-by: Geliang Tang <tanggeliang@kylinos.cn>
> >  Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
> >  Signed-off-by: Gang Yan <yangang@kylinos.cn>
> >  ---
> >  tools/testing/selftests/net/mptcp/Makefile | 1 +
> >  .../testing/selftests/net/mptcp/multi_chunk.c | 148 ++++++++++++++++++
> >  .../selftests/net/mptcp/multi_chunk.sh | 37 +++++
> >  3 files changed, 186 insertions(+)
> >  create mode 100644 tools/testing/selftests/net/mptcp/multi_chunk.c
> >  create mode 100755 tools/testing/selftests/net/mptcp/multi_chunk.sh
> >  
> >  diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile
> >  index 22ba0da2adb8..087e6ae6f0b8 100644
> >  --- a/tools/testing/selftests/net/mptcp/Makefile
> >  +++ b/tools/testing/selftests/net/mptcp/Makefile
> >  @@ -24,6 +24,7 @@ TEST_GEN_FILES := \
> >  mptcp_diag \
> >  mptcp_inq \
> >  mptcp_sockopt \
> >  + multi_chunk \
> >  pm_nl_ctl \
> >  # end of TEST_GEN_FILES
> >  
> >  diff --git a/tools/testing/selftests/net/mptcp/multi_chunk.c b/tools/testing/selftests/net/mptcp/multi_chunk.c
> >  new file mode 100644
> >  index 000000000000..8c97db58a6db
> >  --- /dev/null
> >  +++ b/tools/testing/selftests/net/mptcp/multi_chunk.c
> >  @@ -0,0 +1,148 @@
> >  +#define _GNU_SOURCE
> >  +#include <stdio.h>
> >  +#include <stdlib.h>
> >  +#include <string.h>
> >  +#include <unistd.h>
> >  +#include <fcntl.h>
> >  +#include <sys/socket.h>
> >  +#include <netinet/in.h>
> >  +#include <sys/sendfile.h>
> >  +#include <sys/types.h>
> >  +#include <sys/stat.h>
> >  +#include <errno.h>
> >  +#include <arpa/inet.h>
> >  +#include <sys/wait.h>
> >  +
> >  +#ifndef IPPROTO_MPTCP
> >  +#define IPPROTO_MPTCP 262
> >  +#endif
> > 
> Minor nit: I think we can avoid the compiler guards here; older test
> program have them for historical reasons.
> 
> > 
> > +
> >  +#define TLS_PAYLOAD_MAX_LEN 16384
> > 
> Possibly this macro should be renamed, i.e. to PAYLOAD_MAX_LEN and
> likely the value increased to 64K
> 
> > 
> > +#define TEST_PORT 12345
> >  +
> >  +static void chunked_sendfile(int cfd, int sfd,
> >  + size_t chunk_size,
> >  + size_t extra_payload_size)
> >  +{
> >  + char buf[TLS_PAYLOAD_MAX_LEN];
> >  + uint16_t test_payload_size;
> >  + size_t recved = 0;
> >  + size_t sent = 0;
> >  + int size = 0;
> >  + int ret;
> >  + char filename[] = "/tmp/mytemp.XXXXXX";
> >  + int fd = mkstemp(filename);
> >  + off_t offset = 0;
> >  +
> >  + unlink(filename);
> >  + if (fd <= 0) {
> >  + perror("tempfile");
> >  + exit(1);
> > 
> Here and below you could use the error() libcall instead of the
> perror/exit combo.
> 
> > 
> > + }
> >  + if (chunk_size < 1) {
> >  + perror("chunksize");
> >  + exit(1);
> >  + }
> >  +
> >  + test_payload_size = chunk_size + extra_payload_size;
> >  + if (test_payload_size > TLS_PAYLOAD_MAX_LEN) {
> >  + perror("payload_size");
> >  + exit(1);
> >  + }
> >  + memset(buf, 1, test_payload_size);
> >  + size = write(fd, buf, test_payload_size);
> >  + if (size != test_payload_size) {
> >  + perror("file write");
> >  + exit(1);
> >  + }
> >  + fsync(fd);
> >  +
> >  + while (size > 0) {
> >  + ret = sendfile(sfd, fd, &offset, chunk_size);
> >  + if (ret <= 0)
> >  + exit(1);
> >  + size -= ret;
> >  + sent += ret;
> >  + }
> >  + printf("[client] sent %zu bytes\n", sent);
> >  +
> >  + recved = recv(cfd, buf, test_payload_size, MSG_WAITALL);
> >  + printf("[server] receieved %zu bytes\n", recved);
> > 
> Possibly the above 2 print should be off by default and enabled via
> command line switch.
> 
> > 
> > +
> >  + if (recved != test_payload_size)
> >  + exit(1);
> >  +
> >  + close(fd);
> >  +}
> >  +
> >  +int main()
> >  +{
> >  + int sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
> >  + int cfd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
> >  + struct sockaddr_in addr = {0};
> >  + socklen_t addrlen = sizeof(addr);
> >  +
> >  + printf("==== multi_chunk_sendfile MPTCP test ====\n");
> >  +
> >  + if (sfd < 0 || cfd < 0) {
> >  + perror("socket");
> >  + exit(1);
> >  + }
> >  +
> >  + addr.sin_family = AF_INET;
> >  + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> >  + addr.sin_port = htons(TEST_PORT);
> >  +
> >  + if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> >  + perror("bind");
> >  + exit(1);
> >  + }
> >  +
> >  + if (listen(sfd, 1) < 0) {
> >  + perror("listen");
> >  + exit(1);
> >  + }
> >  +
> >  + if (getsockname(sfd, (struct sockaddr *)&addr, &addrlen) < 0) {
> >  + perror("getsockname");
> >  + exit(1);
> >  + }
> >  +
> >  + if (connect(cfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
> >  + perror("connect");
> >  + exit(1);
> >  + }
> >  +
> >  + int nfd = accept(sfd, NULL, NULL);
> >  + if (nfd < 0) {
> >  + perror("accept");
> >  + exit(1);
> >  + }
> >  +
> >  + chunked_sendfile(cfd, nfd, 4096, 4096);
> >  + chunked_sendfile(cfd, nfd, 4096, 0);
> >  + chunked_sendfile(cfd, nfd, 4096, 1);
> >  + chunked_sendfile(cfd, nfd, 4096, 2048);
> >  + chunked_sendfile(cfd, nfd, 8192, 2048);
> >  + chunked_sendfile(cfd, nfd, 4096, 8192);
> >  + chunked_sendfile(cfd, nfd, 8192, 4096);
> >  + chunked_sendfile(cfd, nfd, 12288, 1024);
> >  + chunked_sendfile(cfd, nfd, 12288, 2000);
> >  + chunked_sendfile(cfd, nfd, 15360, 100);
> >  + chunked_sendfile(cfd, nfd, 15360, 300);
> >  + chunked_sendfile(cfd, nfd, 1, 4096);
> >  + chunked_sendfile(cfd, nfd, 2048, 4096);
> >  + chunked_sendfile(cfd, nfd, 2048, 8192);
> >  + chunked_sendfile(cfd, nfd, 4096, 8192);
> >  + chunked_sendfile(cfd, nfd, 1024, 12288);
> >  + chunked_sendfile(cfd, nfd, 2000, 12288);
> >  + chunked_sendfile(cfd, nfd, 100, 15360);
> >  + chunked_sendfile(cfd, nfd, 300, 15360);
> >  +
> >  + close(cfd);
> >  + close(nfd);
> >  + close(sfd);
> >  +
> >  + printf("==== test ends ====\n");
> >  + return 0;
> >  +}
> >  diff --git a/tools/testing/selftests/net/mptcp/multi_chunk.sh b/tools/testing/selftests/net/mptcp/multi_chunk.sh
> >  new file mode 100755
> >  index 000000000000..c0352c89087f
> >  --- /dev/null
> >  +++ b/tools/testing/selftests/net/mptcp/multi_chunk.sh
> >  @@ -0,0 +1,37 @@
> >  +#!/bin/bash
> >  +# SPDX-License-Identifier: GPL-2.0
> >  +
> >  +. "$(dirname "${0}")/mptcp_lib.sh"
> >  +
> >  +cleanup()
> >  +{
> >  + if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
> >  + kill "$pid" 2>/dev/null
> >  + wait "$pid" 2>/dev/null
> >  + fi
> >  +
> >  + mptcp_lib_ns_exit "${ns1}"
> >  +}
> >  +
> >  +init()
> >  +{
> >  + mptcp_lib_ns_init ns1
> >  +
> >  + local i
> >  + for i in $(seq 1 4); do
> >  + mptcp_lib_pm_nl_add_endpoint "${ns1}" "127.0.0.1" flags signal port 1000$i
> >  + done
> >  +
> >  + mptcp_lib_pm_nl_set_limits "${ns1}" 8 8
> >  +
> >  + ip netns exec ${ns1} ip mptcp endpoint show
> >  + ip netns exec ${ns1} ip mptcp limits
> >  +}
> >  +
> >  +init
> >  +trap cleanup EXIT
> >  +
> >  +ip netns exec $ns1 ./multi_chunk &
> >  +
> >  +pid=$!
> >  +wait $pid
> > 
> I think it would be nice to emit an ok/fail message as other tests
> 
> /P
>