NAT with IPsec on 2.6 kernel

This mini HOWTO explains a solution for a typical situation, where a homeworker...

The picture attempts to describes the situation:

Network scheme

Kernel 2.4.x + FreeS/WAN

In the old forgotten ages of 2.4 it was simple - the IPsec tunnel created its own interface called ipsec0 and all traffic leaving through the tunnel could have been NATted by attaching a hook to that interface.

Example:

iptables -t nat -A POSTROUTING -o ipsec0 -j SNAT --to 10.20.30.2
iptables -t nat -A POSTROUTING -o eth0 -j SNAT --to 200.2.2.2

Kernel 2.6.x

Unfortunately there is no ipsec0 in 2.6 anymore. Life is hard. Your first idea might be to do the NAT on the outgoing interface and differentiate the resulting address (--to a.b.c.d) according to the packet destination. E.g. NAT to 10.20.30.2 for packets to 10.0.0.0/8, otherwise NAT to 200.2.2.2. This does not work. The reason is that on the outgoing interface you normally see only the resulting ESP packets. NATing these doesn't make any sense.

The proper way is to catch the packet yet before it is encrypted, NAT it and then encrypt and encapsulate to ESP and send out. With vanilla kernel 2.6.5 this is not possible. But don't give up! There are patches by Patrick McHardy that allow these packet games. In order to use it you need to patch both kernel and the userspace iptables. From now on I assume that you have the patched kernel running and patched iptables ready.

Background

Every packed being received, forwarded or sent is checked against SPD (Security Policy Database) to see if it should be encapsulated (e.g. to ESP), discarded, or sent as is. SPD rules are of three types: in, fwd and out and are usually set by the userspace IPsec tool (e.g. setkey in the case of IPsec-tools or pluto in case of FreeS/WAN and its otherSWAN successors).

Example of such a SPD rule can be:

   # setkey -DP
1: 10.20.30.2[any] 10.0.0.0/8[any] any
2:        out ipsec
3:        esp/tunnel/200.2.2.2-100.1.1.1/require
4:        created: Jun 17 18:01:03 2004  lastused: Jun 17 18:33:17 2004
5:        lifetime: 0(s) validtime: 0(s)
6:        spid=305 seq=12 pid=3848
7:        refcnt=1

Every SPD rule applies to a packet going from one address (10.20.30.2) to another (10.0.0.0/8), optionally with some more constraints, e.g. protocol. In our example case a packet going from 10.20.30.2 to 10.0.0.1 would match the rule. Now the kernel learns that it should use ipsec (line #2, other possibilities are none to send it as is and discard to drop it silently). From line #3 it learns that it must encapsulate the packet to esp in tunnel mode. As a result of this transformation there will be an ESP packet prepared to travel from 200.2.2.2 to 100.1.1.1.

[ ... to be continued ... ]
Place for your feedback...
13th April 2006 at 12:25
kernel-xfrm-block.diff causes repeateable kernel crash
Hi Michal!

First - thank you very much for this "Resource temporarily unavailable" patch. I am using it now and I do not understand why it is still not in the main kernel trunk.

Second. I am getting repeateable crashes with the patch. The kernel oops messages are at
http://konstantin.shemyak.com/tmp/kernel-xfrm-block-crash.txt

This happens when issuing setkey command on a machine, whose IPSec peer is behind *a slow network*. No crashes were noticed with normal Ethernet, but when the packets from the peer are delayed/dropped (artificially, at the gateway machine), crash happens about each 10th call of setkey.

No crashes were observed with unpatched kernel, although the test could not be repeated 100% the same (because of that EAGAIN).

Kernels tried are 2.6.16-2 and 2.6.9-22.

If you are not planning to solve/look at this problem, could you please suggest me someone else "whom to complain" or maybe give a pointer where to look for the problem - I am very much willing to get this to work.

Thank you!
Oct 4   12:19 kernel-xfrm-block.diff Kernel Crash (by Bob Martin)
Apr 13   12:25 kernel-xfrm-block.diff causes repeateable kernel crash (by Konstantin Shemyak)