connmark Plugin

Purpose

The connmark plugin for libcharon uses Linux Netfilter conntrack marks on transport mode connections to separate flows between clients. As two transport mode clients behind the same NAT use identical IPsec policies, some special binding of upper layer protocols is required to return data over the correct SA. While any client-initiated protocol supported by conntrack can be separated, the main purpose is to differentiate multiple L2TP sessions. The plugin only works for transport mode connections.

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

--enable-connmark

Building the plugin requires iptables development headers to be installed.

Configuration

The connmark plugin currently is used on any transport mode SA negotiated that uses a unique mark. To configure such a connection as responder, use the following options in your connection definition:

connections {

  transport-connmark {
    # ...
    children {
      transport-connmark {
        mode = transport
        mark = %unique
        #...
      }
    }
  }

A unique mark per negotiated SA is required so that the SAs can be distinguished. The plugin automatically configures Netfilter rules in the mangle table to apply and restore the marks.

Netfilter Rules and connmark

To find the correct return path for a protected upper layer connection, Netfilter connmarks in the mangle table are used.

In PREROUTING a gateway applies the unique mark assigned to the SA to the packet. This makes sure the IPsec policy actually matches, as we require the correct mark for a policy match. For non-NAT situations, ESP matching is used to MARK packets, for NAT situations the packets get selected based on UDP encapsulation ports.

In the INPUT chain IPsec policy matching is used to apply the IPsec policy mark as a CONNMARK. This basically copies the IPsec policy mark to the conntrack entry, so it can later be restored.

On the OUTPUT chain the CONNMARK target is used to to restore the mark from the conntrack entry to the packet. This ensures that the correct mark is applied to select the SA from the same pair that has been used during inbound processing.

The rules installed by the plugin for two clients behind the same NAT router looks something like:

Chain PREROUTING (policy ACCEPT 82 packets, 16106 bytes)
 pkts bytes target    prot opt in out  source       destination
   19  3920 MARK      udp  --  *  *    192.168.0.1  192.168.0.2  udp spt:1024 dpt:4500 MARK set 0x2
   16  3584 MARK      udp  --  *  *    192.168.0.1  192.168.0.2  udp spt:4500 dpt:4500 MARK set 0x1

Chain INPUT (policy ACCEPT 82 packets, 16106 bytes)
 pkts bytes target    prot opt in out  source       destination
   19  2803 CONNMARK  all  --  *  *    192.168.0.1  192.168.0.2  policy match dir in pol ipsec spi 0xb05370cd CONNMARK set 0x2
   16  2647 CONNMARK  all  --  *  *    192.168.0.1  192.168.0.2  policy match dir in pol ipsec spi 0x8ab8f2c3 CONNMARK set 0x1

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target    prot opt in out  source       destination

Chain OUTPUT (policy ACCEPT 91 packets, 17770 bytes)
 pkts bytes target    prot opt in out  source       destination
   73 14634 CONNMARK  all  --  *  *    192.168.0.2  192.168.0.1  CONNMARK restore
   75 16755 CONNMARK  all  --  *  *    192.168.0.2  192.168.0.1  CONNMARK restore

Windows L2TP

The Windows L2TP client always uses udp/1701 as source and destination ports. Unfortunately conntrack can’t properly track these udp streams when clients are behind the same NAT. Because all streams have identical src/dst tuples, the flow can’t be separated and only one conntrack entry will exist. Only traffic to the client that last sent an inbound packet is active as all outbound packets will be set to the same mark (and thus be returned over the same SA). A solution might be for a L2TP daemon to track which mark belongs to which client and set correct marks on the outgoing packets. Together with Connmark plugin this would make 2 Windows hosts behind the same NAT successfully use L2TP.

An experimental patch for xl2tpd can be found in the xl2tpd Github issue 82

Example

topology
Figure 1. strongSwan example showing the use of the connmark plugin