IPsec Tunnels Masterclass
This guide covers IPsec from first principles to production deployment: the IKE negotiation phases, Security Associations, the Security Parameter Index, ESP and AH protocols, transport vs tunnel mode, hashing and integrity verification, the four deployment types (and what each one actually does to every packet), and complete strongSwan and Libreswan configurations on kldload. By the end you will understand exactly what happens to a packet from the moment it enters an IPsec tunnel to the moment it exits the other side — and why every field in the SA exists.
The premise: WireGuard is the right answer for most tunnelling needs — it is simple, fast, and hard to misconfigure. But IPsec is the standard that every firewall, router, cloud provider VPN gateway, and government network speaks. If you need to connect to an AWS VPN Gateway, a Cisco ASA, a Juniper SRX, or a partner's network, you need IPsec. It is also the only tunnel protocol with FIPS 140-3 validated implementations, which matters for compliance.
What this page covers: IPsec architecture, IKE Phase 1 and Phase 2, Security Associations (SA), Security Parameter Index (SPI), ESP encryption and authentication, AH integrity, transport mode vs tunnel mode, the four IPsec deployment types, hashing algorithms and integrity checking, anti-replay protection, Dead Peer Detection, strongSwan site-to-site and roadwarrior configs, Libreswan examples, IPsec with BGP, XFRM interface, nftables integration, and troubleshooting. All on kldload.
Prerequisites: a running kldload system. Networking fundamentals from the Backplane Networks masterclass. Firewall rules from the nftables Masterclass.
1. IPsec Architecture
IPsec is not a single protocol. It is a framework of protocols that work together to provide confidentiality (encryption), integrity (tamper detection), authentication (peer identity), and anti-replay (packet sequence protection) at the IP layer.
IKE — Internet Key Exchange
The control plane. IKE negotiates the cryptographic parameters and establishes shared keys between two peers. IKEv2 (RFC 7296) is the current version. IKEv1 is legacy. IKE runs on UDP port 500 (and UDP 4500 when NAT traversal is needed).
ESP — Encapsulating Security Payload
The data plane for encryption + authentication. ESP encrypts the packet payload and appends an Integrity Check Value (ICV) — a cryptographic hash that proves the packet was not tampered with. ESP is IP protocol 50. It provides confidentiality, integrity, and anti-replay.
AH — Authentication Header
Integrity and authentication only — no encryption. AH hashes the entire IP packet including most header fields, proving nothing was modified in transit. AH is IP protocol 51. It is rarely used alone because ESP with NULL encryption achieves the same thing.
SA — Security Association
A one-way agreement between two peers: the encryption algorithm, the key, the integrity algorithm, the SPI, and the lifetime. An IPsec tunnel needs at least two SAs (one per direction). SAs live in the kernel's Security Association Database (SAD).
SPI — Security Parameter Index
A 32-bit identifier in every ESP/AH packet header. The receiving host uses the SPI + destination IP + protocol (ESP or AH) to look up the correct SA in its SAD. Without the SPI, the receiver would not know which key to use for decryption.
SP — Security Policy
Rules that tell the kernel what to do with traffic: encrypt it (PROTECT), let it pass (BYPASS), or drop it (DISCARD). Policies live in the Security Policy Database (SPD) and match on source/destination IP, port, and protocol. Policies reference SAs.
2. IKE Negotiation — Phase 1 and Phase 2
IKEv2 negotiates in two exchanges (not "phases" technically, but the Phase 1/Phase 2 terminology from IKEv1 is still universally used). Phase 1 establishes a secure channel between the peers. Phase 2 negotiates the actual IPsec SAs for data traffic.
Phase 1 — IKE SA (the control channel)
IKE_SA_INIT
The initiator sends proposed cipher suites, a Diffie-Hellman public value, and a nonce. The responder picks a suite, sends its DH value and nonce. After this exchange both sides derive the IKE SA keys (encryption and integrity keys for the IKE channel itself). Two packets total.
IKE_AUTH
Both sides authenticate. They send their identity (IP, FQDN, or certificate DN), the authentication payload (pre-shared key MAC or certificate signature), and the first Child SA proposal (the Phase 2 negotiation piggybacks on this exchange). Two more packets.
Result of Phase 1: an encrypted, authenticated IKE channel (the IKE SA). All further negotiation happens inside this channel. The IKE SA has its own lifetime (typically 4–24 hours) and is rekeyed before it expires.
Phase 2 — Child SA (the data tunnel)
CREATE_CHILD_SA
Negotiates the IPsec SAs (Child SAs) for actual data traffic. Each side proposes encryption and integrity algorithms, the traffic selectors (which IP ranges and ports to protect), and optionally a new DH exchange for Perfect Forward Secrecy (PFS). The result is a pair of SAs — one for each direction, each with its own SPI.
Traffic Selectors (TS)
Define what traffic flows through the tunnel. TSi (initiator's traffic selector) and TSr (responder's traffic selector) specify IP ranges, protocol, and port ranges. A tunnel from 10.1.0.0/24 to 10.2.0.0/24 has TSi=10.1.0.0/24 and TSr=10.2.0.0/24.
Perfect Forward Secrecy (PFS)
Optional DH exchange during Phase 2 rekeying. Without PFS, all Child SAs derive keys from the Phase 1 DH material — if Phase 1 keys leak, all sessions are compromised. With PFS, each rekey does a fresh DH exchange, so past sessions stay secure even if current keys leak.
IKEv2 in four packets
# Complete IKEv2 exchange:
#
# Packet 1: Initiator → Responder IKE_SA_INIT request
# - SA proposal (ciphers), KE (DH public value), Nonce
#
# Packet 2: Responder → Initiator IKE_SA_INIT response
# - SA chosen cipher, KE (DH public value), Nonce
# — Both sides now derive IKE SA keys —
#
# Packet 3: Initiator → Responder IKE_AUTH request (encrypted)
# - Identity, Auth (PSK or cert), SA proposal for Child SA, TSi, TSr
#
# Packet 4: Responder → Initiator IKE_AUTH response (encrypted)
# - Identity, Auth, SA chosen for Child SA, TSi, TSr
# — Child SA established, data can flow —
# Total: 4 packets, 2 round trips. Compare to IKEv1: 6-9 packets.
3. The SPI — How Every Packet Finds Its Key
The Security Parameter Index is the most important field in an IPsec packet. Without it, the receiving host cannot decrypt or verify anything.
How the SPI works
# An ESP packet on the wire looks like:
#
# ┌──────────────────────────────────────────────┐
# │ IP Header (proto=50 for ESP) │
# ├──────────────────────────────────────────────┤
# │ ESP Header │
# │ SPI: 0xc0a80123 (32 bits) │
# │ Sequence Number: 00000042 (32 bits) │
# ├──────────────────────────────────────────────┤
# │ Encrypted Payload │
# │ (original IP packet, padded) │
# ├──────────────────────────────────────────────┤
# │ ESP Trailer │
# │ Padding + Pad Length + Next Header │
# ├──────────────────────────────────────────────┤
# │ Integrity Check Value (ICV) │
# │ (HMAC or GCM tag, covers ESP header + │
# │ encrypted payload + trailer) │
# └──────────────────────────────────────────────┘
# The receiver:
# 1. Reads the SPI (0xc0a80123) from the ESP header
# 2. Looks up SPI + destination IP + protocol in its SAD
# 3. Finds the SA: AES-256-GCM, key=..., anti-replay window=64
# 4. Checks the sequence number against the anti-replay window
# 5. Verifies the ICV (integrity check)
# 6. Decrypts the payload
# 7. Delivers the original IP packet to the stack
SPI assignment
# Each peer generates its own inbound SPI during IKE negotiation
# Peer A says: "Send ESP to me with SPI 0xaaa11111"
# Peer B says: "Send ESP to me with SPI 0xbbb22222"
#
# Traffic A→B carries SPI 0xbbb22222 (B's inbound SPI)
# Traffic B→A carries SPI 0xaaa11111 (A's inbound SPI)
# View current SAs and their SPIs on Linux:
ip xfrm state
# src 203.0.113.1 dst 198.51.100.1
# proto esp spi 0xc0a80123 reqid 1 mode tunnel
# replay-window 64 flag af-unspec
# auth-trunc hmac(sha256) 0x... 128
# enc cbc(aes) 0x...
# encap type espinudp sport 4500 dport 4500 addr 0.0.0.0
# anti-replay context: seq 0x0, oseq 0x2a, bitmap 0x0000003f
4. ESP vs AH — Encryption and Integrity
ESP — Encapsulating Security Payload
ESP provides encryption + integrity + anti-replay. Modern ESP uses AEAD (Authenticated Encryption with Associated Data) ciphers like AES-256-GCM, which combine encryption and integrity into a single operation. Older configurations separate them: AES-CBC for encryption + HMAC-SHA256 for integrity.
| Algorithm | Type | Status | Notes |
|---|---|---|---|
AES-256-GCM |
AEAD (enc + auth) | Recommended | Single operation, hardware accelerated (AES-NI), FIPS approved |
AES-256-CBC + HMAC-SHA256 |
Encrypt-then-MAC | Acceptable | Widely supported, FIPS approved, slower than GCM |
ChaCha20-Poly1305 |
AEAD | Good | Excellent on CPUs without AES-NI, not FIPS approved |
3DES |
Encrypt | Deprecated | 56-bit effective security. Legacy only. Never use for new tunnels. |
AH — Authentication Header
AH provides integrity and authentication but no encryption. It hashes the entire IP packet including immutable header fields (source IP, destination IP, protocol). This means AH detects modifications to the IP header itself — something ESP does not do in transport mode. However, AH breaks with NAT (because NAT modifies the IP header, invalidating the hash). For this reason, AH is rarely used in modern deployments.
# AH packet structure:
#
# ┌──────────────────────────────────────────────┐
# │ IP Header (proto=51 for AH) │
# ├──────────────────────────────────────────────┤
# │ AH Header │
# │ Next Header, Payload Length │
# │ SPI: 0xdead0001 │
# │ Sequence Number: 00000001 │
# │ ICV: HMAC-SHA256 over entire packet │
# │ (IP header + AH header + payload) │
# ├──────────────────────────────────────────────┤
# │ Original Payload (NOT encrypted) │
# └──────────────────────────────────────────────┘
#
# AH covers the IP header → breaks with NAT
# ESP does NOT cover the outer IP header → works with NAT
5. Transport Mode vs Tunnel Mode
Transport Mode
Encrypts only the payload of the original IP packet. The original IP header stays intact (visible). Used for host-to-host encryption where both endpoints are the IPsec peers. No new IP header is added — lower overhead.
Tunnel Mode
Encrypts the entire original IP packet (header + payload) and wraps it in a new IP header. The outer header has the tunnel endpoint addresses; the inner header (encrypted) has the actual source and destination. Used for site-to-site VPNs and roadwarrior access.
# Transport mode packet:
# [Original IP Header] [ESP Header] [Encrypted Original Payload] [ESP Trailer] [ICV]
# src=10.1.0.5 SPI (TCP/UDP data)
# dst=10.2.0.5
# Tunnel mode packet:
# [New IP Header] [ESP Header] [Encrypted: Original IP Header + Payload] [ESP Trailer] [ICV]
# src=203.0.113.1 SPI (entire original packet)
# dst=198.51.100.1
# In tunnel mode, the original source (10.1.0.5) and destination (10.2.0.5)
# are invisible to anyone observing the outer packet. They only see the
# tunnel endpoints (203.0.113.1 ↔ 198.51.100.1).
When to use which
| Scenario | Mode | Why |
|---|---|---|
| Site-to-site VPN between two gateways | Tunnel | Hosts behind each gateway are not IPsec-aware |
| Roadwarrior laptop to corporate gateway | Tunnel | Laptop gets a virtual IP inside corporate network |
| Host-to-host encryption (e.g. database replication) | Transport | Both hosts are IPsec peers, no tunnelling needed |
| L2TP/IPsec (legacy remote access) | Transport | IPsec encrypts the L2TP tunnel, L2TP handles the tunnelling |
6. Hashing, Integrity Checking & Anti-Replay
Every ESP packet includes an Integrity Check Value (ICV) — a cryptographic hash that proves the packet was not modified in transit. The receiver recomputes the hash and compares it to the ICV. If they do not match, the packet is dropped silently.
How integrity verification works
# For AEAD ciphers (AES-GCM):
# The GCM authentication tag IS the ICV. Encryption and integrity are one operation.
# AES-GCM-256 produces a 128-bit (16-byte) authentication tag.
#
# For separate integrity (HMAC):
# The sender computes HMAC-SHA256 over: ESP Header + Encrypted Payload + ESP Trailer
# and appends the truncated result (128 bits for HMAC-SHA256-128) as the ICV.
#
# The receiver:
# 1. Reads the ICV from the end of the packet
# 2. Recomputes the HMAC over the same fields using the SA's integrity key
# 3. Compares computed HMAC to received ICV
# 4. If mismatch → DROP (tampered or corrupted)
# 5. If match → proceed to decryption
# This is MAC-then-Encrypt for old configs, or AEAD for modern configs.
# AEAD is always preferred — it's faster and avoids padding oracle attacks.
Anti-replay protection
# Every ESP packet has a 32-bit (or 64-bit with ESN) sequence number.
# The receiver maintains a sliding window (typically 64 or 128 packets).
#
# Window: [sequence 100 ───────── sequence 163]
#
# Packet with seq=150 → inside window → check if already seen → accept
# Packet with seq=99 → before window → DROP (too old, possible replay)
# Packet with seq=164 → ahead of window → advance window → accept
# Packet with seq=150 (again) → inside window, already marked → DROP (replay)
#
# This prevents an attacker from recording and replaying legitimate packets.
# View the anti-replay state:
ip xfrm state | grep -A2 "anti-replay"
# anti-replay context: seq 0x0, oseq 0x2a, bitmap 0x0000003f
# oseq = outbound sequence counter (42 packets sent)
# bitmap = which sequence numbers in the window have been received
7. The Four IPsec Deployment Types
IPsec deployments are categorised by where encryption happens and what gets protected. These are often called Type 1 through Type 4, though the terminology varies across vendors and standards bodies. Understanding the types is understanding the architectural choices.
Type 1 — Gateway-to-Gateway (Site-to-Site)
Two IPsec gateways protect traffic between their respective networks. Hosts behind each gateway are unaware of IPsec. The gateways encrypt/decrypt at the network boundary. This is the classic site-to-site VPN: office A talks to office B through an encrypted tunnel between the edge firewalls.
Type 2 — Host-to-Gateway (Roadwarrior)
A single host (laptop, mobile device) establishes a tunnel to a gateway to access the network behind it. The host is an IPsec peer; hosts on the remote network are not. The roadwarrior gets a virtual IP from the gateway's pool and appears to be on the remote network.
Type 3 — Host-to-Host (Transport Encryption)
Two hosts encrypt traffic directly between each other, typically in transport mode. No gateway involved. Both hosts run IPsec. Used for database replication, cluster heartbeats, or any host-pair communication that needs encryption without a tunnel overlay. The original IP headers stay visible.
Type 4 — Gateway with Integrity Verification (Hashing Service)
A gateway that inspects every packet's integrity (ICV) against the SPI-keyed hash before forwarding. This is the "hashing service" deployment — the gateway does not necessarily decrypt the payload, but it verifies that every packet is authentic and untampered before allowing it through. Packets that fail the integrity check are dropped at the gateway, never reaching the destination. Used in high-security environments where a dedicated security appliance sits in the path and validates traffic integrity on behalf of the network.
Type 4 in detail — the integrity gateway
Type 4 is the least understood deployment type. The gateway receives ESP packets, extracts the SPI from the header, looks up the corresponding SA (it has the integrity key but may or may not have the encryption key), recomputes the ICV hash, and compares it to the packet's ICV. If the hash matches, the packet is forwarded. If not, it is dropped.
# Type 4 gateway packet flow:
#
# 1. ESP packet arrives at the integrity gateway
# ┌─────────────────────────────────────────┐
# │ IP Header → Integrity GW address │
# │ ESP Header → SPI: 0xabc12345 │
# │ Encrypted Payload │
# │ ICV: 0x9f8e7d6c5b4a3928 │
# └─────────────────────────────────────────┘
#
# 2. Gateway reads SPI 0xabc12345
# 3. Looks up SA in its SAD → finds integrity key (HMAC-SHA256)
# 4. Computes HMAC-SHA256 over ESP Header + Encrypted Payload
# 5. Compares computed hash to ICV 0x9f8e7d6c5b4a3928
#
# 6a. Match → FORWARD packet to destination (still encrypted)
# 6b. Mismatch → DROP packet (integrity violation)
#
# The gateway NEVER decrypts the payload.
# It only verifies that the packet was not tampered with.
# The destination host performs the actual decryption.
# This provides:
# - Tamper detection at the network perimeter
# - Anti-replay validation (sequence number checking)
# - Protection against packet injection attacks
# - All without giving the gateway access to plaintext traffic
Deployment type comparison
| Type | Peers | Mode | Gateway Decrypts? | Use Case |
|---|---|---|---|---|
| Type 1 | Gateway ↔ Gateway | Tunnel | Yes (both ends) | Site-to-site VPN, multi-office |
| Type 2 | Host ↔ Gateway | Tunnel | Yes (gateway end) | Remote access, roadwarrior |
| Type 3 | Host ↔ Host | Transport | N/A (no gateway) | DB replication, cluster comms |
| Type 4 | Host ↔ Host via Gateway | Transport/Tunnel | No (verify only) | Integrity validation, high-sec perimeter |
8. strongSwan — Site-to-Site Configuration
strongSwan is the standard IPsec implementation on Linux. It handles IKE negotiation and installs SAs into the kernel's XFRM subsystem.
Install
# CentOS / RHEL / Rocky
dnf install -y strongswan
# Debian / Ubuntu
apt install -y strongswan strongswan-pki
# Enable and start
systemctl enable --now strongswan
Site-to-site tunnel with PSK
# Gateway A: 203.0.113.1, LAN 10.1.0.0/24
# Gateway B: 198.51.100.1, LAN 10.2.0.0/24
# === Gateway A: /etc/swanctl/conf.d/site-to-site.conf ===
connections {
site-b {
version = 2
local_addrs = 203.0.113.1
remote_addrs = 198.51.100.1
local {
auth = psk
id = gateway-a@example.com
}
remote {
auth = psk
id = gateway-b@example.com
}
children {
lan-to-lan {
local_ts = 10.1.0.0/24
remote_ts = 10.2.0.0/24
esp_proposals = aes256gcm128-sha256-modp2048
start_action = start
dpd_action = restart
close_action = restart
rekey_time = 3600
}
}
proposals = aes256-sha256-modp2048
rekey_time = 14400
dpd_delay = 30
}
}
secrets {
ike-site-b {
id-a = gateway-a@example.com
id-b = gateway-b@example.com
secret = "CHANGE_THIS_TO_A_STRONG_RANDOM_PSK_64_CHARS_MINIMUM"
}
}
# === Gateway B: /etc/swanctl/conf.d/site-to-site.conf ===
connections {
site-a {
version = 2
local_addrs = 198.51.100.1
remote_addrs = 203.0.113.1
local {
auth = psk
id = gateway-b@example.com
}
remote {
auth = psk
id = gateway-a@example.com
}
children {
lan-to-lan {
local_ts = 10.2.0.0/24
remote_ts = 10.1.0.0/24
esp_proposals = aes256gcm128-sha256-modp2048
start_action = start
dpd_action = restart
close_action = restart
rekey_time = 3600
}
}
proposals = aes256-sha256-modp2048
rekey_time = 14400
dpd_delay = 30
}
}
secrets {
ike-site-a {
id-a = gateway-a@example.com
id-b = gateway-b@example.com
secret = "CHANGE_THIS_TO_A_STRONG_RANDOM_PSK_64_CHARS_MINIMUM"
}
}
# Load and start
swanctl --load-all
swanctl --initiate --child lan-to-lan
# Verify
swanctl --list-sas
# site-b: #1, ESTABLISHED, IKEv2, ...
# lan-to-lan: #1, reqid 1, INSTALLED, TUNNEL, ESP:AES_GCM_16_256
# 10.1.0.0/24 === 10.2.0.0/24
9. strongSwan with Certificates
# Generate a CA and gateway certificates using strongSwan PKI
# (Or use step-ca from the TLS masterclass)
# Create CA
pki --gen --type rsa --size 4096 --outform pem > /etc/swanctl/private/ca-key.pem
pki --self --ca --lifetime 3650 \
--in /etc/swanctl/private/ca-key.pem \
--dn "C=CA, O=kldload, CN=IPsec CA" \
--outform pem > /etc/swanctl/x509ca/ca-cert.pem
# Generate Gateway A key and certificate
pki --gen --type rsa --size 2048 --outform pem > /etc/swanctl/private/gw-a-key.pem
pki --req --type rsa \
--in /etc/swanctl/private/gw-a-key.pem \
--dn "C=CA, O=kldload, CN=gateway-a.example.com" \
--san gateway-a.example.com --san 203.0.113.1 \
--outform pem > /tmp/gw-a-req.pem
pki --issue --cacert /etc/swanctl/x509ca/ca-cert.pem \
--cakey /etc/swanctl/private/ca-key.pem \
--in /tmp/gw-a-req.pem --lifetime 365 \
--outform pem > /etc/swanctl/x509/gw-a-cert.pem
# /etc/swanctl/conf.d/site-to-site-certs.conf (Gateway A)
connections {
site-b {
version = 2
local_addrs = 203.0.113.1
remote_addrs = 198.51.100.1
local {
auth = pubkey
certs = gw-a-cert.pem
id = gateway-a.example.com
}
remote {
auth = pubkey
id = gateway-b.example.com
}
children {
lan-to-lan {
local_ts = 10.1.0.0/24
remote_ts = 10.2.0.0/24
esp_proposals = aes256gcm128-modp2048
start_action = start
dpd_action = restart
}
}
proposals = aes256-sha256-x25519
}
}
10. Roadwarrior Configuration
# Gateway: /etc/swanctl/conf.d/roadwarrior.conf
connections {
roadwarrior {
version = 2
local_addrs = %any
remote_addrs = %any
pools = rw-pool
local {
auth = pubkey
certs = gw-cert.pem
id = vpn.example.com
}
remote {
auth = eap-mschapv2
eap_id = %any
}
children {
rw-child {
local_ts = 10.0.0.0/8
esp_proposals = aes256gcm128-modp2048
dpd_action = clear
}
}
proposals = aes256-sha256-x25519
send_certreq = yes
}
}
pools {
rw-pool {
addrs = 10.99.0.0/24
dns = 10.1.0.53
}
}
secrets {
eap-alice {
id = alice
secret = "alice_password_here"
}
eap-bob {
id = bob
secret = "bob_password_here"
}
}
# Client (laptop) — strongSwan or built-in IKEv2 client
# macOS/iOS/Windows all have native IKEv2 support
# Android: strongSwan app from F-Droid/Play Store
# Linux client: /etc/swanctl/conf.d/client.conf
connections {
office {
version = 2
remote_addrs = vpn.example.com
vips = 0.0.0.0
local {
auth = eap-mschapv2
eap_id = alice
}
remote {
auth = pubkey
id = vpn.example.com
}
children {
office-net {
remote_ts = 10.0.0.0/8
start_action = trap
dpd_action = restart
}
}
}
}
secrets {
eap-alice {
id = alice
secret = "alice_password_here"
}
}
11. Libreswan Alternative
Libreswan is the other major Linux IPsec implementation. It is the default on RHEL/CentOS/Rocky
(installed as ipsec) and has FIPS 140-3 validation. The configuration syntax is different
from strongSwan.
# Install
dnf install -y libreswan
systemctl enable --now ipsec
# Initialise the NSS database (Libreswan uses NSS for crypto)
ipsec initnss
# /etc/ipsec.d/site-to-site.conf
conn site-b
type=tunnel
authby=secret
left=203.0.113.1
leftsubnet=10.1.0.0/24
leftid=@gateway-a
right=198.51.100.1
rightsubnet=10.2.0.0/24
rightid=@gateway-b
ike=aes256-sha2_256-modp2048
esp=aes256gcm128
ikelifetime=4h
salifetime=1h
dpddelay=30
dpdtimeout=120
dpdaction=restart
auto=start
# /etc/ipsec.d/site-b.secrets
@gateway-a @gateway-b : PSK "STRONG_RANDOM_KEY_HERE"
# Load and start
ipsec auto --add site-b
ipsec auto --up site-b
# Verify
ipsec status
ipsec trafficstatus
12. XFRM Interface — Modern Route-Based IPsec
Traditional IPsec on Linux uses policy-based routing: traffic selectors determine which packets enter the tunnel. XFRM interfaces (since kernel 4.19) create a virtual tunnel interface that you route traffic through, just like a WireGuard or GRE interface. This is much easier to manage with BGP and dynamic routing.
# Create an XFRM interface
ip link add ipsec0 type xfrm dev eth0 if_id 42
ip addr add 169.254.0.1/30 dev ipsec0
ip link set ipsec0 up
# Route traffic through the XFRM interface
ip route add 10.2.0.0/24 dev ipsec0
# strongSwan config with XFRM interface
connections {
site-b {
version = 2
local_addrs = 203.0.113.1
remote_addrs = 198.51.100.1
if_id_in = 42
if_id_out = 42
local {
auth = pubkey
certs = gw-a-cert.pem
}
remote {
auth = pubkey
}
children {
pass-all {
local_ts = 0.0.0.0/0
remote_ts = 0.0.0.0/0
esp_proposals = aes256gcm128-modp2048
start_action = trap
}
}
}
}
# Now you can run BGP over ipsec0 with BIRD
# This is the kldload way: XFRM interface + BIRD BGP = route-based IPsec VPN
ip -s link show ipsec0. This is the same model that WireGuard uses, and it is the model that the backplane and BIRD BGP masterclasses assume.13. nftables Rules for IPsec
# Allow IKE and ESP through the firewall
nft add rule inet filter input udp dport 500 accept comment "IKE"
nft add rule inet filter input udp dport 4500 accept comment "IKE NAT-T"
nft add rule inet filter input ip protocol esp accept comment "ESP"
nft add rule inet filter input ip protocol ah accept comment "AH"
# Allow traffic on the XFRM interface
nft add rule inet filter input iifname "ipsec0" accept
nft add rule inet filter forward iifname "ipsec0" accept
nft add rule inet filter forward oifname "ipsec0" accept
# NAT exemption for IPsec traffic (if you have masquerading)
nft add rule inet nat postrouting oifname "ipsec0" accept
# Match on IPsec policy (for policy-based routing)
nft add rule inet filter forward meta ipsec exists accept comment "IPsec-protected traffic"
# Log dropped IPsec-related traffic for debugging
nft add rule inet filter input udp dport { 500, 4500 } log prefix "ipsec-ike: " accept
nft add rule inet filter input ip protocol esp log prefix "ipsec-esp: " accept
14. Dead Peer Detection & Rekeying
# DPD — checks if the remote peer is alive
# Sends an IKE INFORMATIONAL request at the configured interval
# If no response after timeout, takes the configured action
# strongSwan DPD settings:
connections {
site-b {
dpd_delay = 30 # Send DPD every 30 seconds of inactivity
children {
lan-to-lan {
dpd_action = restart # restart = tear down and re-establish
# clear = tear down only
# trap = install trap policy, rekey on demand
}
}
}
}
# Rekeying — replace keys before they expire
# IKE SA rekey: negotiates new IKE keys (default: 4 hours)
# Child SA rekey: negotiates new ESP keys (default: 1 hour)
# Rekeying happens automatically, no traffic interruption
# strongSwan rekey settings:
connections {
site-b {
rekey_time = 14400 # IKE SA lifetime: 4 hours
children {
lan-to-lan {
rekey_time = 3600 # Child SA lifetime: 1 hour
rekey_bytes = 0 # Rekey after N bytes (0 = disabled)
rekey_packets = 0 # Rekey after N packets (0 = disabled)
}
}
}
}
# Monitor rekey events
swanctl --log
15. Monitoring & Troubleshooting
# === strongSwan ===
swanctl --list-sas # Show all active SAs
swanctl --list-conns # Show configured connections
swanctl --list-certs # Show loaded certificates
swanctl --stats # IKE statistics
swanctl --log # Real-time log (IKE events, rekeys, DPD)
# === Kernel XFRM ===
ip xfrm state # Show all SAs (SPIs, algorithms, keys, counters)
ip xfrm policy # Show all security policies
ip -s xfrm state # SA statistics (bytes, packets, errors)
ip xfrm monitor # Real-time XFRM events
# === Packet capture ===
# Capture IKE negotiation
tcpdump -ni eth0 udp port 500 or udp port 4500
# Capture ESP packets (encrypted — you won't see payloads)
tcpdump -ni eth0 esp
# Capture decrypted traffic on the XFRM interface
tcpdump -ni ipsec0 -w /tmp/decrypted.pcap
# === Libreswan ===
ipsec status # Connection status
ipsec trafficstatus # Bytes/packets per tunnel
ipsec auto --status # Detailed SA info
ipsec look # Quick overview
# === Common issues ===
# "no proposal chosen" → cipher mismatch between peers
# "peer not responding" → firewall blocking UDP 500/4500 or ESP
# "authentication failed" → wrong PSK or certificate
# "traffic selector mismatch" → local_ts/remote_ts don't match
# "DPD timeout" → peer is down or unreachable
Verify SPI and integrity on the wire
# View SPI values in kernel state
ip xfrm state | grep spi
# proto esp spi 0xc0a80123 reqid 1 mode tunnel
# proto esp spi 0xdead5678 reqid 1 mode tunnel
# Watch packets and confirm SPI matches
tcpdump -ni eth0 esp -v 2>&1 | grep SPI
# SPI 0xc0a80123
# Check packet counts per SA
ip -s xfrm state
# stats:
# replay-window 64 replay 0 failed 0
# bytes: 1234567 packets: 8901 errors: 0
# "failed" = integrity check failures (ICV mismatch)
# "replay" = replayed packets detected and dropped
# Both should be 0 in normal operation. Non-zero = attack or corruption.
ip -s xfrm state output is your single most important diagnostic tool. The failed counter tells you how many packets failed integrity verification — meaning either the path is corrupting packets, there is a key mismatch between peers, or someone is injecting traffic. The replay counter tells you how many replay attempts were detected. In a healthy tunnel, both are zero. In a tunnel under attack, they are not. This is the Type 4 deployment in action: every packet is verified against its SPI-keyed hash, and failures are counted and dropped.16. IPsec vs WireGuard — When to Use Which
| Criterion | IPsec | WireGuard |
|---|---|---|
| Configuration complexity | High (IKE proposals, traffic selectors, SA lifetimes) | Low (public key, endpoint, allowed IPs) |
| Interoperability | Universal (every router, firewall, cloud gateway) | Limited (needs WireGuard on both ends) |
| FIPS 140-3 | Yes (Libreswan validated) | No (ChaCha20/Curve25519 not FIPS) |
| Native OS support | Built-in on macOS, iOS, Windows, Android | Requires app/driver on most platforms |
| Performance | Good (AES-NI accelerated) | Excellent (in-kernel, minimal overhead) |
| Transport mode | Yes | No (tunnel only) |
| Key exchange | IKE (automated, with rekeying) | Static keys (no built-in rekeying protocol) |
| Code audit surface | ~400K lines (strongSwan) | ~4K lines |
Use IPsec when: connecting to third-party equipment (cloud VPN gateways, partner networks, hardware firewalls), FIPS compliance is required, native OS VPN clients are needed, or transport mode encryption is needed. Use WireGuard when: you control both ends, simplicity and performance are priorities, and compliance is not a factor.
17. Quick Reference — Troubleshooting Table
| Symptom | Likely Cause | Fix |
|---|---|---|
| NO_PROPOSAL_CHOSEN | Cipher suite mismatch | Ensure both sides have matching proposals and esp_proposals |
| AUTHENTICATION_FAILED | PSK mismatch or wrong certificate | Verify PSK matches exactly. Check cert CN/SAN matches id |
| TS_UNACCEPTABLE | Traffic selectors mismatch | A's local_ts must equal B's remote_ts and vice versa |
| Tunnel up, no traffic flows | Missing routes or nftables blocking | Check ip route, ip xfrm policy, nftables forward rules |
| Frequent disconnects | DPD timeout or NAT device dropping UDP state | Reduce dpd_delay. Ensure NAT-T is active (UDP 4500) |
| Integrity failures (non-zero failed count) | Path corruption, key mismatch, or injection attack | Check ip -s xfrm state. Rekey the SA. Investigate the path. |
| Replay counter incrementing | Packet reordering or replay attack | Increase replay window or investigate network path |
| Cannot connect from behind NAT | NAT-T not enabled | Allow UDP 4500. strongSwan does NAT-T automatically |
Related pages
- WireGuard Masterclass — the simpler tunnel protocol for controlled environments
- Backplane Networks Masterclass — network architecture for multi-plane isolation
- BIRD & BGP Masterclass — dynamic routing over XFRM interfaces
- nftables Masterclass — firewall rules for IPsec traffic
- TLS & PKI Masterclass — certificates for IPsec authentication
- FIPS 140-3 Compliance — validated IPsec with Libreswan
- Security — kldload security posture overview