Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys Support IPv6 NAT-T. Implemented by hsuenaga@IIJ and ohis...



details:   https://anonhg.NetBSD.org/src/rev/fc63637bb960
branches:  trunk
changeset: 837171:fc63637bb960
user:      knakahara <knakahara%NetBSD.org@localhost>
date:      Thu Nov 22 04:48:34 2018 +0000

description:
Support IPv6 NAT-T. Implemented by hsuenaga@IIJ and ohishi@IIJ.

Add ATF later.

diffstat:

 sys/netinet/udp_usrreq.c    |    6 +-
 sys/netinet6/in6_pcb.h      |    4 +-
 sys/netinet6/udp6_usrreq.c  |  194 ++++++++++++++++++++++++++++++++++++++++++-
 sys/netinet6/udp6_var.h     |    4 +-
 sys/netipsec/ipsec.c        |   40 ++++++++-
 sys/netipsec/ipsec.h        |    5 +-
 sys/netipsec/ipsec_output.c |   39 +++++++-
 7 files changed, 270 insertions(+), 22 deletions(-)

diffs (truncated from 506 to 300 lines):

diff -r 380a39a57849 -r fc63637bb960 sys/netinet/udp_usrreq.c
--- a/sys/netinet/udp_usrreq.c  Wed Nov 21 22:42:26 2018 +0000
+++ b/sys/netinet/udp_usrreq.c  Thu Nov 22 04:48:34 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: udp_usrreq.c,v 1.256 2018/09/14 05:09:51 maxv Exp $    */
+/*     $NetBSD: udp_usrreq.c,v 1.257 2018/11/22 04:48:34 knakahara Exp $       */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -66,7 +66,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: udp_usrreq.c,v 1.256 2018/09/14 05:09:51 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: udp_usrreq.c,v 1.257 2018/11/22 04:48:34 knakahara Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -413,7 +413,7 @@
                in6_in_2_v4mapin6(&ip->ip_dst, &dst6.sin6_addr);
                dst6.sin6_port = uh->uh_dport;
 
-               n += udp6_realinput(AF_INET, &src6, &dst6, m, iphlen);
+               n += udp6_realinput(AF_INET, &src6, &dst6, &m, iphlen);
        }
 #endif
 
diff -r 380a39a57849 -r fc63637bb960 sys/netinet6/in6_pcb.h
--- a/sys/netinet6/in6_pcb.h    Wed Nov 21 22:42:26 2018 +0000
+++ b/sys/netinet6/in6_pcb.h    Thu Nov 22 04:48:34 2018 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: in6_pcb.h,v 1.49 2017/03/02 05:26:24 ozaki-r Exp $     */
+/*     $NetBSD: in6_pcb.h,v 1.50 2018/11/22 04:48:34 knakahara Exp $   */
 /*     $KAME: in6_pcb.h,v 1.45 2001/02/09 05:59:46 itojun Exp $        */
 
 /*
@@ -137,6 +137,8 @@
 #define IN6P_LOWPORT           0x2000000 /* user wants "low" port binding */
 #define IN6P_ANONPORT          0x4000000 /* port chosen for user */
 #define IN6P_FAITH             0x8000000 /* accept FAITH'ed connections */
+/* XXX should move to an UDP control block */
+#define IN6P_ESPINUDP          INP_ESPINUDP /* ESP over UDP for NAT-T */
 
 #define IN6P_RFC2292           0x40000000 /* RFC2292 */
 #define IN6P_MTU               0x80000000 /* use minimum MTU */
diff -r 380a39a57849 -r fc63637bb960 sys/netinet6/udp6_usrreq.c
--- a/sys/netinet6/udp6_usrreq.c        Wed Nov 21 22:42:26 2018 +0000
+++ b/sys/netinet6/udp6_usrreq.c        Thu Nov 22 04:48:34 2018 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: udp6_usrreq.c,v 1.143 2018/11/06 04:27:41 ozaki-r Exp $ */
+/* $NetBSD: udp6_usrreq.c,v 1.144 2018/11/22 04:48:34 knakahara Exp $ */
 /* $KAME: udp6_usrreq.c,v 1.86 2001/05/27 17:33:00 itojun Exp $ */
 /* $KAME: udp6_output.c,v 1.43 2001/10/15 09:19:52 itojun Exp $ */
 
@@ -63,7 +63,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: udp6_usrreq.c,v 1.143 2018/11/06 04:27:41 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: udp6_usrreq.c,v 1.144 2018/11/22 04:48:34 knakahara Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_inet.h"
@@ -109,6 +109,7 @@
 
 #ifdef IPSEC
 #include <netipsec/ipsec.h>
+#include <netipsec/esp.h>
 #ifdef INET6
 #include <netipsec/ipsec6.h>
 #endif
@@ -135,6 +136,10 @@
 
 static void udp6_notify(struct in6pcb *, int);
 static void sysctl_net_inet6_udp6_setup(struct sysctllog **);
+#ifdef IPSEC
+static int udp6_espinudp(struct mbuf **, int, struct sockaddr *,
+       struct socket *);
+#endif
 
 #ifdef UDP_CSUM_COUNTERS
 #include <sys/device.h>
@@ -298,7 +303,9 @@
 {
        int s;
        int error = 0;
+       struct in6pcb *in6p;
        int family;
+       int optval;
 
        family = so->so_proto->pr_domain->dom_family;
 
@@ -324,7 +331,42 @@
                error = EAFNOSUPPORT;
                goto end;
        }
-       error = EINVAL;
+
+       switch (op) {
+       case PRCO_SETOPT:
+               in6p = sotoin6pcb(so);
+
+               switch (sopt->sopt_name) {
+               case UDP_ENCAP:
+                       error = sockopt_getint(sopt, &optval);
+                       if (error)
+                               break;
+
+                       switch(optval) {
+                       case 0:
+                               in6p->in6p_flags &= ~IN6P_ESPINUDP;
+                               break;
+
+                       case UDP_ENCAP_ESPINUDP:
+                               in6p->in6p_flags |= IN6P_ESPINUDP;
+                               break;
+
+                       default:
+                               error = EINVAL;
+                               break;
+                       }
+                       break;
+
+               default:
+                       error = ENOPROTOOPT;
+                       break;
+               }
+               break;
+
+       default:
+               error = EINVAL;
+               break;
+       }
 
 end:
        splx(s);
@@ -374,7 +416,7 @@
 
 int
 udp6_realinput(int af, struct sockaddr_in6 *src, struct sockaddr_in6 *dst,
-    struct mbuf *m, int off)
+    struct mbuf **mp, int off)
 {
        u_int16_t sport, dport;
        int rcvcnt;
@@ -382,6 +424,7 @@
        const struct in_addr *dst4;
        struct inpcb_hdr *inph;
        struct in6pcb *in6p;
+       struct mbuf *m = *mp;
 
        rcvcnt = 0;
        off += sizeof(struct udphdr);   /* now, offset of payload */
@@ -481,6 +524,32 @@
                                return rcvcnt;
                }
 
+#ifdef IPSEC
+               /* Handle ESP over UDP */
+               if (in6p->in6p_flags & IN6P_ESPINUDP) {
+                       struct sockaddr *sa = (struct sockaddr *)src;
+
+                       switch (udp6_espinudp(mp, off, sa, in6p->in6p_socket)) {
+                       case -1: /* Error, m was freed */
+                               rcvcnt = -1;
+                               goto bad;
+
+                       case 1: /* ESP over UDP */
+                               rcvcnt++;
+                               goto bad;
+
+                       case 0: /* plain UDP */
+                       default: /* Unexpected */
+                               /*
+                                * Normal UDP processing will take place,
+                                * m may have changed.
+                                */
+                               m = *mp;
+                               break;
+                       }
+               }
+#endif
+
                udp6_sendup(m, off, sin6tosa(src), in6p->in6p_socket);
                rcvcnt++;
        }
@@ -624,7 +693,7 @@
        dst.sin6_addr = ip6->ip6_dst;
        dst.sin6_port = uh->uh_dport;
 
-       if (udp6_realinput(AF_INET6, &src, &dst, m, off) == 0) {
+       if (udp6_realinput(AF_INET6, &src, &dst, &m, off) == 0) {
                if (m->m_flags & M_MCAST) {
                        UDP6_STATINC(UDP6_STAT_NOPORTMCAST);
                        goto bad;
@@ -1308,6 +1377,121 @@
        UDP6_STATINC(stat);
 }
 
+#ifdef IPSEC
+/*
+ * Returns:
+ *     1 if the packet was processed
+ *     0 if normal UDP processing should take place
+ *    -1 if an error occurred and m was freed
+ */
+static int
+udp6_espinudp(struct mbuf **mp, int off, struct sockaddr *src,
+    struct socket *so)
+{
+       const size_t skip = sizeof(struct udphdr);
+       size_t len;
+       void *data;
+       size_t minlen;
+       int ip6hdrlen;
+       struct ip6_hdr *ip6;
+       struct m_tag *tag;
+       struct udphdr *udphdr;
+       u_int16_t sport, dport;
+       struct mbuf *m = *mp;
+       uint32_t *marker;
+
+       /*
+        * Collapse the mbuf chain if the first mbuf is too short
+        * The longest case is: UDP + non ESP marker + ESP
+        */
+       minlen = off + sizeof(u_int64_t) + sizeof(struct esp);
+       if (minlen > m->m_pkthdr.len)
+               minlen = m->m_pkthdr.len;
+
+       if (m->m_len < minlen) {
+               if ((*mp = m_pullup(m, minlen)) == NULL) {
+                       return -1;
+               }
+               m = *mp;
+       }
+
+       len = m->m_len - off;
+       data = mtod(m, char *) + off;
+
+       /* Ignore keepalive packets */
+       if ((len == 1) && (*(unsigned char *)data == 0xff)) {
+               m_freem(m);
+               *mp = NULL; /* avoid any further processing by caller ... */
+               return 1;
+       }
+
+       /* Handle Non-ESP marker (32bit). If zero, then IKE. */
+       marker = (uint32_t *)data;
+       if (len <= sizeof(uint32_t))
+               return 0;
+       if (marker[0] == 0)
+               return 0;
+
+       /*
+        * Get the UDP ports. They are handled in network
+        * order everywhere in IPSEC_NAT_T code.
+        */
+       udphdr = (struct udphdr *)((char *)data - skip);
+       sport = udphdr->uh_sport;
+       dport = udphdr->uh_dport;
+
+       /*
+        * Remove the UDP header (and possibly the non ESP marker)
+        * IPv6 header length is ip6hdrlen
+        * Before:
+        *   <---- off --->
+        *   +-----+------+-----+
+        *   | IP6 |  UDP | ESP |
+        *   +-----+------+-----+
+        *         <-skip->
+        * After:
+        *          +-----+-----+
+        *          | IP6 | ESP |
+        *          +-----+-----+
+        *   <-skip->
+        */
+       ip6hdrlen = off - sizeof(struct udphdr);
+       memmove(mtod(m, char *) + skip, mtod(m, void *), ip6hdrlen);
+       m_adj(m, skip);
+
+       ip6 = mtod(m, struct ip6_hdr *);
+       ip6->ip6_plen = htons(ntohs(ip6->ip6_plen) - skip);
+       ip6->ip6_nxt = IPPROTO_ESP;
+
+       /*
+        * We have modified the packet - it is now ESP, so we should not
+        * return to UDP processing ...
+        *
+        * Add a PACKET_TAG_IPSEC_NAT_T_PORT tag to remember
+        * the source UDP port. This is required if we want
+        * to select the right SPD for multiple hosts behind
+        * same NAT
+        */
+       if ((tag = m_tag_get(PACKET_TAG_IPSEC_NAT_T_PORTS,
+           sizeof(sport) + sizeof(dport), M_DONTWAIT)) == NULL) {
+               m_freem(m);
+               return -1;
+       }
+       ((u_int16_t *)(tag + 1))[0] = sport;



Home | Main Index | Thread Index | Old Index