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: