* [PATCH net-next 1/9] selftests: ovpn: allow compiling ovpn-cli.c with mbedtls3
[not found] <20260304230643.1014-1-antonio@openvpn.net>
@ 2026-03-04 23:06 ` Antonio Quartulli
2026-03-04 23:06 ` [PATCH net-next 4/9] ovpn: notify userspace on client float event Antonio Quartulli
` (3 subsequent siblings)
4 siblings, 0 replies; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-04 23:06 UTC (permalink / raw)
To: netdev
Cc: Antonio Quartulli, Sabrina Dubroca, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, David S. Miller, Eric Dumazet, Shuah Khan,
linux-kselftest, horms
mbedtls 3 installs headers and calls the shared object
differently than version 2, therefore we must now rely
on pkgconfig to fill the right C/LDFLAGS.
Moreover the mbedtls3 library expects any base64 file to
have their content on one line.
Since this change does no break older versions,
let's change the sample key file format and make mbedtls3
happy.
Cc: Shuah Khan <shuah@kernel.org>
Cc: linux-kselftest@vger.kernel.org
Cc: horms@kernel.org
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
tools/testing/selftests/net/ovpn/Makefile | 14 ++++++++++----
tools/testing/selftests/net/ovpn/data64.key | 6 +-----
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/tools/testing/selftests/net/ovpn/Makefile b/tools/testing/selftests/net/ovpn/Makefile
index dbe0388c8512..d3a070db0bb5 100644
--- a/tools/testing/selftests/net/ovpn/Makefile
+++ b/tools/testing/selftests/net/ovpn/Makefile
@@ -2,19 +2,25 @@
# 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)
+NL_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 = -lnl-genl-3 -lnl-3
endif
-LDLIBS += $(VAR_LDLIBS)
+LDLIBS += $(NL_LDLIBS)
TEST_FILES = common.sh
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==
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next 4/9] ovpn: notify userspace on client float event
[not found] <20260304230643.1014-1-antonio@openvpn.net>
2026-03-04 23:06 ` [PATCH net-next 1/9] selftests: ovpn: allow compiling ovpn-cli.c with mbedtls3 Antonio Quartulli
@ 2026-03-04 23:06 ` Antonio Quartulli
2026-03-04 23:06 ` [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching Antonio Quartulli
` (2 subsequent siblings)
4 siblings, 0 replies; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-04 23:06 UTC (permalink / raw)
To: netdev
Cc: Ralf Lici, Sabrina Dubroca, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, David S. Miller, Eric Dumazet, linux-kselftest,
horms, shuah, donald.hunter, Antonio Quartulli
From: Ralf Lici <ralf@mandelbit.com>
Send a netlink notification when a client updates its remote UDP
endpoint. The notification includes the new IP address, port, and scope
ID (for IPv6).
Cc: linux-kselftest@vger.kernel.org
Cc: horms@kernel.org
Cc: shuah@kernel.org
Cc: donald.hunter@gmail.com
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
Reviewed-by: Sabrina Dubroca <sd@queasysnail.net>
---
Documentation/netlink/specs/ovpn.yaml | 6 ++
drivers/net/ovpn/netlink.c | 82 +++++++++++++++++++++
drivers/net/ovpn/netlink.h | 2 +
drivers/net/ovpn/peer.c | 2 +
include/uapi/linux/ovpn.h | 1 +
tools/testing/selftests/net/ovpn/ovpn-cli.c | 3 +
6 files changed, 96 insertions(+)
diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml
index 1b91045cee2e..0d0c028bf96f 100644
--- a/Documentation/netlink/specs/ovpn.yaml
+++ b/Documentation/netlink/specs/ovpn.yaml
@@ -502,6 +502,12 @@ operations:
- ifindex
- keyconf
+ -
+ name: peer-float-ntf
+ doc: Notification about a peer floating (changing its remote UDP endpoint)
+ notify: peer-get
+ mcgrp: peers
+
mcast-groups:
list:
-
diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c
index fed0e46b32a3..e10d7f9a28f5 100644
--- a/drivers/net/ovpn/netlink.c
+++ b/drivers/net/ovpn/netlink.c
@@ -1203,6 +1203,88 @@ int ovpn_nl_peer_del_notify(struct ovpn_peer *peer)
return ret;
}
+/**
+ * ovpn_nl_peer_float_notify - notify userspace about peer floating
+ * @peer: the floated peer
+ * @ss: sockaddr representing the new remote endpoint
+ *
+ * Return: 0 on success or a negative error code otherwise
+ */
+int ovpn_nl_peer_float_notify(struct ovpn_peer *peer,
+ const struct sockaddr_storage *ss)
+{
+ struct ovpn_socket *sock;
+ struct sockaddr_in6 *sa6;
+ struct sockaddr_in *sa;
+ struct sk_buff *msg;
+ struct nlattr *attr;
+ int ret = -EMSGSIZE;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+ if (!msg)
+ return -ENOMEM;
+
+ hdr = genlmsg_put(msg, 0, 0, &ovpn_nl_family, 0,
+ OVPN_CMD_PEER_FLOAT_NTF);
+ if (!hdr) {
+ ret = -ENOBUFS;
+ goto err_free_msg;
+ }
+
+ if (nla_put_u32(msg, OVPN_A_IFINDEX, peer->ovpn->dev->ifindex))
+ goto err_cancel_msg;
+
+ attr = nla_nest_start(msg, OVPN_A_PEER);
+ if (!attr)
+ goto err_cancel_msg;
+
+ if (nla_put_u32(msg, OVPN_A_PEER_ID, peer->id))
+ goto err_cancel_msg;
+
+ if (ss->ss_family == AF_INET) {
+ sa = (struct sockaddr_in *)ss;
+ if (nla_put_in_addr(msg, OVPN_A_PEER_REMOTE_IPV4,
+ sa->sin_addr.s_addr) ||
+ nla_put_net16(msg, OVPN_A_PEER_REMOTE_PORT, sa->sin_port))
+ goto err_cancel_msg;
+ } else if (ss->ss_family == AF_INET6) {
+ sa6 = (struct sockaddr_in6 *)ss;
+ if (nla_put_in6_addr(msg, OVPN_A_PEER_REMOTE_IPV6,
+ &sa6->sin6_addr) ||
+ nla_put_u32(msg, OVPN_A_PEER_REMOTE_IPV6_SCOPE_ID,
+ sa6->sin6_scope_id) ||
+ nla_put_net16(msg, OVPN_A_PEER_REMOTE_PORT, sa6->sin6_port))
+ goto err_cancel_msg;
+ } else {
+ ret = -EAFNOSUPPORT;
+ goto err_cancel_msg;
+ }
+
+ nla_nest_end(msg, attr);
+ genlmsg_end(msg, hdr);
+
+ rcu_read_lock();
+ sock = rcu_dereference(peer->sock);
+ if (!sock) {
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+ genlmsg_multicast_netns(&ovpn_nl_family, sock_net(sock->sk), msg,
+ 0, OVPN_NLGRP_PEERS, GFP_ATOMIC);
+ rcu_read_unlock();
+
+ return 0;
+
+err_unlock:
+ rcu_read_unlock();
+err_cancel_msg:
+ genlmsg_cancel(msg, hdr);
+err_free_msg:
+ nlmsg_free(msg);
+ return ret;
+}
+
/**
* ovpn_nl_key_swap_notify - notify userspace peer's key must be renewed
* @peer: the peer whose key needs to be renewed
diff --git a/drivers/net/ovpn/netlink.h b/drivers/net/ovpn/netlink.h
index 8615dfc3c472..11ee7c681885 100644
--- a/drivers/net/ovpn/netlink.h
+++ b/drivers/net/ovpn/netlink.h
@@ -13,6 +13,8 @@ int ovpn_nl_register(void);
void ovpn_nl_unregister(void);
int ovpn_nl_peer_del_notify(struct ovpn_peer *peer);
+int ovpn_nl_peer_float_notify(struct ovpn_peer *peer,
+ const struct sockaddr_storage *ss);
int ovpn_nl_key_swap_notify(struct ovpn_peer *peer, u8 key_id);
#endif /* _NET_OVPN_NETLINK_H_ */
diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c
index 3716a1d82801..4e145b4497e6 100644
--- a/drivers/net/ovpn/peer.c
+++ b/drivers/net/ovpn/peer.c
@@ -287,6 +287,8 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb)
spin_unlock_bh(&peer->lock);
+ ovpn_nl_peer_float_notify(peer, &ss);
+
/* rehashing is required only in MP mode as P2P has one peer
* only and thus there is no hashtable
*/
diff --git a/include/uapi/linux/ovpn.h b/include/uapi/linux/ovpn.h
index 959b41def61f..0cce0d58b830 100644
--- a/include/uapi/linux/ovpn.h
+++ b/include/uapi/linux/ovpn.h
@@ -100,6 +100,7 @@ enum {
OVPN_CMD_KEY_SWAP,
OVPN_CMD_KEY_SWAP_NTF,
OVPN_CMD_KEY_DEL,
+ OVPN_CMD_PEER_FLOAT_NTF,
__OVPN_CMD_MAX,
OVPN_CMD_MAX = (__OVPN_CMD_MAX - 1)
diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c
index 0f3babf19fd0..7178abae1b2f 100644
--- a/tools/testing/selftests/net/ovpn/ovpn-cli.c
+++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c
@@ -1516,6 +1516,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;
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching
[not found] <20260304230643.1014-1-antonio@openvpn.net>
2026-03-04 23:06 ` [PATCH net-next 1/9] selftests: ovpn: allow compiling ovpn-cli.c with mbedtls3 Antonio Quartulli
2026-03-04 23:06 ` [PATCH net-next 4/9] ovpn: notify userspace on client float event Antonio Quartulli
@ 2026-03-04 23:06 ` Antonio Quartulli
2026-03-06 3:17 ` Jakub Kicinski
2026-03-06 21:01 ` Jakub Kicinski
2026-03-04 23:06 ` [PATCH net-next 7/9] selftests: ovpn: check asymmetric peer-id Antonio Quartulli
2026-03-04 23:06 ` [PATCH net-next 8/9] selftests: ovpn: add test for the FW mark feature Antonio Quartulli
4 siblings, 2 replies; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-04 23:06 UTC (permalink / raw)
To: netdev
Cc: Ralf Lici, Sabrina Dubroca, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, David S. Miller, Eric Dumazet, linux-kselftest,
shuah, horms, Antonio Quartulli
From: Ralf Lici <ralf@mandelbit.com>
To verify that netlink notifications are correctly emitted and contain
the expected fields, this commit uses the tools/net/ynl/pyynl/cli.py
script to create multicast listeners. These listeners record the
captured notifications to a JSON file, which is later compared to the
expected output.
Since this change introduces additional dependencies (jq, pyyaml,
jsonschema), the tests are configured to check for their presence and
conditionally skip the notification check if they are missing.
Cc: linux-kselftest@vger.kernel.org
Cc: shuah@kernel.org
Cc: horms@kernel.org
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
.../selftests/net/ovpn/check_requirements.py | 47 +++++++++++++++++++
tools/testing/selftests/net/ovpn/common.sh | 35 ++++++++++++++
.../selftests/net/ovpn/json/peer0-float.json | 9 ++++
.../selftests/net/ovpn/json/peer0.json | 6 +++
.../selftests/net/ovpn/json/peer1-float.json | 1 +
.../selftests/net/ovpn/json/peer1.json | 1 +
.../selftests/net/ovpn/json/peer2-float.json | 1 +
.../selftests/net/ovpn/json/peer2.json | 1 +
.../selftests/net/ovpn/json/peer3-float.json | 1 +
.../selftests/net/ovpn/json/peer3.json | 1 +
.../selftests/net/ovpn/json/peer4-float.json | 1 +
.../selftests/net/ovpn/json/peer4.json | 1 +
.../selftests/net/ovpn/json/peer5-float.json | 1 +
.../selftests/net/ovpn/json/peer5.json | 1 +
.../selftests/net/ovpn/json/peer6-float.json | 1 +
.../selftests/net/ovpn/json/peer6.json | 1 +
.../selftests/net/ovpn/requirements.txt | 1 +
.../testing/selftests/net/ovpn/tcp_peers.txt | 1 +
tools/testing/selftests/net/ovpn/test.sh | 10 ++++
19 files changed, 121 insertions(+)
create mode 100755 tools/testing/selftests/net/ovpn/check_requirements.py
create mode 100644 tools/testing/selftests/net/ovpn/json/peer0-float.json
create mode 100644 tools/testing/selftests/net/ovpn/json/peer0.json
create mode 120000 tools/testing/selftests/net/ovpn/json/peer1-float.json
create mode 100644 tools/testing/selftests/net/ovpn/json/peer1.json
create mode 120000 tools/testing/selftests/net/ovpn/json/peer2-float.json
create mode 100644 tools/testing/selftests/net/ovpn/json/peer2.json
create mode 120000 tools/testing/selftests/net/ovpn/json/peer3-float.json
create mode 100644 tools/testing/selftests/net/ovpn/json/peer3.json
create mode 120000 tools/testing/selftests/net/ovpn/json/peer4-float.json
create mode 100644 tools/testing/selftests/net/ovpn/json/peer4.json
create mode 120000 tools/testing/selftests/net/ovpn/json/peer5-float.json
create mode 100644 tools/testing/selftests/net/ovpn/json/peer5.json
create mode 120000 tools/testing/selftests/net/ovpn/json/peer6-float.json
create mode 100644 tools/testing/selftests/net/ovpn/json/peer6.json
create mode 120000 tools/testing/selftests/net/ovpn/requirements.txt
diff --git a/tools/testing/selftests/net/ovpn/check_requirements.py b/tools/testing/selftests/net/ovpn/check_requirements.py
new file mode 100755
index 000000000000..161396989380
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/check_requirements.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020-2026 OpenVPN, Inc.
+#
+# Author: Antonio Quartulli <antonio@openvpn.net>
+# Ralf Lici <ralf@mandelbit.com>
+
+"""Check whether YNL python requirements are installed and version-compatible."""
+
+import sys
+from importlib.metadata import version, PackageNotFoundError
+from packaging.requirements import Requirement, InvalidRequirement
+from packaging.version import Version, InvalidVersion
+
+
+def check_requirements(requirements_path):
+ """Return dependency issues from requirements_path, or an empty list."""
+ issues = []
+ with open(requirements_path, encoding="utf-8") as f:
+ for line in f:
+ line = line.strip()
+ if not line or line.startswith("#"):
+ continue
+ try:
+ req = Requirement(line)
+ installed_version = Version(version(req.name))
+ if req.specifier and installed_version not in req.specifier:
+ issues.append(
+ f"{req.name}=={installed_version} does not satisfy {req.specifier}"
+ )
+ except PackageNotFoundError:
+ issues.append(f"{req.name} is not installed")
+ except InvalidVersion:
+ issues.append(f"{req.name} has an invalid installed version")
+ except InvalidRequirement as e:
+ issues.append(f"Could not parse requirement line: '{line}' ({e})")
+ return issues
+
+
+problems = check_requirements("requirements.txt")
+if problems:
+ print("Dependency issues found:")
+ for p in problems:
+ print(" -", p)
+ sys.exit(1)
+else:
+ sys.exit(0)
diff --git a/tools/testing/selftests/net/ovpn/common.sh b/tools/testing/selftests/net/ovpn/common.sh
index 88869c675d03..f2b58361255d 100644
--- a/tools/testing/selftests/net/ovpn/common.sh
+++ b/tools/testing/selftests/net/ovpn/common.sh
@@ -7,12 +7,18 @@
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}
+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,6 +54,18 @@ setup_ns() {
ip -n peer${1} link set tun${1} up
}
+has_listener_requirements() {
+ ./check_requirements.py && jq --version >/dev/null 2>&1
+}
+
+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() {
if [ "${PROTO}" == "UDP" ]; then
if [ ${1} -eq 0 ]; then
@@ -82,6 +100,23 @@ add_peer() {
fi
}
+compare_ntfs() {
+ if [ ${#tmp_jsons[@]} -gt 0 ]; then
+ [ "$FLOAT" == 1 ] && 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}... "
+ diff <(jq -s "${JQ_FILTER}" ${expected}) \
+ <(jq -s "${JQ_FILTER}" ${received})
+ echo "OK"
+
+ rm -f ${received} || true
+ fi
+}
+
cleanup() {
# some ovpn-cli processes sleep in background so they need manual poking
killall $(basename ${OVPN_CLI}) 2>/dev/null || true
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.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.json b/tools/testing/selftests/net/ovpn/json/peer1.json
new file mode 100644
index 000000000000..5da4ea9d51fb
--- /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": 1}}}
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.json b/tools/testing/selftests/net/ovpn/json/peer2.json
new file mode 100644
index 000000000000..8f6db4f8c2ac
--- /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": 2}}}
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.json b/tools/testing/selftests/net/ovpn/json/peer3.json
new file mode 100644
index 000000000000..bdabd6fa2e64
--- /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": 3}}}
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.json b/tools/testing/selftests/net/ovpn/json/peer4.json
new file mode 100644
index 000000000000..c3734bb9251b
--- /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": 4}}}
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.json b/tools/testing/selftests/net/ovpn/json/peer5.json
new file mode 100644
index 000000000000..46c4a348299d
--- /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": 5}}}
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.json b/tools/testing/selftests/net/ovpn/json/peer6.json
new file mode 100644
index 000000000000..aa30f2cff625
--- /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": 6}}}
diff --git a/tools/testing/selftests/net/ovpn/requirements.txt b/tools/testing/selftests/net/ovpn/requirements.txt
new file mode 120000
index 000000000000..da9fd54081c5
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/requirements.txt
@@ -0,0 +1 @@
+../../../../net/ynl/requirements.txt
\ No newline at end of file
diff --git a/tools/testing/selftests/net/ovpn/tcp_peers.txt b/tools/testing/selftests/net/ovpn/tcp_peers.txt
index d753eebe8716..b8f3cb33eaa2 100644
--- a/tools/testing/selftests/net/ovpn/tcp_peers.txt
+++ b/tools/testing/selftests/net/ovpn/tcp_peers.txt
@@ -3,3 +3,4 @@
3 5.5.5.4
4 5.5.5.5
5 5.5.5.6
+6 5.5.5.7
diff --git a/tools/testing/selftests/net/ovpn/test.sh b/tools/testing/selftests/net/ovpn/test.sh
index e8acdc303307..3ec036fd7ebc 100755
--- a/tools/testing/selftests/net/ovpn/test.sh
+++ b/tools/testing/selftests/net/ovpn/test.sh
@@ -17,6 +17,12 @@ for p in $(seq 0 ${NUM_PEERS}); do
create_ns ${p}
done
+if has_listener_requirements; then
+ for p in $(seq 0 ${NUM_PEERS}); do
+ setup_listener ${p}
+ done
+fi
+
for p in $(seq 0 ${NUM_PEERS}); do
setup_ns ${p} 5.5.5.$((${p} + 1))/24 ${MTU}
done
@@ -112,6 +118,10 @@ for p in $(seq 3 ${NUM_PEERS}); do
done
sleep 5
+for p in $(seq 0 ${NUM_PEERS}); do
+ compare_ntfs ${p}
+done
+
cleanup
modprobe -r ovpn || true
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next 7/9] selftests: ovpn: check asymmetric peer-id
[not found] <20260304230643.1014-1-antonio@openvpn.net>
` (2 preceding siblings ...)
2026-03-04 23:06 ` [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching Antonio Quartulli
@ 2026-03-04 23:06 ` Antonio Quartulli
2026-03-06 3:20 ` Jakub Kicinski
2026-03-04 23:06 ` [PATCH net-next 8/9] selftests: ovpn: add test for the FW mark feature Antonio Quartulli
4 siblings, 1 reply; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-04 23:06 UTC (permalink / raw)
To: netdev
Cc: Ralf Lici, Sabrina Dubroca, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, David S. Miller, Eric Dumazet, Shuah Khan,
linux-kselftest, horms, Antonio Quartulli
From: Ralf Lici <ralf@mandelbit.com>
Extend the base test to verify that the correct peer-id is set in data
packet headers. This is done by capturing ping packets with ngrep during
the initial exchange and matching the first portion of the header
against the expected sequence for every connection.
Cc: Shuah Khan <shuah@kernel.org>
Cc: linux-kselftest@vger.kernel.org
Cc: horms@kernel.org
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
tools/testing/selftests/net/ovpn/common.sh | 20 +++----
.../selftests/net/ovpn/json/peer1.json | 2 +-
.../selftests/net/ovpn/json/peer2.json | 2 +-
.../selftests/net/ovpn/json/peer3.json | 2 +-
.../selftests/net/ovpn/json/peer4.json | 2 +-
.../selftests/net/ovpn/json/peer5.json | 2 +-
.../selftests/net/ovpn/json/peer6.json | 2 +-
tools/testing/selftests/net/ovpn/ovpn-cli.c | 53 ++++++++++++-------
.../testing/selftests/net/ovpn/tcp_peers.txt | 12 ++---
.../selftests/net/ovpn/test-close-socket.sh | 2 +-
tools/testing/selftests/net/ovpn/test.sh | 47 +++++++++++-----
.../testing/selftests/net/ovpn/udp_peers.txt | 12 ++---
12 files changed, 97 insertions(+), 61 deletions(-)
diff --git a/tools/testing/selftests/net/ovpn/common.sh b/tools/testing/selftests/net/ovpn/common.sh
index f2b58361255d..d25cbfda15cc 100644
--- a/tools/testing/selftests/net/ovpn/common.sh
+++ b/tools/testing/selftests/net/ovpn/common.sh
@@ -76,13 +76,14 @@ add_peer() {
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
+ TX_ID=$(awk "NR == ${1} {print \$2}" ${UDP_PEERS_FILE})
+ 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} ${TX_ID} ${1} \
+ ${LPORT} ${RADDR} ${RPORT}
+ ip netns exec peer${1} ${OVPN_CLI} new_key tun${1} ${TX_ID} 1 0 \
+ ${ALG} 1 data64.key
fi
else
if [ ${1} -eq 0 ]; then
@@ -94,8 +95,9 @@ add_peer() {
}) &
sleep 5
else
- ip netns exec peer${1} ${OVPN_CLI} connect tun${1} ${1} 10.10.${1}.1 1 \
- data64.key
+ TX_ID=$(awk "NR == ${1} {print \$2}" ${TCP_PEERS_FILE})
+ ip netns exec peer${1} ${OVPN_CLI} connect tun${1} ${TX_ID} ${1} \
+ 10.10.${1}.1 1 data64.key
fi
fi
}
diff --git a/tools/testing/selftests/net/ovpn/json/peer1.json b/tools/testing/selftests/net/ovpn/json/peer1.json
index 5da4ea9d51fb..1009d26dc14a 100644
--- a/tools/testing/selftests/net/ovpn/json/peer1.json
+++ b/tools/testing/selftests/net/ovpn/json/peer1.json
@@ -1 +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": 10}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer2.json b/tools/testing/selftests/net/ovpn/json/peer2.json
index 8f6db4f8c2ac..44e9fad2b622 100644
--- a/tools/testing/selftests/net/ovpn/json/peer2.json
+++ b/tools/testing/selftests/net/ovpn/json/peer2.json
@@ -1 +1 @@
-{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "userspace", "id": 2}}}
+{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "userspace", "id": 11}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer3.json b/tools/testing/selftests/net/ovpn/json/peer3.json
index bdabd6fa2e64..d4be8ba130ae 100644
--- a/tools/testing/selftests/net/ovpn/json/peer3.json
+++ b/tools/testing/selftests/net/ovpn/json/peer3.json
@@ -1 +1 @@
-{"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": 12}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer4.json b/tools/testing/selftests/net/ovpn/json/peer4.json
index c3734bb9251b..67d27e2d48ac 100644
--- a/tools/testing/selftests/net/ovpn/json/peer4.json
+++ b/tools/testing/selftests/net/ovpn/json/peer4.json
@@ -1 +1 @@
-{"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": 13}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer5.json b/tools/testing/selftests/net/ovpn/json/peer5.json
index 46c4a348299d..ecd9bd0b2f37 100644
--- a/tools/testing/selftests/net/ovpn/json/peer5.json
+++ b/tools/testing/selftests/net/ovpn/json/peer5.json
@@ -1 +1 @@
-{"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": 14}}}
diff --git a/tools/testing/selftests/net/ovpn/json/peer6.json b/tools/testing/selftests/net/ovpn/json/peer6.json
index aa30f2cff625..7fded29c5804 100644
--- a/tools/testing/selftests/net/ovpn/json/peer6.json
+++ b/tools/testing/selftests/net/ovpn/json/peer6.json
@@ -1 +1 @@
-{"name": "peer-del-ntf", "msg": {"ifindex": 0, "peer": {"del-reason": "expired", "id": 6}}}
+{"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 7178abae1b2f..8c684f61220b 100644
--- a/tools/testing/selftests/net/ovpn/ovpn-cli.c
+++ b/tools/testing/selftests/net/ovpn/ovpn-cli.c
@@ -103,7 +103,7 @@ struct ovpn_ctx {
sa_family_t sa_family;
- unsigned long peer_id;
+ unsigned long peer_id, tx_id;
unsigned long lport;
union {
@@ -649,6 +649,7 @@ 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);
+ 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 +768,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]));
@@ -1676,11 +1681,13 @@ static void usage(const char *cmd)
"\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\n");
fprintf(stderr, "\traddr: peer IP address\n");
fprintf(stderr, "\trport: peer UDP port\n");
fprintf(stderr, "\tvpnaddr: peer VPN IP\n");
@@ -1691,7 +1698,7 @@ static void usage(const char *cmd)
fprintf(stderr, "\tlport: local UDP port to bind to\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,
"* set_peer <iface> <peer_id> <keepalive_interval> <keepalive_timeout>: set peer attributes\n");
@@ -1804,12 +1811,18 @@ static int ovpn_parse_remote(struct ovpn_ctx *ovpn, const char *host,
}
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;
+ }
+
+ 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;
}
@@ -1939,7 +1952,7 @@ 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 peer_id[10], tx_id[10], vpnip[INET6_ADDRSTRLEN], laddr[128], lport[10];
char raddr[128], rport[10];
int n, ret;
FILE *fp;
@@ -1967,7 +1980,7 @@ 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) {
@@ -1987,7 +2000,7 @@ 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,
+ ret = ovpn_parse_new_peer(&peer_ctx, peer_id, tx_id, NULL,
NULL, vpnip);
if (ret < 0) {
fprintf(stderr, "error while parsing line\n");
@@ -2056,15 +2069,15 @@ 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;
- ret = ovpn_parse_new_peer(&peer_ctx, peer_id, raddr,
+ ret = ovpn_parse_new_peer(&peer_ctx, peer_id, tx_id, raddr,
rport, vpnip);
if (ret < 0) {
fprintf(stderr, "error while parsing line\n");
@@ -2177,25 +2190,25 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
ovpn->sa_family = AF_INET6;
break;
case CMD_CONNECT:
- if (argc < 6)
+ if (argc < 7)
return -EINVAL;
ovpn->sa_family = AF_INET;
- ret = ovpn_parse_new_peer(ovpn, argv[3], argv[4], argv[5],
+ ret = ovpn_parse_new_peer(ovpn, argv[3], argv[4], argv[5], 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;
}
@@ -2204,15 +2217,15 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
if (argc < 7)
return -EINVAL;
- ovpn->lport = strtoul(argv[4], NULL, 10);
+ 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],
+ ret = ovpn_parse_new_peer(ovpn, argv[3], argv[4], argv[6], argv[7],
vpnip);
if (ret < 0)
return -1;
diff --git a/tools/testing/selftests/net/ovpn/tcp_peers.txt b/tools/testing/selftests/net/ovpn/tcp_peers.txt
index b8f3cb33eaa2..3cb67b560705 100644
--- a/tools/testing/selftests/net/ovpn/tcp_peers.txt
+++ b/tools/testing/selftests/net/ovpn/tcp_peers.txt
@@ -1,6 +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
-6 5.5.5.7
+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.sh b/tools/testing/selftests/net/ovpn/test.sh
index 3ec036fd7ebc..7fadf35813bd 100755
--- a/tools/testing/selftests/net/ovpn/test.sh
+++ b/tools/testing/selftests/net/ovpn/test.sh
@@ -33,14 +33,35 @@ 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
+NGREP_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 ("${p}" one way and "${p} + 9" the other way)
+ HEADER1=$(printf "0x4800000%x" ${p})
+ HEADER2=$(printf "0x4800000%x" $((${p} + 9)))
+ CAPTURE_LEN=8
+
+ timeout ${NGREP_TIMEOUT} ip netns exec peer${p} ngrep -xqn 1 -X "${HEADER1}" \
+ -S ${CAPTURE_LEN} -d veth${p} 1>/dev/null &
+ NGREP_PID1=$!
+ timeout ${NGREP_TIMEOUT} ip netns exec peer${p} ngrep -xqn 1 -X "${HEADER2}" \
+ -S ${CAPTURE_LEN} -d veth${p} 1>/dev/null &
+ NGREP_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 ${NGREP_PID1}
+ wait ${NGREP_PID2}
done
# ping LAN behind client 1
@@ -64,8 +85,8 @@ 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 peer${p} ${OVPN_CLI} new_key tun${p} $((${p} + 9)) 2 1 ${ALG} 1 data64.key
+ ip netns exec peer${p} ${OVPN_CLI} swap_keys tun${p} $((${p} + 9))
done
sleep 1
@@ -77,17 +98,17 @@ 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 10
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} + 9)) 1
+ ip netns exec peer${p} ${OVPN_CLI} get_key tun${p} $((${p} + 9)) 2
done
echo "Deleting peer while sending traffic:"
@@ -96,25 +117,25 @@ 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 11 || 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} + 9)) 1
+ ip netns exec peer${p} ${OVPN_CLI} del_key tun${p} $((${p} + 9)) 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} + 9)) 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} + 9)) 3 3
done
sleep 5
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
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next 8/9] selftests: ovpn: add test for the FW mark feature
[not found] <20260304230643.1014-1-antonio@openvpn.net>
` (3 preceding siblings ...)
2026-03-04 23:06 ` [PATCH net-next 7/9] selftests: ovpn: check asymmetric peer-id Antonio Quartulli
@ 2026-03-04 23:06 ` Antonio Quartulli
2026-03-06 3:22 ` Jakub Kicinski
4 siblings, 1 reply; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-04 23:06 UTC (permalink / raw)
To: netdev
Cc: Ralf Lici, Sabrina Dubroca, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, David S. Miller, Eric Dumazet, Shuah Khan,
linux-kselftest, horms, Antonio Quartulli
From: Ralf Lici <ralf@mandelbit.com>
Add a selftest to verify that the FW mark socket option is correctly
supported and its value propagated by ovpn.
The test adds and removes nftables DROP rules based on the mark value,
and checks that the rule counter aligns with the number of lost ping
packets.
Cc: Shuah Khan <shuah@kernel.org>
Cc: linux-kselftest@vger.kernel.org
Cc: horms@kernel.org
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
tools/testing/selftests/net/ovpn/Makefile | 1 +
tools/testing/selftests/net/ovpn/ovpn-cli.c | 26 ++++-
tools/testing/selftests/net/ovpn/test-mark.sh | 95 +++++++++++++++++++
3 files changed, 120 insertions(+), 2 deletions(-)
create mode 100755 tools/testing/selftests/net/ovpn/test-mark.sh
diff --git a/tools/testing/selftests/net/ovpn/Makefile b/tools/testing/selftests/net/ovpn/Makefile
index d3a070db0bb5..7c87c95d957e 100644
--- a/tools/testing/selftests/net/ovpn/Makefile
+++ b/tools/testing/selftests/net/ovpn/Makefile
@@ -31,6 +31,7 @@ TEST_PROGS := \
test-close-socket.sh \
test-float.sh \
test-large-mtu.sh \
+ test-mark.sh \
test-tcp.sh \
test.sh \
# end of TEST_PROGS
diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c
index 8c684f61220b..01bdcc0a8049 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>
@@ -133,6 +134,8 @@ struct ovpn_ctx {
enum ovpn_key_slot key_slot;
int key_id;
+ uint32_t mark;
+
const char *peers_file;
};
@@ -521,6 +524,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,
@@ -1693,12 +1705,13 @@ static void usage(const char *cmd)
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> <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,
"\tpeers_file: text file containing one peer per line. Line format:\n");
- fprintf(stderr, "\t\t<peer_id> <tx_id> <raddr> <rport> <laddr> <lport> <vpnaddr>\n");
+ fprintf(stderr, "\t\t<peer_id> <tx_id> <raddr> <rport> <laddr> <lport> <vpnaddr> [mark]\n");
+ fprintf(stderr, "\tmark: socket FW mark value\n");
fprintf(stderr,
"* set_peer <iface> <peer_id> <keepalive_interval> <keepalive_timeout>: set peer attributes\n");
@@ -2241,6 +2254,15 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
}
ovpn->peers_file = argv[4];
+
+ ovpn->mark = 0;
+ if (argc > 5) {
+ ovpn->mark = strtoul(argv[5], 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/test-mark.sh b/tools/testing/selftests/net/ovpn/test-mark.sh
new file mode 100755
index 000000000000..1a107f9b5431
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/test-mark.sh
@@ -0,0 +1,95 @@
+#!/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 any any 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
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching
2026-03-04 23:06 ` [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching Antonio Quartulli
@ 2026-03-06 3:17 ` Jakub Kicinski
2026-03-06 13:05 ` Antonio Quartulli
2026-03-06 21:01 ` Jakub Kicinski
1 sibling, 1 reply; 19+ messages in thread
From: Jakub Kicinski @ 2026-03-06 3:17 UTC (permalink / raw)
To: Antonio Quartulli
Cc: netdev, Ralf Lici, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, linux-kselftest, shuah, horms
On Thu, 5 Mar 2026 00:06:23 +0100 Antonio Quartulli wrote:
> .../selftests/net/ovpn/check_requirements.py | 47 +++++++++++++++++++
this file is not included in the Makefile, it should probably be under
TEST_FILES ? otherwise when tests are installed with make install this
file won't be copied
> +if has_listener_requirements; then
> + for p in $(seq 0 ${NUM_PEERS}); do
> + setup_listener ${p}
> + done
> +fi
There should probably be an else branch which prints some kind of
echo "SKIP - requirements missing"
Your requirements.py script prints stuff but the output needs to
contain the word SKIP
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 7/9] selftests: ovpn: check asymmetric peer-id
2026-03-04 23:06 ` [PATCH net-next 7/9] selftests: ovpn: check asymmetric peer-id Antonio Quartulli
@ 2026-03-06 3:20 ` Jakub Kicinski
2026-03-06 13:19 ` Antonio Quartulli
0 siblings, 1 reply; 19+ messages in thread
From: Jakub Kicinski @ 2026-03-06 3:20 UTC (permalink / raw)
To: Antonio Quartulli
Cc: netdev, Ralf Lici, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, Shuah Khan, linux-kselftest, horms
On Thu, 5 Mar 2026 00:06:25 +0100 Antonio Quartulli wrote:
> From: Ralf Lici <ralf@mandelbit.com>
>
> Extend the base test to verify that the correct peer-id is set in data
> packet headers. This is done by capturing ping packets with ngrep during
> the initial exchange and matching the first portion of the header
> against the expected sequence for every connection.
AI asks
> @@ -2204,15 +2217,15 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
> if (argc < 7)
> return -EINVAL;
The CMD_CONNECT case was correctly updated from 'argc < 6' to 'argc < 7'
for the new tx_id argument. However, was CMD_NEW_PEER also updated?
Looking at the usage text, CMD_NEW_PEER now expects:
new_peer <iface> <peer_id> <tx_id> <lport> <raddr> <rport> [vpnaddr]
That's argv[0] through argv[7] for 8 required arguments (indices 0-7),
but the check at the start of CMD_NEW_PEER still requires only argc < 7.
With argc=7, argv[7] would be NULL according to the C standard, so when
ovpn_parse_new_peer() passes argv[7] as rport and calls getaddrinfo(),
the NULL service parameter would silently produce port 0 instead of
the intended port.
Should the check be 'argc < 8' instead?
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 8/9] selftests: ovpn: add test for the FW mark feature
2026-03-04 23:06 ` [PATCH net-next 8/9] selftests: ovpn: add test for the FW mark feature Antonio Quartulli
@ 2026-03-06 3:22 ` Jakub Kicinski
0 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2026-03-06 3:22 UTC (permalink / raw)
To: Antonio Quartulli
Cc: netdev, Ralf Lici, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, Shuah Khan, linux-kselftest, horms
On Thu, 5 Mar 2026 00:06:26 +0100 Antonio Quartulli wrote:
> Add a selftest to verify that the FW mark socket option is correctly
> supported and its value propagated by ovpn.
>
> The test adds and removes nftables DROP rules based on the mark value,
> and checks that the rule counter aligns with the number of lost ping
> packets.
This test does not work for us:
TAP version 13
1..1
# overriding timeout to 240
# selftests: net/ovpn: test-mark.sh
# Creating interface tun0 with mode 1
# Creating interface tun1 with mode 0
# Creating interface tun2 with mode 0
# Creating interface tun3 with mode 0
# cannot open file: any
# Cannot execute command: Operation not permitted (-1)
not ok 1 selftests: net/ovpn: test-mark.sh # exit=255
kernel config file:
https://netdev-ctrl.bots.linux.dev/logs/vmksft/net-extra-dbg/results/545921/config
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching
2026-03-06 3:17 ` Jakub Kicinski
@ 2026-03-06 13:05 ` Antonio Quartulli
2026-03-06 20:57 ` Jakub Kicinski
0 siblings, 1 reply; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-06 13:05 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, Ralf Lici, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, linux-kselftest, shuah, horms
On 06/03/2026 04:17, Jakub Kicinski wrote:
> On Thu, 5 Mar 2026 00:06:23 +0100 Antonio Quartulli wrote:
>> .../selftests/net/ovpn/check_requirements.py | 47 +++++++++++++++++++
>
> this file is not included in the Makefile, it should probably be under
> TEST_FILES ? otherwise when tests are installed with make install this
> file won't be copied
Right, will do.
>
>> +if has_listener_requirements; then
>> + for p in $(seq 0 ${NUM_PEERS}); do
>> + setup_listener ${p}
>> + done
>> +fi
>
> There should probably be an else branch which prints some kind of
> echo "SKIP - requirements missing"
>
> Your requirements.py script prints stuff but the output needs to
> contain the word SKIP
If "has_listener_requirements" is false, the test script still goes on,
but it won't check the received netlink notification content.
So we're not really skipping the whole test, just one specific check
that requires extra tooling (which is normally found on all standard
distros).
Do you think we should rather SKIP the whole test so that we can easily
spot that something is missing?
Regards,
--
Antonio Quartulli
OpenVPN Inc.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 7/9] selftests: ovpn: check asymmetric peer-id
2026-03-06 3:20 ` Jakub Kicinski
@ 2026-03-06 13:19 ` Antonio Quartulli
0 siblings, 0 replies; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-06 13:19 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, Ralf Lici, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, Shuah Khan, linux-kselftest, horms
On 06/03/2026 04:20, Jakub Kicinski wrote:
> On Thu, 5 Mar 2026 00:06:25 +0100 Antonio Quartulli wrote:
>> From: Ralf Lici <ralf@mandelbit.com>
>>
>> Extend the base test to verify that the correct peer-id is set in data
>> packet headers. This is done by capturing ping packets with ngrep during
>> the initial exchange and matching the first portion of the header
>> against the expected sequence for every connection.
>
> AI asks
>
>> @@ -2204,15 +2217,15 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
>> if (argc < 7)
>> return -EINVAL;
>
> The CMD_CONNECT case was correctly updated from 'argc < 6' to 'argc < 7'
> for the new tx_id argument. However, was CMD_NEW_PEER also updated?
>
> Looking at the usage text, CMD_NEW_PEER now expects:
> new_peer <iface> <peer_id> <tx_id> <lport> <raddr> <rport> [vpnaddr]
>
> That's argv[0] through argv[7] for 8 required arguments (indices 0-7),
> but the check at the start of CMD_NEW_PEER still requires only argc < 7.
>
> With argc=7, argv[7] would be NULL according to the C standard, so when
> ovpn_parse_new_peer() passes argv[7] as rport and calls getaddrinfo(),
> the NULL service parameter would silently produce port 0 instead of
> the intended port.
>
> Should the check be 'argc < 8' instead?
Right. Some rebase went wrong, sorry about that.
--
Antonio Quartulli
OpenVPN Inc.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching
2026-03-06 13:05 ` Antonio Quartulli
@ 2026-03-06 20:57 ` Jakub Kicinski
2026-03-06 21:03 ` Antonio Quartulli
0 siblings, 1 reply; 19+ messages in thread
From: Jakub Kicinski @ 2026-03-06 20:57 UTC (permalink / raw)
To: Antonio Quartulli
Cc: netdev, Ralf Lici, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, linux-kselftest, shuah, horms
On Fri, 6 Mar 2026 14:05:53 +0100 Antonio Quartulli wrote:
> > There should probably be an else branch which prints some kind of
> > echo "SKIP - requirements missing"
> >
> > Your requirements.py script prints stuff but the output needs to
> > contain the word SKIP
>
> If "has_listener_requirements" is false, the test script still goes on,
> but it won't check the received netlink notification content.
>
> So we're not really skipping the whole test, just one specific check
> that requires extra tooling (which is normally found on all standard
> distros).
>
>
> Do you think we should rather SKIP the whole test so that we can easily
> spot that something is missing?
No strong preference. But speaking speaking just from the netdev CI
perspective it's probably best to report SKIP and then keep going.
The system does string matches on the output. If we report FAIL after
the pass it will show as FAIL, if it's SKIP + PASS the report will be
SKIP.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching
2026-03-04 23:06 ` [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching Antonio Quartulli
2026-03-06 3:17 ` Jakub Kicinski
@ 2026-03-06 21:01 ` Jakub Kicinski
2026-03-06 21:12 ` Antonio Quartulli
1 sibling, 1 reply; 19+ messages in thread
From: Jakub Kicinski @ 2026-03-06 21:01 UTC (permalink / raw)
To: Antonio Quartulli
Cc: netdev, Ralf Lici, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, linux-kselftest, shuah, horms
On Thu, 5 Mar 2026 00:06:23 +0100 Antonio Quartulli wrote:
> +def check_requirements(requirements_path):
> + """Return dependency issues from requirements_path, or an empty list."""
> + issues = []
> + with open(requirements_path, encoding="utf-8") as f:
Semi-jokingly let me also mention that you do all this requirements
checking here, all the dependencies seem to have been installed on
netdev CI already. One thing for which you don't check is ngrep,
and that was not installed.
IMO the requirements checking is a waste of effort, things always
slip thru. But unless you have a strong reason to use ngrep maybe
switch to a more standard tool?
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching
2026-03-06 20:57 ` Jakub Kicinski
@ 2026-03-06 21:03 ` Antonio Quartulli
0 siblings, 0 replies; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-06 21:03 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, Ralf Lici, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, linux-kselftest, shuah, horms
On 06/03/2026 21:57, Jakub Kicinski wrote:
> On Fri, 6 Mar 2026 14:05:53 +0100 Antonio Quartulli wrote:
>>> There should probably be an else branch which prints some kind of
>>> echo "SKIP - requirements missing"
>>>
>>> Your requirements.py script prints stuff but the output needs to
>>> contain the word SKIP
>>
>> If "has_listener_requirements" is false, the test script still goes on,
>> but it won't check the received netlink notification content.
>>
>> So we're not really skipping the whole test, just one specific check
>> that requires extra tooling (which is normally found on all standard
>> distros).
>>
>>
>> Do you think we should rather SKIP the whole test so that we can easily
>> spot that something is missing?
>
> No strong preference. But speaking speaking just from the netdev CI
> perspective it's probably best to report SKIP and then keep going.
> The system does string matches on the output. If we report FAIL after
> the pass it will show as FAIL, if it's SKIP + PASS the report will be
> SKIP.
Got it.
I agree that reporting SKIP and then keep going is probably the best option.
Will change the script accordingly.
Thanks!
--
Antonio Quartulli
OpenVPN Inc.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching
2026-03-06 21:01 ` Jakub Kicinski
@ 2026-03-06 21:12 ` Antonio Quartulli
2026-03-06 21:31 ` Jakub Kicinski
0 siblings, 1 reply; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-06 21:12 UTC (permalink / raw)
To: Jakub Kicinski
Cc: netdev, Ralf Lici, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, linux-kselftest, shuah, horms
On 06/03/2026 22:01, Jakub Kicinski wrote:
> On Thu, 5 Mar 2026 00:06:23 +0100 Antonio Quartulli wrote:
>> +def check_requirements(requirements_path):
>> + """Return dependency issues from requirements_path, or an empty list."""
>> + issues = []
>> + with open(requirements_path, encoding="utf-8") as f:
>
> Semi-jokingly let me also mention that you do all this requirements
> checking here, all the dependencies seem to have been installed on
> netdev CI already.
as expected :-)
> One thing for which you don't check is ngrep,
> and that was not installed.
yeah, because the script is only checking the python deps.
>
> IMO the requirements checking is a waste of effort, things always
> slip thru.
I am fine with dropping the dep check if you also don't find it of much
value.
We'd have to keep it updated and it won't help much once packages have
been installed the first time.
> But unless you have a strong reason to use ngrep maybe
> switch to a more standard tool?
Any recommendation?
ngrep is used to make sure that received packets start with a specific
pattern (OpenVPN header with certain values in it).
I presume we can just replace ngrep with tcpdump, assuming it's "more
standard".
Regards,
--
Antonio Quartulli
OpenVPN Inc.
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching
2026-03-06 21:12 ` Antonio Quartulli
@ 2026-03-06 21:31 ` Jakub Kicinski
0 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2026-03-06 21:31 UTC (permalink / raw)
To: Antonio Quartulli
Cc: netdev, Ralf Lici, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, linux-kselftest, shuah, horms
On Fri, 6 Mar 2026 22:12:18 +0100 Antonio Quartulli wrote:
> > But unless you have a strong reason to use ngrep maybe
> > switch to a more standard tool?
>
> Any recommendation?
>
> ngrep is used to make sure that received packets start with a specific
> pattern (OpenVPN header with certain values in it).
>
> I presume we can just replace ngrep with tcpdump, assuming it's "more
> standard".
Right, tcpdump probably. FWIW tools/testing/selftests/net/forwarding/lib.sh
has some helpers to deal with tcpdump from bash.
But no big deal, honestly. More of a general guidance to try to stick to
the tools which are already used by other tests.
^ permalink raw reply [flat|nested] 19+ messages in thread
* [PATCH net-next 8/9] selftests: ovpn: add test for the FW mark feature
[not found] <20260310145006.30858-1-antonio@openvpn.net>
@ 2026-03-10 14:50 ` Antonio Quartulli
0 siblings, 0 replies; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-10 14:50 UTC (permalink / raw)
To: netdev
Cc: Ralf Lici, Sabrina Dubroca, Jakub Kicinski, Paolo Abeni,
Andrew Lunn, David S. Miller, Eric Dumazet, Shuah Khan,
linux-kselftest, horms, Antonio Quartulli
From: Ralf Lici <ralf@mandelbit.com>
Add a selftest to verify that the FW mark socket option is correctly
supported and its value propagated by ovpn.
The test adds and removes nftables DROP rules based on the mark value,
and checks that the rule counter aligns with the number of lost ping
packets.
Cc: Shuah Khan <shuah@kernel.org>
Cc: linux-kselftest@vger.kernel.org
Cc: horms@kernel.org
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
tools/testing/selftests/net/ovpn/Makefile | 1 +
tools/testing/selftests/net/ovpn/ovpn-cli.c | 26 ++++-
tools/testing/selftests/net/ovpn/test-mark.sh | 95 +++++++++++++++++++
3 files changed, 120 insertions(+), 2 deletions(-)
create mode 100755 tools/testing/selftests/net/ovpn/test-mark.sh
diff --git a/tools/testing/selftests/net/ovpn/Makefile b/tools/testing/selftests/net/ovpn/Makefile
index 0a673327ba9d..5d17d7330bd5 100644
--- a/tools/testing/selftests/net/ovpn/Makefile
+++ b/tools/testing/selftests/net/ovpn/Makefile
@@ -38,6 +38,7 @@ TEST_PROGS := \
test-close-socket.sh \
test-float.sh \
test-large-mtu.sh \
+ test-mark.sh \
test-tcp.sh \
test.sh \
# end of TEST_PROGS
diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c
index 5b58aca9366c..7f5a3737666c 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>
@@ -133,6 +134,8 @@ struct ovpn_ctx {
enum ovpn_key_slot key_slot;
int key_id;
+ uint32_t mark;
+
const char *peers_file;
};
@@ -521,6 +524,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,
@@ -1693,13 +1705,14 @@ static void usage(const char *cmd)
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> <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,
"\tpeers_file: text file containing one peer per line. Line format:\n");
fprintf(stderr,
- "\t\t<peer_id> <tx_id> <raddr> <rport> <laddr> <lport> <vpnaddr>\n");
+ "\t\t<peer_id> <tx_id> <raddr> <rport> <laddr> <lport> <vpnaddr> [mark]\n");
+ fprintf(stderr, "\tmark: socket FW mark value\n");
fprintf(stderr,
"* set_peer <iface> <peer_id> <keepalive_interval> <keepalive_timeout>: set peer attributes\n");
@@ -2243,6 +2256,15 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
}
ovpn->peers_file = argv[4];
+
+ ovpn->mark = 0;
+ if (argc > 5) {
+ ovpn->mark = strtoul(argv[5], 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/test-mark.sh b/tools/testing/selftests/net/ovpn/test-mark.sh
new file mode 100755
index 000000000000..80fb31b99710
--- /dev/null
+++ b/tools/testing/selftests/net/ovpn/test-mark.sh
@@ -0,0 +1,95 @@
+#!/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 "${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
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next 8/9] selftests: ovpn: add test for the FW mark feature
[not found] <20260313205139.2950-1-antonio@openvpn.net>
@ 2026-03-13 20:51 ` Antonio Quartulli
0 siblings, 0 replies; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-13 20:51 UTC (permalink / raw)
To: netdev
Cc: ralf, Sabrina Dubroca, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, Shuah Khan, linux-kselftest, horms,
Antonio Quartulli
From: Ralf Lici <ralf@mandelbit.com>
Add a selftest to verify that the FW mark socket option is correctly
supported and its value propagated by ovpn.
The test adds and removes nftables DROP rules based on the mark value,
and checks that the rule counter aligns with the number of lost ping
packets.
Cc: Shuah Khan <shuah@kernel.org>
Cc: linux-kselftest@vger.kernel.org
Cc: horms@kernel.org
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
tools/testing/selftests/net/ovpn/Makefile | 1 +
tools/testing/selftests/net/ovpn/ovpn-cli.c | 23 ++++-
tools/testing/selftests/net/ovpn/test-mark.sh | 96 +++++++++++++++++++
3 files changed, 119 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/net/ovpn/test-mark.sh
diff --git a/tools/testing/selftests/net/ovpn/Makefile b/tools/testing/selftests/net/ovpn/Makefile
index ce9f79c4f892..169f0464ac3a 100644
--- a/tools/testing/selftests/net/ovpn/Makefile
+++ b/tools/testing/selftests/net/ovpn/Makefile
@@ -38,6 +38,7 @@ 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 \
diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c
index 085446471397..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>
@@ -133,6 +134,7 @@ struct ovpn_ctx {
enum ovpn_key_slot key_slot;
int key_id;
+ uint32_t mark;
bool asymm_id;
const char *peers_file;
@@ -523,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,
@@ -1704,7 +1715,7 @@ static void usage(const char *cmd)
fprintf(stderr, "\tvpnaddr: peer VPN IP\n");
fprintf(stderr,
- "* new_multi_peer <iface> <lport> <id_type> <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");
@@ -1716,6 +1727,7 @@ static void usage(const char *cmd)
"\tpeers_file: text file containing one peer per line. Line format:\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");
@@ -2284,6 +2296,15 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
}
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/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
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* [PATCH net-next 8/9] selftests: ovpn: add test for the FW mark feature
[not found] <20260317104023.192548-1-antonio@openvpn.net>
@ 2026-03-17 10:40 ` Antonio Quartulli
2026-03-19 19:41 ` Jakub Kicinski
0 siblings, 1 reply; 19+ messages in thread
From: Antonio Quartulli @ 2026-03-17 10:40 UTC (permalink / raw)
To: netdev
Cc: ralf, Sabrina Dubroca, Jakub Kicinski, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, Shuah Khan, linux-kselftest, horms,
Antonio Quartulli
From: Ralf Lici <ralf@mandelbit.com>
Add a selftest to verify that the FW mark socket option is correctly
supported and its value propagated by ovpn.
The test adds and removes nftables DROP rules based on the mark value,
and checks that the rule counter aligns with the number of lost ping
packets.
Cc: Shuah Khan <shuah@kernel.org>
Cc: linux-kselftest@vger.kernel.org
Cc: horms@kernel.org
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
Signed-off-by: Antonio Quartulli <antonio@openvpn.net>
---
tools/testing/selftests/net/ovpn/Makefile | 1 +
tools/testing/selftests/net/ovpn/ovpn-cli.c | 23 ++++-
tools/testing/selftests/net/ovpn/test-mark.sh | 96 +++++++++++++++++++
3 files changed, 119 insertions(+), 1 deletion(-)
create mode 100755 tools/testing/selftests/net/ovpn/test-mark.sh
diff --git a/tools/testing/selftests/net/ovpn/Makefile b/tools/testing/selftests/net/ovpn/Makefile
index ce9f79c4f892..169f0464ac3a 100644
--- a/tools/testing/selftests/net/ovpn/Makefile
+++ b/tools/testing/selftests/net/ovpn/Makefile
@@ -38,6 +38,7 @@ 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 \
diff --git a/tools/testing/selftests/net/ovpn/ovpn-cli.c b/tools/testing/selftests/net/ovpn/ovpn-cli.c
index 085446471397..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>
@@ -133,6 +134,7 @@ struct ovpn_ctx {
enum ovpn_key_slot key_slot;
int key_id;
+ uint32_t mark;
bool asymm_id;
const char *peers_file;
@@ -523,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,
@@ -1704,7 +1715,7 @@ static void usage(const char *cmd)
fprintf(stderr, "\tvpnaddr: peer VPN IP\n");
fprintf(stderr,
- "* new_multi_peer <iface> <lport> <id_type> <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");
@@ -1716,6 +1727,7 @@ static void usage(const char *cmd)
"\tpeers_file: text file containing one peer per line. Line format:\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");
@@ -2284,6 +2296,15 @@ static int ovpn_parse_cmd_args(struct ovpn_ctx *ovpn, int argc, char *argv[])
}
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/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
--
2.52.0
^ permalink raw reply related [flat|nested] 19+ messages in thread
* Re: [PATCH net-next 8/9] selftests: ovpn: add test for the FW mark feature
2026-03-17 10:40 ` Antonio Quartulli
@ 2026-03-19 19:41 ` Jakub Kicinski
0 siblings, 0 replies; 19+ messages in thread
From: Jakub Kicinski @ 2026-03-19 19:41 UTC (permalink / raw)
To: Antonio Quartulli
Cc: netdev, ralf, Sabrina Dubroca, Paolo Abeni, Andrew Lunn,
David S. Miller, Eric Dumazet, Shuah Khan, linux-kselftest, horms
On Tue, 17 Mar 2026 11:40:22 +0100 Antonio Quartulli wrote:
> From: Ralf Lici <ralf@mandelbit.com>
>
> Add a selftest to verify that the FW mark socket option is correctly
> supported and its value propagated by ovpn.
>
> The test adds and removes nftables DROP rules based on the mark value,
> and checks that the rule counter aligns with the number of lost ping
> packets.
Hi! Now that the other fixes are in tree we can stop ignoring ovpn
tests :) This new mark test is failing, looks like netfilter is not
enabled. All CONFIG options you depend on which are not in defconfig
should be listed in tools/testing/selftests/net/ovpn/config
Our CI currently builds this config for ovpn tests:
https://netdev.bots.linux.dev/logs/vmksft/net-extra/results/565721/config
Please add to tools/testing/selftests/net/ovpn/config
what this test needs.
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2026-03-19 19:41 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260304230643.1014-1-antonio@openvpn.net>
2026-03-04 23:06 ` [PATCH net-next 1/9] selftests: ovpn: allow compiling ovpn-cli.c with mbedtls3 Antonio Quartulli
2026-03-04 23:06 ` [PATCH net-next 4/9] ovpn: notify userspace on client float event Antonio Quartulli
2026-03-04 23:06 ` [PATCH net-next 5/9] selftests: ovpn: add notification parsing and matching Antonio Quartulli
2026-03-06 3:17 ` Jakub Kicinski
2026-03-06 13:05 ` Antonio Quartulli
2026-03-06 20:57 ` Jakub Kicinski
2026-03-06 21:03 ` Antonio Quartulli
2026-03-06 21:01 ` Jakub Kicinski
2026-03-06 21:12 ` Antonio Quartulli
2026-03-06 21:31 ` Jakub Kicinski
2026-03-04 23:06 ` [PATCH net-next 7/9] selftests: ovpn: check asymmetric peer-id Antonio Quartulli
2026-03-06 3:20 ` Jakub Kicinski
2026-03-06 13:19 ` Antonio Quartulli
2026-03-04 23:06 ` [PATCH net-next 8/9] selftests: ovpn: add test for the FW mark feature Antonio Quartulli
2026-03-06 3:22 ` Jakub Kicinski
[not found] <20260310145006.30858-1-antonio@openvpn.net>
2026-03-10 14:50 ` Antonio Quartulli
[not found] <20260313205139.2950-1-antonio@openvpn.net>
2026-03-13 20:51 ` Antonio Quartulli
[not found] <20260317104023.192548-1-antonio@openvpn.net>
2026-03-17 10:40 ` Antonio Quartulli
2026-03-19 19:41 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox