testing: Add ikev2/per-cpu-sas-encap-transport scenario

Tests transport mode and UDP encapsulation with random source ports.
Interestingly, the responder always uses the same SA to respond (maybe
due to the cache on the policy).
This commit is contained in:
Tobias Brunner
2025-05-28 16:35:27 +02:00
parent 3b2f8cf282
commit b7d3349000
9 changed files with 229 additions and 0 deletions
@@ -0,0 +1,13 @@
The two hosts <b>moon</b> and <b>sun</b> are configured to use per-CPU
CHILD_SAs in transport mode in order to improve performance. Using the
<b>taskset</b> utility pings are sent from different CPUs triggering the
creation of additional SA via the installed trap policy. The first ping triggers
the creation of a fallback SA that's used while per-CPU SAs are created for
later packets. Note that responses sent from the peer may create per-CPU SAs
that are not triggered by local acquires.
<p>
UDP encapsulation is forced and the special value <b>encap</b> is used when
enabling per-CPU SAs to force random source ports for the outbound SAs.
<p>
The updown script automatically inserts iptables-based firewall rules
that let pass the tunneled traffic.
@@ -0,0 +1,78 @@
moon::swanctl --list-pols --raw 2> /dev/null::net-net.*mode=TRANSPORT local-ts=\[PH_IP_MOON/32] remote-ts=\[PH_IP_SUN/32]::YES
moon::cat /var/log/daemon.log::creating acquire job for policy PH_IP_MOON/32\[icmp/8] === PH_IP_SUN/32\[icmp/8]::YES
moon::cat /var/log/daemon.log::creating acquire job for policy PH_IP_MOON/32\[icmp/8] === PH_IP_SUN/32\[icmp/8].*cpu::NO
moon::swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.1 local-port=4500 local-id=moon.strongswan.org remote-host=192.168.0.2 remote-port=4500 remote-id=sun.strongswan.org initiator=yes.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*state=INSTALLED mode=TRANSPORT.*ESP.*per-cpu-sas=yes.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[PH_IP_MOON/32] remote-ts=\[PH_IP_SUN/32]::YES
sun:: swanctl --list-sas --raw 2> /dev/null::gw-gw.*version=2 state=ESTABLISHED local-host=192.168.0.2 local-port=4500 local-id=sun.strongswan.org remote-host=192.168.0.1 remote-port=4500 remote-id=moon.strongswan.org.*encr-alg=AES_CBC encr-keysize=128 integ-alg=HMAC_SHA2_256_128 prf-alg=PRF_HMAC_SHA2_256 dh-group=CURVE_25519.*child-sas.*net-net.*state=INSTALLED mode=TRANSPORT.*ESP.*per-cpu-sas=yes.*encr-alg=AES_GCM_16 encr-keysize=128.*local-ts=\[PH_IP_SUN/32] remote-ts=\[PH_IP_MOON/32]::YES
# this triggers a per-CPU SA for CPU 1 and creates one for CPU 3 as responder,
# the packet is sent and received via fallback SA
moon::taskset -c 1 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::cat /var/log/daemon.log::creating acquire job for policy PH_IP_MOON/32\[icmp/8] === PH_IP_SUN/32\[icmp/8].*cpu {1}::YES
moon::swanctl --list-sas --child-id 2 --raw 2> /dev/null::child-sas.*net-net.*packets-in=1.*packets-out=1::YES
moon::swanctl --list-sas --child-id 3 --raw 2> /dev/null::child-sas.*net-net.*cpu=1.*packets-in=0.*packets-out=0::YES
moon::swanctl --list-sas --child-id 4 --raw 2> /dev/null::child-sas.*net-net.*cpu=3.*packets-in=0.*packets-out=0::YES
# this takes the per-CPU SA created above, the response most likely the one
# created as responder
moon::taskset -c 1 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::swanctl --list-sas --child-id 2 --raw 2> /dev/null::child-sas.*net-net.*packets-in=1.*packets-out=1::YES
moon::swanctl --list-sas --child-id 3 --raw 2> /dev/null::child-sas.*net-net.*cpu=1.*packets-in=0.*packets-out=1::YES
moon::swanctl --list-sas --child-id 4 --raw 2> /dev/null::child-sas.*net-net.*cpu=3.*packets-in=1.*packets-out=0::YES
# trigger another SA for CPU 0, request is sent via fallback SA, received via sub SA
moon::taskset -c 0 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::cat /var/log/daemon.log::creating acquire job for policy PH_IP_MOON/32\[icmp/8] === PH_IP_SUN/32\[icmp/8].*cpu {0}::YES
moon::swanctl --list-sas --child-id 2 --raw 2> /dev/null::child-sas.*net-net.*packets-in=1.*packets-out=2::YES
moon::swanctl --list-sas --child-id 3 --raw 2> /dev/null::child-sas.*net-net.*cpu=1.*packets-in=0.*packets-out=1::YES
moon::swanctl --list-sas --child-id 4 --raw 2> /dev/null::child-sas.*net-net.*cpu=3.*packets-in=2.*packets-out=0::YES
moon::swanctl --list-sas --child-id 5 --raw 2> /dev/null::child-sas.*net-net.*cpu=0.*packets-in=0.*packets-out=0::YES
# create a final SA for CPU 2, request is sent via fallback SA, response via one of the
# sub SAs (depends on the CPU assignment on the responder as it has to assign multiple
# SAs to the same CPU, so it could be either the one with unique ID 5, as it might
# be the newer one for that CPU, or it could be the original SA with unique ID 4)
moon::taskset -c 2 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::cat /var/log/daemon.log::creating acquire job for policy PH_IP_MOON/32\[icmp/8] === PH_IP_SUN/32\[icmp/8].*cpu {2}::YES
moon::swanctl --list-sas --child-id 2 --raw 2> /dev/null::child-sas.*net-net.*packets-in=1.*packets-out=3::YES
moon::swanctl --list-sas --child-id 3 --raw 2> /dev/null::child-sas.*net-net.*cpu=1.*packets-in=0.*packets-out=1::YES
moon::swanctl --list-sas --child-id 4 --raw 2> /dev/null::child-sas.*net-net.*cpu=3.*packets-in=3.*packets-out=0::YES
moon::swanctl --list-sas --child-id 5 --raw 2> /dev/null::child-sas.*net-net.*cpu=0.*packets-in=0.*packets-out=0::YES
moon::swanctl --list-sas --child-id 6 --raw 2> /dev/null::child-sas.*net-net.*cpu=2.*packets-in=0.*packets-out=0::YES
# all these pings must be sent via their respective SAs, never the fallback SA
moon::taskset -c 0 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::taskset -c 1 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::taskset -c 2 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::taskset -c 3 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::swanctl --list-sas --child-id 2 --raw 2> /dev/null::child-sas.*net-net.*packets-in=1.*packets-out=3::YES
moon::swanctl --list-sas --child-id 3 --raw 2> /dev/null::child-sas.*net-net.*cpu=1.*packets-in=0.*packets-out=2::YES
moon::swanctl --list-sas --child-id 4 --raw 2> /dev/null::child-sas.*net-net.*cpu=3.*packets-in=7.*packets-out=1::YES
moon::swanctl --list-sas --child-id 5 --raw 2> /dev/null::child-sas.*net-net.*cpu=0.*packets-in=0.*packets-out=1::YES
moon::swanctl --list-sas --child-id 6 --raw 2> /dev/null::child-sas.*net-net.*cpu=2.*packets-in=0.*packets-out=1::YES
# rekey an SA as initiator
moon::swanctl --rekey --child-id 5
moon::sleep .5
moon::taskset -c 0 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::swanctl --list-sas --child-id 4 --raw 2> /dev/null::child-sas.*net-net.*cpu=3.*packets-in=8.*packets-out=1::YES
moon::swanctl --list-sas --child-id 7 --raw 2> /dev/null::child-sas.*net-net.*cpu=0.*packets-in=0.*packets-out=1::YES
# rekey an SA as responder
sun::swanctl --rekey --child-id 6
moon::sleep .5
moon::taskset -c 2 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::swanctl --list-sas --child-id 4 --raw 2> /dev/null::child-sas.*net-net.*cpu=3.*packets-in=9.*packets-out=1::YES
moon::swanctl --list-sas --child-id 8 --raw 2> /dev/null::child-sas.*net-net.*cpu=2.*packets-in=0.*packets-out=1::YES
# rekey the fallback SA
moon::swanctl --rekey --child-id 2
moon::sleep .5
moon::swanctl --list-sas --child-id 9 --raw 2> /dev/null::child-sas.*net-net.*packets-in=0.*packets-out=0::YES
# all these pings must be sent via their respective SAs, never the fallback SA and not
# the old ones that remain to process delayed inbound packets
moon::taskset -c 0 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::taskset -c 1 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::taskset -c 2 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::taskset -c 3 ping -c 1 -I PH_IP_MOON PH_IP_SUN::64 bytes from PH_IP_SUN: icmp_.eq=1::YES
moon::swanctl --list-sas --child-id 2 --raw 2> /dev/null::child-sas.*net-net.*state=DELETED.*packets-in=1.*packets-out=3::YES
moon::swanctl --list-sas --child-id 3 --raw 2> /dev/null::child-sas.*net-net.*state=INSTALLED.*cpu=1.*packets-in=0.*packets-out=3::YES
moon::swanctl --list-sas --child-id 4 --raw 2> /dev/null::child-sas.*net-net.*state=INSTALLED.*cpu=3.*packets-in=13.*packets-out=2::YES
moon::swanctl --list-sas --child-id 5 --raw 2> /dev/null::child-sas.*net-net.*state=DELETED.*cpu=0.*packets-in=0.*packets-out=1::YES
moon::swanctl --list-sas --child-id 6 --raw 2> /dev/null::child-sas.*net-net.*state=DELETED.*cpu=2.*packets-in=0.*packets-out=1::YES
moon::swanctl --list-sas --child-id 7 --raw 2> /dev/null::child-sas.*net-net.*state=INSTALLED.*cpu=0.*packets-in=0.*packets-out=2::YES
moon::swanctl --list-sas --child-id 8 --raw 2> /dev/null::child-sas.*net-net.*state=INSTALLED.*cpu=2.*packets-in=0.*packets-out=2::YES
moon::swanctl --list-sas --child-id 9 --raw 2> /dev/null::child-sas.*net-net.*state=INSTALLED.*packets-in=0.*packets-out=0::YES
sun::tcpdump::IP moon.strongswan.org.* > sun.strongswan.org.ipsec-nat-t: UDP-encap: ESP::YES
sun::tcpdump::IP sun.strongswan.org.* > moon.strongswan.org.ipsec-nat-t: UDP-encap: ESP::YES
@@ -0,0 +1,15 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce openssl pem pkcs1 revocation curl kernel-netlink socket-default updown vici
syslog {
daemon {
knl = 2
}
}
}
@@ -0,0 +1,32 @@
connections {
gw-gw {
local_addrs = 192.168.0.1
remote_addrs = 192.168.0.2
local {
auth = pubkey
certs = moonCert.pem
id = moon.strongswan.org
}
remote {
auth = pubkey
id = sun.strongswan.org
}
children {
net-net {
mode = transport
per_cpu_sas = encap
start_action = trap
updown = /usr/local/libexec/ipsec/_updown iptables
hostaccess = yes
esp_proposals = aes128gcm128-x25519
}
}
version = 2
mobike = no
encap = yes
proposals = aes128-sha256-x25519
}
}
@@ -0,0 +1,15 @@
# /etc/strongswan.conf - strongSwan configuration file
swanctl {
load = pem pkcs1 revocation constraints pubkey openssl random
}
charon-systemd {
load = random nonce openssl pem pkcs1 revocation curl kernel-netlink socket-default updown vici
syslog {
daemon {
knl = 2
}
}
}
@@ -0,0 +1,32 @@
connections {
gw-gw {
local_addrs = 192.168.0.2
remote_addrs = 192.168.0.1
local {
auth = pubkey
certs = sunCert.pem
id = sun.strongswan.org
}
remote {
auth = pubkey
id = moon.strongswan.org
}
children {
net-net {
mode = transport
per_cpu_sas = encap
start_action = trap
updown = /usr/local/libexec/ipsec/_updown iptables
hostaccess = yes
esp_proposals = aes128gcm128-x25519
}
}
version = 2
mobike = no
encap = yes
proposals = aes128-sha256-x25519
}
}
@@ -0,0 +1,5 @@
moon::swanctl --terminate --ike gw-gw 2> /dev/null
moon::systemctl stop strongswan
sun::systemctl stop strongswan
moon::iptables-restore < /etc/iptables.flush
sun::iptables-restore < /etc/iptables.flush
@@ -0,0 +1,14 @@
sun::iptables-restore < /etc/iptables.rules
moon::iptables-restore < /etc/iptables.rules
sun::systemctl start strongswan
moon::systemctl start strongswan
sun::expect-connection gw-gw
moon::expect-connection gw-gw
# allow UDP encap from any source to 4500
moon::iptables -A INPUT -i eth0 -p udp --dport 4500 -j ACCEPT
moon::iptables -A OUTPUT -o eth0 -p udp --dport 4500 -j ACCEPT
sun:: iptables -A INPUT -i eth0 -p udp --dport 4500 -j ACCEPT
sun:: iptables -A OUTPUT -o eth0 -p udp --dport 4500 -j ACCEPT
# trigger the creation of the fallback SA, use -I so no UDP socket is used to
# determine the source address and trigger the acqurie (this packet is lost)
moon::ping -c 1 -W 1 -I PH_IP_MOON PH_IP_SUN
+25
View File
@@ -0,0 +1,25 @@
#!/bin/bash
#
# This configuration file provides information on the
# guest instances used for this test
# All guest instances that are required for this test
#
VIRTHOSTS="alice moon winnetou sun bob"
# Corresponding block diagram
#
DIAGRAM="a-m-w-s-b.png"
# Guest instances on which tcpdump is to be started
#
TCPDUMPHOSTS="sun"
# Guest instances on which IPsec is started
# Used for IPsec logging purposes
#
IPSECHOSTS="moon sun"
# charon controlled by swanctl
#
SWANCTL=1