lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250501035116.69391-2-jiayuan.chen@linux.dev>
Date: Thu,  1 May 2025 11:51:09 +0800
From: Jiayuan Chen <jiayuan.chen@...ux.dev>
To: netdev@...r.kernel.org
Cc: Jiayuan Chen <jiayuan.chen@...ux.dev>,
	Willem de Bruijn <willemdebruijn.kernel@...il.com>,
	"David S. Miller" <davem@...emloft.net>,
	David Ahern <dsahern@...nel.org>,
	Eric Dumazet <edumazet@...gle.com>,
	Jakub Kicinski <kuba@...nel.org>,
	Paolo Abeni <pabeni@...hat.com>,
	Simon Horman <horms@...nel.org>,
	Shuah Khan <shuah@...nel.org>,
	linux-kernel@...r.kernel.org,
	linux-kselftest@...r.kernel.org
Subject: [RFC net-next v1 2/2] selftests/net: Add udp UDP_STOP_RCV selftest

Add a new selftest, which uses UDP_STOP_RCV to make UDP simulate TCP's
listen and accept.

Signed-off-by: Jiayuan Chen <jiayuan.chen@...ux.dev>
---
 tools/testing/selftests/net/.gitignore        |   1 +
 tools/testing/selftests/net/Makefile          |   1 +
 .../testing/selftests/net/test_udp_stop_rcv.c | 275 ++++++++++++++++++
 3 files changed, 277 insertions(+)
 create mode 100644 tools/testing/selftests/net/test_udp_stop_rcv.c

diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index 532bb732bc6d..293f7cd27e5e 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -61,3 +61,4 @@ udpgso
 udpgso_bench_rx
 udpgso_bench_tx
 unix_connect
+test_udp_stop_rcv
diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile
index 124078b56fa4..0e8fcca9f133 100644
--- a/tools/testing/selftests/net/Makefile
+++ b/tools/testing/selftests/net/Makefile
@@ -108,6 +108,7 @@ TEST_GEN_PROGS += proc_net_pktgen
 TEST_PROGS += lwt_dst_cache_ref_loop.sh
 TEST_PROGS += skf_net_off.sh
 TEST_GEN_FILES += skf_net_off
+TEST_GEN_FILES += test_udp_stop_rcv
 
 # YNL files, must be before "include ..lib.mk"
 YNL_GEN_FILES := busy_poller netlink-dumps
diff --git a/tools/testing/selftests/net/test_udp_stop_rcv.c b/tools/testing/selftests/net/test_udp_stop_rcv.c
new file mode 100644
index 000000000000..e01d097a93be
--- /dev/null
+++ b/tools/testing/selftests/net/test_udp_stop_rcv.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define _GNU_SOURCE
+
+#include <stddef.h>
+#include <arpa/inet.h>
+#include <error.h>
+#include <errno.h>
+#include <net/if.h>
+#include <linux/in.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef UDP_STOP_RCV
+#define UDP_STOP_RCV	105
+#endif
+
+static bool			cfg_do_ipv4;
+static bool			cfg_do_ipv6;
+
+static char			buf[1024];
+static const char *syn		= "client request";
+static const char *synack	= "server accepted";
+static const char *ack		= "established";
+
+const struct in6_addr addr6 = {
+	{ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, /* 0::1 */
+};
+
+const struct in_addr addr4 = {
+	__constant_htonl(0x7f000001), /* 127.0.0.1 */
+};
+
+static int __send_one(const struct sockaddr *srv, const socklen_t srv_len)
+{
+	int cli_fd = -1, ret = 0;
+
+	cli_fd = socket(srv->sa_family, SOCK_DGRAM, 0);
+	if (cli_fd <= 0)
+		goto err;
+
+	ret = connect(cli_fd, srv, srv_len);
+	if (ret < 0)
+		goto err;
+
+	ret = send(cli_fd, syn, strlen(syn), 0);
+	if (ret != strlen(syn)) {
+		ret = -1;
+		goto err;
+	}
+
+	return cli_fd;
+err:
+	if (cli_fd > 0)
+		close(cli_fd);
+	return -1;
+}
+
+static int send_one(const struct sockaddr *srv, const socklen_t srv_len)
+{
+	int cli_fd;
+
+	cli_fd = __send_one(srv, srv_len);
+	if (cli_fd <= 0)
+		return -1;
+
+	close(cli_fd);
+	return 0;
+}
+
+static int send_many(const struct sockaddr *addr, const socklen_t alen)
+{
+	int i = 0, err;
+
+	for (i = 0; i < 100; i++) {
+		err = send_one(addr, alen);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+/*	client			server
+ *	"client request"->
+ *			<-	"server accepted"
+ *	"established"	->
+ */
+static void run_test(struct sockaddr *srv, socklen_t srv_len,
+		     struct sockaddr *cli, socklen_t cli_len)
+{
+	socklen_t size;
+	struct timeval tv = { .tv_sec = 1, .tv_usec = 0 };
+	int one = 1, srv_fd = -1, ret;
+	int session_fd = -1;
+	int cli_fd;
+
+	srv_fd = socket(srv->sa_family, SOCK_DGRAM, 0);
+	if (srv_fd == -1)
+		error(1, errno, "socket srv_fd");
+
+	if (setsockopt(srv_fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)))
+		error(1, errno, "SO_REUSEPORT");
+
+	if (bind(srv_fd, srv, srv_len))
+		error(1, errno, "bind srv_fd");
+
+	if (getsockname(srv_fd, srv, &srv_len))
+		error(1, errno, "getsockname()");
+
+	/* send syn to server */
+	cli_fd = __send_one(srv, srv_len);
+	if (cli_fd < 0)
+		error(1, errno, "new_client_req()");
+
+	ret = recvfrom(srv_fd, (char *)buf, sizeof(buf), MSG_WAITALL, cli, &cli_len);
+	if (ret < 0)
+		error(1, errno, "recvfrom()");
+
+	/* create session for this request */
+	session_fd = socket(srv->sa_family, SOCK_DGRAM, 0);
+	if (session_fd == -1)
+		error(1, errno, "socket session_fd");
+
+	if (setsockopt(session_fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)))
+		error(1, errno, "SO_REUSEPORT");
+
+	/* we ready to bind the server address and do not want to receive any packets */
+	if (setsockopt(session_fd, SOL_UDP, UDP_STOP_RCV, &one, sizeof(one)))
+		error(1, errno, "setsockopt WAIT_CONNECT");
+
+	if (setsockopt(session_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))
+		error(1, errno, "setsockopt SO_RCVTIMEO");
+
+	one = 0;
+	size = sizeof(one);
+	if (getsockopt(session_fd, SOL_UDP, UDP_STOP_RCV, &one, &size) ||
+	    one != 1)
+		error(1, errno, "getsockopt UDP_STOP_RCV");
+
+	/* bind the same address as srv_fd */
+	if (bind(session_fd, srv, srv_len))
+		error(1, errno, "bind srv_fd");
+
+	/* simulate many other requests */
+	if (send_many(srv, srv_len))
+		error(1, errno, "send_many()");
+
+	/* should no data assigned to session_fd
+	 * as we set UDP_STOP_RCV before
+	 */
+	ret = read(session_fd, (char *)buf, sizeof(buf));
+	if (ret > 0)
+		error(1, errno, "session_fd should no data received");
+
+	/* build 4-tuple */
+	ret = connect(session_fd, cli, cli_len);
+	if (ret < 0)
+		error(1, errno, "connect(cli)");
+
+	/* now we are ready to communicate with specified client */
+	one = 0;
+	if (setsockopt(session_fd, SOL_UDP, UDP_STOP_RCV, &one, sizeof(one)))
+		error(1, errno, "setsockopt WAIT_CONNECT");
+
+	/* server sends synack to the client */
+	ret = send(session_fd, synack, strlen(synack), 0);
+	if (ret != strlen(synack))
+		error(1, errno, "send(synack)");
+
+	/* client receives the synack */
+	ret = read(cli_fd, (char *)buf, sizeof(buf));
+	if (ret != strlen(synack))
+		error(1, errno, "read(synack)");
+
+	/* client sends the ack to server */
+	ret = send(cli_fd, ack, strlen(ack), 0);
+	if (ret != strlen(ack))
+		error(1, errno, "send(ack)");
+
+	/* the server should receive the ack */
+	ret = read(session_fd, (char *)buf, sizeof(buf));
+	if (ret != strlen(ack))
+		error(1, errno, "read(ack)");
+
+	/* send many requests that not belongs to the session */
+	if (send_many(srv, srv_len))
+		error(1, errno, "send_many()");
+
+	ret = read(session_fd, (char *)buf, sizeof(buf));
+	if (ret > 0)
+		error(1, errno, "session_fd should no data received");
+
+	if (cli_fd != -1)
+		close(cli_fd);
+	if (srv_fd != -1)
+		close(srv_fd);
+	if (session_fd != -1)
+		close(session_fd);
+}
+
+static void run_test_v4(void)
+{
+	struct sockaddr_in addr = {0};
+	struct sockaddr_in cli = {0};
+
+	addr.sin_family = AF_INET;
+	addr.sin_port = 0;
+	addr.sin_addr = addr4;
+
+	run_test((void *)&addr, sizeof(addr), (void *)&cli, sizeof(cli));
+	fprintf(stderr, "v4 OK\n");
+}
+
+static void run_test_v6(void)
+{
+	struct sockaddr_in6 addr = {0};
+	struct sockaddr_in6 cli = {0};
+
+	addr.sin6_family = AF_INET6;
+	addr.sin6_port = 0;
+	addr.sin6_addr = addr6;
+
+	run_test((void *)&addr, sizeof(addr), (void *)&cli, sizeof(cli));
+	fprintf(stderr, "v6 OK\n");
+}
+
+static void parse_opts(int argc, char **argv)
+{
+	int c;
+
+	while ((c = getopt(argc, argv, "46")) != -1) {
+		switch (c) {
+		case '4':
+			cfg_do_ipv4 = true;
+			break;
+		case '6':
+			cfg_do_ipv6 = true;
+			break;
+		default:
+			error(1, 0, "%s: parse error", argv[0]);
+		}
+	}
+
+	if (!cfg_do_ipv4 && !cfg_do_ipv6) {
+		cfg_do_ipv4 = 1;
+		cfg_do_ipv6 = 1;
+	}
+}
+
+int main(int argc, char **argv)
+{
+	parse_opts(argc, argv);
+
+	if (cfg_do_ipv4)
+		run_test_v4();
+	if (cfg_do_ipv6)
+		run_test_v6();
+
+	fprintf(stderr, "test OK\n");
+	return 0;
+}
-- 
2.47.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ