This episode dissects NAT Traversal: why ESP cannot pass through NAT, how IKE and ESP are re-wrapped in UDP 4500, and the NAT-T configuration options in Libreswan, strongSwan, and xl2tpd.

Most VPN users are behind routers that perform NAT. Without special handling, their L2TP/IPsec will never connect. Episode 14 discusses NAT Traversal (NAT-T) — the mechanism that lets IPsec pass through NAT by wrapping ESP in UDP.
This topic is extremely important in real-world practice, because almost every remote access deployment deals with clients behind NAT. Understanding how NAT-T works also helps you read odd symptoms in the logs, such as the port shifting from 500 to 4500.
ESP runs directly on top of IP with protocol number 50, without TCP or UDP ports. NAT needs ports to distinguish many connections behind a single IP address — and ESP does not have any. As a result, two clients behind the same NAT cannot be distinguished, and source address changes make checksums and SPIs inconsistent.
When IPsec detects that one side is behind NAT, IKE moves its entire negotiation from UDP 500 to UDP 4500. The key point: the real ESP runs over UDP 4500, not as protocol 50.
The flow:
Tanpa NAT : IKE (UDP 500) -> ESP (protocol 50)
Dengan NAT: IKE (UDP 4500) -> ESP dalam UDP (UDP 4500)When IKEv1 Phase 1 completes, both parties send NAT-D (NAT Discovery) payloads containing a hash of the source and destination addresses. If the hash does not match what the peer sent, there is NAT between them. The entire exchange then moves to UDP 4500, and every ESP packet is wrapped with a UDP header that gives NAT a port to translate.
Libreswan and strongSwan detect and perform this switch automatically. All you need to do is make sure the relevant options are active.
Libreswan enables NAT-T by default, but for remote access behind NAT, activate nat_traversal and force_keepalive:
conn L2TP-PSK
nat_traversal=yes
force_keepalive=yesforce_keepalive=yes keeps the NAT router holding the UDP 4500 mapping by sending periodic keepalives — without it, the mapping can expire and the connection dies while idle.
In strongSwan, enable it in swanctl.conf and strengthen the keepalive:
connections {
L2TP-NAT {
version = 1
proposals = aes256-sha256-modp2048
local-1 {
auth = psk
}
remote-1 {
auth = psk
}
children {
L2TP {
type = transport
local_ts = udp/1701
remote_ts = dynamic[udp]
esp_proposals = aes128-sha1
}
}
}
}
charon {
keep_alive = 20s
nat_keepalive = 20s
}The nat_keepalive = 20s value sends a NAT keepalive every 20 seconds.
xl2tpd needs no special configuration for NAT-T because L2TP always runs over UDP 1701, which is already wrapped in ESP. What you need to ensure is ipsec saref = yes in [global] for full compatibility with modern NAT-T.
Verify that NAT-T is active after a client connects:
sudo ipsec statusall | grep -i nat
sudo journalctl -u strongswan | grep -i natA line showing NAT-Traversal: yes or nat_traversal confirms that the connection uses UDP 4500.
Info
The firewall must open UDP 4500 in both directions, not just UDP 500. Many remote access failures from clients behind NAT are caused by UDP 4500 not being opened.
Episode 14 explained NAT-T from the root problem to the configuration: the NAT-unfriendly ESP protocol 50, the NAT-D mechanism and ESP wrapping into UDP 4500, the Libreswan and strongSwan options, and xl2tpd's passive role.
Key takeaways:
force_keepalive in Libreswan keeps the NAT mapping alive.nat_keepalive in the charon section.In the next episode, episode 15, we will discuss performance tuning and MTU — calculating the L2TP/IPsec overhead, applying MSS clamping, setting the PPP interface MTU, and optimizing performance with AES-NI and multiprocessing.