Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 05 Sep 2012 15:02:20 +0200
From:      Andreas Longwitz <longwitz@incore.de>
To:        freebsd-net@freebsd.org
Subject:   Support for IPSec VPN's: some patches for netipsec/key.c
Message-ID:  <50474D5C.4020003@incore.de>

next in thread | raw e-mail | index | archive | help
Hi, as continuation of
http://lists.freebsd.org/pipermail/freebsd-stable/2012-April/067307.html
I like to describe what I have done to get smartphones with IPSec VPN's
working with a FreeBSD 8.3 server.

The clients are IPhones with Cisco IPSec (authentication_method
xauth_rsa_server in tunnel mode) and Androids with L2TP over IPSec
(authentication_method rsasig in transport mode). On the server I have
FreeBSD 8.3 with NAT-T support and the ports ipsec-tools-0.8.0_2 and
mpd-5.5.

To filter all packets in transport and tunnel mode on the enc0
interface, I use net.enc.out.ipsec_filter_mask=1 and
net.enc.in.ipsec_filter_mask=3. Further my server has included
the patches given in kern/146190 to ignore checksums and kern/169620 to
avoid packet bypass on ngX.

The following patches are all for netipsec/key.c:

I use parameter "generate_policy on" in racoon.conf. This works for
clients with NAT-T, but direct connected clients need the following
patch (likewise in ipsec-tools/roadwarrior/client/phase1-up.sh):

@@ -1927,19 +1930,27 @@
 #if 1
        if (newsp->req && newsp->req->saidx.src.sa.sa_family) {
              struct sockaddr *sa;
+             uint16_t *pport;
              sa = (struct sockaddr *)(src0 + 1);
              if (sa->sa_family != newsp->req->saidx.src.sa.sa_family) {
                      _key_delsp(newsp);
                      return key_senderror(so, m, EINVAL);
              }
+             pport = (uint16_t *)newsp->req->saidx.src.sa.sa_data;
+             if ( *pport == htons(500) ) /* UDP_ENCAP_ESPINUDP_PORT */
+                *pport = 0;
        }
        if (newsp->req && newsp->req->saidx.dst.sa.sa_family) {
              struct sockaddr *sa;
+             uint16_t *pport;
              sa = (struct sockaddr *)(dst0 + 1);
              if (sa->sa_family != newsp->req->saidx.dst.sa.sa_family) {
                      _key_delsp(newsp);
                      return key_senderror(so, m, EINVAL);
              }
+             pport = (uint16_t *)newsp->req->saidx.dst.sa.sa_data;
+             if ( *pport == htons(500) ) /* UDP_ENCAP_ESPINUDP_PORT */
+                *pport = 0;
        }
 #endif

The next patch eliminates a (probably not important) mistake in loop
handling and an important change in calling key_cmpsaidx() from
key_getsah(). With this patch mixed transport and tunnel modes behind
the same router work correct.

@@ -1312,11 +1312,14 @@
                        continue;
                if (key_cmpspidx_exactly(spidx, &sp->spidx)) {
                        SP_ADDREF(sp);
-                       break;
+                       SPTREE_UNLOCK();
+                       goto found;
                }
        }
        SPTREE_UNLOCK();
+       return NULL;

+   found:
        return sp;
 }

@@ -2968,11 +2983,15 @@
        LIST_FOREACH(sah, &V_sahtree, chain) {
                if (sah->state == SADB_SASTATE_DEAD)
                        continue;
-               if (key_cmpsaidx(&sah->saidx, saidx, CMP_REQID))
-                       break;
+               if (key_cmpsaidx(&sah->saidx, saidx, CMP_MODE_REQID)) {
+                       SAHTREE_UNLOCK();
+                       goto found;
+               }
        }
        SAHTREE_UNLOCK();
+       return NULL;

+   found:
        return sah;
 }

The last patch makes it possible for a transport mode client to open a
new connection to the server immediately after closing an old
connection. Without this patch the client must wait for the routers to
forget all there NAT entries.

@@ -4065,10 +4084,12 @@
          /*
           * If NAT-T is enabled, check ports for tunnel mode.
           * Do not check ports if they are set to zero in the SPD.
-          * Also do not do it for transport mode, as there is no
+          * Also do not do it for native transport mode, as there is no
           * port information available in the SP.
           */
-         if (saidx1->mode == IPSEC_MODE_TUNNEL &&
+         if ((saidx1->mode == IPSEC_MODE_TUNNEL ||
+             (saidx1->mode == IPSEC_MODE_TRANSPORT &&
+             saidx1->proto == IPPROTO_ESP)) &&
              saidx1->src.sa.sa_family == AF_INET &&
              saidx1->dst.sa.sa_family == AF_INET &&
              ((const struct sockaddr_in *)(&saidx1->src))->sin_port &&


One case is left: At the moment it is not possible for the kernel to
handle more than one IPSEC/L2TP (transport mode) connection from clients
behind the same NAT router. To get rid of this limitation the kernel
must do some housekeeping for the clients inner and outer udp src ports
(the corresponding dst ports are 1701 and 4500) to find the correct
SA for outgoing packets. I do not know what would be the best place to
store these informations. Any suggestions ?

At the end a question: At the beginning of ip_ipsec_output() in
ip_ipsec.c the flag PACKET_TAG_IPSEC_PENDING_TDB is used, but I can not
find the place where this flag is set in the kernel. Can somebody
enlighten me ?

-- 
Dr. Andreas Longwitz

Data Service GmbH
Beethovenstr. 2A
23617 Stockelsdorf
Amtsgericht Lübeck, HRB 318 BS
Geschäftsführer: Wilfried Paepcke, Dr. Andreas Longwitz, Josef Flatau




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?50474D5C.4020003>