mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-01 00:58:39 +02:00 
			
		
		
		
	selftests: net: add support for testing SO_RCVMARK and SO_RCVPRIORITY
Introduce tests to verify the correct functionality of the SO_RCVMARK and SO_RCVPRIORITY socket options. Suggested-by: Jakub Kicinski <kuba@kernel.org> Suggested-by: Ferenc Fejes <fejes@inf.elte.hu> Signed-off-by: Anna Emese Nyiri <annaemesenyiri@gmail.com> Reviewed-by: Willem de Bruijn <willemb@google.com> Reviewed-by: Ido Schimmel <idosch@nvidia.com> Tested-by: Ido Schimmel <idosch@nvidia.com> Link: https://patch.msgid.link/20250214205828.48503-1-annaemesenyiri@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
		
							parent
							
								
									b9d752105e
								
							
						
					
					
						commit
						c935af429e
					
				
					 4 changed files with 244 additions and 0 deletions
				
			
		
							
								
								
									
										1
									
								
								tools/testing/selftests/net/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								tools/testing/selftests/net/.gitignore
									
									
									
									
										vendored
									
									
								
							|  | @ -42,6 +42,7 @@ socket | ||||||
| so_incoming_cpu | so_incoming_cpu | ||||||
| so_netns_cookie | so_netns_cookie | ||||||
| so_txtime | so_txtime | ||||||
|  | so_rcv_listener | ||||||
| stress_reuseport_listen | stress_reuseport_listen | ||||||
| tap | tap | ||||||
| tcp_fastopen_backup_key | tcp_fastopen_backup_key | ||||||
|  |  | ||||||
|  | @ -33,6 +33,7 @@ TEST_PROGS += gro.sh | ||||||
| TEST_PROGS += gre_gso.sh | TEST_PROGS += gre_gso.sh | ||||||
| TEST_PROGS += cmsg_so_mark.sh | TEST_PROGS += cmsg_so_mark.sh | ||||||
| TEST_PROGS += cmsg_so_priority.sh | TEST_PROGS += cmsg_so_priority.sh | ||||||
|  | TEST_PROGS += test_so_rcv.sh | ||||||
| TEST_PROGS += cmsg_time.sh cmsg_ipv6.sh | TEST_PROGS += cmsg_time.sh cmsg_ipv6.sh | ||||||
| TEST_PROGS += netns-name.sh | TEST_PROGS += netns-name.sh | ||||||
| TEST_PROGS += nl_netdev.py | TEST_PROGS += nl_netdev.py | ||||||
|  | @ -76,6 +77,7 @@ TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict tls tun tap epoll_busy_ | ||||||
| TEST_GEN_FILES += toeplitz | TEST_GEN_FILES += toeplitz | ||||||
| TEST_GEN_FILES += cmsg_sender | TEST_GEN_FILES += cmsg_sender | ||||||
| TEST_GEN_FILES += stress_reuseport_listen | TEST_GEN_FILES += stress_reuseport_listen | ||||||
|  | TEST_GEN_FILES += so_rcv_listener | ||||||
| TEST_PROGS += test_vxlan_vnifiltering.sh | TEST_PROGS += test_vxlan_vnifiltering.sh | ||||||
| TEST_GEN_FILES += io_uring_zerocopy_tx | TEST_GEN_FILES += io_uring_zerocopy_tx | ||||||
| TEST_PROGS += io_uring_zerocopy_tx.sh | TEST_PROGS += io_uring_zerocopy_tx.sh | ||||||
|  |  | ||||||
							
								
								
									
										168
									
								
								tools/testing/selftests/net/so_rcv_listener.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								tools/testing/selftests/net/so_rcv_listener.c
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,168 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0
 | ||||||
|  | 
 | ||||||
|  | #include <errno.h> | ||||||
|  | #include <netdb.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <linux/types.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <netinet/in.h> | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | 
 | ||||||
|  | #ifndef SO_RCVPRIORITY | ||||||
|  | #define SO_RCVPRIORITY 82 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | struct options { | ||||||
|  | 	__u32 val; | ||||||
|  | 	int name; | ||||||
|  | 	int rcvname; | ||||||
|  | 	const char *host; | ||||||
|  | 	const char *service; | ||||||
|  | } opt; | ||||||
|  | 
 | ||||||
|  | static void __attribute__((noreturn)) usage(const char *bin) | ||||||
|  | { | ||||||
|  | 	printf("Usage: %s [opts] <dst host> <dst port / service>\n", bin); | ||||||
|  | 	printf("Options:\n" | ||||||
|  | 		"\t\t-M val  Test SO_RCVMARK\n" | ||||||
|  | 		"\t\t-P val  Test SO_RCVPRIORITY\n" | ||||||
|  | 		""); | ||||||
|  | 	exit(EXIT_FAILURE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void parse_args(int argc, char *argv[]) | ||||||
|  | { | ||||||
|  | 	int o; | ||||||
|  | 
 | ||||||
|  | 	while ((o = getopt(argc, argv, "M:P:")) != -1) { | ||||||
|  | 		switch (o) { | ||||||
|  | 		case 'M': | ||||||
|  | 			opt.val = atoi(optarg); | ||||||
|  | 			opt.name = SO_MARK; | ||||||
|  | 			opt.rcvname = SO_RCVMARK; | ||||||
|  | 			break; | ||||||
|  | 		case 'P': | ||||||
|  | 			opt.val = atoi(optarg); | ||||||
|  | 			opt.name = SO_PRIORITY; | ||||||
|  | 			opt.rcvname = SO_RCVPRIORITY; | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			usage(argv[0]); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (optind != argc - 2) | ||||||
|  | 		usage(argv[0]); | ||||||
|  | 
 | ||||||
|  | 	opt.host = argv[optind]; | ||||||
|  | 	opt.service = argv[optind + 1]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int main(int argc, char *argv[]) | ||||||
|  | { | ||||||
|  | 	int err = 0; | ||||||
|  | 	int recv_fd = -1; | ||||||
|  | 	int ret_value = 0; | ||||||
|  | 	__u32 recv_val; | ||||||
|  | 	struct cmsghdr *cmsg; | ||||||
|  | 	char cbuf[CMSG_SPACE(sizeof(__u32))]; | ||||||
|  | 	char recv_buf[CMSG_SPACE(sizeof(__u32))]; | ||||||
|  | 	struct iovec iov[1]; | ||||||
|  | 	struct msghdr msg; | ||||||
|  | 	struct sockaddr_in recv_addr4; | ||||||
|  | 	struct sockaddr_in6 recv_addr6; | ||||||
|  | 
 | ||||||
|  | 	parse_args(argc, argv); | ||||||
|  | 
 | ||||||
|  | 	int family = strchr(opt.host, ':') ? AF_INET6 : AF_INET; | ||||||
|  | 
 | ||||||
|  | 	recv_fd = socket(family, SOCK_DGRAM, IPPROTO_UDP); | ||||||
|  | 	if (recv_fd < 0) { | ||||||
|  | 		perror("Can't open recv socket"); | ||||||
|  | 		ret_value = -errno; | ||||||
|  | 		goto cleanup; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = setsockopt(recv_fd, SOL_SOCKET, opt.rcvname, &opt.val, sizeof(opt.val)); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		perror("Recv setsockopt error"); | ||||||
|  | 		ret_value = -errno; | ||||||
|  | 		goto cleanup; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (family == AF_INET) { | ||||||
|  | 		memset(&recv_addr4, 0, sizeof(recv_addr4)); | ||||||
|  | 		recv_addr4.sin_family = family; | ||||||
|  | 		recv_addr4.sin_port = htons(atoi(opt.service)); | ||||||
|  | 
 | ||||||
|  | 		if (inet_pton(family, opt.host, &recv_addr4.sin_addr) <= 0) { | ||||||
|  | 			perror("Invalid IPV4 address"); | ||||||
|  | 			ret_value = -errno; | ||||||
|  | 			goto cleanup; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		err = bind(recv_fd, (struct sockaddr *)&recv_addr4, sizeof(recv_addr4)); | ||||||
|  | 	} else { | ||||||
|  | 		memset(&recv_addr6, 0, sizeof(recv_addr6)); | ||||||
|  | 		recv_addr6.sin6_family = family; | ||||||
|  | 		recv_addr6.sin6_port = htons(atoi(opt.service)); | ||||||
|  | 
 | ||||||
|  | 		if (inet_pton(family, opt.host, &recv_addr6.sin6_addr) <= 0) { | ||||||
|  | 			perror("Invalid IPV6 address"); | ||||||
|  | 			ret_value = -errno; | ||||||
|  | 			goto cleanup; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		err = bind(recv_fd, (struct sockaddr *)&recv_addr6, sizeof(recv_addr6)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		perror("Recv bind error"); | ||||||
|  | 		ret_value = -errno; | ||||||
|  | 		goto cleanup; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	iov[0].iov_base = recv_buf; | ||||||
|  | 	iov[0].iov_len = sizeof(recv_buf); | ||||||
|  | 
 | ||||||
|  | 	memset(&msg, 0, sizeof(msg)); | ||||||
|  | 	msg.msg_iov = iov; | ||||||
|  | 	msg.msg_iovlen = 1; | ||||||
|  | 	msg.msg_control = cbuf; | ||||||
|  | 	msg.msg_controllen = sizeof(cbuf); | ||||||
|  | 
 | ||||||
|  | 	err = recvmsg(recv_fd, &msg, 0); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		perror("Message receive error"); | ||||||
|  | 		ret_value = -errno; | ||||||
|  | 		goto cleanup; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { | ||||||
|  | 		if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == opt.name) { | ||||||
|  | 			recv_val = *(__u32 *)CMSG_DATA(cmsg); | ||||||
|  | 			printf("Received value: %u\n", recv_val); | ||||||
|  | 
 | ||||||
|  | 			if (recv_val != opt.val) { | ||||||
|  | 				fprintf(stderr, "Error: expected value: %u, got: %u\n", | ||||||
|  | 					opt.val, recv_val); | ||||||
|  | 				ret_value = -EINVAL; | ||||||
|  | 			} | ||||||
|  | 			goto cleanup; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fprintf(stderr, "Error: No matching cmsg received\n"); | ||||||
|  | 	ret_value = -ENOMSG; | ||||||
|  | 
 | ||||||
|  | cleanup: | ||||||
|  | 	if (recv_fd >= 0) | ||||||
|  | 		close(recv_fd); | ||||||
|  | 
 | ||||||
|  | 	return ret_value; | ||||||
|  | } | ||||||
							
								
								
									
										73
									
								
								tools/testing/selftests/net/test_so_rcv.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										73
									
								
								tools/testing/selftests/net/test_so_rcv.sh
									
									
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,73 @@ | ||||||
|  | #!/bin/bash | ||||||
|  | # SPDX-License-Identifier: GPL-2.0 | ||||||
|  | 
 | ||||||
|  | source lib.sh | ||||||
|  | 
 | ||||||
|  | HOSTS=("127.0.0.1" "::1") | ||||||
|  | PORT=1234 | ||||||
|  | TOTAL_TESTS=0 | ||||||
|  | FAILED_TESTS=0 | ||||||
|  | 
 | ||||||
|  | declare -A TESTS=( | ||||||
|  | 	["SO_RCVPRIORITY"]="-P 2" | ||||||
|  | 	["SO_RCVMARK"]="-M 3" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | check_result() { | ||||||
|  | 	((TOTAL_TESTS++)) | ||||||
|  | 	if [ "$1" -ne 0 ]; then | ||||||
|  | 		((FAILED_TESTS++)) | ||||||
|  | 	fi | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | cleanup() | ||||||
|  | { | ||||||
|  | 	cleanup_ns $NS | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | trap cleanup EXIT | ||||||
|  | 
 | ||||||
|  | setup_ns NS | ||||||
|  | 
 | ||||||
|  | for HOST in "${HOSTS[@]}"; do | ||||||
|  | 	PROTOCOL="IPv4" | ||||||
|  | 	if [[ "$HOST" == "::1" ]]; then | ||||||
|  | 		PROTOCOL="IPv6" | ||||||
|  | 	fi | ||||||
|  | 	for test_name in "${!TESTS[@]}"; do | ||||||
|  | 		echo "Running $test_name test, $PROTOCOL" | ||||||
|  | 		arg=${TESTS[$test_name]} | ||||||
|  | 
 | ||||||
|  | 		ip netns exec $NS ./so_rcv_listener $arg $HOST $PORT & | ||||||
|  | 		LISTENER_PID=$! | ||||||
|  | 
 | ||||||
|  | 		sleep 0.5 | ||||||
|  | 
 | ||||||
|  | 		if ! ip netns exec $NS ./cmsg_sender $arg $HOST $PORT; then | ||||||
|  | 			echo "Sender failed for $test_name, $PROTOCOL" | ||||||
|  | 			kill "$LISTENER_PID" 2>/dev/null | ||||||
|  | 			wait "$LISTENER_PID" | ||||||
|  | 			check_result 1 | ||||||
|  | 			continue | ||||||
|  | 		fi | ||||||
|  | 
 | ||||||
|  | 		wait "$LISTENER_PID" | ||||||
|  | 		LISTENER_EXIT_CODE=$? | ||||||
|  | 
 | ||||||
|  | 		if [ "$LISTENER_EXIT_CODE" -eq 0 ]; then | ||||||
|  | 			echo "Rcv test OK for $test_name, $PROTOCOL" | ||||||
|  | 			check_result 0 | ||||||
|  | 		else | ||||||
|  | 			echo "Rcv test FAILED for $test_name, $PROTOCOL" | ||||||
|  | 			check_result 1 | ||||||
|  | 		fi | ||||||
|  | 	done | ||||||
|  | done | ||||||
|  | 
 | ||||||
|  | if [ "$FAILED_TESTS" -ne 0 ]; then | ||||||
|  | 	echo "FAIL - $FAILED_TESTS/$TOTAL_TESTS tests failed" | ||||||
|  | 	exit ${KSFT_FAIL} | ||||||
|  | else | ||||||
|  | 	echo "OK - All $TOTAL_TESTS tests passed" | ||||||
|  | 	exit ${KSFT_PASS} | ||||||
|  | fi | ||||||
		Loading…
	
		Reference in a new issue
	
	 Anna Emese Nyiri
						Anna Emese Nyiri