From patchwork Tue Oct 27 22:21:10 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 10515 Return-Path: Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519) (Client CN "mail01.haj.ipfire.org", Issuer "YR1" (not verified)) by web04.haj.ipfire.org (Postfix) with ESMTPS id 4jDlMX45HZz3xZk for ; Tue, 27 Oct 2026 22:21:28 +0000 (UTC) Received: from mail02.haj.ipfire.org (mail02.haj.ipfire.org [IPv6:2001:678:b28::201]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519) (Client CN "mail02.haj.ipfire.org", Issuer "YE2" (not verified)) by mail01.ipfire.org (Postfix) with ESMTPS id 4jDlMW2zNrz7BR for ; Tue, 27 Oct 2026 22:21:27 +0000 (UTC) Received: from mail02.haj.ipfire.org (localhost [IPv6:::1]) by mail02.haj.ipfire.org (Postfix) with ESMTP id 4jDlMV3Mj2z36X6 for ; Tue, 27 Oct 2026 22:21:26 +0000 (UTC) X-Original-To: development@lists.ipfire.org Received: from mail01.ipfire.org (mail01.haj.ipfire.org [IPv6:2001:678:b28::25]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519) (Client CN "mail01.haj.ipfire.org", Issuer "YR1" (not verified)) by mail02.haj.ipfire.org (Postfix) with ESMTPS id 4jDlMH440Lz30Gc for ; Tue, 27 Oct 2026 22:21:15 +0000 (UTC) Received: from mout-b-203.mailbox.org (mout-b-203.mailbox.org [IPv6:2001:67c:2050:102:465::203]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bit raw public key) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "*.mailbox.org", Issuer "Thawte TLS RSA CA G1" (verified OK)) by mail01.ipfire.org (Postfix) with ESMTPS id 4jDlMG6Vkmz3Gb for ; Tue, 27 Oct 2026 22:21:14 +0000 (UTC) Authentication-Results: mail01.ipfire.org; dkim=pass header.d=mandelbit.com header.s=MBO0001 header.b=IwDrZzYd; spf=pass (mail01.ipfire.org: domain of antonio@mandelbit.com designates 2001:67c:2050:102:465::203 as permitted sender) smtp.mailfrom=antonio@mandelbit.com; dmarc=none ARC-Seal: i=1; a=rsa-sha256; d=lists.ipfire.org; s=202003rsa; cv=none; t=1793139675; b=O+1fSWxW9lzXdjfRzHdphhpRTDqWLs1RUXhsBNzBL2v3NETYr/+PAzeSdrHdyrtwWmZa/A +DESA5cMZByz56mdPAjXO29RY5hAnOyWhvBEAOoFzj1caxhgHijLzzPg8k04wd2/FWSeCw UBS/27FAnI5/F4yI95l0PB1ZYzTpxfJXkc1AGgBCGKxHwapnd+GkjrMk9QmcTvz+uliIOC 9nXeTsbCiQPRnHZkjpKOOz2WsToeUgaFG3h3IB0hz63YVFGTfOMta4zug22CJyjMLMZeo5 eYoONqdhHZpKipYwJL8PUuHXVuEhxm7Dl67pXS5QRaOQjM/1poW0xaDnKBB91A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=lists.ipfire.org; s=202003rsa; t=1793139675; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=j58qZJCWQrzyAIgF+2lhjSeLjSzQVW0AGdXwb0spUCA=; b=sJM9xKzQqNbO2vkUgywbDD/rXikiyX9LlNE+G/PSF+spQHRDoKsjU2e1IsZZHSrOgAeROL CDNvP9Aw/zLzx0TlRStg7xn2dV5Ap6aES313EEA23fzko565igYe9vzJNMbNo7YSLimQsH vXRsTtG/ZSggYjIZh4j/+WyZL33FKN9LBvMaxN55Ef58Ewj/YdEedvMIqZvoB1gKPC78Ji C2AHZpJ6ZkDKACXRlXjhQ7/0KrmKBIyDZE2bonEV8B/YttwPTecRWI9CvGeAcoLta89keA YHCctomHQ0poxlyY74zJDzCXBsifzfICOg0EkHNeEQcHnQy7MVlFMTlqqnSAIQ== ARC-Authentication-Results: i=1; mail01.ipfire.org; dkim=pass header.d=mandelbit.com header.s=MBO0001 header.b=IwDrZzYd; spf=pass (mail01.ipfire.org: domain of antonio@mandelbit.com designates 2001:67c:2050:102:465::203 as permitted sender) smtp.mailfrom=antonio@mandelbit.com; dmarc=none Received: from smtp1.mailbox.org (smtp1.mailbox.org [IPv6:2001:67c:2050:b231:465::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519MLKEM768 server-signature RSA-PSS (4096 bits) server-digest SHA512) (No client certificate requested) by mout-b-203.mailbox.org (Postfix) with ESMTPS id 4jDlMG200BzLmHG; Tue, 27 Oct 2026 23:21:14 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mandelbit.com; s=MBO0001; t=1793139674; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=j58qZJCWQrzyAIgF+2lhjSeLjSzQVW0AGdXwb0spUCA=; b=IwDrZzYdVGgsNbh7zEQ13XlkSRroJCVZKd8Q3Fydlahvs7/xKbJXNPpViBpzwJfnvidaNe FzSIA45xRQjA2aRwfkfcnY1eVQEXC5ShdJXXZjsU5XjJb7zYnbBpCOaQ7iN9doULV1QT1n M2nOQxkubPpCI4ZIKZp40dS+pAcsLgzsEFPuH9IwW37L0OVoJq3uKMAnSreeUhxz1JQ08Z 0LfhatGEh0xLSthyCtl6k+2zXSyNjpe9/j2GqkoyjpWdGkjb/2mgsEOV4+yGhoIYeClfJU j5k8A51jpOtaFn0JiCMnqiUJyB3dH1Nwn2ITUUHgfZSJVk2FumhSwxik3J6VMg== From: Antonio Quartulli To: development@lists.ipfire.org Cc: Antonio Quartulli Subject: [PATCH 3/3] tests: Add tests for OpenVPN::n2n_status() Date: Tue, 27 Oct 2026 23:21:10 +0100 Message-ID: <20261027222110.2642078-4-antonio@mandelbit.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20261027222110.2642078-1-antonio@mandelbit.com> References: <20261027222110.2642078-1-antonio@mandelbit.com> Precedence: list List-Id: List-Subscribe: , List-Unsubscribe: , List-Post: List-Help: Sender: Mail-Followup-To: MIME-Version: 1.0 X-Rspamd-Action: no action X-Spamd-Result: default: False [-2.20 / 11.00]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; R_MISSING_CHARSET(0.50)[]; R_SPF_ALLOW(-0.20)[+ip6:2001:67c:2050::/48:c]; R_DKIM_ALLOW(-0.10)[mandelbit.com:s=MBO0001]; RCVD_IN_DNSWL_LOW(-0.10)[2001:67c:2050:b231:465::1:received]; MIME_GOOD(-0.10)[text/plain]; MX_GOOD(-0.10)[mxext2.mailbox.org]; R_DKIM_ALIGNED(-0.10)[strict]; ASN(0.00)[asn:199118, ipnet:2001:67c:2050::/48, country:DE]; RCVD_COUNT_ONE(0.00)[1]; TO_DN_SOME(0.00)[]; ARC_SIGNED(0.00)[lists.ipfire.org:s=202003rsa:i=1]; MIME_TRACE(0.00)[0:+]; ARC_NA(0.00)[]; IP_REPUTATION_HAM(0.00)[asn: 199118(-0.31), country: DE(-0.01), ip: 2001:67c:2050:102::(0.00)]; DMARC_NA(0.00)[mandelbit.com]; FROM_EQ_ENVFROM(0.00)[]; FROM_HAS_DN(0.00)[]; DKIM_REPUTATION(0.00)[0]; RCVD_TLS_ALL(0.00)[]; RCPT_COUNT_TWO(0.00)[2]; TO_MATCH_ENVRCPT_SOME(0.00)[]; DKIM_TRACE(0.00)[mandelbit.com:+] X-Rspamd-Server: mail01.haj.ipfire.org X-Rspamd-Queue-Id: 4jDlMG6Vkmz3Gb Run n2n_status() against a fake management interface, including a new session that never completes on an established tunnel (#13897). Signed-off-by: Antonio Quartulli --- .../ovpn-functions.pl/data/auth_pending | 6 ++ .../cfgroot/ovpn-functions.pl/data/connected | 7 ++ .../data/connected_stray_session | 8 ++ .../cfgroot/ovpn-functions.pl/data/empty | 1 + .../cfgroot/ovpn-functions.pl/data/exiting | 7 ++ .../ovpn-functions.pl/data/never_connected | 6 ++ .../ovpn-functions.pl/data/notifications | 11 +++ .../ovpn-functions.pl/data/reconnected | 11 +++ .../ovpn-functions.pl/data/reconnecting | 9 +++ .../cfgroot/ovpn-functions.pl/data/truncated | 3 + .../cfgroot/ovpn-functions.pl/n2n_status.pl | 73 +++++++++++++++++++ .../config/cfgroot/ovpn-functions.pl/test.sh | 70 ++++++++++++++++++ 12 files changed, 212 insertions(+) create mode 100644 tests/config/cfgroot/ovpn-functions.pl/data/auth_pending create mode 100644 tests/config/cfgroot/ovpn-functions.pl/data/connected create mode 100644 tests/config/cfgroot/ovpn-functions.pl/data/connected_stray_session create mode 100644 tests/config/cfgroot/ovpn-functions.pl/data/empty create mode 100644 tests/config/cfgroot/ovpn-functions.pl/data/exiting create mode 100644 tests/config/cfgroot/ovpn-functions.pl/data/never_connected create mode 100644 tests/config/cfgroot/ovpn-functions.pl/data/notifications create mode 100644 tests/config/cfgroot/ovpn-functions.pl/data/reconnected create mode 100644 tests/config/cfgroot/ovpn-functions.pl/data/reconnecting create mode 100644 tests/config/cfgroot/ovpn-functions.pl/data/truncated create mode 100644 tests/config/cfgroot/ovpn-functions.pl/n2n_status.pl create mode 100755 tests/config/cfgroot/ovpn-functions.pl/test.sh diff --git a/tests/config/cfgroot/ovpn-functions.pl/data/auth_pending b/tests/config/cfgroot/ovpn-functions.pl/data/auth_pending new file mode 100644 index 000000000..aca1f1b57 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/data/auth_pending @@ -0,0 +1,6 @@ +1793050400,CONNECTING,,,,,, +1793050417,CONNECTED,SUCCESS,10.8.0.1,127.0.0.1,21195,, +1793050448,AUTH_PENDING,,,,,, +1793054017,RECONNECTING,ping-restart,,,,, +1793054022,WAIT,,,,,, +END diff --git a/tests/config/cfgroot/ovpn-functions.pl/data/connected b/tests/config/cfgroot/ovpn-functions.pl/data/connected new file mode 100644 index 000000000..afd358d07 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/data/connected @@ -0,0 +1,7 @@ +1793050400,CONNECTING,,,,,, +1793050401,WAIT,,,,,, +1793050402,AUTH,,,,,, +1793050403,GET_CONFIG,,,,,, +1793050404,ASSIGN_IP,,10.8.0.1,,,, +1793050417,CONNECTED,SUCCESS,10.8.0.1,127.0.0.1,21195,, +END diff --git a/tests/config/cfgroot/ovpn-functions.pl/data/connected_stray_session b/tests/config/cfgroot/ovpn-functions.pl/data/connected_stray_session new file mode 100644 index 000000000..78a3a5bd5 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/data/connected_stray_session @@ -0,0 +1,8 @@ +1793050400,CONNECTING,,,,,, +1793050401,WAIT,,,,,, +1793050402,AUTH,,,,,, +1793050404,ASSIGN_IP,,10.8.0.1,,,, +1793050417,CONNECTED,SUCCESS,10.8.0.1,127.0.0.1,21195,, +1793054017,AUTH,,,,,, +1793054018,WAIT,,,,,, +END diff --git a/tests/config/cfgroot/ovpn-functions.pl/data/empty b/tests/config/cfgroot/ovpn-functions.pl/data/empty new file mode 100644 index 000000000..dce2c1d51 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/data/empty @@ -0,0 +1 @@ +END diff --git a/tests/config/cfgroot/ovpn-functions.pl/data/exiting b/tests/config/cfgroot/ovpn-functions.pl/data/exiting new file mode 100644 index 000000000..cc3fd5300 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/data/exiting @@ -0,0 +1,7 @@ +1793050400,CONNECTING,,,,,, +1793050401,WAIT,,,,,, +1793050402,AUTH,,,,,, +1793050404,ASSIGN_IP,,10.8.0.1,,,, +1793050417,CONNECTED,SUCCESS,10.8.0.1,127.0.0.1,21195,, +1793054017,EXITING,SIGTERM,,,,, +END diff --git a/tests/config/cfgroot/ovpn-functions.pl/data/never_connected b/tests/config/cfgroot/ovpn-functions.pl/data/never_connected new file mode 100644 index 000000000..d40357abd --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/data/never_connected @@ -0,0 +1,6 @@ +1793050400,CONNECTING,,,,,, +1793050401,WAIT,,,,,, +1793050402,AUTH,,,,,, +1793050462,WAIT,,,,,, +1793050463,AUTH,,,,,, +END diff --git a/tests/config/cfgroot/ovpn-functions.pl/data/notifications b/tests/config/cfgroot/ovpn-functions.pl/data/notifications new file mode 100644 index 000000000..a2db8e956 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/data/notifications @@ -0,0 +1,11 @@ +>HOLD:Waiting for hold release:0 +1793050400,CONNECTING,,,,,, +>STATE:1793050401,WAIT,,,,,, +1793050401,WAIT,,,,,, +1793050402,AUTH,,,,,, +>LOG:1793050403,I,Peer Connection Initiated +1793050417,CONNECTED,SUCCESS,10.8.0.1,127.0.0.1,21195,, +>STATE:1793054017,AUTH,,,,,, +1793054017,AUTH,,,,,, +1793054018,WAIT,,,,,, +END diff --git a/tests/config/cfgroot/ovpn-functions.pl/data/reconnected b/tests/config/cfgroot/ovpn-functions.pl/data/reconnected new file mode 100644 index 000000000..3b1530458 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/data/reconnected @@ -0,0 +1,11 @@ +1793050400,CONNECTING,,,,,, +1793050401,WAIT,,,,,, +1793050402,AUTH,,,,,, +1793050404,ASSIGN_IP,,10.8.0.1,,,, +1793050417,CONNECTED,SUCCESS,10.8.0.1,127.0.0.1,21195,, +1793054017,RECONNECTING,ping-restart,,,,, +1793054022,WAIT,,,,,, +1793054023,AUTH,,,,,, +1793054024,GET_CONFIG,,,,,, +1793054025,CONNECTED,SUCCESS,10.8.0.1,127.0.0.1,21195,, +END diff --git a/tests/config/cfgroot/ovpn-functions.pl/data/reconnecting b/tests/config/cfgroot/ovpn-functions.pl/data/reconnecting new file mode 100644 index 000000000..97c0d32d6 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/data/reconnecting @@ -0,0 +1,9 @@ +1793050400,CONNECTING,,,,,, +1793050401,WAIT,,,,,, +1793050402,AUTH,,,,,, +1793050404,ASSIGN_IP,,10.8.0.1,,,, +1793050417,CONNECTED,SUCCESS,10.8.0.1,127.0.0.1,21195,, +1793054017,RECONNECTING,ping-restart,,,,, +1793054022,WAIT,,,,,, +1793054023,AUTH,,,,,, +END diff --git a/tests/config/cfgroot/ovpn-functions.pl/data/truncated b/tests/config/cfgroot/ovpn-functions.pl/data/truncated new file mode 100644 index 000000000..a4137f117 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/data/truncated @@ -0,0 +1,3 @@ +1793050417,AUTH,,,,,, +1793050418,WAIT,,,,,, +END diff --git a/tests/config/cfgroot/ovpn-functions.pl/n2n_status.pl b/tests/config/cfgroot/ovpn-functions.pl/n2n_status.pl new file mode 100644 index 000000000..5876ef2f0 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/n2n_status.pl @@ -0,0 +1,73 @@ +#!/usr/bin/perl +# +# Runs OpenVPN::n2n_status() against a fake OpenVPN management interface +# and prints the result. +# +# Usage: n2n_status.pl +# +# The fake management interface sends the banner, waits for "state all" +# and replies with the content of the data file. + +use strict; +use warnings; +use IO::Socket::INET; + +my ($functions, $mode) = @ARGV; + +require $functions; + +my $server = IO::Socket::INET->new( + LocalAddr => "127.0.0.1", + LocalPort => 0, + Proto => "tcp", + Listen => 1, + ReuseAddr => 1, +) or die "Could not create socket: $!\n"; + +my $port = $server->sockport(); + +# Nothing listening on the port +if ($mode eq "--no-server") { + $server->close(); + print OpenVPN::n2n_status($port) . "\n"; + exit 0; +} + +my $pid = fork() // die "Could not fork: $!\n"; + +if ($pid == 0) { + my $client = $server->accept() or exit 1; + + # Close the connection right away + if ($mode eq "--close") { + $client->close(); + exit 0; + } + + open(my $fh, "<", $mode) or die "Could not open $mode: $!\n"; + my @reply = <$fh>; + close($fh); + + $client->autoflush(1); + print $client ">INFO:OpenVPN Management Interface Version 5 -- type 'help' for more info\r\n"; + + while (my $line = <$client>) { + last if ($line =~ m/^state all\r?\n/); + } + + foreach my $line (@reply) { + chomp($line); + print $client "$line\r\n"; + } + + # Wait for the client to disconnect + while (<$client>) {} + + exit 0; +} + +$server->close(); + +print OpenVPN::n2n_status($port) . "\n"; + +waitpid($pid, 0); diff --git a/tests/config/cfgroot/ovpn-functions.pl/test.sh b/tests/config/cfgroot/ovpn-functions.pl/test.sh new file mode 100755 index 000000000..2110cf4f7 --- /dev/null +++ b/tests/config/cfgroot/ovpn-functions.pl/test.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +SCRIPT_PATH="$(dirname "$(readlink -f "$0")")" + +ROOT="$(readlink -f "${SCRIPT_PATH}/../../../..")" + +. ${ROOT}/tests/lib.sh + +n2n_status() { + perl "${SCRIPT_PATH}/n2n_status.pl" "${ROOT}/config/cfgroot/ovpn-functions.pl" "$@" +} + +# Run n2n_status() against a fake management interface replying with the given +# state history and check the result +test_n2n_status() { + local data="${1}" + local expected="${2}" + local status + + status="$(n2n_status "${data}")" + + if [[ "${status}" == "${expected}" ]]; then + log_test_succeded "n2n_status() returns '${status}' for '${data##*/}'" + return 0 + else + log_test_failed "n2n_status() returns '${status}' for '${data##*/}' and not '${expected}'" + return 1 + fi +} + +if ! perl -MNet::Telnet -e 1 &>/dev/null; then + log_test_failed "The perl module Net::Telnet is not installed" + exit 1 +fi + +# The tunnel came up +test_n2n_status "${SCRIPT_PATH}/data/connected" "CONNECTED" + +# A new session that never completes does not affect the tunnel (#13897) +test_n2n_status "${SCRIPT_PATH}/data/connected_stray_session" "CONNECTED" + +# Notifications are ignored +test_n2n_status "${SCRIPT_PATH}/data/notifications" "CONNECTED" + +# The tunnel went down and came back up +test_n2n_status "${SCRIPT_PATH}/data/reconnected" "CONNECTED" + +# The process has been running for a long time +test_n2n_status "${SCRIPT_PATH}/data/truncated" "CONNECTED" + +# The tunnel went down +test_n2n_status "${SCRIPT_PATH}/data/reconnecting" "DISCONNECTED" + +# The tunnel never came up +test_n2n_status "${SCRIPT_PATH}/data/never_connected" "DISCONNECTED" + +# A state containing "END" does not end the history early +test_n2n_status "${SCRIPT_PATH}/data/auth_pending" "DISCONNECTED" + +# The process is exiting +test_n2n_status "${SCRIPT_PATH}/data/exiting" "DISCONNECTED" + +# No states at all +test_n2n_status "${SCRIPT_PATH}/data/empty" "DISCONNECTED" + +# The management interface closes the connection +test_n2n_status --close "DISCONNECTED" + +# The management interface is not running +test_n2n_status --no-server "DISCONNECTED"