この記事は「PQC Advent Calendar 2025」の 24 日目です。
※この記事はAIによって生成されたコンテンツを含みます。スクリーンショット、コマンド出力、ログ、トレースなどには含みません。
本記事では、Ubuntu 25.10(questing)上でビルドした strongSwan 6.0.3 を用いて、RFC9272 および RFC9370 に準拠した PQC IPsec が動作することを確認します。
また、Wireshark による IKE_INTERMEDIATE のパケットについても確認します。
背景
PQC 時代の IPsec については、こちらの記事でご確認ください。
PQC 時代の IPsec を比較する : RFC8784 / RFC9242 + RFC9370
検証環境
OS / strongSwan
-
OS:Ubuntu 25.10
- UTM 4.7.4
- M1 MacBook Pro (14inch, 2021)
- strongSwan:6.0.3(ビルド)
strongSwan 6.0.3 のインストール・ビルド
strongSwan 6.0.0 以降をインストールするために、ソースからビルドします。
git clone https://github.com/strongswan/strongswan.git
cd strongswan/
./autogen.sh
./configure --prefix=/usr --sysconfdir=/etc --enable-swanctl --enable-openssl --enable-kem --enable-mlkem --enable-systemd --disable-stroke --enable-pki --enable-pem --enable-random --enable-constraints --enable-xauth-generic
make -j$(nproc)
sudo make install
sudo systemctl daemon-reload
sudo systemctl restart strongswan
対応アルゴリズム確認
swanctl --list-algs の鍵交換アルゴリズムの項目で ML_KEM がサポートされていることを確認します。
(snip)
ke:
MODP_3072[openssl]
MODP_4096[openssl]
MODP_6144[openssl]
MODP_8192[openssl]
MODP_2048[openssl]
MODP_2048_224[openssl]
MODP_2048_256[openssl]
MODP_1536[openssl]
MODP_1024[openssl]
MODP_1024_160[openssl]
MODP_768[openssl]
MODP_CUSTOM[openssl]
ML_KEM_512[openssl]
ML_KEM_768[openssl]
ML_KEM_1024[openssl]
ECP_256[openssl]
ECP_384[openssl]
ECP_521[openssl]
ECP_224[openssl]
ECP_192[openssl]
ECP_256_BP[openssl]
ECP_384_BP[openssl]
ECP_512_BP[openssl]
ECP_224_BP[openssl]
CURVE_25519[openssl]
CURVE_448[openssl]
設定
ネットワーク名前空間の設定
複数ノードを用意できないため、Ubuntu 上で Linux Network Namespace (ns) を利用して、IPsec 接続を試行します。
また、仕様上、複数 ns で同一 charon を共有することを前提とします。
なお、各インターフェースの MTU を 9000 byte まで拡張していますが、フラグメントを回避するため(後述)です。
sudo ip netns add ipsec1
sudo ip netns add router
sudo ip netns add ipsec2
sudo ip link add ipsec1-veth0 type veth peer name gw-veth0
sudo ip link add ipsec2-veth0 type veth peer name gw-veth1
sudo ip link set ipsec1-veth0 netns ipsec1
sudo ip link set gw-veth0 netns router
sudo ip link set gw-veth1 netns router
sudo ip link set ipsec2-veth0 netns ipsec2
sudo ip netns exec ipsec1 ip address add 192.0.2.1/24 dev ipsec1-veth0
sudo ip netns exec ipsec1 ip addr add 203.0.113.1/32 dev lo
sudo ip netns exec ipsec2 ip address add 198.51.100.1/24 dev ipsec2-veth0
sudo ip netns exec ipsec2 ip addr add 203.0.113.2/32 dev lo
sudo ip netns exec router ip address add 192.0.2.254/24 dev gw-veth0
sudo ip netns exec router ip address add 198.51.100.254/24 dev gw-veth1
sudo ip netns exec ipsec1 ip link set dev ipsec1-veth0 address 00:00:5E:00:53:11
sudo ip netns exec ipsec2 ip link set dev ipsec2-veth0 address 00:00:5E:00:53:22
sudo ip netns exec router ip link set dev gw-veth0 address 00:00:5E:00:53:12
sudo ip netns exec router ip link set dev gw-veth1 address 00:00:5E:00:53:21
sudo ip netns exec ipsec1 ip link set dev ipsec1-veth0 mtu 9000
sudo ip netns exec router ip link set dev gw-veth0 mtu 9000
sudo ip netns exec router ip link set dev gw-veth1 mtu 9000
sudo ip netns exec ipsec2 ip link set dev ipsec2-veth0 mtu 9000
sudo ip netns exec ipsec1 ip link set ipsec1-veth0 up
sudo ip netns exec ipsec1 ip link set lo up
sudo ip netns exec router ip link set gw-veth0 up
sudo ip netns exec router ip link set gw-veth1 up
sudo ip netns exec ipsec2 ip link set ipsec2-veth0 up
sudo ip netns exec ipsec2 ip link set lo up
sudo ip netns exec ipsec1 ip route add default via 192.0.2.254
sudo ip netns exec ipsec2 ip route add default via 198.51.100.254
sudo ip netns exec router sysctl net.ipv4.ip_forward=1
strongSwan 設定ディレクトリ
ns 毎異なる設定を読み込むため、それぞれのディレクトリを作成、バインドマウントします。
sudo mkdir -p /etc/ipsec.d/run
sudo mkdir -p /etc/netns/ipsec1/ipsec.d/run
sudo mkdir -p /etc/netns/ipsec2/ipsec.d/run
sudo mkdir -p /etc/netns/ipsec1/swanctl
sudo mkdir -p /etc/netns/ipsec2/swanctl
sudo mkdir -p /etc/swanctl/ipsec1/conf.d
sudo mkdir -p /etc/swanctl/ipsec2/conf.d
sudo mount --bind /etc/ipsec.d/run /etc/netns/ipsec1/ipsec.d/run
sudo mount --bind /etc/ipsec.d/run /etc/netns/ipsec2/ipsec.d/run
sudo mount --bind /etc/swanctl/ipsec1 /etc/netns/ipsec1/swanctl
sudo mount --bind /etc/swanctl/ipsec2 /etc/netns/ipsec2/swanctl
strongSwan 設定
/etc/swanctl/ipsec1/conf.d/ipsec1.conf:
connections {
ipsec1-ipsec2 {
version = 2
fragmentation = no
local_addrs = 192.0.2.1
remote_addrs = 198.51.100.1
# IKEv2 proposal:
# - AES-256-GCM
# - PRF SHA-384
# - 1st KE: ecp384 (ECDH)
# - 2nd KE: mlkem1024 (ML-KEM, RFC9370+9242 で IKE_INTERMEDIATE を使う)
proposals = aes256gcm16-prfsha384-x25519-ke1_mlkem1024
rekey_time = 1h
dpd_delay = 30s
dpd_timeout = 120s
local {
auth = psk
id = ipsec1
}
remote {
auth = psk
id = ipsec2
}
children {
net {
mode = tunnel
local_ts = 203.0.113.1/32
remote_ts = 203.0.113.2/32
# ESP 側も PFS + hybrid KE
esp_proposals = aes256gcm16-x25519-ke1_mlkem1024
start_action = start
dpd_action = restart
}
}
}
}
secrets {
ike-psk {
id-0 = ipsec1
id-1 = ipsec2
secret = "foo-bar-hoge-fuga"
}
}
/etc/swanctl/ipsec2/conf.d/ipsec2.conf:
connections {
ipsec2-ipsec1 {
version = 2
fragmentation = no
local_addrs = 198.51.100.1
remote_addrs = 192.0.2.1
proposals = aes256gcm16-prfsha384-x25519-ke1_mlkem1024
rekey_time = 1h
dpd_delay = 30s
dpd_timeout = 120s
local {
auth = psk
id = ipsec2
}
remote {
auth = psk
id = ipsec1
}
children {
net {
mode = tunnel
local_ts = 203.0.113.2/32
remote_ts = 203.0.113.1/32
esp_proposals = aes256gcm16-x25519-ke1_mlkem1024
start_action = trap
dpd_action = restart
}
}
}
}
secrets {
ike-psk {
id-0 = ipsec1
id-1 = ipsec2
secret = "foo-bar-hoge-fuga"
}
}
strongSwan デーモン起動 & 設定ロード
systemd 経由で起動すると netns の設定がロードできないため事前に停止してから各 ns で charon-systemd を起動します。
sudo systemctl stop strongswan
sudo systemctl disable strongswan
sudo ip netns exec ipsec2 charon-systemd &
sleep 1
sudo ip netns exec ipsec2 swanctl --load-all
sudo ip netns exec ipsec1 charon-systemd &
sleep 1
sudo ip netns exec ipsec1 swanctl --load-all
接続施行
パケットキャプチャ & テストトラフィック発生
IPsec 関連のパケットに限定して、パケットキャプチャを実施し、テストトラフィックとして ICMP パケットを発生させます。
sudo ip netns exec ipsec1 tcpdump -i ipsec1-veth0 -nn -vv -w ipsec1.pcap '(udp port 500 or udp port 4500 or esp)' &
sudo ip netns exec ipsec1 ping -c 3 203.0.113.2 -I 203.0.113.1
接続確認
IKE SA で PQ/T Hybrid KEX CURVE_25519/KE1_ML_KEM_1024 が使われていることを確認できます。
sudo ip netns exec ipsec1 swanctl --list-sas
ipsec1-ipsec2: #1, ESTABLISHED, IKEv2, d3aaa4ac1e7dc15b_i* 1d16d7f3431e6e1d_r
local 'ipsec1' @ 192.0.2.1[4500]
remote 'ipsec2' @ 198.51.100.1[4500]
AES_GCM_16-256/PRF_HMAC_SHA2_384/CURVE_25519/KE1_ML_KEM_1024
established 17s ago, rekeying in 3335s
net: #1, reqid 1, INSTALLED, TUNNEL, ESP:AES_GCM_16-256
installed 17s ago, rekeying in 3314s, expires in 3943s
in cd809f41, 252 bytes, 3 packets, 7s ago
out ccac11d7, 252 bytes, 3 packets, 7s ago
local 203.0.113.1/32
remote 203.0.113.2/32
iniciator spi: d3aaa4ac1e7dc15b responder spi: 1d16d7f3431e6e1d であり、上記の結果と一致することが確認できます。
IKE_INTERMEDIATE で 1679 byte のパケットを送信していることを確認できます。
PQC KEM のためサイズが大きく、MTU を増やしていない場合、フラグメントが発生してしまいます。

なお、MTU を増やしていない場合は、IKE_INTERMEDIATE がフラグメントしていることが確認できます。

まとめ
本記事では、StrongSwan 6.0.3 を用いて IKE_INTERMEDIATE で PQ/T Hybrid KEX CURVE_25519/KE1_ML_KEM_1024 が利用できることを確認しました。
また、PQC KEX のサイズが大きいことも併せて確認できました。
PQC への移行は今後ますます重要となりますので、ぜひご自身の環境でも検証してみてください。
参考文献
Network Namespaceとvethでルーターのあるネットワークを作ってみる - Qiita
strongSwan - strongSwan 6.0.0 Released
strongSwan in Linux Network Namespaces :: strongSwan Documentation
