git: d2761422eb0a - main - pf: Use different address family for source and redirection address

From: Kajetan Staszkiewicz <ks_at_FreeBSD.org>
Date: Fri, 01 Aug 2025 10:12:50 UTC
The branch main has been updated by ks:

URL: https://cgit.FreeBSD.org/src/commit/?id=d2761422eb0a811976390743921b3f15e9d8331a

commit d2761422eb0a811976390743921b3f15e9d8331a
Author:     Kajetan Staszkiewicz <ks@FreeBSD.org>
AuthorDate: 2025-07-31 14:28:30 +0000
Commit:     Kajetan Staszkiewicz <ks@FreeBSD.org>
CommitDate: 2025-08-01 10:11:30 +0000

    pf: Use different address family for source and redirection address
    
    The function pf_map_addr() and source tracking operate on a single
    address family. This made sense before introducing address family
    translation. When combining af-to with route-to or with sticky-address,
    the next-hop or the NAT address are of different address family than
    the source address. For example in NAT64 scenaro an IPv6 source address
    is translated to an IPv4 address and routed over IPv4 gateway.
    
    Make source nodes dual-AF, that is have a separate source AF and
    redirection AF. Store route AF in struct pf_kstate, export it to pfctl.
    When loading rules with redirection pools with pfctl store address
    family of each address. When printing states don't deduce next-hop's
    address family from af-to, use the one stored in state.
    
    Reviewed by:    kp
    Approved by:    kp
    Sponsored by:   InnoGames GmbH
    Differential Revision:  https://reviews.freebsd.org/D51659
---
 lib/libpfctl/libpfctl.c           |   3 +-
 lib/libpfctl/libpfctl.h           |  16 ++++-
 sbin/pfctl/parse.y                |  14 ++--
 sbin/pfctl/pf_print_state.c       |  13 ++--
 sbin/pfctl/pfctl.c                |  25 +++----
 sbin/pfctl/pfctl_parser.c         |  28 +++-----
 sbin/pfctl/pfctl_parser.h         |   4 +-
 sys/net/pfvar.h                   |  15 ++--
 sys/netpfil/pf/if_pfsync.c        |  12 ++++
 sys/netpfil/pf/pf.c               |  21 +++---
 sys/netpfil/pf/pf_ioctl.c         |   2 +
 sys/netpfil/pf/pf_lb.c            | 139 +++++++++++++++++++++++---------------
 sys/netpfil/pf/pf_nl.c            |   5 +-
 sys/netpfil/pf/pf_nl.h            |   3 +-
 tests/sys/netpfil/pf/nat64.sh     |   9 +++
 tests/sys/netpfil/pf/src_track.sh |  61 +++++++++++++++++
 16 files changed, 252 insertions(+), 118 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index d8e60075e103..104777352d8b 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -1940,6 +1940,7 @@ static struct snl_attr_parser ap_state[] = {
 	{ .type = PF_ST_RT, .off = _OUT(rt), .cb = snl_attr_get_uint8 },
 	{ .type = PF_ST_RT_IFNAME, .off = _OUT(rt_ifname), .cb = snl_attr_store_ifname },
 	{ .type = PF_ST_SRC_NODE_FLAGS, .off = _OUT(src_node_flags), .cb = snl_attr_get_uint8 },
+	{ .type = PF_ST_RT_AF, .off = _OUT(rt_af), .cb = snl_attr_get_uint8 },
 };
 #undef _IN
 #undef _OUT
@@ -3043,7 +3044,7 @@ static struct snl_attr_parser ap_srcnode[] = {
 	{ .type = PF_SN_CREATION, .off = _OUT(creation), .cb = snl_attr_get_uint64 },
 	{ .type = PF_SN_EXPIRE, .off = _OUT(expire), .cb = snl_attr_get_uint64 },
 	{ .type = PF_SN_CONNECTION_RATE, .off = _OUT(conn_rate), .arg = &pfctl_threshold_parser, .cb = snl_attr_get_nested },
-	{ .type = PF_SN_NAF, .off = _OUT(naf), .cb = snl_attr_get_uint8 },
+	{ .type = PF_SN_RAF, .off = _OUT(raf), .cb = snl_attr_get_uint8 },
 	{ .type = PF_SN_NODE_TYPE, .off = _OUT(type), .cb = snl_attr_get_uint8 },
 };
 #undef _OUT
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 98a80758ca74..116f9243fda9 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -143,9 +143,18 @@ struct pfctl_eth_anchor {
 	int				 match;	/* XXX: used for pfctl black magic */
 };
 
+struct pfctl_pooladdr {
+	struct pf_addr_wrap		 addr;
+	TAILQ_ENTRY(pfctl_pooladdr)	 entries;
+	char				 ifname[IFNAMSIZ];
+	sa_family_t		 	 af;
+};
+
+TAILQ_HEAD(pfctl_palist, pfctl_pooladdr);
+
 struct pfctl_pool {
-	struct pf_palist	 list;
-	struct pf_pooladdr	*cur;
+	struct pfctl_palist	 list;
+	struct pfctl_pooladdr	*cur;
 	struct pf_poolhashkey	 key;
 	struct pf_addr		 counter;
 	struct pf_mape_portset	 mape;
@@ -383,6 +392,7 @@ struct pfctl_state {
 	uint8_t			 set_prio[2];
 	uint8_t			 rt;
 	char			 rt_ifname[IFNAMSIZ];
+	sa_family_t		 rt_af;
 	uint8_t			 src_node_flags;
 };
 
@@ -414,7 +424,7 @@ struct pfctl_src_node {
 	uint32_t		states;
 	uint32_t		conn;
 	sa_family_t		af;
-	sa_family_t		naf;
+	sa_family_t		raf;
 	uint8_t			ruletype;
 	uint64_t		creation;
 	uint64_t		expire;
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index ddb0f5dc6565..00c36b218055 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -4852,7 +4852,7 @@ binatrule	: no BINAT natpasslog interface af proto FROM ipspec toipspec tag
 		    tagged rtable binat_redirspec
 		{
 			struct pfctl_rule	binat;
-			struct pf_pooladdr	*pa;
+			struct pfctl_pooladdr	*pa;
 
 			if (check_rulestate(PFCTL_STATE_NAT))
 				YYERROR;
@@ -5011,11 +5011,12 @@ binatrule	: no BINAT natpasslog interface af proto FROM ipspec toipspec tag
 					YYERROR;
 				}
 
-				pa = calloc(1, sizeof(struct pf_pooladdr));
+				pa = calloc(1, sizeof(struct pfctl_pooladdr));
 				if (pa == NULL)
 					err(1, "binat: calloc");
 				pa->addr = $13->host->addr;
 				pa->ifname[0] = 0;
+				pa->af = $13->host->af;
 				TAILQ_INSERT_TAIL(&binat.rdr.list,
 				    pa, entries);
 
@@ -6115,7 +6116,7 @@ int
 apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs)
 {
 	struct node_host	*h;
-	struct pf_pooladdr	*pa;
+	struct pfctl_pooladdr	*pa;
 
 	if (rs == NULL)
 		return 0;
@@ -6155,10 +6156,11 @@ apply_redirspec(struct pfctl_pool *rpool, struct redirspec *rs)
 		    sizeof(struct pf_poolhashkey));
 
 	for (h = rs->host; h != NULL; h = h->next) {
-		pa = calloc(1, sizeof(struct pf_pooladdr));
+		pa = calloc(1, sizeof(struct pfctl_pooladdr));
 		if (pa == NULL)
 			err(1, "%s: calloc", __func__);
 		pa->addr = h->addr;
+		pa->af = h->af;
 		if (h->ifname != NULL) {
 			if (strlcpy(pa->ifname, h->ifname,
 			    sizeof(pa->ifname)) >= sizeof(pa->ifname))
@@ -6175,7 +6177,7 @@ int
 check_binat_redirspec(struct node_host *src_host, struct pfctl_rule *r,
     sa_family_t af)
 {
-	struct pf_pooladdr	*nat_pool = TAILQ_FIRST(&(r->nat.list));
+	struct pfctl_pooladdr	*nat_pool = TAILQ_FIRST(&(r->nat.list));
 	int			error = 0;
 
 	/* XXX: FreeBSD allows syntax like "{ host1 host2 }" for redirection
@@ -6248,7 +6250,7 @@ add_binat_rdr_rule(
 
 	/*
 	 * We're copying the whole rule, but we must re-init redir pools.
-	 * FreeBSD uses lists of pf_pooladdr, we can't just overwrite them.
+	 * FreeBSD uses lists of pfctl_pooladdr, we can't just overwrite them.
 	 */
 	bcopy(binat_rule, rdr_rule, sizeof(struct pfctl_rule));
 	TAILQ_INIT(&(rdr_rule->rdr.list));
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
index 5abdc2de419d..417ff70de975 100644
--- a/sbin/pfctl/pf_print_state.c
+++ b/sbin/pfctl/pf_print_state.c
@@ -113,10 +113,11 @@ print_addr(struct pf_addr_wrap *addr, sa_family_t af, int verbose)
 	if (addr->type != PF_ADDR_RANGE &&
 	    !(PF_AZERO(&addr->v.a.addr, AF_INET6) &&
 	    PF_AZERO(&addr->v.a.mask, AF_INET6))) {
-		int bits = unmask(&addr->v.a.mask);
-
-		if (bits < (af == AF_INET ? 32 : 128))
-			printf("/%d", bits);
+		if (af == AF_INET || af == AF_INET6) {
+			int bits = unmask(&addr->v.a.mask);
+			if (bits < (af == AF_INET ? 32 : 128))
+				printf("/%d", bits);
+		}
 	}
 }
 
@@ -228,7 +229,6 @@ print_state(struct pfctl_state *s, int opts)
 	struct pfctl_state_key *key, *sk, *nk;
 	const char *protoname;
 	int min, sec;
-	sa_family_t af;
 	uint8_t proto;
 	int afto = (s->key[PF_SK_STACK].af != s->key[PF_SK_WIRE].af);
 	int idx;
@@ -242,7 +242,6 @@ print_state(struct pfctl_state *s, int opts)
 	key = s->key;
 #endif
 
-	af = s->key[PF_SK_WIRE].af;
 	proto = s->key[PF_SK_WIRE].proto;
 
 	if (s->direction == PF_OUT) {
@@ -430,7 +429,7 @@ print_state(struct pfctl_state *s, int opts)
 				default:
 					printf(" gateway: ");
 			}
-			print_host(&s->rt_addr, 0, af, opts);
+			print_host(&s->rt_addr, 0, s->rt_af, opts);
 			if (s->rt_ifname[0])
 				printf("@%s", s->rt_ifname);
 		}
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 2015e0a09549..9aa50a73ba04 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1038,7 +1038,7 @@ pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr,
     u_int32_t ticket, int r_action, const char *anchorname, int which)
 {
 	struct pfioc_pooladdr pp;
-	struct pf_pooladdr *pa;
+	struct pfctl_pooladdr *pa;
 	u_int32_t pnr, mpnr;
 	int ret;
 
@@ -1054,10 +1054,11 @@ pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr,
 			warnc(ret, "DIOCGETADDR");
 			return (-1);
 		}
-		pa = calloc(1, sizeof(struct pf_pooladdr));
+		pa = calloc(1, sizeof(struct pfctl_pooladdr));
 		if (pa == NULL)
 			err(1, "calloc");
-		bcopy(&pp.addr, pa, sizeof(struct pf_pooladdr));
+		bcopy(&pp.addr, pa, sizeof(struct pfctl_pooladdr));
+		pa->af = pp.af;
 		TAILQ_INSERT_TAIL(&pool->list, pa, entries);
 	}
 
@@ -1067,7 +1068,7 @@ pfctl_get_pool(int dev, struct pfctl_pool *pool, u_int32_t nr,
 void
 pfctl_move_pool(struct pfctl_pool *src, struct pfctl_pool *dst)
 {
-	struct pf_pooladdr *pa;
+	struct pfctl_pooladdr *pa;
 
 	while ((pa = TAILQ_FIRST(&src->list)) != NULL) {
 		TAILQ_REMOVE(&src->list, pa, entries);
@@ -1078,7 +1079,7 @@ pfctl_move_pool(struct pfctl_pool *src, struct pfctl_pool *dst)
 void
 pfctl_clear_pool(struct pfctl_pool *pool)
 {
-	struct pf_pooladdr *pa;
+	struct pfctl_pooladdr *pa;
 
 	while ((pa = TAILQ_FIRST(&pool->list)) != NULL) {
 		TAILQ_REMOVE(&pool->list, pa, entries);
@@ -1794,14 +1795,14 @@ pfctl_show_creators(int opts)
 
 /* callbacks for rule/nat/rdr/addr */
 int
-pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, sa_family_t af, int which)
+pfctl_add_pool(struct pfctl *pf, struct pfctl_pool *p, int which)
 {
-	struct pf_pooladdr *pa;
+	struct pfctl_pooladdr *pa;
 	int ret;
 
-	pf->paddr.af = af;
 	TAILQ_FOREACH(pa, &p->list, entries) {
-		memcpy(&pf->paddr.addr, pa, sizeof(struct pf_pooladdr));
+		memcpy(&pf->paddr.addr, pa, sizeof(struct pfctl_pooladdr));
+		pf->paddr.af = pa->af;
 		if ((pf->opts & PF_OPT_NOACTION) == 0) {
 			if ((ret = pfctl_add_addr(pf->h, &pf->paddr, which)) != 0)
 				errc(1, ret, "DIOCADDADDR");
@@ -2165,11 +2166,11 @@ pfctl_load_rule(struct pfctl *pf, char *path, struct pfctl_rule *r, int depth)
 				errc(1, error, "DIOCBEGINADDRS");
 		}
 
-		if (pfctl_add_pool(pf, &r->rdr, r->af, PF_RDR))
+		if (pfctl_add_pool(pf, &r->rdr, PF_RDR))
 			return (1);
-		if (pfctl_add_pool(pf, &r->nat, r->naf ? r->naf : r->af, PF_NAT))
+		if (pfctl_add_pool(pf, &r->nat, PF_NAT))
 			return (1);
-		if (pfctl_add_pool(pf, &r->route, r->af, PF_RT))
+		if (pfctl_add_pool(pf, &r->route, PF_RT))
 			return (1);
 		error = pfctl_add_rule_h(pf->h, r, anchor, name, ticket,
 		    pf->paddr.ticket);
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index f2eb75135609..18b78a150c28 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -429,10 +429,9 @@ print_fromto(struct pf_rule_addr *src, pf_osfp_t osfp, struct pf_rule_addr *dst,
 }
 
 void
-print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2,
-    sa_family_t af, int id)
+print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2, int id)
 {
-	struct pf_pooladdr	*pooladdr;
+	struct pfctl_pooladdr	*pooladdr;
 
 	if ((TAILQ_FIRST(&pool->list) != NULL) &&
 	    TAILQ_NEXT(TAILQ_FIRST(&pool->list), entries) != NULL)
@@ -442,15 +441,15 @@ print_pool(struct pfctl_pool *pool, u_int16_t p1, u_int16_t p2,
 		case PF_NAT:
 		case PF_RDR:
 		case PF_BINAT:
-			print_addr(&pooladdr->addr, af, 0);
+			print_addr(&pooladdr->addr, pooladdr->af, 0);
 			break;
 		case PF_PASS:
 		case PF_MATCH:
-			if (PF_AZERO(&pooladdr->addr.v.a.addr, af))
+			if (PF_AZERO(&pooladdr->addr.v.a.addr, pooladdr->af))
 				printf("%s", pooladdr->ifname);
 			else {
 				printf("(%s ", pooladdr->ifname);
-				print_addr(&pooladdr->addr, af, 0);
+				print_addr(&pooladdr->addr, pooladdr->af, 0);
 				printf(")");
 			}
 			break;
@@ -674,7 +673,7 @@ print_src_node(struct pfctl_src_node *sn, int opts)
 	print_addr(&aw, sn->af, opts & PF_OPT_VERBOSE2);
 	printf(" -> ");
 	aw.v.a.addr = sn->raddr;
-	print_addr(&aw, sn->naf ? sn->naf : sn->af, opts & PF_OPT_VERBOSE2);
+	print_addr(&aw, sn->raf, opts & PF_OPT_VERBOSE2);
 	printf(" ( states %u, connections %u, rate %u.%u/%us )\n", sn->states,
 	    sn->conn, sn->conn_rate.count / 1000,
 	    (sn->conn_rate.count % 1000) / 100, sn->conn_rate.seconds);
@@ -952,10 +951,7 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
 		else if (r->rt == PF_DUPTO)
 			printf(" dup-to");
 		printf(" ");
-		print_pool(&r->rdr, 0, 0, r->af, PF_PASS);
-		print_pool(&r->route, 0, 0,
-		    r->rule_flag & PFRULE_AFTO && r->rt != PF_REPLYTO ? r->naf : r->af,
-		    PF_PASS);
+		print_pool(&r->route, 0, 0, PF_PASS);
 	}
 	if (r->af) {
 		if (r->af == AF_INET)
@@ -1252,17 +1248,16 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
 	if (r->action == PF_NAT || r->action == PF_BINAT || r->action == PF_RDR) {
 		printf(" -> ");
 		print_pool(&r->rdr, r->rdr.proxy_port[0],
-		    r->rdr.proxy_port[1], r->af, r->action);
+		    r->rdr.proxy_port[1], r->action);
 	} else {
 		if (!TAILQ_EMPTY(&r->nat.list)) {
 			if (r->rule_flag & PFRULE_AFTO) {
-				printf(" af-to %s from ", r->naf == AF_INET ? "inet" : "inet6");
+				printf(" af-to %s from ", r->naf == AF_INET ? "inet" : (r->naf == AF_INET6 ? "inet6" : "? "));
 			} else {
 				printf(" nat-to ");
 			}
 			print_pool(&r->nat, r->nat.proxy_port[0],
-			    r->nat.proxy_port[1], r->naf ? r->naf : r->af,
-			    PF_NAT);
+			    r->nat.proxy_port[1], PF_NAT);
 		}
 		if (!TAILQ_EMPTY(&r->rdr.list)) {
 			if (r->rule_flag & PFRULE_AFTO) {
@@ -1271,8 +1266,7 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
 				printf(" rdr-to ");
 			}
 			print_pool(&r->rdr, r->rdr.proxy_port[0],
-			    r->rdr.proxy_port[1], r->naf ? r->naf : r->af,
-			    PF_RDR);
+			    r->rdr.proxy_port[1], PF_RDR);
 		}
 	}
 }
diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h
index 7a3c0c2a523f..721950967661 100644
--- a/sbin/pfctl/pfctl_parser.h
+++ b/sbin/pfctl/pfctl_parser.h
@@ -286,7 +286,7 @@ int	pfctl_append_rule(struct pfctl *, struct pfctl_rule *, const char *);
 int	pfctl_append_eth_rule(struct pfctl *, struct pfctl_eth_rule *,
 	    const char *);
 int	pfctl_add_altq(struct pfctl *, struct pf_altq *);
-int	pfctl_add_pool(struct pfctl *, struct pfctl_pool *, sa_family_t, int);
+int	pfctl_add_pool(struct pfctl *, struct pfctl_pool *, int);
 void	pfctl_move_pool(struct pfctl_pool *, struct pfctl_pool *);
 void	pfctl_clear_pool(struct pfctl_pool *);
 
@@ -304,7 +304,7 @@ int	parse_config(char *, struct pfctl *);
 int	parse_flags(char *);
 int	pfctl_load_anchors(int, struct pfctl *, struct pfr_buffer *);
 
-void	print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, sa_family_t, int);
+void	print_pool(struct pfctl_pool *, u_int16_t, u_int16_t, int);
 void	print_src_node(struct pfctl_src_node *, int);
 void	print_eth_rule(struct pfctl_eth_rule *, const char *, int);
 void	print_rule(struct pfctl_rule *, const char *, int, int);
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index d55afe750869..8dae95c2cc2e 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -629,6 +629,7 @@ struct pf_kpooladdr {
 	struct pf_addr_wrap		 addr;
 	TAILQ_ENTRY(pf_kpooladdr)	 entries;
 	char				 ifname[IFNAMSIZ];
+	sa_family_t		 	 af;
 	struct pfi_kkif			*kif;
 };
 
@@ -656,6 +657,7 @@ struct pf_rule_actions {
 	uint16_t	 max_mss;
 	uint16_t	 dnpipe;
 	uint16_t	 dnrpipe;	/* Reverse direction pipe */
+	sa_family_t	 rt_af;
 	uint8_t		 log;
 	uint8_t		 set_tos;
 	uint8_t		 min_ttl;
@@ -911,7 +913,7 @@ struct pf_ksrc_node {
 	u_int32_t		 creation;
 	u_int32_t		 expire;
 	sa_family_t		 af;
-	sa_family_t		 naf;
+	sa_family_t		 raf;
 	u_int8_t		 ruletype;
 	pf_sn_types_t		 type;
 	struct mtx		*lock;
@@ -2717,14 +2719,15 @@ int			 pf_step_out_of_keth_anchor(struct pf_keth_anchor_stackframe *,
 			    struct pf_keth_rule **, struct pf_keth_rule **,
 			    int *);
 
-u_short			 pf_map_addr(u_int8_t, struct pf_krule *,
+u_short			 pf_map_addr(sa_family_t, struct pf_krule *,
 			    struct pf_addr *, struct pf_addr *,
-			    struct pfi_kkif **nkif, struct pf_addr *,
-			    struct pf_kpool *);
+			    struct pfi_kkif **nkif, sa_family_t *,
+			    struct pf_addr *, struct pf_kpool *);
 u_short			 pf_map_addr_sn(u_int8_t, struct pf_krule *,
 			    struct pf_addr *, struct pf_addr *,
-			    struct pfi_kkif **nkif, struct pf_addr *,
-			    struct pf_kpool *, pf_sn_types_t);
+			    sa_family_t *, struct pfi_kkif **nkif,
+			    struct pf_addr *, struct pf_kpool *,
+			    pf_sn_types_t);
 int			 pf_get_transaddr_af(struct pf_krule *,
 			    struct pf_pdesc *);
 u_short			 pf_get_translation(struct pf_test_ctx *);
diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c
index ee10a997c977..e34c08c8c4db 100644
--- a/sys/netpfil/pf/if_pfsync.c
+++ b/sys/netpfil/pf/if_pfsync.c
@@ -529,6 +529,7 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 	struct pfi_kkif		*rt_kif = NULL;
 	struct pf_kpooladdr	*rpool_first;
 	int			 error;
+	sa_family_t		 rt_af = 0;
 	uint8_t			 rt = 0;
 	int			 n = 0;
 
@@ -602,6 +603,11 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 			}
 			rt = r->rt;
 			rt_kif = rpool_first->kif;
+			/*
+			 * Guess the AF of the route address, FreeBSD 13 does
+			 * not support af-to so it should be safe.
+			 */
+			rt_af = r->af;
 		} else if (!PF_AZERO(&sp->pfs_1301.rt_addr, sp->pfs_1301.af)) {
 			/*
 			 * Ruleset different, routing *supposedly* requested,
@@ -627,6 +633,11 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 				return ((flags & PFSYNC_SI_IOCTL) ? EINVAL : 0);
 			}
 			rt = sp->pfs_1400.rt;
+			/*
+			 * Guess the AF of the route address, FreeBSD 13 does
+			 * not support af-to so it should be safe.
+			 */
+			rt_af = sp->pfs_1400.af;
 		}
 	break;
 	}
@@ -706,6 +717,7 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 
 	st->act.rt = rt;
 	st->act.rt_kif = rt_kif;
+	st->act.rt_af = rt_af;
 
 	switch (msg_version) {
 		case PFSYNC_MSG_VERSION_1301:
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 79c298c18b46..f6ee02626624 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -401,7 +401,7 @@ static void		 pf_overload_task(void *v, int pending);
 static u_short		 pf_insert_src_node(struct pf_ksrc_node *[PF_SN_MAX],
 			    struct pf_srchash *[PF_SN_MAX], struct pf_krule *,
 			    struct pf_addr *, sa_family_t, struct pf_addr *,
-			    struct pfi_kkif *, pf_sn_types_t);
+			    struct pfi_kkif *, sa_family_t, pf_sn_types_t);
 static u_int		 pf_purge_expired_states(u_int, int);
 static void		 pf_purge_unlinked_rules(void);
 static int		 pf_mtag_uminit(void *, int, int);
@@ -1017,7 +1017,7 @@ static u_short
 pf_insert_src_node(struct pf_ksrc_node *sns[PF_SN_MAX],
     struct pf_srchash *snhs[PF_SN_MAX], struct pf_krule *rule,
     struct pf_addr *src, sa_family_t af, struct pf_addr *raddr,
-    struct pfi_kkif *rkif, pf_sn_types_t sn_type)
+    struct pfi_kkif *rkif, sa_family_t raf, pf_sn_types_t sn_type)
 {
 	u_short			 reason = 0;
 	struct pf_krule		*r_track = rule;
@@ -1089,8 +1089,9 @@ pf_insert_src_node(struct pf_ksrc_node *sns[PF_SN_MAX],
 		(*sn)->rule = r_track;
 		pf_addrcpy(&(*sn)->addr, src, af);
 		if (raddr != NULL)
-			pf_addrcpy(&(*sn)->raddr, raddr, af);
+			pf_addrcpy(&(*sn)->raddr, raddr, raf);
 		(*sn)->rkif = rkif;
+		(*sn)->raf = raf;
 		LIST_INSERT_HEAD(&(*sh)->nodes, *sn, entry);
 		(*sn)->creation = time_uptime;
 		(*sn)->ruletype = rule->action;
@@ -5907,9 +5908,13 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
 		 * it is applied only from the last pass rule.
 		 */
 		pd->act.rt = r->rt;
+		if (r->rt == PF_REPLYTO)
+			pd->act.rt_af = pd->af;
+		else
+			pd->act.rt_af = pd->naf;
 		if ((transerror = pf_map_addr_sn(pd->af, r, pd->src,
-		    &pd->act.rt_addr, &pd->act.rt_kif, NULL, &(r->route),
-		    PF_SN_ROUTE)) != PFRES_MATCH) {
+		    &pd->act.rt_addr, &pd->act.rt_af, &pd->act.rt_kif, NULL,
+		    &(r->route), PF_SN_ROUTE)) != PFRES_MATCH) {
 			REASON_SET(&ctx.reason, transerror);
 			goto cleanup;
 		}
@@ -6039,7 +6044,7 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx,
 	/* src node for limits */
 	if ((r->rule_flag & PFRULE_SRCTRACK) &&
 	    (sn_reason = pf_insert_src_node(sns, snhs, r, pd->src, pd->af,
-	        NULL, NULL, PF_SN_LIMIT)) != 0) {
+	    NULL, NULL, pd->af, PF_SN_LIMIT)) != 0) {
 		REASON_SET(&ctx->reason, sn_reason);
 		goto csfailed;
 	}
@@ -6047,7 +6052,7 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx,
 	if (r->rt) {
 		if ((r->route.opts & PF_POOL_STICKYADDR) &&
 		    (sn_reason = pf_insert_src_node(sns, snhs, r, pd->src,
-		    pd->af, &pd->act.rt_addr, pd->act.rt_kif,
+		    pd->af, &pd->act.rt_addr, pd->act.rt_kif, pd->act.rt_af,
 		    PF_SN_ROUTE)) != 0) {
 			REASON_SET(&ctx->reason, sn_reason);
 			goto csfailed;
@@ -6066,7 +6071,7 @@ pf_create_state(struct pf_krule *r, struct pf_test_ctx *ctx,
 		    (sn_reason = pf_insert_src_node(sns, snhs, ctx->nr,
 		    ctx->sk ? &(ctx->sk->addr[pd->sidx]) : pd->src, pd->af,
 		    ctx->nk ? &(ctx->nk->addr[1]) : &(pd->nsaddr), NULL,
-		    PF_SN_NAT)) != 0 ) {
+		    pd->naf, PF_SN_NAT)) != 0 ) {
 			REASON_SET(&ctx->reason, sn_reason);
 			goto csfailed;
 		}
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 9abc07c36788..178ee01649c6 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2661,6 +2661,7 @@ pf_ioctl_add_addr(struct pf_nl_pooladdr *pp)
 		PF_RULES_WUNLOCK();
 		goto out;
 	}
+	pa->af = pp->af;
 	switch (pp->which) {
 	case PF_NAT:
 		TAILQ_INSERT_TAIL(&V_pf_pabuf[0], pa, entries);
@@ -2742,6 +2743,7 @@ pf_ioctl_get_addr(struct pf_nl_pooladdr *pp)
 		return (EBUSY);
 	}
 	pf_kpooladdr_to_pooladdr(pa, &pp->addr);
+	pp->af = pa->af;
 	pf_addr_copyout(&pp->addr.addr);
 	PF_RULES_RUNLOCK();
 
diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c
index ea0d6facf695..bc9e1dc72902 100644
--- a/sys/netpfil/pf/pf_lb.c
+++ b/sys/netpfil/pf/pf_lb.c
@@ -345,8 +345,8 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r, struct pf_addr *naddr,
 		}
 	}
 
-	if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL, &init_addr,
-	    rpool, sn_type))
+	if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, &(pd->naf), NULL,
+	    &init_addr, rpool, sn_type))
 		goto failed;
 
 	if (pd->proto == IPPROTO_ICMP) {
@@ -470,8 +470,8 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r, struct pf_addr *naddr,
 			 * pick a different source address since we're out
 			 * of free port choices for the current one.
 			 */
-			if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL,
-			    &init_addr, rpool, sn_type))
+			if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr,
+			    &(pd->naf), NULL, &init_addr, rpool, sn_type))
 				return (1);
 			break;
 		case PF_POOL_NONE:
@@ -501,8 +501,8 @@ pf_islinklocal(const sa_family_t af, const struct pf_addr *addr)
 
 static int
 pf_get_mape_sport(struct pf_pdesc *pd, struct pf_krule *r,
-    struct pf_addr *naddr, uint16_t *nport,
-    struct pf_udp_mapping **udp_mapping, struct pf_kpool *rpool)
+    struct pf_addr *naddr, uint16_t *nport, struct pf_udp_mapping **udp_mapping,
+    struct pf_kpool *rpool)
 {
 	uint16_t psmask, low, highmask;
 	uint16_t i, ahigh, cut;
@@ -535,15 +535,22 @@ pf_get_mape_sport(struct pf_pdesc *pd, struct pf_krule *r,
 }
 
 u_short
-pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
-    struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr,
-    struct pf_kpool *rpool)
+pf_map_addr(sa_family_t saf, struct pf_krule *r, struct pf_addr *saddr,
+    struct pf_addr *naddr, struct pfi_kkif **nkif, sa_family_t *naf,
+    struct pf_addr *init_addr, struct pf_kpool *rpool)
 {
 	u_short			 reason = PFRES_MATCH;
 	struct pf_addr		*raddr = NULL, *rmask = NULL;
 	struct pfr_ktable	*kt;
 	uint64_t		 hashidx;
 	int			 cnt;
+	sa_family_t		 wanted_af;
+
+	KASSERT(saf != 0, ("%s: saf == 0", __func__));
+	KASSERT(naf != NULL, ("%s: naf = NULL", __func__));
+	KASSERT((*naf) != 0, ("%s: *naf = 0", __func__));
+
+	wanted_af = (*naf);
 
 	mtx_lock(&rpool->mtx);
 	/* Find the route using chosen algorithm. Store the found route
@@ -553,7 +560,7 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 		goto done_pool_mtx;
 	}
 	if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
-		switch (af) {
+		switch (wanted_af) {
 #ifdef INET
 		case AF_INET:
 			if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 &&
@@ -577,7 +584,7 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 			break;
 #endif /* INET6 */
 		default:
-			unhandled_af(af);
+			unhandled_af(wanted_af);
 		}
 	} else if (rpool->cur->addr.type == PF_ADDR_TABLE) {
 		if (!PF_POOL_DYNTYPE(rpool->opts)) {
@@ -587,14 +594,24 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 	} else {
 		raddr = &rpool->cur->addr.v.a.addr;
 		rmask = &rpool->cur->addr.v.a.mask;
+		/*
+		 * For single addresses check their address family. Unless they
+		 * have none, which happens when addresses are added with
+		 * the old ioctl mechanism. In such case trust that the address
+		 * has the proper AF.
+		 */
+		if (rpool->cur->af && rpool->cur->af != wanted_af) {
+			reason = PFRES_MAPFAILED;
+			goto done_pool_mtx;
+		}
 	}
 
 	switch (rpool->opts & PF_POOL_TYPEMASK) {
 	case PF_POOL_NONE:
-		pf_addrcpy(naddr, raddr, af);
+		pf_addrcpy(naddr, raddr, wanted_af);
 		break;
 	case PF_POOL_BITMASK:
-		pf_poolmask(naddr, raddr, rmask, saddr, af);
+		pf_poolmask(naddr, raddr, rmask, saddr, wanted_af);
 		break;
 	case PF_POOL_RANDOM:
 		if (rpool->cur->addr.type == PF_ADDR_TABLE ||
@@ -615,13 +632,14 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 				rpool->tblidx = (int)arc4random_uniform(cnt);
 			memset(&rpool->counter, 0, sizeof(rpool->counter));
 			if (pfr_pool_get(kt, &rpool->tblidx, &rpool->counter,
-			    af, pf_islinklocal, false)) {
+			    wanted_af, pf_islinklocal, false)) {
 				reason = PFRES_MAPFAILED;
 				goto done_pool_mtx; /* unsupported */
 			}
-			pf_addrcpy(naddr, &rpool->counter, af);
-		} else if (init_addr != NULL && PF_AZERO(init_addr, af)) {
-			switch (af) {
+			pf_addrcpy(naddr, &rpool->counter, wanted_af);
+		} else if (init_addr != NULL && PF_AZERO(init_addr,
+		    wanted_af)) {
+			switch (wanted_af) {
 #ifdef INET
 			case AF_INET:
 				rpool->counter.addr32[0] = arc4random();
@@ -650,12 +668,14 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 				break;
 #endif /* INET6 */
 			}
-			pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
-			pf_addrcpy(init_addr, naddr, af);
+			pf_poolmask(naddr, raddr, rmask, &rpool->counter,
+			    wanted_af);
+			pf_addrcpy(init_addr, naddr, wanted_af);
 
 		} else {
-			pf_addr_inc(&rpool->counter, af);
-			pf_poolmask(naddr, raddr, rmask, &rpool->counter, af);
+			pf_addr_inc(&rpool->counter, wanted_af);
+			pf_poolmask(naddr, raddr, rmask, &rpool->counter,
+			    wanted_af);
 		}
 		break;
 	case PF_POOL_SRCHASH:
@@ -663,7 +683,8 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 		unsigned char hash[16];
 
 		hashidx =
-		    pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key, af);
+		    pf_hash(saddr, (struct pf_addr *)&hash, &rpool->key,
+		    wanted_af);
 		if (rpool->cur->addr.type == PF_ADDR_TABLE ||
 		    rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
 			if (rpool->cur->addr.type == PF_ADDR_TABLE)
@@ -682,14 +703,14 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 				rpool->tblidx = (int)(hashidx % cnt);
 			memset(&rpool->counter, 0, sizeof(rpool->counter));
 			if (pfr_pool_get(kt, &rpool->tblidx, &rpool->counter,
-			    af, pf_islinklocal, false)) {
+			    wanted_af, pf_islinklocal, false)) {
 				reason = PFRES_MAPFAILED;
 				goto done_pool_mtx; /* unsupported */
 			}
-			pf_addrcpy(naddr, &rpool->counter, af);
+			pf_addrcpy(naddr, &rpool->counter, wanted_af);
 		} else {
 			pf_poolmask(naddr, raddr, rmask,
-			    (struct pf_addr *)&hash, af);
+			    (struct pf_addr *)&hash, wanted_af);
 		}
 		break;
 	    }
@@ -699,14 +720,16 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 
 		if (rpool->cur->addr.type == PF_ADDR_TABLE) {
 			if (!pfr_pool_get(rpool->cur->addr.p.tbl,
-			    &rpool->tblidx, &rpool->counter, af, NULL, true))
+			    &rpool->tblidx, &rpool->counter, wanted_af,
+			    NULL, true))
 				goto get_addr;
 		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
 			if (!pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
-			    &rpool->tblidx, &rpool->counter, af, pf_islinklocal,
-			    true))
+			    &rpool->tblidx, &rpool->counter, wanted_af,
+			    pf_islinklocal, true))
 				goto get_addr;
-		} else if (pf_match_addr(0, raddr, rmask, &rpool->counter, af))
+		} else if (pf_match_addr(0, raddr, rmask, &rpool->counter,
+		    wanted_af))
 			goto get_addr;
 
 	try_next:
@@ -717,8 +740,9 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 		rpool->tblidx = -1;
 		if (rpool->cur->addr.type == PF_ADDR_TABLE) {
 			if (pfr_pool_get(rpool->cur->addr.p.tbl,
-			    &rpool->tblidx, &rpool->counter, af, NULL, true)) {
-				/* table contains no address of type 'af' */
+			    &rpool->tblidx, &rpool->counter, wanted_af, NULL,
+			    true)) {
+				/* table contains no address of type 'wanted_af' */
 				if (rpool->cur != acur)
 					goto try_next;
 				reason = PFRES_MAPFAILED;
@@ -726,9 +750,9 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 			}
 		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
 			if (pfr_pool_get(rpool->cur->addr.p.dyn->pfid_kt,
-			    &rpool->tblidx, &rpool->counter, af, pf_islinklocal,
-			    true)) {
-				/* table contains no address of type 'af' */
+			    &rpool->tblidx, &rpool->counter, wanted_af,
+			    pf_islinklocal, true)) {
+				/* interface has no address of type 'wanted_af' */
 				if (rpool->cur != acur)
 					goto try_next;
 				reason = PFRES_MAPFAILED;
@@ -737,14 +761,18 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 		} else {
 			raddr = &rpool->cur->addr.v.a.addr;
 			rmask = &rpool->cur->addr.v.a.mask;
-			pf_addrcpy(&rpool->counter, raddr, af);
+			if (rpool->cur->af && rpool->cur->af != wanted_af) {
+				reason = PFRES_MAPFAILED;
+				goto done_pool_mtx;
+			}
+			pf_addrcpy(&rpool->counter, raddr, wanted_af);
 		}
 
 	get_addr:
-		pf_addrcpy(naddr, &rpool->counter, af);
-		if (init_addr != NULL && PF_AZERO(init_addr, af))
-			pf_addrcpy(init_addr, naddr, af);
-		pf_addr_inc(&rpool->counter, af);
+		pf_addrcpy(naddr, &rpool->counter, wanted_af);
+		if (init_addr != NULL && PF_AZERO(init_addr, wanted_af))
+			pf_addrcpy(init_addr, naddr, wanted_af);
+		pf_addr_inc(&rpool->counter, wanted_af);
 		break;
 	    }
 	}
@@ -759,9 +787,9 @@ done_pool_mtx:
 }
 
 u_short
-pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
-    struct pf_addr *naddr, struct pfi_kkif **nkif, struct pf_addr *init_addr,
-    struct pf_kpool *rpool, pf_sn_types_t sn_type)
+pf_map_addr_sn(sa_family_t saf, struct pf_krule *r, struct pf_addr *saddr,
+    struct pf_addr *naddr, sa_family_t *naf, struct pfi_kkif **nkif,
+    struct pf_addr *init_addr, struct pf_kpool *rpool, pf_sn_types_t sn_type)
 {
 	struct pf_ksrc_node	*sn = NULL;
 	struct pf_srchash	*sh = NULL;
@@ -772,27 +800,31 @@ pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 	 */
 	if (rpool->opts & PF_POOL_STICKYADDR &&
 	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
-		sn = pf_find_src_node(saddr, r, af, &sh, sn_type, false);
+		sn = pf_find_src_node(saddr, r, saf, &sh, sn_type, false);
 
 	if (sn != NULL) {
 		PF_SRC_NODE_LOCK_ASSERT(sn);
+		(*naf) = sn->raf;
 
 		/* If the supplied address is the same as the current one we've
 		 * been asked before, so tell the caller that there's no other
 		 * address to be had. */
-		if (PF_AEQ(naddr, &(sn->raddr), af)) {
+
+		if (PF_AEQ(naddr, &(sn->raddr), *naf)) {
+			printf("%s: no more addresses\n", __func__);
 			reason = PFRES_MAPFAILED;
 			goto done;
 		}
 
-		pf_addrcpy(naddr, &(sn->raddr), af);
+		pf_addrcpy(naddr, &(sn->raddr), *naf);
+
 		if (nkif)
 			*nkif = sn->rkif;
 		if (V_pf_status.debug >= PF_DEBUG_NOISY) {
 			printf("%s: src tracking maps ", __func__);
-			pf_print_host(saddr, 0, af);
+			pf_print_host(saddr, 0, saf);
 			printf(" to ");
-			pf_print_host(naddr, 0, af);
+			pf_print_host(naddr, 0, *naf);
 			if (nkif)
 				printf("@%s", (*nkif)->pfik_name);
 			printf("\n");
@@ -804,7 +836,7 @@ pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 	 * Source node has not been found. Find a new address and store it
 	 * in variables given by the caller.
 	 */
-	if ((reason = pf_map_addr(af, r, saddr, naddr, nkif, init_addr,
+	if ((reason = pf_map_addr(saf, r, saddr, naddr, nkif, naf, init_addr,
 	    rpool)) != 0) {
 		if (V_pf_status.debug >= PF_DEBUG_MISC)
 			printf("%s: pf_map_addr has failed\n", __func__);
@@ -814,7 +846,7 @@ pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 	if (V_pf_status.debug >= PF_DEBUG_NOISY &&
 	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
 		printf("%s: selected address ", __func__);
-		pf_print_host(naddr, 0, af);
+		pf_print_host(naddr, 0, *naf);
 		if (nkif)
 			printf("@%s", (*nkif)->pfik_name);
 		printf("\n");
@@ -996,8 +1028,9 @@ pf_get_transaddr(struct pf_test_ctx *ctx, struct pf_krule *r,
 		int tries;
 		uint16_t cut, low, high, nport;
 
-		reason = pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL,
-		    NULL, rpool, PF_SN_NAT);
+		reason = pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr,
+		    &(pd->naf), NULL, NULL, rpool, PF_SN_NAT);
+
 		if (reason != 0)
 			goto notrans;
 		if ((rpool->opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK)
@@ -1161,8 +1194,8 @@ pf_get_transaddr_af(struct pf_krule *r, struct pf_pdesc *pd)
 
 	/* get the destination address and port */
 	if (! TAILQ_EMPTY(&r->rdr.list)) {
-		if (pf_map_addr_sn(pd->naf, r, &nsaddr, &naddr, NULL, NULL,
-		    &r->rdr, PF_SN_NAT))
+		if (pf_map_addr_sn(pd->naf, r, &nsaddr, &naddr, &(pd->naf),
+		    NULL, NULL, &r->rdr, PF_SN_NAT))
 			return (-1);
 		if (r->rdr.proxy_port[0])
 			pd->ndport = htons(r->rdr.proxy_port[0]);
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index 73933c022ca2..c5de1e84a287 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -178,7 +178,7 @@ dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s,
 
 	nlattr_add_string(nw, PF_ST_IFNAME, s->kif->pfik_name);
 	nlattr_add_string(nw, PF_ST_ORIG_IFNAME, s->orig_kif->pfik_name);
-	dump_addr(nw, PF_ST_RT_ADDR, &s->act.rt_addr, af);
+	dump_addr(nw, PF_ST_RT_ADDR, &s->act.rt_addr, s->act.rt_af);
 	nlattr_add_u32(nw, PF_ST_CREATION, time_uptime - (s->creation / 1000));
 	uint32_t expire = pf_state_expires(s);
 	if (expire > time_uptime)
@@ -224,6 +224,7 @@ dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s,
 	if (s->sns[PF_SN_ROUTE] != NULL)
 		src_node_flags |= PFSTATE_SRC_NODE_ROUTE;
 	nlattr_add_u8(nw, PF_ST_SRC_NODE_FLAGS, src_node_flags);
+	nlattr_add_u8(nw, PF_ST_RT_AF, s->act.rt_af);
 
 	if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src))
 		goto enomem;
@@ -1761,7 +1762,7 @@ pf_handle_get_srcnodes(struct nlmsghdr *hdr, struct nl_pstate *npt)
 			nlattr_add_u32(nw, PF_SN_STATES, n->states);
 			nlattr_add_u32(nw, PF_SN_CONNECTIONS, n->conn);
 			nlattr_add_u8(nw, PF_SN_AF, n->af);
-			nlattr_add_u8(nw, PF_SN_NAF, n->naf);
+			nlattr_add_u8(nw, PF_SN_RAF, n->raf);
 			nlattr_add_u8(nw, PF_SN_RULE_TYPE, n->ruletype);
 
 			nlattr_add_u64(nw, PF_SN_CREATION, secs - n->creation);
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
index 929c20e4c582..d263a0b22deb 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -135,6 +135,7 @@ enum pfstate_type_t {
 	PF_ST_RT		= 36, /* u8 */
 	PF_ST_RT_IFNAME		= 37, /* string */
 	PF_ST_SRC_NODE_FLAGS	= 38, /* u8 */
+	PF_ST_RT_AF		= 39, /* u8 */
 };
 
 enum pf_addr_type_t {
@@ -433,7 +434,7 @@ enum pf_srcnodes_types_t {
 	PF_SN_CREATION		= 12, /* u64 */
 	PF_SN_EXPIRE		= 13, /* u64 */
 	PF_SN_CONNECTION_RATE	= 14, /* nested, pf_threshold */
-	PF_SN_NAF		= 15, /* u8 */
+	PF_SN_RAF		= 15, /* u8 */
 	PF_SN_NODE_TYPE		= 16, /* u8 */
*** 99 LINES SKIPPED ***