mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +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_netns_cookie | ||||
| so_txtime | ||||
| so_rcv_listener | ||||
| stress_reuseport_listen | ||||
| tap | ||||
| tcp_fastopen_backup_key | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ TEST_PROGS += gro.sh | |||
| TEST_PROGS += gre_gso.sh | ||||
| TEST_PROGS += cmsg_so_mark.sh | ||||
| TEST_PROGS += cmsg_so_priority.sh | ||||
| TEST_PROGS += test_so_rcv.sh | ||||
| TEST_PROGS += cmsg_time.sh cmsg_ipv6.sh | ||||
| TEST_PROGS += netns-name.sh | ||||
| 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 += cmsg_sender | ||||
| TEST_GEN_FILES += stress_reuseport_listen | ||||
| TEST_GEN_FILES += so_rcv_listener | ||||
| TEST_PROGS += test_vxlan_vnifiltering.sh | ||||
| TEST_GEN_FILES += io_uring_zerocopy_tx | ||||
| 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