背景と理由:深夜2時に素のIPsecがOSPFをダウンさせる原因
土曜日の午前2時15分、監視ダッシュボードに大量のアラートが押し寄せます。3つのリモート拠点にまたがるサブネットが、コアルーティングテーブルから一斉に消失しました。エッジルーター上では、フェーズ1およびフェーズ2のIPsec Security Association(SA)はいずれもアクティブで正常を示しています。しかし、OSPFデーモンはINIT状態のままスタックし、正常なネイバー隣接関係を確立できていません。
これが発生する理由は明確です。標準的なポリシーベースのIPsecトンネルモードは、明示的なトラフィックセレクターに一致するユニキャストIPパケットのみを暗号化します。ブロードキャストやマルチキャストのパケットは完全に破棄されてしまいます。
OSPFは、5〜10秒ごとに224.0.0.5(すべてのOSPFルーター宛て)および224.0.0.6(すべてのDesignated Router宛て)に送信されるマルチキャストのHelloパケットに依存しています。これらのマルチキャストが届かなければ、ノードはネイバーの検出、リンクステートデータベース(LSDB)の同期、あるいはダウンしたリンクの検知ができません。トポロジーが2拠点から20拠点へと拡大した場合、素のIPsecトンネル上で何十もの静的フォールバックルートを維持管理することは、運用上の悪夢と化します。
Generic Routing Encapsulation(GRE)はこのギャップを埋める技術です。GREはIPプロトコル47を使用し、任意のレイヤー3ペイロードを標準的なPoint-to-Point IPパケット内にカプセル化します。ただし、ネイティブのGREはパブリックインターネット上で全ペイロードデータを平文のまま送信してしまいます。そこでGREをIPsecのトランスポートモードでラップすることにより、両者の利点を同時に享受できます。GREがOSPFに必要なマルチキャストパケットを処理し、IPsecがWANに送出される前のすべてのバイトを暗号化します。
このアーキテクチャは、汎用ハードウェアが高価な専用ゲートウェイを代替するLinuxベースのエッジルーターやSD-WAN構築における標準的な構成となっています。
インストール:LinuxへのstrongSwanとFRRoutingの導入
各Linuxゲートウェイには2つのソフトウェアパッケージが必要です。IKEv2ネゴシエーションとIPsecカーネルポリシーを担うstrongSwan、そしてOSPFデーモンを管理するFRRouting (FRR)です。GREトンネリング自体は、Linuxカーネルがiproute2を介してネイティブに処理します。
本ガイドでは、エッジルーターとして動作する2台のDebian/Ubuntuゲートウェイを構成します:
- 本社ゲートウェイ(ノードA): パブリックWAN
198.51.100.1(eth0上)、LAN10.10.0.0/24、トンネルIP172.16.1.1/30 - 支社ゲートウェイ(ノードB): パブリックWAN
203.0.113.2(eth0上)、LAN10.20.0.0/24、トンネルIP172.16.1.2/30
両方のマシンに必要なパッケージをインストールします:
sudo apt-get update
sudo apt-get install -y strongswan strongswan-pki libcharon-extra-plugins frr iproute2 iptables
次に、IPv4パケット転送を有効化し、必要なカーネルモジュールをロードします。rp_filter = 0またはルーズモード(2)に設定することで、仮想インターフェースに到達したカプセル化GREパケットがカーネルによって暗黙的に破棄されるのを防ぎます:
sudo modprobe ip_gre
sudo modprobe esp4
sudo modprobe xfrm4_mode_transport
# sysctlパラメータを永続化
sudo tee /etc/sysctl.d/99-router.conf << 'EOF'
net.ipv4.ip_forward = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0
EOF
sudo sysctl --system
設定:暗号化GREパイプラインの構築
ステップ1:GREトンネルインターフェースの作成
本社ゲートウェイ(ノードA)で、仮想Point-to-Point GREインターフェースを作成します:
sudo ip tunnel add gre1 mode gre remote 203.0.113.2 local 198.51.100.1 ttl 255
sudo ip addr add 172.16.1.1/30 dev gre1
sudo ip link set dev gre1 mtu 1400
sudo ip link set dev gre1 up
対向となる支社ゲートウェイ(ノードB)でも同様にインターフェースを設定します:
sudo ip tunnel add gre1 mode gre remote 198.51.100.1 local 203.0.113.2 ttl 255
sudo ip addr add 172.16.1.2/30 dev gre1
sudo ip link set dev gre1 mtu 1400
sudo ip link set dev gre1 up
インターフェースのMTUを1400に下げ(必要に応じてTCP MSSを1360にクランプし)ておくことは極めて重要です。24バイトのGREヘッダーに加え、およそ50〜73バイトのIPsec ESPオーバーヘッドを考慮することで、標準的な1500バイトのWANリンクにおけるパケットのサイレントフラグメンテーションを防止します。
ステップ2:strongSwan IPsecをトランスポートモードで設定
ここではトンネルモードではなく、トランスポートモードでIPsecを使用します。外側のGREパケットには既にルーティング可能なパブリックIPヘッダー(プロトコル47)が存在するため、トランスポートモードを使用すれば、冗長な2つ目のIPヘッダーを付加することなく、元のIPヘッダーの直後にESPヘッダーが挿入されます。
本社ゲートウェイ(ノードA)の/etc/ipsec.secretsで事前共有鍵(PSK)を定義します:
198.51.100.1 203.0.113.2 : PSK "SuperSecretProductionKey2026!"
本社ゲートウェイ(ノードA)の/etc/ipsec.confに接続定義を追加します:
config setup
charondebug="ike 1, knl 1, cfg 1"
conn %default
keyexchange=ikev2
ike=aes256gcm128-sha256-modp2048!
esp=aes256gcm128-modp2048!
dpdaction=restart
dpddelay=10s
dpdtimeout=30s
auto=start
conn gre-ipsec
type=transport
left=198.51.100.1
leftprotoport=gre
right=203.0.113.2
rightprotoport=gre
authby=secret
支社ゲートウェイ(ノードB)にも同様の設定を適用します:
# /etc/ipsec.secrets
203.0.113.2 198.51.100.1 : PSK "SuperSecretProductionKey2026!"
# /etc/ipsec.conf
config setup
charondebug="ike 1, knl 1, cfg 1"
conn %default
keyexchange=ikev2
ike=aes256gcm128-sha256-modp2048!
esp=aes256gcm128-modp2048!
dpdaction=restart
dpddelay=10s
dpdtimeout=30s
auto=start
conn gre-ipsec
type=transport
left=203.0.113.2
leftprotoport=gre
right=198.51.100.1
rightprotoport=gre
authby=secret
両方のゲートウェイでstrongSwanを再起動し、トンネルを開始します:
sudo systemctl restart strongswan-starter
sudo ipsec up gre-ipsec
ステップ3:FRRoutingによるOSPFの設定
/etc/frr/daemons内でospfd=yesに設定してOSPFデーモンを有効化し、サービスを再起動します:
sudo sed -i 's/ospfd=no/ospfd=yes/' /etc/frr/daemons
sudo systemctl restart frr
本社ゲートウェイ(ノードA)でvtyshを実行してFRR統合シェルを開き、ルーティングインスタンスを設定します:
sudo vtysh
configure terminal
router ospf
ospf router-id 172.16.1.1
network 172.16.1.0/30 area 0.0.0.0
network 10.10.0.0/24 area 0.0.0.0
exit
interface gre1
ip ospf network point-to-point
ip ospf hello-interval 5
ip ospf dead-interval 20
exit
exit
write memory
支社ゲートウェイ(ノードB)でも対応する設定を実行します:
sudo vtysh
configure terminal
router ospf
ospf router-id 172.16.1.2
network 172.16.1.0/30 area 0.0.0.0
network 10.20.0.0/24 area 0.0.0.0
exit
interface gre1
ip ospf network point-to-point
ip ospf hello-interval 5
ip ospf dead-interval 20
exit
exit
write memory
ip ospf network point-to-pointを明示的に指定することで、Designated Router(DR)およびBackup Designated Router(BDR)の選出プロセスをスキップします。これにより、リンクステートトラフィックの無駄なやり取りを削減し、トンネル越しのネイバーコンバージェンスを高速化できます。
検証と監視:正常動作の確認
コマンドの終了ステータスが正常だからといって、トラフィックが安全に流れているとは限りません。ネットワークスタックの各レイヤーを必ず個別に検証してください。
1. IPsecトランスポートSAの確認
strongSwanのステータスを確認し、トランスポートモードのSAが確立されているかを検証します:
sudo ipsec statusall
GREエンドポイントと一致するアクティブなSecurity Association(SA)を確認します:
gre-ipsec{1}: INSTALLED, TRANSPORT, reqid 1, ESP SPIs: c8f4a120_e21b5591
gre-ipsec{1}: AES_GCM_16 256, 128450 bytes_i, 129320 bytes_o, rekeying in 45 minutes
gre-ipsec{1}: 198.51.100.1/32[gre] === 203.0.113.2/32[gre]
次に、LinuxカーネルのXFRMサブシステムにトランスポート変換ルールがインストールされていることを確認します:
sudo ip xfrm state
sudo ip xfrm policy
2. OSPFネイバー隣接関係の確認
FRR内で2台のゲートウェイがFull隣接状態に達していることを確認します:
sudo vtysh -c "show ip ospf neighbor"
期待される出力:
Neighbor ID Pri State Dead Time Address Interface
172.16.1.2 0 Full/ - 00:00:18 172.16.1.2 gre1:172.16.1.1
ネイバーの状態がInitや2-Wayのままスタックしている場合は、iptablesまたはnftablesのルールを確認してください。外部インターフェースでGRE(プロトコル47)およびUDPポート500、4500がファイアウォールによって許可されていることを確認します。
3. カーネルルートの確認とパケットキャプチャ
OSPFによってリモート拠点のLANサブネットがLinuxルーティングテーブルに登録されたかを確認します:
ip route show proto ospf
仮想GREインターフェース経由でリモートサブネットへのルートが表示されるはずです:
10.20.0.0/24 via 172.16.1.2 dev gre1 proto ospf metric 20
最後に、リモートLAN上のホストに対してpingを実行しながら(例:ping -c 3 10.20.0.15)、物理WANインターフェース上でパケットキャプチャを実行します:
sudo tcpdump -ni eth0 host 203.0.113.2
eth0上を通過するパケットとしてESP(spi=0x...)のみが観測されるはずです。もし暗号化されていないGREやOSPFv2-Helloパケットが回線上に現れた場合は、直ちにトラフィックを停止し、IPsec設定のleftprotoport=greディレクティブを再確認してください。

