kernel-libipsec Plugin


The kernel-libipsec plugin for libcharon provides an IPsec backend that works entirely in userland, using TUN devices and our own libipsec IPsec implementation, see src/libipsec.

Both other kernel interfaces, kernel-netlink (the default) and kernel-pfkey, install IPsec SAs in the operating system’s IPsec stack. This plugin provides an alternative, for instance, if the OS implementation does not support a required algorithm (e.g. AES-GCM on macOS). However, it generally performs worse than the OS kernel’s IPsec stack. In particular, when handling a lot of SAs and/or traffic, thus it is not recommended to be used on security gateways.

The plugin is disabled by default and can be enabled with the ./configure option


A network kernel backend is still required, so either the kernel-netlink or the kernel-pfroute plugin has to be enabled, too.


With the plugin enabled, a TUN device is created on startup that will be used to handle cleartext traffic from and to the host. For each IPsec SA routes get installed that direct traffic to the TUN device, from there the plugin reads the cleartext packets and encrypts them via libipsec.

The resulting ESP packets are usually sent over the UDP sockets the daemon uses for IKE traffic, that is, the plugin enforces UDP encapsulation (NAT-T). On Linux, handling of raw ESP packets can be enabled since version 5.9.11 to also send/receive ESP packets without UDP-encapsulation (this uses RAW sockets).

ESP packets that are received are decrypted by libipsec and then injected via TUN device. libipsec can use all ciphers supported for IKE to encrypt and authenticate traffic.

Since version 5.9.11, trap policies are supported to trigger the negotiation of SAs once matching traffic hits the TUN device.

libipsec is not intended for scenarios with high amounts of traffic or high burst traffic. It is not optimized for performance and buffers each packet in memory. In combination with insufficient processor power this will lead to a out of memory condition and a crash of charon or the whole device. SA and policy lookups are also not particularly optimized.

On systems that use the kernel-pfroute plugin (FreeBSD or macOS) a separate TUN device will be created for each virtual IP. On Linux this is not required.


The kernel-libipsec plugin is configured using the following options in the charon.plugins.kernel-libipsec section of strongswan.conf:

Key Default Description



Allow that the remote traffic selector equals the IKE peer



Firewall mark to set on outbound raw ESP packets. Since version 5.9.11 [charon.plugins.socket-default.fwmark]



Whether to send and receive ESP packets without UDP encapsulation if supported on this platform and no NAT is detected. Since version 5.9.11

Host-to-Host Tunnels

If the IKE peer is included in the remote traffic selector a separate route is installed that excepts such traffic from the route via TUN device to allow further IKE traffic between the peers (otherwise a routing loop would ensue). But if the remote traffic selector equals the IKE peer this won’t work anymore. Therefore such traffic selectors are not allowed by default.

It is possible to use such traffic selectors on newer Linux hosts by using fwmark options with the kernel-netlink and socket-default plugins.

The relevant strongswan.conf options are as follows:

charon {
  plugins {
    kernel-netlink {
      fwmark = !0x42
    socket-default {
      fwmark = 0x42
    kernel-libipsec {
      allow_peer_ts = yes
  • The first option configures the routing rule for strongSwan’s own routing table in such a way that the routes in that table will only apply to packets that do not feature the configured fwmark (0x42 in the example above).

  • The second option forces an fwmark of 0x42 on all packets sent by the IKE daemon. This includes IKE packets but also the UDP encapsulated ESP packets that are sent over that socket. Such traffic is now not affected by the routes (via TUN device) installed by strongSwan in its own routing table.

  • The third option finally enables negotiation of host-to-host tunnels.

To make the Linux kernel actually consider the routes as required the net.ipv4.conf.all.rp_filter setting has to be set:

# sysctl -w net.ipv4.conf.all.rp_filter=2

Read the documentation about this setting to understand the impact. It basically disables the rp_filter You possibly need to alter your local firewall rules to plug a hole caused by changing the setting.


Figure 1. strongSwan net-to-net example using libipsec
Figure 2. strongSwan host-to-host example using libipsec with fmark options