Support IPv6 NAT-T. Implemented by hsuenaga@IIJ and ohishi@IIJ.
Add ATF later.
This commit is contained in:
parent
415ab74c4d
commit
890dda538a
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
||||
/*
|
||||
|
|
|
@ -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 **);
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue