NAT Traversal

The IKEv2 protocol includes NAT Traversal (NAT-T) in the core standard but it is optional to implement for vendors. The strongSwan charon daemon implements NAT-Traversal without any special prior configuration but the mechanism cannot be disabled, either.
If you don’t like the automatic port floating to UDP port 4500 due to the MOBIKE protocol which happens even if no NAT situation exists, then you can disable MOBIKE by setting mobike = no in the swanctl.conf connection definition.

Since the ESP protocol with IP protocol number 50 doesn’t have any ports, per se it is not suited for Port Address Translation, the standard method of traversing a NAT router for the TCP and UDP protocols.

Some NAT routers have a feature, often called something like IPsec Passthrough that detects outbound IKE traffic from a single host behind the NAT device and will forward inbound IKE and ESP packets to that specific host as shown in the figure below

IPsec Forwarding

Unfortunately this won’t work with multiple IPsec clients behind the same NAT router that all want to communicate with the same VPN gateway as shown in the network topology below

NAT Traversal

The solution proposed by RFC 3948 is to encapsulate ESP packets in UDP datagrams which then allows to apply Port Address Translation as shown in the figure above. The well-known NAT Traversal UDP port 4500 is shared with the IKE protocol when a NAT situation is detected between the two IPsec endpoints. The detection is based on the NAT_DETECTION_SOURCE_IP and NAT_DETECTION_DESTINATION_IP notifications sent in the IKE_SA_INIT exchange that contain source and destination IP address hashes, respectively.

ESP-in-UDP encapsulation can be enforced even if no NAT situation exists by setting encap = yes for a given connection definition in swanctl.conf. If enabled, the charon daemon will send a manipulated NAT_DETECTION_SOURCE_IP notify payload so that it will look to the remote peer as if there were a NAT situation.

ESP-in-UDP Encapsulation

ESP-in-UDP encapsulation means that an eight octet UDP header is inserted between the IP Header and the ESP Header of the ESP packet. At the outset the UDP source and destination ports are both set to the well-known value 4500 but might get changed on the way by one or several NAT routers.

ESP in UDP Encapsulation

The first field in the ESP header right after the UDP header is the 32 bit non-zero Security Parameters Index (SPI).

Non-ESP Marker

If the first 32 bits right after the UDP header are set to zero then instead of an encapsulated ESP payload packet, an IKE management packet is carried. Thus this four octet all-zero Non-ESP Marker is used to differentiate between ESP and IKE traffic. ESP packets are processed in the kernel, whereas the IKE packets are forwarded to the charon userland IKE daemon.

Non-ESP Marker

The insertion of a Non-ESP Marker means that the default UDP 4500 socket/port has to handle traffic differently from the default IKE UDP 500 socket/port. This has some implications when using a custom server port (see below).

NAT-T Keepalives

When a NAT router applies Port Address Translation to an outbound IP packet, the address/port mapping is stored in an internal lookup table together with a time-to-live value. This mapping is needed by the router so that inbound IP packets can be translated back to the original address/port values.

NAT Keepalive

Since an established IPsec connection can be inactive for minutes or even hours, the IPsec peer behind a NAT router has to send periodic NAT-T keepalive UDP packets containing a single 0xff byte in order to refresh the NAT mapping entry in the NAT router’s lookup table.

Of course the NAT-T keepalives also reach the IPsec peer on the other side of the connection but the packets are silently dropped by the kernel. By default the keep-alives are sent ever 20s but the interval can configured via the charon.keep_alive parameter in strongswan.conf (set to 0 to disable sending keepalives, e.g. behind a static DNAT aka port forwarding).

Custom Server Ports

When using custom server ports, the client for simplicity only uses a single remote port configured with remote_port in swanctl.conf. This means that there will not be a port switch while establishing the connection. As described above, if UDP encapsulation is used, the ESP packets are sent on the ports already used for IKE traffic. Therefore, the server must be prepared to process UDP-encapsulated ESP packets on that custom port and consequently is only able to accept IKE packets with non-ESP marker on it. That in turn forces the client to send all its IKE packets (including the initial IKE_SA_INIT request) with a non-ESP marker. Otherwise they would be treated as UDP-encapsulated ESP packets.

This has implications for the client and the server configuration:


Because the client has to connect to a socket/port that is prepared to process UDP-encapsulated ESP packets, the correct setting to specify a custom port is charon.port_nat_t. The charon.port setting is not relevant in this scenario (i.e. the default socket/port will not be used, hence inbound traffic to port 500 could be blocked).


The client must add a non-ESP marker when sending IKE packets to a custom server port or port 4500. strongSwan adds one if neither source nor destination port is 500. This means the client can’t use port 500 in order to already add a non-ESP marker when sending the initial IKE_SA_INIT request. This can be achieved by either setting the local port used for IKE (local_port in swanctl.conf) to 4500 or by setting charon.port on the client to either 0 to allocate a random port or any number != 500 (if that port is not used by any other process), so that the source port won’t be 500 and does not have to be set explicitly in the connection config. If the latter is done, the client will, however, switch to the second source port configured via charon.port_nat_t if a NAT situation is detected or MOBIKE is enabled, so setting both to 0 usually makes most sense for mobile clients that connect to a custom server port (but leaving that at 4500 is usually not a problem, either).


IKEv1 traffic is automatically handled by the charon daemon which supports NAT traversal according to RFC 3947 and some of its early drafts without having to enable NAT traversal explicitly but it can’t be disabled either, though.