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: