From 104f082f5ed6d19c5d85ca905ccd4e4d01aef66e Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Mon, 13 Apr 2026 19:45:20 +0800 Subject: selftests: fib_nexthops: test stale has_v4 on nexthop replace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add test cases that exercise the scenario where an IPv6 nexthop is replaced with an IPv4 nexthop while being part of a group. The group's has_v4 flag must be updated so that subsequent IPv6 route additions are properly rejected. Two cases are covered: 1. Gateway nexthop replaced across families with an existing IPv6 route on the group (rejected by fib6_check_nh_list). 2. Blackhole nexthop replaced across families with no existing IPv6 route on the group (fib6_check_nh_list returns early) — this is the path that triggers a NULL ptr deref without the kernel fix. Signed-off-by: Jiayuan Chen Reviewed-by: David Ahern Link: https://patch.msgid.link/20260413114522.147784-2-jiayuan.chen@linux.dev Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/fib_nexthops.sh | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/fib_nexthops.sh b/tools/testing/selftests/net/fib_nexthops.sh index 6eb7f95e70e1..ac868a731694 100755 --- a/tools/testing/selftests/net/fib_nexthops.sh +++ b/tools/testing/selftests/net/fib_nexthops.sh @@ -1209,6 +1209,28 @@ ipv6_fcnal_runtime() run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 124" log_test $? 0 "IPv6 route using a group after replacing v4 gateways" + # Replacing an IPv6 nexthop with an IPv4 nexthop should update has_v4 + # for all groups using it, preventing IPv6 routes from referencing the + # group after the replace. + run_cmd "$IP nexthop add id 89 via 2001:db8:91::2 dev veth1" + run_cmd "$IP nexthop add id 125 group 89" + run_cmd "$IP nexthop replace id 89 via 172.16.1.1 dev veth1" + run_cmd "$IP ro replace 2001:db8:101::1/128 nhid 125" + log_test $? 2 "IPv6 route can not use group after v6 nexthop replaced by v4" + + # Same scenario but with a blackhole nexthop: the group has no IPv6 + # routes yet when the replace happens, so fib6_check_nh_list returns + # early without checking. has_v4 must still be updated to block + # subsequent IPv6 route additions. + run_cmd "$IP nexthop flush >/dev/null 2>&1" + run_cmd "$IP -6 nexthop add id 90 blackhole" + run_cmd "$IP nexthop add id 125 group 90" + run_cmd "$IP nexthop replace id 90 blackhole" + run_cmd "$IP -6 ro add 2001:db8:101::1/128 nhid 125" + log_test $? 2 "IPv6 route reject v6 blackhole replaced by v4 blackhole" + run_cmd "ip netns exec $me ping -6 2001:db8:101::1 -c1 -w$PING_TIMEOUT" + log_test $? 2 "Ping unreachable after rejected route" + $IP nexthop flush >/dev/null 2>&1 # -- cgit v1.2.3 From a3f77afbf67d5ddbc8938fd5627a11221d8a3368 Mon Sep 17 00:00:00 2001 From: Luigi Leonardi Date: Wed, 15 Apr 2026 17:09:29 +0200 Subject: vsock/test: fix MSG_PEEK handling in recv_buf() `recv_buf` does not handle the MSG_PEEK flag correctly: it keeps calling `recv` until all requested bytes are available or an error occurs. The problem is how it calculates the number of bytes read: MSG_PEEK doesn't consume any bytes and will re-read the same bytes from the buffer head, so summing the return value every time is wrong. Moreover, MSG_PEEK doesn't consume the bytes in the buffer, so if more bytes are requested than are available, the loop will never terminate, because `recv` will never return EOF. For this reason, we need to compare the number of bytes read with the number of bytes expected. Add a check: if the MSG_PEEK flag is present, update the byte counter and break out of the loop only after at least the expected number of bytes have been received; otherwise, retry after a short delay to avoid consuming too many CPU cycles. This allows us to simplify the `test_stream_credit_update_test` by reusing `recv_buf`, like some other tests already do. Suggested-by: Stefano Garzarella Signed-off-by: Luigi Leonardi Reviewed-by: Stefano Garzarella Link: https://patch.msgid.link/20260415-fix_peek-v4-2-8207e872759e@redhat.com Signed-off-by: Jakub Kicinski --- tools/testing/vsock/util.c | 15 +++++++++++++++ tools/testing/vsock/vsock_test.c | 13 +------------ 2 files changed, 16 insertions(+), 12 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index 1fe1338c79cd..fe316b02a590 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -381,7 +381,13 @@ void send_buf(int fd, const void *buf, size_t len, int flags, } } +#define RECV_PEEK_RETRY_USEC (10 * 1000) + /* Receive bytes in a buffer and check the return value. + * + * When MSG_PEEK is set, recv() is retried until it returns at least + * expected_ret bytes. The function returns on error, EOF, or timeout + * as usual. * * expected_ret: * <0 Negative errno (for testing errors) @@ -403,6 +409,15 @@ void recv_buf(int fd, void *buf, size_t len, int flags, ssize_t expected_ret) if (ret <= 0) break; + if (flags & MSG_PEEK) { + if (ret >= expected_ret) { + nread = ret; + break; + } + timeout_usleep(RECV_PEEK_RETRY_USEC); + continue; + } + nread += ret; } while (nread < len); timeout_end(); diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index 5bd20ccd9335..bdb0754965df 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -1500,18 +1500,7 @@ static void test_stream_credit_update_test(const struct test_opts *opts, } /* Wait until there will be 128KB of data in rx queue. */ - while (1) { - ssize_t res; - - res = recv(fd, buf, buf_size, MSG_PEEK); - if (res == buf_size) - break; - - if (res <= 0) { - fprintf(stderr, "unexpected 'recv()' return: %zi\n", res); - exit(EXIT_FAILURE); - } - } + recv_buf(fd, buf, buf_size, MSG_PEEK, buf_size); /* There is 128KB of data in the socket's rx queue, dequeue first * 64KB, credit update is sent if 'low_rx_bytes_test' == true. -- cgit v1.2.3 From 2a2675ef619010912a5826297cd3cab00d7dc697 Mon Sep 17 00:00:00 2001 From: Luigi Leonardi Date: Wed, 15 Apr 2026 17:09:30 +0200 Subject: vsock/test: add MSG_PEEK after partial recv test Add a test that verifies MSG_PEEK works correctly after a partial recv(). This is to test a bug that was present in the `virtio_transport_stream_do_peek()` when computing the number of bytes to copy: After a partial read, the peek function didn't take into consideration the number of bytes that were already read. So peeking the whole buffer would cause an out-of-bounds read, that resulted in a -EFAULT. This test does exactly this: do a partial recv on a buffer, then try to peek the whole buffer content. The test re-uses `test_stream_msg_peek_client()` to also cover this scenario. Reviewed-by: Stefano Garzarella Signed-off-by: Luigi Leonardi Link: https://patch.msgid.link/20260415-fix_peek-v4-3-8207e872759e@redhat.com Signed-off-by: Jakub Kicinski --- tools/testing/vsock/vsock_test.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/vsock/vsock_test.c b/tools/testing/vsock/vsock_test.c index bdb0754965df..76be0e4a7f0e 100644 --- a/tools/testing/vsock/vsock_test.c +++ b/tools/testing/vsock/vsock_test.c @@ -346,6 +346,38 @@ static void test_stream_msg_peek_server(const struct test_opts *opts) return test_msg_peek_server(opts, false); } +static void test_stream_peek_after_recv_server(const struct test_opts *opts) +{ + unsigned char buf_normal[MSG_PEEK_BUF_LEN]; + unsigned char buf_peek[MSG_PEEK_BUF_LEN]; + int fd; + + fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL); + if (fd < 0) { + perror("accept"); + exit(EXIT_FAILURE); + } + + control_writeln("SRVREADY"); + + /* Partial recv to advance offset within the skb */ + recv_buf(fd, buf_normal, 1, 0, 1); + + /* Peek with a buffer larger than the remaining data */ + recv_buf(fd, buf_peek, sizeof(buf_peek), MSG_PEEK, sizeof(buf_peek) - 1); + + /* Consume the remaining data */ + recv_buf(fd, buf_normal, sizeof(buf_normal) - 1, 0, sizeof(buf_normal) - 1); + + /* Compare full peek and normal read. */ + if (memcmp(buf_peek, buf_normal, sizeof(buf_peek) - 1)) { + fprintf(stderr, "Full peek data mismatch\n"); + exit(EXIT_FAILURE); + } + + close(fd); +} + #define SOCK_BUF_SIZE (2 * 1024 * 1024) #define SOCK_BUF_SIZE_SMALL (64 * 1024) #define MAX_MSG_PAGES 4 @@ -2509,6 +2541,11 @@ static struct test_case test_cases[] = { .run_client = test_stream_tx_credit_bounds_client, .run_server = test_stream_tx_credit_bounds_server, }, + { + .name = "SOCK_STREAM MSG_PEEK after partial recv", + .run_client = test_stream_msg_peek_client, + .run_server = test_stream_peek_after_recv_server, + }, {}, }; -- cgit v1.2.3 From 82c21069028c5db3463f851ae8ac9cc2e38a3827 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 15 Apr 2026 18:04:39 -0700 Subject: selftests: net: add missing CMAC to tcp_ao config Recent changes to crypto and wifi made CMAC no longer selected by default on x86 and tcp_ao needs it. Add the missing config. Reviewed-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260416010439.1053587-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/tcp_ao/config | 1 + 1 file changed, 1 insertion(+) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/tcp_ao/config b/tools/testing/selftests/net/tcp_ao/config index 971cb6fa2d63..f22148512365 100644 --- a/tools/testing/selftests/net/tcp_ao/config +++ b/tools/testing/selftests/net/tcp_ao/config @@ -1,3 +1,4 @@ +CONFIG_CRYPTO_CMAC=y CONFIG_CRYPTO_HMAC=y CONFIG_CRYPTO_RMD160=y CONFIG_CRYPTO_SHA1=y -- cgit v1.2.3 From e5fd34ab8dff6c5bd4f2e9ee4f3945b79e511068 Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Tue, 24 Mar 2026 08:48:57 +0100 Subject: selftests: ovpn: add nftables config dependencies for test-mark test-mark.sh installs nftables rules in an inet/filter output chain and verifies packet drops via nft counters. In vmksft this can fail when the nftables core is not enabled by the ovpn selftest config. Add the missing kernel options required by this test: - CONFIG_NETFILTER - CONFIG_NF_TABLES - CONFIG_NF_TABLES_INET Fixes: 7b80d8a33500 ("selftests: ovpn: add test for the FW mark feature") Reported-by: Jakub Kicinski Closes: https://lore.kernel.org/all/20260319124114.42f91f72@kernel.org/ Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- tools/testing/selftests/net/ovpn/config | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/ovpn/config b/tools/testing/selftests/net/ovpn/config index 42699740936d..d6cf033d555e 100644 --- a/tools/testing/selftests/net/ovpn/config +++ b/tools/testing/selftests/net/ovpn/config @@ -5,6 +5,9 @@ CONFIG_CRYPTO_GCM=y CONFIG_DST_CACHE=y CONFIG_INET=y CONFIG_NET=y +CONFIG_NETFILTER=y CONFIG_NET_UDP_TUNNEL=y +CONFIG_NF_TABLES=m +CONFIG_NF_TABLES_INET=y CONFIG_OVPN=m CONFIG_STREAM_PARSER=y -- cgit v1.2.3 From c409da0fe15e2b2aae7f93edbab977e23117ce4d Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Mon, 23 Mar 2026 15:32:26 +0100 Subject: selftests: ovpn: fail notification check on mismatch compare_ntfs doesn't fail when expected and received notification streams diverge. Fix this bug by tracking the diff exit status explicitly and return it to the caller so notification mismatches propagate as test failures. Fixes: 77de28cd7cf1 ("selftests: ovpn: add notification parsing and matching") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- tools/testing/selftests/net/ovpn/common.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/ovpn/common.sh b/tools/testing/selftests/net/ovpn/common.sh index 4c08f756e63a..c92415aaddfc 100644 --- a/tools/testing/selftests/net/ovpn/common.sh +++ b/tools/testing/selftests/net/ovpn/common.sh @@ -140,23 +140,35 @@ add_peer() { } compare_ntfs() { + local diff_rc=0 + local diff_file + if [ ${#tmp_jsons[@]} -gt 0 ]; then suffix="" [ "${SYMMETRIC_ID}" -eq 1 ] && suffix="${suffix}-symm" [ "$FLOAT" == 1 ] && suffix="${suffix}-float" expected="json/peer${1}${suffix}.json" received="${tmp_jsons[$1]}" + diff_file=$(mktemp) kill -TERM ${listener_pids[$1]} || true wait ${listener_pids[$1]} || true printf "Checking notifications for peer ${1}... " if diff <(jq -s "${JQ_FILTER}" ${expected}) \ - <(jq -s "${JQ_FILTER}" ${received}); then + <(jq -s "${JQ_FILTER}" ${received}) \ + >"${diff_file}" 2>&1; then echo "OK" + else + diff_rc=$? + echo "failed" + cat "${diff_file}" fi + rm -f "${diff_file}" || true rm -f ${received} || true fi + + return "${diff_rc}" } cleanup() { -- cgit v1.2.3 From 222e7f8d1ca3aaebe7588a79bf64d9820813785c Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Tue, 24 Mar 2026 15:54:18 +0100 Subject: selftests: ovpn: flatten slurped notification JSON before filtering Notification comparison uses jq -s, which slurps all inputs into an array. Some inputs can be arrays themselves, and applying the .msg.peer filter directly on those entries triggers jq type errors. Expand any array-valued JSON items returned by jq -s before selecting .msg.peer, so the filter handles both normal notification objects and [] entries without type errors. Fixes: 77de28cd7cf1 ("selftests: ovpn: add notification parsing and matching") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- tools/testing/selftests/net/ovpn/common.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/ovpn/common.sh b/tools/testing/selftests/net/ovpn/common.sh index c92415aaddfc..d3b322e84fab 100644 --- a/tools/testing/selftests/net/ovpn/common.sh +++ b/tools/testing/selftests/net/ovpn/common.sh @@ -15,7 +15,8 @@ SYMMETRIC_ID=${SYMMETRIC_ID:-0} export ID_OFFSET=$(( 9 * (SYMMETRIC_ID == 0) )) -JQ_FILTER='map(select(.msg.peer | has("remote-ipv6") | not)) | +JQ_FILTER='map(if type == "array" then .[] else . end) | + map(select(.msg.peer | has("remote-ipv6") | not)) | map(del(.msg.ifindex)) | sort_by(.msg.peer.id)[]' LAN_IP="11.11.11.11" -- cgit v1.2.3 From 7c29665a3a3cce1b0e9d6b96054eef64bfc4cebd Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Fri, 20 Mar 2026 17:29:38 +0100 Subject: selftests: ovpn: add prefix to helpers and shared variables Current naming for shared variables, helpers and netnamespaces is a bit unfortunate as it doesn't come with a clean prefix. This showed to be problematic in case of name clashes with external scripts or in case of abrupt test termination (hanging netns' weren't easily reconducible to ovpn). Rename common helper entry points and all shared globals in the ovpn selftests to ovpn_ or OVPN_ names so test scripts and wrappers use a single explicit prefix. Also rename the temporary network namespaces created by the tests from peerN to ovpn_peerN. This makes leaked namespaces easier to identify. This is a mechanical refactor only, behavior is unchanged. Fixes: 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- tools/testing/selftests/net/ovpn/common.sh | 186 ++++++++++++--------- .../testing/selftests/net/ovpn/test-chachapoly.sh | 2 +- .../selftests/net/ovpn/test-close-socket-tcp.sh | 2 +- .../selftests/net/ovpn/test-close-socket.sh | 32 ++-- tools/testing/selftests/net/ovpn/test-float.sh | 2 +- tools/testing/selftests/net/ovpn/test-mark.sh | 45 ++--- .../selftests/net/ovpn/test-symmetric-id-float.sh | 4 +- .../selftests/net/ovpn/test-symmetric-id-tcp.sh | 4 +- .../selftests/net/ovpn/test-symmetric-id.sh | 2 +- tools/testing/selftests/net/ovpn/test-tcp.sh | 2 +- tools/testing/selftests/net/ovpn/test.sh | 139 +++++++-------- 11 files changed, 226 insertions(+), 194 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/ovpn/common.sh b/tools/testing/selftests/net/ovpn/common.sh index d3b322e84fab..38f187b9de23 100644 --- a/tools/testing/selftests/net/ovpn/common.sh +++ b/tools/testing/selftests/net/ovpn/common.sh @@ -4,63 +4,72 @@ # # Author: Antonio Quartulli -UDP_PEERS_FILE=${UDP_PEERS_FILE:-udp_peers.txt} -TCP_PEERS_FILE=${TCP_PEERS_FILE:-tcp_peers.txt} +OVPN_UDP_PEERS_FILE=${OVPN_UDP_PEERS_FILE:-udp_peers.txt} +OVPN_TCP_PEERS_FILE=${OVPN_TCP_PEERS_FILE:-tcp_peers.txt} OVPN_CLI=${OVPN_CLI:-./ovpn-cli} -YNL_CLI=${YNL_CLI:-../../../../net/ynl/pyynl/cli.py} -ALG=${ALG:-aes} -PROTO=${PROTO:-UDP} -FLOAT=${FLOAT:-0} -SYMMETRIC_ID=${SYMMETRIC_ID:-0} +OVPN_YNL=${OVPN_YNL:-../../../../net/ynl/pyynl/cli.py} +OVPN_ALG=${OVPN_ALG:-aes} +OVPN_PROTO=${OVPN_PROTO:-UDP} +OVPN_FLOAT=${OVPN_FLOAT:-0} +OVPN_SYMMETRIC_ID=${OVPN_SYMMETRIC_ID:-0} -export ID_OFFSET=$(( 9 * (SYMMETRIC_ID == 0) )) +export OVPN_ID_OFFSET=$(( 9 * (OVPN_SYMMETRIC_ID == 0) )) -JQ_FILTER='map(if type == "array" then .[] else . end) | +OVPN_JQ_FILTER='map(if type == "array" then .[] else . end) | map(select(.msg.peer | has("remote-ipv6") | not)) | map(del(.msg.ifindex)) | sort_by(.msg.peer.id)[]' -LAN_IP="11.11.11.11" +OVPN_LAN_IP="11.11.11.11" -declare -A tmp_jsons=() -declare -A listener_pids=() +declare -A OVPN_TMP_JSONS=() +declare -A OVPN_LISTENER_PIDS=() -create_ns() { - ip netns add peer${1} +ovpn_create_ns() { + ip netns add "ovpn_peer${1}" } -setup_ns() { +ovpn_setup_ns() { + local peer="ovpn_peer${1}" + local server_ns="ovpn_peer0" + local peer_ns MODE="P2P" if [ ${1} -eq 0 ]; then MODE="MP" - for p in $(seq 1 ${NUM_PEERS}); do - ip link add veth${p} netns peer0 type veth peer name veth${p} netns peer${p} - - ip -n peer0 addr add 10.10.${p}.1/24 dev veth${p} - ip -n peer0 addr add fd00:0:0:${p}::1/64 dev veth${p} - ip -n peer0 link set veth${p} up - - ip -n peer${p} addr add 10.10.${p}.2/24 dev veth${p} - ip -n peer${p} addr add fd00:0:0:${p}::2/64 dev veth${p} - ip -n peer${p} link set veth${p} up + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ip link add veth${p} netns "${server_ns}" type veth \ + peer name veth${p} netns "${peer_ns}" + + ip -n "${server_ns}" addr add 10.10.${p}.1/24 dev \ + veth${p} + ip -n "${server_ns}" addr add fd00:0:0:${p}::1/64 dev \ + veth${p} + ip -n "${server_ns}" link set veth${p} up + + ip -n "${peer_ns}" addr add 10.10.${p}.2/24 dev veth${p} + ip -n "${peer_ns}" addr add fd00:0:0:${p}::2/64 dev \ + veth${p} + ip -n "${peer_ns}" link set veth${p} up done fi - ip netns exec peer${1} ${OVPN_CLI} new_iface tun${1} $MODE - ip -n peer${1} addr add ${2} dev tun${1} + ip netns exec "${peer}" ${OVPN_CLI} new_iface tun${1} $MODE + ip -n "${peer}" addr add ${2} dev tun${1} # add a secondary IP to peer 1, to test a LAN behind a client - if [ ${1} -eq 1 -a -n "${LAN_IP}" ]; then - ip -n peer${1} addr add ${LAN_IP} dev tun${1} - ip -n peer0 route add ${LAN_IP} via $(echo ${2} |sed -e s'!/.*!!') dev tun0 + if [ ${1} -eq 1 -a -n "${OVPN_LAN_IP}" ]; then + ip -n "${peer}" addr add ${OVPN_LAN_IP} dev tun${1} + ip -n "${server_ns}" route add ${OVPN_LAN_IP} via \ + $(echo ${2} |sed -e s'!/.*!!') dev tun0 fi if [ -n "${3}" ]; then - ip -n peer${1} link set mtu ${3} dev tun${1} + ip -n "${peer}" link set mtu ${3} dev tun${1} fi - ip -n peer${1} link set tun${1} up + ip -n "${peer}" link set tun${1} up } -build_capture_filter() { +ovpn_build_capture_filter() { # match the first four bytes of the openvpn data payload - if [ "${PROTO}" == "UDP" ]; then + if [ "${OVPN_PROTO}" == "UDP" ]; then # For UDP, libpcap transport indexing only works for IPv4, so # use an explicit IPv4 or IPv6 expression based on the peer # address. The IPv6 branch assumes there are no extension @@ -77,86 +86,98 @@ build_capture_filter() { fi } -setup_listener() { +ovpn_setup_listener() { + local peer_ns="ovpn_peer${p}" file=$(mktemp) - PYTHONUNBUFFERED=1 ip netns exec peer${p} ${YNL_CLI} --family ovpn \ - --subscribe peers --output-json --duration 40 > ${file} & - listener_pids[$1]=$! - tmp_jsons[$1]="${file}" + PYTHONUNBUFFERED=1 ip netns exec "${peer_ns}" "${OVPN_YNL}" --family \ + ovpn --subscribe peers --output-json --duration 40 > ${file} & + OVPN_LISTENER_PIDS[$1]=$! + OVPN_TMP_JSONS[$1]="${file}" } -add_peer() { +ovpn_add_peer() { labels=("ASYMM" "SYMM") - M_ID=${labels[SYMMETRIC_ID]} + local peer_ns + local server_ns="ovpn_peer0" + M_ID=${labels[OVPN_SYMMETRIC_ID]} - if [ "${PROTO}" == "UDP" ]; then + if [ "${OVPN_PROTO}" == "UDP" ]; then if [ ${1} -eq 0 ]; then - ip netns exec peer0 ${OVPN_CLI} new_multi_peer tun0 1 \ - ${M_ID} ${UDP_PEERS_FILE} + ip netns exec "${server_ns}" ${OVPN_CLI} \ + new_multi_peer tun0 1 ${M_ID} \ + ${OVPN_UDP_PEERS_FILE} - for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} new_key tun0 ${p} 1 0 ${ALG} 0 \ + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ip netns exec "${server_ns}" ${OVPN_CLI} \ + new_key tun0 ${p} 1 0 ${OVPN_ALG} 0 \ data64.key done else - if [ "${SYMMETRIC_ID}" -eq 1 ]; then + peer_ns="ovpn_peer${1}" + if [ "${OVPN_SYMMETRIC_ID}" -eq 1 ]; then PEER_ID=${1} TX_ID="none" else PEER_ID=$(awk "NR == ${1} {print \$2}" \ - ${UDP_PEERS_FILE}) + ${OVPN_UDP_PEERS_FILE}) TX_ID=${1} fi - RADDR=$(awk "NR == ${1} {print \$3}" ${UDP_PEERS_FILE}) - RPORT=$(awk "NR == ${1} {print \$4}" ${UDP_PEERS_FILE}) - LPORT=$(awk "NR == ${1} {print \$6}" ${UDP_PEERS_FILE}) - ip netns exec peer${1} ${OVPN_CLI} new_peer tun${1} \ - ${PEER_ID} ${TX_ID} ${LPORT} ${RADDR} ${RPORT} - ip netns exec peer${1} ${OVPN_CLI} new_key tun${1} \ - ${PEER_ID} 1 0 ${ALG} 1 data64.key + RADDR=$(awk "NR == ${1} {print \$3}" \ + ${OVPN_UDP_PEERS_FILE}) + RPORT=$(awk "NR == ${1} {print \$4}" \ + ${OVPN_UDP_PEERS_FILE}) + LPORT=$(awk "NR == ${1} {print \$6}" \ + ${OVPN_UDP_PEERS_FILE}) + ip netns exec "${peer_ns}" ${OVPN_CLI} new_peer \ + tun${1} ${PEER_ID} ${TX_ID} ${LPORT} ${RADDR} \ + ${RPORT} + ip netns exec "${peer_ns}" ${OVPN_CLI} new_key tun${1} \ + ${PEER_ID} 1 0 ${OVPN_ALG} 1 data64.key fi else if [ ${1} -eq 0 ]; then - (ip netns exec peer0 ${OVPN_CLI} listen tun0 1 ${M_ID} \ - ${TCP_PEERS_FILE} && { - for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} new_key tun0 ${p} 1 0 \ - ${ALG} 0 data64.key + (ip netns exec "${server_ns}" ${OVPN_CLI} listen tun0 \ + 1 ${M_ID} ${OVPN_TCP_PEERS_FILE} && { + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ip netns exec "${server_ns}" \ + ${OVPN_CLI} new_key tun0 ${p} \ + 1 0 ${OVPN_ALG} 0 data64.key done }) & sleep 5 else - if [ "${SYMMETRIC_ID}" -eq 1 ]; then + peer_ns="ovpn_peer${1}" + if [ "${OVPN_SYMMETRIC_ID}" -eq 1 ]; then PEER_ID=${1} TX_ID="none" else PEER_ID=$(awk "NR == ${1} {print \$2}" \ - ${TCP_PEERS_FILE}) + ${OVPN_TCP_PEERS_FILE}) TX_ID=${1} fi - ip netns exec peer${1} ${OVPN_CLI} connect tun${1} \ + ip netns exec "${peer_ns}" ${OVPN_CLI} connect tun${1} \ ${PEER_ID} ${TX_ID} 10.10.${1}.1 1 data64.key fi fi } -compare_ntfs() { +ovpn_compare_ntfs() { local diff_rc=0 local diff_file - if [ ${#tmp_jsons[@]} -gt 0 ]; then + if [ ${#OVPN_TMP_JSONS[@]} -gt 0 ]; then suffix="" - [ "${SYMMETRIC_ID}" -eq 1 ] && suffix="${suffix}-symm" - [ "$FLOAT" == 1 ] && suffix="${suffix}-float" + [ "${OVPN_SYMMETRIC_ID}" -eq 1 ] && suffix="${suffix}-symm" + [ "$OVPN_FLOAT" == 1 ] && suffix="${suffix}-float" expected="json/peer${1}${suffix}.json" - received="${tmp_jsons[$1]}" + received="${OVPN_TMP_JSONS[$1]}" diff_file=$(mktemp) - kill -TERM ${listener_pids[$1]} || true - wait ${listener_pids[$1]} || true + kill -TERM ${OVPN_LISTENER_PIDS[$1]} || true + wait ${OVPN_LISTENER_PIDS[$1]} || true printf "Checking notifications for peer ${1}... " - if diff <(jq -s "${JQ_FILTER}" ${expected}) \ - <(jq -s "${JQ_FILTER}" ${received}) \ + if diff <(jq -s "${OVPN_JQ_FILTER}" ${expected}) \ + <(jq -s "${OVPN_JQ_FILTER}" ${received}) \ >"${diff_file}" 2>&1; then echo "OK" else @@ -172,25 +193,30 @@ compare_ntfs() { return "${diff_rc}" } -cleanup() { +ovpn_cleanup() { + local peer_ns # some ovpn-cli processes sleep in background so they need manual poking killall $(basename ${OVPN_CLI}) 2>/dev/null || true # netns peer0 is deleted without erasing ifaces first for p in $(seq 1 10); do - ip -n peer${p} link set tun${p} down 2>/dev/null || true - ip netns exec peer${p} ${OVPN_CLI} del_iface tun${p} 2>/dev/null || true + peer_ns="ovpn_peer${p}" + ip -n "${peer_ns}" link set tun${p} down 2>/dev/null || true + ip netns exec "${peer_ns}" ${OVPN_CLI} del_iface tun${p} \ + 2>/dev/null || true done for p in $(seq 1 10); do - ip -n peer0 link del veth${p} 2>/dev/null || true + ip -n ovpn_peer0 link del veth${p} 2>/dev/null || true done for p in $(seq 0 10); do - ip netns del peer${p} 2>/dev/null || true + ip netns del "ovpn_peer${p}" 2>/dev/null || true done } -if [ "${PROTO}" == "UDP" ]; then - NUM_PEERS=${NUM_PEERS:-$(wc -l ${UDP_PEERS_FILE} | awk '{print $1}')} +if [ "${OVPN_PROTO}" == "UDP" ]; then + OVPN_NUM_PEERS=${OVPN_NUM_PEERS:-$(wc -l ${OVPN_UDP_PEERS_FILE} | \ + awk '{print $1}')} else - NUM_PEERS=${NUM_PEERS:-$(wc -l ${TCP_PEERS_FILE} | awk '{print $1}')} + OVPN_NUM_PEERS=${OVPN_NUM_PEERS:-$(wc -l ${OVPN_TCP_PEERS_FILE} | \ + awk '{print $1}')} fi diff --git a/tools/testing/selftests/net/ovpn/test-chachapoly.sh b/tools/testing/selftests/net/ovpn/test-chachapoly.sh index 32504079a2b8..cd3d94355d58 100755 --- a/tools/testing/selftests/net/ovpn/test-chachapoly.sh +++ b/tools/testing/selftests/net/ovpn/test-chachapoly.sh @@ -4,6 +4,6 @@ # # Author: Antonio Quartulli -ALG="chachapoly" +OVPN_ALG="chachapoly" source test.sh diff --git a/tools/testing/selftests/net/ovpn/test-close-socket-tcp.sh b/tools/testing/selftests/net/ovpn/test-close-socket-tcp.sh index 093d44772ffd..392d269bada5 100755 --- a/tools/testing/selftests/net/ovpn/test-close-socket-tcp.sh +++ b/tools/testing/selftests/net/ovpn/test-close-socket-tcp.sh @@ -4,6 +4,6 @@ # # Author: Antonio Quartulli -PROTO="TCP" +OVPN_PROTO="TCP" source test-close-socket.sh diff --git a/tools/testing/selftests/net/ovpn/test-close-socket.sh b/tools/testing/selftests/net/ovpn/test-close-socket.sh index 0d09df14fe8e..6bc1b6eab8ac 100755 --- a/tools/testing/selftests/net/ovpn/test-close-socket.sh +++ b/tools/testing/selftests/net/ovpn/test-close-socket.sh @@ -8,38 +8,40 @@ set -e source ./common.sh +server_ns="ovpn_peer0" -cleanup +ovpn_cleanup modprobe -q ovpn || true -for p in $(seq 0 ${NUM_PEERS}); do - create_ns ${p} +for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_create_ns ${p} done -for p in $(seq 0 ${NUM_PEERS}); do - setup_ns ${p} 5.5.5.$((${p} + 1))/24 +for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_setup_ns ${p} 5.5.5.$((${p} + 1))/24 done -for p in $(seq 0 ${NUM_PEERS}); do - add_peer ${p} +for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_add_peer ${p} done -for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} set_peer tun0 ${p} 60 120 - ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} $((${p}+9)) 60 120 +for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ip netns exec "${server_ns}" ${OVPN_CLI} set_peer tun0 ${p} 60 120 + ip netns exec "ovpn_peer${p}" ${OVPN_CLI} set_peer tun${p} $((${p}+9)) \ + 60 120 done sleep 1 -for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ping -qfc 500 -w 3 5.5.5.$((${p} + 1)) +for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ip netns exec "${server_ns}" ping -qfc 500 -w 3 5.5.5.$((${p} + 1)) done -ip netns exec peer0 iperf3 -1 -s & +ip netns exec "${server_ns}" iperf3 -1 -s & sleep 1 -ip netns exec peer1 iperf3 -Z -t 3 -c 5.5.5.1 +ip netns exec ovpn_peer1 iperf3 -Z -t 3 -c 5.5.5.1 -cleanup +ovpn_cleanup modprobe -r ovpn || true diff --git a/tools/testing/selftests/net/ovpn/test-float.sh b/tools/testing/selftests/net/ovpn/test-float.sh index ba5d725e18b0..91f8e113718e 100755 --- a/tools/testing/selftests/net/ovpn/test-float.sh +++ b/tools/testing/selftests/net/ovpn/test-float.sh @@ -4,6 +4,6 @@ # # Author: Antonio Quartulli -FLOAT="1" +OVPN_FLOAT="1" source test.sh diff --git a/tools/testing/selftests/net/ovpn/test-mark.sh b/tools/testing/selftests/net/ovpn/test-mark.sh index 8534428ed3eb..2ee5dc5fc538 100755 --- a/tools/testing/selftests/net/ovpn/test-mark.sh +++ b/tools/testing/selftests/net/ovpn/test-mark.sh @@ -11,62 +11,63 @@ set -e MARK=1056 source ./common.sh +server_ns="ovpn_peer0" -cleanup +ovpn_cleanup modprobe -q ovpn || true -for p in $(seq 0 "${NUM_PEERS}"); do - create_ns "${p}" +for p in $(seq 0 "${OVPN_NUM_PEERS}"); do + ovpn_create_ns "${p}" done for p in $(seq 0 3); do - setup_ns "${p}" 5.5.5.$((p + 1))/24 + ovpn_setup_ns "${p}" 5.5.5.$((p + 1))/24 done # add peer0 with mark -ip netns exec peer0 "${OVPN_CLI}" new_multi_peer tun0 1 ASYMM \ - "${UDP_PEERS_FILE}" \ +ip netns exec "${server_ns}" "${OVPN_CLI}" new_multi_peer tun0 1 ASYMM \ + "${OVPN_UDP_PEERS_FILE}" \ ${MARK} for p in $(seq 1 3); do - ip netns exec peer0 "${OVPN_CLI}" new_key tun0 "${p}" 1 0 "${ALG}" 0 \ - data64.key + ip netns exec "${server_ns}" "${OVPN_CLI}" new_key tun0 "${p}" 1 0 \ + "${OVPN_ALG}" 0 data64.key done for p in $(seq 1 3); do - add_peer "${p}" + ovpn_add_peer "${p}" done for p in $(seq 1 3); do - ip netns exec peer0 "${OVPN_CLI}" set_peer tun0 "${p}" 60 120 - ip netns exec peer"${p}" "${OVPN_CLI}" set_peer tun"${p}" \ + ip netns exec "${server_ns}" "${OVPN_CLI}" set_peer tun0 "${p}" 60 120 + ip netns exec "ovpn_peer${p}" "${OVPN_CLI}" set_peer tun"${p}" \ $((p + 9)) 60 120 done sleep 1 for p in $(seq 1 3); do - ip netns exec peer0 ping -qfc 500 -w 3 5.5.5.$((p + 1)) + ip netns exec "${server_ns}" ping -qfc 500 -w 3 5.5.5.$((p + 1)) done echo "Adding an nftables drop rule based on mark value ${MARK}" -ip netns exec peer0 nft flush ruleset -ip netns exec peer0 nft 'add table inet filter' -ip netns exec peer0 nft 'add chain inet filter output { +ip netns exec "${server_ns}" nft flush ruleset +ip netns exec "${server_ns}" nft 'add table inet filter' +ip netns exec "${server_ns}" nft 'add chain inet filter output { type filter hook output priority 0; policy accept; }' -ip netns exec peer0 nft add rule inet filter output \ +ip netns exec "${server_ns}" nft add rule inet filter output \ meta mark == ${MARK} \ counter drop -DROP_COUNTER=$(ip netns exec peer0 nft list chain inet filter output \ +DROP_COUNTER=$(ip netns exec "${server_ns}" nft list chain inet filter output \ | sed -n 's/.*packets \([0-9]*\).*/\1/p') sleep 1 # ping should fail for p in $(seq 1 3); do - PING_OUTPUT=$(ip netns exec peer0 ping \ + PING_OUTPUT=$(ip netns exec "${server_ns}" ping \ -qfc 500 -w 1 5.5.5.$((p + 1)) 2>&1) && exit 1 echo "${PING_OUTPUT}" LOST_PACKETS=$(echo "$PING_OUTPUT" \ @@ -76,7 +77,7 @@ for p in $(seq 1 3); do done # check if the final nft counter matches our counter -TOTAL_COUNT=$(ip netns exec peer0 nft list chain inet filter output \ +TOTAL_COUNT=$(ip netns exec "${server_ns}" nft list chain inet filter output \ | sed -n 's/.*packets \([0-9]*\).*/\1/p') if [ "${DROP_COUNTER}" -ne "${TOTAL_COUNT}" ]; then echo "Expected ${TOTAL_COUNT} drops, got ${DROP_COUNTER}" @@ -84,13 +85,13 @@ if [ "${DROP_COUNTER}" -ne "${TOTAL_COUNT}" ]; then fi echo "Removing the drop rule" -ip netns exec peer0 nft flush ruleset +ip netns exec "${server_ns}" nft flush ruleset sleep 1 for p in $(seq 1 3); do - ip netns exec peer0 ping -qfc 500 -w 3 5.5.5.$((p + 1)) + ip netns exec "${server_ns}" ping -qfc 500 -w 3 5.5.5.$((p + 1)) done -cleanup +ovpn_cleanup modprobe -r ovpn || true diff --git a/tools/testing/selftests/net/ovpn/test-symmetric-id-float.sh b/tools/testing/selftests/net/ovpn/test-symmetric-id-float.sh index b3711a81b463..75296fe72c39 100755 --- a/tools/testing/selftests/net/ovpn/test-symmetric-id-float.sh +++ b/tools/testing/selftests/net/ovpn/test-symmetric-id-float.sh @@ -5,7 +5,7 @@ # Author: Ralf Lici # Antonio Quartulli -SYMMETRIC_ID="1" -FLOAT="1" +OVPN_SYMMETRIC_ID="1" +OVPN_FLOAT="1" source test.sh diff --git a/tools/testing/selftests/net/ovpn/test-symmetric-id-tcp.sh b/tools/testing/selftests/net/ovpn/test-symmetric-id-tcp.sh index 188cafb67b2f..680a465c49d2 100755 --- a/tools/testing/selftests/net/ovpn/test-symmetric-id-tcp.sh +++ b/tools/testing/selftests/net/ovpn/test-symmetric-id-tcp.sh @@ -5,7 +5,7 @@ # Author: Ralf Lici # Antonio Quartulli -PROTO="TCP" -SYMMETRIC_ID=1 +OVPN_PROTO="TCP" +OVPN_SYMMETRIC_ID=1 source test.sh diff --git a/tools/testing/selftests/net/ovpn/test-symmetric-id.sh b/tools/testing/selftests/net/ovpn/test-symmetric-id.sh index 35b119c72e4f..a2e2808959d9 100755 --- a/tools/testing/selftests/net/ovpn/test-symmetric-id.sh +++ b/tools/testing/selftests/net/ovpn/test-symmetric-id.sh @@ -5,6 +5,6 @@ # Author: Ralf Lici # Antonio Quartulli -SYMMETRIC_ID="1" +OVPN_SYMMETRIC_ID="1" source test.sh diff --git a/tools/testing/selftests/net/ovpn/test-tcp.sh b/tools/testing/selftests/net/ovpn/test-tcp.sh index ba3f1f315a34..27cc6e7b98bc 100755 --- a/tools/testing/selftests/net/ovpn/test-tcp.sh +++ b/tools/testing/selftests/net/ovpn/test-tcp.sh @@ -4,6 +4,6 @@ # # Author: Antonio Quartulli -PROTO="TCP" +OVPN_PROTO="TCP" source test.sh diff --git a/tools/testing/selftests/net/ovpn/test.sh b/tools/testing/selftests/net/ovpn/test.sh index b60e94a4094e..b766f4842940 100755 --- a/tools/testing/selftests/net/ovpn/test.sh +++ b/tools/testing/selftests/net/ovpn/test.sh @@ -8,37 +8,38 @@ set -e source ./common.sh +server_ns="ovpn_peer0" -cleanup +ovpn_cleanup modprobe -q ovpn || true -for p in $(seq 0 ${NUM_PEERS}); do - create_ns ${p} +for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_create_ns ${p} done -for p in $(seq 0 ${NUM_PEERS}); do - setup_listener ${p} +for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_setup_listener ${p} done -for p in $(seq 0 ${NUM_PEERS}); do - setup_ns ${p} 5.5.5.$((${p} + 1))/24 ${MTU} +for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_setup_ns ${p} 5.5.5.$((${p} + 1))/24 ${MTU} done -for p in $(seq 0 ${NUM_PEERS}); do - add_peer ${p} +for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_add_peer ${p} done -for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} set_peer tun0 ${p} 60 120 - ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} \ - $((${p}+ID_OFFSET)) 60 120 +for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ip netns exec "${server_ns}" ${OVPN_CLI} set_peer tun0 ${p} 60 120 + ip netns exec "ovpn_peer${p}" ${OVPN_CLI} set_peer tun${p} \ + $((${p}+OVPN_ID_OFFSET)) 60 120 done sleep 1 TCPDUMP_TIMEOUT="1.5s" -for p in $(seq 1 ${NUM_PEERS}); do +for p in $(seq 1 ${OVPN_NUM_PEERS}); do # The first part of the data packet header consists of: # - TCP only: 2 bytes for the packet length # - 5 bits for opcode ("9" for DATA_V2) @@ -47,119 +48,121 @@ for p in $(seq 1 ${NUM_PEERS}); do # - with asymmetric ID: "${p}" one way and "${p} + 9" the other way # - with symmetric ID: "${p}" both ways HEADER1=$(printf "0x4800000%x" ${p}) - HEADER2=$(printf "0x4800000%x" $((${p} + ID_OFFSET))) + HEADER2=$(printf "0x4800000%x" $((${p} + OVPN_ID_OFFSET))) RADDR="" - if [ "${PROTO}" == "UDP" ]; then - RADDR=$(awk "NR == ${p} {print \$3}" ${UDP_PEERS_FILE}) + if [ "${OVPN_PROTO}" == "UDP" ]; then + RADDR=$(awk "NR == ${p} {print \$3}" ${OVPN_UDP_PEERS_FILE}) fi - timeout ${TCPDUMP_TIMEOUT} ip netns exec peer${p} \ + timeout ${TCPDUMP_TIMEOUT} ip netns exec "ovpn_peer${p}" \ tcpdump --immediate-mode -p -ni veth${p} -c 1 \ - "$(build_capture_filter "${HEADER1}" "${RADDR}")" \ + "$(ovpn_build_capture_filter "${HEADER1}" "${RADDR}")" \ >/dev/null 2>&1 & TCPDUMP_PID1=$! - timeout ${TCPDUMP_TIMEOUT} ip netns exec peer${p} \ + timeout ${TCPDUMP_TIMEOUT} ip netns exec "ovpn_peer${p}" \ tcpdump --immediate-mode -p -ni veth${p} -c 1 \ - "$(build_capture_filter "${HEADER2}" "${RADDR}")" \ + "$(ovpn_build_capture_filter "${HEADER2}" "${RADDR}")" \ >/dev/null 2>&1 & TCPDUMP_PID2=$! sleep 0.3 - ip netns exec peer0 ping -qfc 500 -w 3 5.5.5.$((${p} + 1)) - ip netns exec peer0 ping -qfc 500 -s 3000 -w 3 5.5.5.$((${p} + 1)) + ip netns exec "${server_ns}" ping -qfc 500 -w 3 5.5.5.$((${p} + 1)) + ip netns exec "${server_ns}" ping -qfc 500 -s 3000 -w 3 \ + 5.5.5.$((${p} + 1)) wait ${TCPDUMP_PID1} wait ${TCPDUMP_PID2} done # ping LAN behind client 1 -ip netns exec peer0 ping -qfc 500 -w 3 ${LAN_IP} +ip netns exec "${server_ns}" ping -qfc 500 -w 3 ${OVPN_LAN_IP} -if [ "$FLOAT" == "1" ]; then +if [ "$OVPN_FLOAT" == "1" ]; then # make clients float.. - for p in $(seq 1 ${NUM_PEERS}); do - ip -n peer${p} addr del 10.10.${p}.2/24 dev veth${p} - ip -n peer${p} addr add 10.10.${p}.3/24 dev veth${p} + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ip -n "ovpn_peer${p}" addr del 10.10.${p}.2/24 dev veth${p} + ip -n "ovpn_peer${p}" addr add 10.10.${p}.3/24 dev veth${p} done - for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer${p} ping -qfc 500 -w 3 5.5.5.1 + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ip netns exec "ovpn_peer${p}" ping -qfc 500 -w 3 5.5.5.1 done fi -ip netns exec peer0 iperf3 -1 -s & +ip netns exec "${server_ns}" iperf3 -1 -s & sleep 1 -ip netns exec peer1 iperf3 -Z -t 3 -c 5.5.5.1 +ip netns exec ovpn_peer1 iperf3 -Z -t 3 -c 5.5.5.1 echo "Adding secondary key and then swap:" -for p in $(seq 1 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} new_key tun0 ${p} 2 1 ${ALG} 0 \ - data64.key - ip netns exec peer${p} ${OVPN_CLI} new_key tun${p} \ - $((${p} + ID_OFFSET)) 2 1 ${ALG} 1 data64.key - ip netns exec peer${p} ${OVPN_CLI} swap_keys tun${p} \ - $((${p} + ID_OFFSET)) +for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ip netns exec "${server_ns}" ${OVPN_CLI} new_key tun0 ${p} 2 1 \ + ${OVPN_ALG} 0 data64.key + ip netns exec "ovpn_peer${p}" ${OVPN_CLI} new_key tun${p} \ + $((${p} + OVPN_ID_OFFSET)) 2 1 ${OVPN_ALG} 1 data64.key + ip netns exec "ovpn_peer${p}" ${OVPN_CLI} swap_keys tun${p} \ + $((${p} + OVPN_ID_OFFSET)) done sleep 1 echo "Querying all peers:" -ip netns exec peer0 ${OVPN_CLI} get_peer tun0 -ip netns exec peer1 ${OVPN_CLI} get_peer tun1 +ip netns exec "${server_ns}" ${OVPN_CLI} get_peer tun0 +ip netns exec ovpn_peer1 ${OVPN_CLI} get_peer tun1 echo "Querying peer 1:" -ip netns exec peer0 ${OVPN_CLI} get_peer tun0 1 +ip netns exec "${server_ns}" ${OVPN_CLI} get_peer tun0 1 echo "Querying non-existent peer 20:" -ip netns exec peer0 ${OVPN_CLI} get_peer tun0 20 || true +ip netns exec "${server_ns}" ${OVPN_CLI} get_peer tun0 20 || true echo "Deleting peer 1:" -ip netns exec peer0 ${OVPN_CLI} del_peer tun0 1 -ip netns exec peer1 ${OVPN_CLI} del_peer tun1 $((1 + ID_OFFSET)) +ip netns exec "${server_ns}" ${OVPN_CLI} del_peer tun0 1 +ip netns exec ovpn_peer1 ${OVPN_CLI} del_peer tun1 $((1 + OVPN_ID_OFFSET)) echo "Querying keys:" -for p in $(seq 2 ${NUM_PEERS}); do - ip netns exec peer${p} ${OVPN_CLI} get_key tun${p} \ - $((${p} + ID_OFFSET)) 1 - ip netns exec peer${p} ${OVPN_CLI} get_key tun${p} \ - $((${p} + ID_OFFSET)) 2 +for p in $(seq 2 ${OVPN_NUM_PEERS}); do + ip netns exec "ovpn_peer${p}" ${OVPN_CLI} get_key tun${p} \ + $((${p} + OVPN_ID_OFFSET)) 1 + ip netns exec "ovpn_peer${p}" ${OVPN_CLI} get_key tun${p} \ + $((${p} + OVPN_ID_OFFSET)) 2 done echo "Deleting peer while sending traffic:" -(ip netns exec peer2 ping -qf -w 4 5.5.5.1)& +(ip netns exec ovpn_peer2 ping -qf -w 4 5.5.5.1)& sleep 2 -ip netns exec peer0 ${OVPN_CLI} del_peer tun0 2 +ip netns exec "${server_ns}" ${OVPN_CLI} del_peer tun0 2 # following command fails in TCP mode # (both ends get conn reset when one peer disconnects) -ip netns exec peer2 ${OVPN_CLI} del_peer tun2 $((2 + ID_OFFSET)) || true +ip netns exec ovpn_peer2 ${OVPN_CLI} del_peer tun2 $((2 + OVPN_ID_OFFSET)) || \ + true echo "Deleting keys:" -for p in $(seq 3 ${NUM_PEERS}); do - ip netns exec peer${p} ${OVPN_CLI} del_key tun${p} \ - $((${p} + ID_OFFSET)) 1 - ip netns exec peer${p} ${OVPN_CLI} del_key tun${p} \ - $((${p} + ID_OFFSET)) 2 +for p in $(seq 3 ${OVPN_NUM_PEERS}); do + ip netns exec "ovpn_peer${p}" ${OVPN_CLI} del_key tun${p} \ + $((${p} + OVPN_ID_OFFSET)) 1 + ip netns exec "ovpn_peer${p}" ${OVPN_CLI} del_key tun${p} \ + $((${p} + OVPN_ID_OFFSET)) 2 done echo "Setting timeout to 3s MP:" -for p in $(seq 3 ${NUM_PEERS}); do - ip netns exec peer0 ${OVPN_CLI} set_peer tun0 ${p} 3 3 || true - ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} \ - $((${p} + ID_OFFSET)) 0 0 +for p in $(seq 3 ${OVPN_NUM_PEERS}); do + ip netns exec "${server_ns}" ${OVPN_CLI} set_peer tun0 ${p} 3 3 || true + ip netns exec "ovpn_peer${p}" ${OVPN_CLI} set_peer tun${p} \ + $((${p} + OVPN_ID_OFFSET)) 0 0 done # wait for peers to timeout sleep 5 echo "Setting timeout to 3s P2P:" -for p in $(seq 3 ${NUM_PEERS}); do - ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} \ - $((${p} + ID_OFFSET)) 3 3 +for p in $(seq 3 ${OVPN_NUM_PEERS}); do + ip netns exec "ovpn_peer${p}" ${OVPN_CLI} set_peer tun${p} \ + $((${p} + OVPN_ID_OFFSET)) 3 3 done sleep 5 -for p in $(seq 0 ${NUM_PEERS}); do - compare_ntfs ${p} +for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_compare_ntfs ${p} done -cleanup +ovpn_cleanup modprobe -r ovpn || true -- cgit v1.2.3 From 1be93bb979ab02554541b04406e9e3a6a8e0ce9e Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Mon, 23 Mar 2026 15:12:32 +0100 Subject: selftests: ovpn: align command flow with TAP Current tests do not properly adhere to the TAP infrastructure therefore they do not properly report failures leading to hangs of the CI machinery. Restructure ovpn selftests into using the TAP infrastructure: split each test in stages, execute stage bodies with fail-fast semantics, and emit KTAP pass/fail for each stage. Centralize behavior control in common.sh and makes the scripts use dedicated wrappers for required-success, expected-failure, and non-fatal commands. Also add the OVPN_VERBOSE mode that exposes captured command output for debugging. This way tests won't hang anymore in case of failure when executed within the CI machinery. This change also makes default OVPN_CLI and YNL resolution independent from the caller CWD by anchoring both to COMMON_DIR, so behavior is stable across direct execution and run_tests-style execution. Fixes: 959bc330a439 ("testing/selftests: add test tool and scripts for ovpn module") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- tools/testing/selftests/net/ovpn/common.sh | 182 ++++++++- .../selftests/net/ovpn/test-close-socket.sh | 90 +++-- tools/testing/selftests/net/ovpn/test-mark.sh | 236 +++++++---- tools/testing/selftests/net/ovpn/test.sh | 441 ++++++++++++++------- 4 files changed, 676 insertions(+), 273 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/ovpn/common.sh b/tools/testing/selftests/net/ovpn/common.sh index 38f187b9de23..2d844eb3aa6e 100644 --- a/tools/testing/selftests/net/ovpn/common.sh +++ b/tools/testing/selftests/net/ovpn/common.sh @@ -4,14 +4,18 @@ # # Author: Antonio Quartulli +OVPN_COMMON_DIR=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")") +source "$OVPN_COMMON_DIR/../../kselftest/ktap_helpers.sh" + OVPN_UDP_PEERS_FILE=${OVPN_UDP_PEERS_FILE:-udp_peers.txt} OVPN_TCP_PEERS_FILE=${OVPN_TCP_PEERS_FILE:-tcp_peers.txt} -OVPN_CLI=${OVPN_CLI:-./ovpn-cli} -OVPN_YNL=${OVPN_YNL:-../../../../net/ynl/pyynl/cli.py} +OVPN_CLI=${OVPN_CLI:-${OVPN_COMMON_DIR}/ovpn-cli} +OVPN_YNL=${OVPN_YNL:-${OVPN_COMMON_DIR}/../../../../net/ynl/pyynl/cli.py} OVPN_ALG=${OVPN_ALG:-aes} OVPN_PROTO=${OVPN_PROTO:-UDP} OVPN_FLOAT=${OVPN_FLOAT:-0} OVPN_SYMMETRIC_ID=${OVPN_SYMMETRIC_ID:-0} +OVPN_VERBOSE=${OVPN_VERBOSE:-0} export OVPN_ID_OFFSET=$(( 9 * (OVPN_SYMMETRIC_ID == 0) )) @@ -22,6 +26,111 @@ OVPN_LAN_IP="11.11.11.11" declare -A OVPN_TMP_JSONS=() declare -A OVPN_LISTENER_PIDS=() +OVPN_CURRENT_STAGE="" + +ovpn_is_verbose() { + [[ "${OVPN_VERBOSE}" == "1" ]] +} + +ovpn_log() { + ovpn_is_verbose || return 0 + printf '%s\n' "$*" +} + +ovpn_print_cmd_output() { + local output_file="$1" + local line + + [[ -s "${output_file}" ]] || return 0 + + while IFS= read -r line; do + ovpn_log "${line}" + done < "${output_file}" +} + +ovpn_cmd_run() { + local mode="$1" + local label="$2" + local output_file + local rc + local ret=0 + + shift 2 + + output_file=$(mktemp) + if "$@" >"${output_file}" 2>&1; then + rc=0 + else + rc=$? + fi + + case "${mode}" in + ok) + if [[ "${rc}" -ne 0 ]]; then + cat "${output_file}" + printf '%s\n' \ + "${label}: command failed with rc=${rc}: $*" + ret="${rc}" + fi + ;; + mayfail) + ;; + fail) + [[ "${rc}" -eq 0 ]] && ret=1 + ;; + esac + + if ovpn_is_verbose && [[ "${rc}" -eq 0 || "${mode}" != "ok" ]]; then + ovpn_print_cmd_output "${output_file}" + fi + + rm -f "${output_file}" + return "${ret}" +} + +ovpn_cmd_ok() { + ovpn_cmd_run ok "$@" +} + +ovpn_cmd_mayfail() { + ovpn_cmd_run mayfail "$@" +} + +ovpn_cmd_fail() { + ovpn_cmd_run fail "$@" +} + +ovpn_run_bg() { + local pid_var="$1" + + shift + if ovpn_is_verbose; then + "$@" & + else + "$@" >/dev/null 2>&1 & + fi + + printf -v "${pid_var}" '%s' "$!" +} + +ovpn_run_stage() { + local label="$1" + + shift + OVPN_CURRENT_STAGE="${label}" + "$@" + OVPN_CURRENT_STAGE="" + ktap_test_pass "${label}" +} + +ovpn_stage_err() { + # ERR trap is global under set -eE: only report failures that happen + # while ovpn_run_stage() is actively executing a stage body. + if [[ -n "${OVPN_CURRENT_STAGE}" ]]; then + ktap_test_fail "${OVPN_CURRENT_STAGE}" + OVPN_CURRENT_STAGE="" + fi +} ovpn_create_ns() { ip netns add "ovpn_peer${1}" @@ -87,12 +196,16 @@ ovpn_build_capture_filter() { } ovpn_setup_listener() { - local peer_ns="ovpn_peer${p}" + local peer="$1" + local file + local peer_ns="ovpn_peer${peer}" + file=$(mktemp) PYTHONUNBUFFERED=1 ip netns exec "${peer_ns}" "${OVPN_YNL}" --family \ - ovpn --subscribe peers --output-json --duration 40 > ${file} & - OVPN_LISTENER_PIDS[$1]=$! - OVPN_TMP_JSONS[$1]="${file}" + ovpn --subscribe peers --output-json > "${file}" \ + 2>/dev/null & + OVPN_LISTENER_PIDS["${peer}"]=$! + OVPN_TMP_JSONS["${peer}"]="${file}" } ovpn_add_peer() { @@ -173,8 +286,7 @@ ovpn_compare_ntfs() { received="${OVPN_TMP_JSONS[$1]}" diff_file=$(mktemp) - kill -TERM ${OVPN_LISTENER_PIDS[$1]} || true - wait ${OVPN_LISTENER_PIDS[$1]} || true + ovpn_stop_listener "${1}" 1 printf "Checking notifications for peer ${1}... " if diff <(jq -s "${OVPN_JQ_FILTER}" ${expected}) \ <(jq -s "${OVPN_JQ_FILTER}" ${received}) \ @@ -187,30 +299,60 @@ ovpn_compare_ntfs() { fi rm -f "${diff_file}" || true - rm -f ${received} || true + rm -f "${received}" || true + unset "OVPN_TMP_JSONS[$1]" fi return "${diff_rc}" } +ovpn_stop_listener() { + local peer="$1" + local keep_json="${2:-0}" + local pid="${OVPN_LISTENER_PIDS[$peer]:-}" + local json="${OVPN_TMP_JSONS[$peer]:-}" + + if [[ -n "${pid}" ]]; then + kill -TERM "${pid}" 2>/dev/null || true + wait "${pid}" 2>/dev/null || true + unset "OVPN_LISTENER_PIDS[$peer]" + fi + + if [[ -n "${json}" && "${keep_json}" -eq 0 ]]; then + rm -f "${json}" || true + unset "OVPN_TMP_JSONS[$peer]" + fi +} + +ovpn_cleanup_peer_ns() { + local peer="$1" + local peer_id="${peer#ovpn_peer}" + + ip -n "${peer}" link set tun${peer_id} down 2>/dev/null || true + ip netns exec "${peer}" ${OVPN_CLI} del_iface tun${peer_id} \ + 1>/dev/null 2>&1 || true + ip netns del "${peer}" 2>/dev/null || true +} + ovpn_cleanup() { - local peer_ns + local peer + # some ovpn-cli processes sleep in background so they need manual poking - killall $(basename ${OVPN_CLI}) 2>/dev/null || true + killall "$(basename "${OVPN_CLI}")" 2>/dev/null || true - # netns peer0 is deleted without erasing ifaces first - for p in $(seq 1 10); do - peer_ns="ovpn_peer${p}" - ip -n "${peer_ns}" link set tun${p} down 2>/dev/null || true - ip netns exec "${peer_ns}" ${OVPN_CLI} del_iface tun${p} \ - 2>/dev/null || true + for peer in "${!OVPN_LISTENER_PIDS[@]}"; do + ovpn_stop_listener "${peer}" 2>/dev/null done + for p in $(seq 1 10); do ip -n ovpn_peer0 link del veth${p} 2>/dev/null || true done - for p in $(seq 0 10); do - ip netns del "ovpn_peer${p}" 2>/dev/null || true - done + + # remove from ovpn's netns pool + while IFS= read -r peer; do + [[ -n "${peer}" ]] || continue + ovpn_cleanup_peer_ns "${peer}" 2>/dev/null + done < <(ip netns list 2>/dev/null | awk '/^ovpn_/ {print $1}') } if [ "${OVPN_PROTO}" == "UDP" ]; then diff --git a/tools/testing/selftests/net/ovpn/test-close-socket.sh b/tools/testing/selftests/net/ovpn/test-close-socket.sh index 6bc1b6eab8ac..af1532b4d2da 100755 --- a/tools/testing/selftests/net/ovpn/test-close-socket.sh +++ b/tools/testing/selftests/net/ovpn/test-close-socket.sh @@ -5,43 +5,81 @@ # Author: Antonio Quartulli #set -x -set -e +set -eE source ./common.sh -server_ns="ovpn_peer0" -ovpn_cleanup +ovpn_test_finished=0 -modprobe -q ovpn || true +ovpn_test_exit() { + ovpn_cleanup + modprobe -r ovpn || true + + if [ "${ovpn_test_finished}" -eq 0 ]; then + ktap_print_totals + fi +} + +ovpn_prepare_network() { + local p + local peer_ns + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "create namespace peer${p}" ovpn_create_ns "${p}" + done -for p in $(seq 0 ${OVPN_NUM_PEERS}); do - ovpn_create_ns ${p} -done + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "configure peer${p} namespace" ovpn_setup_ns \ + "${p}" 5.5.5.$((p + 1))/24 + done -for p in $(seq 0 ${OVPN_NUM_PEERS}); do - ovpn_setup_ns ${p} 5.5.5.$((${p} + 1))/24 -done + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "register peer${p} in overlay" ovpn_add_peer "${p}" + done -for p in $(seq 0 ${OVPN_NUM_PEERS}); do - ovpn_add_peer ${p} -done + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "set peer0 timeout for peer ${p}" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \ + ${p} 60 120 + ovpn_cmd_ok "set peer${p} timeout for peer ${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + tun${p} $((p + OVPN_ID_OFFSET)) 60 120 + done +} -for p in $(seq 1 ${OVPN_NUM_PEERS}); do - ip netns exec "${server_ns}" ${OVPN_CLI} set_peer tun0 ${p} 60 120 - ip netns exec "ovpn_peer${p}" ${OVPN_CLI} set_peer tun${p} $((${p}+9)) \ - 60 120 -done +ovpn_run_ping_traffic() { + local p -sleep 1 + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "send ping traffic to peer ${p}" \ + ip netns exec ovpn_peer0 ping -qfc 500 -w 3 \ + 5.5.5.$((p + 1)) + done +} -for p in $(seq 1 ${OVPN_NUM_PEERS}); do - ip netns exec "${server_ns}" ping -qfc 500 -w 3 5.5.5.$((${p} + 1)) -done +ovpn_run_iperf() { + local iperf_pid -ip netns exec "${server_ns}" iperf3 -1 -s & -sleep 1 -ip netns exec ovpn_peer1 iperf3 -Z -t 3 -c 5.5.5.1 + ovpn_run_bg iperf_pid ip netns exec ovpn_peer0 iperf3 -1 -s + sleep 1 + ovpn_cmd_ok "run iperf throughput flow" \ + ip netns exec ovpn_peer1 iperf3 -Z -t 3 -c 5.5.5.1 + wait "${iperf_pid}" || return 1 +} + +trap ovpn_test_exit EXIT +trap ovpn_stage_err ERR + +ktap_print_header +ktap_set_plan 3 ovpn_cleanup +modprobe -q ovpn || true + +ovpn_run_stage "setup network topology" ovpn_prepare_network +ovpn_run_stage "run ping traffic" ovpn_run_ping_traffic +ovpn_run_stage "run iperf throughput" ovpn_run_iperf -modprobe -r ovpn || true +ovpn_test_finished=1 +ktap_finished diff --git a/tools/testing/selftests/net/ovpn/test-mark.sh b/tools/testing/selftests/net/ovpn/test-mark.sh index 2ee5dc5fc538..5a8f47554286 100755 --- a/tools/testing/selftests/net/ovpn/test-mark.sh +++ b/tools/testing/selftests/net/ovpn/test-mark.sh @@ -6,92 +6,166 @@ # Antonio Quartulli #set -x -set -e +set -eE MARK=1056 +MARK_DROP_COUNTER=0 source ./common.sh -server_ns="ovpn_peer0" -ovpn_cleanup - -modprobe -q ovpn || true - -for p in $(seq 0 "${OVPN_NUM_PEERS}"); do - ovpn_create_ns "${p}" -done - -for p in $(seq 0 3); do - ovpn_setup_ns "${p}" 5.5.5.$((p + 1))/24 -done - -# add peer0 with mark -ip netns exec "${server_ns}" "${OVPN_CLI}" new_multi_peer tun0 1 ASYMM \ - "${OVPN_UDP_PEERS_FILE}" \ - ${MARK} -for p in $(seq 1 3); do - ip netns exec "${server_ns}" "${OVPN_CLI}" new_key tun0 "${p}" 1 0 \ - "${OVPN_ALG}" 0 data64.key -done - -for p in $(seq 1 3); do - ovpn_add_peer "${p}" -done - -for p in $(seq 1 3); do - ip netns exec "${server_ns}" "${OVPN_CLI}" set_peer tun0 "${p}" 60 120 - ip netns exec "ovpn_peer${p}" "${OVPN_CLI}" set_peer tun"${p}" \ - $((p + 9)) 60 120 -done - -sleep 1 - -for p in $(seq 1 3); do - ip netns exec "${server_ns}" ping -qfc 500 -w 3 5.5.5.$((p + 1)) -done - -echo "Adding an nftables drop rule based on mark value ${MARK}" -ip netns exec "${server_ns}" nft flush ruleset -ip netns exec "${server_ns}" nft 'add table inet filter' -ip netns exec "${server_ns}" nft 'add chain inet filter output { - type filter hook output priority 0; - policy accept; -}' -ip netns exec "${server_ns}" nft add rule inet filter output \ - meta mark == ${MARK} \ - counter drop - -DROP_COUNTER=$(ip netns exec "${server_ns}" nft list chain inet filter output \ - | sed -n 's/.*packets \([0-9]*\).*/\1/p') -sleep 1 - -# ping should fail -for p in $(seq 1 3); do - PING_OUTPUT=$(ip netns exec "${server_ns}" ping \ - -qfc 500 -w 1 5.5.5.$((p + 1)) 2>&1) && exit 1 - echo "${PING_OUTPUT}" - LOST_PACKETS=$(echo "$PING_OUTPUT" \ - | awk '/packets transmitted/ { print $1 }') - # increment the drop counter by the amount of lost packets - DROP_COUNTER=$((DROP_COUNTER + LOST_PACKETS)) -done - -# check if the final nft counter matches our counter -TOTAL_COUNT=$(ip netns exec "${server_ns}" nft list chain inet filter output \ - | sed -n 's/.*packets \([0-9]*\).*/\1/p') -if [ "${DROP_COUNTER}" -ne "${TOTAL_COUNT}" ]; then - echo "Expected ${TOTAL_COUNT} drops, got ${DROP_COUNTER}" - exit 1 -fi - -echo "Removing the drop rule" -ip netns exec "${server_ns}" nft flush ruleset -sleep 1 - -for p in $(seq 1 3); do - ip netns exec "${server_ns}" ping -qfc 500 -w 3 5.5.5.$((p + 1)) -done +ovpn_test_finished=0 + +ovpn_test_exit() { + ovpn_cleanup + modprobe -r ovpn || true + + if [ "${ovpn_test_finished}" -eq 0 ]; then + ktap_print_totals + fi +} + +ovpn_mark_prepare_network() { + local p + local peer_ns + + for p in $(seq 0 "${OVPN_NUM_PEERS}"); do + ovpn_cmd_ok "create namespace peer${p}" ovpn_create_ns "${p}" + done + + for p in $(seq 0 3); do + ovpn_cmd_ok "configure peer${p} namespace" ovpn_setup_ns \ + "${p}" 5.5.5.$((p + 1))/24 + done + + ovpn_cmd_ok "create server-side multi-peer with fwmark" \ + ip netns exec ovpn_peer0 "${OVPN_CLI}" new_multi_peer tun0 1 \ + ASYMM "${OVPN_UDP_PEERS_FILE}" "${MARK}" + for p in $(seq 1 3); do + ovpn_cmd_ok "install server key for peer ${p}" \ + ip netns exec ovpn_peer0 "${OVPN_CLI}" new_key tun0 \ + "${p}" 1 0 "${OVPN_ALG}" 0 data64.key + done + + for p in $(seq 1 3); do + ovpn_cmd_ok "register peer${p} in overlay" ovpn_add_peer "${p}" + done + + for p in $(seq 1 3); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "set peer0 timeout for peer ${p}" \ + ip netns exec ovpn_peer0 "${OVPN_CLI}" set_peer tun0 \ + "${p}" 60 120 + ovpn_cmd_ok "set peer${p} timeout for peer ${p}" \ + ip netns exec "${peer_ns}" "${OVPN_CLI}" set_peer \ + tun"${p}" $((p + OVPN_ID_OFFSET)) 60 120 + done +} + +ovpn_mark_run_baseline_traffic() { + local p + + for p in $(seq 1 3); do + ovpn_cmd_ok "send baseline traffic to peer ${p}" \ + ip netns exec ovpn_peer0 ping -qfc 500 -w 3 \ + 5.5.5.$((p + 1)) + done +} + +ovpn_mark_add_drop_rule() { + ovpn_log "Adding an nftables drop rule based on mark value ${MARK}" + + ovpn_cmd_ok "flush nft ruleset" ip netns exec ovpn_peer0 nft flush \ + ruleset + ovpn_cmd_ok "create nft filter table" ip netns exec ovpn_peer0 nft \ + "add table inet filter" + ovpn_cmd_ok "create nft filter output chain" \ + ip netns exec ovpn_peer0 nft "add chain inet filter output { \ + type filter hook output priority 0; policy accept; }" + ovpn_cmd_ok "add nft drop rule for mark ${MARK}" \ + ip netns exec ovpn_peer0 nft add rule inet filter output \ + meta mark == "${MARK}" \ + counter drop + + MARK_DROP_COUNTER=$(ip netns exec ovpn_peer0 nft list chain inet \ + filter output | sed -n 's/.*packets \([0-9]*\).*/\1/p') + if [ -z "${MARK_DROP_COUNTER}" ]; then + printf '%s\n' "unable to read nft drop counter" + return 1 + fi +} + +ovpn_mark_verify_drop_traffic() { + local p + local ping_output + local lost_packets + local total_count + + for p in $(seq 1 3); do + if ping_output=$(ip netns exec ovpn_peer0 ping -qfc 500 -w 1 \ + 5.5.5.$((p + 1)) 2>&1); then + printf '%s\n' "expected ping to peer ${p} to fail \ + after nft drop rule" + return 1 + fi + ovpn_log "${ping_output}" + lost_packets=$(echo "${ping_output}" | \ + awk '/packets transmitted/ { print $1 }') + if [ -z "${lost_packets}" ]; then + printf '%s\n' "unable to parse lost packets for peer \ + ${p}" + return 1 + fi + MARK_DROP_COUNTER=$((MARK_DROP_COUNTER + lost_packets)) + done + + total_count=$(ip netns exec ovpn_peer0 nft list chain inet filter \ + output | sed -n 's/.*packets \([0-9]*\).*/\1/p') + if [ -z "${total_count}" ]; then + printf '%s\n' "unable to read final nft drop counter" + return 1 + fi + if [ "${MARK_DROP_COUNTER}" -ne "${total_count}" ]; then + printf '%s\n' "expected ${MARK_DROP_COUNTER} drops, got \ + ${total_count}" + return 1 + fi +} + +ovpn_mark_remove_drop_rule() { + ovpn_log "Removing the drop rule" + + ovpn_cmd_ok "flush nft ruleset" ip netns exec ovpn_peer0 nft flush \ + ruleset +} + +ovpn_mark_verify_traffic_recovery() { + local p + + sleep 1 + for p in $(seq 1 3); do + ovpn_cmd_ok "send recovery traffic to peer ${p}" \ + ip netns exec ovpn_peer0 ping -qfc 500 -w 3 \ + 5.5.5.$((p + 1)) + done +} + +trap ovpn_test_exit EXIT +trap ovpn_stage_err ERR + +ktap_print_header +ktap_set_plan 6 ovpn_cleanup +modprobe -q ovpn || true -modprobe -r ovpn || true +ovpn_run_stage "setup marked network topology" ovpn_mark_prepare_network +ovpn_run_stage "run baseline traffic" ovpn_mark_run_baseline_traffic +ovpn_run_stage "install nft mark drop rule" ovpn_mark_add_drop_rule +ovpn_run_stage "drop marked traffic and count packets" \ + ovpn_mark_verify_drop_traffic +ovpn_run_stage "remove nft drop rule" ovpn_mark_remove_drop_rule +ovpn_run_stage "traffic recovers after drop removal" \ + ovpn_mark_verify_traffic_recovery + +ovpn_test_finished=1 +ktap_finished diff --git a/tools/testing/selftests/net/ovpn/test.sh b/tools/testing/selftests/net/ovpn/test.sh index b766f4842940..eca653112aeb 100755 --- a/tools/testing/selftests/net/ovpn/test.sh +++ b/tools/testing/selftests/net/ovpn/test.sh @@ -5,164 +5,313 @@ # Author: Antonio Quartulli #set -x -set -e +set -eE source ./common.sh -server_ns="ovpn_peer0" -ovpn_cleanup +ovpn_test_finished=0 -modprobe -q ovpn || true +ovpn_test_exit() { + ovpn_cleanup + modprobe -r ovpn || true -for p in $(seq 0 ${OVPN_NUM_PEERS}); do - ovpn_create_ns ${p} -done - -for p in $(seq 0 ${OVPN_NUM_PEERS}); do - ovpn_setup_listener ${p} -done - -for p in $(seq 0 ${OVPN_NUM_PEERS}); do - ovpn_setup_ns ${p} 5.5.5.$((${p} + 1))/24 ${MTU} -done - -for p in $(seq 0 ${OVPN_NUM_PEERS}); do - ovpn_add_peer ${p} -done - -for p in $(seq 1 ${OVPN_NUM_PEERS}); do - ip netns exec "${server_ns}" ${OVPN_CLI} set_peer tun0 ${p} 60 120 - ip netns exec "ovpn_peer${p}" ${OVPN_CLI} set_peer tun${p} \ - $((${p}+OVPN_ID_OFFSET)) 60 120 -done - -sleep 1 - -TCPDUMP_TIMEOUT="1.5s" -for p in $(seq 1 ${OVPN_NUM_PEERS}); do - # The first part of the data packet header consists of: - # - TCP only: 2 bytes for the packet length - # - 5 bits for opcode ("9" for DATA_V2) - # - 3 bits for key-id ("0" at this point) - # - 12 bytes for peer-id: - # - with asymmetric ID: "${p}" one way and "${p} + 9" the other way - # - with symmetric ID: "${p}" both ways - HEADER1=$(printf "0x4800000%x" ${p}) - HEADER2=$(printf "0x4800000%x" $((${p} + OVPN_ID_OFFSET))) - RADDR="" - if [ "${OVPN_PROTO}" == "UDP" ]; then - RADDR=$(awk "NR == ${p} {print \$3}" ${OVPN_UDP_PEERS_FILE}) + if [ "${ovpn_test_finished}" -eq 0 ]; then + ktap_print_totals fi +} + +ovpn_prepare_network() { + local p + local peer_ns + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "create namespace peer${p}" ovpn_create_ns "${p}" + done + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "start notification listener peer${p}" \ + ovpn_setup_listener "${p}" + done + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "configure peer${p} namespace" ovpn_setup_ns \ + "${p}" 5.5.5.$((p + 1))/24 "${MTU}" + done + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "register peer${p} in overlay" ovpn_add_peer "${p}" + done - timeout ${TCPDUMP_TIMEOUT} ip netns exec "ovpn_peer${p}" \ - tcpdump --immediate-mode -p -ni veth${p} -c 1 \ - "$(ovpn_build_capture_filter "${HEADER1}" "${RADDR}")" \ - >/dev/null 2>&1 & - TCPDUMP_PID1=$! - timeout ${TCPDUMP_TIMEOUT} ip netns exec "ovpn_peer${p}" \ - tcpdump --immediate-mode -p -ni veth${p} -c 1 \ - "$(ovpn_build_capture_filter "${HEADER2}" "${RADDR}")" \ - >/dev/null 2>&1 & - TCPDUMP_PID2=$! - - sleep 0.3 - ip netns exec "${server_ns}" ping -qfc 500 -w 3 5.5.5.$((${p} + 1)) - ip netns exec "${server_ns}" ping -qfc 500 -s 3000 -w 3 \ - 5.5.5.$((${p} + 1)) - - wait ${TCPDUMP_PID1} - wait ${TCPDUMP_PID2} -done - -# ping LAN behind client 1 -ip netns exec "${server_ns}" ping -qfc 500 -w 3 ${OVPN_LAN_IP} - -if [ "$OVPN_FLOAT" == "1" ]; then - # make clients float.. for p in $(seq 1 ${OVPN_NUM_PEERS}); do - ip -n "ovpn_peer${p}" addr del 10.10.${p}.2/24 dev veth${p} - ip -n "ovpn_peer${p}" addr add 10.10.${p}.3/24 dev veth${p} + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "set peer0 timeout for peer ${p}" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \ + ${p} 60 120 + ovpn_cmd_ok "set peer${p} timeout for peer ${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + tun${p} $((p + OVPN_ID_OFFSET)) 60 120 done +} + +ovpn_run_basic_traffic() { + local p + local header1 + local header2 + local peer_ns + local raddr + local tcpdump_pid1 + local tcpdump_pid2 + local tcpdump_timeout="1.5s" + for p in $(seq 1 ${OVPN_NUM_PEERS}); do - ip netns exec "ovpn_peer${p}" ping -qfc 500 -w 3 5.5.5.1 + # The first part of the data packet header consists of: + # - TCP only: 2 bytes for the packet length + # - 5 bits for opcode ("9" for DATA_V2) + # - 3 bits for key-id ("0" at this point) + # - 12 bytes for peer-id: + # - with asymmetric ID: "${p}" one way and "${p} + 9" the + # other way + # - with symmetric ID: "${p}" both ways + header1=$(printf "0x4800000%x" ${p}) + header2=$(printf "0x4800000%x" $((p + OVPN_ID_OFFSET))) + raddr="" + if [ "${OVPN_PROTO}" == "UDP" ]; then + raddr=$(awk "NR == ${p} {print \$3}" \ + "${OVPN_UDP_PEERS_FILE}") + fi + peer_ns="ovpn_peer${p}" + + timeout ${tcpdump_timeout} ip netns exec "${peer_ns}" \ + tcpdump --immediate-mode -p -ni veth${p} -c 1 \ + "$(ovpn_build_capture_filter "${header1}" "${raddr}")" \ + >/dev/null 2>&1 & + tcpdump_pid1=$! + timeout ${tcpdump_timeout} ip netns exec "${peer_ns}" \ + tcpdump --immediate-mode -p -ni veth${p} -c 1 \ + "$(ovpn_build_capture_filter "${header2}" "${raddr}")" \ + >/dev/null 2>&1 & + tcpdump_pid2=$! + + sleep 0.3 + ovpn_cmd_ok "send baseline traffic to peer ${p}" \ + ip netns exec ovpn_peer0 \ + ping -qfc 500 -w 3 5.5.5.$((p + 1)) + ovpn_cmd_ok "send large-payload traffic to peer ${p}" \ + ip netns exec ovpn_peer0 \ + ping -qfc 500 -s 3000 -w 3 5.5.5.$((p + 1)) + + wait "${tcpdump_pid1}" || return 1 + wait "${tcpdump_pid2}" || return 1 done -fi +} + +ovpn_run_lan_traffic() { + ovpn_cmd_ok "ping LAN behind peer1" \ + ip netns exec ovpn_peer0 ping -qfc 500 -w 3 "${OVPN_LAN_IP}" +} + +ovpn_run_float_mode() { + local p + local peer_ns + + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "float: remove old transport address on peer${p}" \ + ip -n "${peer_ns}" addr del 10.10.${p}.2/24 dev veth${p} + ovpn_cmd_ok "float: add new transport address on peer${p}" \ + ip -n "${peer_ns}" addr add 10.10.${p}.3/24 dev veth${p} + done + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "ping tunnel after float peer ${p}" \ + ip netns exec "${peer_ns}" ping -qfc 500 -w 3 5.5.5.1 + done +} + +ovpn_run_iperf() { + local iperf_pid + + ovpn_run_bg iperf_pid ip netns exec ovpn_peer0 iperf3 -1 -s + sleep 1 + + ovpn_cmd_ok "run iperf throughput flow" \ + ip netns exec ovpn_peer1 iperf3 -Z -t 3 -c 5.5.5.1 + wait "${iperf_pid}" || return 1 +} + +ovpn_run_key_rollover() { + local p + local peer_ns + + ovpn_log "Adding secondary key and then swap:" + + for p in $(seq 1 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "add secondary key on peer0 for peer ${p}" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} new_key tun0 \ + ${p} 2 1 ${OVPN_ALG} 0 data64.key + ovpn_cmd_ok "add secondary key on peer${p} for peer ${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} new_key tun${p} \ + $((p + OVPN_ID_OFFSET)) 2 1 ${OVPN_ALG} 1 \ + data64.key + ovpn_cmd_ok "swap keys on peer${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} swap_keys \ + tun${p} $((p + OVPN_ID_OFFSET)) + done +} + +ovpn_run_queries() { + ovpn_log "Querying all peers:" + + ovpn_cmd_ok "query all peers from peer0" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 + ovpn_cmd_ok "query all peers from peer1" \ + ip netns exec ovpn_peer1 ${OVPN_CLI} get_peer tun1 -ip netns exec "${server_ns}" iperf3 -1 -s & -sleep 1 -ip netns exec ovpn_peer1 iperf3 -Z -t 3 -c 5.5.5.1 - -echo "Adding secondary key and then swap:" -for p in $(seq 1 ${OVPN_NUM_PEERS}); do - ip netns exec "${server_ns}" ${OVPN_CLI} new_key tun0 ${p} 2 1 \ - ${OVPN_ALG} 0 data64.key - ip netns exec "ovpn_peer${p}" ${OVPN_CLI} new_key tun${p} \ - $((${p} + OVPN_ID_OFFSET)) 2 1 ${OVPN_ALG} 1 data64.key - ip netns exec "ovpn_peer${p}" ${OVPN_CLI} swap_keys tun${p} \ - $((${p} + OVPN_ID_OFFSET)) -done - -sleep 1 - -echo "Querying all peers:" -ip netns exec "${server_ns}" ${OVPN_CLI} get_peer tun0 -ip netns exec ovpn_peer1 ${OVPN_CLI} get_peer tun1 - -echo "Querying peer 1:" -ip netns exec "${server_ns}" ${OVPN_CLI} get_peer tun0 1 - -echo "Querying non-existent peer 20:" -ip netns exec "${server_ns}" ${OVPN_CLI} get_peer tun0 20 || true - -echo "Deleting peer 1:" -ip netns exec "${server_ns}" ${OVPN_CLI} del_peer tun0 1 -ip netns exec ovpn_peer1 ${OVPN_CLI} del_peer tun1 $((1 + OVPN_ID_OFFSET)) - -echo "Querying keys:" -for p in $(seq 2 ${OVPN_NUM_PEERS}); do - ip netns exec "ovpn_peer${p}" ${OVPN_CLI} get_key tun${p} \ - $((${p} + OVPN_ID_OFFSET)) 1 - ip netns exec "ovpn_peer${p}" ${OVPN_CLI} get_key tun${p} \ - $((${p} + OVPN_ID_OFFSET)) 2 -done - -echo "Deleting peer while sending traffic:" -(ip netns exec ovpn_peer2 ping -qf -w 4 5.5.5.1)& -sleep 2 -ip netns exec "${server_ns}" ${OVPN_CLI} del_peer tun0 2 -# following command fails in TCP mode -# (both ends get conn reset when one peer disconnects) -ip netns exec ovpn_peer2 ${OVPN_CLI} del_peer tun2 $((2 + OVPN_ID_OFFSET)) || \ - true - -echo "Deleting keys:" -for p in $(seq 3 ${OVPN_NUM_PEERS}); do - ip netns exec "ovpn_peer${p}" ${OVPN_CLI} del_key tun${p} \ - $((${p} + OVPN_ID_OFFSET)) 1 - ip netns exec "ovpn_peer${p}" ${OVPN_CLI} del_key tun${p} \ - $((${p} + OVPN_ID_OFFSET)) 2 -done - -echo "Setting timeout to 3s MP:" -for p in $(seq 3 ${OVPN_NUM_PEERS}); do - ip netns exec "${server_ns}" ${OVPN_CLI} set_peer tun0 ${p} 3 3 || true - ip netns exec "ovpn_peer${p}" ${OVPN_CLI} set_peer tun${p} \ - $((${p} + OVPN_ID_OFFSET)) 0 0 -done -# wait for peers to timeout -sleep 5 - -echo "Setting timeout to 3s P2P:" -for p in $(seq 3 ${OVPN_NUM_PEERS}); do - ip netns exec "ovpn_peer${p}" ${OVPN_CLI} set_peer tun${p} \ - $((${p} + OVPN_ID_OFFSET)) 3 3 -done -sleep 5 - -for p in $(seq 0 ${OVPN_NUM_PEERS}); do - ovpn_compare_ntfs ${p} -done + ovpn_log "Querying peer 1:" + + ovpn_cmd_ok "query peer 1 from peer0" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 1 +} + +ovpn_query_peer_missing() { + ovpn_log "Querying non-existent peer 20:" + + ovpn_cmd_fail "query missing peer 20 on peer0" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} get_peer tun0 20 +} + +ovpn_run_peer_cleanup() { + local p + local peer_ns + + ovpn_log "Deleting peer 1:" + + ovpn_cmd_ok "delete peer1 on peer0" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} del_peer tun0 1 + ovpn_cmd_ok "delete peer1 on peer1" \ + ip netns exec ovpn_peer1 ${OVPN_CLI} del_peer tun1 \ + $((1 + OVPN_ID_OFFSET)) + + ovpn_log "Querying keys:" + + for p in $(seq 2 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "query peer${p} key 1" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} get_key tun${p} \ + $((p + OVPN_ID_OFFSET)) 1 + ovpn_cmd_ok "query peer${p} key 2" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} get_key tun${p} \ + $((p + OVPN_ID_OFFSET)) 2 + done +} + +ovpn_run_traffic_delete_peer() { + local ping_pid + + ovpn_log "Deleting peer while sending traffic:" + + ovpn_run_bg ping_pid ip netns exec ovpn_peer2 ping -qf -w 4 5.5.5.1 + sleep 2 + ovpn_cmd_ok "delete peer0 peer 2" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} del_peer tun0 2 + + if [ "${OVPN_PROTO}" == "TCP" ]; then + # In TCP mode this command is expected to fail for both peers. + ovpn_cmd_mayfail "delete peer2 peer 2 (TCP non-fatal)" \ + ip netns exec ovpn_peer2 ${OVPN_CLI} del_peer tun2 \ + $((2 + OVPN_ID_OFFSET)) + else + ovpn_cmd_ok "delete peer2 peer 2" ip netns exec ovpn_peer2 \ + ${OVPN_CLI} del_peer tun2 $((2 + OVPN_ID_OFFSET)) + fi + + wait "${ping_pid}" || true +} + +ovpn_run_key_cleanup() { + local p + local peer_ns + + ovpn_log "Deleting keys:" + + for p in $(seq 3 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "delete key 1 for peer${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} del_key tun${p} \ + $((p + OVPN_ID_OFFSET)) 1 + ovpn_cmd_ok "delete key 2 for peer${p}" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} del_key tun${p} \ + $((p + OVPN_ID_OFFSET)) 2 + done +} + +ovpn_run_timeouts() { + local p + local peer_ns + + ovpn_log "Setting timeout to 3s MP:" + + for p in $(seq 3 ${OVPN_NUM_PEERS}); do + # Non-fatal: this may fail in some protocol modes. + ovpn_cmd_mayfail "set peer0 timeout for peer ${p} (non-fatal)" \ + ip netns exec ovpn_peer0 ${OVPN_CLI} set_peer tun0 \ + ${p} 3 3 + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "disable timeout on peer${p} while peer0 adjusts \ + state" ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + tun${p} $((p + OVPN_ID_OFFSET)) 0 0 + done + # wait for peers to timeout + sleep 5 + + ovpn_log "Setting timeout to 3s P2P:" + + for p in $(seq 3 ${OVPN_NUM_PEERS}); do + peer_ns="ovpn_peer${p}" + ovpn_cmd_ok "set peer${p} P2P timeout" \ + ip netns exec "${peer_ns}" ${OVPN_CLI} set_peer \ + tun${p} $((p + OVPN_ID_OFFSET)) 3 3 + done + sleep 5 +} + +ovpn_run_notifications() { + local p + + for p in $(seq 0 ${OVPN_NUM_PEERS}); do + ovpn_cmd_ok "validate listener output for peer ${p}" \ + ovpn_compare_ntfs "${p}" + done +} + +trap ovpn_test_exit EXIT +trap ovpn_stage_err ERR + +ktap_print_header +if [ "${OVPN_FLOAT}" == "1" ]; then + ktap_set_plan 13 +else + ktap_set_plan 12 +fi ovpn_cleanup +modprobe -q ovpn || true + +ovpn_run_stage "setup network topology" ovpn_prepare_network +ovpn_run_stage "run baseline data traffic" ovpn_run_basic_traffic +ovpn_run_stage "run LAN traffic behind peer1" ovpn_run_lan_traffic +[ "${OVPN_FLOAT}" == "1" ] && ovpn_run_stage "run floating peer checks" \ + ovpn_run_float_mode +ovpn_run_stage "run iperf throughput" ovpn_run_iperf +ovpn_run_stage "run key rollout" ovpn_run_key_rollover +ovpn_run_stage "query peers" ovpn_run_queries +ovpn_run_stage "query missing peer fails" ovpn_query_peer_missing +ovpn_run_stage "peer lifecycle and key queries" ovpn_run_peer_cleanup +ovpn_run_stage "delete peer while traffic" ovpn_run_traffic_delete_peer +ovpn_run_stage "delete stale keys" ovpn_run_key_cleanup +ovpn_run_stage "check timeout behavior" ovpn_run_timeouts +ovpn_run_stage "validate notification output" ovpn_run_notifications -modprobe -r ovpn || true +ovpn_test_finished=1 +ktap_finished -- cgit v1.2.3 From 6c9b1dc218fea8b15893953f5299b209f11fa0a8 Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Thu, 16 Apr 2026 09:19:28 +0200 Subject: selftests: ovpn: serialize YNL listener startup Starting one background YNL notification listener per peer back-to-back can intermittently stall the test setup before the listeners even reach the Python main function. This was reproducible in a reduced test.sh setup-only loop: a single listener stayed stable across repeated runs, while starting listeners for all peers could hang early in the listener launch phase. Adding a short delay between listener launches makes the listeners start cleanly and eliminates the reproduced hangs in repeated normal and slow-runner tests. Serialize listener startup with a small sleep between setup_listener calls. Fixes: 77de28cd7cf1 ("selftests: ovpn: add notification parsing and matching") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- tools/testing/selftests/net/ovpn/test.sh | 3 +++ 1 file changed, 3 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/ovpn/test.sh b/tools/testing/selftests/net/ovpn/test.sh index eca653112aeb..b50dbe45a4d0 100755 --- a/tools/testing/selftests/net/ovpn/test.sh +++ b/tools/testing/selftests/net/ovpn/test.sh @@ -31,6 +31,9 @@ ovpn_prepare_network() { for p in $(seq 0 ${OVPN_NUM_PEERS}); do ovpn_cmd_ok "start notification listener peer${p}" \ ovpn_setup_listener "${p}" + # starting all YNL listeners back-to-back can intermittently + # stall their startup so serialize launches a bit + sleep 0.5 done for p in $(seq 0 ${OVPN_NUM_PEERS}); do -- cgit v1.2.3 From ee514cdb07b330b2a9012e5373d26959fbbc4f81 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Thu, 16 Apr 2026 11:57:11 -0700 Subject: selftests: net: add team_bridge_macvlan rx_mode test Add a test that exercises the ndo_change_rx_flags path through a macvlan -> bridge -> team -> dummy stack. This triggers dev_uc_add under addr_list_lock which flips promiscuity on the lower device. With the new work queue approach, this must not deadlock. Link: https://lore.kernel.org/netdev/20260214033859.43857-1-jiayuan.chen@linux.dev/ Reviewed-by: Breno Leitao Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260416185712.2155425-15-sdf@fomichev.me Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/config | 1 + tools/testing/selftests/net/rtnetlink.sh | 44 ++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/config b/tools/testing/selftests/net/config index 2a390cae41bf..94d722770420 100644 --- a/tools/testing/selftests/net/config +++ b/tools/testing/selftests/net/config @@ -101,6 +101,7 @@ CONFIG_NET_SCH_HTB=m CONFIG_NET_SCH_INGRESS=m CONFIG_NET_SCH_NETEM=y CONFIG_NET_SCH_PRIO=m +CONFIG_NET_TEAM=y CONFIG_NET_VRF=y CONFIG_NF_CONNTRACK=m CONFIG_NF_CONNTRACK_OVS=y diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh index 5a5ff88321d5..c499953d4885 100755 --- a/tools/testing/selftests/net/rtnetlink.sh +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -23,6 +23,7 @@ ALL_TESTS=" kci_test_encap kci_test_macsec kci_test_macsec_vlan + kci_test_team_bridge_macvlan kci_test_ipsec kci_test_ipsec_offload kci_test_fdb_get @@ -636,6 +637,49 @@ kci_test_macsec_vlan() end_test "PASS: macsec_vlan" } +# Test ndo_change_rx_flags call from dev_uc_add under addr_list_lock spinlock. +# When we are flipping the promisc, make sure it runs on the work queue. +# +# https://lore.kernel.org/netdev/20260214033859.43857-1-jiayuan.chen@linux.dev/ +# With (more conventional) macvlan instead of macsec. +# macvlan -> bridge -> team -> dummy +kci_test_team_bridge_macvlan() +{ + local vlan="test_macv1" + local bridge="test_br1" + local team="test_team1" + local dummy="test_dummy1" + local ret=0 + + run_cmd ip link add $team type team + if [ $ret -ne 0 ]; then + end_test "SKIP: team_bridge_macvlan: can't add team interface" + return $ksft_skip + fi + + run_cmd ip link add $dummy type dummy + run_cmd ip link set $dummy master $team + run_cmd ip link set $team up + run_cmd ip link add $bridge type bridge vlan_filtering 1 + run_cmd ip link set $bridge up + run_cmd ip link set $team master $bridge + run_cmd ip link add link $bridge name $vlan \ + address 00:aa:bb:cc:dd:ee type macvlan mode bridge + run_cmd ip link set $vlan up + + run_cmd ip link del $vlan + run_cmd ip link del $bridge + run_cmd ip link del $team + run_cmd ip link del $dummy + + if [ $ret -ne 0 ]; then + end_test "FAIL: team_bridge_macvlan" + return 1 + fi + + end_test "PASS: team_bridge_macvlan" +} + #------------------------------------------------------------------- # Example commands # ip x s add proto esp src 14.0.0.52 dst 14.0.0.70 \ -- cgit v1.2.3 From c4dde411bc366f568dbe33366253bbfea049e8ea Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Thu, 16 Apr 2026 11:57:12 -0700 Subject: selftests: net: use ip commands instead of teamd in team rx_mode test Replace teamd daemon usage with ip link commands for team device setup. teamd -d daemonizes and returns to the shell before port addition completes, creating a race: the test may create the macvlan (and check for its address on a slave) before teamd has finished adding ports. This makes the test inherently dependent on scheduling timing. Using ip commands makes port addition synchronous, removing the race and making the test deterministic. Cc: Jiri Pirko Cc: Jay Vosburgh Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260416185712.2155425-16-sdf@fomichev.me Signed-off-by: Paolo Abeni --- tools/testing/selftests/drivers/net/bonding/lag_lib.sh | 17 +++-------------- .../selftests/drivers/net/team/dev_addr_lists.sh | 2 -- 2 files changed, 3 insertions(+), 16 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/drivers/net/bonding/lag_lib.sh b/tools/testing/selftests/drivers/net/bonding/lag_lib.sh index bf9bcd1b5ec0..f2e43b6c4c81 100644 --- a/tools/testing/selftests/drivers/net/bonding/lag_lib.sh +++ b/tools/testing/selftests/drivers/net/bonding/lag_lib.sh @@ -23,20 +23,9 @@ test_LAG_cleanup() ip link set dev dummy2 master "$name" elif [ "$driver" = "team" ]; then name="team0" - teamd -d -c ' - { - "device": "'"$name"'", - "runner": { - "name": "'"$mode"'" - }, - "ports": { - "dummy1": - {}, - "dummy2": - {} - } - } - ' + ip link add "$name" type team + ip link set dev dummy1 master "$name" + ip link set dev dummy2 master "$name" ip link set dev "$name" up else check_err 1 diff --git a/tools/testing/selftests/drivers/net/team/dev_addr_lists.sh b/tools/testing/selftests/drivers/net/team/dev_addr_lists.sh index b1ec7755b783..26469f3be022 100755 --- a/tools/testing/selftests/drivers/net/team/dev_addr_lists.sh +++ b/tools/testing/selftests/drivers/net/team/dev_addr_lists.sh @@ -42,8 +42,6 @@ team_cleanup() } -require_command teamd - trap cleanup EXIT tests_run -- cgit v1.2.3 From d0576eb8508e68b950ee9d2820116a6fc205fd07 Mon Sep 17 00:00:00 2001 From: Gang Yan Date: Mon, 20 Apr 2026 18:19:24 +0200 Subject: selftests: mptcp: add a check for sndbuf of S/C Add a new chk_sndbuf() helper to diag.sh that extracts the sndbuf (the 'tb' field from 'ss -m' skmem output) for both server and client MPTCP sockets, and verifies they are equal. Without the previous patch, it will fail: ''' 07 ....chk sndbuf server/client [FAIL] sndbuf S=20480 != C=2630656 ''' Signed-off-by: Gang Yan Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260420-net-mptcp-sync-sndbuf-accept-v1-2-e3523e3aeb44@kernel.org Signed-off-by: Paolo Abeni --- tools/testing/selftests/net/mptcp/diag.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/mptcp/diag.sh b/tools/testing/selftests/net/mptcp/diag.sh index d847ff1737c3..27cbda68144e 100755 --- a/tools/testing/selftests/net/mptcp/diag.sh +++ b/tools/testing/selftests/net/mptcp/diag.sh @@ -322,6 +322,33 @@ wait_connected() done } +chk_sndbuf() +{ + local server_sndbuf client_sndbuf msg + local port=${1} + + msg="....chk sndbuf server/client" + server_sndbuf=$(ss -N "${ns}" -inmHM "sport" "${port}" | \ + sed -n 's/.*tb\([0-9]\+\).*/\1/p') + client_sndbuf=$(ss -N "${ns}" -inmHM "dport" "${port}" | \ + sed -n 's/.*tb\([0-9]\+\).*/\1/p') + + mptcp_lib_print_title "${msg}" + if [ -z "${server_sndbuf}" ] || [ -z "${client_sndbuf}" ]; then + mptcp_lib_pr_fail "sndbuf S=${server_sndbuf} C=${client_sndbuf}" + mptcp_lib_result_fail "${msg}" + ret=${KSFT_FAIL} + elif [ "${server_sndbuf}" != "${client_sndbuf}" ]; then + mptcp_lib_pr_fail "sndbuf S=${server_sndbuf} != C=${client_sndbuf}" + mptcp_lib_result_fail "${msg}" + ret=${KSFT_FAIL} + else + mptcp_lib_pr_ok + mptcp_lib_result_pass "${msg}" + fi +} + + trap cleanup EXIT mptcp_lib_ns_init ns @@ -341,6 +368,7 @@ echo "b" | \ 127.0.0.1 >/dev/null & wait_connected $ns 10000 chk_msk_nr 2 "after MPC handshake" +chk_sndbuf 10000 chk_last_time_info 10000 chk_msk_remote_key_nr 2 "....chk remote_key" chk_msk_fallback_nr 0 "....chk no fallback" -- cgit v1.2.3 From 42726ec644cbdde0035c3e0417fee8ed9547e120 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Wed, 22 Apr 2026 20:35:38 +0800 Subject: tcp: send a challenge ACK on SEG.ACK > SND.NXT RFC 5961 Section 5.2 validates an incoming segment's ACK value against the range [SND.UNA - MAX.SND.WND, SND.NXT] and states: "All incoming segments whose ACK value doesn't satisfy the above condition MUST be discarded and an ACK sent back." Commit 354e4aa391ed ("tcp: RFC 5961 5.2 Blind Data Injection Attack Mitigation") opted Linux into this mitigation and implements the challenge ACK on the lower side (SEG.ACK < SND.UNA - MAX.SND.WND), but the symmetric upper side (SEG.ACK > SND.NXT) still takes the pre-RFC-5961 path and silently returns SKB_DROP_REASON_TCP_ACK_UNSENT_DATA, even though RFC 793 Section 3.9 (now RFC 9293 Section 3.10.7.4) has always required: "If the ACK acknowledges something not yet sent (SEG.ACK > SND.NXT) then send an ACK, drop the segment, and return." Complete the mitigation by sending a challenge ACK on that branch, reusing the existing tcp_send_challenge_ack() path which already enforces the per-socket RFC 5961 Section 7 rate limit via __tcp_oow_rate_limited(). FLAG_NO_CHALLENGE_ACK is honoured for symmetry with the lower-edge case. Update the existing tcp_ts_recent_invalid_ack.pkt selftest, which drives this exact path, to consume the new challenge ACK. Fixes: 354e4aa391ed ("tcp: RFC 5961 5.2 Blind Data Injection Attack Mitigation") Signed-off-by: Jiayuan Chen Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260422123605.320000-2-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_input.c | 10 +++++++--- .../selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'tools/testing') diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index e04ae105893c..d5c9e65d9760 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4286,11 +4286,15 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) goto old_ack; } - /* If the ack includes data we haven't sent yet, discard - * this segment (RFC793 Section 3.9). + /* If the ack includes data we haven't sent yet, drop the + * segment. RFC 793 Section 3.9 and RFC 5961 Section 5.2 + * require us to send an ACK back in that case. */ - if (after(ack, tp->snd_nxt)) + if (after(ack, tp->snd_nxt)) { + if (!(flag & FLAG_NO_CHALLENGE_ACK)) + tcp_send_challenge_ack(sk, false); return -SKB_DROP_REASON_TCP_ACK_UNSENT_DATA; + } if (after(ack, prior_snd_una)) { flag |= FLAG_SND_UNA_ADVANCED; diff --git a/tools/testing/selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt b/tools/testing/selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt index 174ce9a1bfc0..ee6baf7c36cf 100644 --- a/tools/testing/selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt +++ b/tools/testing/selftests/net/packetdrill/tcp_ts_recent_invalid_ack.pkt @@ -19,7 +19,9 @@ // bad packet with high tsval (its ACK sequence is above our sndnxt) +0 < F. 1:1(0) ack 9999 win 20000 - +// Challenge ACK for SEG.ACK > SND.NXT (RFC 5961 5.2 / RFC 793 3.9). +// ecr=200 (not 200000) proves ts_recent was not updated from the bad packet. + +0 > . 1:1(0) ack 1 +0 < . 1:1001(1000) ack 1 win 20000 +0 > . 1:1(0) ack 1001 -- cgit v1.2.3 From cf94b3c0f052c2674328b330309604af2dedd3a0 Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Wed, 22 Apr 2026 20:35:39 +0800 Subject: selftests/net: packetdrill: cover RFC 5961 5.2 challenge ACK on both edges RFC 5961 Section 5.2 / RFC 793 Section 3.9 require a challenge ACK whenever an incoming SEG.ACK falls outside [SND.UNA - MAX.SND.WND, SND.NXT]. There is currently no packetdrill coverage for either edge. Add tcp_rfc5961_ack-out-of-window.pkt, which in a single passive-open connection exercises: - Upper edge (SEG.ACK > SND.NXT): peer ACKs data that was never sent before the server has transmitted anything. - Lower edge (SEG.ACK < SND.UNA - MAX.SND.WND): after the server has sent 2000 bytes (the peer-advertised rwnd forces two 1000-byte segments, both acknowledged), peer sends an ACK that is older than the acceptable window. Both cases must elicit a challenge ACK . The per-socket RFC 5961 Section 7 rate limit is disabled for the duration of the test so that both challenge ACKs can fire back-to-back. Signed-off-by: Jiayuan Chen Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260422123605.320000-3-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- .../packetdrill/tcp_rfc5961_ack-out-of-window.pkt | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-out-of-window.pkt (limited to 'tools/testing') diff --git a/tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-out-of-window.pkt b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-out-of-window.pkt new file mode 100644 index 000000000000..2776b8728085 --- /dev/null +++ b/tools/testing/selftests/net/packetdrill/tcp_rfc5961_ack-out-of-window.pkt @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// RFC 5961 Section 5.2 / RFC 793 Section 3.9: an incoming segment's +// ACK value must lie in [SND.UNA - MAX.SND.WND, SND.NXT]; otherwise +// the receiver MUST discard the segment and send a challenge ACK +// back. Exercise both edges of that window in a single connection. + +`./defaults.sh +sysctl -q net.ipv4.tcp_invalid_ratelimit=0 +` + + 0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3 + +0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 + +0 bind(3, ..., ...) = 0 + +0 listen(3, 1) = 0 + +// Three-way handshake. Peer advertises rwnd = 1000 (no wscale), so +// MAX.SND.WND is tracked as 1000. + +0 < S 0:0(0) win 1000 + +0 > S. 0:0(0) ack 1 <...> ++.1 < . 1:1(0) ack 1 win 1000 + +0 accept(3, ..., ...) = 4 + +// ---- Upper edge: SEG.ACK > SND.NXT -------------------------------- +// Server has sent nothing yet, so SND.UNA = SND.NXT = 1. +// Peer sends a pure ACK with SEG.ACK = 2, beyond SND.NXT. + +0 < . 1:1(0) ack 2 win 1000 +// Expect a challenge ACK: . + +0 > . 1:1(0) ack 1 + +// Advance SND.UNA past MAX.SND.WND so that the lower edge becomes +// reachable. Issue two 1-MSS writes so each skb is exactly one MSS +// and PSH is set by tcp_push() at the end of each sendmsg, keeping +// the setup independent of the TSO / tcp_fragment split path. + +0 write(4, ..., 1000) = 1000 + +0 > P. 1:1001(1000) ack 1 ++.01 < . 1:1(0) ack 1001 win 1000 + +0 write(4, ..., 1000) = 1000 + +0 > P. 1001:2001(1000) ack 1 ++.01 < . 1:1(0) ack 2001 win 1000 +// Now SND.UNA = SND.NXT = 2001, MAX.SND.WND = 1000, bytes_acked = 2000. + +// ---- Lower edge: SEG.ACK < SND.UNA - MAX.SND.WND ------------------ +// SND.UNA - MAX.SND.WND = 2001 - 1000 = 1001, so SEG.ACK = 1000 falls +// below the acceptable range. + +0 < . 1:1(0) ack 1000 win 1000 +// Expect a challenge ACK: . + +0 > . 2001:2001(0) ack 1 -- cgit v1.2.3 From c01cfc4886752c86f6d48c58f7b103a01b0caeca Mon Sep 17 00:00:00 2001 From: Zhenzhong Wu Date: Wed, 22 Apr 2026 10:45:54 +0800 Subject: selftests/bpf: check epoll readiness during reuseport migration Inside migrate_dance(), add epoll checks around shutdown() to verify that the target listener is not ready before shutdown() and becomes ready immediately after shutdown() triggers migration. Cover TCP_ESTABLISHED and TCP_SYN_RECV. Exclude TCP_NEW_SYN_RECV as it depends on later handshake completion. Suggested-by: Kuniyuki Iwashima Reviewed-by: Kuniyuki Iwashima Signed-off-by: Zhenzhong Wu Link: https://patch.msgid.link/20260422024554.130346-3-jt26wzz@gmail.com Signed-off-by: Jakub Kicinski --- .../selftests/bpf/prog_tests/migrate_reuseport.c | 49 ++++++++++++++++++---- 1 file changed, 42 insertions(+), 7 deletions(-) (limited to 'tools/testing') diff --git a/tools/testing/selftests/bpf/prog_tests/migrate_reuseport.c b/tools/testing/selftests/bpf/prog_tests/migrate_reuseport.c index 653b0a20fab9..c62907732c19 100644 --- a/tools/testing/selftests/bpf/prog_tests/migrate_reuseport.c +++ b/tools/testing/selftests/bpf/prog_tests/migrate_reuseport.c @@ -7,24 +7,29 @@ * 3. call listen() for 1 server socket. (migration target) * 4. update a map to migrate all child sockets * to the last server socket (migrate_map[cookie] = 4) - * 5. call shutdown() for first 4 server sockets + * 5. for TCP_ESTABLISHED and TCP_SYN_RECV cases, verify via epoll + * that the last server socket is not ready before migration. + * 6. call shutdown() for first 4 server sockets * and migrate the requests in the accept queue * to the last server socket. - * 6. call listen() for the second server socket. - * 7. call shutdown() for the last server + * 7. for TCP_ESTABLISHED and TCP_SYN_RECV cases, verify via epoll + * that the last server socket is ready after migration. + * 8. call listen() for the second server socket. + * 9. call shutdown() for the last server * and migrate the requests in the accept queue * to the second server socket. - * 8. call listen() for the last server. - * 9. call shutdown() for the second server + * 10. call listen() for the last server. + * 11. call shutdown() for the second server * and migrate the requests in the accept queue * to the last server socket. - * 10. call accept() for the last server socket. + * 12. call accept() for the last server socket. * * Author: Kuniyuki Iwashima */ #include #include +#include #include "test_progs.h" #include "test_migrate_reuseport.skel.h" @@ -350,21 +355,51 @@ static int update_maps(struct migrate_reuseport_test_case *test_case, static int migrate_dance(struct migrate_reuseport_test_case *test_case) { + struct epoll_event ev = { + .events = EPOLLIN, + }; + int epoll = -1, nfds; int i, err; + if (test_case->state != BPF_TCP_NEW_SYN_RECV) { + epoll = epoll_create1(0); + if (!ASSERT_NEQ(epoll, -1, "epoll_create1")) + return -1; + + ev.data.fd = test_case->servers[MIGRATED_TO]; + if (!ASSERT_OK(epoll_ctl(epoll, EPOLL_CTL_ADD, + test_case->servers[MIGRATED_TO], &ev), + "epoll_ctl")) + goto close_epoll; + + nfds = epoll_wait(epoll, &ev, 1, 0); + if (!ASSERT_EQ(nfds, 0, "epoll_wait 1")) + goto close_epoll; + } + /* Migrate TCP_ESTABLISHED and TCP_SYN_RECV requests * to the last listener based on eBPF. */ for (i = 0; i < MIGRATED_TO; i++) { err = shutdown(test_case->servers[i], SHUT_RDWR); if (!ASSERT_OK(err, "shutdown")) - return -1; + goto close_epoll; } /* No dance for TCP_NEW_SYN_RECV to migrate based on eBPF */ if (test_case->state == BPF_TCP_NEW_SYN_RECV) return 0; + nfds = epoll_wait(epoll, &ev, 1, 0); + if (!ASSERT_EQ(nfds, 1, "epoll_wait 2")) { +close_epoll: + if (epoll >= 0) + close(epoll); + return -1; + } + + close(epoll); + /* Note that we use the second listener instead of the * first one here. * -- cgit v1.2.3