git: c49c9da239ca - main - pf: Move route-to information to rule actions

From: Kajetan Staszkiewicz <ks_at_FreeBSD.org>
Date: Tue, 03 Dec 2024 13:10:17 UTC
The branch main has been updated by ks:

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

commit c49c9da239ca59722f104a64d9128e4b6052885a
Author:     Kajetan Staszkiewicz <ks@FreeBSD.org>
AuthorDate: 2024-11-28 13:08:15 +0000
Commit:     Kajetan Staszkiewicz <ks@FreeBSD.org>
CommitDate: 2024-12-03 12:46:34 +0000

    pf: Move route-to information to rule actions
    
    Route-to redirection information (rt, rt_kif, rt_addr) can be considered an
    action of a rule. This information is duplicated in struct pf_kstate which
    means that the pf_route() function must always figure out where to get this
    information from: state for stateful forwarding, or rule for stateless.
    
    Create the necessary members in struct pf_rule_action. Fill them in right after
    parsing the ruleset, similar for how NAT redirection is applied right after
    parsing the NAT ruleset. Remove the logic for finding the right source for
    route-to redirection from pf_route().
    
    As a bonus simplify pf_map_addr_sn() and source node handling. Both for the
    NAT and the filter ruleset there is now only one path:
    1. parse the rules
    2. apply redirection either from an existing source node or by load balancing
       for the last matching rule
    3. create the source node using the redirection if the node does not yet exist
    
    Reviewed by:            kp
    Approved by:            kp (mentor)
    Sponsored by:           InnoGames GmbH
    Differential Revision:  https://reviews.freebsd.org/D47827
---
 sys/net/pfvar.h            |  12 ++--
 sys/netpfil/pf/if_pfsync.c |   6 +-
 sys/netpfil/pf/pf.c        | 153 +++++++++++++++++----------------------------
 sys/netpfil/pf/pf_ioctl.c  |  18 +++---
 sys/netpfil/pf/pf_lb.c     |  32 +++-------
 sys/netpfil/pf/pf_nl.c     |   8 +--
 sys/netpfil/pf/pf_nv.c     |   2 +-
 7 files changed, 91 insertions(+), 140 deletions(-)

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 0a0a17d2c754..30229cca2e74 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -648,17 +648,20 @@ struct pf_kpool {
 };
 
 struct pf_rule_actions {
+	struct pf_addr	 rt_addr;
+	struct pfi_kkif	*rt_kif;
 	int32_t		 rtableid;
+	uint32_t	 flags;
 	uint16_t	 qid;
 	uint16_t	 pqid;
 	uint16_t	 max_mss;
+	uint16_t	 dnpipe;
+	uint16_t	 dnrpipe;	/* Reverse direction pipe */
 	uint8_t		 log;
 	uint8_t		 set_tos;
 	uint8_t		 min_ttl;
-	uint16_t	 dnpipe;
-	uint16_t	 dnrpipe;	/* Reverse direction pipe */
-	uint32_t	 flags;
 	uint8_t		 set_prio[2];
+	uint8_t		 rt;
 };
 
 union pf_keth_rule_ptr {
@@ -1089,12 +1092,10 @@ struct pf_kstate {
 	struct pf_krule		*rule;
 	struct pf_krule		*anchor;
 	struct pf_krule		*nat_rule;
-	struct pf_addr		 rt_addr;
 	struct pf_state_key	*key[2];	/* addresses stack and wire  */
 	struct pf_udp_mapping	*udp_mapping;
 	struct pfi_kkif		*kif;
 	struct pfi_kkif		*orig_kif;	/* The real kif, even if we're a floating state (i.e. if == V_pfi_all). */
-	struct pfi_kkif		*rt_kif;
 	struct pf_ksrc_node	*src_node;
 	struct pf_ksrc_node	*nat_src_node;
 	u_int64_t		 packets[2];
@@ -1104,7 +1105,6 @@ struct pf_kstate {
 	u_int32_t		 pfsync_time;
 	struct pf_rule_actions	 act;
 	u_int16_t		 tag;
-	u_int8_t		 rt;
 	u_int16_t		 if_index_in;
 	u_int16_t		 if_index_out;
 };
diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c
index 0dd1b480b313..8dd3d875dc0b 100644
--- a/sys/netpfil/pf/if_pfsync.c
+++ b/sys/netpfil/pf/if_pfsync.c
@@ -611,7 +611,7 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 	}
 
 	/* copy to state */
-	bcopy(&sp->pfs_1301.rt_addr, &st->rt_addr, sizeof(st->rt_addr));
+	bcopy(&sp->pfs_1301.rt_addr, &st->act.rt_addr, sizeof(st->act.rt_addr));
 	st->creation = (time_uptime - ntohl(sp->pfs_1301.creation)) * 1000;
 	st->expire = pf_get_uptime();
 	if (sp->pfs_1301.expire) {
@@ -680,8 +680,8 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version)
 			st->act.max_mss = ntohs(sp->pfs_1400.max_mss);
 			st->act.set_prio[0] = sp->pfs_1400.set_prio[0];
 			st->act.set_prio[1] = sp->pfs_1400.set_prio[1];
-			st->rt = sp->pfs_1400.rt;
-			if (st->rt && (st->rt_kif = pfi_kkif_find(sp->pfs_1400.rt_ifname)) == NULL) {
+			st->act.rt = sp->pfs_1400.rt;
+			if (st->act.rt && (st->act.rt_kif = pfi_kkif_find(sp->pfs_1400.rt_ifname)) == NULL) {
 				if (V_pf_status.debug >= PF_DEBUG_MISC)
 					printf("%s: unknown route interface: %s\n",
 					    __func__, sp->pfs_1400.rt_ifname);
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index db23559774d0..fa656f92f79b 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -379,7 +379,8 @@ static void		 pf_counters_inc(int, struct pf_pdesc *,
 static void		 pf_overload_task(void *v, int pending);
 static u_short		 pf_insert_src_node(struct pf_ksrc_node **,
 			    struct pf_srchash **, struct pf_krule *,
-			    struct pf_addr *, sa_family_t);
+			    struct pf_addr *, sa_family_t, struct pf_addr *,
+			    struct pfi_kkif *);
 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);
@@ -453,11 +454,11 @@ BOUND_IFACE(struct pf_kstate *st, struct pfi_kkif *k)
 		return (k);
 
 	/* No route-to, so don't overrule. */
-	if (st->rt != PF_ROUTETO)
+	if (st->act.rt != PF_ROUTETO)
 		return (k);
 
 	/* Bind to the route-to interface. */
-	return (st->rt_kif);
+	return (st->act.rt_kif);
 }
 
 #define	STATE_INC_COUNTERS(s)						\
@@ -1020,7 +1021,8 @@ pf_free_src_node(struct pf_ksrc_node *sn)
 
 static u_short
 pf_insert_src_node(struct pf_ksrc_node **sn, struct pf_srchash **sh,
-    struct pf_krule *rule, struct pf_addr *src, sa_family_t af)
+    struct pf_krule *rule, struct pf_addr *src, sa_family_t af,
+    struct pf_addr *raddr, struct pfi_kkif *rkif)
 {
 	u_short			 reason = 0;
 
@@ -1071,6 +1073,8 @@ pf_insert_src_node(struct pf_ksrc_node **sn, struct pf_srchash **sh,
 		(*sn)->af = af;
 		(*sn)->rule = rule;
 		PF_ACPY(&(*sn)->addr, src, af);
+		PF_ACPY(&(*sn)->raddr, raddr, af);
+		(*sn)->rkif = rkif;
 		LIST_INSERT_HEAD(&(*sh)->nodes, *sn, entry);
 		(*sn)->creation = time_uptime;
 		(*sn)->ruletype = rule->action;
@@ -2709,8 +2713,8 @@ relock:
 				s->kif->pfik_flags |= PFI_IFLAG_REFS;
 				SLIST_FOREACH(mrm, &s->match_rules, entry)
 					mrm->r->rule_ref |= PFRULE_REFS;
-				if (s->rt_kif)
-					s->rt_kif->pfik_flags |= PFI_IFLAG_REFS;
+				if (s->act.rt_kif)
+					s->act.rt_kif->pfik_flags |= PFI_IFLAG_REFS;
 				count++;
 			}
 			PF_HASHROW_UNLOCK(ih);
@@ -5307,6 +5311,21 @@ nextrule:
 	if (pd->act.rtableid >= 0)
 		M_SETFIB(pd->m, pd->act.rtableid);
 
+	if (r->rt) {
+		struct pf_ksrc_node	*sn = NULL;
+		struct pf_srchash	*snh = NULL;
+		/*
+		 * Set act.rt here instead of in pf_rule_to_actions() because
+		 * it is applied only from the last pass rule.
+		 */
+		pd->act.rt = r->rt;
+		/* Don't use REASON_SET, pf_map_addr increases the reason counters */
+		reason = pf_map_addr_sn(pd->af, r, pd->src, &pd->act.rt_addr,
+		    &pd->act.rt_kif, NULL, &sn, &snh);
+		if (reason != 0)
+			goto cleanup;
+	}
+
 	if (pd->virtual_proto != PF_VPROTO_FRAGMENT &&
 	   (!state_icmp && (r->keep_state || nr != NULL ||
 	    (pd->flags & PFDESC_TCP_NORM)))) {
@@ -5391,14 +5410,15 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
 	/* src node for filter rule */
 	if ((r->rule_flag & PFRULE_SRCTRACK ||
 	    r->rpool.opts & PF_POOL_STICKYADDR) &&
-	    (sn_reason = pf_insert_src_node(&sn, &snh, r, pd->src, pd->af)) != 0) {
+	    (sn_reason = pf_insert_src_node(&sn, &snh, r, pd->src, pd->af,
+	    &pd->act.rt_addr, pd->act.rt_kif)) != 0) {
 		REASON_SET(&reason, sn_reason);
 		goto csfailed;
 	}
 	/* src node for translation rule */
 	if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) &&
 	    (sn_reason = pf_insert_src_node(&nsn, &nsnh, nr, &sk->addr[pd->sidx],
-	    pd->af)) != 0 ) {
+	    pd->af, &nk->addr[1], NULL)) != 0 ) {
 		REASON_SET(&reason, sn_reason);
 		goto csfailed;
 	}
@@ -5488,14 +5508,6 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
 		s->timeout = PFTM_OTHER_FIRST_PACKET;
 	}
 
-	if (r->rt) {
-		/* pf_map_addr increases the reason counters */
-		if ((reason = pf_map_addr_sn(pd->af, r, pd->src, &s->rt_addr,
-		    &s->rt_kif, NULL, &sn, &snh)) != 0)
-			goto csfailed;
-		s->rt = r->rt;
-	}
-
 	s->creation = s->expire = pf_get_uptime();
 
 	if (pd->proto == IPPROTO_TCP) {
@@ -5552,8 +5564,6 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
 		PF_HASHROW_UNLOCK(snh);
 	}
 	if (pf_src_node_exists(&nsn, nsnh)) {
-		/* XXX We only modify one side for now. */
-		PF_ACPY(&nsn->raddr, &nk->addr[1], pd->af);
 		s->nat_src_node = nsn;
 		PF_HASHROW_UNLOCK(nsnh);
 	}
@@ -7686,23 +7696,20 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 	struct mbuf		*m0, *m1, *md;
 	struct sockaddr_in	dst;
 	struct ip		*ip;
-	struct pfi_kkif		*nkif = NULL;
 	struct ifnet		*ifp = NULL;
 	struct pf_addr		 naddr;
 	int			 error = 0;
 	uint16_t		 ip_len, ip_off;
 	uint16_t		 tmp;
-	int			 r_rt, r_dir;
+	int			 r_dir;
 
 	KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__));
 
 	SDT_PROBE4(pf, ip, route_to, entry, *m, pd, s, oifp);
 
 	if (s) {
-		r_rt = s->rt;
 		r_dir = s->direction;
 	} else {
-		r_rt = r->rt;
 		r_dir = r->direction;
 	}
 
@@ -7719,17 +7726,14 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 		goto bad_locked;
 	}
 
-	if (r_rt == PF_DUPTO) {
+	if (pd->act.rt == PF_DUPTO) {
 		if ((pd->pf_mtag->flags & PF_MTAG_FLAG_DUPLICATED)) {
-			if (s == NULL) {
+			ifp = pd->act.rt_kif ? pd->act.rt_kif->pfik_ifp : NULL;
+			/* If pfsync'd from FreeBSD < 14 */
+			if (ifp == NULL && r->rpool.cur != NULL)
 				ifp = r->rpool.cur->kif ?
 				    r->rpool.cur->kif->pfik_ifp : NULL;
-			} else {
-				ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL;
-				/* If pfsync'd */
-				if (ifp == NULL && r->rpool.cur != NULL)
-					ifp = r->rpool.cur->kif ?
-					    r->rpool.cur->kif->pfik_ifp : NULL;
+			if (s != NULL) {
 				PF_STATE_UNLOCK(s);
 			}
 			if (ifp == oifp) {
@@ -7750,7 +7754,7 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 			}
 		}
 	} else {
-		if ((r_rt == PF_REPLYTO) == (r_dir == pd->dir)) {
+		if ((pd->act.rt == PF_REPLYTO) == (r_dir == pd->dir)) {
 			pf_dummynet(pd, s, r, m);
 			if (s)
 				PF_STATE_UNLOCK(s);
@@ -7765,30 +7769,16 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 	dst.sin_family = AF_INET;
 	dst.sin_len = sizeof(dst);
 	dst.sin_addr = ip->ip_dst;
+	dst.sin_addr.s_addr = pd->act.rt_addr.v4.s_addr;
+	ifp = pd->act.rt_kif ? pd->act.rt_kif->pfik_ifp : NULL;
 
 	bzero(&naddr, sizeof(naddr));
 
-	if (s == NULL) {
-		if (TAILQ_EMPTY(&r->rpool.list)) {
-			DPFPRINTF(PF_DEBUG_URGENT,
-			    ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__));
-			SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
-			goto bad_locked;
-		}
-		pf_map_addr(AF_INET, r, (struct pf_addr *)&ip->ip_src,
-		    &naddr, &nkif, NULL);
-		if (!PF_AZERO(&naddr, AF_INET))
-			dst.sin_addr.s_addr = naddr.v4.s_addr;
-		ifp = nkif ? nkif->pfik_ifp : NULL;
-	} else {
+	if (s != NULL){
 		struct pfi_kkif *kif;
 
-		if (!PF_AZERO(&s->rt_addr, AF_INET))
-			dst.sin_addr.s_addr =
-			    s->rt_addr.v4.s_addr;
-		ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL;
-		kif = s->rt_kif;
-		/* If pfsync'd */
+		kif = pd->act.rt_kif;
+		/* If pfsync'd from FreeBSD < 14 */
 		if (ifp == NULL && r->rpool.cur != NULL) {
 			ifp = r->rpool.cur->kif ?
 			    r->rpool.cur->kif->pfik_ifp : NULL;
@@ -7796,7 +7786,7 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 		}
 		if (ifp != NULL && kif != NULL &&
 		    r->rule_flag & PFRULE_IFBOUND &&
-		    r->rt == PF_REPLYTO &&
+		    pd->act.rt == PF_REPLYTO &&
 		    s->kif == V_pfi_all) {
 			s->kif = kif;
 			s->orig_kif = oifp->if_pf_kif;
@@ -7892,7 +7882,7 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 	if ((ip_off & IP_DF) || (m0->m_pkthdr.csum_flags & CSUM_TSO)) {
 		error = EMSGSIZE;
 		KMOD_IPSTAT_INC(ips_cantfrag);
-		if (r_rt != PF_DUPTO) {
+		if (pd->act.rt != PF_DUPTO) {
 			if (s && s->nat_rule != NULL)
 				PACKET_UNDO_NAT(m0, pd,
 				    (ip->ip_hl << 2) + (ip_off & IP_OFFMASK),
@@ -7936,7 +7926,7 @@ pf_route(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 		KMOD_IPSTAT_INC(ips_fragmented);
 
 done:
-	if (r_rt != PF_DUPTO)
+	if (pd->act.rt != PF_DUPTO)
 		*m = NULL;
 	return;
 
@@ -7958,20 +7948,17 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 	struct m_tag		*mtag;
 	struct sockaddr_in6	dst;
 	struct ip6_hdr		*ip6;
-	struct pfi_kkif		*nkif = NULL;
 	struct ifnet		*ifp = NULL;
 	struct pf_addr		 naddr;
-	int			 r_rt, r_dir;
+	int			 r_dir;
 
 	KASSERT(m && *m && r && oifp, ("%s: invalid parameters", __func__));
 
 	SDT_PROBE4(pf, ip6, route_to, entry, *m, pd, s, oifp);
 
 	if (s) {
-		r_rt = s->rt;
 		r_dir = s->direction;
 	} else {
-		r_rt = r->rt;
 		r_dir = r->direction;
 	}
 
@@ -7988,17 +7975,14 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 		goto bad_locked;
 	}
 
-	if (r_rt == PF_DUPTO) {
+	if (pd->act.rt == PF_DUPTO) {
 		if ((pd->pf_mtag->flags & PF_MTAG_FLAG_DUPLICATED)) {
-			if (s == NULL) {
+			ifp = pd->act.rt_kif ? pd->act.rt_kif->pfik_ifp : NULL;
+			/* If pfsync'd from FreeBSD < 14 */
+			if (ifp == NULL && r->rpool.cur != NULL)
 				ifp = r->rpool.cur->kif ?
 				    r->rpool.cur->kif->pfik_ifp : NULL;
-			} else {
-				ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL;
-				/* If pfsync'd */
-				if (ifp == NULL && r->rpool.cur != NULL)
-					ifp = r->rpool.cur->kif ?
-					    r->rpool.cur->kif->pfik_ifp : NULL;
+			if (s != NULL) {
 				PF_STATE_UNLOCK(s);
 			}
 			if (ifp == oifp) {
@@ -8019,7 +8003,7 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 			}
 		}
 	} else {
-		if ((r_rt == PF_REPLYTO) == (r_dir == pd->dir)) {
+		if ((pd->act.rt == PF_REPLYTO) == (r_dir == pd->dir)) {
 			pf_dummynet(pd, s, r, m);
 			if (s)
 				PF_STATE_UNLOCK(s);
@@ -8034,31 +8018,15 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 	dst.sin6_family = AF_INET6;
 	dst.sin6_len = sizeof(dst);
 	dst.sin6_addr = ip6->ip6_dst;
-
+	PF_ACPY((struct pf_addr *)&dst.sin6_addr, &pd->act.rt_addr, AF_INET6);
 	bzero(&naddr, sizeof(naddr));
+	ifp = pd->act.rt_kif ? pd->act.rt_kif->pfik_ifp : NULL;
 
-	if (s == NULL) {
-		if (TAILQ_EMPTY(&r->rpool.list)) {
-			DPFPRINTF(PF_DEBUG_URGENT,
-			    ("%s: TAILQ_EMPTY(&r->rpool.list)\n", __func__));
-			SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
-			goto bad_locked;
-		}
-		pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src,
-		    &naddr, &nkif, NULL);
-		if (!PF_AZERO(&naddr, AF_INET6))
-			PF_ACPY((struct pf_addr *)&dst.sin6_addr,
-			    &naddr, AF_INET6);
-		ifp = nkif ? nkif->pfik_ifp : NULL;
-	} else {
+	if (s != NULL) {
 		struct pfi_kkif *kif;
 
-		if (!PF_AZERO(&s->rt_addr, AF_INET6))
-			PF_ACPY((struct pf_addr *)&dst.sin6_addr,
-			    &s->rt_addr, AF_INET6);
-		ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL;
-		kif = s->rt_kif;
-		/* If pfsync'd */
+		kif = pd->act.rt_kif;
+		/* If pfsync'd from FreeBSD < 14 */
 		if (ifp == NULL && r->rpool.cur != NULL) {
 			ifp = r->rpool.cur->kif ?
 			    r->rpool.cur->kif->pfik_ifp : NULL;
@@ -8066,7 +8034,7 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 		}
 		if (ifp != NULL && kif != NULL &&
 		    r->rule_flag & PFRULE_IFBOUND &&
-		    r->rt == PF_REPLYTO &&
+		    pd->act.rt == PF_REPLYTO &&
 		    s->kif == V_pfi_all) {
 			s->kif = kif;
 			s->orig_kif = oifp->if_pf_kif;
@@ -8135,7 +8103,7 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 	}
 	else {
 		in6_ifstat_inc(ifp, ifs6_in_toobig);
-		if (r_rt != PF_DUPTO) {
+		if (pd->act.rt != PF_DUPTO) {
 			if (s && s->nat_rule != NULL)
 				PACKET_UNDO_NAT(m0, pd,
 				    ((caddr_t)ip6 - m0->m_data) +
@@ -8150,7 +8118,7 @@ pf_route6(struct mbuf **m, struct pf_krule *r, struct ifnet *oifp,
 	}
 
 done:
-	if (r_rt != PF_DUPTO)
+	if (pd->act.rt != PF_DUPTO)
 		*m = NULL;
 	return;
 
@@ -9036,7 +9004,6 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
 	struct pf_pdesc		 pd;
 	int			 use_2nd_queue = 0;
 	uint16_t		 tag;
-	uint8_t			 rt;
 
 	PF_RULES_RLOCK_TRACKER;
 	KASSERT(dir == PF_IN || dir == PF_OUT, ("%s: bad direction %d\n", __func__, dir));
@@ -9328,10 +9295,8 @@ done:
 		memcpy(&pd.act, &s->act, sizeof(struct pf_rule_actions));
 		pd.act.log |= log;
 		tag = s->tag;
-		rt = s->rt;
 	} else {
 		tag = r->tag;
-		rt = r->rt;
 	}
 
 	if (tag > 0 && pf_tag_packet(&pd, tag)) {
@@ -9474,7 +9439,7 @@ done:
 		*m0 = NULL;
 		break;
 	default:
-		if (rt) {
+		if (pd.act.rt) {
 			switch (af) {
 #ifdef INET
 			case AF_INET:
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 59c442698b95..0fdf41a9811f 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -2341,7 +2341,7 @@ relock_DIOCKILLSTATES:
 		if (!  PF_MATCHA(psk->psk_rt_addr.neg,
 		    &psk->psk_rt_addr.addr.v.a.addr,
 		    &psk->psk_rt_addr.addr.v.a.mask,
-		    &s->rt_addr, sk->af))
+		    &s->act.rt_addr, sk->af))
 			continue;
 
 		if (psk->psk_src.port_op != 0 &&
@@ -5587,7 +5587,7 @@ pfsync_state_export(union pfsync_state_union *sp, struct pf_kstate *st, int msg_
 
 	/* copy from state */
 	strlcpy(sp->pfs_1301.ifname, st->kif->pfik_name, sizeof(sp->pfs_1301.ifname));
-	bcopy(&st->rt_addr, &sp->pfs_1301.rt_addr, sizeof(sp->pfs_1301.rt_addr));
+	bcopy(&st->act.rt_addr, &sp->pfs_1301.rt_addr, sizeof(sp->pfs_1301.rt_addr));
 	sp->pfs_1301.creation = htonl(time_uptime - (st->creation / 1000));
 	sp->pfs_1301.expire = pf_state_expires(st);
 	if (sp->pfs_1301.expire <= time_uptime)
@@ -5615,10 +5615,10 @@ pfsync_state_export(union pfsync_state_union *sp, struct pf_kstate *st, int msg_
 			sp->pfs_1400.max_mss = htons(st->act.max_mss);
 			sp->pfs_1400.set_prio[0] = st->act.set_prio[0];
 			sp->pfs_1400.set_prio[1] = st->act.set_prio[1];
-			sp->pfs_1400.rt = st->rt;
-			if (st->rt_kif)
+			sp->pfs_1400.rt = st->act.rt;
+			if (st->act.rt_kif)
 				strlcpy(sp->pfs_1400.rt_ifname,
-				    st->rt_kif->pfik_name,
+				    st->act.rt_kif->pfik_name,
 				    sizeof(sp->pfs_1400.rt_ifname));
 			break;
 		default:
@@ -5678,7 +5678,7 @@ pf_state_export(struct pf_state_export *sp, struct pf_kstate *st)
 	strlcpy(sp->ifname, st->kif->pfik_name, sizeof(sp->ifname));
 	strlcpy(sp->orig_ifname, st->orig_kif->pfik_name,
 	    sizeof(sp->orig_ifname));
-	bcopy(&st->rt_addr, &sp->rt_addr, sizeof(sp->rt_addr));
+	bcopy(&st->act.rt_addr, &sp->rt_addr, sizeof(sp->rt_addr));
 	sp->creation = htonl(time_uptime - (st->creation / 1000));
 	sp->expire = pf_state_expires(st);
 	if (sp->expire <= time_uptime)
@@ -5728,9 +5728,9 @@ pf_state_export(struct pf_state_export *sp, struct pf_kstate *st)
 	sp->min_ttl = st->act.min_ttl;
 	sp->set_tos = st->act.set_tos;
 	sp->max_mss = htons(st->act.max_mss);
-	sp->rt = st->rt;
-	if (st->rt_kif)
-		strlcpy(sp->rt_ifname, st->rt_kif->pfik_name,
+	sp->rt = st->act.rt;
+	if (st->act.rt_kif)
+		strlcpy(sp->rt_ifname, st->act.rt_kif->pfik_name,
 		    sizeof(sp->rt_ifname));
 	sp->set_prio[0] = st->act.set_prio[0];
 	sp->set_prio[1] = st->act.set_prio[1];
diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c
index e180f87d2998..8087546683af 100644
--- a/sys/netpfil/pf/pf_lb.c
+++ b/sys/netpfil/pf/pf_lb.c
@@ -631,24 +631,18 @@ pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 	u_short			 reason = 0;
 	struct pf_kpool		*rpool = &r->rpool;
 
+	KASSERT(*sn == NULL, ("*sn not NULL"));
+
 	/*
-	 * Try to find a src_node if none was given and this is
-	 * a sticky-address rule. Request the sh to be unlocked if
-	 * sn was not found, as here we never insert a new sn.
+	 * If this is a sticky-address rule, try to find an existing src_node.
+	 * Request the sh to be unlocked if sn was not found, as we never
+	 * insert a new sn when parsing the ruleset.
 	 */
-	if (*sn == NULL) {
-		if (r->rpool.opts & PF_POOL_STICKYADDR &&
-		    (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
-			*sn = pf_find_src_node(saddr, r, af, sh, false);
-	} else {
-		pf_src_node_exists(sn, *sh);
-	}
+	if (r->rpool.opts & PF_POOL_STICKYADDR &&
+	    (r->rpool.opts & PF_POOL_TYPEMASK) != PF_POOL_NONE)
+		*sn = pf_find_src_node(saddr, r, af, sh, false);
 
-	/* If a src_node was found or explicitly given and it has a non-zero
-	   route address, use this address. A zeroed address is found if the
-	   src node was created just a moment ago in pf_create_state and it
-	   needs to be filled in with routing decision calculated here. */
-	if (*sn != NULL && !PF_AZERO(&(*sn)->raddr, af)) {
+	if (*sn != NULL) {
 		PF_SRC_NODE_LOCK_ASSERT(*sn);
 
 		/* If the supplied address is the same as the current one we've
@@ -683,14 +677,6 @@ pf_map_addr_sn(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 		goto done;
 	}
 
-	if (*sn != NULL) {
-		PF_SRC_NODE_LOCK_ASSERT(*sn);
-
-		PF_ACPY(&(*sn)->raddr, naddr, af);
-		if (nkif)
-			(*sn)->rkif = *nkif;
-	}
-
 	if (V_pf_status.debug >= PF_DEBUG_NOISY &&
 	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
 		printf("pf_map_addr: selected address ");
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index bdfa9a60faa4..68b3659b0ed2 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -173,7 +173,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->rt_addr, af);
+	dump_addr(nw, PF_ST_RT_ADDR, &s->act.rt_addr, af);
 	nlattr_add_u32(nw, PF_ST_CREATION, time_uptime - (s->creation / 1000));
 	uint32_t expire = pf_state_expires(s);
 	if (expire > time_uptime)
@@ -205,9 +205,9 @@ dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, struct pf_kstate *s,
 	nlattr_add_u16(nw, PF_ST_MAX_MSS, s->act.max_mss);
 	nlattr_add_u16(nw, PF_ST_DNPIPE, s->act.dnpipe);
 	nlattr_add_u16(nw, PF_ST_DNRPIPE, s->act.dnrpipe);
-	nlattr_add_u8(nw, PF_ST_RT, s->rt);
-	if (s->rt_kif != NULL)
-		nlattr_add_string(nw, PF_ST_RT_IFNAME, s->rt_kif->pfik_name);
+	nlattr_add_u8(nw, PF_ST_RT, s->act.rt);
+	if (s->act.rt_kif != NULL)
+		nlattr_add_string(nw, PF_ST_RT_IFNAME, s->act.rt_kif->pfik_name);
 
 	if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src))
 		goto enomem;
diff --git a/sys/netpfil/pf/pf_nv.c b/sys/netpfil/pf/pf_nv.c
index 87c2211a84e0..b24d274db85d 100644
--- a/sys/netpfil/pf/pf_nv.c
+++ b/sys/netpfil/pf/pf_nv.c
@@ -963,7 +963,7 @@ pf_state_to_nvstate(const struct pf_kstate *s)
 	nvlist_add_nvlist(nvl, "dst", tmp);
 	nvlist_destroy(tmp);
 
-	tmp = pf_addr_to_nvaddr(&s->rt_addr);
+	tmp = pf_addr_to_nvaddr(&s->act.rt_addr);
 	if (tmp == NULL)
 		goto errout;
 	nvlist_add_nvlist(nvl, "rt_addr", tmp);