Subject: IPsec NAT traversal
To: None <tech-kern@netbsd.org>
From: Emmanuel Dreyfus <manu@netbsd.org>
List: tech-kern
Date: 09/12/2004 16:55:04
Hi

I'm working on IPsec NAT traversal (NAT-T) support in our kernel. The
goal is to have this quickly working with ipsec-tools's racoon (the
racoon for Linux, I've sent them patches to support NetBSD again), and
with KAME racoon once it will have integrated NAT-T.

Traversing NAT with IPsec is difficult for various reasons. Most can be
resolved at userland level, one cannot: getting ESP going through a NAT
is difficult because ESP has no port number, thus it cannot be
multiplexed.

The solution is to encapsulate ESP in UDP. Here is how this is done in
Linux, as far as I understood:

Sender side: racoon sends a message to the PF_KEY socket to tell the
kernel that a given SA should use ESP over UDP instead of plain ESP. It
also gives a few informations
- NAT-T type (older draft or newer, the encapsulation method has
changed)
- sender and reciever UDP ports
- original address of the system behind the NAT (sender or receiver? no
idea yet)

The kernel part must flag the SA as using ESP over UDP, and store sender
and reciever UDP ports. Easy. I don't know where the original address is
used.

When the data is sent, the ESP output method checks the status of the SA
and add an UDP header if needed, using the stored UDP port numbers.

Receiver side: racoon uset setsockopt to tell the kernel that an UDP
socket can receive ESP over UDP.

The kernel part must remeber that for the socket. As I understood, we
have no UDP control block in our kernel. Because it's only one bit, I
stored the information in the flags field of the IP control block
instead of adding an UDP control block. That's both simplier and more
efficient, but it might be not clean enough. Should I add an UDP control
block for a single bit to be stored?

In the UDP input method, the recieving socket is checked for the ESP
over UDP flag. If it is set and if the packet contains and ESP datagram,
then the UDP header is removed (IP header is changed a bit to reflect
that the encapsulated protocol is ESP and that the length is shorter),
and the result is sent to the ESP input method.

If the UDP payload does not look like ESP, the packet continues its
route as a normal UDP packet. As I understood, racoon uses the same
socket for ISAKMP exchanges and ESP over UDP. That's why the UDP payload
must be checked: the socket gets both ESP over UDP traffic and ISAKMP
over UDP.

Does all of this looks sane enough for our kernel? Note that it wouldn't
be easy to change the API, because it would require tweaks to racoon,
that we don't maintain on our own (either ipsec-tools racoon or KAME
racoon).
 
-- 
Emmanuel Dreyfus
Il y a 10 sortes de personnes dans le monde: ceux qui comprennent 
le binaire et ceux qui ne le comprennent pas.
manu@netbsd.org