Index: conf/options
===================================================================
RCS file: /home/ncvs/src/sys/conf/options,v
retrieving revision 1.608
diff -u -p -r1.608 options
--- conf/options	23 Sep 2007 07:34:23 -0000	1.608
+++ conf/options	22 Oct 2007 08:47:43 -0000
@@ -361,6 +361,7 @@ MROUTING		opt_mrouting.h
 INET			opt_inet.h
 INET6			opt_inet6.h
 IPSEC			opt_ipsec.h
+IPSEC_NAT_T		opt_ipsec.h
 IPSEC_DEBUG		opt_ipsec.h
 IPSEC_FILTERTUNNEL	opt_ipsec.h
 IPDIVERT
Index: net/pfkeyv2.h
===================================================================
RCS file: /home/ncvs/src/sys/net/pfkeyv2.h,v
retrieving revision 1.16
diff -u -p -r1.16 pfkeyv2.h
--- net/pfkeyv2.h	3 Jul 2007 12:13:43 -0000	1.16
+++ net/pfkeyv2.h	22 Oct 2007 08:48:42 -0000
@@ -75,7 +75,8 @@ you leave this credit intact on any copi
 #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 @@ struct sadb_x_ipsecrequest {
    */
 };
 
+/* 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 @@ struct sadb_x_ipsecrequest {
 #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: netinet/in_pcb.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_pcb.h,v
retrieving revision 1.100
diff -u -p -r1.100 in_pcb.h
--- netinet/in_pcb.h	6 Aug 2007 14:26:01 -0000	1.100
+++ netinet/in_pcb.h	22 Oct 2007 08:48:45 -0000
@@ -315,6 +315,11 @@ struct inpcbinfo {
 #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/in_proto.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/in_proto.c,v
retrieving revision 1.87
diff -u -p -r1.87 in_proto.c
--- netinet/in_proto.c	7 Oct 2007 20:44:22 -0000	1.87
+++ netinet/in_proto.c	22 Oct 2007 08:48:49 -0000
@@ -118,7 +118,7 @@ struct protosw inetsw[] = {
 	.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/ip_output.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.276
diff -u -p -r1.276 ip_output.c
--- netinet/ip_output.c	7 Oct 2007 20:44:23 -0000	1.276
+++ netinet/ip_output.c	22 Oct 2007 08:48:54 -0000
@@ -66,6 +66,10 @@ __FBSDID("$FreeBSD: src/sys/netinet/ip_o
 #include <netipsec/ipsec.h>
 #endif /* IPSEC*/
 
+#ifdef IPSEC_NAT_T
+#include <netinet/udp.h>
+#endif
+
 #include <machine/in_cksum.h>
 
 #include <security/mac/mac_framework.h>
Index: netinet/udp.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/udp.h,v
retrieving revision 1.10
diff -u -p -r1.10 udp.h
--- netinet/udp.h	20 Feb 2007 10:13:11 -0000	1.10
+++ netinet/udp.h	22 Oct 2007 08:48:54 -0000
@@ -45,4 +45,17 @@ struct udphdr {
 	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/udp_usrreq.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/udp_usrreq.c,v
retrieving revision 1.218
diff -u -p -r1.218 udp_usrreq.c
--- netinet/udp_usrreq.c	7 Oct 2007 20:44:24 -0000	1.218
+++ netinet/udp_usrreq.c	22 Oct 2007 08:49:01 -0000
@@ -82,6 +82,7 @@ __FBSDID("$FreeBSD: src/sys/netinet/udp_
 
 #ifdef IPSEC
 #include <netipsec/ipsec.h>
+#include <netipsec/esp.h>
 #endif
 
 #include <machine/in_cksum.h>
@@ -141,6 +142,11 @@ SYSCTL_STRUCT(_net_inet_udp, UDPCTL_STAT
 static void	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)
@@ -204,6 +210,41 @@ udp_append(struct inpcb *inp, struct ip 
 		ipsec4stat.in_polvio++;
 		return;
 	}
+#ifdef IPSEC_NAT_T
+	/* Handle ESP over UDP */
+	if (inp->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(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, inp->inp_socket) != 0) {
+			m_freem(n);
+			return;
+		}
+		
+		/* Normal UDP processing will take place */
+	}
+#endif
 #endif /* IPSEC */
 #ifdef MAC
 	if (mac_check_inpcb_deliver(inp, n) != 0) {
@@ -756,6 +797,80 @@ SYSCTL_PROC(_net_inet_udp, OID_AUTO, get
     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;
+
+	error = 0;
+
+	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(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr,
     struct mbuf *control, struct thread *td)
@@ -982,6 +1097,158 @@ release:
 	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;
+
+	/*
+	 * 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;
+	}
+
+	iphdrlen = off - sizeof(struct udphdr);
+
+	if ((n = m_pullup(n, iphdrlen + skip)) == NULL) {
+		printf("udp4_espinudp: m_pullup failed\n");
+		return 0;
+	}
+
+	/*
+	 * 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->
+	 */
+	ovbcopy(mtod(n, caddr_t), mtod(n, caddr_t) + skip, iphdrlen);
+	m_adj(n, skip);
+
+	ip = mtod(n, struct ip *);
+	ip->ip_len -= skip;
+	ip->ip_p = IPPROTO_ESP;
+
+	/*
+	 * Clear the flags in mbuf because they are no longer valid
+	 * in case the NIC supports UDP checksum offloading.
+	 */
+	if (n->m_pkthdr.csum_flags & CSUM_DATA_VALID)
+		n->m_pkthdr.csum_flags &= ~(CSUM_DATA_VALID|CSUM_PSEUDO_HDR);
+
+	/*
+	 * 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 IPSEC
+	ipsec4_common_input(n, iphdrlen, ip->ip_p);
+#endif
+
+	/* We handled it, it shoudln't be handled by UDP */
+	return 1;
+}
+#endif
+#endif
+
 static void
 udp_abort(struct socket *so)
 {
Index: netinet/udp_var.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/udp_var.h,v
retrieving revision 1.33
diff -u -p -r1.33 udp_var.h
--- netinet/udp_var.h	10 Jul 2007 09:30:46 -0000	1.33
+++ netinet/udp_var.h	22 Oct 2007 08:49:03 -0000
@@ -103,6 +103,7 @@ extern int			udp_blackhole;
 extern int			udp_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);
 struct inpcb	*udp_notify(struct inpcb *inp, int errno);
Index: netipsec/ipsec.c
===================================================================
RCS file: /home/ncvs/src/sys/netipsec/ipsec.c,v
retrieving revision 1.24
diff -u -p -r1.24 ipsec.c
--- netipsec/ipsec.c	1 Jul 2007 11:38:29 -0000	1.24
+++ netipsec/ipsec.c	22 Oct 2007 08:49:13 -0000
@@ -1842,16 +1842,16 @@ vshiftl(bitmap, nbit, wsize)
 
 /* 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;
 
 	/* XXX-BZ returns static buffer. */
 	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]);
 }
 
@@ -1865,7 +1865,7 @@ ipsec_address(union sockaddr_union* sa)
 	switch (sa->sa.sa_family) {
 #ifdef INET
 	case AF_INET:
-		return inet_ntoa4(sa->sin.sin_addr);
+		return inet_ntoa4(&sa->sin);
 #endif /* INET */
 
 #ifdef INET6
Index: netipsec/ipsec_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netipsec/ipsec_input.c,v
retrieving revision 1.19
diff -u -p -r1.19 ipsec_input.c
--- netipsec/ipsec_input.c	12 Sep 2007 05:54:53 -0000	1.19
+++ netipsec/ipsec_input.c	22 Oct 2007 08:49:14 -0000
@@ -111,6 +111,9 @@ ipsec_common_input(struct mbuf *m, int s
 	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);
@@ -165,6 +168,13 @@ ipsec_common_input(struct mbuf *m, int s
 		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
@@ -184,7 +194,7 @@ ipsec_common_input(struct mbuf *m, int s
 	}
 
 	/* 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
===================================================================
RCS file: /home/ncvs/src/sys/netipsec/ipsec_output.c,v
retrieving revision 1.16
diff -u -p -r1.16 ipsec_output.c
--- netipsec/ipsec_output.c	19 Jul 2007 09:57:54 -0000	1.16
+++ netipsec/ipsec_output.c	22 Oct 2007 08:49:19 -0000
@@ -82,6 +82,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)
 {
@@ -174,6 +178,51 @@ ipsec_process_done(struct mbuf *m, struc
 		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
===================================================================
RCS file: /home/ncvs/src/sys/netipsec/key.c,v
retrieving revision 1.28
diff -u -p -r1.28 key.c
--- netipsec/key.c	1 Jul 2007 11:38:29 -0000	1.28
+++ netipsec/key.c	22 Oct 2007 08:49:44 -0000
@@ -210,6 +210,11 @@ static const int minsize[] = {
 	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 @@ static const int maxsize[] = {
 	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 @@ static int key_spdflush __P((struct sock
 	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_setsadbmsg __P((
 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));
@@ -1049,12 +1070,20 @@ key_allocsa(
 	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
@@ -1086,11 +1115,11 @@ key_allocsa(
 					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;
@@ -2352,6 +2381,71 @@ key_spdflush(so, m, mhp)
 	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
@@ -2985,6 +3079,10 @@ key_setsaval(sav, m, mhp)
 	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 */
@@ -3299,6 +3397,11 @@ key_setdumpsa(sav, type, satype, seq, pi
 		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);
@@ -3383,6 +3486,31 @@ key_setdumpsa(sav, type, satype, seq, pi
 				goto fail;
 			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:
@@ -3587,6 +3715,133 @@ key_setsadbxsa2(mode, seq, reqid)
 	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
  */
@@ -3779,6 +4034,8 @@ key_cmpsaidx(
 	const struct secasindex *saidx1,
 	int flag)
 {
+	int chkport = 0;
+
 	/* sanity */
 	if (saidx0 == NULL && saidx1 == NULL)
 		return 1;
@@ -3802,6 +4059,19 @@ key_cmpsaidx(
 		/* 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.
@@ -3809,6 +4079,10 @@ key_cmpsaidx(
 			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
@@ -3816,10 +4090,10 @@ key_cmpsaidx(
 				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;
 		}
 	}
@@ -4440,13 +4714,17 @@ key_getspi(so, m, mhp)
 		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:
 		; /*???*/
@@ -4456,13 +4734,17 @@ key_getspi(so, m, mhp)
 		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:
 		; /*???*/
@@ -4471,6 +4753,12 @@ key_getspi(so, m, mhp)
 	/* 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);
@@ -4724,6 +5012,12 @@ key_update(so, m, mhp)
 	/* 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__));
@@ -4790,6 +5084,68 @@ key_update(so, m, mhp)
 		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;
 
@@ -4922,6 +5278,11 @@ key_add(so, m, mhp)
 	/* 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 */
@@ -4958,6 +5319,68 @@ key_add(so, m, mhp)
 		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.
@@ -5161,6 +5584,11 @@ key_delete(so, m, mhp)
 	/* 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) {
@@ -5230,6 +5658,11 @@ key_delete_all(so, m, mhp, proto)
 	/* 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)
@@ -5344,6 +5777,11 @@ key_get(so, m, mhp)
 	/* 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) {
@@ -6031,6 +6469,11 @@ key_acquire2(so, m, mhp)
 	/* 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) {
@@ -6639,6 +7082,11 @@ static int (*key_typesw[]) __P((struct s
 	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
 };
 
 /*
@@ -6975,6 +7423,13 @@ key_align(m, mhp)
 		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
===================================================================
RCS file: /home/ncvs/src/sys/netipsec/key.h,v
retrieving revision 1.4
diff -u -p -r1.4 key.h
--- netipsec/key.h	7 Jan 2005 01:45:46 -0000	1.4
+++ netipsec/key.h	22 Oct 2007 08:49:44 -0000
@@ -99,6 +99,10 @@ extern void key_init __P((void));
 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: netipsec/keydb.h
===================================================================
RCS file: /home/ncvs/src/sys/netipsec/keydb.h,v
retrieving revision 1.6
diff -u -p -r1.6 keydb.h
--- netipsec/keydb.h	25 Mar 2006 13:38:52 -0000	1.6
+++ netipsec/keydb.h	22 Oct 2007 08:49:44 -0000
@@ -142,6 +142,12 @@ struct secasvar {
 	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: sys/mbuf.h
===================================================================
RCS file: /home/ncvs/src/sys/sys/mbuf.h,v
retrieving revision 1.217
diff -u -p -r1.217 mbuf.h
--- sys/mbuf.h	6 Oct 2007 21:42:39 -0000	1.217
+++ sys/mbuf.h	22 Oct 2007 08:49:52 -0000
@@ -848,6 +848,7 @@ struct mbuf	*m_unshare(struct mbuf *, in
 #define	PACKET_TAG_RTSOCKFAM			25 /* rtsock sa family */
 #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 */
 
 /* Specific cookies and tags. */
 
