[1/3] ovpn-functions.pl: Add a helper to get the status of N2N connections
Commit Message
OpenVPN switches the management state to AUTH and WAIT when a new
session is started while a tunnel is up, and keeps it there if that
session never completes (e.g. a stray packet from the internet), although
the tunnel keeps working.
Therefore determine the status from the state history: an N2N connection
is up if it has reached CONNECTED and has not restarted since.
Signed-off-by: Antonio Quartulli <antonio@mandelbit.com>
---
config/cfgroot/ovpn-functions.pl | 96 ++++++++++++++++++++++++++++++
config/rootfiles/common/configroot | 1 +
lfs/configroot | 1 +
3 files changed, 98 insertions(+)
create mode 100644 config/cfgroot/ovpn-functions.pl
new file mode 100644
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+###############################################################################
+# #
+# IPFire.org - A linux based firewall #
+# Copyright (C) 2026 Antonio Quartulli <antonio@mandelbit.com> #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###############################################################################
+
+package OpenVPN;
+
+use strict;
+use Net::Telnet;
+
+# Returns "CONNECTED" or "DISCONNECTED" for the N2N connection whose
+# management interface listens on the given port.
+#
+# The current state alone cannot be trusted: when a new session is started
+# while the tunnel is up (a peer that gives up, or just a stray packet from
+# the internet), OpenVPN switches the state to AUTH and WAIT and keeps it
+# there if that session never completes, although the tunnel keeps working.
+#
+# Therefore we look at the state history: the tunnel is up if the process
+# has reached CONNECTED and has not restarted since. A tunnel that goes down
+# is detected by ping-restart, which logs RECONNECTING.
+sub n2n_status($) {
+ my $port = shift;
+
+ # Create a new Telnet session
+ my $telnet = new Net::Telnet(
+ Port => $port,
+ Timeout => 3,
+ Errmode => "return",
+ );
+
+ # Connect
+ $telnet->open("127.0.0.1") or return "DISCONNECTED";
+
+ # Fetch the state history
+ my @output = $telnet->cmd(
+ String => "state all",
+ Prompt => "/(?:^|\n)(?:END|ERROR:.*)\n/"
+ );
+
+ $telnet->close();
+
+ # Collect all states, skipping the banner and any notifications
+ my @states;
+
+ foreach my $line (@output) {
+ if ($line =~ m/^\d+,([A-Z_]+),/) {
+ push(@states, $1);
+ }
+ }
+
+ return "DISCONNECTED" unless (@states);
+
+ # The history always starts with CONNECTING, unless it has been truncated.
+ # A truncated history comes from a process that has not been restarted
+ # for a long time, which does not happen to an unconnected tunnel
+ # because of ping-restart. OpenVPN keeps the last 100 states only, so
+ # ~50 stray packets within one ping-restart interval after a restart
+ # could still make a down tunnel look connected.
+ my $connected = ($states[0] ne "CONNECTING");
+
+ foreach my $state (@states) {
+ if ($state eq "CONNECTED") {
+ $connected = 1;
+
+ # The process starts (CONNECTING, logged once), restarts
+ # (RECONNECTING) or exits (EXITING)
+ } elsif ($state =~ m/^(CONNECTING|RECONNECTING|EXITING)$/) {
+ $connected = 0;
+ }
+
+ # Any other state (WAIT, AUTH, ...) is either part of the initial
+ # connection or an attempt to start a new session that does not
+ # affect the existing one
+ }
+
+ return $connected ? "CONNECTED" : "DISCONNECTED";
+}
+
+1;
@@ -138,6 +138,7 @@ var/ipfire/network-functions.pl
var/ipfire/optionsfw
#var/ipfire/optionsfw/settings
var/ipfire/ovpn
+var/ipfire/ovpn-functions.pl
#var/ipfire/ovpn/ccd.conf
#var/ipfire/ovpn/ccdroute
#var/ipfire/ovpn/ccdroute2
@@ -82,6 +82,7 @@ $(TARGET) :
cp $(DIR_SRC)/config/cfgroot/location-functions.pl $(CONFIG_ROOT)/
cp $(DIR_SRC)/config/cfgroot/ipblocklist-functions.pl $(CONFIG_ROOT)/
cp $(DIR_SRC)/config/cfgroot/ids-functions.pl $(CONFIG_ROOT)/
+ cp $(DIR_SRC)/config/cfgroot/ovpn-functions.pl $(CONFIG_ROOT)/
cp $(DIR_SRC)/config/cfgroot/wireguard-functions.pl $(CONFIG_ROOT)/
cp $(DIR_SRC)/config/cfgroot/lang.pl $(CONFIG_ROOT)/
cp $(DIR_SRC)/config/cfgroot/countries.pl $(CONFIG_ROOT)/