]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
selftests: ovpn: add test for the FW mark feature
authorRalf Lici <ralf@mandelbit.com>
Wed, 19 Nov 2025 10:56:52 +0000 (11:56 +0100)
committerAntonio Quartulli <antonio@openvpn.net>
Tue, 17 Mar 2026 10:09:20 +0000 (11:09 +0100)
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
tools/testing/selftests/net/ovpn/ovpn-cli.c
tools/testing/selftests/net/ovpn/test-mark.sh [new file with mode: 0755]

index ce9f79c4f892a7c1ff9a1b4ee158ab030eaa700f..169f0464ac3a63ceb681b3020382db66d6e01fa4 100644 (file)
@@ -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 \
index 0854464713977e71ba73706a1f04570d4062f67c..d40953375c86fa6074ddbf3d8b01c6f2451d4edb 100644 (file)
@@ -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 (executable)
index 0000000..8534428
--- /dev/null
@@ -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