mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-04-13 09:59:31 +00: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…
Add table
Reference in a new issue