-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
Anna Emese Nyiri
authored and
Jakub Kicinski
committed
Feb 18, 2025
1 parent
b9d7521
commit c935af4
Showing
4 changed files
with
244 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |