Openiked

FreeBSD port

I have made a FreeBSD port of the portable version of OpenBSD's IKEv2 daemon, Openiked. It's submitted as security/openiked.

Please note that this is the straight version and not my NAT-T development version (see below). Also, since there hasn't been any official releases of Openiked yet the port uses a Git version from 2013-03-12.

You can follow its progress here:

http://www.freebsd.org/cgi/query-pr.cgi?pr=177651

It's also available here as a compressed share archive: openiked-20130404.shar.bz2.

This shar archive can be unpacked as openiked under /usr/ports/security.

You will also have to add this to /usr/ports/UIDs and /usr/ports/GIDs accordingly:

--- UIDs~       2013-04-04 13:35:18.000000000 +0200
+++ UIDs        2013-04-04 13:35:18.000000000 +0200
@@ -258,4 +258,5 @@
 ossecm:*:967:966::0:0:OSSEC mail user:/usr/local/ossec-hids:/usr/sbin/nologin
 ossecr:*:968:966::0:0:OSSEC rem user:/usr/local/ossec-hids:/usr/sbin/nologin
 kippo:*:969:969::0:0:kippo user:/nonexistent:/usr/sbin/nologin
+_iked:*:970:970::0:0:iked privsep user:/nonexistent:/usr/sbin/nologin
 nobody:*:65534:65534::0:0:Unprivileged user:/nonexistent:/usr/sbin/nologin

--- GIDs~       2013-04-04 13:35:31.000000000 +0200
+++ GIDs        2013-04-04 13:35:31.000000000 +0200
@@ -250,5 +250,6 @@
 elasticsearch:*:965:
 ossec:*:966:
 kippo:*:969:
+_iked:*:970:
 nogroup:*:65533:
 nobody:*:65534:

NAT-T on FreeBSD, Linux

I also tried to add NAT-traversal support on FreeBSD. No development has happened since May, 2013.

My NAT-T branch is called "natt" and lives here:

http://github.com/mchackorg/openiked/

It seems to work, but only one way. The receiving node doesn't seem to unpack the ESP from UDP.

The IKE dialogue succesfully negotiates security associations and both ends agree to use ESP-UDP. When I try to ping a node on the other end my ICMP packets are succesfully encapsulated as ESP-UDP and can be seen as such on the other end. However, it seems the ESP-UDP packets are thrown away.

In May 2013 I declared that I'm stuck. Does anyone have any idea what's happening here and what I can do about it?

Here I've collected some information to recreate my test setup so someone, perhaps myself, can be brought up to speed to finish the work. Consider this a sort of braindump.

Here's an old fashioned patch: patch-20130515.txt

The code is quite simple. For every Security Association established, usually one in each direction, we tell the IPsec stack to use UDP encapsulation

SADB_X_EXT_NAT_T_TYPE UDP_ENCAP_ESPINUDP

and set the ports we're going to use, source port:

SADB_X_EXT_NAT_T_SPORT 4500

and destination port

SADB_X_EXT_NAT_T_DPORT 4500

When run this looks like this in a setkey -x dump:

sadb_ext{ len=1 type=20 }
sadb_x_nat_t_type{ type=2 }
sadb_ext{ len=1 type=21 }
sadb_x_nat_t_port{ port=4500 }
sadb_ext{ len=1 type=22 }
sadb_x_nat_t_port{ port=4500 }

That should do it, I thought. And it does, partially. Packets going out are encapsulated in UDP.

The situation is the same for FreeBSD and Linux.

ESP in UDP isn't recognized

The test network consists of two virtual machines running Linux on a FreeBSD hosts that also acts as router:

active             router/nat           passive
10.0.0.8 - 10.0.0.5 em0 em1 10.0.2.1 - 10.0.2.2

After a succesful IKEv2 dialogue we have situation described below. This is under Linux. Note that if you're using FreeBSD on the test nodes you need setkey from the security/ipsec-tools port. The one included in base doesn't know anything about ESP-UDP.

On the active host:

# setkey -D
10.0.2.2[4500] 10.0.0.8[4500] 
        esp-udp mode=tunnel spi=2161504314(0x80d5f03a) reqid=0(0x00000000)
        E: aes-cbc  bb188555 0231925f 0169e61c d78cac7b 517cd4bb f32e9f9a 12fafda4 effa71c0
        A: hmac-sha1  3b2d6aa8 dfd99e30 fd7fcbd5 a164cfb4 e8cc8860
        seq=0x00000000 replay=64 flags=0x00000000 state=mature 
        created: May  6 15:35:13 2013   current: May  6 15:35:25 2013
        diff: 12(s)     hard: 10800(s)  soft: 9568(s)
        last:                           hard: 0(s)      soft: 0(s)
        current: 0(bytes)       hard: 536870912(bytes)  soft: 475667628(bytes)
        allocated: 0    hard: 0 soft: 0
        sadb_seq=1 pid=727 refcnt=0
10.0.0.8[4500] 10.0.2.2[4500] 
        esp-udp mode=tunnel spi=2189894628(0x828723e4) reqid=0(0x00000000)
        E: aes-cbc  0c2d9d3f d41169c8 24974e89 94cccd4a 504136e3 54e95e96 77b44011 0f599363
        A: hmac-sha1  8ec0eafb 8d56565a 815a66c7 3f9d2c00 f9c1e4e3
        seq=0x00000000 replay=64 flags=0x00000000 state=mature 
        created: May  6 15:35:13 2013   current: May  6 15:35:25 2013
        diff: 12(s)     hard: 10800(s)  soft: 9968(s)
        last:                           hard: 0(s)      soft: 0(s)
        current: 0(bytes)       hard: 536870912(bytes)  soft: 495531851(bytes)
        allocated: 0    hard: 0 soft: 0
        sadb_seq=0 pid=727 refcnt=0
# setkey -DP
10.0.2.2[any] 10.0.0.8[any] reserved
        in ipsec
        esp/tunnel/10.0.2.2-10.0.0.8/use
        created: May  6 15:35:13 2013  lastused:                     
        lifetime: 0(s) validtime: 0(s)
        spid=312 seq=1 pid=728
        refcnt=1
10.0.0.8[any] 10.0.2.2[any] reserved
        out ipsec
        esp/tunnel/10.0.0.8-10.0.2.2/require
        created: May  6 15:35:13 2013  lastused:                     
        lifetime: 0(s) validtime: 0(s)
        spid=305 seq=2 pid=728
        refcnt=1

On the passive host:

# setkey -D
10.0.2.2[4500] 10.0.2.1[4500] 
        esp-udp mode=tunnel spi=2161504314(0x80d5f03a) reqid=0(0x00000000)
        E: aes-cbc  bb188555 0231925f 0169e61c d78cac7b 517cd4bb f32e9f9a 12fafda4 effa71c0
        A: hmac-sha1  3b2d6aa8 dfd99e30 fd7fcbd5 a164cfb4 e8cc8860
        seq=0x00000000 replay=64 flags=0x00000000 state=mature 
        created: May  6 15:35:13 2013   current: May  6 15:36:15 2013
        diff: 62(s)     hard: 10800(s)  soft: 9741(s)
        last:                           hard: 0(s)      soft: 0(s)
        current: 0(bytes)       hard: 536870912(bytes)  soft: 484257562(bytes)
        allocated: 0    hard: 0 soft: 0
        sadb_seq=1 pid=722 refcnt=0
10.0.2.1[4500] 10.0.2.2[4500] 
        esp-udp mode=tunnel spi=2189894628(0x828723e4) reqid=0(0x00000000)
        E: aes-cbc  0c2d9d3f d41169c8 24974e89 94cccd4a 504136e3 54e95e96 77b44011 0f599363
        A: hmac-sha1  8ec0eafb 8d56565a 815a66c7 3f9d2c00 f9c1e4e3
        seq=0x00000000 replay=64 flags=0x00000000 state=mature 
        created: May  6 15:35:13 2013   current: May  6 15:36:15 2013
        diff: 62(s)     hard: 10800(s)  soft: 9763(s)
        last:                           hard: 0(s)      soft: 0(s)
        current: 0(bytes)       hard: 536870912(bytes)  soft: 485331304(bytes)
        allocated: 0    hard: 0 soft: 0
        sadb_seq=0 pid=722 refcnt=0
# setkey -DP
10.0.0.8[any] 10.0.2.2[any] reserved
        in ipsec
        esp/tunnel/10.0.2.1-10.0.2.2/use
        created: May  6 15:35:13 2013  lastused:                     
        lifetime: 0(s) validtime: 0(s)
        spid=312 seq=1 pid=723
        refcnt=1
10.0.2.2[any] 10.0.0.8[any] reserved
        out ipsec
        esp/tunnel/10.0.2.2-10.0.2.1/require
        created: May  6 15:35:13 2013  lastused:                     
        lifetime: 0(s) validtime: 0(s)
        spid=305 seq=2 pid=723
        refcnt=1

Pinging from active to passive we see this on the passive side:

# tcpdump -i eth0 -n udp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
16:58:13.658616 IP 10.0.2.1.4500 > 10.0.2.2.4500: UDP-encap: ESP(spi=0x133a7a52,seq=0x1), length 132
16:58:14.658114 IP 10.0.2.1.4500 > 10.0.2.2.4500: UDP-encap: ESP(spi=0x133a7a52,seq=0x2), length 132
16:58:15.658020 IP 10.0.2.1.4500 > 10.0.2.2.4500: UDP-encap: ESP(spi=0x133a7a52,seq=0x3), length 132
16:58:16.657999 IP 10.0.2.1.4500 > 10.0.2.2.4500: UDP-encap: ESP(spi=0x133a7a52,seq=0x4), length 132

so traffic is coming through. Note that the source 10.0.0.8 is translated to 10.0.2.1. If I ping the other way (10.0.2.2->10.0.0.8) traffic comes through in the other direction as well:

# tcpdump -i eth0 -n udp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
17:00:02.226220 IP 10.0.2.2.4500 > 10.0.0.8.4500: UDP-encap: ESP(spi=0xe759e15b,seq=0x1), length 132
17:00:03.223184 IP 10.0.2.2.4500 > 10.0.0.8.4500: UDP-encap: ESP(spi=0xe759e15b,seq=0x2), length 132
17:00:04.223011 IP 10.0.2.2.4500 > 10.0.0.8.4500: UDP-encap: ESP(spi=0xe759e15b,seq=0x3), length 132

but nothing is unpacked from UDP.

This was under Linux. The situation is the same in FreeBSD.

I looked through the FreeBSD kernel to find the way packets are unpacked. The packets seem to go this path:

sys/netinet/udp_usrreq.c: udp_input() -> udp_append() -> udp4_espdecap()

In udp_append() there's some code dealing with UDP encapsulated ESP, so I added a small debug printout:

--- udp_usrreq.c-orig   2013-02-12 14:28:30.000000000 +0100
+++ udp_usrreq.c        2013-02-12 14:28:08.000000000 +0100
@@ -284,6 +284,7 @@ udp_append(struct inpcb *inp, struct ip
 #ifdef IPSEC_NAT_T
        up = intoudpcb(inp);
        KASSERT(up != NULL, ("%s: udpcb NULL", __func__));
+       log(LOG_DEBUG, "udp_append in NAT_T. up->u_flags: %d\n", up->u_flags);
        if (up->u_flags & UF_ESPINUDP_ALL) {    /* IPSec UDP encaps. */
                n = udp4_espdecap(inp, n, off);
                if (n == NULL)                          /* Consumed. */

When I see ESP-UDP traffic coming in I see this in the log for every packet received:

udp_append in NAT_T. up->u_flags: 0

I was expecting this flag variable to be set. Where should it be set?

Strongswan

I tried to use Strongswan under Linux in the same test network to see if it works with NAT-traversal. It does. Configuration below.

Active ipsec.conf:

conn rw
    left=10.0.0.8
    leftcert=brain-cert.pem
    type=tunnel
    right=10.0.2.2
    rightcert=ipsec3-cert.pem
    keyexchange=ikev2
    auto=start

Passive ipsec.conf:

conn rw
    left=10.0.2.2
    leftcert=ipsec3-cert.pem
    leftid=@ipsec3.hack.org
    type=tunnel
    right=%any
    rightcert=brain-cert.pem
    rightsubnet=10.0.0.8/32
    keyexchange=ikev2
    auto=add

I dumped all the configuration Strongswan's charon does to the IPsec stack with setkey -x: strongswan-client.dump.bz2.

Compare it to what Openiked does: openiked-client.dump.bz2.

enc0

enc0 under FreeBSD gives nothing when I use tcpdump on it when I'm running Openiked on FreeBSD. I don't know why.

pf NAT stops working

I may have stumbled on a possible bug in FreeBSD's version of pf(4). With this simple rule as the sole rule in pf.conf:

nat on em1 inet from ! (em1) to any -> (em1)

NAT works fine until IKE has concluded and ESP traffic starts flowing. Then address translation stops working.

This is under FreeBSD 9.0. I have not yet tested 9.1 or current.

If I use ipfw(8) instead of pf, everything works fine.

Configuration

My test network is made up of two virtual machines running on a host which acts as the router and NAT between them. The network looks like this:

active             router/nat           passive
10.0.0.8 - 10.0.0.5 em0 em1 10.0.2.1 - 10.0.2.2

FreeBSD kernel

If you want to use IPsec on FreeBSD you need to build a custom kernel with

options IPSEC
options IPSEC_NAT_T
    
device crypto

and, if you want an enc(4) device:

device enc

Router/NAT

# ipfw nat 123 config if em1
# ipfw add 50 nat 123 ip from any to any via em1

iked.conf

Active

ikev2 active esp from 10.0.0.8 to 10.0.2.2 srcid totoro.hack.org

Passive

ikev2 passive esp from 10.0.2.2/32 to 10.0.0.8/32 local 10.0.2.2 peer any srcid totoro.hack.org

Keys

Create a CA:

% ikectl ca vpn create

Generate a host key pair and certificate:

% ikectl ca vpn certificate totoro.hack.org create

Or use these: totoro.hack.org.tgz.


Last updated: <2013-12-06 16:12:08 CET>