Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 14 Sep 2006 09:30:34 -0400
From:      Larry Baird <lab@gta.com>
To:        freebsd-net@freebsd.org
Subject:   FAST_IPSEC NAT-T support
Message-ID:  <20060914093034.A83805@gta.com>

next in thread | raw e-mail | index | archive | help

--NzB8fVQJ5HfG6fxh
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Please find attached two patches for adding FAST_IPSEC NAT-T support to
FreeBSD 6.x.  The patch "freebsd6-fastipsec-natt.diff" is dependent
upon Yvan's IPSEC NAT-T patch "freebsd6-natt.diff" which can be found at
http://ipsec-tools.cvs.sourceforge.net/ipsec-tools/htdocs/.  The second
patch "freebsd6-ipsec-fastipsec-natt.diff" is a cumulative patch
combining both patches together.  

-- 
------------------------------------------------------------------------
Larry Baird                        | http://www.gta.com
Global Technology Associates, Inc. | Orlando, FL
Email: lab@gta.com                 | TEL 407-380-0220, FAX 407-380-6080

--NzB8fVQJ5HfG6fxh
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="freebsd6-fastipsec-natt.diff"

Index: netinet/udp_usrreq.c
===================================================================
--- netinet/udp_usrreq.c	(.../6.x-IPSEC-NATT)	(revision 8180)
+++ netinet/udp_usrreq.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -1170,7 +1170,7 @@
 	m_tag_prepend(n, tag);
 
 #ifdef FAST_IPSEC
-	ipsec4_common_input(n, iphdrlen);
+	ipsec4_common_input(n, iphdrlen, ip->ip_p);
 #else /* IPSEC */
 	esp4_input(n, iphdrlen);
 #endif
Index: netipsec/ipsec.c
===================================================================
--- netipsec/ipsec.c	(.../6.x-IPSEC-NATT)	(revision 8180)
+++ netipsec/ipsec.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -1808,15 +1808,15 @@
 
 /* Return a printable string for the IPv4 address. */
 static char *
-inet_ntoa4(struct in_addr ina)
+inet_ntoa4(const struct sockaddr_in *sin)
 {
-	static char buf[4][4 * sizeof "123" + 4];
-	unsigned char *ucp = (unsigned char *) &ina;
+	static char buf[4][4 * sizeof "123" + 4 + 10];
+	const unsigned char *ucp = (const unsigned char *)&sin->sin_addr;
 	static int i = 3;
 
 	i = (i + 1) % 4;
-	sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff,
-	    ucp[2] & 0xff, ucp[3] & 0xff);
+	sprintf(buf[i], "%d.%d.%d.%d[%u]", ucp[0] & 0xff, ucp[1] & 0xff,
+	    ucp[2] & 0xff, ucp[3] & 0xff, ntohs(sin->sin_port));
 	return (buf[i]);
 }
 
@@ -1827,7 +1827,7 @@
 	switch (sa->sa.sa_family) {
 #if INET
 	case AF_INET:
-		return inet_ntoa4(sa->sin.sin_addr);
+		return inet_ntoa4(&sa->sin);
 #endif /* INET */
 
 #if INET6
Index: netipsec/keydb.h
===================================================================
--- netipsec/keydb.h	(.../6.x-IPSEC-NATT)	(revision 8180)
+++ netipsec/keydb.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -117,6 +117,12 @@
 	struct secashead *sah;		/* back pointer to the secashead */
 
 	/*
+	 * NAT-Traversal 
+	 */
+	u_int16_t	natt_type;
+	u_int16_t	esp_frag;
+
+	/*
 	 * NB: Fields with a tdb_ prefix are part of the "glue" used
 	 *     to interface to the OpenBSD crypto support.  This was done
 	 *     to distinguish this code from the mainline KAME code.
Index: netipsec/ipsec_input.c
===================================================================
--- netipsec/ipsec_input.c	(.../6.x-IPSEC-NATT)	(revision 8180)
+++ netipsec/ipsec_input.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -110,6 +110,9 @@
 	struct secasvar *sav;
 	u_int32_t spi;
 	int error;
+#ifdef IPSEC_NAT_T
+	struct m_tag *tag;
+#endif
 
 	IPSEC_ISTAT(sproto, espstat.esps_input, ahstat.ahs_input,
 		ipcompstat.ipcomps_input);
@@ -160,6 +163,13 @@
 		m_copydata(m, offsetof(struct ip, ip_dst),
 		    sizeof(struct in_addr),
 		    (caddr_t) &dst_address.sin.sin_addr);
+#ifdef IPSEC_NAT_T
+		/* find the source port for NAT_T */
+		if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL))
+				    != NULL) {
+			dst_address.sin.sin_port = ((u_int16_t *)(tag + 1))[1];
+		}
+#endif /* IPSEC_NAT_T */
 		break;
 #endif /* INET */
 #ifdef INET6
@@ -179,7 +189,7 @@
 	}
 
 	/* NB: only pass dst since key_allocsa follows RFC2401 */
-	sav = KEY_ALLOCSA(&dst_address, sproto, spi);
+	sav = KEY_ALLOCSA( &dst_address, sproto, spi);
 	if (sav == NULL) {
 		DPRINTF(("%s: no key association found for SA %s/%08lx/%u\n",
 			  __func__, ipsec_address(&dst_address),
Index: netipsec/ipsec_output.c
===================================================================
--- netipsec/ipsec_output.c	(.../6.x-IPSEC-NATT)	(revision 8180)
+++ netipsec/ipsec_output.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -81,6 +81,10 @@
 
 #include <machine/in_cksum.h>
 
+#ifdef IPSEC_NAT_T
+#include <netinet/udp.h>
+#endif
+
 int
 ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
 {
@@ -173,6 +177,51 @@
 		ip->ip_len = ntohs(ip->ip_len);
 		ip->ip_off = ntohs(ip->ip_off);
 
+#ifdef IPSEC_NAT_T
+		/*
+		 * If NAT-T is enabled, now that all IPSEC processing is done
+		 * insert UDP encapsulation header after IP header.
+		 */
+		if (sav->natt_type != 0) {
+			int size = sizeof(struct udphdr);
+#ifdef _IP_VHL
+			int hlen = IP_VHL_HL(ip->ip_vhl);
+#else
+			int hlen = (ip->ip_hl << 2);
+#endif
+			int off;
+			struct mbuf *mi;
+			struct udphdr *udp;
+
+			if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
+				size += sizeof(u_int64_t);
+
+			if ( (mi = m_makespace(m, hlen, size, &off)) == NULL ) {
+				error = ENOBUFS;
+				goto bad;
+			}
+
+			udp = (struct udphdr *)(mtod(mi, caddr_t) + off);
+
+			if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
+				udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT);
+			else
+				udp->uh_sport =
+					KEY_PORTFROMSADDR(&sav->sah->saidx.src);
+
+			udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst);
+			udp->uh_sum = 0;
+			udp->uh_ulen = htons(m->m_pkthdr.len - hlen);
+			ip->ip_len = m->m_pkthdr.len;
+			ip->ip_p = IPPROTO_UDP;
+
+			if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) {
+				u_int64_t *marker = (u_int64_t *)(udp + 1);
+				*marker = 0;
+			}
+		}
+#endif /* IPSEC_NAT_T */
+
 		return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL);
 #endif /* INET */
 #ifdef INET6
Index: netipsec/key.c
===================================================================
--- netipsec/key.c	(.../6.x-IPSEC-NATT)	(revision 8180)
+++ netipsec/key.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -210,6 +210,11 @@
 	0,				/* SADB_X_EXT_KMPRIVATE */
 	sizeof(struct sadb_x_policy),	/* SADB_X_EXT_POLICY */
 	sizeof(struct sadb_x_sa2),	/* SADB_X_SA2 */
+	sizeof(struct sadb_x_nat_t_type), /* SADB_X_EXT_NAT_T_TYPE */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_SPORT */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_DPORT */
+	sizeof(struct sadb_address),	/* SADB_X_EXT_NAT_T_OA */
+	sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */
 };
 static const int maxsize[] = {
 	sizeof(struct sadb_msg),	/* SADB_EXT_RESERVED */
@@ -232,6 +237,11 @@
 	0,				/* SADB_X_EXT_KMPRIVATE */
 	0,				/* SADB_X_EXT_POLICY */
 	sizeof(struct sadb_x_sa2),	/* SADB_X_SA2 */
+	sizeof(struct sadb_x_nat_t_type), /* SADB_X_EXT_NAT_T_TYPE */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_SPORT */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_DPORT */
+	0,				/* SADB_X_EXT_NAT_T_OA */
+	sizeof(struct sadb_x_nat_t_frag), /* SADB_X_EXT_NAT_T_FRAG */
 };
 
 static int ipsec_esp_keymin = 256;
@@ -393,6 +403,10 @@
 	const struct sadb_msghdr *));
 static int key_spddump __P((struct socket *, struct mbuf *,
 	const struct sadb_msghdr *));
+#ifdef IPSEC_NAT_T
+static int key_nat_map(struct socket *, struct mbuf *,
+	const struct sadb_msghdr *);
+#endif
 static struct mbuf *key_setdumpsp __P((struct secpolicy *,
 	u_int8_t, u_int32_t, u_int32_t));
 static u_int key_getspreqmsglen __P((struct secpolicy *));
@@ -418,6 +432,13 @@
 static struct mbuf *key_setsadbsa __P((struct secasvar *));
 static struct mbuf *key_setsadbaddr __P((u_int16_t,
 	const struct sockaddr *, u_int8_t, u_int16_t));
+#ifdef IPSEC_NAT_T
+static struct mbuf *key_setsadbxport __P((u_int16_t, u_int16_t));
+static struct mbuf *key_setsadbxtype __P((u_int16_t));
+#endif
+static void key_porttosaddr __P((struct sockaddr *, u_int16_t));
+#define KEY_PORTTOSADDR(saddr, port) \
+	key_porttosaddr((struct sockaddr *)(saddr), (port))
 static struct mbuf *key_setsadbxsa2 __P((u_int8_t, u_int32_t, u_int32_t));
 static struct mbuf *key_setsadbxpolicy __P((u_int16_t, u_int8_t,
 	u_int32_t));
@@ -1042,12 +1063,20 @@
 	struct secasvar *sav;
 	u_int stateidx, arraysize, state;
 	const u_int *saorder_state_valid;
+	int chkport = 0;
 
 	IPSEC_ASSERT(dst != NULL, ("null dst address"));
 
 	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
 		printf("DP %s from %s:%u\n", __func__, where, tag));
 
+#ifdef IPSEC_NAT_T
+        if (dst->sa.sa_family == AF_INET &&
+	    dst->sa.sa_len == sizeof(struct sockaddr_in) &&
+	    dst->sin.sin_port != 0)
+		chkport = 1;
+#endif
+
 	/*
 	 * searching SAD.
 	 * XXX: to be checked internal IP header somewhere.  Also when
@@ -1079,11 +1108,11 @@
 					continue;
 #if 0	/* don't check src */
 				/* check src address */
-				if (key_sockaddrcmp(&src->sa, &sav->sah->saidx.src.sa, 0) != 0)
+				if (key_sockaddrcmp(&src->sa, &sav->sah->saidx.src.sa, chkport) != 0)
 					continue;
 #endif
 				/* check dst address */
-				if (key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, 0) != 0)
+				if (key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, chkport) != 0)
 					continue;
 				sa_addref(sav);
 				goto done;
@@ -1847,6 +1876,52 @@
 		return key_senderror(so, m, error);
 	}
 
+#ifndef IPSEC_NAT_T
+	for (isr = newsp->req; isr; isr = isr->next) {
+		struct sockaddr *sa;
+
+		/*
+		 * port spec is not permitted for tunnel mode
+		 */
+		if (isr->saidx.mode == IPSEC_MODE_TUNNEL && src0 && dst0) {
+			sa = (struct sockaddr *)(src0 + 1);
+			switch (sa->sa_family) {
+			case AF_INET:
+				if (((struct sockaddr_in *)sa)->sin_port) {
+					keydb_delsecpolicy(newsp);
+					return key_senderror(so, m, EINVAL);
+				}
+				break;
+			case AF_INET6:
+				if (((struct sockaddr_in6 *)sa)->sin6_port) {
+					keydb_delsecpolicy(newsp);
+					return key_senderror(so, m, EINVAL);
+				}
+				break;
+			default:
+				break;
+			}
+			sa = (struct sockaddr *)(dst0 + 1);
+			switch (sa->sa_family) {
+			case AF_INET:
+				if (((struct sockaddr_in *)sa)->sin_port) {
+					keydb_delsecpolicy(newsp);
+					return key_senderror(so, m, EINVAL);
+				}
+				break;
+			case AF_INET6:
+				if (((struct sockaddr_in6 *)sa)->sin6_port) {
+					keydb_delsecpolicy(newsp);
+					return key_senderror(so, m, EINVAL);
+				}
+				break;
+			default:
+				break;
+			}
+		}
+	}
+#endif /* !IPSEC_NAT_T */
+
 	if ((newsp->id = key_getnewspid()) == 0) {
 		_key_delsp(newsp);
 		return key_senderror(so, m, ENOBUFS);
@@ -2355,6 +2430,71 @@
 	return key_sendup_mbuf(so, m, KEY_SENDUP_ALL);
 }
 
+#ifdef IPSEC_NAT_T
+/* 
+ * SADB_X_NAT_T_NEW_MAPPING 
+ */
+static int
+key_nat_map(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_x_nat_t_type *type;
+	struct sadb_x_nat_t_port *sport;
+	struct sadb_x_nat_t_port *dport;
+	struct sadb_address *addr;
+	struct sadb_x_nat_t_frag *frag;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_nat_map: NULL pointer is passed.");
+
+	if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] == NULL ||
+	    mhp->ext[SADB_X_EXT_NAT_T_SPORT] == NULL ||
+	    mhp->ext[SADB_X_EXT_NAT_T_DPORT] == NULL) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if ((mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type)) ||
+	    (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport)) ||
+	    (mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport))) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && 
+	    (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) &&
+	    (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	type = (struct sadb_x_nat_t_type *)mhp->ext[SADB_X_EXT_NAT_T_TYPE];
+	sport = (struct sadb_x_nat_t_port *)mhp->ext[SADB_X_EXT_NAT_T_SPORT];
+	dport = (struct sadb_x_nat_t_port *)mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+	addr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OA];
+	frag = (struct sadb_x_nat_t_frag *) mhp->ext[SADB_X_EXT_NAT_T_FRAG];
+
+	printf("sadb_nat_map: type %d, sport = %d, dport = %d\n", 
+		type->sadb_x_nat_t_type_type,
+		sport->sadb_x_nat_t_port_port,
+		dport->sadb_x_nat_t_port_port);
+
+	/* 
+	 * XXX handle that, it should also contain a SA, or anything 
+	 * that enable to update the SA information.
+	 */
+
+	return 0;
+}
+#endif /* IPSEC_NAT_T */
+
 /*
  * SADB_SPDDUMP processing
  * receive
@@ -2984,6 +3124,10 @@
 	sav->lft_c = NULL;
 	sav->lft_h = NULL;
 	sav->lft_s = NULL;
+#ifdef IPSEC_NAT_T
+	sav->natt_type = 0;
+	sav->esp_frag = 0;
+#endif
 	sav->tdb_xform = NULL;		/* transform */
 	sav->tdb_encalgxform = NULL;	/* encoding algorithm */
 	sav->tdb_authalgxform = NULL;	/* authentication algorithm */
@@ -3294,6 +3438,11 @@
 		SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY, SADB_EXT_KEY_AUTH,
 		SADB_EXT_KEY_ENCRYPT, SADB_EXT_IDENTITY_SRC,
 		SADB_EXT_IDENTITY_DST, SADB_EXT_SENSITIVITY,
+#ifdef IPSEC_NAT_T
+		SADB_X_EXT_NAT_T_TYPE, SADB_X_EXT_NAT_T_SPORT, 
+		SADB_X_EXT_NAT_T_DPORT, SADB_X_EXT_NAT_T_OA,
+		SADB_X_EXT_NAT_T_FRAG,
+#endif
 	};
 
 	m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt);
@@ -3370,6 +3519,31 @@
 			p = sav->lft_s;
 			break;
 
+#ifdef IPSEC_NAT_T
+		case SADB_X_EXT_NAT_T_TYPE:
+			if ((m = key_setsadbxtype(sav->natt_type)) == NULL)
+				goto fail;
+			break;
+		
+		case SADB_X_EXT_NAT_T_DPORT:
+			if ((m = key_setsadbxport(KEY_PORTFROMSADDR
+			    (&sav->sah->saidx.dst),
+			    SADB_X_EXT_NAT_T_DPORT)) == NULL)
+				goto fail;
+			break;
+
+		case SADB_X_EXT_NAT_T_SPORT:
+			if ((m = key_setsadbxport(KEY_PORTFROMSADDR
+			    (&sav->sah->saidx.src),
+			    SADB_X_EXT_NAT_T_SPORT)) == NULL)
+				goto fail;
+			break;
+
+		case SADB_X_EXT_NAT_T_OA:
+		case SADB_X_EXT_NAT_T_FRAG:
+			continue;
+#endif
+
 		case SADB_EXT_ADDRESS_PROXY:
 		case SADB_EXT_IDENTITY_SRC:
 		case SADB_EXT_IDENTITY_DST:
@@ -3588,7 +3762,134 @@
 	return m;
 }
 
+#ifdef IPSEC_NAT_T
 /*
+ * set a type in sadb_x_nat_t_type
+ */
+static struct mbuf *
+key_setsadbxtype(type)
+	u_int16_t type;
+{
+	struct mbuf *m;
+	size_t len;
+	struct sadb_x_nat_t_type *p;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_x_nat_t_type));
+
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_x_nat_t_type *);
+
+	bzero(p, len);
+	p->sadb_x_nat_t_type_len = PFKEY_UNIT64(len);
+	p->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
+	p->sadb_x_nat_t_type_type = type;
+
+	return m;
+}
+/*
+ * set a port in sadb_x_nat_t_port. port is in network order
+ */
+static struct mbuf *
+key_setsadbxport(port, type)
+	u_int16_t port;
+	u_int16_t type;
+{
+	struct mbuf *m;
+	size_t len;
+	struct sadb_x_nat_t_port *p;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_x_nat_t_port));
+
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_x_nat_t_port *);
+
+	bzero(p, len);
+	p->sadb_x_nat_t_port_len = PFKEY_UNIT64(len);
+	p->sadb_x_nat_t_port_exttype = type;
+	p->sadb_x_nat_t_port_port = port;
+
+	return m;
+}
+
+/* 
+ * Get port from sockaddr, port is in network order
+ */
+u_int16_t 
+key_portfromsaddr(saddr)
+	struct sockaddr *saddr;
+{
+	u_int16_t port;
+
+	switch (saddr->sa_family) {
+	case AF_INET: {
+		struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
+
+		port = sin->sin_port;
+		break;
+	}
+#ifdef INET6
+	case AF_INET6: {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr;
+
+		port = sin6->sin6_port;
+		break;
+	}
+#endif
+	default:
+		printf("key_portfromsaddr: unexpected address family\n");
+		port = 0;
+		break;
+	}
+
+	return port;
+}
+#endif /* IPSEC_NAT_T */
+
+/*
+ * Set port is struct sockaddr. port is in network order
+ */
+static void
+key_porttosaddr(saddr, port)
+	struct sockaddr *saddr;
+	u_int16_t port;
+{
+	switch (saddr->sa_family) {
+	case AF_INET: {
+		struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
+
+		sin->sin_port = port;
+		break;
+	}
+#ifdef INET6
+	case AF_INET6: {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr;
+
+		sin6->sin6_port = port;
+		break;
+	}
+#endif
+	default:
+		printf("key_porttosaddr: unexpected address family %d\n", 
+		    saddr->sa_family);
+		break;
+	}
+
+	return;
+}
+
+/*
  * set data into sadb_x_policy
  */
 static struct mbuf *
@@ -3738,6 +4039,8 @@
 	const struct secasindex *saidx1,
 	int flag)
 {
+	int chkport = 0;
+
 	/* sanity */
 	if (saidx0 == NULL && saidx1 == NULL)
 		return 1;
@@ -3761,13 +4064,30 @@
 		/* CMP_MODE_REQID, CMP_REQID, CMP_HEAD */
 		if (flag == CMP_MODE_REQID
 		  ||flag == CMP_REQID) {
+#ifdef IPSEC_NAT_T
 			/*
+			 * If NAT-T is enabled, check ports for tunnel mode.
+			 * Don't do it for transport mode, as there is no
+			 * port information available in the SP.
+			 * XXX also don't check ports if they are set to zero in the SPD:
+			 * This means we bave a non-generated SPD, which can't know UDP ports.
+			 */
+			if (saidx1->mode == IPSEC_MODE_TUNNEL &&
+			    ((const struct sockaddr_in *)(&saidx1->src))->sin_port &&
+			    ((const struct sockaddr_in *)(&saidx1->dst))->sin_port )
+				chkport = 1;
+#endif /* IPSEC_NAT_T */
+			/*
 			 * If reqid of SPD is non-zero, unique SA is required.
 			 * The result must be of same reqid in this case.
 			 */
 			if (saidx1->reqid != 0 && saidx0->reqid != saidx1->reqid)
 				return 0;
 		}
+#ifdef IPSEC_NAT_T
+		else
+			chkport = 1;
+#endif
 
 		if (flag == CMP_MODE_REQID) {
 			if (saidx0->mode != IPSEC_MODE_ANY
@@ -3775,10 +4095,10 @@
 				return 0;
 		}
 
-		if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, 0) != 0) {
+		if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, chkport) != 0) {
 			return 0;
 		}
-		if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, 0) != 0) {
+		if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, chkport) != 0) {
 			return 0;
 		}
 	}
@@ -4398,13 +4718,17 @@
 		if (((struct sockaddr *)(src0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in *)(src0 + 1))->sin_port = 0;
+#endif
 		break;
 	case AF_INET6:
 		if (((struct sockaddr *)(src0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in6))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0;
+#endif
 		break;
 	default:
 		; /*???*/
@@ -4414,13 +4738,17 @@
 		if (((struct sockaddr *)(dst0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in *)(dst0 + 1))->sin_port = 0;
+#endif
 		break;
 	case AF_INET6:
 		if (((struct sockaddr *)(dst0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in6))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0;
+#endif
 		break;
 	default:
 		; /*???*/
@@ -4429,6 +4757,12 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
 
+	/* If not using NAT-T, make sure port numbers are set to zero. */
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* SPI allocation */
 	spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE],
 	                       &saidx);
@@ -4684,6 +5018,12 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
 
+	/* If not using NAT-T, make sure if port number is zero. */
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	if ((sah = key_getsah(&saidx)) == NULL) {
 		ipseclog((LOG_DEBUG, "%s: no SA index found.\n", __func__));
@@ -4750,6 +5090,68 @@
 		return key_senderror(so, m, 0);
 	}
 
+#ifdef IPSEC_NAT_T
+	/*
+	 * Handle NAT-T info if present
+	 */
+	if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL)
+		printf("update: NAT-T OA present\n");
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL)) {
+		struct sadb_x_nat_t_type *type;
+		struct sadb_x_nat_t_port *sport;
+		struct sadb_x_nat_t_port *dport;
+		struct sadb_address *addr;
+		struct sadb_x_nat_t_frag *frag;
+
+		if ((mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport))) {
+			ipseclog((LOG_DEBUG, "key_update: "
+			    "invalid message.\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) {
+			ipseclog((LOG_DEBUG, "key_update: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) {
+			ipseclog((LOG_DEBUG, "key_update: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		type = (struct sadb_x_nat_t_type *)
+		    mhp->ext[SADB_X_EXT_NAT_T_TYPE];
+		sport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_SPORT];
+		dport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+		addr = (struct sadb_address *)
+		    mhp->ext[SADB_X_EXT_NAT_T_OA];
+		frag = (struct sadb_x_nat_t_frag *)
+		    mhp->ext[SADB_X_EXT_NAT_T_FRAG];
+
+		if (type)
+			sav->natt_type = type->sadb_x_nat_t_type_type;
+		if (sport)
+			KEY_PORTTOSADDR(&sav->sah->saidx.src, 
+			    sport->sadb_x_nat_t_port_port);
+		if (dport)
+			KEY_PORTTOSADDR(&sav->sah->saidx.dst,
+			    dport->sadb_x_nat_t_port_port);
+		if (frag)
+			sav->esp_frag = frag->sadb_x_nat_t_frag_fraglen;
+		else
+			sav->esp_frag = IP_MAXPACKET;
+	}
+#endif /* IPSEC_NAT_T */
+
     {
 	struct mbuf *n;
 
@@ -4882,6 +5284,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	if ((newsah = key_getsah(&saidx)) == NULL) {
 		/* create a new SA header */
@@ -4918,7 +5325,69 @@
 		return key_senderror(so, m, error);
 	}
 
+#ifdef IPSEC_NAT_T
 	/*
+	 * Handle NAT-T info if present
+	 */
+	if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL)
+		printf("add: NAT-T OA present\n");
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL)) {
+		struct sadb_x_nat_t_type *type;
+		struct sadb_x_nat_t_port *sport;
+		struct sadb_x_nat_t_port *dport;
+		struct sadb_address *addr;
+		struct sadb_x_nat_t_frag *frag;
+
+		if ((mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport))) {
+			ipseclog((LOG_DEBUG, "key_add: "
+			    "invalid message.\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) {
+			ipseclog((LOG_DEBUG, "key_add: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) {
+			ipseclog((LOG_DEBUG, "key_update: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		type = (struct sadb_x_nat_t_type *)
+		    mhp->ext[SADB_X_EXT_NAT_T_TYPE];
+		sport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_SPORT];
+		dport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+		addr = (struct sadb_address *)
+		    mhp->ext[SADB_X_EXT_NAT_T_OA];
+		frag = (struct sadb_x_nat_t_frag *)
+		    mhp->ext[SADB_X_EXT_NAT_T_FRAG];
+
+		if (type)
+			newsav->natt_type = type->sadb_x_nat_t_type_type;
+		if (sport)
+			KEY_PORTTOSADDR(&newsav->sah->saidx.src, 
+			    sport->sadb_x_nat_t_port_port);
+		if (dport)
+			KEY_PORTTOSADDR(&newsav->sah->saidx.dst,
+			    dport->sadb_x_nat_t_port_port);
+		if (frag)
+			newsav->esp_frag = frag->sadb_x_nat_t_frag_fraglen;
+		else
+			newsav->esp_frag = IP_MAXPACKET;
+	}
+#endif
+
+	/*
 	 * don't call key_freesav() here, as we would like to keep the SA
 	 * in the database on success.
 	 */
@@ -5118,6 +5587,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	SAHTREE_LOCK();
 	LIST_FOREACH(sah, &sahtree, chain) {
@@ -5187,6 +5661,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	SAHTREE_LOCK();
 	LIST_FOREACH(sah, &sahtree, chain) {
 		if (sah->state == SADB_SASTATE_DEAD)
@@ -5301,6 +5780,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	SAHTREE_LOCK();
 	LIST_FOREACH(sah, &sahtree, chain) {
@@ -5988,6 +6472,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA index */
 	SAHTREE_LOCK();
 	LIST_FOREACH(sah, &sahtree, chain) {
@@ -6596,6 +7085,11 @@
 	key_spdadd,	/* SADB_X_SPDSETIDX */
 	NULL,		/* SADB_X_SPDEXPIRE */
 	key_spddelete2,	/* SADB_X_SPDDELETE2 */
+#ifdef IPSEC_NAT_T
+	key_nat_map, /* SADB_X_NAT_T_NEW_MAPPING */
+#else
+	NULL,
+#endif
 };
 
 /*
@@ -6932,6 +7426,13 @@
 		case SADB_EXT_SPIRANGE:
 		case SADB_X_EXT_POLICY:
 		case SADB_X_EXT_SA2:
+#ifdef IPSEC_NAT_T
+		case SADB_X_EXT_NAT_T_TYPE:
+		case SADB_X_EXT_NAT_T_SPORT:
+		case SADB_X_EXT_NAT_T_DPORT:
+		case SADB_X_EXT_NAT_T_OA:
+		case SADB_X_EXT_NAT_T_FRAG:
+#endif
 			/* duplicate check */
 			/*
 			 * XXX Are there duplication payloads of either
Index: netipsec/key.h
===================================================================
--- netipsec/key.h	(.../6.x-IPSEC-NATT)	(revision 8180)
+++ netipsec/key.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -99,6 +99,10 @@
 extern void key_sa_recordxfer __P((struct secasvar *, struct mbuf *));
 extern void key_sa_routechange __P((struct sockaddr *));
 extern void key_sa_stir_iv __P((struct secasvar *));
+#ifdef IPSEC_NAT_T
+u_int16_t key_portfromsaddr __P((struct sockaddr *));
+#define KEY_PORTFROMSADDR(saddr) key_portfromsaddr((struct sockaddr *)(saddr))
+#endif
 
 #ifdef MALLOC_DECLARE
 MALLOC_DECLARE(M_IPSEC_SA);

--NzB8fVQJ5HfG6fxh
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="freebsd6-ipsec-fastipsec-natt.diff"

Index: conf/options
===================================================================
--- conf/options	(.../6.x)	(revision 8180)
+++ conf/options	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -352,6 +352,7 @@
 INET6			opt_inet6.h
 IPSEC			opt_ipsec.h
 IPSEC_ESP		opt_ipsec.h
+IPSEC_NAT_T		opt_ipsec.h
 IPSEC_DEBUG		opt_ipsec.h
 IPSEC_FILTERGIF		opt_ipsec.h
 FAST_IPSEC		opt_ipsec.h
Index: netinet/udp_var.h
===================================================================
--- netinet/udp_var.h	(.../6.x)	(revision 8180)
+++ netinet/udp_var.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -100,6 +100,7 @@
 extern int	log_in_vain;
 
 void	udp_ctlinput(int, struct sockaddr *, void *);
+int		udp_ctloutput(struct socket *, struct sockopt *sopt);
 void	udp_init(void);
 void	udp_input(struct mbuf *, int);
 
Index: netinet/udp.h
===================================================================
--- netinet/udp.h	(.../6.x)	(revision 8180)
+++ netinet/udp.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -44,4 +44,17 @@
 	u_short	uh_sum;			/* udp checksum */
 };
 
+/* socket options for UDP */
+#define UDP_ENCAP	100
+
+/* Encapsulation types */
+#define UDP_ENCAP_ESPINUDP_NON_IKE 	1 /* draft-ietf-ipsec-nat-t-ike-00/01 */
+#define UDP_ENCAP_ESPINUDP		2 /* draft-ietf-ipsec-udp-encaps-02+ */
+
+/* Default encapsulation port */
+#define UDP_ENCAP_ESPINUDP_PORT		500
+
+/* Maximum UDP fragment size for ESP over UDP */
+#define UDP_ENCAP_ESPINUDP_MAXFRAGLEN	552
+
 #endif
Index: netinet/in_pcb.h
===================================================================
--- netinet/in_pcb.h	(.../6.x)	(revision 8180)
+++ netinet/in_pcb.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -298,6 +298,11 @@
 #define	IN6P_RFC2292		0x40000000 /* used RFC2292 API on the socket */
 #define	IN6P_MTU		0x80000000 /* receive path MTU */
 
+/* XXX should move to an UDP control block */
+#define INP_ESPINUDP		0x1000	/* ESP over UDP for NAT-T */
+#define INP_ESPINUDP_NON_IKE	0x2000	/* ESP over UDP for NAT-T */
+#define INP_ESPINUDP_ALL	(INP_ESPINUDP|INP_ESPINUDP_NON_IKE)
+
 #define	INP_CONTROLOPTS		(INP_RECVOPTS|INP_RECVRETOPTS|INP_RECVDSTADDR|\
 				 INP_RECVIF|INP_RECVTTL|\
 				 IN6P_PKTINFO|IN6P_HOPLIMIT|IN6P_HOPOPTS|\
Index: netinet/ip_output.c
===================================================================
--- netinet/ip_output.c	(.../6.x)	(revision 8180)
+++ netinet/ip_output.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -58,6 +58,10 @@
 #include <netinet/in_var.h>
 #include <netinet/ip_var.h>
 
+#ifdef IPSEC_NAT_T
+#include <netinet/udp.h>
+#endif
+
 #include <machine/in_cksum.h>
 
 static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options");
Index: netinet/in_proto.c
===================================================================
--- netinet/in_proto.c	(.../6.x)	(revision 8180)
+++ netinet/in_proto.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -122,7 +122,7 @@
 	.pr_flags =		PR_ATOMIC|PR_ADDR,
 	.pr_input =		udp_input,
 	.pr_ctlinput =		udp_ctlinput,
-	.pr_ctloutput =		ip_ctloutput,
+	.pr_ctloutput =		udp_ctloutput,
 	.pr_init =		udp_init,
 	.pr_usrreqs =		&udp_usrreqs
 },
Index: netinet/udp_usrreq.c
===================================================================
--- netinet/udp_usrreq.c	(.../6.x)	(revision 8180)
+++ netinet/udp_usrreq.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -78,10 +78,12 @@
 
 #ifdef FAST_IPSEC
 #include <netipsec/ipsec.h>
+#include <netipsec/esp.h>
 #endif /*FAST_IPSEC*/
 
 #ifdef IPSEC
 #include <netinet6/ipsec.h>
+#include <netinet6/esp.h>
 #endif /*IPSEC*/
 
 #include <machine/in_cksum.h>
@@ -128,6 +130,11 @@
 static int udp_detach(struct socket *so);
 static	int udp_output(struct inpcb *, struct mbuf *, struct sockaddr *,
 		struct mbuf *, struct thread *);
+#ifdef INET
+#ifdef IPSEC_NAT_T
+static int udp4_espinudp (struct mbuf *, int, struct sockaddr *, struct socket *);
+#endif
+#endif
 
 static void
 udp_zone_change(void *tag)
@@ -464,6 +471,41 @@
 		return;
 	}
 #endif /*IPSEC || FAST_IPSEC*/
+#ifdef IPSEC_NAT_T
+	/* Handle ESP over UDP */
+	if (last->inp_flags & INP_ESPINUDP_ALL) {
+		struct sockaddr_in src;
+		struct sockaddr *sa = (struct sockaddr *)(&src);
+		size_t minlen;
+
+		bzero(&src, sizeof(src));
+		src.sin_family = AF_INET;
+		src.sin_len = sizeof(struct sockaddr_in);
+		bcopy(&ip->ip_src, &src.sin_addr, sizeof(src.sin_addr));
+		src.sin_port = udp_in->sin_port;
+
+		/* 
+		 * Collapse the mbuf chain if the first mbuf is too short
+		 * The longest case is: UDP + non ESP marker + ESP
+		 */
+		minlen = off + sizeof(struct udphdr) + sizeof(u_int64_t) + sizeof(struct esp);
+		if (minlen > n->m_pkthdr.len)
+			minlen = n->m_pkthdr.len;
+
+		if ((n = m_pullup(n, minlen)) == NULL) {
+			printf("udp_append: m_pullup failed\n");
+			m_freem(n);
+			return;
+		}
+
+		if (udp4_espinudp(n, off, sa, last->inp_socket) != 0) {
+			m_freem(n);
+			return;
+		}
+		
+		/* Normal UDP processing will take place */
+	}
+#endif
 #ifdef MAC
 	if (mac_check_inpcb_deliver(last, n) != 0) {
 		m_freem(n);
@@ -702,6 +744,82 @@
     CTLTYPE_OPAQUE|CTLFLAG_RW|CTLFLAG_PRISON, 0, 0,
     udp_getcred, "S,xucred", "Get the xucred of a UDP connection");
 
+
+int
+udp_ctloutput(so, sopt)
+	struct socket *so;
+	struct sockopt *sopt;
+{
+	int error, optval, s;
+	struct inpcb *inp;
+	int family;
+
+	error = 0;
+	family = so->so_proto->pr_domain->dom_family;
+
+	s = splnet();
+	if (sopt->sopt_level != IPPROTO_UDP) {
+#ifdef INET6
+		if (INP_CHECK_SOCKAF(so, AF_INET6))
+			error = ip6_ctloutput(so, sopt);
+		else
+#endif /* INET6 */
+		error = ip_ctloutput(so, sopt);
+		splx(s);
+		return (error);
+	}
+	inp = sotoinpcb(so);
+
+	switch (sopt->sopt_dir) {
+	case SOPT_SET:
+		switch (sopt->sopt_name) {
+			case UDP_ENCAP:
+			error = sooptcopyin(sopt, &optval, sizeof optval,
+					    sizeof optval);
+			if (error)
+				break;
+
+			switch(optval){
+#ifdef IPSEC_NAT_T
+			case 0:
+				inp->inp_flags &= ~INP_ESPINUDP_ALL;
+				break;
+
+			case UDP_ENCAP_ESPINUDP:
+				inp->inp_flags |= INP_ESPINUDP;
+				break;
+				
+			case UDP_ENCAP_ESPINUDP_NON_IKE:
+				inp->inp_flags |= INP_ESPINUDP_NON_IKE;
+				break;
+#endif
+
+			default:
+				error = EINVAL;
+				goto end;
+				break;
+			}
+			break;
+
+			default:
+			error = ENOPROTOOPT;
+			goto end;
+			break;
+		}
+		break;
+
+	default:
+		error = EINVAL;
+		goto end;
+		break;
+	}	
+	
+end:
+	splx(s);
+	return error;
+}
+
+
 static int
 udp_output(inp, m, addr, control, td)
 	register struct inpcb *inp;
@@ -923,6 +1041,146 @@
 	return (error);
 }
 
+#ifdef INET
+#ifdef IPSEC_NAT_T
+/*
+ * Returns:
+ * 1 if the packet was processed
+ * 0 if normal UDP processing should take place
+ */
+static int
+udp4_espinudp(m, off, src, so)
+	struct mbuf *m;
+	int off;
+	struct sockaddr *src;
+	struct socket *so;
+{
+	size_t len;
+	caddr_t data;
+	struct inpcb *inp;
+	size_t skip = 0;
+	size_t minlen;
+	size_t iphdrlen;
+	struct m_tag *tag;
+	struct ip *ip;
+	struct udphdr *udphdr;
+	u_int16_t sport, dport;
+	struct mbuf *n;
+
+	/* 
+	 * Cannot collapse the mbuf chain here, must have been done in
+	 * calling function
+	 * 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)
+		return 0;
+
+	len = m->m_len - off;	
+	data = mtod(m, caddr_t) + off;
+	inp = sotoinpcb(so);
+
+	/* Ignore keepalive packets */
+	if ((len == 1) && (data[0] == '\xff')) {
+		return 1;
+	}
+
+	/* 
+	 * Check that the payload is long enough to hold 
+	 * an ESP header and compute the length of encapsulation
+	 * header to remove 
+	 */
+	if (inp->inp_flags & INP_ESPINUDP) {
+		u_int32_t *st = (u_int32_t *)data;
+
+		if ((len <= sizeof(struct esp)) || (*st == 0))
+			return 0; /* Normal UDP processing */
+
+		skip = sizeof(struct udphdr);
+	}
+
+	if (inp->inp_flags & INP_ESPINUDP_NON_IKE) {
+		u_int64_t *st = (u_int64_t *)data;
+
+		if ((len <= sizeof(u_int64_t) + sizeof(struct esp))
+		    || (*st != 0))
+			return 0; /* Normal UDP processing */
+		
+		skip = sizeof(struct udphdr) + sizeof(u_int64_t);
+	}
+
+	/*
+	 * Get the UDP ports. They are handled in network 
+	 * order everywhere in IPSEC_NAT_T code.
+	 */
+	udphdr = (struct udphdr *)(data - skip);
+	sport = udphdr->uh_sport;
+	dport = udphdr->uh_dport;
+
+	/*
+	 * Remove the UDP header (and possibly the non ESP marker)
+	 * IP header lendth is iphdrlen
+	 * Before:   
+	 *   <--- off --->
+	 *   +----+------+-----+
+	 *   | IP |  UDP | ESP |
+	 *   +----+------+-----+
+	 *        <-skip->
+	 * After:
+	 *          +----+-----+
+	 *          | IP | ESP |
+	 *          +----+-----+
+	 *   <-skip->
+	 */
+	iphdrlen = off - sizeof(struct udphdr);
+	ovbcopy(mtod(m, caddr_t), mtod(m, caddr_t) + skip, iphdrlen);
+	m_adj(m, skip);
+
+	ip = mtod(m, struct ip *);
+	ip->ip_len = htons(ntohs(ip->ip_len) - skip);
+	ip->ip_p = IPPROTO_ESP;
+
+	/*
+	 * Copy the mbuf to avoid multiple free, as both 
+	 * esp4_input (which we call) and udp_input (which 
+	 * called us) free the mbuf.
+	 */
+	if ((n = m_dup(m, M_DONTWAIT)) == NULL) {
+		printf("udp4_espinudp: m_dup failed\n");
+		return 0;
+	}
+
+	/*
+	 * 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) {
+		printf("udp4_espinudp: m_tag_get failed\n");
+		m_freem(n);
+		return 0;
+	}
+	((u_int16_t *)(tag + 1))[0] = sport;
+	((u_int16_t *)(tag + 1))[1] = dport;
+	m_tag_prepend(n, tag);
+
+#ifdef FAST_IPSEC
+	ipsec4_common_input(n, iphdrlen, ip->ip_p);
+#else /* IPSEC */
+	esp4_input(n, iphdrlen);
+#endif
+
+	/* We handled it, it shoudln't be handled by UDP */
+	return 1;
+}
+#endif
+#endif
+
 u_long	udp_sendspace = 9216;		/* really max datagram size */
 					/* 40 1K datagrams */
 SYSCTL_INT(_net_inet_udp, UDPCTL_MAXDGRAM, maxdgram, CTLFLAG_RW,
Index: net/pfkeyv2.h
===================================================================
--- net/pfkeyv2.h	(.../6.x)	(revision 8180)
+++ net/pfkeyv2.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -75,7 +75,8 @@
 #define SADB_X_SPDSETIDX  20
 #define SADB_X_SPDEXPIRE  21
 #define SADB_X_SPDDELETE2 22	/* by policy id */
-#define SADB_MAX          22
+#define SADB_X_NAT_T_NEW_MAPPING 23
+#define SADB_MAX          23
 
 struct sadb_msg {
   u_int8_t sadb_msg_version;
@@ -255,6 +256,34 @@
    */
 };
 
+/* NAT traversal type, see RFC 3948 */
+/* sizeof(struct sadb_x_nat_t_type) == 8 */
+struct sadb_x_nat_t_type {
+  u_int16_t sadb_x_nat_t_type_len;
+  u_int16_t sadb_x_nat_t_type_exttype;
+  u_int8_t sadb_x_nat_t_type_type;
+  u_int8_t sadb_x_nat_t_type_reserved[3];
+};
+
+/* NAT traversal source or destination port */
+/* sizeof(struct sadb_x_nat_t_port) == 8 */
+struct sadb_x_nat_t_port { 
+  u_int16_t sadb_x_nat_t_port_len;
+  u_int16_t sadb_x_nat_t_port_exttype;
+  u_int16_t sadb_x_nat_t_port_port;
+  u_int16_t sadb_x_nat_t_port_reserved;
+};
+
+/* ESP fragmentation size */
+/* sizeof(struct sadb_x_nat_t_frag) == 8 */
+struct sadb_x_nat_t_frag {
+  u_int16_t sadb_x_nat_t_frag_len;
+  u_int16_t sadb_x_nat_t_frag_exttype;
+  u_int16_t sadb_x_nat_t_frag_fraglen;
+  u_int16_t sadb_x_nat_t_frag_reserved;
+};
+
+
 #define SADB_EXT_RESERVED             0
 #define SADB_EXT_SA                   1
 #define SADB_EXT_LIFETIME_CURRENT     2
@@ -275,7 +304,12 @@
 #define SADB_X_EXT_KMPRIVATE          17
 #define SADB_X_EXT_POLICY             18
 #define SADB_X_EXT_SA2                19
-#define SADB_EXT_MAX                  19
+#define SADB_X_EXT_NAT_T_TYPE         20
+#define SADB_X_EXT_NAT_T_SPORT        21
+#define SADB_X_EXT_NAT_T_DPORT        22
+#define SADB_X_EXT_NAT_T_OA           23
+#define SADB_X_EXT_NAT_T_FRAG	      24
+#define SADB_EXT_MAX                  24
 
 #define SADB_SATYPE_UNSPEC	0
 #define SADB_SATYPE_AH		2
Index: netinet6/ipcomp_input.c
===================================================================
--- netinet6/ipcomp_input.c	(.../6.x)	(revision 8180)
+++ netinet6/ipcomp_input.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -36,6 +36,7 @@
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
+#include "opt_ipsec.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -100,6 +101,11 @@
 	int error;
 	size_t newlen, olen;
 	struct secasvar *sav = NULL;
+	u_int16_t sport = 0;
+	u_int16_t dport = 0;
+#ifdef IPSEC_NAT_T
+	struct m_tag *tag = NULL;
+#endif
 
 	if (m->m_pkthdr.len < off + sizeof(struct ipcomp)) {
 		ipseclog((LOG_DEBUG, "IPv4 IPComp input: assumption failed "
@@ -108,6 +114,14 @@
 		goto fail;
 	}
 
+#ifdef IPSEC_NAT_T
+	/* find the source port for NAT-T */
+	if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL)) != NULL) {
+		sport = ((u_int16_t *)(tag + 1))[0];
+		dport = ((u_int16_t *)(tag + 1))[1];
+	}
+#endif
+
 	md = m_pulldown(m, off, sizeof(*ipcomp), NULL);
 	if (!md) {
 		m = NULL;	/* already freed */
@@ -129,7 +143,7 @@
 
 	if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) {
 		sav = key_allocsa(AF_INET, (caddr_t)&ip->ip_src,
-			(caddr_t)&ip->ip_dst, IPPROTO_IPCOMP, htonl(cpi));
+			(caddr_t)&ip->ip_dst, IPPROTO_IPCOMP, htonl(cpi), sport, dport);
 		if (sav != NULL
 		 && (sav->state == SADB_SASTATE_MATURE
 		  || sav->state == SADB_SASTATE_DYING)) {
@@ -273,7 +287,7 @@
 
 	if (cpi >= IPCOMP_CPI_NEGOTIATE_MIN) {
 		sav = key_allocsa(AF_INET6, (caddr_t)&ip6->ip6_src,
-		    (caddr_t)&ip6->ip6_dst, IPPROTO_IPCOMP, htonl(cpi));
+		    (caddr_t)&ip6->ip6_dst, IPPROTO_IPCOMP, htonl(cpi), 0, 0);
 		if (sav != NULL
 		 && (sav->state == SADB_SASTATE_MATURE
 		  || sav->state == SADB_SASTATE_DYING)) {
Index: netinet6/esp_output.c
===================================================================
--- netinet6/esp_output.c	(.../6.x)	(revision 8180)
+++ netinet6/esp_output.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -32,6 +32,7 @@
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
+#include "opt_ipsec.h"
 
 /*
  * RFC1827/2406 Encapsulated Security Payload.
@@ -56,6 +57,10 @@
 #include <netinet/ip.h>
 #include <netinet/in_var.h>
 
+#ifdef IPSEC_NAT_T
+#include <netinet/udp.h>
+#endif
+
 #ifdef INET6
 #include <netinet/ip6.h>
 #include <netinet6/ip6_var.h>
@@ -139,6 +144,17 @@
 		hdrsiz = sizeof(struct newesp) + ivlen + 9 + authlen;
 	}
 
+#ifdef IPSEC_NAT_T
+	/*
+	 * If NAT-T is enabled, add the space for UDP encapsulation
+	 */
+	if (sav->natt_type != 0) {
+		hdrsiz += sizeof(struct udphdr);
+		if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
+			hdrsiz += sizeof(u_int64_t);
+	}
+#endif
+
 	return hdrsiz;
 
    estimate:
@@ -149,8 +165,15 @@
 	 *	9 = (maximum padding length without random padding length)
 	 *	   + (Pad Length field) + (Next Header field).
 	 *	16 = maximum ICV we support.
+	 *	sizeof(u_int64_t) = non IKE marker (NAT-T)
+	 * 	sizeof(struct udphdr) = UDP encapsulation (NAT-T)
 	 */
+#ifdef IPSEC_NAT_T
+ 	return sizeof(struct newesp) + esp_max_ivlen() + 9 + 16 +
+	    sizeof(u_int64_t) + sizeof(struct udphdr);
+#else
 	return sizeof(struct newesp) + esp_max_ivlen() + 9 + 16;
+#endif
 }
 
 /*
@@ -196,6 +219,9 @@
 	size_t extendsiz;
 	int error = 0;
 	struct ipsecstat *stat;
+#ifdef IPSEC_NAT_T
+	struct udphdr *udp = NULL;
+#endif
 
 	switch (af) {
 #ifdef INET
@@ -334,10 +360,25 @@
 
 	espoff = m->m_pkthdr.len - plen;
 
+#ifdef IPSEC_NAT_T
+	if (sav->natt_type != 0) {
+		esphlen += sizeof(struct udphdr);
+		espoff += sizeof(struct udphdr);
+		
+		if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) {
+			/* NON-IKE marker */
+			esphlen += sizeof(u_int64_t);
+			espoff += sizeof(u_int64_t);
+		}
+	}
+#endif
+
 	/*
 	 * grow the mbuf to accomodate ESP header.
 	 * before: IP ... payload
-	 * after:  IP ... ESP IV payload
+	 * after (without NAT-T):  IP ... ESP IV payload
+	 * after (with older NAT-T):  IP ... UDP non-IKE-marker ESP IV payload
+	 * after (with newer NAT-T):  IP ... UDP ESP IV payload
 	 */
 	if (M_LEADINGSPACE(md) < esphlen || (md->m_flags & M_EXT) != 0) {
 		MGET(n, M_DONTWAIT, MT_DATA);
@@ -358,6 +399,21 @@
 		esp = mtod(md, struct esp *);
 	}
 
+#ifdef IPSEC_NAT_T
+	if (sav->natt_type != 0) {
+		udp = (struct udphdr *)esp;
+		esp = (struct esp *)(udp + 1);
+
+		if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) {
+			u_int64_t *data = (u_int64_t *)esp;
+
+			*data = 0; /* NON-IKE marker */
+			esp = (struct esp *)(data + 1);
+		}
+	}
+#endif
+
+
 	nxt = *nexthdrp;
 	*nexthdrp = IPPROTO_ESP;
 	switch (af) {
@@ -523,6 +579,27 @@
 		break;
 	}
 
+#ifdef IPSEC_NAT_T
+	if (sav->natt_type != 0) {
+		*nexthdrp = IPPROTO_UDP;
+
+		/* 
+		 * Create the UDP encapsulation header for NAT-T
+		 * uh_len is set later, when the size is known.
+		 */
+		if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
+			udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT);
+		else
+			udp->uh_sport = KEY_PORTFROMSADDR(&sav->sah->saidx.src);
+
+
+		udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst);
+		udp->uh_sum = 0;
+	} else {
+		*nexthdrp = IPPROTO_ESP;
+	}
+#endif
+
 	/* initialize esp trailer. */
 	esptail = (struct esptail *)
 		(mtod(n, u_int8_t *) + n->m_len - sizeof(struct esptail));
@@ -666,6 +743,18 @@
 	}
     }
 
+#ifdef IPSEC_NAT_T
+	if (sav->natt_type != 0) {
+		struct ip *ip;
+		ip = mtod(m, struct ip *);
+#ifdef _IP_VHL
+		udp->uh_ulen = htons(ntohs(ip->ip_len) - (IP_VHL_HL(ip->ip_vhl) << 2));
+#else
+		udp->uh_ulen = htons(ntohs(ip->ip_len) - (ip->ip_hl << 2));
+#endif
+	}
+#endif
+
 noantireplay:
 	if (!m) {
 		ipseclog((LOG_ERR,
Index: netinet6/esp_input.c
===================================================================
--- netinet6/esp_input.c	(.../6.x)	(revision 8180)
+++ netinet6/esp_input.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -36,6 +36,7 @@
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
+#include "opt_ipsec.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -116,6 +117,11 @@
 	int ivlen;
 	size_t hlen;
 	size_t esplen;
+	u_int16_t sport = 0;
+	u_int16_t dport = 0;
+#ifdef IPSEC_NAT_T
+	struct m_tag *tag = NULL;
+#endif
 
 	/* sanity check for alignment. */
 	if (off % 4 != 0 || m->m_pkthdr.len % 4 != 0) {
@@ -135,6 +141,14 @@
 		}
 	}
 
+#ifdef IPSEC_NAT_T
+	/* find the source port for NAT_T */
+	if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL)) != NULL) {
+		sport = ((u_int16_t *)(tag + 1))[0];
+		dport = ((u_int16_t *)(tag + 1))[1];
+	}
+#endif
+
 	ip = mtod(m, struct ip *);
 	esp = (struct esp *)(((u_int8_t *)ip) + off);
 #ifdef _IP_VHL
@@ -148,7 +162,7 @@
 
 	if ((sav = key_allocsa(AF_INET,
 	                      (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
-	                      IPPROTO_ESP, spi)) == 0) {
+	                      IPPROTO_ESP, spi, sport, dport)) == 0) {
 		ipseclog((LOG_WARNING,
 		    "IPv4 ESP input: no key association found for spi %u\n",
 		    (u_int32_t)ntohl(spi)));
@@ -509,7 +523,7 @@
 
 	if ((sav = key_allocsa(AF_INET6,
 	                      (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
-	                      IPPROTO_ESP, spi)) == 0) {
+	                      IPPROTO_ESP, spi, 0, 0)) == 0) {
 		ipseclog((LOG_WARNING,
 		    "IPv6 ESP input: no key association found for spi %u\n",
 		    (u_int32_t)ntohl(spi)));
@@ -951,7 +965,7 @@
 			sav = key_allocsa(AF_INET6,
 					  (caddr_t)&sa6_src->sin6_addr,
 					  (caddr_t)&sa6_dst->sin6_addr,
-					  IPPROTO_ESP, espp->esp_spi);
+					  IPPROTO_ESP, espp->esp_spi, 0, 0);
 			if (sav) {
 				if (sav->state == SADB_SASTATE_MATURE ||
 				    sav->state == SADB_SASTATE_DYING)
Index: netinet6/ah_input.c
===================================================================
--- netinet6/ah_input.c	(.../6.x)	(revision 8180)
+++ netinet6/ah_input.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -36,6 +36,7 @@
 
 #include "opt_inet.h"
 #include "opt_inet6.h"
+#include "opt_ipsec.h"
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -113,6 +114,11 @@
 	u_int16_t nxt;
 	size_t hlen;
 	size_t stripsiz = 0;
+	u_int16_t sport = 0;
+	u_int16_t dport = 0;
+#ifdef IPSEC_NAT_T
+	struct m_tag *tag = NULL;
+#endif
 
 #ifndef PULLDOWN_TEST
 	if (m->m_len < off + sizeof(struct newah)) {
@@ -125,6 +131,14 @@
 		}
 	}
 
+#ifdef IPSEC_NAT_T
+	/* find the source port for NAT-T */
+	if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL)) != NULL) {
+		sport = ((u_int16_t *)(tag + 1))[0];
+		dport = ((u_int16_t *)(tag + 1))[1];
+	}
+#endif
+
 	ip = mtod(m, struct ip *);
 	ah = (struct ah *)(((caddr_t)ip) + off);
 #else
@@ -149,7 +163,7 @@
 
 	if ((sav = key_allocsa(AF_INET,
 	                      (caddr_t)&ip->ip_src, (caddr_t)&ip->ip_dst,
-	                      IPPROTO_AH, spi)) == 0) {
+	                      IPPROTO_AH, spi, sport, dport)) == 0) {
 		ipseclog((LOG_WARNING,
 		    "IPv4 AH input: no key association found for spi %u\n",
 		    (u_int32_t)ntohl(spi)));
@@ -599,7 +613,7 @@
 
 	if ((sav = key_allocsa(AF_INET6,
 	                      (caddr_t)&ip6->ip6_src, (caddr_t)&ip6->ip6_dst,
-	                      IPPROTO_AH, spi)) == 0) {
+	                      IPPROTO_AH, spi, 0, 0)) == 0) {
 		ipseclog((LOG_WARNING,
 		    "IPv6 AH input: no key association found for spi %u\n",
 		    (u_int32_t)ntohl(spi)));
@@ -998,7 +1012,7 @@
 			sav = key_allocsa(AF_INET6,
 					  (caddr_t)&sa6_src->sin6_addr,
 					  (caddr_t)&sa6_dst->sin6_addr,
-					  IPPROTO_AH, ahp->ah_spi);
+					  IPPROTO_AH, ahp->ah_spi, 0, 0);
 			if (sav) {
 				if (sav->state == SADB_SASTATE_MATURE ||
 				    sav->state == SADB_SASTATE_DYING)
Index: netkey/keydb.h
===================================================================
--- netkey/keydb.h	(.../6.x)	(revision 8180)
+++ netkey/keydb.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -114,6 +114,10 @@
 	pid_t pid;			/* message's pid */
 
 	struct secashead *sah;		/* back pointer to the secashead */
+	/* NAT-Traversal 
+	 */
+	u_int16_t	natt_type;
+	u_int16_t	esp_frag;
 
 	u_int32_t id;			/* SA id */
 };
Index: netkey/key.c
===================================================================
--- netkey/key.c	(.../6.x)	(revision 8180)
+++ netkey/key.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -194,6 +194,11 @@
 	0,				/* SADB_X_EXT_KMPRIVATE */
 	sizeof(struct sadb_x_policy),	/* SADB_X_EXT_POLICY */
 	sizeof(struct sadb_x_sa2),	/* SADB_X_SA2 */
+	sizeof(struct sadb_x_nat_t_type), /* SADB_X_EXT_NAT_T_TYPE */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_SPORT */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_DPORT */
+	sizeof(struct sadb_address),	/* SADB_X_EXT_NAT_T_OA */
+	sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */
 };
 static const int maxsize[] = {
 	sizeof(struct sadb_msg),	/* SADB_EXT_RESERVED */
@@ -216,6 +221,11 @@
 	0,				/* SADB_X_EXT_KMPRIVATE */
 	0,				/* SADB_X_EXT_POLICY */
 	sizeof(struct sadb_x_sa2),	/* SADB_X_SA2 */
+	sizeof(struct sadb_x_nat_t_type), /* SADB_X_EXT_NAT_T_TYPE */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_SPORT */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_DPORT */
+	0,				/* SADB_X_EXT_NAT_T_OA */
+	sizeof(struct sadb_x_nat_t_frag), /* SADB_X_EXT_NAT_T_FRAG */
 };
 
 static int ipsec_esp_keymin = 256;
@@ -384,6 +394,10 @@
 	const struct sadb_msghdr *);
 static int key_spddump(struct socket *, struct mbuf *,
 	const struct sadb_msghdr *);
+#ifdef IPSEC_NAT_T
+static int key_nat_map(struct socket *, struct mbuf *,
+	const struct sadb_msghdr *);
+#endif
 static struct mbuf *key_setdumpsp(struct secpolicy *,
 	u_int8_t, u_int32_t, u_int32_t);
 static u_int key_getspreqmsglen(struct secpolicy *);
@@ -406,6 +420,13 @@
 static struct mbuf *key_setsadbsa(struct secasvar *);
 static struct mbuf *key_setsadbaddr(u_int16_t,
 	struct sockaddr *, u_int8_t, u_int16_t);
+#ifdef IPSEC_NAT_T
+static struct mbuf *key_setsadbxport __P((u_int16_t, u_int16_t));
+static struct mbuf *key_setsadbxtype __P((u_int16_t));
+#endif
+static void key_porttosaddr __P((struct sockaddr *, u_int16_t));
+#define KEY_PORTTOSADDR(saddr, port) \
+	key_porttosaddr((struct sockaddr *)(saddr), (port))
 #if 0
 static struct mbuf *key_setsadbident(u_int16_t, u_int16_t, caddr_t,
 	int, u_int64_t);
@@ -927,10 +948,11 @@
  * keep source address in IPsec SA.  We see a tricky situation here.
  */
 struct secasvar *
-key_allocsa(family, src, dst, proto, spi)
+key_allocsa(family, src, dst, proto, spi, sport, dport)
 	u_int family, proto;
 	caddr_t src, dst;
 	u_int32_t spi;
+	u_int16_t sport, dport;
 {
 	struct secasvar *sav, *match;
 	u_int stateidx, state, tmpidx, matchidx;
@@ -941,11 +963,17 @@
 	int s;
 	const u_int *saorder_state_valid;
 	int arraysize;
+	int chkport = 0;
 
 	/* sanity check */
 	if (src == NULL || dst == NULL)
 		panic("key_allocsa: NULL pointer is passed.");
 
+#ifdef IPSEC_NAT_T
+	if ((sport != 0) && (dport != 0))
+		chkport = 1;
+#endif
+
 	/*
 	 * when both systems employ similar strategy to use a SA.
 	 * the search order is important even in the inbound case.
@@ -1004,8 +1032,11 @@
 		switch (family) {
 		case AF_INET:
 			bcopy(src, &sin.sin_addr, sizeof(sin.sin_addr));
+#ifdef IPSEC_NAT_T
+			sin.sin_port = sport;
+#endif
 			if (key_sockaddrcmp((struct sockaddr*)&sin,
-			    (struct sockaddr *)&sav->sah->saidx.src, 0) != 0)
+			    (struct sockaddr *)&sav->sah->saidx.src, chkport) != 0)
 				continue;
 
 			break;
@@ -1013,10 +1044,13 @@
 		case AF_INET6:
 			bcopy(src, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
 			sin6.sin6_scope_id = 0;
+#ifdef IPSEC_NAT_T
+			sin6.sin6_port = sport;
+#endif
 			if (sa6_recoverscope(&sin6))
 				continue;
 			if (key_sockaddrcmp((struct sockaddr *)&sin6,
-			    (struct sockaddr *)&sav->sah->saidx.src, 0) != 0)
+			    (struct sockaddr *)&sav->sah->saidx.src, chkport) != 0)
 				continue;
 			break;
 #endif
@@ -1032,8 +1066,11 @@
 		switch (family) {
 		case AF_INET:
 			bcopy(dst, &sin.sin_addr, sizeof(sin.sin_addr));
+#ifdef IPSEC_NAT_T
+			sin.sin_port = dport;
+#endif
 			if (key_sockaddrcmp((struct sockaddr*)&sin,
-			    (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0)
+			    (struct sockaddr *)&sav->sah->saidx.dst, chkport) != 0)
 				continue;
 
 			break;
@@ -1041,10 +1078,13 @@
 		case AF_INET6:
 			bcopy(dst, &sin6.sin6_addr, sizeof(sin6.sin6_addr));
 			sin6.sin6_scope_id = 0;
+#ifdef IPSEC_NAT_T
+			sin6.sin6_port = dport;
+#endif
 			if (sa6_recoverscope(&sin6))
 				continue;
 			if (key_sockaddrcmp((struct sockaddr *)&sin6,
-			    (struct sockaddr *)&sav->sah->saidx.dst, 0) != 0)
+			    (struct sockaddr *)&sav->sah->saidx.dst, chkport) != 0)
 				continue;
 			break;
 #endif
@@ -1873,6 +1913,7 @@
 		}
 	}
 
+#ifndef IPSEC_NAT_T
 	for (isr = newsp->req; isr; isr = isr->next) {
 		struct sockaddr *sa;
 
@@ -1916,6 +1957,7 @@
 			}
 		}
 	}
+#endif /* !IPSEC_NAT_T */
 
 	/*
 	 * bark if we have different address family on tunnel address
@@ -2475,6 +2517,72 @@
 	return 0;
 }
 
+#ifdef IPSEC_NAT_T
+/* 
+ * SADB_X_NAT_T_NEW_MAPPING 
+ */
+static int
+key_nat_map(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_x_nat_t_type *type;
+	struct sadb_x_nat_t_port *sport;
+	struct sadb_x_nat_t_port *dport;
+	struct sadb_address *addr;
+	struct sadb_x_nat_t_frag *frag;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_nat_map: NULL pointer is passed.");
+
+	if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] == NULL ||
+	    mhp->ext[SADB_X_EXT_NAT_T_SPORT] == NULL ||
+	    mhp->ext[SADB_X_EXT_NAT_T_DPORT] == NULL) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if ((mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type)) ||
+	    (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport)) ||
+	    (mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport))) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && 
+	    (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) &&
+	    (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	type = (struct sadb_x_nat_t_type *)mhp->ext[SADB_X_EXT_NAT_T_TYPE];
+	sport = (struct sadb_x_nat_t_port *)mhp->ext[SADB_X_EXT_NAT_T_SPORT];
+	dport = (struct sadb_x_nat_t_port *)mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+	addr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OA];
+	frag = (struct sadb_x_nat_t_frag *) mhp->ext[SADB_X_EXT_NAT_T_FRAG];
+
+	printf("sadb_nat_map: type %d, sport = %d, dport = %d\n", 
+		type->sadb_x_nat_t_type_type,
+		sport->sadb_x_nat_t_port_port,
+		dport->sadb_x_nat_t_port_port);
+
+	/* 
+	 * XXX handle that, it should also contain a SA, or anything 
+	 * that enable to update the SA information.
+	 */
+
+	return 0;
+}
+#endif /* IPSEC_NAT_T */
+
+
 static struct mbuf *
 key_setdumpsp(sp, type, seq, pid)
 	struct secpolicy *sp;
@@ -3025,6 +3133,10 @@
 	sav->lft_c = NULL;
 	sav->lft_h = NULL;
 	sav->lft_s = NULL;
+#ifdef IPSEC_NAT_T
+	sav->natt_type = 0;
+	sav->esp_frag = 0;
+#endif
 
 	/* SA */
 	if (mhp->ext[SADB_EXT_SA] != NULL) {
@@ -3491,6 +3603,11 @@
 		SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY, SADB_EXT_KEY_AUTH,
 		SADB_EXT_KEY_ENCRYPT, SADB_EXT_IDENTITY_SRC,
 		SADB_EXT_IDENTITY_DST, SADB_EXT_SENSITIVITY,
+#ifdef IPSEC_NAT_T
+		SADB_X_EXT_NAT_T_TYPE, SADB_X_EXT_NAT_T_SPORT, 
+		SADB_X_EXT_NAT_T_DPORT, SADB_X_EXT_NAT_T_OA,
+		SADB_X_EXT_NAT_T_FRAG,
+#endif
 	};
 
 	m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt);
@@ -3567,6 +3684,31 @@
 			p = sav->lft_s;
 			break;
 
+#ifdef IPSEC_NAT_T
+		case SADB_X_EXT_NAT_T_TYPE:
+			if ((m = key_setsadbxtype(sav->natt_type)) == NULL)
+				goto fail;
+			break;
+		
+		case SADB_X_EXT_NAT_T_DPORT:
+			if ((m = key_setsadbxport(KEY_PORTFROMSADDR
+			    (&sav->sah->saidx.dst),
+			    SADB_X_EXT_NAT_T_DPORT)) == NULL)
+				goto fail;
+			break;
+
+		case SADB_X_EXT_NAT_T_SPORT:
+			if ((m = key_setsadbxport(KEY_PORTFROMSADDR
+			    (&sav->sah->saidx.src),
+			    SADB_X_EXT_NAT_T_SPORT)) == NULL)
+				goto fail;
+			break;
+
+		case SADB_X_EXT_NAT_T_OA:
+		case SADB_X_EXT_NAT_T_FRAG:
+			continue;
+#endif
+
 		case SADB_EXT_ADDRESS_PROXY:
 		case SADB_EXT_IDENTITY_SRC:
 		case SADB_EXT_IDENTITY_DST:
@@ -3825,7 +3967,134 @@
 	return m;
 }
 
+#ifdef IPSEC_NAT_T
 /*
+ * set a type in sadb_x_nat_t_type
+ */
+static struct mbuf *
+key_setsadbxtype(type)
+	u_int16_t type;
+{
+	struct mbuf *m;
+	size_t len;
+	struct sadb_x_nat_t_type *p;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_x_nat_t_type));
+
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_x_nat_t_type *);
+
+	bzero(p, len);
+	p->sadb_x_nat_t_type_len = PFKEY_UNIT64(len);
+	p->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
+	p->sadb_x_nat_t_type_type = type;
+
+	return m;
+}
+/*
+ * set a port in sadb_x_nat_t_port. port is in network order
+ */
+static struct mbuf *
+key_setsadbxport(port, type)
+	u_int16_t port;
+	u_int16_t type;
+{
+	struct mbuf *m;
+	size_t len;
+	struct sadb_x_nat_t_port *p;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_x_nat_t_port));
+
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_x_nat_t_port *);
+
+	bzero(p, len);
+	p->sadb_x_nat_t_port_len = PFKEY_UNIT64(len);
+	p->sadb_x_nat_t_port_exttype = type;
+	p->sadb_x_nat_t_port_port = port;
+
+	return m;
+}
+
+/* 
+ * Get port from sockaddr, port is in network order
+ */
+u_int16_t 
+key_portfromsaddr(saddr)
+	struct sockaddr *saddr;
+{
+	u_int16_t port;
+
+	switch (saddr->sa_family) {
+	case AF_INET: {
+		struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
+
+		port = sin->sin_port;
+		break;
+	}
+#ifdef INET6
+	case AF_INET6: {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr;
+
+		port = sin6->sin6_port;
+		break;
+	}
+#endif
+	default:
+		printf("key_portfromsaddr: unexpected address family\n");
+		port = 0;
+		break;
+	}
+
+	return port;
+}
+#endif /* IPSEC_NAT_T */
+
+/*
+ * Set port is struct sockaddr. port is in network order
+ */
+static void
+key_porttosaddr(saddr, port)
+	struct sockaddr *saddr;
+	u_int16_t port;
+{
+	switch (saddr->sa_family) {
+	case AF_INET: {
+		struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
+
+		sin->sin_port = port;
+		break;
+	}
+#ifdef INET6
+	case AF_INET6: {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr;
+
+		sin6->sin6_port = port;
+		break;
+	}
+#endif
+	default:
+		printf("key_porttosaddr: unexpected address family %d\n", 
+		    saddr->sa_family);
+		break;
+	}
+
+	return;
+}
+
+/*
  * set data into sadb_lifetime
  */
 static struct mbuf *
@@ -4015,6 +4284,8 @@
 	struct secasindex *saidx0, *saidx1;
 	int flag;
 {
+	int chkport = 0;
+
 	/* sanity */
 	if (saidx0 == NULL && saidx1 == NULL)
 		return 1;
@@ -4037,13 +4308,30 @@
 
 		/* CMP_MODE_REQID, CMP_HEAD */
 		if (flag == CMP_MODE_REQID) {
+#ifdef IPSEC_NAT_T
 			/*
+			 * If NAT-T is enabled, check ports for tunnel mode.
+			 * Don't do it for transport mode, as there is no
+			 * port information available in the SP.
+			 * XXX also don't check ports if they are set to zero in the SPD:
+			 * This means we bave a non-generated SPD, which can't know UDP ports.
+			 */
+			if (saidx1->mode == IPSEC_MODE_TUNNEL &&
+			    satosin(&saidx1->src)->sin_port &&
+			    satosin(&saidx1->dst)->sin_port )
+				chkport = 1;
+#endif
+			/*
 			 * If reqid of SPD is non-zero, unique SA is required.
 			 * The result must be of same reqid in this case.
 			 */
 			if (saidx1->reqid != 0 && saidx0->reqid != saidx1->reqid)
 				return 0;
 		}
+#ifdef IPSEC_NAT_T
+		else
+			chkport = 1;
+#endif
 
 		if (flag == CMP_MODE_REQID) {
 			if (saidx0->mode != IPSEC_MODE_ANY &&
@@ -4052,11 +4340,11 @@
 		}
 
 		if (key_sockaddrcmp((struct sockaddr *)&saidx0->src,
-				(struct sockaddr *)&saidx1->src, 0) != 0) {
+				(struct sockaddr *)&saidx1->src, chkport) != 0) {
 			return 0;
 		}
 		if (key_sockaddrcmp((struct sockaddr *)&saidx0->dst,
-				(struct sockaddr *)&saidx1->dst, 0) != 0) {
+				(struct sockaddr *)&saidx1->dst, chkport) != 0) {
 			return 0;
 		}
 	}
@@ -4704,19 +4992,23 @@
 		return key_senderror(so, m, EINVAL);
 	}
 
-	/* make sure if port number is zero. */
+	/* make sure if port number is zero if NAT-T support is NOT compiled. */
 	switch (((struct sockaddr *)(src0 + 1))->sa_family) {
 	case AF_INET:
 		if (((struct sockaddr *)(src0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in *)(src0 + 1))->sin_port = 0;
+#endif
 		break;
 	case AF_INET6:
 		if (((struct sockaddr *)(src0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in6))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0;
+#endif
 		break;
 	default:
 		; /*???*/
@@ -4726,13 +5018,17 @@
 		if (((struct sockaddr *)(dst0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in *)(dst0 + 1))->sin_port = 0;
+#endif
 		break;
 	case AF_INET6:
 		if (((struct sockaddr *)(dst0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in6))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0;
+#endif
 		break;
 	default:
 		; /*???*/
@@ -4741,6 +5037,12 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
 
+	/* If not using NAT-T, make sure port numbers are set to zero. */
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* SPI allocation */
 	spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE],
 	                       &saidx);
@@ -4994,6 +5296,12 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
 
+	/* If not using NAT-T, make sure if port number is zero. */
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	if ((sah = key_getsah(&saidx)) == NULL) {
 		ipseclog((LOG_DEBUG, "key_update: no SA index found.\n"));
@@ -5060,6 +5368,68 @@
 		return key_senderror(so, m, error);
 	}
 
+#ifdef IPSEC_NAT_T
+	/*
+	 * Handle NAT-T info if present
+	 */
+	if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL)
+		printf("update: NAT-T OA present\n");
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL)) {
+		struct sadb_x_nat_t_type *type;
+		struct sadb_x_nat_t_port *sport;
+		struct sadb_x_nat_t_port *dport;
+		struct sadb_address *addr;
+		struct sadb_x_nat_t_frag *frag;
+
+		if ((mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport))) {
+			ipseclog((LOG_DEBUG, "key_update: "
+			    "invalid message.\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) {
+			ipseclog((LOG_DEBUG, "key_update: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) {
+			ipseclog((LOG_DEBUG, "key_update: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		type = (struct sadb_x_nat_t_type *)
+		    mhp->ext[SADB_X_EXT_NAT_T_TYPE];
+		sport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_SPORT];
+		dport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+		addr = (struct sadb_address *)
+		    mhp->ext[SADB_X_EXT_NAT_T_OA];
+		frag = (struct sadb_x_nat_t_frag *)
+		    mhp->ext[SADB_X_EXT_NAT_T_FRAG];
+
+		if (type)
+			sav->natt_type = type->sadb_x_nat_t_type_type;
+		if (sport)
+			KEY_PORTTOSADDR(&sav->sah->saidx.src, 
+			    sport->sadb_x_nat_t_port_port);
+		if (dport)
+			KEY_PORTTOSADDR(&sav->sah->saidx.dst,
+			    dport->sadb_x_nat_t_port_port);
+		if (frag)
+			sav->esp_frag = frag->sadb_x_nat_t_frag_fraglen;
+		else
+			sav->esp_frag = IP_MAXPACKET;
+	}
+#endif /* IPSEC_NAT_T */
+
     {
 	struct mbuf *n;
 
@@ -5189,6 +5559,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	if ((newsah = key_getsah(&saidx)) == NULL) {
 		/* create a new SA header */
@@ -5222,7 +5597,69 @@
 		return key_senderror(so, m, error);
 	}
 
+#ifdef IPSEC_NAT_T
 	/*
+	 * Handle NAT-T info if present
+	 */
+	if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL)
+		printf("add: NAT-T OA present\n");
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL)) {
+		struct sadb_x_nat_t_type *type;
+		struct sadb_x_nat_t_port *sport;
+		struct sadb_x_nat_t_port *dport;
+		struct sadb_address *addr;
+		struct sadb_x_nat_t_frag *frag;
+
+		if ((mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport))) {
+			ipseclog((LOG_DEBUG, "key_add: "
+			    "invalid message.\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) {
+			ipseclog((LOG_DEBUG, "key_add: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) {
+			ipseclog((LOG_DEBUG, "key_update: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		type = (struct sadb_x_nat_t_type *)
+		    mhp->ext[SADB_X_EXT_NAT_T_TYPE];
+		sport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_SPORT];
+		dport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+		addr = (struct sadb_address *)
+		    mhp->ext[SADB_X_EXT_NAT_T_OA];
+		frag = (struct sadb_x_nat_t_frag *)
+		    mhp->ext[SADB_X_EXT_NAT_T_FRAG];
+
+		if (type)
+			newsav->natt_type = type->sadb_x_nat_t_type_type;
+		if (sport)
+			KEY_PORTTOSADDR(&newsav->sah->saidx.src, 
+			    sport->sadb_x_nat_t_port_port);
+		if (dport)
+			KEY_PORTTOSADDR(&newsav->sah->saidx.dst,
+			    dport->sadb_x_nat_t_port_port);
+		if (frag)
+			newsav->esp_frag = frag->sadb_x_nat_t_frag_fraglen;
+		else
+			newsav->esp_frag = IP_MAXPACKET;
+	}
+#endif
+
+	/*
 	 * don't call key_freesav() here, as we would like to keep the SA
 	 * in the database on success.
 	 */
@@ -5416,6 +5853,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	LIST_FOREACH(sah, &sahtree, chain) {
 		if (sah->state == SADB_SASTATE_DEAD)
@@ -5483,6 +5925,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	LIST_FOREACH(sah, &sahtree, chain) {
 		if (sah->state == SADB_SASTATE_DEAD)
 			continue;
@@ -5592,6 +6039,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	LIST_FOREACH(sah, &sahtree, chain) {
 		if (sah->state == SADB_SASTATE_DEAD)
@@ -6272,6 +6724,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA index */
 	LIST_FOREACH(sah, &sahtree, chain) {
 		if (sah->state == SADB_SASTATE_DEAD)
@@ -6875,6 +7332,11 @@
 	key_spdadd,	/* SADB_X_SPDSETIDX */
 	NULL,		/* SADB_X_SPDEXPIRE */
 	key_spddelete2,	/* SADB_X_SPDDELETE2 */
+#ifdef IPSEC_NAT_T
+	key_nat_map, /* SADB_X_NAT_T_NEW_MAPPING */
+#else
+	NULL,
+#endif
 };
 
 /*
@@ -7227,6 +7689,13 @@
 		case SADB_EXT_SPIRANGE:
 		case SADB_X_EXT_POLICY:
 		case SADB_X_EXT_SA2:
+#ifdef IPSEC_NAT_T
+		case SADB_X_EXT_NAT_T_TYPE:
+		case SADB_X_EXT_NAT_T_SPORT:
+		case SADB_X_EXT_NAT_T_DPORT:
+		case SADB_X_EXT_NAT_T_OA:
+		case SADB_X_EXT_NAT_T_FRAG:
+#endif
 			/* duplicate check */
 			/*
 			 * XXX Are there duplication payloads of either
Index: netkey/key.h
===================================================================
--- netkey/key.h	(.../6.x)	(revision 8180)
+++ netkey/key.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -58,7 +58,8 @@
 	struct sockaddr *, struct sockaddr *, struct sockaddr *);
 extern int key_checkrequest
 	(struct ipsecrequest *isr, struct secasindex *);
-extern struct secasvar *key_allocsa(u_int, caddr_t, caddr_t, u_int, u_int32_t);
+extern struct secasvar *key_allocsa(u_int, caddr_t, caddr_t, u_int, u_int32_t,
+									u_int16_t, u_int16_t);
 extern void key_freesp(struct secpolicy *);
 extern void key_freesav(struct secasvar *);
 extern struct secpolicy *key_newsp(u_int32_t);
@@ -78,6 +79,10 @@
 extern void key_sa_recordxfer(struct secasvar *, struct mbuf *);
 extern void key_sa_routechange(struct sockaddr *);
 extern void key_sa_stir_iv(struct secasvar *);
+#ifdef IPSEC_NAT_T
+u_int16_t key_portfromsaddr __P((struct sockaddr *));
+#define KEY_PORTFROMSADDR(saddr) key_portfromsaddr((struct sockaddr *)(saddr))
+#endif
 
 /* to keep compatibility with FAST_IPSEC */
 #define	KEY_ALLOCSA(dst, proto, spi)	\
Index: netipsec/ipsec.c
===================================================================
--- netipsec/ipsec.c	(.../6.x)	(revision 8180)
+++ netipsec/ipsec.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -1808,15 +1808,15 @@
 
 /* Return a printable string for the IPv4 address. */
 static char *
-inet_ntoa4(struct in_addr ina)
+inet_ntoa4(const struct sockaddr_in *sin)
 {
-	static char buf[4][4 * sizeof "123" + 4];
-	unsigned char *ucp = (unsigned char *) &ina;
+	static char buf[4][4 * sizeof "123" + 4 + 10];
+	const unsigned char *ucp = (const unsigned char *)&sin->sin_addr;
 	static int i = 3;
 
 	i = (i + 1) % 4;
-	sprintf(buf[i], "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff,
-	    ucp[2] & 0xff, ucp[3] & 0xff);
+	sprintf(buf[i], "%d.%d.%d.%d[%u]", ucp[0] & 0xff, ucp[1] & 0xff,
+	    ucp[2] & 0xff, ucp[3] & 0xff, ntohs(sin->sin_port));
 	return (buf[i]);
 }
 
@@ -1827,7 +1827,7 @@
 	switch (sa->sa.sa_family) {
 #if INET
 	case AF_INET:
-		return inet_ntoa4(sa->sin.sin_addr);
+		return inet_ntoa4(&sa->sin);
 #endif /* INET */
 
 #if INET6
Index: netipsec/keydb.h
===================================================================
--- netipsec/keydb.h	(.../6.x)	(revision 8180)
+++ netipsec/keydb.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -117,6 +117,12 @@
 	struct secashead *sah;		/* back pointer to the secashead */
 
 	/*
+	 * NAT-Traversal 
+	 */
+	u_int16_t	natt_type;
+	u_int16_t	esp_frag;
+
+	/*
 	 * NB: Fields with a tdb_ prefix are part of the "glue" used
 	 *     to interface to the OpenBSD crypto support.  This was done
 	 *     to distinguish this code from the mainline KAME code.
Index: netipsec/ipsec_input.c
===================================================================
--- netipsec/ipsec_input.c	(.../6.x)	(revision 8180)
+++ netipsec/ipsec_input.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -110,6 +110,9 @@
 	struct secasvar *sav;
 	u_int32_t spi;
 	int error;
+#ifdef IPSEC_NAT_T
+	struct m_tag *tag;
+#endif
 
 	IPSEC_ISTAT(sproto, espstat.esps_input, ahstat.ahs_input,
 		ipcompstat.ipcomps_input);
@@ -160,6 +163,13 @@
 		m_copydata(m, offsetof(struct ip, ip_dst),
 		    sizeof(struct in_addr),
 		    (caddr_t) &dst_address.sin.sin_addr);
+#ifdef IPSEC_NAT_T
+		/* find the source port for NAT_T */
+		if ((tag = m_tag_find(m, PACKET_TAG_IPSEC_NAT_T_PORTS, NULL))
+				    != NULL) {
+			dst_address.sin.sin_port = ((u_int16_t *)(tag + 1))[1];
+		}
+#endif /* IPSEC_NAT_T */
 		break;
 #endif /* INET */
 #ifdef INET6
@@ -179,7 +189,7 @@
 	}
 
 	/* NB: only pass dst since key_allocsa follows RFC2401 */
-	sav = KEY_ALLOCSA(&dst_address, sproto, spi);
+	sav = KEY_ALLOCSA( &dst_address, sproto, spi);
 	if (sav == NULL) {
 		DPRINTF(("%s: no key association found for SA %s/%08lx/%u\n",
 			  __func__, ipsec_address(&dst_address),
Index: netipsec/ipsec_output.c
===================================================================
--- netipsec/ipsec_output.c	(.../6.x)	(revision 8180)
+++ netipsec/ipsec_output.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -81,6 +81,10 @@
 
 #include <machine/in_cksum.h>
 
+#ifdef IPSEC_NAT_T
+#include <netinet/udp.h>
+#endif
+
 int
 ipsec_process_done(struct mbuf *m, struct ipsecrequest *isr)
 {
@@ -173,6 +177,51 @@
 		ip->ip_len = ntohs(ip->ip_len);
 		ip->ip_off = ntohs(ip->ip_off);
 
+#ifdef IPSEC_NAT_T
+		/*
+		 * If NAT-T is enabled, now that all IPSEC processing is done
+		 * insert UDP encapsulation header after IP header.
+		 */
+		if (sav->natt_type != 0) {
+			int size = sizeof(struct udphdr);
+#ifdef _IP_VHL
+			int hlen = IP_VHL_HL(ip->ip_vhl);
+#else
+			int hlen = (ip->ip_hl << 2);
+#endif
+			int off;
+			struct mbuf *mi;
+			struct udphdr *udp;
+
+			if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
+				size += sizeof(u_int64_t);
+
+			if ( (mi = m_makespace(m, hlen, size, &off)) == NULL ) {
+				error = ENOBUFS;
+				goto bad;
+			}
+
+			udp = (struct udphdr *)(mtod(mi, caddr_t) + off);
+
+			if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE)
+				udp->uh_sport = htons(UDP_ENCAP_ESPINUDP_PORT);
+			else
+				udp->uh_sport =
+					KEY_PORTFROMSADDR(&sav->sah->saidx.src);
+
+			udp->uh_dport = KEY_PORTFROMSADDR(&sav->sah->saidx.dst);
+			udp->uh_sum = 0;
+			udp->uh_ulen = htons(m->m_pkthdr.len - hlen);
+			ip->ip_len = m->m_pkthdr.len;
+			ip->ip_p = IPPROTO_UDP;
+
+			if (sav->natt_type == UDP_ENCAP_ESPINUDP_NON_IKE) {
+				u_int64_t *marker = (u_int64_t *)(udp + 1);
+				*marker = 0;
+			}
+		}
+#endif /* IPSEC_NAT_T */
+
 		return ip_output(m, NULL, NULL, IP_RAWOUTPUT, NULL, NULL);
 #endif /* INET */
 #ifdef INET6
Index: netipsec/key.c
===================================================================
--- netipsec/key.c	(.../6.x)	(revision 8180)
+++ netipsec/key.c	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -210,6 +210,11 @@
 	0,				/* SADB_X_EXT_KMPRIVATE */
 	sizeof(struct sadb_x_policy),	/* SADB_X_EXT_POLICY */
 	sizeof(struct sadb_x_sa2),	/* SADB_X_SA2 */
+	sizeof(struct sadb_x_nat_t_type), /* SADB_X_EXT_NAT_T_TYPE */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_SPORT */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_DPORT */
+	sizeof(struct sadb_address),	/* SADB_X_EXT_NAT_T_OA */
+	sizeof(struct sadb_x_nat_t_frag),/* SADB_X_EXT_NAT_T_FRAG */
 };
 static const int maxsize[] = {
 	sizeof(struct sadb_msg),	/* SADB_EXT_RESERVED */
@@ -232,6 +237,11 @@
 	0,				/* SADB_X_EXT_KMPRIVATE */
 	0,				/* SADB_X_EXT_POLICY */
 	sizeof(struct sadb_x_sa2),	/* SADB_X_SA2 */
+	sizeof(struct sadb_x_nat_t_type), /* SADB_X_EXT_NAT_T_TYPE */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_SPORT */
+	sizeof(struct sadb_x_nat_t_port), /* SADB_X_EXT_NAT_T_DPORT */
+	0,				/* SADB_X_EXT_NAT_T_OA */
+	sizeof(struct sadb_x_nat_t_frag), /* SADB_X_EXT_NAT_T_FRAG */
 };
 
 static int ipsec_esp_keymin = 256;
@@ -393,6 +403,10 @@
 	const struct sadb_msghdr *));
 static int key_spddump __P((struct socket *, struct mbuf *,
 	const struct sadb_msghdr *));
+#ifdef IPSEC_NAT_T
+static int key_nat_map(struct socket *, struct mbuf *,
+	const struct sadb_msghdr *);
+#endif
 static struct mbuf *key_setdumpsp __P((struct secpolicy *,
 	u_int8_t, u_int32_t, u_int32_t));
 static u_int key_getspreqmsglen __P((struct secpolicy *));
@@ -418,6 +432,13 @@
 static struct mbuf *key_setsadbsa __P((struct secasvar *));
 static struct mbuf *key_setsadbaddr __P((u_int16_t,
 	const struct sockaddr *, u_int8_t, u_int16_t));
+#ifdef IPSEC_NAT_T
+static struct mbuf *key_setsadbxport __P((u_int16_t, u_int16_t));
+static struct mbuf *key_setsadbxtype __P((u_int16_t));
+#endif
+static void key_porttosaddr __P((struct sockaddr *, u_int16_t));
+#define KEY_PORTTOSADDR(saddr, port) \
+	key_porttosaddr((struct sockaddr *)(saddr), (port))
 static struct mbuf *key_setsadbxsa2 __P((u_int8_t, u_int32_t, u_int32_t));
 static struct mbuf *key_setsadbxpolicy __P((u_int16_t, u_int8_t,
 	u_int32_t));
@@ -1042,12 +1063,20 @@
 	struct secasvar *sav;
 	u_int stateidx, arraysize, state;
 	const u_int *saorder_state_valid;
+	int chkport = 0;
 
 	IPSEC_ASSERT(dst != NULL, ("null dst address"));
 
 	KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
 		printf("DP %s from %s:%u\n", __func__, where, tag));
 
+#ifdef IPSEC_NAT_T
+        if (dst->sa.sa_family == AF_INET &&
+	    dst->sa.sa_len == sizeof(struct sockaddr_in) &&
+	    dst->sin.sin_port != 0)
+		chkport = 1;
+#endif
+
 	/*
 	 * searching SAD.
 	 * XXX: to be checked internal IP header somewhere.  Also when
@@ -1079,11 +1108,11 @@
 					continue;
 #if 0	/* don't check src */
 				/* check src address */
-				if (key_sockaddrcmp(&src->sa, &sav->sah->saidx.src.sa, 0) != 0)
+				if (key_sockaddrcmp(&src->sa, &sav->sah->saidx.src.sa, chkport) != 0)
 					continue;
 #endif
 				/* check dst address */
-				if (key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, 0) != 0)
+				if (key_sockaddrcmp(&dst->sa, &sav->sah->saidx.dst.sa, chkport) != 0)
 					continue;
 				sa_addref(sav);
 				goto done;
@@ -1847,6 +1876,52 @@
 		return key_senderror(so, m, error);
 	}
 
+#ifndef IPSEC_NAT_T
+	for (isr = newsp->req; isr; isr = isr->next) {
+		struct sockaddr *sa;
+
+		/*
+		 * port spec is not permitted for tunnel mode
+		 */
+		if (isr->saidx.mode == IPSEC_MODE_TUNNEL && src0 && dst0) {
+			sa = (struct sockaddr *)(src0 + 1);
+			switch (sa->sa_family) {
+			case AF_INET:
+				if (((struct sockaddr_in *)sa)->sin_port) {
+					keydb_delsecpolicy(newsp);
+					return key_senderror(so, m, EINVAL);
+				}
+				break;
+			case AF_INET6:
+				if (((struct sockaddr_in6 *)sa)->sin6_port) {
+					keydb_delsecpolicy(newsp);
+					return key_senderror(so, m, EINVAL);
+				}
+				break;
+			default:
+				break;
+			}
+			sa = (struct sockaddr *)(dst0 + 1);
+			switch (sa->sa_family) {
+			case AF_INET:
+				if (((struct sockaddr_in *)sa)->sin_port) {
+					keydb_delsecpolicy(newsp);
+					return key_senderror(so, m, EINVAL);
+				}
+				break;
+			case AF_INET6:
+				if (((struct sockaddr_in6 *)sa)->sin6_port) {
+					keydb_delsecpolicy(newsp);
+					return key_senderror(so, m, EINVAL);
+				}
+				break;
+			default:
+				break;
+			}
+		}
+	}
+#endif /* !IPSEC_NAT_T */
+
 	if ((newsp->id = key_getnewspid()) == 0) {
 		_key_delsp(newsp);
 		return key_senderror(so, m, ENOBUFS);
@@ -2355,6 +2430,71 @@
 	return key_sendup_mbuf(so, m, KEY_SENDUP_ALL);
 }
 
+#ifdef IPSEC_NAT_T
+/* 
+ * SADB_X_NAT_T_NEW_MAPPING 
+ */
+static int
+key_nat_map(so, m, mhp)
+	struct socket *so;
+	struct mbuf *m;
+	const struct sadb_msghdr *mhp;
+{
+	struct sadb_x_nat_t_type *type;
+	struct sadb_x_nat_t_port *sport;
+	struct sadb_x_nat_t_port *dport;
+	struct sadb_address *addr;
+	struct sadb_x_nat_t_frag *frag;
+
+	/* sanity check */
+	if (so == NULL || m == NULL || mhp == NULL || mhp->msg == NULL)
+		panic("key_nat_map: NULL pointer is passed.");
+
+	if (mhp->ext[SADB_X_EXT_NAT_T_TYPE] == NULL ||
+	    mhp->ext[SADB_X_EXT_NAT_T_SPORT] == NULL ||
+	    mhp->ext[SADB_X_EXT_NAT_T_DPORT] == NULL) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+	if ((mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type)) ||
+	    (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport)) ||
+	    (mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport))) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message.\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) && 
+	    (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) &&
+	    (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) {
+		ipseclog((LOG_DEBUG, "key_nat_map: invalid message\n"));
+		return key_senderror(so, m, EINVAL);
+	}
+
+	type = (struct sadb_x_nat_t_type *)mhp->ext[SADB_X_EXT_NAT_T_TYPE];
+	sport = (struct sadb_x_nat_t_port *)mhp->ext[SADB_X_EXT_NAT_T_SPORT];
+	dport = (struct sadb_x_nat_t_port *)mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+	addr = (struct sadb_address *)mhp->ext[SADB_X_EXT_NAT_T_OA];
+	frag = (struct sadb_x_nat_t_frag *) mhp->ext[SADB_X_EXT_NAT_T_FRAG];
+
+	printf("sadb_nat_map: type %d, sport = %d, dport = %d\n", 
+		type->sadb_x_nat_t_type_type,
+		sport->sadb_x_nat_t_port_port,
+		dport->sadb_x_nat_t_port_port);
+
+	/* 
+	 * XXX handle that, it should also contain a SA, or anything 
+	 * that enable to update the SA information.
+	 */
+
+	return 0;
+}
+#endif /* IPSEC_NAT_T */
+
 /*
  * SADB_SPDDUMP processing
  * receive
@@ -2984,6 +3124,10 @@
 	sav->lft_c = NULL;
 	sav->lft_h = NULL;
 	sav->lft_s = NULL;
+#ifdef IPSEC_NAT_T
+	sav->natt_type = 0;
+	sav->esp_frag = 0;
+#endif
 	sav->tdb_xform = NULL;		/* transform */
 	sav->tdb_encalgxform = NULL;	/* encoding algorithm */
 	sav->tdb_authalgxform = NULL;	/* authentication algorithm */
@@ -3294,6 +3438,11 @@
 		SADB_EXT_ADDRESS_DST, SADB_EXT_ADDRESS_PROXY, SADB_EXT_KEY_AUTH,
 		SADB_EXT_KEY_ENCRYPT, SADB_EXT_IDENTITY_SRC,
 		SADB_EXT_IDENTITY_DST, SADB_EXT_SENSITIVITY,
+#ifdef IPSEC_NAT_T
+		SADB_X_EXT_NAT_T_TYPE, SADB_X_EXT_NAT_T_SPORT, 
+		SADB_X_EXT_NAT_T_DPORT, SADB_X_EXT_NAT_T_OA,
+		SADB_X_EXT_NAT_T_FRAG,
+#endif
 	};
 
 	m = key_setsadbmsg(type, 0, satype, seq, pid, sav->refcnt);
@@ -3370,6 +3519,31 @@
 			p = sav->lft_s;
 			break;
 
+#ifdef IPSEC_NAT_T
+		case SADB_X_EXT_NAT_T_TYPE:
+			if ((m = key_setsadbxtype(sav->natt_type)) == NULL)
+				goto fail;
+			break;
+		
+		case SADB_X_EXT_NAT_T_DPORT:
+			if ((m = key_setsadbxport(KEY_PORTFROMSADDR
+			    (&sav->sah->saidx.dst),
+			    SADB_X_EXT_NAT_T_DPORT)) == NULL)
+				goto fail;
+			break;
+
+		case SADB_X_EXT_NAT_T_SPORT:
+			if ((m = key_setsadbxport(KEY_PORTFROMSADDR
+			    (&sav->sah->saidx.src),
+			    SADB_X_EXT_NAT_T_SPORT)) == NULL)
+				goto fail;
+			break;
+
+		case SADB_X_EXT_NAT_T_OA:
+		case SADB_X_EXT_NAT_T_FRAG:
+			continue;
+#endif
+
 		case SADB_EXT_ADDRESS_PROXY:
 		case SADB_EXT_IDENTITY_SRC:
 		case SADB_EXT_IDENTITY_DST:
@@ -3588,7 +3762,134 @@
 	return m;
 }
 
+#ifdef IPSEC_NAT_T
 /*
+ * set a type in sadb_x_nat_t_type
+ */
+static struct mbuf *
+key_setsadbxtype(type)
+	u_int16_t type;
+{
+	struct mbuf *m;
+	size_t len;
+	struct sadb_x_nat_t_type *p;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_x_nat_t_type));
+
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_x_nat_t_type *);
+
+	bzero(p, len);
+	p->sadb_x_nat_t_type_len = PFKEY_UNIT64(len);
+	p->sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE;
+	p->sadb_x_nat_t_type_type = type;
+
+	return m;
+}
+/*
+ * set a port in sadb_x_nat_t_port. port is in network order
+ */
+static struct mbuf *
+key_setsadbxport(port, type)
+	u_int16_t port;
+	u_int16_t type;
+{
+	struct mbuf *m;
+	size_t len;
+	struct sadb_x_nat_t_port *p;
+
+	len = PFKEY_ALIGN8(sizeof(struct sadb_x_nat_t_port));
+
+	m = key_alloc_mbuf(len);
+	if (!m || m->m_next) {	/*XXX*/
+		if (m)
+			m_freem(m);
+		return NULL;
+	}
+
+	p = mtod(m, struct sadb_x_nat_t_port *);
+
+	bzero(p, len);
+	p->sadb_x_nat_t_port_len = PFKEY_UNIT64(len);
+	p->sadb_x_nat_t_port_exttype = type;
+	p->sadb_x_nat_t_port_port = port;
+
+	return m;
+}
+
+/* 
+ * Get port from sockaddr, port is in network order
+ */
+u_int16_t 
+key_portfromsaddr(saddr)
+	struct sockaddr *saddr;
+{
+	u_int16_t port;
+
+	switch (saddr->sa_family) {
+	case AF_INET: {
+		struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
+
+		port = sin->sin_port;
+		break;
+	}
+#ifdef INET6
+	case AF_INET6: {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr;
+
+		port = sin6->sin6_port;
+		break;
+	}
+#endif
+	default:
+		printf("key_portfromsaddr: unexpected address family\n");
+		port = 0;
+		break;
+	}
+
+	return port;
+}
+#endif /* IPSEC_NAT_T */
+
+/*
+ * Set port is struct sockaddr. port is in network order
+ */
+static void
+key_porttosaddr(saddr, port)
+	struct sockaddr *saddr;
+	u_int16_t port;
+{
+	switch (saddr->sa_family) {
+	case AF_INET: {
+		struct sockaddr_in *sin = (struct sockaddr_in *)saddr;
+
+		sin->sin_port = port;
+		break;
+	}
+#ifdef INET6
+	case AF_INET6: {
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)saddr;
+
+		sin6->sin6_port = port;
+		break;
+	}
+#endif
+	default:
+		printf("key_porttosaddr: unexpected address family %d\n", 
+		    saddr->sa_family);
+		break;
+	}
+
+	return;
+}
+
+/*
  * set data into sadb_x_policy
  */
 static struct mbuf *
@@ -3738,6 +4039,8 @@
 	const struct secasindex *saidx1,
 	int flag)
 {
+	int chkport = 0;
+
 	/* sanity */
 	if (saidx0 == NULL && saidx1 == NULL)
 		return 1;
@@ -3761,13 +4064,30 @@
 		/* CMP_MODE_REQID, CMP_REQID, CMP_HEAD */
 		if (flag == CMP_MODE_REQID
 		  ||flag == CMP_REQID) {
+#ifdef IPSEC_NAT_T
 			/*
+			 * If NAT-T is enabled, check ports for tunnel mode.
+			 * Don't do it for transport mode, as there is no
+			 * port information available in the SP.
+			 * XXX also don't check ports if they are set to zero in the SPD:
+			 * This means we bave a non-generated SPD, which can't know UDP ports.
+			 */
+			if (saidx1->mode == IPSEC_MODE_TUNNEL &&
+			    ((const struct sockaddr_in *)(&saidx1->src))->sin_port &&
+			    ((const struct sockaddr_in *)(&saidx1->dst))->sin_port )
+				chkport = 1;
+#endif /* IPSEC_NAT_T */
+			/*
 			 * If reqid of SPD is non-zero, unique SA is required.
 			 * The result must be of same reqid in this case.
 			 */
 			if (saidx1->reqid != 0 && saidx0->reqid != saidx1->reqid)
 				return 0;
 		}
+#ifdef IPSEC_NAT_T
+		else
+			chkport = 1;
+#endif
 
 		if (flag == CMP_MODE_REQID) {
 			if (saidx0->mode != IPSEC_MODE_ANY
@@ -3775,10 +4095,10 @@
 				return 0;
 		}
 
-		if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, 0) != 0) {
+		if (key_sockaddrcmp(&saidx0->src.sa, &saidx1->src.sa, chkport) != 0) {
 			return 0;
 		}
-		if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, 0) != 0) {
+		if (key_sockaddrcmp(&saidx0->dst.sa, &saidx1->dst.sa, chkport) != 0) {
 			return 0;
 		}
 	}
@@ -4398,13 +4718,17 @@
 		if (((struct sockaddr *)(src0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in *)(src0 + 1))->sin_port = 0;
+#endif
 		break;
 	case AF_INET6:
 		if (((struct sockaddr *)(src0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in6))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in6 *)(src0 + 1))->sin6_port = 0;
+#endif
 		break;
 	default:
 		; /*???*/
@@ -4414,13 +4738,17 @@
 		if (((struct sockaddr *)(dst0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in *)(dst0 + 1))->sin_port = 0;
+#endif
 		break;
 	case AF_INET6:
 		if (((struct sockaddr *)(dst0 + 1))->sa_len !=
 		    sizeof(struct sockaddr_in6))
 			return key_senderror(so, m, EINVAL);
+#ifndef IPSEC_NAT_T
 		((struct sockaddr_in6 *)(dst0 + 1))->sin6_port = 0;
+#endif
 		break;
 	default:
 		; /*???*/
@@ -4429,6 +4757,12 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
 
+	/* If not using NAT-T, make sure port numbers are set to zero. */
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* SPI allocation */
 	spi = key_do_getnewspi((struct sadb_spirange *)mhp->ext[SADB_EXT_SPIRANGE],
 	                       &saidx);
@@ -4684,6 +5018,12 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
 
+	/* If not using NAT-T, make sure if port number is zero. */
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	if ((sah = key_getsah(&saidx)) == NULL) {
 		ipseclog((LOG_DEBUG, "%s: no SA index found.\n", __func__));
@@ -4750,6 +5090,68 @@
 		return key_senderror(so, m, 0);
 	}
 
+#ifdef IPSEC_NAT_T
+	/*
+	 * Handle NAT-T info if present
+	 */
+	if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL)
+		printf("update: NAT-T OA present\n");
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL)) {
+		struct sadb_x_nat_t_type *type;
+		struct sadb_x_nat_t_port *sport;
+		struct sadb_x_nat_t_port *dport;
+		struct sadb_address *addr;
+		struct sadb_x_nat_t_frag *frag;
+
+		if ((mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport))) {
+			ipseclog((LOG_DEBUG, "key_update: "
+			    "invalid message.\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) {
+			ipseclog((LOG_DEBUG, "key_update: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) {
+			ipseclog((LOG_DEBUG, "key_update: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		type = (struct sadb_x_nat_t_type *)
+		    mhp->ext[SADB_X_EXT_NAT_T_TYPE];
+		sport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_SPORT];
+		dport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+		addr = (struct sadb_address *)
+		    mhp->ext[SADB_X_EXT_NAT_T_OA];
+		frag = (struct sadb_x_nat_t_frag *)
+		    mhp->ext[SADB_X_EXT_NAT_T_FRAG];
+
+		if (type)
+			sav->natt_type = type->sadb_x_nat_t_type_type;
+		if (sport)
+			KEY_PORTTOSADDR(&sav->sah->saidx.src, 
+			    sport->sadb_x_nat_t_port_port);
+		if (dport)
+			KEY_PORTTOSADDR(&sav->sah->saidx.dst,
+			    dport->sadb_x_nat_t_port_port);
+		if (frag)
+			sav->esp_frag = frag->sadb_x_nat_t_frag_fraglen;
+		else
+			sav->esp_frag = IP_MAXPACKET;
+	}
+#endif /* IPSEC_NAT_T */
+
     {
 	struct mbuf *n;
 
@@ -4882,6 +5284,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	if ((newsah = key_getsah(&saidx)) == NULL) {
 		/* create a new SA header */
@@ -4918,7 +5325,69 @@
 		return key_senderror(so, m, error);
 	}
 
+#ifdef IPSEC_NAT_T
 	/*
+	 * Handle NAT-T info if present
+	 */
+	if (mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL)
+		printf("add: NAT-T OA present\n");
+
+	if ((mhp->ext[SADB_X_EXT_NAT_T_TYPE] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_SPORT] != NULL) &&
+	    (mhp->ext[SADB_X_EXT_NAT_T_DPORT] != NULL)) {
+		struct sadb_x_nat_t_type *type;
+		struct sadb_x_nat_t_port *sport;
+		struct sadb_x_nat_t_port *dport;
+		struct sadb_address *addr;
+		struct sadb_x_nat_t_frag *frag;
+
+		if ((mhp->extlen[SADB_X_EXT_NAT_T_TYPE] < sizeof(*type)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_SPORT] < sizeof(*sport)) ||
+		    (mhp->extlen[SADB_X_EXT_NAT_T_DPORT] < sizeof(*dport))) {
+			ipseclog((LOG_DEBUG, "key_add: "
+			    "invalid message.\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_OA] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_OA] < sizeof(*addr))) {
+			ipseclog((LOG_DEBUG, "key_add: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		if ((mhp->ext[SADB_X_EXT_NAT_T_FRAG] != NULL) &&
+		    (mhp->extlen[SADB_X_EXT_NAT_T_FRAG] < sizeof(*frag))) {
+			ipseclog((LOG_DEBUG, "key_update: invalid message\n"));
+			return key_senderror(so, m, EINVAL);
+		}
+
+		type = (struct sadb_x_nat_t_type *)
+		    mhp->ext[SADB_X_EXT_NAT_T_TYPE];
+		sport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_SPORT];
+		dport = (struct sadb_x_nat_t_port *)
+		    mhp->ext[SADB_X_EXT_NAT_T_DPORT];
+		addr = (struct sadb_address *)
+		    mhp->ext[SADB_X_EXT_NAT_T_OA];
+		frag = (struct sadb_x_nat_t_frag *)
+		    mhp->ext[SADB_X_EXT_NAT_T_FRAG];
+
+		if (type)
+			newsav->natt_type = type->sadb_x_nat_t_type_type;
+		if (sport)
+			KEY_PORTTOSADDR(&newsav->sah->saidx.src, 
+			    sport->sadb_x_nat_t_port_port);
+		if (dport)
+			KEY_PORTTOSADDR(&newsav->sah->saidx.dst,
+			    dport->sadb_x_nat_t_port_port);
+		if (frag)
+			newsav->esp_frag = frag->sadb_x_nat_t_frag_fraglen;
+		else
+			newsav->esp_frag = IP_MAXPACKET;
+	}
+#endif
+
+	/*
 	 * don't call key_freesav() here, as we would like to keep the SA
 	 * in the database on success.
 	 */
@@ -5118,6 +5587,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	SAHTREE_LOCK();
 	LIST_FOREACH(sah, &sahtree, chain) {
@@ -5187,6 +5661,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	SAHTREE_LOCK();
 	LIST_FOREACH(sah, &sahtree, chain) {
 		if (sah->state == SADB_SASTATE_DEAD)
@@ -5301,6 +5780,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA header */
 	SAHTREE_LOCK();
 	LIST_FOREACH(sah, &sahtree, chain) {
@@ -5988,6 +6472,11 @@
 	/* XXX boundary check against sa_len */
 	KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
 
+#ifndef IPSEC_NAT_T
+	KEY_PORTTOSADDR(&saidx.src, 0);
+	KEY_PORTTOSADDR(&saidx.dst, 0);
+#endif
+
 	/* get a SA index */
 	SAHTREE_LOCK();
 	LIST_FOREACH(sah, &sahtree, chain) {
@@ -6596,6 +7085,11 @@
 	key_spdadd,	/* SADB_X_SPDSETIDX */
 	NULL,		/* SADB_X_SPDEXPIRE */
 	key_spddelete2,	/* SADB_X_SPDDELETE2 */
+#ifdef IPSEC_NAT_T
+	key_nat_map, /* SADB_X_NAT_T_NEW_MAPPING */
+#else
+	NULL,
+#endif
 };
 
 /*
@@ -6932,6 +7426,13 @@
 		case SADB_EXT_SPIRANGE:
 		case SADB_X_EXT_POLICY:
 		case SADB_X_EXT_SA2:
+#ifdef IPSEC_NAT_T
+		case SADB_X_EXT_NAT_T_TYPE:
+		case SADB_X_EXT_NAT_T_SPORT:
+		case SADB_X_EXT_NAT_T_DPORT:
+		case SADB_X_EXT_NAT_T_OA:
+		case SADB_X_EXT_NAT_T_FRAG:
+#endif
 			/* duplicate check */
 			/*
 			 * XXX Are there duplication payloads of either
Index: netipsec/key.h
===================================================================
--- netipsec/key.h	(.../6.x)	(revision 8180)
+++ netipsec/key.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -99,6 +99,10 @@
 extern void key_sa_recordxfer __P((struct secasvar *, struct mbuf *));
 extern void key_sa_routechange __P((struct sockaddr *));
 extern void key_sa_stir_iv __P((struct secasvar *));
+#ifdef IPSEC_NAT_T
+u_int16_t key_portfromsaddr __P((struct sockaddr *));
+#define KEY_PORTFROMSADDR(saddr) key_portfromsaddr((struct sockaddr *)(saddr))
+#endif
 
 #ifdef MALLOC_DECLARE
 MALLOC_DECLARE(M_IPSEC_SA);
Index: sys/mbuf.h
===================================================================
--- sys/mbuf.h	(.../6.x)	(revision 8180)
+++ sys/mbuf.h	(.../6.x-FAST_IPSEC-NATT)	(revision 8180)
@@ -778,6 +778,7 @@
 #define	PACKET_TAG_PF_TRANSLATE_LOCALHOST	26 /* PF translate localhost */
 #define	PACKET_TAG_IPOPTIONS			27 /* Saved IP options */
 #define	PACKET_TAG_CARP                         28 /* CARP info */
+#define	PACKET_TAG_IPSEC_NAT_T_PORTS		29 /* two uint16_t */
 
 /* Packet tag routines. */
 struct	m_tag	*m_tag_alloc(u_int32_t, int, int, int);

--NzB8fVQJ5HfG6fxh--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20060914093034.A83805>