Index: conf/files
===================================================================
RCS file: /home/cvs/src/sys/conf/files,v
retrieving revision 1.512
diff -u -p -r1.512 files
--- conf/files	7 Apr 2011 13:42:53 -0000	1.512
+++ conf/files	14 Apr 2011 09:19:03 -0000
@@ -851,6 +851,7 @@ file netinet/igmp.c			inet
 file netinet/in.c			inet
 file netinet/in_pcb.c			inet
 file netinet/in_proto.c			inet
+file netinet/inet_nat64.c		inet & pf
 file netinet/ip_divert.c		inet & pf
 file netinet/ip_icmp.c			inet
 file netinet/ip_id.c			inet
Index: net/if_pflog.c
===================================================================
RCS file: /home/cvs/src/sys/net/if_pflog.c,v
retrieving revision 1.33
diff -u -p -r1.33 if_pflog.c
--- net/if_pflog.c	7 Dec 2010 11:39:40 -0000	1.33
+++ net/if_pflog.c	11 May 2011 10:07:51 -0000
@@ -63,6 +63,7 @@
 #ifndef INET
 #include <netinet/in.h>
 #endif
+#include <netinet/ip6.h>
 #include <netinet6/nd6.h>
 #include <netinet/icmp6.h>
 #endif /* INET6 */
@@ -91,7 +92,7 @@ struct if_clone	pflog_cloner =
     IF_CLONE_INITIALIZER("pflog", pflog_clone_create, pflog_clone_destroy);
 
 struct ifnet	*pflogifs[PFLOGIFS_MAX];	/* for fast access */
-struct mbuf	*mfake = NULL;
+struct mbuf	*pflog_mhdr = NULL, *pflog_mptr = NULL;
 
 void
 pflogattach(int npflog)
@@ -100,8 +101,12 @@ pflogattach(int npflog)
 	LIST_INIT(&pflogif_list);
 	for (i = 0; i < PFLOGIFS_MAX; i++)
 		pflogifs[i] = NULL;
-	if (mfake == NULL)
-		mfake = m_get(M_DONTWAIT, MT_HEADER);
+	if (pflog_mhdr == NULL)
+		if ((pflog_mhdr = m_get(M_DONTWAIT, MT_HEADER)) == NULL)
+			panic("pflogattach: no mbuf");
+	if (pflog_mptr == NULL)
+		if ((pflog_mptr = m_get(M_DONTWAIT, MT_DATA)) == NULL)
+			panic("pflogattach: no mbuf");
 	if_clone_attach(&pflog_cloner);
 }
 
@@ -226,7 +231,6 @@ pflog_packet(struct pfi_kif *kif, struct
 
 	bzero(&hdr, sizeof(hdr));
 	hdr.length = PFLOG_REAL_HDRLEN;
-	hdr.af = af;
 	hdr.action = rm->action;
 	hdr.reason = reason;
 	memcpy(hdr.ifname, kif->pfik_name, sizeof(hdr.ifname));
@@ -254,8 +258,10 @@ pflog_packet(struct pfi_kif *kif, struct
 	hdr.rule_pid = rm->cpid;
 	hdr.dir = dir;
 
-	PF_ACPY(&hdr.saddr, &pd->nsaddr, pd->af);
-	PF_ACPY(&hdr.daddr, &pd->ndaddr, pd->af);
+	PF_ACPY(&hdr.saddr, &pd->nsaddr, pd->naf);
+	PF_ACPY(&hdr.daddr, &pd->ndaddr, pd->naf);
+	hdr.af = pd->af;
+	hdr.naf = pd->naf;
 	hdr.sport = pd->nsport;
 	hdr.dport = pd->ndport;
 
@@ -271,12 +277,12 @@ pflog_packet(struct pfi_kif *kif, struct
 void
 pflog_bpfcopy(const void *src_arg, void *dst_arg, size_t len)
 {
-	const struct mbuf	*m;
+	struct mbuf		*m, *mp, *mhdr, *mptr;
 	struct pfloghdr		*pfloghdr;
 	u_int			 count;
-	u_char			*dst;
+	u_char			*dst, *mdst, *cp;
 	u_short			 action, reason;
-	int			 off = 0, hdrlen = 0;
+	int			 afto, hlen, mlen, off = 0, hdrlen = 0;
 	union {
 		struct tcphdr		tcp;
 		struct udphdr		udp;
@@ -290,14 +296,18 @@ pflog_bpfcopy(const void *src_arg, void 
 	struct pf_addr		 osaddr, odaddr;
 	u_int16_t		 osport = 0, odport = 0;
 
-	m = src_arg;
+	m = (struct mbuf *)src_arg;
 	dst = dst_arg;
 
+	mhdr = pflog_mhdr;
+	mptr = pflog_mptr;
+
 	if (m == NULL)
 		panic("pflog_bpfcopy got no mbuf");
 
 	/* first mbuf holds struct pfloghdr */
 	pfloghdr = mtod(m, struct pfloghdr *);
+	afto = pfloghdr->af != pfloghdr->naf;
 	count = min(m->m_len, len);
 	bcopy(pfloghdr, dst, count);
 	pfloghdr = (struct pfloghdr *)dst;
@@ -305,38 +315,72 @@ pflog_bpfcopy(const void *src_arg, void 
 	len -= count;
 	m = m->m_next;
 
-	/* second mbuf is pkthdr */
-	if (len > 0) {
-		if (m == NULL)
-			panic("no second mbuf");
-		bcopy(m, mfake, sizeof(*mfake));
-		mfake->m_flags &= ~(M_EXT|M_CLUSTER);
-		mfake->m_next = NULL;
-		mfake->m_nextpkt = NULL;
-		mfake->m_data = dst;
-		mfake->m_len = len;
-	} else
+	if (len <= 0)
 		return;
 
-	while (len > 0) {
-		if (m == 0)
-			panic("bpf_mcopy");
-		count = min(m->m_len, len);
-		bcopy(mtod(m, caddr_t), (caddr_t)dst, count);
-		m = m->m_next;
-		dst += count;
-		len -= count;
+	/* second mbuf is pkthdr */
+	if (m == NULL)
+		panic("no second mbuf");
+
+	/* mhdr will hold an ip/ip6 header and 8 bytes of the protocol header */
+	m_inithdr(mhdr);
+	mhdr->m_len = 0;
+	mhdr->m_pkthdr.len = 0;
+
+	/* reserve space for header expansion (ip -> ip6) */
+	if (afto && pfloghdr->af == AF_INET)
+		mhdr->m_data += sizeof(struct ip6_hdr) -
+		    sizeof(struct ip);
+
+	mdst = mtod(mhdr, char *);
+	switch (pfloghdr->af) {
+	case AF_INET: {
+		struct ip	*h;
+
+		m_copydata(m, 0, sizeof(*h), mdst);
+		h = (struct ip *)mdst;
+		hlen = h->ip_hl << 2;
+		if (hlen > sizeof(*h))
+			m_copydata(m, sizeof(*h), hlen - sizeof(*h),
+			    mdst + sizeof(*h));
+		break;
+	    }
+	case AF_INET6: {
+		hlen = sizeof(struct ip6_hdr);
+		m_copydata(m, 0, hlen, mdst);
+		break;
+	    }
+	default:
+		/* shouldn't happen ever :-) */
+		m_copydata(m, 0, len, dst);
+		return;
 	}
 
-	if (mfake->m_flags & M_PKTHDR)
-		mfake->m_pkthdr.len = min(mfake->m_pkthdr.len, mfake->m_len);
+	/* copy 8 bytes of the protocol header */
+	m_copydata(m, hlen, 8, mdst + hlen);
+
+	mhdr->m_len += hlen + 8;
+	mhdr->m_pkthdr.len = mhdr->m_len;
+
+	/* create a chain mhdr -> mptr, mptr->m_data = (m->m_data+hlen+8) */
+	mp = m_getptr(m, hlen + 8, &off);
+	if (mp != NULL) {
+		bcopy(mp, mptr, sizeof(*mptr));
+		cp = mtod(mp, char *);
+		mptr->m_data += off;
+		mptr->m_len -= off;
+		mptr->m_flags &= ~M_PKTHDR;
+		mhdr->m_next = mptr;
+		mhdr->m_pkthdr.len += m->m_pkthdr.len - (hlen + 8);
+	}
 
 	/* rewrite addresses if needed */
 	memset(&pd, 0, sizeof(pd));
 	pd.hdr.any = &pf_hdrs;
-	if (pf_setup_pdesc(pfloghdr->af, pfloghdr->dir, &pd, mfake, &action,
+	if (pf_setup_pdesc(pfloghdr->af, pfloghdr->dir, &pd, mhdr, &action,
 	    &reason, NULL, NULL, NULL, NULL, &off, &hdrlen) == -1)
 		return;
+	pd.naf = pfloghdr->naf;
 
 	PF_ACPY(&osaddr, pd.src, pd.af);
 	PF_ACPY(&odaddr, pd.dst, pd.af);
@@ -348,11 +392,27 @@ pflog_bpfcopy(const void *src_arg, void 
 	if ((pfloghdr->rewritten = pf_translate(&pd, &pfloghdr->saddr,
 	    pfloghdr->sport, &pfloghdr->daddr, pfloghdr->dport, 0,
 	    pfloghdr->dir))) {
-		m_copyback(mfake, off, min(mfake->m_len - off, hdrlen),
-		    pd.hdr.any, M_NOWAIT);
+		m_copyback(mhdr, off, min(mhdr->m_len - off, 8), pd.hdr.any,
+		    M_NOWAIT);
+		if (afto) {
+			PF_ACPY(&pd.nsaddr, &pfloghdr->saddr, pd.naf);
+			PF_ACPY(&pd.ndaddr, &pfloghdr->daddr, pd.naf);
+		}
 		PF_ACPY(&pfloghdr->saddr, &osaddr, pd.af);
 		PF_ACPY(&pfloghdr->daddr, &odaddr, pd.af);
 		pfloghdr->sport = osport;
 		pfloghdr->dport = odport;
 	}
+
+	pd.tot_len = min(pd.tot_len, len);
+	pd.tot_len -= mhdr->m_data - mhdr->m_pktdat;
+
+	if (afto)
+		pf_translate_af(pd.naf, &mhdr, off, &pd);
+
+	mlen = min(mhdr->m_pkthdr.len, len);
+	m_copydata(mhdr, 0, mlen, dst);
+	len -= mlen;
+	if (len > 0)
+		bzero(dst + mlen, len);
 }
Index: net/if_pflog.h
===================================================================
RCS file: /home/cvs/src/sys/net/if_pflog.h,v
retrieving revision 1.17
diff -u -p -r1.17 if_pflog.h
--- net/if_pflog.h	21 Sep 2010 11:05:10 -0000	1.17
+++ net/if_pflog.h	24 Mar 2011 19:59:08 -0000
@@ -54,7 +54,8 @@ struct pfloghdr {
 	pid_t		rule_pid;
 	u_int8_t	dir;
 	u_int8_t	rewritten;
-	u_int8_t	pad[2];
+	sa_family_t	naf;
+	u_int8_t	pad[1];
 	struct pf_addr	saddr;
 	struct pf_addr	daddr;
 	u_int16_t	sport;
Index: net/if_pfsync.c
===================================================================
RCS file: /home/cvs/src/sys/net/if_pfsync.c,v
retrieving revision 1.163
diff -u -p -r1.163 if_pfsync.c
--- net/if_pfsync.c	10 May 2011 01:10:08 -0000	1.163
+++ net/if_pfsync.c	10 May 2011 21:09:41 -0000
@@ -426,11 +426,13 @@ pfsync_state_export(struct pfsync_state 
 	sp->key[PF_SK_WIRE].port[0] = st->key[PF_SK_WIRE]->port[0];
 	sp->key[PF_SK_WIRE].port[1] = st->key[PF_SK_WIRE]->port[1];
 	sp->key[PF_SK_WIRE].rdomain = htons(st->key[PF_SK_WIRE]->rdomain);
+	sp->key[PF_SK_WIRE].af = st->key[PF_SK_WIRE]->af;
 	sp->key[PF_SK_STACK].addr[0] = st->key[PF_SK_STACK]->addr[0];
 	sp->key[PF_SK_STACK].addr[1] = st->key[PF_SK_STACK]->addr[1];
 	sp->key[PF_SK_STACK].port[0] = st->key[PF_SK_STACK]->port[0];
 	sp->key[PF_SK_STACK].port[1] = st->key[PF_SK_STACK]->port[1];
 	sp->key[PF_SK_STACK].rdomain = htons(st->key[PF_SK_STACK]->rdomain);
+	sp->key[PF_SK_STACK].af = st->key[PF_SK_STACK]->af;
 	sp->rtableid[PF_SK_WIRE] = htonl(st->rtableid[PF_SK_WIRE]);
 	sp->rtableid[PF_SK_STACK] = htonl(st->rtableid[PF_SK_STACK]);
 	sp->proto = st->key[PF_SK_WIRE]->proto;
@@ -527,7 +529,9 @@ pfsync_state_import(struct pfsync_state 
 	if ((skw = pf_alloc_state_key(pool_flags)) == NULL)
 		goto cleanup;
 
-	if (PF_ANEQ(&sp->key[PF_SK_WIRE].addr[0],
+	if ((sp->key[PF_SK_WIRE].af &&
+	    (sp->key[PF_SK_WIRE].af != sp->key[PF_SK_STACK].af)) ||
+	    PF_ANEQ(&sp->key[PF_SK_WIRE].addr[0],
 	    &sp->key[PF_SK_STACK].addr[0], sp->af) ||
 	    PF_ANEQ(&sp->key[PF_SK_WIRE].addr[1],
 	    &sp->key[PF_SK_STACK].addr[1], sp->af) ||
@@ -551,7 +555,8 @@ pfsync_state_import(struct pfsync_state 
 	skw->port[1] = sp->key[PF_SK_WIRE].port[1];
 	skw->rdomain = ntohs(sp->key[PF_SK_WIRE].rdomain);
 	skw->proto = sp->proto;
-	skw->af = sp->af;
+	if (!(skw->af = sp->key[PF_SK_WIRE].af))
+		skw->af = sp->af;
 	if (sks != skw) {
 		sks->addr[0] = sp->key[PF_SK_STACK].addr[0];
 		sks->addr[1] = sp->key[PF_SK_STACK].addr[1];
@@ -559,7 +564,8 @@ pfsync_state_import(struct pfsync_state 
 		sks->port[1] = sp->key[PF_SK_STACK].port[1];
 		sks->rdomain = ntohs(sp->key[PF_SK_STACK].rdomain);
 		sks->proto = sp->proto;
-		sks->af = sp->af;
+		if (!(sks->af = sp->key[PF_SK_STACK].af))
+			sks->af = sp->af;
 	}
 	st->rtableid[PF_SK_WIRE] = ntohl(sp->rtableid[PF_SK_WIRE]);
 	st->rtableid[PF_SK_STACK] = ntohl(sp->rtableid[PF_SK_STACK]);
@@ -787,17 +793,23 @@ int
 pfsync_in_ins(caddr_t buf, int len, int count, int flags)
 {
 	struct pfsync_state *sp;
+	sa_family_t af1, af2;
 	int i;
 
 	for (i = 0; i < count; i++) {
 		sp = (struct pfsync_state *)(buf + len * i);
+		af1 = sp->key[0].af;
+		af2 = sp->key[1].af;
 
 		/* check for invalid values */
 		if (sp->timeout >= PFTM_MAX ||
 		    sp->src.state > PF_TCPS_PROXY_DST ||
 		    sp->dst.state > PF_TCPS_PROXY_DST ||
 		    sp->direction > PF_OUT ||
-		    (sp->af != AF_INET && sp->af != AF_INET6)) {
+		    (((af1 || af2) &&
+		     ((af1 != AF_INET && af1 != AF_INET6) ||
+		      (af2 != AF_INET && af2 != AF_INET6))) ||
+		    (sp->af != AF_INET && sp->af != AF_INET6))) {
 			DPFPRINTF(LOG_NOTICE,
 			    "pfsync_input: PFSYNC5_ACT_INS: invalid value");
 			pfsyncstats.pfsyncs_badval++;
Index: net/pf.c
===================================================================
RCS file: /home/cvs/src/sys/net/pf.c,v
retrieving revision 1.742
diff -u -p -r1.742 pf.c
--- net/pf.c	24 Apr 2011 19:36:54 -0000	1.742
+++ net/pf.c	11 May 2011 12:13:57 -0000
@@ -146,7 +146,7 @@ int			 pf_check_threshold(struct pf_thre
 
 void			 pf_change_ap(struct pf_addr *, u_int16_t *,
 			    u_int16_t *, struct pf_addr *, u_int16_t,
-			    u_int8_t, sa_family_t);
+			    u_int8_t, sa_family_t, sa_family_t);
 int			 pf_modulate_sack(struct mbuf *, int, struct pf_pdesc *,
 			    struct tcphdr *, struct pf_state_peer *);
 #ifdef INET6
@@ -159,6 +159,11 @@ void			 pf_change_icmp(struct pf_addr *,
 			    struct pf_addr *, struct pf_addr *, u_int16_t,
 			    u_int16_t *, u_int16_t *, u_int16_t *,
 			    u_int8_t, sa_family_t);
+int			 pf_change_icmp_af(struct mbuf *, int, int *,
+			    struct pf_pdesc *, struct pf_pdesc *,
+			    struct pf_addr *, struct pf_addr *, sa_family_t,
+			    sa_family_t);
+int			 pf_translate_icmp_af(int, void *);
 void			 pf_send_tcp(const struct pf_rule *, sa_family_t,
 			    const struct pf_addr *, const struct pf_addr *,
 			    u_int16_t, u_int16_t, u_int32_t, u_int32_t,
@@ -683,7 +688,11 @@ pf_state_key_attach(struct pf_state_key 
 		/* key exists. check for same kif, if none, add to key */
 		TAILQ_FOREACH(si, &cur->states, entry)
 			if (si->s->kif == s->kif &&
-			    si->s->direction == s->direction) {
+			    ((si->s->key[0]->af == sk->af &&
+			     si->s->direction == s->direction) ||
+			    (si->s->key[0]->af != si->s->key[1]->af &&
+			     sk->af == si->s->key[1]->af &&
+			     si->s->direction != s->direction))) {
 				if (sk->proto == IPPROTO_TCP &&
 				    si->s->src.state >= TCPS_FIN_WAIT_2 &&
 				    si->s->dst.state >= TCPS_FIN_WAIT_2) {
@@ -813,7 +822,8 @@ pf_state_key_setup(struct pf_pdesc *pd, 
 	if (rtableid >= 0)
 		wrdom = rtable_l2(rtableid);
 
-	if (PF_ANEQ(&pd->nsaddr, pd->src, pd->af) ||
+	if (pd->af != pd->naf ||
+	    PF_ANEQ(&pd->nsaddr, pd->src, pd->af) ||
 	    PF_ANEQ(&pd->ndaddr, pd->dst, pd->af) ||
 	    pd->nsport != pd->osport || pd->ndport != pd->odport ||
 	    wrdom != pd->rdomain) {	/* NAT */
@@ -821,12 +831,26 @@ pf_state_key_setup(struct pf_pdesc *pd, 
 			pool_put(&pf_state_key_pl, sk1);
 			return (ENOMEM);
 		}
-		PF_ACPY(&sk2->addr[pd->sidx], &pd->nsaddr, pd->af);
-		PF_ACPY(&sk2->addr[pd->didx], &pd->ndaddr, pd->af);
-		sk2->port[pd->sidx] = pd->nsport;
-		sk2->port[pd->didx] = pd->ndport;
-		sk2->proto = pd->proto;
-		sk2->af = pd->af;
+		PF_ACPY(&sk2->addr[pd->af == pd->naf ? pd->sidx : pd->didx],
+		    &pd->nsaddr, pd->naf);
+		PF_ACPY(&sk2->addr[pd->af == pd->naf ? pd->didx : pd->sidx],
+		    &pd->ndaddr, pd->naf);
+		sk2->port[pd->af == pd->naf ? pd->sidx : pd->didx] = pd->nsport;
+		sk2->port[pd->af == pd->naf ? pd->didx : pd->sidx] = pd->ndport;
+		if (pd->af != pd->naf) {
+			switch (pd->proto) {
+			case IPPROTO_ICMP:
+				sk2->proto = IPPROTO_ICMPV6;
+				break;
+			case IPPROTO_ICMPV6:
+				sk2->proto = IPPROTO_ICMP;
+				break;
+			default:
+				sk2->proto = pd->proto;
+			}
+		} else
+			sk2->proto = pd->proto;
+		sk2->af = pd->naf;
 		sk2->rdomain = wrdom;
 	} else
 		sk2 = sk1;
@@ -980,8 +1004,12 @@ pf_find_state(struct pfi_kif *kif, struc
 	/* list is sorted, if-bound states before floating ones */
 	TAILQ_FOREACH(si, &sk->states, entry)
 		if ((si->s->kif == pfi_all || si->s->kif == kif) &&
-		    sk == (dir == PF_IN ? si->s->key[PF_SK_WIRE] :
-		    si->s->key[PF_SK_STACK]))
+		    ((si->s->key[PF_SK_WIRE]->af == si->s->key[PF_SK_STACK]->af
+		    && sk == (dir == PF_IN ? si->s->key[PF_SK_WIRE] :
+		    si->s->key[PF_SK_STACK])) ||
+		    (si->s->key[PF_SK_WIRE]->af != si->s->key[PF_SK_STACK]->af
+		    && dir == PF_IN && (sk == si->s->key[PF_SK_STACK] ||
+		    sk == si->s->key[PF_SK_WIRE]))))
 			return (si->s);
 
 	return (NULL);
@@ -1535,44 +1563,86 @@ pf_cksum_fixup(u_int16_t cksum, u_int16_
 
 void
 pf_change_ap(struct pf_addr *a, u_int16_t *p, u_int16_t *pc,
-    struct pf_addr *an, u_int16_t pn, u_int8_t u, sa_family_t af)
+    struct pf_addr *an, u_int16_t pn, u_int8_t u, sa_family_t af,
+    sa_family_t naf)
 {
 	struct pf_addr	ao;
 	u_int16_t	po = *p;
 
 	PF_ACPY(&ao, a, af);
-	PF_ACPY(a, an, af);
+	if (af == naf)
+		PF_ACPY(a, an, naf);
+
 	*p = pn;
 
 	switch (af) {
 #ifdef INET
 	case AF_INET:
-		*pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc,
-		    ao.addr16[0], an->addr16[0], u),
-		    ao.addr16[1], an->addr16[1], u),
-		    po, pn, u);
+		switch (naf) {
+		case AF_INET:
+			*pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc,
+			    ao.addr16[0], an->addr16[0], u),
+			    ao.addr16[1], an->addr16[1], u),
+			    po, pn, u);
+			break;
+#ifdef INET6
+		case AF_INET6:
+			*pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(
+			    pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(
+			    pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc,
+			    ao.addr16[0], an->addr16[0], u),
+			    ao.addr16[1], an->addr16[1], u),
+			    0,            an->addr16[2], u),
+			    0,            an->addr16[3], u),
+			    0,            an->addr16[4], u),
+			    0,            an->addr16[5], u),
+			    0,            an->addr16[6], u),
+			    0,            an->addr16[7], u),
+			    po, pn, u);
+			break;
+#endif /* INET6 */
+		}
 		break;
 #endif /* INET */
 #ifdef INET6
 	case AF_INET6:
-		*pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(
-		    pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(
-		    pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc,
-		    ao.addr16[0], an->addr16[0], u),
-		    ao.addr16[1], an->addr16[1], u),
-		    ao.addr16[2], an->addr16[2], u),
-		    ao.addr16[3], an->addr16[3], u),
-		    ao.addr16[4], an->addr16[4], u),
-		    ao.addr16[5], an->addr16[5], u),
-		    ao.addr16[6], an->addr16[6], u),
-		    ao.addr16[7], an->addr16[7], u),
-		    po, pn, u);
+		switch (naf) {
+#ifdef INET
+		case AF_INET:
+			*pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(
+			    pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(
+			    pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc,
+			    ao.addr16[0], an->addr16[0], u),
+			    ao.addr16[1], an->addr16[1], u),
+			    ao.addr16[2], 0,             u),
+			    ao.addr16[3], 0,             u),
+			    ao.addr16[4], 0,             u),
+			    ao.addr16[5], 0,             u),
+			    ao.addr16[6], 0,             u),
+			    ao.addr16[7], 0,             u),
+			    po, pn, u);
+			break;
+#endif /* INET */
+		case AF_INET6:
+			*pc = pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(
+			    pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(
+			    pf_cksum_fixup(pf_cksum_fixup(pf_cksum_fixup(*pc,
+			    ao.addr16[0], an->addr16[0], u),
+			    ao.addr16[1], an->addr16[1], u),
+			    ao.addr16[2], an->addr16[2], u),
+			    ao.addr16[3], an->addr16[3], u),
+			    ao.addr16[4], an->addr16[4], u),
+			    ao.addr16[5], an->addr16[5], u),
+			    ao.addr16[6], an->addr16[6], u),
+			    ao.addr16[7], an->addr16[7], u),
+			    po, pn, u);
+			break;
+		}
 		break;
 #endif /* INET6 */
 	}
 }
 
-
 /* Changes a u_int32_t.  Uses a void * so there are no align restrictions */
 void
 pf_change_a(void *a, u_int16_t *c, u_int32_t an, u_int8_t u)
@@ -1874,6 +1944,390 @@ pf_change_icmp(struct pf_addr *ia, u_int
 	}
 }
 
+int
+pf_translate_af(int af, struct mbuf **m0, int off, struct pf_pdesc *pd)
+{
+#if INET && INET6
+	struct mbuf		*m = *m0, *mp = NULL;
+	struct ip		*ip4;
+	struct ip6_hdr		*ip6;
+	struct icmp6_hdr	*icmp;
+	int			 hlen;
+
+	hlen = af == AF_INET ? sizeof(*ip4) : sizeof(*ip6);
+
+	/* trim the old header */
+	m_adj(m, off);
+
+	/* prepend a new one */
+	if ((M_PREPEND(m, hlen, M_DONTWAIT)) == NULL) {
+		*m0 = NULL;
+		return (-1);
+	}
+
+	switch (af) {
+	case AF_INET:
+		ip4 = mtod(m, struct ip *);
+		bzero(ip4, hlen);
+		ip4->ip_v   = IPVERSION;
+		ip4->ip_hl  = hlen >> 2;
+		ip4->ip_len = htons(hlen + (pd->tot_len - off));
+		ip4->ip_id  = htons(ip_randomid());
+		ip4->ip_off = htons(IP_DF);
+		ip4->ip_ttl = pd->ttl;
+		ip4->ip_p   = pd->proto;
+		ip4->ip_src = pd->nsaddr.v4;
+		ip4->ip_dst = pd->ndaddr.v4;
+		break;
+	case AF_INET6:
+		ip6 = mtod(m, struct ip6_hdr *);
+		bzero(ip6, hlen);
+		ip6->ip6_vfc  = IPV6_VERSION;
+		ip6->ip6_plen = htons(pd->tot_len - off);
+		ip6->ip6_nxt  = pd->proto;
+		if (!pd->ttl || pd->ttl > IPV6_DEFHLIM)
+			ip6->ip6_hlim = IPV6_DEFHLIM;
+		else
+			ip6->ip6_hlim = pd->ttl;
+		ip6->ip6_src  = pd->nsaddr.v6;
+		ip6->ip6_dst  = pd->ndaddr.v6;
+		break;
+	default:
+		return (-1);
+	}
+
+	/* recalculate icmp/icmp6 checksums */
+	if (pd->proto == IPPROTO_ICMP || pd->proto == IPPROTO_ICMPV6) {
+		if ((mp = m_pulldown(m, hlen, sizeof(*icmp), &off)) == NULL) {
+			*m0 = NULL;
+			return (-1);
+		}
+		icmp = (struct icmp6_hdr *)(mp->m_data + off);
+		icmp->icmp6_cksum = 0;
+		icmp->icmp6_cksum = af == AF_INET ?
+		    in4_cksum(m, 0, hlen, ntohs(ip4->ip_len) - hlen) :
+		    in6_cksum(m, IPPROTO_ICMPV6, hlen, ntohs(ip6->ip6_plen));
+	}
+
+	*m0 = m;
+#endif /* INET && INET6 */
+
+	return (0);
+}
+
+int
+pf_change_icmp_af(struct mbuf *m, int off, int *poff,  struct pf_pdesc *pd,
+    struct pf_pdesc *pd2, struct pf_addr *src, struct pf_addr *dst,
+    sa_family_t af, sa_family_t naf)
+{
+#if INET && INET6
+	struct mbuf		*n = NULL;
+	struct ip		*ip4;
+	struct ip6_hdr		*ip6;
+	int			 hlen, olen, mlen;
+
+	if (af == naf || (af != AF_INET && af != AF_INET6) ||
+	    (naf != AF_INET && naf != AF_INET6))
+		return (-1);
+
+	/* split the mbuf chain on the inner ip/ip6 header boundary */
+	if ((n = m_split(m, off, M_DONTWAIT)) == NULL)
+		return (-1);
+
+	/* old header */
+	olen = *poff - off;
+	/* new header */
+	hlen = naf == AF_INET ? sizeof(*ip4) : sizeof(*ip6);
+	/* data lenght */
+	mlen = m->m_pkthdr.len - *poff;
+
+	/* trim old header */
+	m_adj(n, olen);
+
+	/* prepend a new one */
+	if ((M_PREPEND(n, hlen, M_DONTWAIT)) == NULL)
+		return (-1);
+
+	/* translate inner ip/ip6 header */
+	switch (naf) {
+	case AF_INET:
+		ip4 = mtod(n, struct ip *);
+		bzero(ip4, sizeof(*ip4));
+		ip4->ip_v   = IPVERSION;
+		ip4->ip_hl  = sizeof(*ip4) >> 2;
+		ip4->ip_len = htons(sizeof(*ip4) + mlen);
+		ip4->ip_id  = htons(ip_randomid());
+		ip4->ip_off = htons(IP_DF);
+		ip4->ip_ttl = pd2->ttl;
+		if (pd2->proto == IPPROTO_ICMPV6)
+			ip4->ip_p = IPPROTO_ICMP;
+		else
+			ip4->ip_p = pd2->proto;
+		ip4->ip_src = src->v4;
+		ip4->ip_dst = dst->v4;
+		ip4->ip_sum = in_cksum(n, ip4->ip_hl << 2);
+		break;
+	case AF_INET6:
+		ip6 = mtod(n, struct ip6_hdr *);
+		bzero(ip6, sizeof(*ip6));
+		ip6->ip6_vfc  = IPV6_VERSION;
+		ip6->ip6_plen = htons(mlen);
+		if (pd2->proto == IPPROTO_ICMP)
+			ip6->ip6_nxt = IPPROTO_ICMPV6;
+		else
+			ip6->ip6_nxt = pd2->proto;
+		if (!pd2->ttl || pd2->ttl > IPV6_DEFHLIM)
+			ip6->ip6_hlim = IPV6_DEFHLIM;
+		else
+			ip6->ip6_hlim = pd2->ttl;
+		ip6->ip6_src  = src->v6;
+		ip6->ip6_dst  = dst->v6;
+		break;
+	}
+
+	/* adjust payload offset and total packet length */
+	*poff += hlen - olen;
+	pd->tot_len += hlen - olen;
+
+	/* merge modified inner packet with the original header */
+	mlen = n->m_pkthdr.len;
+	m_cat(m, n);
+	m->m_pkthdr.len += mlen;
+#endif /* INET && INET6 */
+
+	return (0);
+}
+
+
+#define PTR_IP(field)	(offsetof(struct ip, field))
+#define PTR_IP6(field)	(offsetof(struct ip6_hdr, field))
+
+int
+pf_translate_icmp_af(int af, void *arg)
+{
+#if INET && INET6
+	struct icmp		*icmp4;
+	struct icmp6_hdr	*icmp6;
+	u_int32_t		 mtu;
+	int32_t			 ptr = -1;
+	u_int8_t		 type;
+	u_int8_t		 code;
+
+	switch (af) {
+	case AF_INET:
+		icmp6 = arg;
+		type  = icmp6->icmp6_type;
+		code  = icmp6->icmp6_code;
+		mtu   = ntohl(icmp6->icmp6_mtu);
+
+		switch (type) {
+		case ICMP6_ECHO_REQUEST:
+			type = ICMP_ECHO;
+			break;
+		case ICMP6_ECHO_REPLY:
+			type = ICMP_ECHOREPLY;
+			break;
+		case ICMP6_DST_UNREACH:
+			type = ICMP_UNREACH;
+			switch (code) {
+			case ICMP6_DST_UNREACH_NOROUTE:
+			case ICMP6_DST_UNREACH_BEYONDSCOPE:
+			case ICMP6_DST_UNREACH_ADDR:
+				code = ICMP_UNREACH_HOST;
+				break;
+			case ICMP6_DST_UNREACH_ADMIN:
+				code = ICMP_UNREACH_HOST_PROHIB;
+				break;
+			case ICMP6_DST_UNREACH_NOPORT:
+				code = ICMP_UNREACH_PORT;
+				break;
+			default:
+				return (-1);
+			}
+			break;
+		case ICMP6_PACKET_TOO_BIG:
+			type = ICMP_UNREACH;
+			code = ICMP_UNREACH_NEEDFRAG;
+			mtu -= 20;
+			break;
+		case ICMP6_TIME_EXCEEDED:
+			type = ICMP_TIMXCEED;
+			break;
+		case ICMP6_PARAM_PROB:
+			switch (code) {
+			case ICMP6_PARAMPROB_HEADER:
+				type = ICMP_PARAMPROB;
+				code = ICMP_PARAMPROB_ERRATPTR;
+				ptr  = ntohl(icmp6->icmp6_pptr);
+
+				if (ptr == PTR_IP6(ip6_vfc))
+					; /* preserve */
+				else if (ptr == PTR_IP6(ip6_vfc) + 1)
+					ptr = PTR_IP(ip_tos);
+				else if (ptr == PTR_IP6(ip6_plen) ||
+				    ptr == PTR_IP6(ip6_plen) + 1)
+					ptr = PTR_IP(ip_len);
+				else if (ptr == PTR_IP6(ip6_nxt))
+					ptr = PTR_IP(ip_p);
+				else if (ptr == PTR_IP6(ip6_hlim))
+					ptr = PTR_IP(ip_ttl);
+				else if (ptr >= PTR_IP6(ip6_src) &&
+				    ptr < PTR_IP6(ip6_dst))
+					ptr = PTR_IP(ip_src);
+				else if (ptr >= PTR_IP6(ip6_dst) &&
+				    ptr < sizeof(struct ip6_hdr))
+					ptr = PTR_IP(ip_dst);
+				else
+					return (-1);
+				break;
+			case ICMP6_PARAMPROB_NEXTHEADER:
+				type = ICMP_UNREACH;
+				code = ICMP_UNREACH_PROTOCOL;
+				break;
+			default:
+				return (-1);
+			}
+			break;
+		default:
+			return (-1);
+		}
+		if (icmp6->icmp6_type != type) {
+			icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum,
+			    icmp6->icmp6_type, type, 0);
+			icmp6->icmp6_type = type;
+		}
+		if (icmp6->icmp6_code != code) {
+			icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum,
+			    icmp6->icmp6_code, code, 0);
+			icmp6->icmp6_code = code;
+		}
+		if (icmp6->icmp6_mtu != htonl(mtu)) {
+			icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum,
+			    htons(ntohl(icmp6->icmp6_mtu)), htons(mtu), 0);
+			/* aligns well with a icmpv4 nextmtu */
+			icmp6->icmp6_mtu = htonl(mtu);
+		}
+		if (ptr >= 0 && icmp6->icmp6_pptr != htonl(ptr)) {
+			icmp6->icmp6_cksum = pf_cksum_fixup(icmp6->icmp6_cksum,
+			    htons(ntohl(icmp6->icmp6_pptr)), htons(ptr), 0);
+			/* icmpv4 pptr is a one most significant byte */
+			icmp6->icmp6_pptr = htonl(ptr << 24);
+		}
+		break;
+	case AF_INET6:
+		icmp4 = arg;
+		type  = icmp4->icmp_type;
+		code  = icmp4->icmp_code;
+		mtu   = ntohs(icmp4->icmp_nextmtu);
+
+		switch (type) {
+		case ICMP_ECHO:
+			type = ICMP6_ECHO_REQUEST;
+			break;
+		case ICMP_ECHOREPLY:
+			type = ICMP6_ECHO_REPLY;
+			break;
+		case ICMP_UNREACH:
+			type = ICMP6_DST_UNREACH;
+			switch (code) {
+			case ICMP_UNREACH_NET:
+			case ICMP_UNREACH_HOST:
+			case ICMP_UNREACH_NET_UNKNOWN:
+			case ICMP_UNREACH_HOST_UNKNOWN:
+			case ICMP_UNREACH_ISOLATED:
+			case ICMP_UNREACH_TOSNET:
+			case ICMP_UNREACH_TOSHOST:
+				code = ICMP6_DST_UNREACH_NOROUTE;
+				break;
+			case ICMP_UNREACH_PORT:
+				code = ICMP6_DST_UNREACH_NOPORT;
+				break;
+			case ICMP_UNREACH_NET_PROHIB:
+			case ICMP_UNREACH_HOST_PROHIB:
+			case ICMP_UNREACH_FILTER_PROHIB:
+			case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+				code = ICMP6_DST_UNREACH_ADMIN;
+				break;
+			case ICMP_UNREACH_PROTOCOL:
+				type = ICMP6_PARAM_PROB;
+				code = ICMP6_PARAMPROB_NEXTHEADER;
+				ptr  = offsetof(struct ip6_hdr, ip6_nxt);
+				break;
+			case ICMP_UNREACH_NEEDFRAG:
+				type = ICMP6_PACKET_TOO_BIG;
+				code = 0;
+				mtu += 20;
+				break;
+			default:
+				return (-1);
+			}
+			break;
+		case ICMP_TIMXCEED:
+			type = ICMP6_TIME_EXCEEDED;
+			break;
+		case ICMP_PARAMPROB:
+			type = ICMP6_PARAM_PROB;
+			switch (code) {
+			case ICMP_PARAMPROB_ERRATPTR:
+				code = ICMP6_PARAMPROB_HEADER;
+				break;
+			case ICMP_PARAMPROB_LENGTH:
+				code = ICMP6_PARAMPROB_HEADER;
+				break;
+			default:
+				return (-1);
+			}
+
+			ptr = icmp4->icmp_pptr;
+			if (ptr == 0 || ptr == PTR_IP(ip_tos))
+				; /* preserve */
+			else if (ptr == PTR_IP(ip_len) ||
+			    ptr == PTR_IP(ip_len) + 1)
+				ptr = PTR_IP6(ip6_plen);
+			else if (ptr == PTR_IP(ip_ttl))
+				ptr = PTR_IP6(ip6_hlim);
+			else if (ptr == PTR_IP(ip_p))
+				ptr = PTR_IP6(ip6_nxt);
+			else if (ptr >= PTR_IP(ip_src) &&
+			    ptr < PTR_IP(ip_dst))
+				ptr = PTR_IP6(ip6_src);
+			else if (ptr >= PTR_IP(ip_dst) &&
+			    ptr < sizeof(struct ip))
+				ptr = PTR_IP6(ip6_dst);
+			else
+				return (-1);
+			break;
+		default:
+			return (-1);
+		}
+		if (icmp4->icmp_type != type) {
+			icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum,
+			    icmp4->icmp_type, type, 0);
+			icmp4->icmp_type = type;
+		}
+		if (icmp4->icmp_code != code) {
+			icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum,
+			    icmp4->icmp_code, code, 0);
+			icmp4->icmp_code = code;
+		}
+		if (icmp4->icmp_nextmtu != htons(mtu)) {
+			icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum,
+			    icmp4->icmp_nextmtu, htons(mtu), 0);
+			icmp4->icmp_nextmtu = htons(mtu);
+		}
+		if (ptr >= 0 && icmp4->icmp_void != ptr) {
+			icmp4->icmp_cksum = pf_cksum_fixup(icmp4->icmp_cksum,
+			    htons(icmp4->icmp_pptr), htons(ptr), 0);
+			icmp4->icmp_void = htonl(ptr);
+		}
+		break;
+	default:
+		return (-1);
+	}
+#endif /* INET && INET6 */
+
+	return (0);
+}
 
 /*
  * Need to modulate the sequence numbers in the TCP SACK option
@@ -2727,7 +3181,6 @@ pf_test_rule(struct pf_rule **rm, struct
     struct ifqueue *ifq, int hdrlen)
 {
 	struct pf_rule		*lastr = NULL;
-	sa_family_t		 af = pd->af;
 	struct pf_rule		*r, *a = NULL;
 	struct pf_ruleset	*ruleset = NULL;
 	struct pf_rule_slist	 rules;
@@ -2815,18 +3268,18 @@ pf_test_rule(struct pf_rule **rm, struct
 		else if (r->onrdomain >= 0  &&
 		    (r->onrdomain == pd->rdomain) == r->ifnot)
 			r = r->skip[PF_SKIP_RDOM].ptr;
-		else if (r->af && r->af != af)
+		else if (r->af && r->af != pd->af)
 			r = r->skip[PF_SKIP_AF].ptr;
 		else if (r->proto && r->proto != pd->proto)
 			r = r->skip[PF_SKIP_PROTO].ptr;
-		else if (PF_MISMATCHAW(&r->src.addr, &pd->nsaddr, af,
+		else if (PF_MISMATCHAW(&r->src.addr, &pd->nsaddr, pd->naf,
 		    r->src.neg, kif, act.rtableid))
 			r = r->skip[PF_SKIP_SRC_ADDR].ptr;
 		/* tcp/udp only. port_op always 0 in other cases */
 		else if (r->src.port_op && !pf_match_port(r->src.port_op,
 		    r->src.port[0], r->src.port[1], pd->nsport))
 			r = r->skip[PF_SKIP_SRC_PORT].ptr;
-		else if (PF_MISMATCHAW(&r->dst.addr, &pd->ndaddr, af,
+		else if (PF_MISMATCHAW(&r->dst.addr, &pd->ndaddr, pd->af,
 		    r->dst.neg, NULL, act.rtableid))
 			r = r->skip[PF_SKIP_DST_ADDR].ptr;
 		/* tcp/udp only. port_op always 0 in other cases */
@@ -2886,6 +3339,8 @@ pf_test_rule(struct pf_rule **rm, struct
 					/* order is irrelevant */
 					SLIST_INSERT_HEAD(&rules, ri, entry);
 					pf_rule_to_actions(r, &act);
+					if (r->naf)
+						pd->naf = r->naf;
 					if (pf_get_transaddr(r, pd, sns) ==
 					    -1) {
 						REASON_SET(&reason,
@@ -2893,7 +3348,7 @@ pf_test_rule(struct pf_rule **rm, struct
 						goto cleanup;
 					}
 					if (r->log || act.log & PF_LOG_MATCHES)
-						PFLOG_PACKET(kif, h, m, af,
+						PFLOG_PACKET(kif, h, m, pd->af,
 						    direction, reason, r,
 						    a, ruleset, pd);
 				} else {
@@ -2902,7 +3357,7 @@ pf_test_rule(struct pf_rule **rm, struct
 					*am = a;
 					*rsm = ruleset;
 					if (act.log & PF_LOG_MATCHES)
-						PFLOG_PACKET(kif, h, m, af,
+						PFLOG_PACKET(kif, h, m, pd->af,
 						    direction, reason, r,
 						    a, ruleset, pd);
 				}
@@ -2924,6 +3379,8 @@ pf_test_rule(struct pf_rule **rm, struct
 
 	/* apply actions for last matching pass/block rule */
 	pf_rule_to_actions(r, &act);
+	if (r->naf)
+		pd->naf = r->naf;
 	if (pf_get_transaddr(r, pd, sns) == -1) {
 		REASON_SET(&reason, PFRES_MEMORY);
 		goto cleanup;
@@ -2931,7 +3388,7 @@ pf_test_rule(struct pf_rule **rm, struct
 	REASON_SET(&reason, PFRES_MATCH);
 
 	if (r->log || act.log & PF_LOG_MATCHES)
-		PFLOG_PACKET(kif, h, m, af, direction, reason,
+		PFLOG_PACKET(kif, h, m, pd->af, direction, reason,
 		    r, a, ruleset, pd);
 
 	if ((r->action == PF_DROP) &&
@@ -2947,7 +3404,7 @@ pf_test_rule(struct pf_rule **rm, struct
 			struct ip	*h4;
 			struct ip6_hdr	*h6;
 
-			switch (af) {
+			switch (pd->af) {
 			case AF_INET:
 				h4 = mtod(m, struct ip *);
 				len = ntohs(h4->ip_len) - off;
@@ -2958,27 +3415,28 @@ pf_test_rule(struct pf_rule **rm, struct
 				break;
 			}
 
-			if (pf_check_proto_cksum(m, off, len, IPPROTO_TCP, af))
+			if (pf_check_proto_cksum(m, off, len, IPPROTO_TCP,
+			    pd->af))
 				REASON_SET(&reason, PFRES_PROTCKSUM);
 			else {
 				if (th->th_flags & TH_SYN)
 					ack++;
 				if (th->th_flags & TH_FIN)
 					ack++;
-				pf_send_tcp(r, af, pd->dst,
+				pf_send_tcp(r, pd->af, pd->dst,
 				    pd->src, th->th_dport, th->th_sport,
 				    ntohl(th->th_ack), ack, TH_RST|TH_ACK, 0, 0,
 				    r->return_ttl, 1, 0, pd->rdomain,
 				    pd->eh, kif->pfik_ifp);
 			}
-		} else if (pd->proto != IPPROTO_ICMP && af == AF_INET &&
+		} else if (pd->proto != IPPROTO_ICMP && pd->af == AF_INET &&
 		    r->return_icmp)
 			pf_send_icmp(m, r->return_icmp >> 8,
-			    r->return_icmp & 255, af, r, pd->rdomain);
-		else if (pd->proto != IPPROTO_ICMPV6 && af == AF_INET6 &&
+			    r->return_icmp & 255, pd->af, r, pd->rdomain);
+		else if (pd->proto != IPPROTO_ICMPV6 && pd->af == AF_INET6 &&
 		    r->return_icmp6)
 			pf_send_icmp(m, r->return_icmp6 >> 8,
-			    r->return_icmp6 & 255, af, r, pd->rdomain);
+			    r->return_icmp6 & 255, pd->af, r, pd->rdomain);
 	}
 
 	if (r->action == PF_DROP)
@@ -2989,7 +3447,7 @@ pf_test_rule(struct pf_rule **rm, struct
 	    rtable_l2(act.rtableid) != pd->rdomain)
 		pd->destchg = 1;
 
-	if (r->action == PF_PASS && af == AF_INET && ! r->allow_opts) {
+	if (r->action == PF_PASS && pd->af == AF_INET && ! r->allow_opts) {
 		struct ip	*h4 = mtod(m, struct ip *);
 			
 		if (h4->ip_hl > 5) {
@@ -3024,8 +3482,10 @@ pf_test_rule(struct pf_rule **rm, struct
 			else
 				sk = skw;
 			rewrite += pf_translate(pd,
-			    &sk->addr[pd->sidx], sk->port[pd->sidx],
-			    &sk->addr[pd->didx], sk->port[pd->didx],
+			    &sk->addr[pd->af == pd->naf ? pd->sidx : pd->didx],
+			    sk->port[pd->af == pd->naf ? pd->sidx : pd->didx],
+			    &sk->addr[pd->af == pd->naf ? pd->didx : pd->sidx],
+			    sk->port[pd->af == pd->naf ? pd->didx : pd->sidx],
 			    virtual_type, icmp_dir);
 		}
 	} else {
@@ -3053,7 +3513,10 @@ pf_test_rule(struct pf_rule **rm, struct
 	}
 #endif
 
-	return (PF_PASS);
+	if (rewrite && skw->af != sks->af) {
+		return (PF_AFRT);
+	} else
+		return (PF_PASS);
 
 cleanup:
 	while ((ri = SLIST_FIRST(&rules))) {
@@ -3266,33 +3729,38 @@ pf_translate(struct pf_pdesc *pd, struct
 	 * -pd is not fully set up
 	 */
 	int	rewrite = 0;
+	int	afto = pd->af != pd->naf;
 
-	if (PF_ANEQ(daddr, pd->dst, pd->af))
+	if (afto || PF_ANEQ(daddr, pd->dst, pd->af))
 		pd->destchg = 1;
 
 	switch (pd->proto) {
 	case IPPROTO_TCP:
-		if (PF_ANEQ(saddr, pd->src, pd->af) || *pd->sport != sport) {
+		if (afto || PF_ANEQ(saddr, pd->src, pd->af) ||
+		    *pd->sport != sport) {
 			pf_change_ap(pd->src, pd->sport, &pd->hdr.tcp->th_sum,
-			    saddr, sport, 0, pd->af);	
+			    saddr, sport, 0, pd->af, pd->naf);
 			rewrite = 1;
 		}
-		if (PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport) {
+		if (afto || PF_ANEQ(daddr, pd->dst, pd->af) ||
+		    *pd->dport != dport) {
 			pf_change_ap(pd->dst, pd->dport, &pd->hdr.tcp->th_sum,
-			    daddr, dport, 0, pd->af);
+			    daddr, dport, 0, pd->af, pd->naf);
 			rewrite = 1;
 		}
 		break;
 
 	case IPPROTO_UDP:
-		if (PF_ANEQ(saddr, pd->src, pd->af) || *pd->sport != sport) {
+		if (afto || PF_ANEQ(saddr, pd->src, pd->af) ||
+		    *pd->sport != sport) {
 			pf_change_ap(pd->src, pd->sport, &pd->hdr.udp->uh_sum,
-			    saddr, sport, 1, pd->af);
+			    saddr, sport, 1, pd->af, pd->naf);
 			rewrite = 1;
 		}
-		if (PF_ANEQ(daddr, pd->dst, pd->af) || *pd->dport != dport) {
+		if (afto || PF_ANEQ(daddr, pd->dst, pd->af) ||
+		    *pd->dport != dport) {
 			pf_change_ap(pd->dst, pd->dport, &pd->hdr.udp->uh_sum,
-			    daddr, dport, 1, pd->af);
+			    daddr, dport, 1, pd->af, pd->naf);
 			rewrite = 1;
 		}
 		break;
@@ -3303,15 +3771,22 @@ pf_translate(struct pf_pdesc *pd, struct
 		if (pd->af != AF_INET)
 			return (0);
 
-		if (PF_ANEQ(saddr, pd->src, pd->af)) {
-			pf_change_a(&pd->src->v4.s_addr, NULL,
-			    saddr->v4.s_addr, 0);
-			rewrite = 1;
-		}
-		if (PF_ANEQ(daddr, pd->dst, pd->af)) {
-			pf_change_a(&pd->dst->v4.s_addr, NULL,
-			    daddr->v4.s_addr, 0);
+		if (afto) {
+			if (pf_translate_icmp_af(AF_INET6, pd->hdr.icmp))
+				return (0);
+			pd->proto = IPPROTO_ICMPV6;
 			rewrite = 1;
+		} else {
+			if (PF_ANEQ(saddr, pd->src, pd->af)) {
+				pf_change_a(&pd->src->v4.s_addr, NULL,
+				    saddr->v4.s_addr, 0);
+				rewrite = 1;
+			}
+			if (PF_ANEQ(daddr, pd->dst, pd->af)) {
+				pf_change_a(&pd->dst->v4.s_addr, NULL,
+				    daddr->v4.s_addr, 0);
+				rewrite = 1;
+			}
 		}
 		if (virtual_type == htons(ICMP_ECHO)) {
 			u_int16_t icmpid = (icmp_dir == PF_IN) ? sport : dport;
@@ -3333,15 +3808,34 @@ pf_translate(struct pf_pdesc *pd, struct
 		if (pd->af != AF_INET6)
 			return (0);
 
-		if (PF_ANEQ(saddr, pd->src, pd->af)) {
-			pf_change_a6(pd->src, &pd->hdr.icmp6->icmp6_cksum,
-			    saddr, 0);
+		if (afto) {
+			/* ip_sum will be recalculated in pf_translate_af */
+			if (pf_translate_icmp_af(AF_INET, pd->hdr.icmp6))
+				return (0);
+			pd->proto = IPPROTO_ICMP;
 			rewrite = 1;
+		} else {
+			if (PF_ANEQ(saddr, pd->src, pd->af)) {
+				pf_change_a6(pd->src,
+				    &pd->hdr.icmp6->icmp6_cksum, saddr, 0);
+				rewrite = 1;
+			}
+			if (PF_ANEQ(daddr, pd->dst, pd->af)) {
+				pf_change_a6(pd->dst,
+				    &pd->hdr.icmp6->icmp6_cksum, daddr, 0);
+				rewrite = 1;
+			}
 		}
-		if (PF_ANEQ(daddr, pd->dst, pd->af)) {
-			pf_change_a6(pd->dst, &pd->hdr.icmp6->icmp6_cksum,
-			    daddr, 0);
-			rewrite = 1;
+		if (virtual_type == htons(ICMP6_ECHO_REQUEST)) {
+			u_int16_t icmpid = (icmp_dir == PF_IN) ? sport : dport;
+
+			if (icmpid != pd->hdr.icmp6->icmp6_id) {
+				pd->hdr.icmp6->icmp6_cksum = pf_cksum_fixup(
+				    pd->hdr.icmp6->icmp6_cksum,
+				    pd->hdr.icmp6->icmp6_id, icmpid, 0);
+				pd->hdr.icmp6->icmp6_id = icmpid;
+				rewrite = 1;
+			}
 		}
 		break;
 #endif /* INET6 */
@@ -3350,12 +3844,12 @@ pf_translate(struct pf_pdesc *pd, struct
 		switch (pd->af) {
 #ifdef INET
 		case AF_INET:
-			if (PF_ANEQ(saddr, pd->src, pd->af)) {
+			if (!afto && PF_ANEQ(saddr, pd->src, pd->af)) {
 				pf_change_a(&pd->src->v4.s_addr, NULL,
 				    saddr->v4.s_addr, 0);
 				rewrite = 1;
 			}
-			if (PF_ANEQ(daddr, pd->dst, pd->af)) {
+			if (!afto && PF_ANEQ(daddr, pd->dst, pd->af)) {
 				pf_change_a(&pd->dst->v4.s_addr, NULL,
 				    daddr->v4.s_addr, 0);
 				rewrite = 1;
@@ -3364,11 +3858,11 @@ pf_translate(struct pf_pdesc *pd, struct
 #endif /* INET */
 #ifdef INET6
 		case AF_INET6:
-			if (PF_ANEQ(saddr, pd->src, pd->af)) {
+			if (!afto && PF_ANEQ(saddr, pd->src, pd->af)) {
 				pf_change_a6(pd->src, NULL, saddr, 0);
 				rewrite = 1;
 			}
-			if (PF_ANEQ(daddr, pd->dst, pd->af)) {
+			if (!afto && PF_ANEQ(daddr, pd->dst, pd->af)) {
 				pf_change_a6(pd->dst, NULL, daddr, 0);
 				rewrite = 1;
 			}
@@ -3871,6 +4365,7 @@ pf_test_state_tcp(struct pf_state **stat
 	int			 copyback = 0;
 	struct pf_state_peer	*src, *dst;
 	struct pf_state_key	*sk;
+	int			 action = PF_PASS;
 
 	key.af = pd->af;
 	key.proto = IPPROTO_TCP;
@@ -4002,27 +4497,52 @@ pf_test_state_tcp(struct pf_state **stat
 		if (pf_tcp_track_sloppy(src, dst, state, pd, reason) == PF_DROP)
 			return (PF_DROP);
 	} else {
-		if (pf_tcp_track_full(src, dst, state, kif, m, off, pd, reason,
-		    &copyback) == PF_DROP)
-			return (PF_DROP);
+		int		ret;
+
+		if (PF_REVERSED_KEY((*state)->key, pd->af))
+			ret = pf_tcp_track_full(dst, src, state, kif, m, off,
+			    pd, reason, &copyback);
+		else
+			ret = pf_tcp_track_full(src, dst, state, kif, m, off,
+			    pd, reason, &copyback);
+		if (ret == PF_DROP)
+			return (ret);
 	}
 
 	/* translate source/destination address, if necessary */
 	if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) {
-		struct pf_state_key *nk = (*state)->key[pd->didx];
+		struct pf_state_key	*nk;
+		int			 afto, sidx, didx;
 
-		if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) ||
-		    nk->port[pd->sidx] != th->th_sport)
+		if (PF_REVERSED_KEY((*state)->key, pd->af))
+			nk = (*state)->key[pd->sidx];
+		else
+			nk = (*state)->key[pd->didx];
+
+		afto = pd->af != nk->af;
+		sidx = afto ? pd->didx : pd->sidx;
+		didx = afto ? pd->sidx : pd->didx;
+
+		if (afto || PF_ANEQ(pd->src, &nk->addr[sidx], pd->af) ||
+		    nk->port[sidx] != th->th_sport)
 			pf_change_ap(pd->src, &th->th_sport, &th->th_sum,
-			    &nk->addr[pd->sidx], nk->port[pd->sidx], 0, pd->af);
-		if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
+			    &nk->addr[sidx], nk->port[sidx], 0, pd->af, nk->af);
+
+		if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) ||
 		    pd->rdomain != nk->rdomain)
 			pd->destchg = 1;
-		if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
-		    nk->port[pd->didx] != th->th_dport)
-			pf_change_ap(pd->dst, &th->th_dport, &th->th_sum,	
-			    &nk->addr[pd->didx], nk->port[pd->didx], 0, pd->af);
+		if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) ||
+		    nk->port[didx] != th->th_dport)
+			pf_change_ap(pd->dst, &th->th_dport, &th->th_sum,
+			    &nk->addr[didx], nk->port[didx], 0, pd->af, nk->af);
 		m->m_pkthdr.rdomain = nk->rdomain;
+
+		if (afto) {
+			PF_ACPY(&pd->nsaddr, &nk->addr[sidx], nk->af);
+			PF_ACPY(&pd->ndaddr, &nk->addr[didx], nk->af);
+			action = PF_AFRT;
+		}
+
 		copyback = 1;
 	}
 
@@ -4030,7 +4550,7 @@ pf_test_state_tcp(struct pf_state **stat
 	if (copyback)
 		m_copyback(m, off, sizeof(*th), th, M_NOWAIT);
 
-	return (PF_PASS);
+	return (action);
 }
 
 int
@@ -4040,6 +4560,7 @@ pf_test_state_udp(struct pf_state **stat
 	struct pf_state_peer	*src, *dst;
 	struct pf_state_key_cmp	 key;
 	struct udphdr		*uh = pd->hdr.udp;
+	int			 action = PF_PASS;
 
 	key.af = pd->af;
 	key.proto = IPPROTO_UDP;
@@ -4081,24 +4602,42 @@ pf_test_state_udp(struct pf_state **stat
 
 	/* translate source/destination address, if necessary */
 	if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) {
-		struct pf_state_key *nk = (*state)->key[pd->didx];
+		struct pf_state_key	*nk;
+		int			 afto, sidx, didx;
+
+		if (PF_REVERSED_KEY((*state)->key, pd->af))
+			nk = (*state)->key[pd->sidx];
+		else
+			nk = (*state)->key[pd->didx];
 
-		if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], pd->af) ||
-		    nk->port[pd->sidx] != uh->uh_sport)
+		afto = pd->af != nk->af;
+		sidx = afto ? pd->didx : pd->sidx;
+		didx = afto ? pd->sidx : pd->didx;
+
+		if (afto || PF_ANEQ(pd->src, &nk->addr[sidx], pd->af) ||
+		    nk->port[sidx] != uh->uh_sport)
 			pf_change_ap(pd->src, &uh->uh_sport, &uh->uh_sum,
-			    &nk->addr[pd->sidx], nk->port[pd->sidx], 1, pd->af);
-		if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
+			    &nk->addr[sidx], nk->port[sidx], 1, pd->af, nk->af);
+
+		if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) ||
 		    pd->rdomain != nk->rdomain)
 			pd->destchg = 1;
-		if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], pd->af) ||
-		    nk->port[pd->didx] != uh->uh_dport)
+		if (afto || PF_ANEQ(pd->dst, &nk->addr[didx], pd->af) ||
+		    nk->port[didx] != uh->uh_dport)
 			pf_change_ap(pd->dst, &uh->uh_dport, &uh->uh_sum,
-			    &nk->addr[pd->didx], nk->port[pd->didx], 1, pd->af);
+			    &nk->addr[didx], nk->port[didx], 1, pd->af, nk->af);
 		m->m_pkthdr.rdomain = nk->rdomain;
+
+		if (afto) {
+			PF_ACPY(&pd->nsaddr, &nk->addr[sidx], nk->af);
+			PF_ACPY(&pd->ndaddr, &nk->addr[didx], nk->af);
+			action = PF_AFRT;
+		}
+
 		m_copyback(m, off, sizeof(*uh), uh, M_NOWAIT);
 	}
 
-	return (PF_PASS);
+	return (action);
 }
 
 int
@@ -4211,7 +4750,18 @@ pf_test_state_icmp(struct pf_state **sta
 
 		/* translate source/destination address, if necessary */
 		if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) {
-			struct pf_state_key *nk = (*state)->key[pd->didx];
+			struct pf_state_key	*nk;
+			int			 afto, sidx, didx;
+
+			if (PF_REVERSED_KEY((*state)->key, pd->af))
+				nk = (*state)->key[pd->sidx];
+			else
+				nk = (*state)->key[pd->didx];
+
+			afto = pd->af != nk->af;
+			sidx = afto ? pd->didx : pd->sidx;
+			didx = afto ? pd->sidx : pd->didx;
+			iidx = afto ? !iidx : iidx;
 
 			if (pd->rdomain != nk->rdomain)
 				pd->destchg = 1;
@@ -4220,13 +4770,21 @@ pf_test_state_icmp(struct pf_state **sta
 			switch (pd->af) {
 #ifdef INET
 			case AF_INET:
-				if (PF_ANEQ(pd->src,
+#ifdef INET6
+				if (afto) {
+					if (pf_translate_icmp_af(AF_INET6,
+					    pd->hdr.icmp))
+						return (PF_DROP);
+					pd->proto = IPPROTO_ICMPV6;
+				}
+#endif
+				if (!afto && PF_ANEQ(pd->src,
 				    &nk->addr[pd->sidx], AF_INET))
 					pf_change_a(&saddr->v4.s_addr, NULL,
 					    nk->addr[pd->sidx].v4.s_addr, 0);
 
-				if (PF_ANEQ(pd->dst, &nk->addr[pd->didx],
-				    AF_INET)) {
+				if (!afto && PF_ANEQ(pd->dst,
+				    &nk->addr[pd->didx], AF_INET)) {
 					pf_change_a(&daddr->v4.s_addr, NULL,
 					    nk->addr[pd->didx].v4.s_addr, 0);
 					pd->destchg = 1;
@@ -4248,13 +4806,21 @@ pf_test_state_icmp(struct pf_state **sta
 #endif /* INET */
 #ifdef INET6
 			case AF_INET6:
-				if (PF_ANEQ(pd->src,
+#ifdef INET
+				if (afto) {
+					if (pf_translate_icmp_af(AF_INET,
+					    pd->hdr.icmp6))
+						return (PF_DROP);
+					pd->proto = IPPROTO_ICMP;
+				}
+#endif
+				if (!afto && PF_ANEQ(pd->src,
 				    &nk->addr[pd->sidx], AF_INET6))
 					pf_change_a6(saddr,
 					    &pd->hdr.icmp6->icmp6_cksum,
 					    &nk->addr[pd->sidx], 0);
 
-				if (PF_ANEQ(pd->dst,
+				if (!afto && PF_ANEQ(pd->dst,
 				    &nk->addr[pd->didx], AF_INET6)) {
 					pf_change_a6(daddr,
 					    &pd->hdr.icmp6->icmp6_cksum,
@@ -4262,12 +4828,28 @@ pf_test_state_icmp(struct pf_state **sta
 					pd->destchg = 1;
 				}
 
+				if (nk->port[iidx] !=
+				    pd->hdr.icmp6->icmp6_id) {
+					pd->hdr.icmp6->icmp6_cksum =
+					    pf_cksum_fixup(
+					    pd->hdr.icmp6->icmp6_cksum,
+					     pd->hdr.icmp6->icmp6_id,
+					    nk->port[iidx], 0);
+					pd->hdr.icmp6->icmp6_id =
+					    nk->port[iidx];
+				}
+
 				m_copyback(m, off,
 				    sizeof(struct icmp6_hdr),
 				    pd->hdr.icmp6, M_NOWAIT);
 				break;
 #endif /* INET6 */
 			}
+			if (afto) {
+				PF_ACPY(&pd->nsaddr, &nk->addr[sidx], nk->af);
+				PF_ACPY(&pd->ndaddr, &nk->addr[didx], nk->af);
+				return (PF_AFRT);
+			}
 		}
 		return (PF_PASS);
 
@@ -4462,8 +5044,46 @@ pf_test_state_icmp(struct pf_state **sta
 			/* translate source/destination address, if necessary */
 			if ((*state)->key[PF_SK_WIRE] !=
 			    (*state)->key[PF_SK_STACK]) {
-				struct pf_state_key *nk =
-				    (*state)->key[pd->didx];
+				struct pf_state_key	*nk;
+				int			 afto, sidx, didx;
+
+				if (PF_REVERSED_KEY((*state)->key, pd->af))
+					nk = (*state)->key[pd->sidx];
+				else
+					nk = (*state)->key[pd->didx];
+
+				afto = pd->af != nk->af;
+				sidx = afto ? pd2.didx : pd2.sidx;
+				didx = afto ? pd2.sidx : pd2.didx;
+
+#if INET && INET6
+				if (afto) {
+					if (pf_translate_icmp_af(nk->af,
+					    pd->hdr.icmp))
+						return (PF_DROP);
+					m_copyback(m, off,
+					    sizeof(struct icmp6_hdr),
+					    pd->hdr.icmp6, M_NOWAIT);
+					if (pf_change_icmp_af(m, ipoff2, &off2,
+					    pd, &pd2, &nk->addr[sidx],
+					    &nk->addr[didx], pd->af, nk->af))
+						return (PF_DROP);
+					if (nk->af == AF_INET)
+						pd->proto = IPPROTO_ICMP;
+					else
+						pd->proto = IPPROTO_ICMPV6;
+					th.th_sport = nk->port[sidx];
+					th.th_dport = nk->port[didx];
+					m_copyback(m, off2, 8, &th, M_NOWAIT);
+					m->m_pkthdr.rdomain = nk->rdomain;
+					pd->destchg = 1;
+					PF_ACPY(&pd->nsaddr,
+					    &nk->addr[pd2.sidx], nk->af);
+					PF_ACPY(&pd->ndaddr,
+					    &nk->addr[pd2.didx], nk->af);
+					return (PF_AFRT);
+				}
+#endif
 
 				if (PF_ANEQ(pd2.src,
 				    &nk->addr[pd2.sidx], pd2.af) ||
@@ -4537,8 +5157,51 @@ pf_test_state_icmp(struct pf_state **sta
 			/* translate source/destination address, if necessary */
 			if ((*state)->key[PF_SK_WIRE] !=
 			    (*state)->key[PF_SK_STACK]) {
-				struct pf_state_key *nk =
-				    (*state)->key[pd->didx];
+				struct pf_state_key	*nk;
+				int			 afto, sidx, didx;
+
+				if (PF_REVERSED_KEY((*state)->key, pd->af))
+					nk = (*state)->key[pd->sidx];
+				else
+					nk = (*state)->key[pd->didx];
+
+				afto = pd->af != nk->af;
+				sidx = afto ? pd2.didx : pd2.sidx;
+				didx = afto ? pd2.sidx : pd2.didx;
+
+#if INET && INET6
+				if (afto) {
+					if (pf_translate_icmp_af(nk->af,
+					    pd->hdr.icmp))
+						return (PF_DROP);
+					m_copyback(m, off,
+					    sizeof(struct icmp6_hdr),
+					    pd->hdr.icmp6, M_NOWAIT);
+					if (pf_change_icmp_af(m, ipoff2, &off2,
+					    pd, &pd2, &nk->addr[sidx],
+					    &nk->addr[didx], pd->af, nk->af))
+						return (PF_DROP);
+					if (nk->af == AF_INET)
+						pd->proto = IPPROTO_ICMP;
+					else
+						pd->proto = IPPROTO_ICMPV6;
+					pf_change_ap(pd2.src, &uh.uh_sport,
+					    &uh.uh_sum, &nk->addr[pd2.sidx],
+					    nk->port[sidx], 1, pd->af, nk->af);
+					pf_change_ap(pd2.dst, &uh.uh_dport,
+					    &uh.uh_sum, &nk->addr[pd2.didx],
+					    nk->port[didx], 1, pd->af, nk->af);
+					m_copyback(m, off2, sizeof(uh), &uh,
+					    M_NOWAIT);
+					m->m_pkthdr.rdomain = nk->rdomain;
+					pd->destchg = 1;
+					PF_ACPY(&pd->nsaddr,
+					    &nk->addr[pd2.sidx], nk->af);
+					PF_ACPY(&pd->ndaddr,
+					    &nk->addr[pd2.didx], nk->af);
+					return (PF_AFRT);
+				}
+#endif
 
 				if (PF_ANEQ(pd2.src,
 				    &nk->addr[pd2.sidx], pd2.af) ||
@@ -4609,8 +5272,50 @@ pf_test_state_icmp(struct pf_state **sta
 			/* translate source/destination address, if necessary */
 			if ((*state)->key[PF_SK_WIRE] !=
 			    (*state)->key[PF_SK_STACK]) {
-				struct pf_state_key *nk =
-				    (*state)->key[pd->didx];
+				struct pf_state_key	*nk;
+				int			 afto, sidx, didx;
+
+				if (PF_REVERSED_KEY((*state)->key, pd->af))
+					nk = (*state)->key[pd->sidx];
+				else
+					nk = (*state)->key[pd->didx];
+
+				afto = pd->af != nk->af;
+				sidx = afto ? pd2.didx : pd2.sidx;
+				didx = afto ? pd2.sidx : pd2.didx;
+				iidx = afto ? !iidx : iidx;
+
+#ifdef INET6
+				if (afto) {
+					if (nk->af != AF_INET6)
+						return (PF_DROP);
+					if (pf_translate_icmp_af(nk->af,
+					    pd->hdr.icmp))
+						return (PF_DROP);
+					m_copyback(m, off,
+					    sizeof(struct icmp6_hdr),
+					    pd->hdr.icmp6, M_NOWAIT);
+					if (pf_change_icmp_af(m, ipoff2, &off2,
+					    pd, &pd2, &nk->addr[sidx],
+					    &nk->addr[didx], pd->af, nk->af))
+						return (PF_DROP);
+					pd->proto = IPPROTO_ICMPV6;
+					if (pf_translate_icmp_af(nk->af, &iih))
+						return (PF_DROP);
+					if (virtual_type == htons(ICMP_ECHO) &&
+					    nk->port[iidx] != iih.icmp_id)
+						iih.icmp_id = nk->port[iidx];
+					m_copyback(m, off2, ICMP_MINLEN, &iih,
+					    M_NOWAIT);
+					m->m_pkthdr.rdomain = nk->rdomain;
+					pd->destchg = 1;
+					PF_ACPY(&pd->nsaddr,
+					    &nk->addr[pd2.sidx], nk->af);
+					PF_ACPY(&pd->ndaddr,
+					    &nk->addr[pd2.didx], nk->af);
+					return (PF_AFRT);
+				}
+#endif
 
 				if (PF_ANEQ(pd2.src,
 				    &nk->addr[pd2.sidx], pd2.af) ||
@@ -4679,8 +5384,52 @@ pf_test_state_icmp(struct pf_state **sta
 			/* translate source/destination address, if necessary */
 			if ((*state)->key[PF_SK_WIRE] !=
 			    (*state)->key[PF_SK_STACK]) {
-				struct pf_state_key *nk =
-				    (*state)->key[pd->didx];
+				struct pf_state_key	*nk;
+				int			 afto, sidx, didx;
+
+				if (PF_REVERSED_KEY((*state)->key, pd->af))
+					nk = (*state)->key[pd->sidx];
+				else
+					nk = (*state)->key[pd->didx];
+
+				afto = pd->af != nk->af;
+				sidx = afto ? pd2.didx : pd2.sidx;
+				didx = afto ? pd2.sidx : pd2.didx;
+				iidx = afto ? !iidx : iidx;
+
+#ifdef INET
+				if (afto) {
+					if (nk->af != AF_INET)
+						return (PF_DROP);
+					if (pf_translate_icmp_af(nk->af,
+					    pd->hdr.icmp))
+						return (PF_DROP);
+					m_copyback(m, off,
+					    sizeof(struct icmp6_hdr),
+					    pd->hdr.icmp6, M_NOWAIT);
+					if (pf_change_icmp_af(m, ipoff2, &off2,
+					    pd, &pd2, &nk->addr[sidx],
+					    &nk->addr[didx], pd->af, nk->af))
+						return (PF_DROP);
+					pd->proto = IPPROTO_ICMP;
+					if (pf_translate_icmp_af(nk->af, &iih))
+						return (PF_DROP);
+					if (virtual_type ==
+					    htons(ICMP6_ECHO_REQUEST) &&
+					    nk->port[iidx] != iih.icmp6_id)
+						iih.icmp6_id = nk->port[iidx];
+					m_copyback(m, off2,
+					    sizeof(struct icmp6_hdr), &iih,
+					    M_NOWAIT);
+					m->m_pkthdr.rdomain = nk->rdomain;
+					pd->destchg = 1;
+					PF_ACPY(&pd->nsaddr,
+					    &nk->addr[pd2.sidx], nk->af);
+					PF_ACPY(&pd->ndaddr,
+					    &nk->addr[pd2.didx], nk->af);
+					return (PF_AFRT);
+				}
+#endif
 
 				if (PF_ANEQ(pd2.src,
 				    &nk->addr[pd2.sidx], pd2.af) ||
@@ -4785,6 +5534,7 @@ pf_test_state_other(struct pf_state **st
 {
 	struct pf_state_peer	*src, *dst;
 	struct pf_state_key_cmp	 key;
+	int			 action = PF_PASS;
 
 	key.af = pd->af;
 	key.proto = pd->proto;
@@ -4824,7 +5574,15 @@ pf_test_state_other(struct pf_state **st
 
 	/* translate source/destination address, if necessary */
 	if ((*state)->key[PF_SK_WIRE] != (*state)->key[PF_SK_STACK]) {
-		struct pf_state_key *nk = (*state)->key[pd->didx];
+		struct pf_state_key	*nk;
+		int			 afto;
+
+		if (PF_REVERSED_KEY((*state)->key, pd->af))
+			nk = (*state)->key[pd->sidx];
+		else
+			nk = (*state)->key[pd->didx];
+
+		afto = pd->af != nk->af;
 
 		KASSERT(nk);
 		KASSERT(pd);
@@ -4834,11 +5592,13 @@ pf_test_state_other(struct pf_state **st
 		switch (pd->af) {
 #ifdef INET
 		case AF_INET:
-			if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET))
+			if (!afto &&
+			    PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET))
 				pf_change_a(&pd->src->v4.s_addr, NULL,
 				    nk->addr[pd->sidx].v4.s_addr,
 				    0);
-			if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) {
+			if (!afto &&
+			    PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET)) {
 				pf_change_a(&pd->dst->v4.s_addr, NULL,
 				    nk->addr[pd->didx].v4.s_addr,
 				    0);
@@ -4848,10 +5608,12 @@ pf_test_state_other(struct pf_state **st
 #endif /* INET */
 #ifdef INET6
 		case AF_INET6:
-			if (PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET6))
+			if (!afto &&
+			    PF_ANEQ(pd->src, &nk->addr[pd->sidx], AF_INET6))
 				PF_ACPY(pd->src, &nk->addr[pd->sidx], pd->af);
 
-			if (PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET6)) {
+			if (!afto &&
+			    PF_ANEQ(pd->dst, &nk->addr[pd->didx], AF_INET6)) {
 				PF_ACPY(pd->dst, &nk->addr[pd->didx], pd->af);
 				pd->destchg = 1;
 			}
@@ -4861,9 +5623,18 @@ pf_test_state_other(struct pf_state **st
 		if (pd->rdomain != nk->rdomain)
 			pd->destchg = 1;
 
+		if (afto) {
+			PF_ACPY(&pd->nsaddr,
+			    &nk->addr[afto ? pd->didx : pd->sidx], nk->af);
+			PF_ACPY(&pd->ndaddr,
+			    &nk->addr[afto ? pd->sidx : pd->didx], nk->af);
+			pd->destchg = 1;
+			action = PF_AFRT;
+		}
+
 		m->m_pkthdr.rdomain = nk->rdomain;
 	}
-	return (PF_PASS);
+	return (action);
 }
 
 /*
@@ -5500,8 +6271,9 @@ pf_setup_pdesc(sa_family_t af, int dir, 
 		pd->dir = dir;
 		pd->sidx = (dir == PF_IN) ? 0 : 1;
 		pd->didx = (dir == PF_IN) ? 1 : 0;
-		pd->af = AF_INET;
+		pd->af = pd->naf = AF_INET;
 		pd->tos = h->ip_tos;
+		pd->ttl = h->ip_ttl;
 		pd->tot_len = ntohs(h->ip_len);
 		pd->rdomain = rtable_l2(m->m_pkthdr.rdomain);
 
@@ -5565,8 +6337,9 @@ pf_setup_pdesc(sa_family_t af, int dir, 
 		pd->dir = dir;
 		pd->sidx = (dir == PF_IN) ? 0 : 1;
 		pd->didx = (dir == PF_IN) ? 1 : 0;
-		pd->af = AF_INET6;
+		pd->af = pd->naf = AF_INET6;
 		pd->tos = 0;
+		pd->ttl = h->ip6_hlim;
 		pd->tot_len = ntohs(h->ip6_plen) + sizeof(struct ip6_hdr);
 		*off = ((caddr_t)h - m->m_data) + sizeof(struct ip6_hdr);
 		pd->proto = h->ip6_nxt;
@@ -5711,7 +6484,7 @@ pf_counters_inc(int dir, int action, str
 	    += pd->tot_len;
 	kif->pfik_packets[pd->af == AF_INET6][dir == PF_OUT][action != PF_PASS]++;
 
-	if (action == PF_PASS || r->action == PF_DROP) {
+	if (action == PF_PASS || action == PF_AFRT || r->action == PF_DROP) {
 		dirndx = (dir == PF_OUT);
 		r->packets[dirndx]++;
 		r->bytes[dirndx] += pd->tot_len;
@@ -5843,7 +6616,7 @@ pf_test(int dir, struct ifnet *ifp, stru
 			goto done;
 		action = pf_test_state_tcp(&s, dir, kif, m, off, &pd,
 		    &reason);
-		if (action == PF_PASS) {
+		if (action == PF_PASS || action == PF_AFRT) {
 #if NPFSYNC > 0
 			pfsync_update_state(s);
 #endif /* NPFSYNC */
@@ -5865,7 +6638,7 @@ pf_test(int dir, struct ifnet *ifp, stru
 
 	case IPPROTO_UDP: {
 		action = pf_test_state_udp(&s, dir, kif, m, off, &pd);
-		if (action == PF_PASS) {
+		if (action == PF_PASS || action == PF_AFRT) {
 #if NPFSYNC > 0
 			pfsync_update_state(s);
 #endif /* NPFSYNC */
@@ -5881,7 +6654,7 @@ pf_test(int dir, struct ifnet *ifp, stru
 	case IPPROTO_ICMP: {
 		action = pf_test_state_icmp(&s, dir, kif, m, off, &pd,
 		    &reason);
-		if (action == PF_PASS) {
+		if (action == PF_PASS || action == PF_AFRT) {
 #if NPFSYNC > 0
 			pfsync_update_state(s);
 #endif /* NPFSYNC */
@@ -5903,7 +6676,7 @@ pf_test(int dir, struct ifnet *ifp, stru
 
 	default:
 		action = pf_test_state_other(&s, dir, kif, m, &pd);
-		if (action == PF_PASS) {
+		if (action == PF_PASS || action == PF_AFRT) {
 #if NPFSYNC > 0
 			pfsync_update_state(s);
 #endif /* NPFSYNC */
@@ -6021,6 +6794,17 @@ done:
 		*m0 = NULL;
 		action = PF_PASS;
 		break;
+	case PF_AFRT:
+		if (pf_translate_af(AF_INET6, &m, off, &pd)) {
+			if (!m)
+				*m0 = NULL;
+			action = PF_DROP;
+			break;
+		}
+		pf_route6(&m, r, dir, kif->pfik_ifp, s);
+		*m0 = NULL;
+		action = PF_PASS;
+		break;
 	default:
 		/* pf_route can free the mbuf causing *m0 to become NULL */
 		if (r->rt)
@@ -6134,7 +6918,7 @@ pf_test6(int fwdir, struct ifnet *ifp, s
 			goto done;
 		action = pf_test_state_tcp(&s, dir, kif, m, off, &pd,
 		    &reason);
-		if (action == PF_PASS) {
+		if (action == PF_PASS || action == PF_AFRT) {
 #if NPFSYNC > 0
 			pfsync_update_state(s);
 #endif /* NPFSYNC */
@@ -6156,7 +6940,7 @@ pf_test6(int fwdir, struct ifnet *ifp, s
 
 	case IPPROTO_UDP: {
 		action = pf_test_state_udp(&s, dir, kif, m, off, &pd);
-		if (action == PF_PASS) {
+		if (action == PF_PASS || action == PF_AFRT) {
 #if NPFSYNC > 0
 			pfsync_update_state(s);
 #endif /* NPFSYNC */
@@ -6179,7 +6963,7 @@ pf_test6(int fwdir, struct ifnet *ifp, s
 	case IPPROTO_ICMPV6: {
 		action = pf_test_state_icmp(&s, dir, kif,
 		    m, off, &pd, &reason);
-		if (action == PF_PASS) {
+		if (action == PF_PASS || action == PF_AFRT) {
 #if NPFSYNC > 0
 			pfsync_update_state(s);
 #endif /* NPFSYNC */
@@ -6194,7 +6978,7 @@ pf_test6(int fwdir, struct ifnet *ifp, s
 
 	default:
 		action = pf_test_state_other(&s, dir, kif, m, &pd);
-		if (action == PF_PASS) {
+		if (action == PF_PASS || action == PF_AFRT) {
 #if NPFSYNC > 0
 			pfsync_update_state(s);
 #endif /* NPFSYNC */
@@ -6305,6 +7089,17 @@ done:
 		break;
 	case PF_DIVERT:
 		divert6_packet(m, dir);
+		*m0 = NULL;
+		action = PF_PASS;
+		break;
+	case PF_AFRT:
+		if (pf_translate_af(AF_INET, &m, off, &pd)) {
+			if (!m)
+				*m0 = NULL;
+			action = PF_DROP;
+			break;
+		}
+		pf_route(&m, r, dir, kif->pfik_ifp, s);
 		*m0 = NULL;
 		action = PF_PASS;
 		break;
Index: net/pf_ioctl.c
===================================================================
RCS file: /home/cvs/src/sys/net/pf_ioctl.c,v
retrieving revision 1.239
diff -u -p -r1.239 pf_ioctl.c
--- net/pf_ioctl.c	19 Apr 2011 21:58:03 -0000	1.239
+++ net/pf_ioctl.c	26 Apr 2011 07:35:16 -0000
@@ -2495,6 +2495,8 @@ pf_rule_copyin(struct pf_rule *from, str
 	pf_pool_copyin(&from->rdr, &to->rdr);
 	pf_pool_copyin(&from->route, &to->route);
 
+	to->naf = from->naf;
+
 	if (pf_kif_setup(to->ifname, &to->kif))
 		return (EINVAL);
 	if (pf_kif_setup(to->rcv_ifname, &to->rcv_kif))
Index: net/pf_lb.c
===================================================================
RCS file: /home/cvs/src/sys/net/pf_lb.c,v
retrieving revision 1.13
diff -u -p -r1.13 pf_lb.c
--- net/pf_lb.c	27 Jun 2010 01:39:43 -0000	1.13
+++ net/pf_lb.c	24 Mar 2011 19:59:08 -0000
@@ -103,6 +103,8 @@ void			 pf_hash(struct pf_addr *, struct
 int			 pf_get_sport(struct pf_pdesc *, struct pf_rule *,
 			    struct pf_addr *, u_int16_t *, u_int16_t,
 			    u_int16_t, struct pf_src_node **);
+int			 pf_get_transaddr_af(struct pf_rule *,
+			    struct pf_pdesc *, struct pf_src_node **);
 
 #define mix(a,b,c) \
 	do {					\
@@ -171,7 +173,7 @@ pf_get_sport(struct pf_pdesc *pd, struct
 	u_int16_t		cut;
 
 	bzero(&init_addr, sizeof(init_addr));
-	if (pf_map_addr(pd->af, r, &pd->nsaddr, naddr, &init_addr, sn, &r->nat,
+	if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr, &init_addr, sn, &r->nat,
 	    PF_SN_NAT))
 		return (1);
 
@@ -185,7 +187,7 @@ pf_get_sport(struct pf_pdesc *pd, struct
 	}
 
 	do {
-		key.af = pd->af;
+		key.af = pd->naf;
 		key.proto = pd->proto;
 		key.rdomain = pd->rdomain;
 		PF_ACPY(&key.addr[0], &pd->ndaddr, key.af);
@@ -249,7 +251,7 @@ pf_get_sport(struct pf_pdesc *pd, struct
 		switch (r->nat.opts & PF_POOL_TYPEMASK) {
 		case PF_POOL_RANDOM:
 		case PF_POOL_ROUNDROBIN:
-			if (pf_map_addr(pd->af, r, &pd->nsaddr, naddr,
+			if (pf_map_addr(pd->naf, r, &pd->nsaddr, naddr,
 			    &init_addr, sn, &r->nat, PF_SN_NAT))
 				return (1);
 			break;
@@ -259,7 +261,7 @@ pf_get_sport(struct pf_pdesc *pd, struct
 		default:
 			return (1);
 		}
-	} while (! PF_AEQ(&init_addr, naddr, pd->af) );
+	} while (! PF_AEQ(&init_addr, naddr, pd->naf) );
 	return (1);					/* none available */
 }
 
@@ -427,6 +429,9 @@ pf_get_transaddr(struct pf_rule *r, stru
 	struct pf_addr	naddr;
 	u_int16_t	nport = 0;
 
+	if (pd->af != pd->naf)
+		return (pf_get_transaddr_af(r, pd, sns));
+
 	if (r->nat.addr.type != PF_ADDR_NONE) {
 		/* XXX is this right? what if rtable is changed at the same
 		 * XXX time? where do I need to figure out the sport? */
@@ -468,6 +473,135 @@ pf_get_transaddr(struct pf_rule *r, stru
 		PF_ACPY(&pd->ndaddr, &naddr, pd->af);
 		if (nport)
 			pd->ndport = nport;
+	}
+
+	return (0);
+}
+
+int
+pf_get_transaddr_af(struct pf_rule *r, struct pf_pdesc *pd,
+    struct pf_src_node **sns)
+{
+	struct pf_addr	ndaddr, nsaddr, naddr;
+	u_int16_t	nport = 0;
+	int		prefixlen = 96;
+
+	if (pf_status.debug >= LOG_NOTICE) {
+		log(LOG_NOTICE, "pf: af-to %s %s, ",
+		    pd->naf == AF_INET ? "inet" : "inet6",
+		    r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr");
+		pf_print_host(&pd->nsaddr, pd->nsport, pd->af);
+		addlog(" -> ");
+		pf_print_host(&pd->ndaddr, pd->ndport, pd->af);
+		addlog("\n");
+	}
+
+	if (r->nat.addr.type == PF_ADDR_NONE)
+		panic("pf_get_transaddr_af: no nat pool for source address");
+
+	/* get source address and port */
+	if (pf_get_sport(pd, r, &nsaddr, &nport,
+	    r->nat.proxy_port[0], r->nat.proxy_port[1], sns)) {
+		DPFPRINTF(LOG_NOTICE,
+		    "pf: af-to NAT proxy port allocation (%u-%u) failed",
+		    r->nat.proxy_port[0],
+		    r->nat.proxy_port[1]);
+		return (-1);
+	}
+	pd->nsport = nport;
+
+	if (pd->proto == IPPROTO_ICMPV6 && pd->naf == AF_INET) {
+		if (pd->dir == PF_IN) {
+			NTOHS(pd->ndport);
+			if (pd->ndport == ICMP6_ECHO_REQUEST)
+				pd->ndport = ICMP_ECHO;
+			else if (pd->ndport == ICMP6_ECHO_REPLY)
+				pd->ndport = ICMP_ECHOREPLY;
+			HTONS(pd->ndport);
+		} else {
+			NTOHS(pd->nsport);
+			if (pd->nsport == ICMP6_ECHO_REQUEST)
+				pd->nsport = ICMP_ECHO;
+			else if (pd->nsport == ICMP6_ECHO_REPLY)
+				pd->nsport = ICMP_ECHOREPLY;
+			HTONS(pd->nsport);
+		}
+	} else if (pd->proto == IPPROTO_ICMP && pd->naf == AF_INET6) {
+		if (pd->dir == PF_IN) {
+			NTOHS(pd->ndport);
+			if (pd->ndport == ICMP_ECHO)
+				pd->ndport = ICMP6_ECHO_REQUEST;
+			else if (pd->ndport == ICMP_ECHOREPLY)
+				pd->ndport = ICMP6_ECHO_REPLY;
+			HTONS(pd->ndport);
+		} else {
+			NTOHS(pd->nsport);
+			if (pd->nsport == ICMP_ECHO)
+				pd->nsport = ICMP6_ECHO_REQUEST;
+			else if (pd->nsport == ICMP_ECHOREPLY)
+				pd->nsport = ICMP6_ECHO_REPLY;
+			HTONS(pd->nsport);
+		}
+	}
+
+	/* get the destination address and port */
+	if (r->rdr.addr.type != PF_ADDR_NONE) {
+		if (pf_map_addr(pd->naf, r, &nsaddr, &naddr, NULL, sns,
+		    &r->rdr, PF_SN_RDR))
+			return (-1);
+		if (r->rdr.proxy_port[0])
+			pd->ndport = htons(r->rdr.proxy_port[0]);
+
+		if (pd->naf == AF_INET) {
+			/* The prefix is the IPv4 rdr address */
+			prefixlen = in_mask2len((struct in_addr *)
+			    &r->rdr.addr.v.a.mask);
+			inet_nat46(pd->naf, &pd->ndaddr,
+			    &ndaddr, &naddr, prefixlen);
+		} else {
+			/* The prefix is the IPv6 rdr address */
+			prefixlen =
+			    in6_mask2len((struct in6_addr *)
+			    &r->rdr.addr.v.a.mask, NULL);
+			inet_nat64(pd->naf, &pd->ndaddr,
+			    &ndaddr, &naddr, prefixlen);
+		}
+	} else {
+		if (pd->naf == AF_INET) {
+			/* The prefix is the IPv6 dst address */
+			prefixlen =
+			    in6_mask2len((struct in6_addr *)
+			    &r->dst.addr.v.a.mask, NULL);
+			if (prefixlen < 32)
+				prefixlen = 96;
+			inet_nat64(pd->naf, &pd->ndaddr,
+			    &ndaddr, &pd->ndaddr, prefixlen);
+		} else {
+			/*
+			 * The prefix is the IPv6 nat address
+			 * (that was stored in pd->nsaddr)
+			 */
+			prefixlen = in6_mask2len((struct in6_addr *)
+			    &r->nat.addr.v.a.mask, NULL);
+			if (prefixlen > 96)
+				prefixlen = 96;
+			inet_nat64(pd->naf, &pd->ndaddr,
+			    &ndaddr, &nsaddr, prefixlen);
+		}
+	}
+
+	PF_ACPY(&pd->nsaddr, &nsaddr, pd->naf);
+	PF_ACPY(&pd->ndaddr, &ndaddr, pd->naf);
+
+	if (pf_status.debug >= LOG_NOTICE) {
+		log(LOG_NOTICE, "pf: af-to %s %s done, prefixlen %d, ",
+		    pd->naf == AF_INET ? "inet" : "inet6",
+		    r->rdr.addr.type == PF_ADDR_NONE ? "nat" : "rdr",
+		    prefixlen);
+		pf_print_host(&pd->nsaddr, pd->nsport, pd->naf);
+		addlog(" -> ");
+		pf_print_host(&pd->ndaddr, pd->ndport, pd->naf);
+		addlog("\n");
 	}
 
 	return (0);
Index: net/pfvar.h
===================================================================
RCS file: /home/cvs/src/sys/net/pfvar.h,v
retrieving revision 1.329
diff -u -p -r1.329 pfvar.h
--- net/pfvar.h	23 Apr 2011 10:00:36 -0000	1.329
+++ net/pfvar.h	10 May 2011 21:06:05 -0000
@@ -61,7 +61,7 @@ struct ip6_hdr;
 enum	{ PF_INOUT, PF_IN, PF_OUT, PF_FWD };
 enum	{ PF_PASS, PF_DROP, PF_SCRUB, PF_NOSCRUB, PF_NAT, PF_NONAT,
 	  PF_BINAT, PF_NOBINAT, PF_RDR, PF_NORDR, PF_SYNPROXY_DROP, PF_DEFER,
-	  PF_MATCH, PF_DIVERT, PF_RT };
+	  PF_MATCH, PF_DIVERT, PF_RT, PF_AFRT };
 enum	{ PF_TRANS_RULESET, PF_TRANS_ALTQ, PF_TRANS_TABLE };
 enum	{ PF_OP_NONE, PF_OP_IRG, PF_OP_EQ, PF_OP_NE, PF_OP_LT,
 	  PF_OP_LE, PF_OP_GT, PF_OP_GE, PF_OP_XRG, PF_OP_RRG };
@@ -627,6 +627,7 @@ struct pf_rule {
 #define PF_STATE_SYNPROXY	0x3
 	u_int8_t		 keep_state;
 	sa_family_t		 af;
+	sa_family_t		 naf;
 	u_int8_t		 proto;
 	u_int8_t		 type;
 	u_int8_t		 code;
@@ -644,7 +645,7 @@ struct pf_rule {
 #define PF_FLUSH		0x01
 #define PF_FLUSH_GLOBAL		0x02
 	u_int8_t		 flush;
-	u_int8_t		 pad2[3];
+	u_int8_t		 pad2[2];
 
 	struct {
 		struct pf_addr		addr;
@@ -704,6 +705,7 @@ struct pf_src_node {
 	u_int32_t		 creation;
 	u_int32_t		 expire;
 	sa_family_t		 af;
+	sa_family_t		 naf;
 	u_int8_t		 type;
 };
 
@@ -781,6 +783,9 @@ struct pf_state_key {
 	struct pf_state_key	*reverse;
 	struct inpcb		*inp;
 };
+#define PF_REVERSED_KEY(key, family)				\
+	((key[PF_SK_WIRE]->af != key[PF_SK_STACK]->af) &&	\
+	 (key[PF_SK_WIRE]->af != (family)))
 
 /* keep synced with struct pf_state, used in RB_FIND */
 struct pf_state_cmp {
@@ -868,7 +873,8 @@ struct pfsync_state_key {
 	struct pf_addr	 addr[2];
 	u_int16_t	 port[2];
 	u_int16_t	 rdomain;
-	u_int8_t	 pad[2];
+	sa_family_t	 af;
+	u_int8_t	 pad[1];
 };
 
 struct pfsync_state {
@@ -1224,8 +1230,10 @@ struct pf_pdesc {
 	u_int16_t	 flags;
 #define PFDESC_IP_REAS	0x0002		/* IP frags would've been reassembled */
 	sa_family_t	 af;
+	sa_family_t	 naf;
 	u_int8_t	 proto;
 	u_int8_t	 tos;
+	u_int8_t	 ttl;
 	u_int8_t	 dir;		/* direction */
 	u_int8_t	 sidx;		/* key index for source */
 	u_int8_t	 didx;		/* key index for destination */
@@ -1764,7 +1772,8 @@ void   *pf_pull_hdr(struct mbuf *, int, 
 void	pf_change_a(void *, u_int16_t *, u_int32_t, u_int8_t);
 int	pflog_packet(struct pfi_kif *, struct mbuf *, sa_family_t, u_int8_t,
 	    u_int8_t, struct pf_rule *, struct pf_rule *, struct pf_ruleset *,
-	    struct pf_pdesc *);
+ 	    struct pf_pdesc *);
+
 void	pf_send_deferred_syn(struct pf_state *);
 int	pf_match_addr(u_int8_t, struct pf_addr *, struct pf_addr *,
 	    struct pf_addr *, sa_family_t);
@@ -1805,6 +1814,7 @@ void	pf_pkt_addr_changed(struct mbuf *);
 int	pf_state_key_attach(struct pf_state_key *, struct pf_state *, int);
 int	pf_translate(struct pf_pdesc *, struct pf_addr *, u_int16_t,
 	    struct pf_addr *, u_int16_t, u_int16_t, int);
+int	pf_translate_af(int, struct mbuf **, int, struct pf_pdesc *);
 
 void	pfr_initialize(void);
 int	pfr_match_addr(struct pfr_ktable *, struct pf_addr *, sa_family_t);
Index: netinet/in.c
===================================================================
RCS file: /home/cvs/src/sys/netinet/in.c,v
retrieving revision 1.65
diff -u -p -r1.65 in.c
--- netinet/in.c	28 Apr 2011 09:56:27 -0000	1.65
+++ netinet/in.c	2 May 2011 13:59:16 -0000
@@ -87,7 +87,6 @@
 
 #ifdef INET
 
-int in_mask2len(struct in_addr *);
 void in_len2mask(struct in_addr *, int);
 int in_lifaddr_ioctl(struct socket *, u_long, caddr_t,
 	struct ifnet *);
Index: netinet/in_var.h
===================================================================
RCS file: /home/cvs/src/sys/netinet/in_var.h,v
retrieving revision 1.16
diff -u -p -r1.16 in_var.h
--- netinet/in_var.h	17 Nov 2010 19:25:49 -0000	1.16
+++ netinet/in_var.h	24 Mar 2011 19:59:08 -0000
@@ -212,6 +212,10 @@ struct	in_multi *in_addmulti(struct in_a
 void	in_delmulti(struct in_multi *);
 void	in_ifscrub(struct ifnet *, struct in_ifaddr *);
 int	in_control(struct socket *, u_long, caddr_t, struct ifnet *);
+
+int	inet_nat64(int, const void *, void *, const void *, u_int8_t);
+int	inet_nat46(int, const void *, void *, const void *, u_int8_t);
+int	in_mask2len(struct in_addr *);
 #endif
 
 
Index: netinet/inet_nat64.c
===================================================================
RCS file: netinet/inet_nat64.c
diff -N netinet/inet_nat64.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ netinet/inet_nat64.c	24 Mar 2011 19:59:08 -0000
@@ -0,0 +1,218 @@
+/*	$OpenBSD$	*/
+/*	$vantronix: inet_nat64.c,v 1.2 2011/02/28 14:57:58 mike Exp $	*/
+
+/*
+ * Copyright (c) 2011 Reyk Floeter <reyk@vantronix.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#ifdef _KERNEL
+#include <sys/mbuf.h>
+#include <netinet/in.h>
+#else
+#include <netinet/in.h>
+#include <errno.h>
+#endif
+
+union inet_nat64_addr {
+	u_int32_t	 u32[4];
+	u_int8_t	 u8[16];
+};
+
+u_int32_t inet_nat64_mask(u_int32_t, u_int32_t, u_int8_t);
+
+int	  inet_nat64(int, const void *, void *, const void *, u_int8_t);
+int	  inet_nat64_inet(const void *, void *, const void *, u_int8_t);
+int	  inet_nat64_inet6(const void *, void *, const void *, u_int8_t);
+
+int	  inet_nat46(int, const void *, void *, const void *, u_int8_t);
+int	  inet_nat46_inet(const void *, void *, const void *, u_int8_t);
+int	  inet_nat46_inet6(const void *, void *, const void *, u_int8_t);
+
+u_int32_t
+inet_nat64_mask(u_int32_t src, u_int32_t pfx, u_int8_t pfxlen)
+{
+	u_int32_t	u32;
+	if (pfxlen == 0)
+		return (src);
+	else if (pfxlen > 32)
+		pfxlen = 32;
+	u32 =
+	    (src & ~htonl(0xffffffff << (32 - pfxlen))) |
+	    (pfx & htonl(0xffffffff << (32 - pfxlen)));
+	return (u32);
+
+}
+
+int
+inet_nat64(int af, const void *src, void *dst,
+    const void *pfx, u_int8_t pfxlen)
+{
+	switch (af) {
+	case AF_INET:
+		return (inet_nat64_inet(src, dst, pfx, pfxlen));
+	case AF_INET6:
+		return (inet_nat64_inet6(src, dst, pfx, pfxlen));
+	default:
+#ifndef _KERNEL
+		errno = EAFNOSUPPORT;
+#endif
+		return (-1);
+	}
+	/* NOTREACHED */
+}
+
+int
+inet_nat64_inet(const void *src, void *dst, const void *pfx, u_int8_t pfxlen)
+{
+	const union inet_nat64_addr	*s = src;
+	const union inet_nat64_addr	*p = pfx;
+	union inet_nat64_addr		*d = dst;
+	int				 i, j;
+
+	switch (pfxlen) {
+	case 32:
+	case 40:
+	case 48:
+	case 56:
+	case 64:
+	case 96:
+		i = pfxlen / 8;
+		break;
+	default:
+		if (pfxlen < 96 || pfxlen > 128) {
+#ifndef _KERNEL
+			errno = EINVAL;
+#endif
+			return (-1);
+		}
+
+		/* as an extension, mask out any other bits */
+		d->u32[0] = inet_nat64_mask(s->u32[3], p->u32[3],
+		    (u_int8_t)(32 - (128 - pfxlen)));
+		return (0);
+	}
+
+	/* fill the octets with the source and skip reserved octet 8 */
+	for (j = 0; j < 4; j++) {
+		if (i == 8)
+			i++;
+		d->u8[j] = s->u8[i++];
+	}
+
+	return (0);
+}
+
+int
+inet_nat64_inet6(const void *src, void *dst, const void *pfx, u_int8_t pfxlen)
+{
+	const union inet_nat64_addr	*s = src;
+	const union inet_nat64_addr	*p = pfx;
+	union inet_nat64_addr		*d = dst;
+	int				 i, j;
+
+	/* first copy the prefix octets to the destination */
+	*d = *p;
+
+	switch (pfxlen) {
+	case 32:
+	case 40:
+	case 48:
+	case 56:
+	case 64:
+	case 96:
+		i = pfxlen / 8;
+		break;
+	default:
+		if (pfxlen < 96 || pfxlen > 128) {
+#ifndef _KERNEL
+			errno = EINVAL;
+#endif
+			return (-1);
+		}
+
+		/* as an extension, mask out any other bits */
+		d->u32[3] = inet_nat64_mask(s->u32[0], p->u32[3],
+		    (u_int8_t)(32 - (128 - pfxlen)));
+		return (0);
+	}
+
+	/* octet 8 is reserved and must be set to zero */
+	d->u8[8] = 0;
+
+	/* fill the other octets with the source and skip octet 8 */
+	for (j = 0; j < 4; j++) {
+		if (i == 8)
+			i++;
+		d->u8[i++] = s->u8[j];
+	}
+
+	return (0);
+}
+
+int
+inet_nat46(int af, const void *src, void *dst,
+    const void *pfx, u_int8_t pfxlen)
+{
+	if (pfxlen > 32) {
+#ifndef _KERNEL
+		errno = EINVAL;
+#endif
+		return (-1);
+	}
+
+	switch (af) {
+	case AF_INET:
+		return (inet_nat46_inet(src, dst, pfx, pfxlen));
+	case AF_INET6:
+		return (inet_nat46_inet6(src, dst, pfx, pfxlen));
+	default:
+#ifndef _KERNEL
+		errno = EAFNOSUPPORT;
+#endif
+		return (-1);
+	}
+	/* NOTREACHED */
+}
+
+int
+inet_nat46_inet(const void *src, void *dst, const void *pfx, u_int8_t pfxlen)
+{
+	const union inet_nat64_addr	*s = src;
+	const union inet_nat64_addr	*p = pfx;
+	union inet_nat64_addr		*d = dst;
+
+	/* set the remaining bits to the source */
+	d->u32[0] = inet_nat64_mask(s->u32[3], p->u32[0], pfxlen);
+
+	return (0);
+}
+
+int
+inet_nat46_inet6(const void *src, void *dst, const void *pfx, u_int8_t pfxlen)
+{
+	const union inet_nat64_addr	*s = src;
+	const union inet_nat64_addr	*p = pfx;
+	union inet_nat64_addr		*d = dst;
+
+	/* set the initial octets to zero */
+	d->u32[0] = d->u32[1] = d->u32[2] = 0;
+
+	/* now set the remaining bits to the source */
+	d->u32[3] = inet_nat64_mask(s->u32[0], p->u32[0], pfxlen);
+
+	return (0);
+}
Index: netinet6/in6.h
===================================================================
RCS file: /home/cvs/src/sys/netinet6/in6.h,v
retrieving revision 1.53
diff -u -p -r1.53 in6.h
--- netinet6/in6.h	2 May 2011 13:48:38 -0000	1.53
+++ netinet6/in6.h	4 May 2011 12:12:24 -0000
@@ -787,6 +787,7 @@ int	in6_addrscope(struct in6_addr *);
 struct	in6_ifaddr *in6_ifawithscope(struct ifnet *, struct in6_addr *);
 extern void in6_if_up(struct ifnet *);
 void 	in6_get_rand_ifid(struct ifnet *, struct in6_addr *);
+int	in6_mask2len(struct in6_addr *, u_char *);
 
 #define	satosin6(sa)	((struct sockaddr_in6 *)(sa))
 #define	sin6tosa(sin6)	((struct sockaddr *)(sin6))
