From ad3b09bc0389cb2e67956abc895575815324e4b4 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Fri, 30 Oct 2026 17:43:49 +0100 Subject: ipsec: T9542: replace removed ipsec script with swanctl and systemctl strongSwan 6.x no longer ships the legacy ipsec starter script, so "show vpn debug", "show vpn debug peer" and "show vpn ipsec status" failed with "ipsec: command not found", and QAT acceleration could not stop or start the IPsec daemon. Debug output and status now come from swanctl, filtered per IKE connection and child SA, and QAT controls strongswan.service via systemctl. Debugging a peer without any IPsec configuration now reports the peer as not found instead of raising a traceback. --- src/conf_mode/system_acceleration.py | 4 ++-- src/op_mode/vpn_ipsec.py | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/conf_mode/system_acceleration.py b/src/conf_mode/system_acceleration.py index 3e7a06465..22dd97596 100755 --- a/src/conf_mode/system_acceleration.py +++ b/src/conf_mode/system_acceleration.py @@ -51,9 +51,9 @@ def get_config(config=None): def vpn_control(action, force_ipsec=False): # XXX: Should these commands report failure? if action == 'restore' and force_ipsec: - return run('ipsec start') + return run('systemctl start strongswan.service') - return run(f'ipsec {action}') + return run(f'systemctl {action} strongswan.service') def verify(qat): diff --git a/src/op_mode/vpn_ipsec.py b/src/op_mode/vpn_ipsec.py index 3d7049a14..f0a42922c 100755 --- a/src/op_mode/vpn_ipsec.py +++ b/src/op_mode/vpn_ipsec.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import os import re import argparse @@ -25,6 +26,8 @@ SWANCTL_CONF = '/etc/swanctl/swanctl.conf' def get_peer_connections(peer, tunnel, return_all = False): search = rf'^[\s]*({peer}-(tunnel-[\d]+|vti))[\s]*{{' matches = [] + if not os.path.exists(SWANCTL_CONF): + return matches with open(SWANCTL_CONF, 'r') as f: for line in f.readlines(): result = re.match(search, line) @@ -39,7 +42,7 @@ def debug_peer(peer, tunnel): peer = peer.replace(':', '-') if not peer or peer == "all": debug_commands = [ - "ipsec statusall", + "swanctl --stats", "swanctl -L", "swanctl -l", "swanctl -P", @@ -65,9 +68,9 @@ def debug_peer(peer, tunnel): print('Peer not found, aborting') return + call(f'swanctl --list-conns --ike {peer}') for conn in conns: - command = f'/usr/sbin/ipsec statusall | grep {conn}' - call(command) + call(f'swanctl --list-sas --ike {peer} --child {conn}') if __name__ == '__main__': -- cgit v1.2.3 From 2c510ff2715400057cbfa49e8909f6c7c9e2ec4e Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Fri, 30 Oct 2026 17:43:51 +0100 Subject: ipsec: T9542: handle stopped IPsec daemon in op-mode debug and status With strongswan not running, "show vpn debug" and "show vpn ipsec status" printed the full swanctl usage text for every call, and the section headers of "show vpn debug" all appeared at the end of the output. Both commands now report that the IPsec process is not running and skip the swanctl calls; the kernel state is still dumped. Section headers are flushed so they precede their output. --- op-mode-definitions/vpn-ipsec.xml.in | 2 +- src/op_mode/vpn_ipsec.py | 35 +++++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/op-mode-definitions/vpn-ipsec.xml.in b/op-mode-definitions/vpn-ipsec.xml.in index 6900e3648..a45ad2f27 100644 --- a/op-mode-definitions/vpn-ipsec.xml.in +++ b/op-mode-definitions/vpn-ipsec.xml.in @@ -285,7 +285,7 @@ Show status of IPsec process - sudo /usr/sbin/swanctl --stats + ${vyos_op_scripts_dir}/vpn_ipsec.py --action="status" diff --git a/src/op_mode/vpn_ipsec.py b/src/op_mode/vpn_ipsec.py index f0a42922c..3c812db7b 100755 --- a/src/op_mode/vpn_ipsec.py +++ b/src/op_mode/vpn_ipsec.py @@ -19,8 +19,10 @@ import re import argparse from vyos.utils.process import call +from vyos.utils.process import is_systemd_service_active SWANCTL_CONF = '/etc/swanctl/swanctl.conf' +IPSEC_SERVICE = 'strongswan.service' def get_peer_connections(peer, tunnel, return_all = False): @@ -38,14 +40,28 @@ def get_peer_connections(peer, tunnel, return_all = False): return matches +def show_status(): + if not is_systemd_service_active(IPSEC_SERVICE): + print('IPsec process not running') + return + call('swanctl --stats') + + def debug_peer(peer, tunnel): peer = peer.replace(':', '-') + ipsec_running = is_systemd_service_active(IPSEC_SERVICE) if not peer or peer == "all": - debug_commands = [ - "swanctl --stats", - "swanctl -L", - "swanctl -l", - "swanctl -P", + debug_commands = [] + if ipsec_running: + debug_commands = [ + "swanctl --stats", + "swanctl -L", + "swanctl -l", + "swanctl -P", + ] + else: + print('IPsec process not running') + debug_commands += [ "ip x sa show", "ip x policy show", "ip tunnel show", @@ -55,7 +71,8 @@ def debug_peer(peer, tunnel): "ip route show table 220" ] for debug_cmd in debug_commands: - print(f'\n### {debug_cmd} ###') + # call() output bypasses our buffer, flush to keep the order + print(f'\n### {debug_cmd} ###', flush=True) call(debug_cmd) return @@ -68,6 +85,10 @@ def debug_peer(peer, tunnel): print('Peer not found, aborting') return + if not ipsec_running: + print('IPsec process not running') + return + call(f'swanctl --list-conns --ike {peer}') for conn in conns: call(f'swanctl --list-sas --ike {peer} --child {conn}') @@ -84,3 +105,5 @@ if __name__ == '__main__': if args.action == "vpn-debug": debug_peer(args.name, args.tunnel) + elif args.action == "status": + show_status() -- cgit v1.2.3 From b1a421fa8e2cfd45057e69ee7f5c3db9a7f3d922 Mon Sep 17 00:00:00 2001 From: Christian Breunig Date: Fri, 30 Oct 2026 17:43:53 +0100 Subject: ipsec: T9542: do not error on missing route table 220 in debug output "show vpn debug" ended with "Error: ipv4: FIB table does not exist" whenever strongSwan had not installed any route, as the kernel only creates table 220 with its first route. The section now lists the routes of table 220 when there are any and stays empty otherwise. --- src/op_mode/vpn_ipsec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/op_mode/vpn_ipsec.py b/src/op_mode/vpn_ipsec.py index 3c812db7b..dae8f837f 100755 --- a/src/op_mode/vpn_ipsec.py +++ b/src/op_mode/vpn_ipsec.py @@ -68,7 +68,8 @@ def debug_peer(peer, tunnel): "ip address", "ip rule show", "ip route | head -100", - "ip route show table 220" + # charon creates table 220 only with its first route + "ip route show table all | grep -w 'table 220'" ] for debug_cmd in debug_commands: # call() output bypasses our buffer, flush to keep the order -- cgit v1.2.3