summaryrefslogtreecommitdiff
path: root/tools/testing
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2026-03-19 14:50:42 +0300
committerPaolo Abeni <pabeni@redhat.com>2026-03-19 14:50:42 +0300
commit0c45064487a10ee02b8bc528aa0664681c245243 (patch)
tree542582ea54a49430abc0bc3f4bff810fd6920bb9 /tools/testing
parent9f4960b94f1a044f76da98a765d6cbd294c22c92 (diff)
parentd3244af9c4c2bbce57465130c9cd509182207c2d (diff)
downloadlinux-0c45064487a10ee02b8bc528aa0664681c245243.tar.xz
Merge tag 'ovpn-net-next-20260317' of https://github.com/OpenVPN/ovpn-net-next
Antonio Quartulli says: ==================== Included features: * use bitops.h API when possible * send netlink notification in case of client float event * implement support for asymmetric peer IDs * consolidate memory allocations during crypto operations * add netlink notification check in selftests * add FW mark check in selftest * tag 'ovpn-net-next-20260317' of https://github.com/OpenVPN/ovpn-net-next: ovpn: consolidate crypto allocations in one chunk selftests: ovpn: add test for the FW mark feature selftests: ovpn: check asymmetric peer-id ovpn: add support for asymmetric peer IDs selftests: ovpn: add notification parsing and matching ovpn: notify userspace on client float event ovpn: pktid: use bitops.h API ovpn: use correct array size to parse nested attributes in ovpn_nl_key_swap_doit selftests: ovpn: allow compiling ovpn-cli.c with mbedtls3 ==================== Link: https://patch.msgid.link/20260317104023.192548-1-antonio@openvpn.net Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'tools/testing')
-rw-r--r--tools/testing/selftests/net/ovpn/Makefile29
-rw-r--r--tools/testing/selftests/net/ovpn/common.sh101
-rw-r--r--tools/testing/selftests/net/ovpn/data64.key6
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer0-float.json9
l---------tools/testing/selftests/net/ovpn/json/peer0-symm-float.json1
l---------tools/testing/selftests/net/ovpn/json/peer0-symm.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer0.json6
l---------tools/testing/selftests/net/ovpn/json/peer1-float.json1
l---------tools/testing/selftests/net/ovpn/json/peer1-symm-float.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer1-symm.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer1.json1
l---------tools/testing/selftests/net/ovpn/json/peer2-float.json1
l---------tools/testing/selftests/net/ovpn/json/peer2-symm-float.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer2-symm.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer2.json1
l---------tools/testing/selftests/net/ovpn/json/peer3-float.json1
l---------tools/testing/selftests/net/ovpn/json/peer3-symm-float.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer3-symm.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer3.json1
l---------tools/testing/selftests/net/ovpn/json/peer4-float.json1
l---------tools/testing/selftests/net/ovpn/json/peer4-symm-float.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer4-symm.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer4.json1
l---------tools/testing/selftests/net/ovpn/json/peer5-float.json1
l---------tools/testing/selftests/net/ovpn/json/peer5-symm-float.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer5-symm.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer5.json1
l---------tools/testing/selftests/net/ovpn/json/peer6-float.json1
l---------tools/testing/selftests/net/ovpn/json/peer6-symm-float.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer6-symm.json1
-rw-r--r--tools/testing/selftests/net/ovpn/json/peer6.json1
-rw-r--r--tools/testing/selftests/net/ovpn/ovpn-cli.c152
-rw-r--r--tools/testing/selftests/net/ovpn/tcp_peers.txt11
-rwxr-xr-xtools/testing/selftests/net/ovpn/test-close-socket.sh2
-rwxr-xr-xtools/testing/selftests/net/ovpn/test-mark.sh96
-rwxr-xr-xtools/testing/selftests/net/ovpn/test-symmetric-id-float.sh11
-rwxr-xr-xtools/testing/selftests/net/ovpn/test-symmetric-id-tcp.sh11
-rwxr-xr-xtools/testing/selftests/net/ovpn/test-symmetric-id.sh10
-rwxr-xr-xtools/testing/selftests/net/ovpn/test.sh76
-rw-r--r--tools/testing/selftests/net/ovpn/udp_peers.txt12
40 files changed, 472 insertions, 86 deletions
diff --git a/tools/testing/selftests/net/ovpn/Makefile b/tools/testing/selftests/net/ovpn/Makefile
index dbe0388c8512..169f0464ac3a 100644
--- a/tools/testing/selftests/net/ovpn/Makefile
+++ b/tools/testing/selftests/net/ovpn/Makefile
@@ -2,22 +2,35 @@
# Copyright (C) 2020-2025 OpenVPN, Inc.
#
CFLAGS = -pedantic -Wextra -Wall -Wl,--no-as-needed -g -O0 -ggdb $(KHDR_INCLUDES)
+CFLAGS += $(shell pkg-config --cflags mbedcrypto-3 mbedtls-3 2>/dev/null)
+
VAR_CFLAGS = $(shell pkg-config --cflags libnl-3.0 libnl-genl-3.0 2>/dev/null)
ifeq ($(VAR_CFLAGS),)
VAR_CFLAGS = -I/usr/include/libnl3
endif
CFLAGS += $(VAR_CFLAGS)
+MTLS_LDLIBS= $(shell pkg-config --libs mbedcrypto-3 mbedtls-3 2>/dev/null)
+ifeq ($(MTLS_LDLIBS),)
+MTLS_LDLIBS = -lmbedtls -lmbedcrypto
+endif
+LDLIBS += $(MTLS_LDLIBS)
-LDLIBS = -lmbedtls -lmbedcrypto
-VAR_LDLIBS = $(shell pkg-config --libs libnl-3.0 libnl-genl-3.0 2>/dev/null)
-ifeq ($(VAR_LDLIBS),)
-VAR_LDLIBS = -lnl-genl-3 -lnl-3
+NL_LDLIBS = $(shell pkg-config --libs libnl-3.0 libnl-genl-3.0 2>/dev/null)
+ifeq ($(NL_LDLIBS),)
+NL_LDLIBS = -lnl-genl-3 -lnl-3
endif
-LDLIBS += $(VAR_LDLIBS)
+LDLIBS += $(NL_LDLIBS)
-TEST_FILES = common.sh
+TEST_FILES = \
+ common.sh \
+ data64.key \
+ json \
+ tcp_peers.txt \
+ udp_peers.txt \
+ ../../../../net/ynl/pyynl/cli.py \
+# end of TEST_FILES
TEST_PROGS := \
test-chachapoly.sh \
@@ -25,6 +38,10 @@ TEST_PROGS := \
test-close-socket.sh \
test-float.sh \
test-large-mtu.sh \
+ test-mark.sh \
+ test-symmetric-id-float.sh \
+ test-symmetric-id-tcp.sh \
+ test-symmetric-id.sh \
test-tcp.sh \
test.sh \
# end of TEST_PROGS
diff --git a/tools/testing/selftests/net/ovpn/common.sh b/tools/testing/selftests/net/ovpn/common.sh
index 88869c675d03..4c08f756e63a 100644
--- a/tools/testing/selftests/net/ovpn/common.sh
+++ b/tools/testing/selftests/net/ovpn/common.sh
@@ -7,12 +7,21 @@
UDP_PEERS_FILE=${UDP_PEERS_FILE:-udp_peers.txt}
TCP_PEERS_FILE=${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}
+export ID_OFFSET=$(( 9 * (SYMMETRIC_ID == 0) ))
+
+JQ_FILTER='map(select(.msg.peer | has("remote-ipv6") | not)) |
+ map(del(.msg.ifindex)) | sort_by(.msg.peer.id)[]'
LAN_IP="11.11.11.11"
+declare -A tmp_jsons=()
+declare -A listener_pids=()
+
create_ns() {
ip netns add peer${1}
}
@@ -48,27 +57,67 @@ setup_ns() {
ip -n peer${1} link set tun${1} up
}
+build_capture_filter() {
+ # match the first four bytes of the openvpn data payload
+ if [ "${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
+ # headers in the outer packet.
+ if [[ "${2}" == *:* ]]; then
+ printf "ip6 and ip6[6] = 17 and ip6[48:4] = %s" "${1}"
+ else
+ printf "ip and udp[8:4] = %s" "${1}"
+ fi
+ else
+ # openvpn over TCP prepends a 2-byte packet length ahead of the
+ # DATA_V2 opcode, so skip it before matching the payload header
+ printf "ip and tcp[(((tcp[12] & 0xf0) >> 2) + 2):4] = %s" "${1}"
+ fi
+}
+
+setup_listener() {
+ 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}"
+}
+
add_peer() {
+ labels=("ASYMM" "SYMM")
+ M_ID=${labels[SYMMETRIC_ID]}
+
if [ "${PROTO}" == "UDP" ]; then
if [ ${1} -eq 0 ]; then
- ip netns exec peer0 ${OVPN_CLI} new_multi_peer tun0 1 ${UDP_PEERS_FILE}
+ ip netns exec peer0 ${OVPN_CLI} new_multi_peer tun0 1 \
+ ${M_ID} ${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 \
data64.key
done
else
- RADDR=$(awk "NR == ${1} {print \$2}" ${UDP_PEERS_FILE})
- RPORT=$(awk "NR == ${1} {print \$3}" ${UDP_PEERS_FILE})
- LPORT=$(awk "NR == ${1} {print \$5}" ${UDP_PEERS_FILE})
- ip netns exec peer${1} ${OVPN_CLI} new_peer tun${1} ${1} ${LPORT} \
- ${RADDR} ${RPORT}
- ip netns exec peer${1} ${OVPN_CLI} new_key tun${1} ${1} 1 0 ${ALG} 1 \
- data64.key
+ if [ "${SYMMETRIC_ID}" -eq 1 ]; then
+ PEER_ID=${1}
+ TX_ID="none"
+ else
+ PEER_ID=$(awk "NR == ${1} {print \$2}" \
+ ${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
fi
else
if [ ${1} -eq 0 ]; then
- (ip netns exec peer0 ${OVPN_CLI} listen tun0 1 ${TCP_PEERS_FILE} && {
+ (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
@@ -76,9 +125,37 @@ add_peer() {
}) &
sleep 5
else
- ip netns exec peer${1} ${OVPN_CLI} connect tun${1} ${1} 10.10.${1}.1 1 \
- data64.key
+ if [ "${SYMMETRIC_ID}" -eq 1 ]; then
+ PEER_ID=${1}
+ TX_ID="none"
+ else
+ PEER_ID=$(awk "NR == ${1} {print \$2}" \
+ ${TCP_PEERS_FILE})
+ TX_ID=${1}
+ fi
+ ip netns exec peer${1} ${OVPN_CLI} connect tun${1} \
+ ${PEER_ID} ${TX_ID} 10.10.${1}.1 1 data64.key
+ fi
+ fi
+}
+
+compare_ntfs() {
+ 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]}"
+
+ 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
+ echo "OK"
fi
+
+ rm -f ${received} || true
fi
}
@@ -104,5 +181,3 @@ if [ "${PROTO}" == "UDP" ]; then
else
NUM_PEERS=${NUM_PEERS:-$(wc -l ${TCP_PEERS_FILE} | awk '{print $1}')}
fi
-
-
diff --git a/tools/testing/selftests/net/ovpn/data64.key b/tools/testing/selftests/net/ovpn/data64.key
index a99e88c4e290..d04febcdf5a2 100644
--- a/tools/testing/selftests/net/ovpn/data64.key
+++ b/tools/testing/selftests/net/ovpn/data64.key
@@ -1,5 +1 @@
-jRqMACN7d7/aFQNT8S7jkrBD8uwrgHbG5OQZP2eu4R1Y7tfpS2bf5RHv06Vi163CGoaIiTX99R3B
-ia9ycAH8Wz1+9PWv51dnBLur9jbShlgZ2QHLtUc4a/gfT7zZwULXuuxdLnvR21DDeMBaTbkgbai9
-uvAa7ne1liIgGFzbv+Bas4HDVrygxIxuAnP5Qgc3648IJkZ0QEXPF+O9f0n5+QIvGCxkAUVx+5K6
-KIs+SoeWXnAopELmoGSjUpFtJbagXK82HfdqpuUxT2Tnuef0/14SzVE/vNleBNu2ZbyrSAaah8tE
-BofkPJUBFY+YQcfZNM5Dgrw3i+Bpmpq/gpdg5w==
+jRqMACN7d7/aFQNT8S7jkrBD8uwrgHbG5OQZP2eu4R1Y7tfpS2bf5RHv06Vi163CGoaIiTX99R3Bia9ycAH8Wz1+9PWv51dnBLur9jbShlgZ2QHLtUc4a/gfT7zZwULXuuxdLnvR21DDeMBaTbkgbai9uvAa7ne1liIgGFzbv+Bas4HDVrygxIxuAnP5Qgc3648IJkZ0QEXPF+O9f0n5+QIvGCxkAUVx+5K6KIs+SoeWXnAopELmoGSjUpFtJbagXK82HfdqpuUxT2Tnuef0/14SzVE/vNleBNu2ZbyrSAaah8tEBofkPJUBFY+YQcfZNM5Dgrw3i+Bpmpq/gpdg5w==
diff --git a/tools/testing/selftests/net/ovpn/json/peer0-float.json b/tools/testing/selftests/net/ovpn/json/peer0-float.json
new file mode 100644
index 000000000000..682fa58ad4ea
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer0-float.json
@@ -0,0 +1,9 @@
+{"name": "peer-float-ntf", "msg": {"ifindex": 0, "peer": {"id": 1, "remote-ipv4": "10.10.1.3", "remote-port": 1}}}
+{"name": "peer-float-ntf", "msg": {"ifindex": 0, "peer": {"id": 2, "remote-ipv4": "10.10.2.3", "remote-port": 1}}}
+{"name": "peer-float-ntf", "msg": {"ifindex": 0, "peer": {"id": 3, "remote-ipv4": "10.10.3.3", "remote-port": 1}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "userspace", "id": 1}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "userspace", "id": 2}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 3}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 4}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 5}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 6}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer0-symm-float.json b/tools/testing/selftests/net/ovpn/json/peer0-symm-float.json
new file mode 120000
index 000000000000..e31a5bd59863
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer0-symm-float.json
@@ -0,0 +1 @@
+peer0-float.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer0-symm.json b/tools/testing/selftests/net/ovpn/json/peer0-symm.json
new file mode 120000
index 000000000000..57a163048eed
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer0-symm.json
@@ -0,0 +1 @@
+peer0.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer0.json b/tools/testing/selftests/net/ovpn/json/peer0.json
new file mode 100644
index 000000000000..7c46a33d5ecd
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer0.json
@@ -0,0 +1,6 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "userspace", "id": 1}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "userspace", "id": 2}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 3}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 4}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 5}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 6}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer1-float.json b/tools/testing/selftests/net/ovpn/json/peer1-float.json
new file mode 120000
index 000000000000..d28c328d1452
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer1-float.json
@@ -0,0 +1 @@
+peer1.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer1-symm-float.json b/tools/testing/selftests/net/ovpn/json/peer1-symm-float.json
new file mode 120000
index 000000000000..b3615dcc523d
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer1-symm-float.json
@@ -0,0 +1 @@
+peer1-symm.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer1-symm.json b/tools/testing/selftests/net/ovpn/json/peer1-symm.json
new file mode 100644
index 000000000000..5da4ea9d51fb
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer1-symm.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "userspace", "id": 1}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer1.json b/tools/testing/selftests/net/ovpn/json/peer1.json
new file mode 100644
index 000000000000..1009d26dc14a
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer1.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "userspace", "id": 10}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer2-float.json b/tools/testing/selftests/net/ovpn/json/peer2-float.json
new file mode 120000
index 000000000000..b9f09980aaa0
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer2-float.json
@@ -0,0 +1 @@
+peer2.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer2-symm-float.json b/tools/testing/selftests/net/ovpn/json/peer2-symm-float.json
new file mode 120000
index 000000000000..28a895cb5170
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer2-symm-float.json
@@ -0,0 +1 @@
+peer2-symm.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer2-symm.json b/tools/testing/selftests/net/ovpn/json/peer2-symm.json
new file mode 100644
index 000000000000..8f6db4f8c2ac
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer2-symm.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "userspace", "id": 2}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer2.json b/tools/testing/selftests/net/ovpn/json/peer2.json
new file mode 100644
index 000000000000..44e9fad2b622
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer2.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "userspace", "id": 11}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer3-float.json b/tools/testing/selftests/net/ovpn/json/peer3-float.json
new file mode 120000
index 000000000000..2700b55bcf2e
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer3-float.json
@@ -0,0 +1 @@
+peer3.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer3-symm-float.json b/tools/testing/selftests/net/ovpn/json/peer3-symm-float.json
new file mode 120000
index 000000000000..ee8b9719c2fd
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer3-symm-float.json
@@ -0,0 +1 @@
+peer3-symm.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer3-symm.json b/tools/testing/selftests/net/ovpn/json/peer3-symm.json
new file mode 100644
index 000000000000..bdabd6fa2e64
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer3-symm.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 3}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer3.json b/tools/testing/selftests/net/ovpn/json/peer3.json
new file mode 100644
index 000000000000..d4be8ba130ae
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer3.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 12}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer4-float.json b/tools/testing/selftests/net/ovpn/json/peer4-float.json
new file mode 120000
index 000000000000..460f6c14cd60
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer4-float.json
@@ -0,0 +1 @@
+peer4.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer4-symm-float.json b/tools/testing/selftests/net/ovpn/json/peer4-symm-float.json
new file mode 120000
index 000000000000..7d34ff7305da
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer4-symm-float.json
@@ -0,0 +1 @@
+peer4-symm.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer4-symm.json b/tools/testing/selftests/net/ovpn/json/peer4-symm.json
new file mode 100644
index 000000000000..c3734bb9251b
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer4-symm.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 4}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer4.json b/tools/testing/selftests/net/ovpn/json/peer4.json
new file mode 100644
index 000000000000..67d27e2d48ac
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer4.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 13}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer5-float.json b/tools/testing/selftests/net/ovpn/json/peer5-float.json
new file mode 120000
index 000000000000..0f725c50ce19
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer5-float.json
@@ -0,0 +1 @@
+peer5.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer5-symm-float.json b/tools/testing/selftests/net/ovpn/json/peer5-symm-float.json
new file mode 120000
index 000000000000..afc0f5f9f13b
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer5-symm-float.json
@@ -0,0 +1 @@
+peer5-symm.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer5-symm.json b/tools/testing/selftests/net/ovpn/json/peer5-symm.json
new file mode 100644
index 000000000000..46c4a348299d
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer5-symm.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 5}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer5.json b/tools/testing/selftests/net/ovpn/json/peer5.json
new file mode 100644
index 000000000000..ecd9bd0b2f37
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer5.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 14}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer6-float.json b/tools/testing/selftests/net/ovpn/json/peer6-float.json
new file mode 120000
index 000000000000..4d9ded3e0a84
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer6-float.json
@@ -0,0 +1 @@
+peer6.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer6-symm-float.json b/tools/testing/selftests/net/ovpn/json/peer6-symm-float.json
new file mode 120000
index 000000000000..e39203204d8c
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer6-symm-float.json
@@ -0,0 +1 @@
+peer6-symm.json \ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/json/peer6-symm.json b/tools/testing/selftests/net/ovpn/json/peer6-symm.json
new file mode 100644
index 000000000000..aa30f2cff625
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer6-symm.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 6}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer6.json b/tools/testing/selftests/net/ovpn/json/peer6.json
new file mode 100644
index 000000000000..7fded29c5804
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/json/peer6.json
@@ -0,0 +1 @@
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 15}}}
diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c
index 0f3babf19fd0..d40953375c86 100644
--- a/tools/testing/selftests/net/ovpn/ovpn-cli.c
+++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c
@@ -6,6 +6,7 @@
* Author: Antonio Quartulli <antonio@openvpn.net>
*/
+#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
@@ -103,7 +104,7 @@ struct ovpn_ctx {
sa_family_t sa_family;
- unsigned long peer_id;
+ unsigned long peer_id, tx_id;
unsigned long lport;
union {
@@ -133,6 +134,9 @@ struct ovpn_ctx {
enum ovpn_key_slot key_slot;
int key_id;
+ uint32_t mark;
+ bool asymm_id;
+
const char *peers_file;
};
@@ -521,6 +525,15 @@ static int ovpn_socket(struct ovpn_ctx *ctx, sa_family_t family, int proto)
return ret;
}
+ if (ctx->mark != 0) {
+ ret = setsockopt(s, SOL_SOCKET, SO_MARK, (void *)&ctx->mark,
+ sizeof(ctx->mark));
+ if (ret < 0) {
+ perror("setsockopt for SO_MARK");
+ return ret;
+ }
+ }
+
if (family == AF_INET6) {
opt = 0;
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &opt,
@@ -649,6 +662,8 @@ static int ovpn_new_peer(struct ovpn_ctx *ovpn, bool is_tcp)
attr = nla_nest_start(ctx->nl_msg, OVPN_A_PEER);
NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_ID, ovpn->peer_id);
+ if (ovpn->asymm_id)
+ NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_TX_ID, ovpn->tx_id);
NLA_PUT_U32(ctx->nl_msg, OVPN_A_PEER_SOCKET, ovpn->socket);
if (!is_tcp) {
@@ -767,6 +782,10 @@ static int ovpn_handle_peer(struct nl_msg *msg, void (*arg)__always_unused)
fprintf(stderr, "* Peer %u\n",
nla_get_u32(pattrs[OVPN_A_PEER_ID]));
+ if (pattrs[OVPN_A_PEER_TX_ID])
+ fprintf(stderr, "\tTX peer ID %u\n",
+ nla_get_u32(pattrs[OVPN_A_PEER_TX_ID]));
+
if (pattrs[OVPN_A_PEER_SOCKET_NETNSID])
fprintf(stderr, "\tsocket NetNS ID: %d\n",
nla_get_s32(pattrs[OVPN_A_PEER_SOCKET_NETNSID]));
@@ -1516,6 +1535,9 @@ static int ovpn_handle_msg(struct nl_msg *msg, void *arg)
case OVPN_CMD_PEER_DEL_NTF:
fprintf(stdout, "received CMD_PEER_DEL_NTF\n");
break;
+ case OVPN_CMD_PEER_FLOAT_NTF:
+ fprintf(stdout, "received CMD_PEER_FLOAT_NTF\n");
+ break;
case OVPN_CMD_KEY_SWAP_NTF:
fprintf(stdout, "received CMD_KEY_SWAP_NTF\n");
break;
@@ -1654,41 +1676,58 @@ static void usage(const char *cmd)
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr,
- "* listen <iface> <lport> <peers_file> [ipv6]: listen for incoming peer TCP connections\n");
+ "* listen <iface> <lport> <id_type> <peers_file> [ipv6]: listen for incoming peer TCP connections\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tlport: TCP port to listen to\n");
+ fprintf(stderr, "\tid_type:\n");
+ fprintf(stderr,
+ "\t\t- SYMM for ignoring the TX peer ID from the peers_file\n");
+ fprintf(stderr,
+ "\t\t- ASYMM for using the TX peer ID from the peers_file\n");
fprintf(stderr,
"\tpeers_file: file containing one peer per line: Line format:\n");
- fprintf(stderr, "\t\t<peer_id> <vpnaddr>\n");
+ fprintf(stderr, "\t\t<peer_id> <tx_id> <vpnaddr>\n");
fprintf(stderr,
"\tipv6: whether the socket should listen to the IPv6 wildcard address\n");
fprintf(stderr,
- "* connect <iface> <peer_id> <raddr> <rport> [key_file]: start connecting peer of TCP-based VPN session\n");
+ "* connect <iface> <peer_id> <tx_id> <raddr> <rport> [key_file]: start connecting peer of TCP-based VPN session\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
- fprintf(stderr, "\tpeer_id: peer ID of the connecting peer\n");
+ fprintf(stderr,
+ "\tpeer_id: peer ID found in data packets received from this peer\n");
+ fprintf(stderr,
+ "\ttx_id: peer ID to be used when sending to this peer, 'none' for symmetric peer ID\n");
fprintf(stderr, "\traddr: peer IP address to connect to\n");
fprintf(stderr, "\trport: peer TCP port to connect to\n");
fprintf(stderr,
"\tkey_file: file containing the symmetric key for encryption\n");
fprintf(stderr,
- "* new_peer <iface> <peer_id> <lport> <raddr> <rport> [vpnaddr]: add new peer\n");
+ "* new_peer <iface> <peer_id> <tx_id> <lport> <raddr> <rport> [vpnaddr]: add new peer\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
- fprintf(stderr, "\tlport: local UDP port to bind to\n");
fprintf(stderr,
- "\tpeer_id: peer ID to be used in data packets to/from this peer\n");
+ "\tpeer_id: peer ID found in data packets received from this peer\n");
+ fprintf(stderr,
+ "\ttx_id: peer ID to be used when sending to this peer, 'none' for symmetric peer ID\n");
+ fprintf(stderr, "\tlport: local UDP port to bind to\n");
fprintf(stderr, "\traddr: peer IP address\n");
fprintf(stderr, "\trport: peer UDP port\n");
fprintf(stderr, "\tvpnaddr: peer VPN IP\n");
fprintf(stderr,
- "* new_multi_peer <iface> <lport> <peers_file>: add multiple peers as listed in the file\n");
+ "* new_multi_peer <iface> <lport> <id_type> <peers_file> [mark]: add multiple peers as listed in the file\n");
fprintf(stderr, "\tiface: ovpn interface name\n");
fprintf(stderr, "\tlport: local UDP port to bind to\n");
+ fprintf(stderr, "\tid_type:\n");
+ fprintf(stderr,
+ "\t\t- SYMM for ignoring the TX peer ID from the peers_file\n");
+ fprintf(stderr,
+ "\t\t- ASYMM for using the TX peer ID from the peers_file\n");
fprintf(stderr,
"\tpeers_file: text file containing one peer per line. Line format:\n");
- fprintf(stderr, "\t\t<peer_id> <raddr> <rport> <vpnaddr>\n");
+ fprintf(stderr,
+ "\t\t<peer_id> <tx_id> <raddr> <rport> <laddr> <lport> <vpnaddr>\n");
+ fprintf(stderr, "\tmark: socket FW mark value\n");
fprintf(stderr,
"* set_peer <iface> <peer_id> <keepalive_interval> <keepalive_timeout>: set peer attributes\n");
@@ -1801,15 +1840,23 @@ out:
}
static int ovpn_parse_new_peer(struct ovpn_ctx *ovpn, const char *peer_id,
- const char *raddr, const char *rport,
- const char *vpnip)
+ const char *tx_id, const char *raddr,
+ const char *rport, const char *vpnip)
{
ovpn->peer_id = strtoul(peer_id, NULL, 10);
if (errno == ERANGE || ovpn->peer_id > PEER_ID_UNDEF) {
- fprintf(stderr, "peer ID value out of range\n");
+ fprintf(stderr, "rx peer ID value out of range\n");
return -1;
}
+ if (ovpn->asymm_id) {
+ ovpn->tx_id = strtoul(tx_id, NULL, 10);
+ if (errno == ERANGE || ovpn->tx_id > PEER_ID_UNDEF) {
+ fprintf(stderr, "tx peer ID value out of range\n");
+ return -1;
+ }
+ }
+
return ovpn_parse_remote(ovpn, raddr, rport, vpnip);
}
@@ -1936,8 +1983,8 @@ static void ovpn_waitbg(void)
static int ovpn_run_cmd(struct ovpn_ctx *ovpn)
{
- char peer_id[10], vpnip[INET6_ADDRSTRLEN], laddr[128], lport[10];
- char raddr[128], rport[10];
+ char peer_id[10], tx_id[10], vpnip[INET6_ADDRSTRLEN], laddr[128];
+ char lport[10], raddr[128], rport[10];
int n, ret;
FILE *fp;
@@ -1964,7 +2011,8 @@ static int ovpn_run_cmd(struct ovpn_ctx *ovpn)
int num_peers = 0;
- while ((n = fscanf(fp, "%s %s\n", peer_id, vpnip)) == 2) {
+ while ((n = fscanf(fp, "%s %s %s\n", peer_id, tx_id,
+ vpnip)) == 3) {
struct ovpn_ctx peer_ctx = { 0 };
if (num_peers == MAX_PEERS) {
@@ -1974,6 +2022,7 @@ static int ovpn_run_cmd(struct ovpn_ctx *ovpn)
peer_ctx.ifindex = ovpn->ifindex;
peer_ctx.sa_family = ovpn->sa_family;
+ peer_ctx.asymm_id = ovpn->asymm_id;
peer_ctx.socket = ovpn_accept(ovpn);
if (peer_ctx.socket < 0) {
@@ -1984,8 +2033,8 @@ static int ovpn_run_cmd(struct ovpn_ctx *ovpn)
/* store peer sockets to test TCP I/O */
ovpn->cli_sockets[num_peers] = peer_ctx.socket;
- ret = ovpn_parse_new_peer(&peer_ctx, peer_id, NULL,
- NULL, vpnip);
+ ret = ovpn_parse_new_peer(&peer_ctx, peer_id, tx_id,
+ NULL, NULL, vpnip);
if (ret < 0) {
fprintf(stderr, "error while parsing line\n");
return -1;
@@ -2053,16 +2102,17 @@ static int ovpn_run_cmd(struct ovpn_ctx *ovpn)
return -1;
}
- while ((n = fscanf(fp, "%s %s %s %s %s %s\n", peer_id, laddr,
- lport, raddr, rport, vpnip)) == 6) {
+ while ((n = fscanf(fp, "%s %s %s %s %s %s %s\n", peer_id, tx_id,
+ laddr, lport, raddr, rport, vpnip)) == 7) {
struct ovpn_ctx peer_ctx = { 0 };
peer_ctx.ifindex = ovpn->ifindex;
peer_ctx.socket = ovpn->socket;
peer_ctx.sa_family = AF_UNSPEC;
+ peer_ctx.asymm_id = ovpn->asymm_id;
- ret = ovpn_parse_new_peer(&peer_ctx, peer_id, raddr,
- rport, vpnip);
+ ret = ovpn_parse_new_peer(&peer_ctx, peer_id, tx_id,
+ raddr, rport, vpnip);
if (ret < 0) {
fprintf(stderr, "error while parsing line\n");
return -1;
@@ -2158,7 +2208,7 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
case CMD_DEL_IFACE:
break;
case CMD_LISTEN:
- if (argc < 5)
+ if (argc < 6)
return -EINVAL;
ovpn->lport = strtoul(argv[3], NULL, 10);
@@ -2167,55 +2217,67 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
return -1;
}
- ovpn->peers_file = argv[4];
+ if (strcmp(argv[4], "SYMM") == 0) {
+ ovpn->asymm_id = false;
+ } else if (strcmp(argv[4], "ASYMM") == 0) {
+ ovpn->asymm_id = true;
+ } else {
+ fprintf(stderr, "Cannot parse id type: %s\n", argv[4]);
+ return -1;
+ }
+
+ ovpn->peers_file = argv[5];
ovpn->sa_family = AF_INET;
- if (argc > 5 && !strcmp(argv[5], "ipv6"))
+ if (argc > 6 && !strcmp(argv[6], "ipv6"))
ovpn->sa_family = AF_INET6;
break;
case CMD_CONNECT:
- if (argc < 6)
+ if (argc < 7)
return -EINVAL;
ovpn->sa_family = AF_INET;
+ ovpn->asymm_id = strcmp(argv[4], "none");
ret = ovpn_parse_new_peer(ovpn, argv[3], argv[4], argv[5],
- NULL);
+ argv[6], NULL);
if (ret < 0) {
fprintf(stderr, "Cannot parse remote peer data\n");
return -1;
}
- if (argc > 6) {
+ if (argc > 7) {
ovpn->key_slot = OVPN_KEY_SLOT_PRIMARY;
ovpn->key_id = 0;
ovpn->cipher = OVPN_CIPHER_ALG_AES_GCM;
ovpn->key_dir = KEY_DIR_OUT;
- ret = ovpn_parse_key(argv[6], ovpn);
+ ret = ovpn_parse_key(argv[7], ovpn);
if (ret)
return -1;
}
break;
case CMD_NEW_PEER:
- if (argc < 7)
+ if (argc < 8)
return -EINVAL;
- ovpn->lport = strtoul(argv[4], NULL, 10);
+ ovpn->asymm_id = strcmp(argv[4], "none");
+
+ ovpn->lport = strtoul(argv[5], NULL, 10);
if (errno == ERANGE || ovpn->lport > 65535) {
fprintf(stderr, "lport value out of range\n");
return -1;
}
- const char *vpnip = (argc > 7) ? argv[7] : NULL;
+ const char *vpnip = (argc > 8) ? argv[8] : NULL;
- ret = ovpn_parse_new_peer(ovpn, argv[3], argv[5], argv[6],
- vpnip);
+ ret = ovpn_parse_new_peer(ovpn, argv[3], argv[4], argv[6],
+ argv[7], vpnip);
if (ret < 0)
return -1;
break;
case CMD_NEW_MULTI_PEER:
- if (argc < 5)
+ if (argc < 6)
return -EINVAL;
ovpn->lport = strtoul(argv[3], NULL, 10);
@@ -2224,7 +2286,25 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
return -1;
}
- ovpn->peers_file = argv[4];
+ if (!strcmp(argv[4], "SYMM")) {
+ ovpn->asymm_id = false;
+ } else if (!strcmp(argv[4], "ASYMM")) {
+ ovpn->asymm_id = true;
+ } else {
+ fprintf(stderr, "Cannot parse id type: %s\n", argv[4]);
+ return -1;
+ }
+
+ ovpn->peers_file = argv[5];
+
+ ovpn->mark = 0;
+ if (argc > 6) {
+ ovpn->mark = strtoul(argv[6], NULL, 10);
+ if (errno == ERANGE || ovpn->mark > UINT32_MAX) {
+ fprintf(stderr, "mark value out of range\n");
+ return -1;
+ }
+ }
break;
case CMD_SET_PEER:
if (argc < 6)
diff --git a/tools/testing/selftests/net/ovpn/tcp_peers.txt b/tools/testing/selftests/net/ovpn/tcp_peers.txt
index d753eebe8716..3cb67b560705 100644
--- a/tools/testing/selftests/net/ovpn/tcp_peers.txt
+++ b/tools/testing/selftests/net/ovpn/tcp_peers.txt
@@ -1,5 +1,6 @@
-1 5.5.5.2
-2 5.5.5.3
-3 5.5.5.4
-4 5.5.5.5
-5 5.5.5.6
+1 10 5.5.5.2
+2 11 5.5.5.3
+3 12 5.5.5.4
+4 13 5.5.5.5
+5 14 5.5.5.6
+6 15 5.5.5.7
diff --git a/tools/testing/selftests/net/ovpn/test-close-socket.sh b/tools/testing/selftests/net/ovpn/test-close-socket.sh
index 5e48a8b67928..0d09df14fe8e 100755
--- a/tools/testing/selftests/net/ovpn/test-close-socket.sh
+++ b/tools/testing/selftests/net/ovpn/test-close-socket.sh
@@ -27,7 +27,7 @@ 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} 60 120
+ ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} $((${p}+9)) 60 120
done
sleep 1
diff --git a/tools/testing/selftests/net/ovpn/test-mark.sh b/tools/testing/selftests/net/ovpn/test-mark.sh
new file mode 100755
index 000000000000..8534428ed3eb
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/test-mark.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020-2025 OpenVPN, Inc.
+#
+# Author: Ralf Lici <ralf@mandelbit.com>
+# Antonio Quartulli <antonio@openvpn.net>
+
+#set -x
+set -e
+
+MARK=1056
+
+source ./common.sh
+
+cleanup
+
+modprobe -q ovpn || true
+
+for p in $(seq 0 "${NUM_PEERS}"); do
+ create_ns "${p}"
+done
+
+for p in $(seq 0 3); do
+ 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}" \
+ ${MARK}
+for p in $(seq 1 3); do
+ ip netns exec peer0 "${OVPN_CLI}" new_key tun0 "${p}" 1 0 "${ALG}" 0 \
+ data64.key
+done
+
+for p in $(seq 1 3); do
+ 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}" \
+ $((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))
+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 {
+ type filter hook output priority 0;
+ policy accept;
+}'
+ip netns exec peer0 nft add rule inet filter output \
+ meta mark == ${MARK} \
+ counter drop
+
+DROP_COUNTER=$(ip netns exec peer0 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 \
+ -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 peer0 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 peer0 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))
+done
+
+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
new file mode 100755
index 000000000000..b3711a81b463
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/test-symmetric-id-float.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2025 OpenVPN, Inc.
+#
+# Author: Ralf Lici <ralf@mandelbit.com>
+# Antonio Quartulli <antonio@openvpn.net>
+
+SYMMETRIC_ID="1"
+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
new file mode 100755
index 000000000000..188cafb67b2f
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/test-symmetric-id-tcp.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2025 OpenVPN, Inc.
+#
+# Author: Ralf Lici <ralf@mandelbit.com>
+# Antonio Quartulli <antonio@openvpn.net>
+
+PROTO="TCP"
+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
new file mode 100755
index 000000000000..35b119c72e4f
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/test-symmetric-id.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2025 OpenVPN, Inc.
+#
+# Author: Ralf Lici <ralf@mandelbit.com>
+# Antonio Quartulli <antonio@openvpn.net>
+
+SYMMETRIC_ID="1"
+
+source test.sh
diff --git a/tools/testing/selftests/net/ovpn/test.sh b/tools/testing/selftests/net/ovpn/test.sh
index e8acdc303307..b60e94a4094e 100755
--- a/tools/testing/selftests/net/ovpn/test.sh
+++ b/tools/testing/selftests/net/ovpn/test.sh
@@ -18,6 +18,10 @@ for p in $(seq 0 ${NUM_PEERS}); do
done
for p in $(seq 0 ${NUM_PEERS}); do
+ setup_listener ${p}
+done
+
+for p in $(seq 0 ${NUM_PEERS}); do
setup_ns ${p} 5.5.5.$((${p} + 1))/24 ${MTU}
done
@@ -27,14 +31,45 @@ 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} 60 120
+ ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} \
+ $((${p}+ID_OFFSET)) 60 120
done
sleep 1
+TCPDUMP_TIMEOUT="1.5s"
for p in $(seq 1 ${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} + ID_OFFSET)))
+ RADDR=""
+ if [ "${PROTO}" == "UDP" ]; then
+ RADDR=$(awk "NR == ${p} {print \$3}" ${UDP_PEERS_FILE})
+ fi
+
+ timeout ${TCPDUMP_TIMEOUT} ip netns exec peer${p} \
+ tcpdump --immediate-mode -p -ni veth${p} -c 1 \
+ "$(build_capture_filter "${HEADER1}" "${RADDR}")" \
+ >/dev/null 2>&1 &
+ TCPDUMP_PID1=$!
+ timeout ${TCPDUMP_TIMEOUT} ip netns exec peer${p} \
+ tcpdump --immediate-mode -p -ni veth${p} -c 1 \
+ "$(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))
+
+ wait ${TCPDUMP_PID1}
+ wait ${TCPDUMP_PID2}
done
# ping LAN behind client 1
@@ -57,9 +92,12 @@ ip netns exec 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} 2 1 ${ALG} 1 data64.key
- ip netns exec peer${p} ${OVPN_CLI} swap_keys tun${p} ${p}
+ 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))
done
sleep 1
@@ -71,17 +109,19 @@ ip netns exec peer1 ${OVPN_CLI} get_peer tun1
echo "Querying peer 1:"
ip netns exec peer0 ${OVPN_CLI} get_peer tun0 1
-echo "Querying non-existent peer 10:"
-ip netns exec peer0 ${OVPN_CLI} get_peer tun0 10 || true
+echo "Querying non-existent peer 20:"
+ip netns exec peer0 ${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
+ip netns exec peer1 ${OVPN_CLI} del_peer tun1 $((1 + 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} 1
- ip netns exec peer${p} ${OVPN_CLI} get_key tun${p} ${p} 2
+ 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
done
echo "Deleting peer while sending traffic:"
@@ -90,28 +130,36 @@ sleep 2
ip netns exec peer0 ${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 || true
+ip netns exec peer2 ${OVPN_CLI} del_peer tun2 $((2 + 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} 1
- ip netns exec peer${p} ${OVPN_CLI} del_key tun${p} ${p} 2
+ 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
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} 0 0
+ ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} \
+ $((${p} + 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} 3 3
+ ip netns exec peer${p} ${OVPN_CLI} set_peer tun${p} \
+ $((${p} + ID_OFFSET)) 3 3
done
sleep 5
+for p in $(seq 0 ${NUM_PEERS}); do
+ compare_ntfs ${p}
+done
+
cleanup
modprobe -r ovpn || true
diff --git a/tools/testing/selftests/net/ovpn/udp_peers.txt b/tools/testing/selftests/net/ovpn/udp_peers.txt
index e9773ddf875c..93de6465353c 100644
--- a/tools/testing/selftests/net/ovpn/udp_peers.txt
+++ b/tools/testing/selftests/net/ovpn/udp_peers.txt
@@ -1,6 +1,6 @@
-1 10.10.1.1 1 10.10.1.2 1 5.5.5.2
-2 10.10.2.1 1 10.10.2.2 1 5.5.5.3
-3 10.10.3.1 1 10.10.3.2 1 5.5.5.4
-4 fd00:0:0:4::1 1 fd00:0:0:4::2 1 5.5.5.5
-5 fd00:0:0:5::1 1 fd00:0:0:5::2 1 5.5.5.6
-6 fd00:0:0:6::1 1 fd00:0:0:6::2 1 5.5.5.7
+1 10 10.10.1.1 1 10.10.1.2 1 5.5.5.2
+2 11 10.10.2.1 1 10.10.2.2 1 5.5.5.3
+3 12 10.10.3.1 1 10.10.3.2 1 5.5.5.4
+4 13 fd00:0:0:4::1 1 fd00:0:0:4::2 1 5.5.5.5
+5 14 fd00:0:0:5::1 1 fd00:0:0:5::2 1 5.5.5.6
+6 15 fd00:0:0:6::1 1 fd00:0:0:6::2 1 5.5.5.7