From patchwork Tue Oct 27 22:21:08 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Antonio Quartulli X-Patchwork-Id: 10514 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 4jDlMX3yTCz3xZj for ; Tue, 27 Oct 2026 22:21:28 +0000 (UTC) Received: from mail02.haj.ipfire.org (mail02.haj.ipfire.org [172.28.1.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 4jDlMW2vC8z6hP 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 4jDlMV3Sxzz36WS 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 4jDlMG6pfPz2xnL for ; Tue, 27 Oct 2026 22:21:14 +0000 (UTC) Received: from mout-b-110.mailbox.org (mout-b-110.mailbox.org [195.10.208.55]) (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 4jDlMG061Lz3kH 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=GGQDjirN; spf=pass (mail01.ipfire.org: domain of antonio@mandelbit.com designates 195.10.208.55 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=1793139674; b=rPHptH/bSRQ/gMz5MXdpKhNWbJsgMlnSyqWeBqM2HeRdKTFGNvCB+DkNEtnIWhzb5IUHF0 3gmuqA6Qm9UBZsbvVdN2kgM6S2MXVBpkkUTIWV08qpnhrDbsDguq1fkQM02Harv/b5x61b 32BwcMMS8+subCjKj4SrbL9pDOCQU7iAaEFE40+24hgi0ZZpbC+/hEnfEEHNCfAJGOjoWH h8idg/n+s7tesmnJvBVZcj1ARkGexOMF1ioPfRc33U8maBfQpCgHPOjfPRKjVTREQrOJEs KWQy2DQIL9RIGY6Hh3L55UfIOCAWC5KE705tobzrJ2gTL8rRObctqORRqfAc8Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=lists.ipfire.org; s=202003rsa; 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:dkim-signature; bh=OeO54wydn+rtyumPpuSFU3hTzP+hzOelLaCn0AtNnY0=; b=RAdFK5cISfk2cYnXYrsXOI7ilycmYn4LOy1ZCcUuxo6YPPsxGI/L2nX6rMP1gGuoZo8u9j RzQ8cMF5zf3CYRtbhKruhUi/5Me7Tmp1HKfe9wZ5bJw2crldFmRKfoTyVSS1/t1xYEqpSB ebAYaB5aqnb6YQn210e65JhP+la9dY/neWkA0VKDMhjzupjQWvF/87fmL66MoIfUC3zY2Z nT6y/cOk0eosGX0NZeNAlsNzK9WoB8+F6ETh9lttwTJiPxidq1fdhVNYF8AZ6flHDWKibU ohSR3WM1Nu3a0A8xMQiCtYXJJ8hV7YPaQTf5O7m2j2tDLIWyqRqjHS59541INg== ARC-Authentication-Results: i=1; mail01.ipfire.org; dkim=pass header.d=mandelbit.com header.s=MBO0001 header.b=GGQDjirN; spf=pass (mail01.ipfire.org: domain of antonio@mandelbit.com designates 195.10.208.55 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 SHA256) (No client certificate requested) by mout-b-110.mailbox.org (Postfix) with ESMTPS id 4jDlMF36mrzNljY; Tue, 27 Oct 2026 23:21:13 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=mandelbit.com; s=MBO0001; t=1793139673; 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=OeO54wydn+rtyumPpuSFU3hTzP+hzOelLaCn0AtNnY0=; b=GGQDjirNBnF2Tuqc59NwrOXnsdgxk3gqeUyJmbT7IhXBUuxbJkWxZGJeNgntnTWNjLTIry UC3PPyk6KwsXBSHnBMqOKIGYqaDDmctaA00xXH2/uPmzExcbDs47QHh1y9F8gpAPGcy60K 2RKTuGhMPbCXgpcIN9yNqALe8QKoh8mdY2kOhWIS3mwUQ8JObfifw+GO4hjJpqVymliGbQ Zp1FEYR6eHiEeKH/7pEV1G4YEArO9cpGtgYYLDFW3KelVHQ5gBhenr9AIvE0xhUiSAWMuo jDHv1agBJN5StEqFBEATj0U6my95lcRvsYGHgtwgsFkVDq/9MigIV99h6VUBAw== From: Antonio Quartulli To: development@lists.ipfire.org Cc: Antonio Quartulli Subject: [PATCH 1/3] ovpn-functions.pl: Add a helper to get the status of N2N connections Date: Tue, 27 Oct 2026 23:21:08 +0100 Message-ID: <20261027222110.2642078-2-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.30 / 11.00]; BAYES_HAM(-3.00)[100.00%]; MID_CONTAINS_FROM(1.00)[]; R_MISSING_CHARSET(0.50)[]; RCVD_IN_DNSWL_LOW(-0.20)[2001:67c:2050:b231:465::1:received,195.10.208.55:from]; R_SPF_ALLOW(-0.20)[+ip4:195.10.208.0/24:c]; R_DKIM_ALLOW(-0.10)[mandelbit.com:s=MBO0001]; 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:195.10.208.0/24, 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: 195.10.208.55(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: 4jDlMG061Lz3kH 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 --- 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 diff --git a/config/cfgroot/ovpn-functions.pl b/config/cfgroot/ovpn-functions.pl new file mode 100644 index 000000000..3c6c02660 --- /dev/null +++ b/config/cfgroot/ovpn-functions.pl @@ -0,0 +1,96 @@ +#!/usr/bin/perl +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2026 Antonio Quartulli # +# # +# 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 . # +# # +############################################################################### + +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; diff --git a/config/rootfiles/common/configroot b/config/rootfiles/common/configroot index 72e05e865..ffe1ebe72 100644 --- a/config/rootfiles/common/configroot +++ b/config/rootfiles/common/configroot @@ -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 diff --git a/lfs/configroot b/lfs/configroot index 3df566f50..0b14f6501 100644 --- a/lfs/configroot +++ b/lfs/configroot @@ -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)/