From: Geliang Tang <tanggeliang@kylinos.cn>
This patch adds a mptcp socket splice test named mptcp_splice. And a new
test in mptcp_join.sh.
Signed-off-by: Geliang Tang <tanggeliang@kylinos.cn>
---
tools/testing/selftests/net/mptcp/Makefile | 2 +-
.../testing/selftests/net/mptcp/mptcp_join.sh | 20 ++
.../selftests/net/mptcp/mptcp_splice.c | 201 ++++++++++++++++++
3 files changed, 222 insertions(+), 1 deletion(-)
create mode 100644 tools/testing/selftests/net/mptcp/mptcp_splice.c
diff --git a/tools/testing/selftests/net/mptcp/Makefile b/tools/testing/selftests/net/mptcp/Makefile
index e47788bfa671..b8c3ffe649f8 100644
--- a/tools/testing/selftests/net/mptcp/Makefile
+++ b/tools/testing/selftests/net/mptcp/Makefile
@@ -7,7 +7,7 @@ CFLAGS += -Wall -Wl,--no-as-needed -O2 -g -I$(top_srcdir)/usr/include $(KHDR_INC
TEST_PROGS := mptcp_connect.sh pm_netlink.sh mptcp_join.sh diag.sh \
simult_flows.sh mptcp_sockopt.sh userspace_pm.sh
-TEST_GEN_FILES = mptcp_connect pm_nl_ctl mptcp_sockopt mptcp_inq mptcp_diag
+TEST_GEN_FILES = mptcp_connect pm_nl_ctl mptcp_sockopt mptcp_inq mptcp_diag mptcp_splice
TEST_FILES := mptcp_lib.sh settings
diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh
index b8af65373b3a..fccbe50d3486 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_join.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh
@@ -3946,6 +3946,25 @@ endpoint_tests()
fi
}
+splice_tests()
+{
+ if reset "splice test"; then
+ mptcp_lib_pm_nl_set_limits $ns1 8 8
+ mptcp_lib_pm_nl_set_limits $ns2 8 8
+ mptcp_lib_pm_nl_add_endpoint $ns2 10.0.2.2 dev ns2eth2 flags subflow
+ mptcp_lib_pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow
+ mptcp_lib_pm_nl_add_endpoint $ns2 10.0.4.2 dev ns2eth4 flags subflow
+
+ ip netns exec $ns1 ./mptcp_splice -l -o "$sout" -p 8080 &
+ local tests_pid=$!
+ sleep 1
+ ip netns exec $ns2 ./mptcp_splice -i "$cin" -a 10.0.1.1 -p 8080
+ mptcp_lib_kill_wait $tests_pid
+ check_transfer "$cin" "$sout" "file received by server" 1052
+ chk_join_nr 3 3 3
+ fi
+}
+
# [$1: error message]
usage()
{
@@ -3994,6 +4013,7 @@ all_tests_sorted=(
F@fail_tests
u@userspace_tests
I@endpoint_tests
+ L@splice_tests
)
all_tests_args=""
diff --git a/tools/testing/selftests/net/mptcp/mptcp_splice.c b/tools/testing/selftests/net/mptcp/mptcp_splice.c
new file mode 100644
index 000000000000..a04ab7e1448d
--- /dev/null
+++ b/tools/testing/selftests/net/mptcp/mptcp_splice.c
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025, Kylin Software */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <errno.h>
+
+#ifndef IPPROTO_MPTCP
+#define IPPROTO_MPTCP 262
+#endif
+
+#define BUFFER_SIZE 65536
+
+char *server_ip;
+int port;
+
+static int server(char *output)
+{
+ int server_fd, client_fd, out_fd;
+ struct sockaddr_in address;
+ int addrlen = sizeof(address);
+ int pipefd[2];
+ ssize_t bytes;
+ int opt = 1;
+
+ server_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
+ if (server_fd < 0) {
+ perror("socket failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
+ &opt, sizeof(opt))) {
+ perror("setsockopt");
+ exit(EXIT_FAILURE);
+ }
+
+ address.sin_family = AF_INET;
+ address.sin_addr.s_addr = INADDR_ANY;
+ address.sin_port = htons(port);
+
+ if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
+ perror("bind failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (listen(server_fd, 3) < 0) {
+ perror("listen");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Server listening on port %d...\n", port);
+
+ client_fd = accept(server_fd, (struct sockaddr *)&address,
+ (socklen_t *)&addrlen);
+ if (client_fd < 0) {
+ perror("accept");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Client connected. Receiving file...\n");
+
+ if (pipe(pipefd)) {
+ perror("pipe");
+ exit(EXIT_FAILURE);
+ }
+
+ out_fd = open(output, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (out_fd < 0) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ while ((bytes = splice(client_fd, NULL, pipefd[1], NULL, BUFFER_SIZE,
+ SPLICE_F_MOVE | SPLICE_F_MORE)) > 0) {
+ splice(pipefd[0], NULL, out_fd, NULL, bytes, SPLICE_F_MOVE | SPLICE_F_MORE);
+ }
+
+ if (bytes == -1)
+ perror("splice");
+
+ printf("File transfer completed. Received %ld bytes.\n",
+ lseek(out_fd, 0, SEEK_CUR));
+
+ close(client_fd);
+ close(out_fd);
+ close(server_fd);
+
+ return 0;
+}
+
+static int client(char *input)
+{
+ struct sockaddr_in serv_addr;
+ int sock_fd, in_fd;
+ int pipefd[2];
+ ssize_t bytes;
+
+ sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_MPTCP);
+ if (sock_fd < 0) {
+ perror("Socket creation error");
+ exit(EXIT_FAILURE);
+ }
+
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = htons(port);
+
+ if (inet_pton(AF_INET, server_ip, &serv_addr.sin_addr) <= 0) {
+ perror("Invalid address/ Address not supported");
+ exit(EXIT_FAILURE);
+ }
+
+ if (connect(sock_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
+ perror("Connection Failed");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Connected to server. Sending file...\n");
+
+ if (pipe(pipefd)) {
+ perror("pipe");
+ exit(EXIT_FAILURE);
+ }
+
+ in_fd = open(input, O_RDONLY);
+ if (in_fd == -1) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ while ((bytes = splice(in_fd, NULL, pipefd[1], NULL, BUFFER_SIZE,
+ SPLICE_F_MOVE | SPLICE_F_MORE)) > 0) {
+ splice(pipefd[0], NULL, sock_fd, NULL, bytes, SPLICE_F_MOVE | SPLICE_F_MORE);
+ }
+
+ if (bytes == -1)
+ perror("splice");
+
+ printf("File transfer completed. Sent %ld bytes.\n",
+ lseek(in_fd, 0, SEEK_CUR));
+
+ shutdown(sock_fd, SHUT_WR);
+ close(in_fd);
+ close(sock_fd);
+
+ return 0;
+}
+
+static void die_usage(void)
+{
+ fprintf(stderr, "Usage: mptcp_splice -l -p <port> -o <output_file>\n");
+ fprintf(stderr, " mptcp_splice -a <addr> -p <port> -i <input_file>\n");
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ char *input = "", *output = "";
+ bool listen_mode = false;
+ int c;
+
+ if (argc < 2)
+ die_usage();
+
+ while ((c = getopt(argc, argv, "hli:o:a:p:")) != -1) {
+ switch (c) {
+ case 'h':
+ die_usage();
+ break;
+ case 'l':
+ listen_mode = true;
+ break;
+ case 'i':
+ input = optarg;
+ break;
+ case 'o':
+ output = optarg;
+ break;
+ case 'a':
+ server_ip = optarg;
+ break;
+ case 'p':
+ port = atoi(optarg);
+ break;
+ default:
+ die_usage();
+ break;
+ }
+ }
+
+ if (listen_mode)
+ return server(output);
+ return client(input);
+}
--
2.43.0
© 2016 - 2025 Red Hat, Inc.