From: Antonio Quartulli <antonio@mandelbit.com>
To: development@lists.ipfire.org
Cc: Antonio Quartulli <antonio@mandelbit.com>
Subject: [PATCH 3/3] tests: Add tests for OpenVPN::n2n_status()
Date: Tue, 27 Oct 2026 23:21:10 +0100 [thread overview]
Message-ID: <20261027222110.2642078-4-antonio@mandelbit.com> (raw)
In-Reply-To: <20261027222110.2642078-1-antonio@mandelbit.com>
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 <antonio@mandelbit.com>
---
.../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 <ovpn-functions.pl> <data file|--no-server|--close>
+#
+# 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"
--
2.55.0
next prev parent reply other threads:[~2026-10-27 22:21 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 22:21 [PATCH 0/3] OpenVPN: Fix N2N connections shown as DISCONNECTED while up Antonio Quartulli
2026-10-27 22:21 ` [PATCH 1/3] ovpn-functions.pl: Add a helper to get the status of N2N connections Antonio Quartulli
2026-10-27 22:21 ` [PATCH 2/3] ovpnmain.cgi/index.cgi: Use OpenVPN::n2n_status() Antonio Quartulli
2026-10-27 22:21 ` Antonio Quartulli [this message]
2026-10-28 17:28 ` [PATCH 0/3] OpenVPN: Fix N2N connections shown as DISCONNECTED while up Michael Tremer
2026-10-28 19:20 ` Antonio Quartulli
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261027222110.2642078-4-antonio@mandelbit.com \
--to=antonio@mandelbit.com \
--cc=development@lists.ipfire.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox