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

Add ATF later.
This commit is contained in:
knakahara 2018-11-22 04:48:34 +00:00
parent 415ab74c4d
commit 890dda538a
7 changed files with 270 additions and 22 deletions

View File

@ -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 proto)
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

View File

@ -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 */

View File

@ -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.c,v 1.143 2018/11/06 04:27:41 ozaki-r Ex
#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 + sizeof(struct sockaddr_in6));
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, struct sockopt *sopt)
{
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, struct sockopt *sopt)
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 /* offset of data portion */,
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_in6 *src, struct sockaddr_in6 *dst,
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_in6 *src, struct sockaddr_in6 *dst,
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, int proto)
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

View File

@ -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 *, size_t *, void *, size_t);
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);

View File

@ -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 */
/*

View File

@ -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 inpcb *, int, u_long *, bool *, bool *);
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 **);

View File

@ -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 struct ipsecrequest *isr,
#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 struct ipsecrequest *isr,
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 struct ipsecrequest *isr,
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 struct ipsecrequest *isr,
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 struct ipsecrequest *isr,
}
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: