Module Name:    src
Committed By:   knakahara
Date:           Thu Nov 22 04:48:34 UTC 2018

Modified Files:
        src/sys/netinet: udp_usrreq.c
        src/sys/netinet6: in6_pcb.h udp6_usrreq.c udp6_var.h
        src/sys/netipsec: ipsec.c ipsec.h ipsec_output.c

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

Add ATF later.


To generate a diff of this commit:
cvs rdiff -u -r1.256 -r1.257 src/sys/netinet/udp_usrreq.c
cvs rdiff -u -r1.49 -r1.50 src/sys/netinet6/in6_pcb.h
cvs rdiff -u -r1.143 -r1.144 src/sys/netinet6/udp6_usrreq.c
cvs rdiff -u -r1.29 -r1.30 src/sys/netinet6/udp6_var.h
cvs rdiff -u -r1.166 -r1.167 src/sys/netipsec/ipsec.c
cvs rdiff -u -r1.85 -r1.86 src/sys/netipsec/ipsec.h
cvs rdiff -u -r1.80 -r1.81 src/sys/netipsec/ipsec_output.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/netinet/udp_usrreq.c
diff -u src/sys/netinet/udp_usrreq.c:1.256 src/sys/netinet/udp_usrreq.c:1.257
--- src/sys/netinet/udp_usrreq.c:1.256	Fri Sep 14 05:09:51 2018
+++ src/sys/netinet/udp_usrreq.c	Thu Nov 22 04:48:34 2018
@@ -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 @@ udp_input(struct mbuf *m, int off, int p
 		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
 

Index: src/sys/netinet6/in6_pcb.h
diff -u src/sys/netinet6/in6_pcb.h:1.49 src/sys/netinet6/in6_pcb.h:1.50
--- src/sys/netinet6/in6_pcb.h:1.49	Thu Mar  2 05:26:24 2017
+++ src/sys/netinet6/in6_pcb.h	Thu Nov 22 04:48:34 2018
@@ -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 @@ struct	in6pcb {
 #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 */

Index: src/sys/netinet6/udp6_usrreq.c
diff -u src/sys/netinet6/udp6_usrreq.c:1.143 src/sys/netinet6/udp6_usrreq.c:1.144
--- src/sys/netinet6/udp6_usrreq.c:1.143	Tue Nov  6 04:27:41 2018
+++ src/sys/netinet6/udp6_usrreq.c	Thu Nov 22 04:48:34 2018
@@ -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 @@ __KERNEL_RCSID(0, "$NetBSD: udp6_usrreq.
 
 #ifdef IPSEC
 #include <netipsec/ipsec.h>
+#include <netipsec/esp.h>
 #ifdef INET6
 #include <netipsec/ipsec6.h>
 #endif
@@ -135,6 +136,10 @@ static int udp6_recvspace = 40 * (1024 +
 
 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 @@ udp6_ctloutput(int op, struct socket *so
 {
 	int s;
 	int error = 0;
+	struct in6pcb *in6p;
 	int family;
+	int optval;
 
 	family = so->so_proto->pr_domain->dom_family;
 
@@ -324,7 +331,42 @@ udp6_ctloutput(int op, struct socket *so
 		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 @@ udp6_sendup(struct mbuf *m, int off /* o
 
 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 @@ udp6_realinput(int af, struct sockaddr_i
 	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 @@ udp6_realinput(int af, struct sockaddr_i
 				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 @@ udp6_input(struct mbuf **mp, int *offp, 
 	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(u_int stat)
 	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;
+	((u_int16_t *)(tag + 1))[1] = dport;
+	m_tag_prepend(m, tag);
+
+	if (ipsec_used)
+		ipsec6_common_input(&m, &ip6hdrlen, IPPROTO_ESP);
+	else
+		m_freem(m);
+
+	/* We handled it, it shouldn't be handled by UDP */
+	*mp = NULL; /* avoid free by caller ... */
+	return 1;
+}
+#endif /* IPSEC */
+
 PR_WRAP_USRREQS(udp6)
 #define	udp6_attach	udp6_attach_wrapper
 #define	udp6_detach	udp6_detach_wrapper

Index: src/sys/netinet6/udp6_var.h
diff -u src/sys/netinet6/udp6_var.h:1.29 src/sys/netinet6/udp6_var.h:1.30
--- src/sys/netinet6/udp6_var.h:1.29	Wed Aug 22 01:05:24 2018
+++ src/sys/netinet6/udp6_var.h	Thu Nov 22 04:48:34 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: udp6_var.h,v 1.29 2018/08/22 01:05:24 msaitoh Exp $	*/
+/*	$NetBSD: udp6_var.h,v 1.30 2018/11/22 04:48:34 knakahara Exp $	*/
 /*	$KAME: udp6_var.h,v 1.11 2000/06/05 00:14:31 itojun Exp $	*/
 
 /*
@@ -103,7 +103,7 @@ int	udp6_sysctl(int *, u_int, void *, si
 int	udp6_usrreq(struct socket *, int, struct mbuf *, struct mbuf *,
     struct mbuf *, struct lwp *);
 int	udp6_realinput(int, struct sockaddr_in6 *, struct sockaddr_in6 *,
-    struct mbuf *, int);
+    struct mbuf **, int);
 int	udp6_input_checksum(struct mbuf *, const struct udphdr *, int, int);
 
 void	udp6_statinc(u_int);

Index: src/sys/netipsec/ipsec.c
diff -u src/sys/netipsec/ipsec.c:1.166 src/sys/netipsec/ipsec.c:1.167
--- src/sys/netipsec/ipsec.c:1.166	Sat Oct 27 05:42:23 2018
+++ src/sys/netipsec/ipsec.c	Thu Nov 22 04:48:34 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: ipsec.c,v 1.166 2018/10/27 05:42:23 maxv Exp $ */
+/* $NetBSD: ipsec.c,v 1.167 2018/11/22 04:48:34 knakahara Exp $ */
 /* $FreeBSD: ipsec.c,v 1.2.2.2 2003/07/01 01:38:13 sam Exp $ */
 /* $KAME: ipsec.c,v 1.103 2001/05/24 07:14:18 sakane Exp $ */
 
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ipsec.c,v 1.166 2018/10/27 05:42:23 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ipsec.c,v 1.167 2018/11/22 04:48:34 knakahara Exp $");
 
 /*
  * IPsec controller part.
@@ -1830,6 +1830,42 @@ skippolicycheck:
 	*needipsecp = needipsec;
 	return sp;
 }
+
+/*
+ * calculate UDP checksum for UDP encapsulated ESP for IPv6.
+ *
+ * RFC2460(Internet Protocol, Version 6 Specification) says:
+ *
+ *   IPv6 receivers MUST discard UDP packets with a zero checksum.
+ *
+ * There is more relaxed speficication RFC6935(IPv6 and UDP Checksums for
+ * Tunneled Packets). The document allows zero checksum. It's too
+ * late to publish, there are a lot of interoperability problems...
+ */
+void
+ipsec6_udp_cksum(struct mbuf *m)
+{
+	struct ip6_hdr *ip6;
+	uint16_t plen, uh_sum;
+	int off;
+
+	/* must called after m_pullup() */
+	KASSERT(m->m_len >= sizeof(struct ip6_hdr));
+
+	ip6 = mtod(m, struct ip6_hdr *);
+	KASSERT(ip6->ip6_nxt == IPPROTO_UDP);
+
+	/* ip6->ip6_plen can not be updated before ip6_output() */
+	plen = m->m_pkthdr.len - sizeof(*ip6);
+	KASSERT(plen >= sizeof(struct udphdr));
+
+	uh_sum = in6_cksum(m, IPPROTO_UDP, sizeof(*ip6), plen);
+	if (uh_sum == 0)
+		uh_sum = 0xffff;
+
+	off = sizeof(*ip6) + offsetof(struct udphdr, uh_sum);
+	m_copyback(m, off, sizeof(uh_sum), (void *)&uh_sum);
+}
 #endif /* INET6 */
 
 /*

Index: src/sys/netipsec/ipsec.h
diff -u src/sys/netipsec/ipsec.h:1.85 src/sys/netipsec/ipsec.h:1.86
--- src/sys/netipsec/ipsec.h:1.85	Thu Nov 15 10:23:56 2018
+++ src/sys/netipsec/ipsec.h	Thu Nov 22 04:48:34 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: ipsec.h,v 1.85 2018/11/15 10:23:56 maxv Exp $	*/
+/*	$NetBSD: ipsec.h,v 1.86 2018/11/22 04:48:34 knakahara Exp $	*/
 /*	$FreeBSD: ipsec.h,v 1.2.4.2 2004/02/14 22:23:23 bms Exp $	*/
 /*	$KAME: ipsec.h,v 1.53 2001/11/20 08:32:38 itojun Exp $	*/
 
@@ -259,6 +259,9 @@ int ipsec4_output(struct mbuf *, struct 
 
 int ipsec_ip_input(struct mbuf *, bool);
 void ipsec_mtu(struct mbuf *, int *);
+#ifdef INET6
+void ipsec6_udp_cksum(struct mbuf *);
+#endif
 
 struct inpcb;
 int ipsec_init_pcbpolicy(struct socket *so, struct inpcbpolicy **);

Index: src/sys/netipsec/ipsec_output.c
diff -u src/sys/netipsec/ipsec_output.c:1.80 src/sys/netipsec/ipsec_output.c:1.81
--- src/sys/netipsec/ipsec_output.c:1.80	Thu May 31 15:06:45 2018
+++ src/sys/netipsec/ipsec_output.c	Thu Nov 22 04:48:34 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: ipsec_output.c,v 1.80 2018/05/31 15:06:45 maxv Exp $	*/
+/*	$NetBSD: ipsec_output.c,v 1.81 2018/11/22 04:48:34 knakahara Exp $	*/
 
 /*
  * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
@@ -29,7 +29,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ipsec_output.c,v 1.80 2018/05/31 15:06:45 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ipsec_output.c,v 1.81 2018/11/22 04:48:34 knakahara Exp $");
 
 #if defined(_KERNEL_OPT)
 #include "opt_inet.h"
@@ -151,7 +151,7 @@ ipsec_process_done(struct mbuf *m, const
 #endif
 	struct mbuf *mo;
 	struct udphdr *udp = NULL;
-	int hlen, roff;
+	int hlen, roff, iphlen;
 
 	KASSERT(m != NULL);
 	KASSERT(isr != NULL);
@@ -160,11 +160,30 @@ ipsec_process_done(struct mbuf *m, const
 	saidx = &sav->sah->saidx;
 
 	if (sav->natt_type != 0) {
-		ip = mtod(m, struct ip *);
-
 		hlen = sizeof(struct udphdr);
 
-		mo = m_makespace(m, sizeof(struct ip), hlen, &roff);
+		switch (saidx->dst.sa.sa_family) {
+#ifdef INET
+		case AF_INET:
+			ip = mtod(m, struct ip *);
+			mo = m_makespace(m, sizeof(struct ip), hlen, &roff);
+			iphlen = ip->ip_hl << 2;
+			break;
+#endif
+#ifdef INET6
+		case AF_INET6:
+			ip6 = mtod(m, struct ip6_hdr *);
+			mo = m_makespace(m, sizeof(struct ip6_hdr), hlen, &roff);
+			iphlen = sizeof(*ip6);
+			break;
+#endif
+		default:
+			IPSECLOG(LOG_DEBUG, "unknown protocol family %u\n",
+			    saidx->dst.sa.sa_family);
+			error = ENXIO;
+			goto bad;
+		}
+
 		if (mo == NULL) {
 			char buf[IPSEC_ADDRSTRLEN];
 			IPSECLOG(LOG_DEBUG,
@@ -179,7 +198,7 @@ ipsec_process_done(struct mbuf *m, const
 		udp->uh_sport = key_portfromsaddr(&saidx->src);
 		udp->uh_dport = key_portfromsaddr(&saidx->dst);
 		udp->uh_sum = 0;
-		udp->uh_ulen = htons(m->m_pkthdr.len - (ip->ip_hl << 2));
+		udp->uh_ulen = htons(m->m_pkthdr.len - iphlen);
 	}
 
 	/*
@@ -190,6 +209,7 @@ ipsec_process_done(struct mbuf *m, const
 	case AF_INET:
 		ip = mtod(m, struct ip *);
 		ip->ip_len = htons(m->m_pkthdr.len);
+		/* IPv4 packet does not have to be set UDP checksum. */
 		if (sav->natt_type != 0)
 			ip->ip_p = IPPROTO_UDP;
 		break;
@@ -207,8 +227,11 @@ ipsec_process_done(struct mbuf *m, const
 		}
 		ip6 = mtod(m, struct ip6_hdr *);
 		ip6->ip6_plen = htons(m->m_pkthdr.len - sizeof(struct ip6_hdr));
-		if (sav->natt_type != 0)
+		/* IPv6 packet should be set UDP checksum. */
+		if (sav->natt_type != 0) {
 			ip6->ip6_nxt = IPPROTO_UDP;
+			ipsec6_udp_cksum(m);
+		}
 		break;
 #endif
 	default:

Reply via email to