Index: parse.y
===================================================================
RCS file: /home/cvs/src/sbin/pfctl/parse.y,v
retrieving revision 1.599
diff -u -p -r1.599 parse.y
--- parse.y	6 Apr 2011 13:19:55 -0000	1.599
+++ parse.y	15 Apr 2011 14:13:27 -0000
@@ -227,6 +227,7 @@ struct redirspec {
 	struct redirection      *rdr;
 	struct pool_opts         pool_opts;
 	int			 binat;
+	int			 af;
 };
 
 struct filter_opts {
@@ -238,6 +239,7 @@ struct filter_opts {
 #define FOM_SRCTRACK	0x0010
 #define FOM_MINTTL	0x0020
 #define FOM_MAXMSS	0x0040
+#define FOM_AFTO	0x0080
 #define FOM_SETTOS	0x0100
 #define FOM_SCRUB_TCP	0x0200
 	struct node_uid		*uid;
@@ -466,7 +468,7 @@ int	parseport(char *, struct range *r, i
 %token	STICKYADDRESS MAXSRCSTATES MAXSRCNODES SOURCETRACK GLOBAL RULE
 %token	MAXSRCCONN MAXSRCCONNRATE OVERLOAD FLUSH SLOPPY PFLOW
 %token	TAGGED TAG IFBOUND FLOATING STATEPOLICY STATEDEFAULTS ROUTE SETTOS
-%token	DIVERTTO DIVERTREPLY DIVERTPACKET NATTO RDRTO RECEIVEDON NE LE GE
+%token	DIVERTTO DIVERTREPLY DIVERTPACKET NATTO AFTO RDRTO RECEIVEDON NE LE GE
 %token	<v.string>		STRING
 %token	<v.number>		NUMBER
 %token	<v.i>			PORTBINARY
@@ -918,6 +920,7 @@ anchorrule	: ANCHOR anchorname dir quick
 
 			decide_address_family($8.src.host, &r.af);
 			decide_address_family($8.dst.host, &r.af);
+			r.naf = r.af;
 
 			expand_rule(&r, 0, $5, NULL, NULL, NULL, $7, $8.src_os,
 			    $8.src.host, $8.src.port, $8.dst.host, $8.dst.port,
@@ -1731,7 +1734,20 @@ pfrule		: action dir logquick interface 
 			if ($8.marker & FOM_SCRUB_TCP)
 				r.scrub_flags |= PFSTATE_SCRUB_TCP;
 
+			if ($8.marker & FOM_AFTO) {
+				if (!$5) {
+					yyerror("must indicate source address "
+					    "family with af-to");
+					YYERROR;
+				}
+				if ($5 == $8.nat.af) {
+					yyerror("incorrect address family "
+					    "translation");
+					YYERROR;
+				}
+			}
 			r.af = $5;
+
 			if ($8.tag)
 				if (strlcpy(r.tagname, $8.tag,
 				    PF_TAG_NAME_SIZE) >= PF_TAG_NAME_SIZE) {
@@ -2015,6 +2031,7 @@ pfrule		: action dir logquick interface 
 
 			decide_address_family($7.src.host, &r.af);
 			decide_address_family($7.dst.host, &r.af);
+			r.naf = r.af;
 
 			if ($8.route.rt) {
 				if (!r.direction) {
@@ -2273,6 +2290,61 @@ filter_opt	: USER uids {
 			memcpy(&filter_opts.nat.pool_opts, &$3,
 			    sizeof(filter_opts.nat.pool_opts));
 		}
+		| AFTO af redirpool {
+			if (filter_opts.nat.rdr) {
+				yyerror("cannot respecify af-to");
+				YYERROR;
+			}
+			if ($2 == 0) {
+				yyerror("no address family specified");
+				YYERROR;
+			}
+			if ($3->host->af && $3->host->af != $2) {
+				yyerror("af-to addresses must be in the "
+				    "target address family");
+				YYERROR;
+			}
+			filter_opts.nat.af = $2;
+			filter_opts.nat.rdr = $3;
+			bzero(&filter_opts.nat.pool_opts,
+			    sizeof(filter_opts.nat.pool_opts));
+			if ($3->host->addr.type == PF_ADDR_DYNIFTL)
+				filter_opts.nat.pool_opts.type =
+				    PF_POOL_ROUNDROBIN;
+			filter_opts.rdr.rdr =
+			    calloc(1, sizeof(struct redirection));
+			bzero(&filter_opts.rdr.pool_opts,
+			    sizeof(filter_opts.rdr.pool_opts));
+			filter_opts.marker |= FOM_AFTO;
+		}
+		| AFTO af '(' redirpool redirpool ')' {
+			if (filter_opts.nat.rdr) {
+				yyerror("cannot respecify af-to");
+				YYERROR;
+			}
+			if ($2 == 0) {
+				yyerror("no address family specified");
+				YYERROR;
+			}
+			if (($4->host->af && $4->host->af != $2) ||
+			    ($5->host->af && $5->host->af != $2)) {
+				yyerror("af-to addresses must be in the "
+				    "target address family");
+				YYERROR;
+			}
+			filter_opts.nat.af = $2;
+			filter_opts.nat.rdr = $4;
+			bzero(&filter_opts.nat.pool_opts,
+			    sizeof(filter_opts.nat.pool_opts));
+			if ($4->host->addr.type == PF_ADDR_DYNIFTL)
+				filter_opts.nat.pool_opts.type =
+				    PF_POOL_ROUNDROBIN;
+			filter_opts.rdr.af = $2;
+			filter_opts.rdr.rdr = $5;
+			bzero(&filter_opts.rdr.pool_opts,
+			    sizeof(filter_opts.rdr.pool_opts));
+			filter_opts.marker |= FOM_AFTO;
+		}
 		| RDRTO redirpool pool_opts {
 			if (filter_opts.rdr.rdr) {
 				yyerror("cannot respecify rdr-to");
@@ -4045,6 +4117,10 @@ rule_consistent(struct pf_rule *r, int a
 			   "must not be used on match rules");
 			problems++;
 		}
+		if (r->nat.addr.type != PF_ADDR_NONE && r->naf != r->af) {
+			yyerror("af-to is not supported on match rules");
+			problems++;
+		}
 	}
 	return (-problems);
 }
@@ -4544,6 +4620,8 @@ collapse_redirspec(struct pf_pool *rpool
 	struct pf_rule_addr ra;
 	int	i = 0;
 
+	if (rs && rs->af)
+		r->naf = rs->af;
 
 	if (!rs || !rs->rdr || rs->rdr->host == NULL) {
 		rpool->addr.type = PF_ADDR_NONE;
@@ -4552,7 +4630,7 @@ collapse_redirspec(struct pf_pool *rpool
 
 	/* count matching addresses */
 	for (h = rs->rdr->host; h != NULL; h = h->next) {
-		if (!r->af || !h->af || h->af == r->af) {
+		if (!r->af || !h->af || rs->af || h->af == r->af) {
 			i++;
 			if (h->af && !r->af)
 				r->af = h->af;
@@ -4565,7 +4643,7 @@ collapse_redirspec(struct pf_pool *rpool
 		return (1);
 	} else if (i == 1) {	/* only one address */
 		for (h = rs->rdr->host; h != NULL; h = h->next)
-			if (!h->af || !r->af || r->af == h->af)
+			if (!h->af || !r->af || rs->af || r->af == h->af)
 				break;
 		rpool->addr = h->addr;
 		if (!allow_if && h->ifname) {
@@ -4585,7 +4663,7 @@ collapse_redirspec(struct pf_pool *rpool
 			return (1);
 		}
 		for (h = rs->rdr->host; h != NULL; h = h->next) {
-			if (r->af != h->af)
+			if (!rs->af && r->af != h->af)
 				continue;
 			if (h->addr.type != PF_ADDR_ADDRMASK &&
 			    h->addr.type != PF_ADDR_NONE) {
@@ -5040,6 +5118,7 @@ lookup(char *s)
 {
 	/* this has to be sorted always */
 	static const struct keywords keywords[] = {
+		{ "af-to",		AFTO},
 		{ "all",		ALL},
 		{ "allow-opts",		ALLOWOPTS},
 		{ "altq",		ALTQ},
Index: pf_print_state.c
===================================================================
RCS file: /home/cvs/src/sbin/pfctl/pf_print_state.c,v
retrieving revision 1.58
diff -u -p -r1.58 pf_print_state.c
--- pf_print_state.c	12 Nov 2010 13:14:41 -0000	1.58
+++ pf_print_state.c	6 Apr 2011 15:59:24 -0000
@@ -213,6 +213,8 @@ print_state(struct pfsync_state *s, int 
 	struct pfsync_state_key *sk, *nk;
 	struct protoent *p;
 	int min, sec;
+	int afto = (s->key[PF_SK_STACK].af != s->key[PF_SK_WIRE].af);
+	int idx;
 
 	if (s->direction == PF_OUT) {
 		src = &s->src;
@@ -235,24 +237,28 @@ print_state(struct pfsync_state *s, int 
 	else
 		printf("%u ", s->proto);
 
-	print_host(&nk->addr[1], nk->port[1], s->af, nk->rdomain, opts);
-	if (PF_ANEQ(&nk->addr[1], &sk->addr[1], s->af) ||
+	print_host(&nk->addr[1], nk->port[1], nk->af, nk->rdomain, opts);
+	if (nk->af != sk->af || PF_ANEQ(&nk->addr[1], &sk->addr[1], nk->af) ||
 	    nk->port[1] != sk->port[1] ||
 	    nk->rdomain != sk->rdomain) {
+		idx = afto ? 0 : 1;
 		printf(" (");
-		print_host(&sk->addr[1], sk->port[1], s->af, sk->rdomain, opts);
+		print_host(&sk->addr[idx], sk->port[idx], sk->af,
+		    sk->rdomain, opts);
 		printf(")");
 	}
-	if (s->direction == PF_OUT)
+	if (s->direction == PF_OUT || (afto && s->direction == PF_IN))
 		printf(" -> ");
 	else
 		printf(" <- ");
-	print_host(&nk->addr[0], nk->port[0], s->af, nk->rdomain, opts);
-	if (PF_ANEQ(&nk->addr[0], &sk->addr[0], s->af) ||
+	print_host(&nk->addr[0], nk->port[0], nk->af, nk->rdomain, opts);
+	if (nk->af != sk->af || PF_ANEQ(&nk->addr[0], &sk->addr[0], nk->af) ||
 	    nk->port[0] != sk->port[0] ||
 	    nk->rdomain != sk->rdomain) {
+		idx = afto ? 1 : 0;
 		printf(" (");
-		print_host(&sk->addr[0], sk->port[0], s->af, sk->rdomain, opts);
+		print_host(&sk->addr[idx], sk->port[idx], sk->af,
+		    sk->rdomain, opts);
 		printf(")");
 	}
 
Index: pfctl_parser.c
===================================================================
RCS file: /home/cvs/src/sbin/pfctl/pfctl_parser.c,v
retrieving revision 1.275
diff -u -p -r1.275 pfctl_parser.c
--- pfctl_parser.c	6 Apr 2011 13:19:55 -0000	1.275
+++ pfctl_parser.c	7 Apr 2011 13:33:14 -0000
@@ -442,7 +442,7 @@ print_fromto(struct pf_rule_addr *src, p
 
 void
 print_pool(struct pf_pool *pool, u_int16_t p1, u_int16_t p2,
-    sa_family_t af, int id, int verbose)
+    sa_family_t af, int id, int verbose, int hideopts)
 {
 	if (pool->ifname[0]) {
 		if (!PF_AZERO(&pool->addr.v.a.addr, af)) {
@@ -472,6 +472,8 @@ print_pool(struct pf_pool *pool, u_int16
 	default:
 		break;
 	}
+	if (hideopts)
+		return;
 	switch (pool->opts & PF_POOL_TYPEMASK) {
 	case PF_POOL_NONE:
 		break;
@@ -641,7 +643,8 @@ print_src_node(struct pf_src_node *sn, i
 		else
 			printf(" ??? (%u) ", sn->type);
 		aw.v.a.addr = sn->raddr;
-		print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
+		print_addr(&aw, sn->naf ? sn->naf : sn->af,
+		    opts & PF_OPT_VERBOSE2);
 	}
 
 	printf(" ( states %u, connections %u, rate %u.%u/%us )\n", sn->states,
@@ -1045,15 +1048,31 @@ print_rule(struct pf_rule *r, const char
 	}
 	if (r->divert_packet.port)
 		printf(" divert-packet port %u", ntohs(r->divert_packet.port));
-	if (!anchor_call[0] && r->nat.addr.type != PF_ADDR_NONE) {
+
+	if (!anchor_call[0] && r->nat.addr.type != PF_ADDR_NONE &&
+	    r->naf != r->af) {
+		printf(" af-to %s ", r->naf == AF_INET ? "inet" : "inet6");
+		if (r->rdr.addr.type != PF_ADDR_NONE)
+			printf("(");
+		print_pool(&r->nat, r->nat.proxy_port[0],
+		    r->nat.proxy_port[1], r->naf ? r->naf : r->af,
+		    PF_POOL_NAT, verbose, 1);
+		if (r->rdr.addr.type != PF_ADDR_NONE) {
+			printf(" ");
+			print_pool(&r->rdr, r->rdr.proxy_port[0],
+			    r->rdr.proxy_port[1], r->naf ? r->naf : r->af,
+			    PF_POOL_RDR, verbose, 1);
+			printf(")");
+		}
+	} else if (!anchor_call[0] && r->nat.addr.type != PF_ADDR_NONE) {
 		printf (" nat-to ");
 		print_pool(&r->nat, r->nat.proxy_port[0],
-		    r->nat.proxy_port[1], r->af, PF_POOL_NAT, verbose);
-	}
-	if (!anchor_call[0] && r->rdr.addr.type != PF_ADDR_NONE) {
+		    r->nat.proxy_port[1], r->naf ? r->naf : r->af,
+		    PF_POOL_NAT, verbose, 0);
+	} else if (!anchor_call[0] && r->rdr.addr.type != PF_ADDR_NONE) {
 		printf (" rdr-to ");
 		print_pool(&r->rdr, r->rdr.proxy_port[0],
-		    r->rdr.proxy_port[1], r->af, PF_POOL_RDR, verbose);
+		    r->rdr.proxy_port[1], r->af, PF_POOL_RDR, verbose, 0);
 	}
 	if (r->rt) {
 		if (r->rt == PF_ROUTETO)
@@ -1063,7 +1082,7 @@ print_rule(struct pf_rule *r, const char
 		else if (r->rt == PF_DUPTO)
 			printf(" dup-to");
 		printf(" ");
-		print_pool(&r->route, 0, 0, r->af, PF_POOL_ROUTE, verbose);
+		print_pool(&r->route, 0, 0, r->af, PF_POOL_ROUTE, verbose, 0);
 	}
 }
 
Index: pfctl_parser.h
===================================================================
RCS file: /home/cvs/src/sbin/pfctl/pfctl_parser.h,v
retrieving revision 1.95
diff -u -p -r1.95 pfctl_parser.h
--- pfctl_parser.h	6 Apr 2011 13:19:55 -0000	1.95
+++ pfctl_parser.h	7 Apr 2011 13:33:14 -0000
@@ -214,7 +214,8 @@ int	parse_config(char *, struct pfctl *)
 int	parse_flags(char *);
 int	pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *);
 
-void	print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int, int);
+void	print_pool(struct pf_pool *, u_int16_t, u_int16_t, sa_family_t, int,
+	    int, int);
 void	print_src_node(struct pf_src_node *, int);
 void	print_rule(struct pf_rule *, const char *, int);
 void	print_tabledef(const char *, int, int, struct node_tinithead *);
