Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Anna Emese Nyiri and committed by
Jakub Kicinski
c935af42 b9d75210

+244
+1
tools/testing/selftests/net/.gitignore
··· 42 42 so_incoming_cpu 43 43 so_netns_cookie 44 44 so_txtime 45 + so_rcv_listener 45 46 stress_reuseport_listen 46 47 tap 47 48 tcp_fastopen_backup_key
+2
tools/testing/selftests/net/Makefile
··· 33 33 TEST_PROGS += gre_gso.sh 34 34 TEST_PROGS += cmsg_so_mark.sh 35 35 TEST_PROGS += cmsg_so_priority.sh 36 + TEST_PROGS += test_so_rcv.sh 36 37 TEST_PROGS += cmsg_time.sh cmsg_ipv6.sh 37 38 TEST_PROGS += netns-name.sh 38 39 TEST_PROGS += nl_netdev.py ··· 77 76 TEST_GEN_FILES += toeplitz 78 77 TEST_GEN_FILES += cmsg_sender 79 78 TEST_GEN_FILES += stress_reuseport_listen 79 + TEST_GEN_FILES += so_rcv_listener 80 80 TEST_PROGS += test_vxlan_vnifiltering.sh 81 81 TEST_GEN_FILES += io_uring_zerocopy_tx 82 82 TEST_PROGS += io_uring_zerocopy_tx.sh
+168
tools/testing/selftests/net/so_rcv_listener.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <errno.h> 4 + #include <netdb.h> 5 + #include <stdbool.h> 6 + #include <stdio.h> 7 + #include <stdlib.h> 8 + #include <string.h> 9 + #include <unistd.h> 10 + #include <linux/types.h> 11 + #include <sys/socket.h> 12 + #include <netinet/in.h> 13 + #include <arpa/inet.h> 14 + 15 + #ifndef SO_RCVPRIORITY 16 + #define SO_RCVPRIORITY 82 17 + #endif 18 + 19 + struct options { 20 + __u32 val; 21 + int name; 22 + int rcvname; 23 + const char *host; 24 + const char *service; 25 + } opt; 26 + 27 + static void __attribute__((noreturn)) usage(const char *bin) 28 + { 29 + printf("Usage: %s [opts] <dst host> <dst port / service>\n", bin); 30 + printf("Options:\n" 31 + "\t\t-M val Test SO_RCVMARK\n" 32 + "\t\t-P val Test SO_RCVPRIORITY\n" 33 + ""); 34 + exit(EXIT_FAILURE); 35 + } 36 + 37 + static void parse_args(int argc, char *argv[]) 38 + { 39 + int o; 40 + 41 + while ((o = getopt(argc, argv, "M:P:")) != -1) { 42 + switch (o) { 43 + case 'M': 44 + opt.val = atoi(optarg); 45 + opt.name = SO_MARK; 46 + opt.rcvname = SO_RCVMARK; 47 + break; 48 + case 'P': 49 + opt.val = atoi(optarg); 50 + opt.name = SO_PRIORITY; 51 + opt.rcvname = SO_RCVPRIORITY; 52 + break; 53 + default: 54 + usage(argv[0]); 55 + break; 56 + } 57 + } 58 + 59 + if (optind != argc - 2) 60 + usage(argv[0]); 61 + 62 + opt.host = argv[optind]; 63 + opt.service = argv[optind + 1]; 64 + } 65 + 66 + int main(int argc, char *argv[]) 67 + { 68 + int err = 0; 69 + int recv_fd = -1; 70 + int ret_value = 0; 71 + __u32 recv_val; 72 + struct cmsghdr *cmsg; 73 + char cbuf[CMSG_SPACE(sizeof(__u32))]; 74 + char recv_buf[CMSG_SPACE(sizeof(__u32))]; 75 + struct iovec iov[1]; 76 + struct msghdr msg; 77 + struct sockaddr_in recv_addr4; 78 + struct sockaddr_in6 recv_addr6; 79 + 80 + parse_args(argc, argv); 81 + 82 + int family = strchr(opt.host, ':') ? AF_INET6 : AF_INET; 83 + 84 + recv_fd = socket(family, SOCK_DGRAM, IPPROTO_UDP); 85 + if (recv_fd < 0) { 86 + perror("Can't open recv socket"); 87 + ret_value = -errno; 88 + goto cleanup; 89 + } 90 + 91 + err = setsockopt(recv_fd, SOL_SOCKET, opt.rcvname, &opt.val, sizeof(opt.val)); 92 + if (err < 0) { 93 + perror("Recv setsockopt error"); 94 + ret_value = -errno; 95 + goto cleanup; 96 + } 97 + 98 + if (family == AF_INET) { 99 + memset(&recv_addr4, 0, sizeof(recv_addr4)); 100 + recv_addr4.sin_family = family; 101 + recv_addr4.sin_port = htons(atoi(opt.service)); 102 + 103 + if (inet_pton(family, opt.host, &recv_addr4.sin_addr) <= 0) { 104 + perror("Invalid IPV4 address"); 105 + ret_value = -errno; 106 + goto cleanup; 107 + } 108 + 109 + err = bind(recv_fd, (struct sockaddr *)&recv_addr4, sizeof(recv_addr4)); 110 + } else { 111 + memset(&recv_addr6, 0, sizeof(recv_addr6)); 112 + recv_addr6.sin6_family = family; 113 + recv_addr6.sin6_port = htons(atoi(opt.service)); 114 + 115 + if (inet_pton(family, opt.host, &recv_addr6.sin6_addr) <= 0) { 116 + perror("Invalid IPV6 address"); 117 + ret_value = -errno; 118 + goto cleanup; 119 + } 120 + 121 + err = bind(recv_fd, (struct sockaddr *)&recv_addr6, sizeof(recv_addr6)); 122 + } 123 + 124 + if (err < 0) { 125 + perror("Recv bind error"); 126 + ret_value = -errno; 127 + goto cleanup; 128 + } 129 + 130 + iov[0].iov_base = recv_buf; 131 + iov[0].iov_len = sizeof(recv_buf); 132 + 133 + memset(&msg, 0, sizeof(msg)); 134 + msg.msg_iov = iov; 135 + msg.msg_iovlen = 1; 136 + msg.msg_control = cbuf; 137 + msg.msg_controllen = sizeof(cbuf); 138 + 139 + err = recvmsg(recv_fd, &msg, 0); 140 + if (err < 0) { 141 + perror("Message receive error"); 142 + ret_value = -errno; 143 + goto cleanup; 144 + } 145 + 146 + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 147 + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == opt.name) { 148 + recv_val = *(__u32 *)CMSG_DATA(cmsg); 149 + printf("Received value: %u\n", recv_val); 150 + 151 + if (recv_val != opt.val) { 152 + fprintf(stderr, "Error: expected value: %u, got: %u\n", 153 + opt.val, recv_val); 154 + ret_value = -EINVAL; 155 + } 156 + goto cleanup; 157 + } 158 + } 159 + 160 + fprintf(stderr, "Error: No matching cmsg received\n"); 161 + ret_value = -ENOMSG; 162 + 163 + cleanup: 164 + if (recv_fd >= 0) 165 + close(recv_fd); 166 + 167 + return ret_value; 168 + }
+73
tools/testing/selftests/net/test_so_rcv.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + source lib.sh 5 + 6 + HOSTS=("127.0.0.1" "::1") 7 + PORT=1234 8 + TOTAL_TESTS=0 9 + FAILED_TESTS=0 10 + 11 + declare -A TESTS=( 12 + ["SO_RCVPRIORITY"]="-P 2" 13 + ["SO_RCVMARK"]="-M 3" 14 + ) 15 + 16 + check_result() { 17 + ((TOTAL_TESTS++)) 18 + if [ "$1" -ne 0 ]; then 19 + ((FAILED_TESTS++)) 20 + fi 21 + } 22 + 23 + cleanup() 24 + { 25 + cleanup_ns $NS 26 + } 27 + 28 + trap cleanup EXIT 29 + 30 + setup_ns NS 31 + 32 + for HOST in "${HOSTS[@]}"; do 33 + PROTOCOL="IPv4" 34 + if [[ "$HOST" == "::1" ]]; then 35 + PROTOCOL="IPv6" 36 + fi 37 + for test_name in "${!TESTS[@]}"; do 38 + echo "Running $test_name test, $PROTOCOL" 39 + arg=${TESTS[$test_name]} 40 + 41 + ip netns exec $NS ./so_rcv_listener $arg $HOST $PORT & 42 + LISTENER_PID=$! 43 + 44 + sleep 0.5 45 + 46 + if ! ip netns exec $NS ./cmsg_sender $arg $HOST $PORT; then 47 + echo "Sender failed for $test_name, $PROTOCOL" 48 + kill "$LISTENER_PID" 2>/dev/null 49 + wait "$LISTENER_PID" 50 + check_result 1 51 + continue 52 + fi 53 + 54 + wait "$LISTENER_PID" 55 + LISTENER_EXIT_CODE=$? 56 + 57 + if [ "$LISTENER_EXIT_CODE" -eq 0 ]; then 58 + echo "Rcv test OK for $test_name, $PROTOCOL" 59 + check_result 0 60 + else 61 + echo "Rcv test FAILED for $test_name, $PROTOCOL" 62 + check_result 1 63 + fi 64 + done 65 + done 66 + 67 + if [ "$FAILED_TESTS" -ne 0 ]; then 68 + echo "FAIL - $FAILED_TESTS/$TOTAL_TESTS tests failed" 69 + exit ${KSFT_FAIL} 70 + else 71 + echo "OK - All $TOTAL_TESTS tests passed" 72 + exit ${KSFT_PASS} 73 + fi