The branch main has been updated by ks:

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

commit 07e070ef086997590cd6d9d47908885c12947bd2
Author:     Kajetan Staszkiewicz <k...@freebsd.org>
AuthorDate: 2025-02-07 11:40:09 +0000
Commit:     Kajetan Staszkiewicz <k...@freebsd.org>
CommitDate: 2025-02-13 14:59:12 +0000

    pf: Add support for multiple source node types
    
    For every state pf creates up to two source nodes: a limiting one
    struct pf_kstate -> src_node and a NAT one struct pf_kstate -> nat_src_node.
    The limiting source node is tracking information needed for limits using
    max-src-states and max-src-nodes and the NAT source node is tracking NAT
    rules only.
    
    On closer inspection some issues emerge:
    - For route-to rules the redirection decision is stored in the limiting 
source
      node. Thus sticky-address and source limiting can't be used separately.
    - Global source tracking, as promised in the man page, is totally absent 
from
      the code. Pfctl is capable of setting flags PFRULE_SRCTRACK (enable source
      tracking) and PFRULE_RULESRCTRACK (make source tracking per rule). The 
kernel
      code checks PFRULE_SRCTRACK but ignores PFRULE_RULESRCTRACK. That makes
      source tracking work per-rule only.
    
    This patch is based on OpenBSD approach where source nodes have a type and 
each
    state has an array of source node pointers indexed by source node type
    instead of just two pointers. The conditions for limiting are applied
    only to source nodes of PF_SN_LIMIT type. For global limit tracking
    source nodes are attached to the default rule.
    
    Reviewed by:            kp
    Approved by:            kp (mentor)
    Sponsored by:           InnoGames GmbH
    Differential Revision:  https://reviews.freebsd.org/D39880
---
 lib/libpfctl/libpfctl.c           |   5 +
 lib/libpfctl/libpfctl.h           |   3 +
 sbin/pfctl/pf_print_state.c       |  13 ++-
 sbin/pfctl/pfctl.c                |   9 ++
 sbin/pfctl/pfctl_parser.c         |   2 +
 sys/net/pfvar.h                   |  30 ++++--
 sys/netpfil/pf/pf.c               | 216 +++++++++++++++++++++-----------------
 sys/netpfil/pf/pf.h               |   6 ++
 sys/netpfil/pf/pf_ioctl.c         |  40 ++++---
 sys/netpfil/pf/pf_lb.c            |  35 +++---
 sys/netpfil/pf/pf_nl.c            |  25 ++++-
 sys/netpfil/pf/pf_nl.h            |   5 +
 sys/netpfil/pf/pf_nv.c            |  10 +-
 tests/sys/netpfil/pf/src_track.sh | 155 ++++++++++++++++++++++++++-
 14 files changed, 405 insertions(+), 149 deletions(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index fe63c91c1174..e93c79758428 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -1665,6 +1665,9 @@ static struct snl_attr_parser ap_getrule[] = {
        { .type = PF_RT_NAF, .off = _OUT(r.naf), .cb = snl_attr_get_uint8 },
        { .type = PF_RT_RPOOL_RT, .off = _OUT(r.route), .arg = &pool_parser, 
.cb = snl_attr_get_nested },
        { .type = PF_RT_RCV_IFNOT, .off = _OUT(r.rcvifnot),.cb = 
snl_attr_get_bool },
+       { .type = PF_RT_SRC_NODES_LIMIT, .off = 
_OUT(r.src_nodes_type[PF_SN_LIMIT]), .cb = snl_attr_get_uint64 },
+       { .type = PF_RT_SRC_NODES_NAT, .off = 
_OUT(r.src_nodes_type[PF_SN_NAT]), .cb = snl_attr_get_uint64 },
+       { .type = PF_RT_SRC_NODES_ROUTE, .off = 
_OUT(r.src_nodes_type[PF_SN_ROUTE]), .cb = snl_attr_get_uint64 },
 };
 #undef _OUT
 SNL_DECLARE_PARSER(getrule_parser, struct genlmsghdr, snl_f_p_empty, 
ap_getrule);
@@ -1910,6 +1913,7 @@ static struct snl_attr_parser ap_state[] = {
        { .type = PF_ST_DNRPIPE, .off = _OUT(dnrpipe), .cb = 
snl_attr_get_uint16 },
        { .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 },
 };
 #undef _IN
 #undef _OUT
@@ -3018,6 +3022,7 @@ static struct snl_attr_parser ap_srcnode[] = {
        { .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_NODE_TYPE, .off = _OUT(type), .cb = snl_attr_get_uint8 
},
 };
 #undef _OUT
 SNL_DECLARE_PARSER(srcnode_parser, struct genlmsghdr, snl_f_p_empty, 
ap_srcnode);
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index e1af4b5e97ff..1108b0ffc693 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -216,6 +216,7 @@ struct pfctl_rule {
        uint64_t                 states_cur;
        uint64_t                 states_tot;
        uint64_t                 src_nodes;
+       uint64_t                 src_nodes_type[PF_SN_MAX];
 
        uint16_t                 return_icmp;
        uint16_t                 return_icmp6;
@@ -373,6 +374,7 @@ struct pfctl_state {
        uint8_t                  set_prio[2];
        uint8_t                  rt;
        char                     rt_ifname[IFNAMSIZ];
+       uint8_t                  src_node_flags;
 };
 
 TAILQ_HEAD(pfctl_statelist, pfctl_state);
@@ -415,6 +417,7 @@ struct pfctl_src_node {
        uint64_t                creation;
        uint64_t                expire;
        struct pfctl_threshold  conn_rate;
+       pf_sn_types_t           type;
 };
 
 #define        PF_DEVICE       "/dev/pf"
diff --git a/sbin/pfctl/pf_print_state.c b/sbin/pfctl/pf_print_state.c
index e6495dfa4ca6..1d2fa45cd9d7 100644
--- a/sbin/pfctl/pf_print_state.c
+++ b/sbin/pfctl/pf_print_state.c
@@ -245,6 +245,7 @@ print_state(struct pfctl_state *s, int opts)
        uint8_t proto;
        int afto = (s->key[PF_SK_STACK].af != s->key[PF_SK_WIRE].af);
        int idx;
+       const char *sn_type_names[] = PF_SN_TYPE_NAMES;
 #ifndef __NO_STRICT_ALIGNMENT
        struct pfctl_state_key aligned_key[2];
 
@@ -405,10 +406,14 @@ print_state(struct pfctl_state *s, int opts)
                                printf(", dummynet queue (%d %d)",
                                s->dnpipe, s->dnrpipe);
                }
-               if (s->sync_flags & PFSYNC_FLAG_SRCNODE)
-                       printf(", source-track");
-               if (s->sync_flags & PFSYNC_FLAG_NATSRCNODE)
-                       printf(", sticky-address");
+               if (s->src_node_flags & PFSTATE_SRC_NODE_LIMIT)
+                       printf(", %s", sn_type_names[PF_SN_LIMIT]);
+               if (s->src_node_flags & PFSTATE_SRC_NODE_LIMIT_GLOBAL)
+                       printf(" global");
+               if (s->src_node_flags & PFSTATE_SRC_NODE_NAT)
+                       printf(", %s", sn_type_names[PF_SN_NAT]);
+               if (s->src_node_flags & PFSTATE_SRC_NODE_ROUTE)
+                       printf(", %s", sn_type_names[PF_SN_ROUTE]);
                if (s->log)
                        printf(", log");
                if (s->log & PF_LOG_ALL)
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 48e1d0b833c5..e05c96a252fc 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1064,6 +1064,15 @@ pfctl_print_rule_counters(struct pfctl_rule *rule, int 
opts)
                            rule->packets[1]),
                            (unsigned long long)(rule->bytes[0] +
                            rule->bytes[1]), (uintmax_t)rule->states_cur);
+               printf("  [ Source Nodes: %-6ju "
+                           "Limit: %-6ju "
+                           "NAT/RDR: %-6ju "
+                           "Route: %-6ju "
+                           "]\n",
+                           (uintmax_t)rule->src_nodes,
+                           (uintmax_t)rule->src_nodes_type[PF_SN_LIMIT],
+                           (uintmax_t)rule->src_nodes_type[PF_SN_NAT],
+                           (uintmax_t)rule->src_nodes_type[PF_SN_ROUTE]);
                if (!(opts & PF_OPT_DEBUG))
                        printf("  [ Inserted: uid %u pid %u "
                            "State Creations: %-6ju]\n",
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index 7a6d2fc8eed5..bb458bce24fb 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -651,6 +651,7 @@ print_src_node(struct pfctl_src_node *sn, int opts)
 {
        struct pf_addr_wrap aw;
        uint64_t min, sec;
+       const char *sn_type_names[] = PF_SN_TYPE_NAMES;
 
        memset(&aw, 0, sizeof(aw));
        if (sn->af == AF_INET)
@@ -699,6 +700,7 @@ print_src_node(struct pfctl_src_node *sn, int opts)
                                printf(", filter rule %u", sn->rule);
                        break;
                }
+               printf(", %s", sn_type_names[sn->type]);
                printf("\n");
        }
 }
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index d973fe15a5c4..076027e436dc 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -624,6 +624,21 @@ extern struct sx pf_end_lock;
 
 #define PF_ALGNMNT(off) (((off) % 2) == 0)
 
+/*
+ * At the moment there are no rules which have both NAT and RDR actions,
+ * apart from af-to rules, but those don't to source tracking for address
+ * translation. And the r->rdr pool is used for both NAT and RDR.
+ * So there is no PF_SN_RDR.
+ */
+enum pf_sn_types { PF_SN_LIMIT, PF_SN_NAT, PF_SN_ROUTE, PF_SN_MAX };
+typedef enum pf_sn_types pf_sn_types_t;
+#define PF_SN_TYPE_NAMES { \
+       "limit source-track", \
+       "NAT/RDR sticky-address", \
+       "route sticky-address", \
+       NULL \
+}
+
 #ifdef _KERNEL
 
 struct pf_kpooladdr {
@@ -822,7 +837,7 @@ struct pf_krule {
 
        counter_u64_t            states_cur;
        counter_u64_t            states_tot;
-       counter_u64_t            src_nodes;
+       counter_u64_t            src_nodes[PF_SN_MAX];
 
        u_int16_t                return_icmp;
        u_int16_t                return_icmp6;
@@ -904,6 +919,7 @@ struct pf_ksrc_node {
        sa_family_t              af;
        sa_family_t              naf;
        u_int8_t                 ruletype;
+       pf_sn_types_t            type;
        struct mtx              *lock;
 };
 #endif
@@ -1104,8 +1120,7 @@ struct pf_kstate {
        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 pf_ksrc_node     *src_node;
-       struct pf_ksrc_node     *nat_src_node;
+       struct pf_ksrc_node     *sns[PF_SN_MAX];/* source nodes */
        u_int64_t                packets[2];
        u_int64_t                bytes[2];
        u_int64_t                creation;
@@ -1118,9 +1133,10 @@ struct pf_kstate {
 };
 
 /*
- * Size <= fits 11 objects per page on LP64. Try to not grow the struct beyond 
that.
+ * 6 cache lines per struct, 10 structs per page.
+ * Try to not grow the struct beyond that.
  */
-_Static_assert(sizeof(struct pf_kstate) <= 372, "pf_kstate size crosses 372 
bytes");
+_Static_assert(sizeof(struct pf_kstate) <= 384, "pf_kstate size crosses 384 
bytes");
 #endif
 
 /*
@@ -2367,7 +2383,7 @@ extern bool                        
pf_src_node_exists(struct pf_ksrc_node **,
                                    struct pf_srchash *);
 extern struct pf_ksrc_node     *pf_find_src_node(struct pf_addr *,
                                    struct pf_krule *, sa_family_t,
-                                   struct pf_srchash **, bool);
+                                   struct pf_srchash **, pf_sn_types_t, bool);
 extern void                     pf_unlink_src_node(struct pf_ksrc_node *);
 extern u_int                    pf_free_src_nodes(struct pf_ksrc_node_list *);
 extern void                     pf_print_state(struct pf_kstate *);
@@ -2670,7 +2686,7 @@ 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_ksrc_node **, struct pf_srchash **,
-                           struct pf_kpool *);
+                           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_pdesc *,
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 378be1e72d9a..c5042a7685c2 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -389,7 +389,7 @@ 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 *,
-                           struct pfi_kkif *);
+                           struct pfi_kkif *, 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);
@@ -835,25 +835,26 @@ pf_check_threshold(struct pf_threshold *threshold)
 static bool
 pf_src_connlimit(struct pf_kstate *state)
 {
-       struct pf_overload_entry *pfoe;
-       bool limited = false;
+       struct pf_overload_entry        *pfoe;
+       struct pf_ksrc_node             *src_node = state->sns[PF_SN_LIMIT];
+       bool                             limited = false;
 
        PF_STATE_LOCK_ASSERT(state);
-       PF_SRC_NODE_LOCK(state->src_node);
+       PF_SRC_NODE_LOCK(src_node);
 
-       state->src_node->conn++;
+       src_node->conn++;
        state->src.tcp_est = 1;
-       pf_add_threshold(&state->src_node->conn_rate);
+       pf_add_threshold(&src_node->conn_rate);
 
        if (state->rule->max_src_conn &&
            state->rule->max_src_conn <
-           state->src_node->conn) {
+           src_node->conn) {
                counter_u64_add(V_pf_status.lcounters[LCNT_SRCCONN], 1);
                limited = true;
        }
 
        if (state->rule->max_src_conn_rate.limit &&
-           pf_check_threshold(&state->src_node->conn_rate)) {
+           pf_check_threshold(&src_node->conn_rate)) {
                counter_u64_add(V_pf_status.lcounters[LCNT_SRCCONNRATE], 1);
                limited = true;
        }
@@ -873,7 +874,7 @@ pf_src_connlimit(struct pf_kstate *state)
        if (pfoe == NULL)
                goto done;  /* too bad :( */
 
-       bcopy(&state->src_node->addr, &pfoe->addr, sizeof(pfoe->addr));
+       bcopy(&src_node->addr, &pfoe->addr, sizeof(pfoe->addr));
        pfoe->af = state->key[PF_SK_WIRE]->af;
        pfoe->rule = state->rule;
        pfoe->dir = state->direction;
@@ -883,7 +884,7 @@ pf_src_connlimit(struct pf_kstate *state)
        taskqueue_enqueue(taskqueue_swi, &V_pf_overloadtask);
 
 done:
-       PF_SRC_NODE_UNLOCK(state->src_node);
+       PF_SRC_NODE_UNLOCK(src_node);
        return (limited);
 }
 
@@ -985,7 +986,7 @@ pf_overload_task(void *v, int pending)
  */
 struct pf_ksrc_node *
 pf_find_src_node(struct pf_addr *src, struct pf_krule *rule, sa_family_t af,
-       struct pf_srchash **sh, bool returnlocked)
+    struct pf_srchash **sh, pf_sn_types_t sn_type, bool returnlocked)
 {
        struct pf_ksrc_node *n;
 
@@ -994,7 +995,7 @@ pf_find_src_node(struct pf_addr *src, struct pf_krule 
*rule, sa_family_t af,
        *sh = &V_pf_srchash[pf_hashsrc(src, af)];
        PF_HASHROW_LOCK(*sh);
        LIST_FOREACH(n, &(*sh)->nodes, entry)
-               if (n->rule == rule && n->af == af &&
+               if (n->rule == rule && n->af == af && n->type == sn_type &&
                    ((af == AF_INET && n->addr.v4.s_addr == src->v4.s_addr) ||
                    (af == AF_INET6 && bcmp(&n->addr, src, sizeof(*src)) == 0)))
                        break;
@@ -1039,27 +1040,43 @@ 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_addr *raddr, struct pfi_kkif *rkif)
+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)
 {
        u_short                  reason = 0;
+       struct pf_krule         *r_track = rule;
+       struct pf_ksrc_node     **sn = &(sns[sn_type]);
+       struct pf_srchash       **sh = &(snhs[sn_type]);
 
-       KASSERT((rule->rule_flag & PFRULE_SRCTRACK ||
-           rule->rdr.opts & PF_POOL_STICKYADDR),
-           ("%s for non-tracking rule %p", __func__, rule));
+       KASSERT(sn_type != PF_SN_LIMIT || (raddr == NULL && rkif == NULL),
+           ("%s: raddr and rkif must be NULL for PF_SN_LIMIT", __func__));
+
+       KASSERT(sn_type != PF_SN_LIMIT || (rule->rule_flag & PFRULE_SRCTRACK),
+           ("%s: PF_SN_LIMIT only valid for rules with PFRULE_SRCTRACK", 
__func__));
+
+       /*
+        * XXX: There could be a KASSERT for
+        * sn_type == PF_SN_LIMIT || (pool->opts & PF_POOL_STICKYADDR)
+        * but we'd need to pass pool *only* for this KASSERT.
+        */
+
+       if ( (rule->rule_flag & PFRULE_SRCTRACK) &&
+           !(rule->rule_flag & PFRULE_RULESRCTRACK))
+               r_track = &V_pf_default_rule;
 
        /*
         * Request the sh to always be locked, as we might insert a new sn.
         */
        if (*sn == NULL)
-               *sn = pf_find_src_node(src, rule, af, sh, true);
+               *sn = pf_find_src_node(src, r_track, af, sh, sn_type, true);
 
        if (*sn == NULL) {
                PF_HASHROW_ASSERT(*sh);
 
-               if (rule->max_src_nodes &&
-                   counter_u64_fetch(rule->src_nodes) >= rule->max_src_nodes) {
+               if (sn_type == PF_SN_LIMIT && rule->max_src_nodes &&
+                   counter_u64_fetch(r_track->src_nodes[sn_type]) >= 
rule->max_src_nodes) {
                        counter_u64_add(V_pf_status.lcounters[LCNT_SRCNODES], 
1);
                        reason = PFRES_SRCLIMIT;
                        goto done;
@@ -1082,26 +1099,28 @@ pf_insert_src_node(struct pf_ksrc_node **sn, struct 
pf_srchash **sh,
                        }
                }
 
-               pf_init_threshold(&(*sn)->conn_rate,
-                   rule->max_src_conn_rate.limit,
-                   rule->max_src_conn_rate.seconds);
+               if (sn_type == PF_SN_LIMIT)
+                       pf_init_threshold(&(*sn)->conn_rate,
+                           rule->max_src_conn_rate.limit,
+                           rule->max_src_conn_rate.seconds);
 
                MPASS((*sn)->lock == NULL);
                (*sn)->lock = &(*sh)->lock;
 
                (*sn)->af = af;
-               (*sn)->rule = rule;
+               (*sn)->rule = r_track;
                PF_ACPY(&(*sn)->addr, src, af);
-               PF_ACPY(&(*sn)->raddr, raddr, af);
+               if (raddr != NULL)
+                       PF_ACPY(&(*sn)->raddr, raddr, af);
                (*sn)->rkif = rkif;
                LIST_INSERT_HEAD(&(*sh)->nodes, *sn, entry);
                (*sn)->creation = time_uptime;
                (*sn)->ruletype = rule->action;
-               if ((*sn)->rule != NULL)
-                       counter_u64_add((*sn)->rule->src_nodes, 1);
+               (*sn)->type = sn_type;
+               counter_u64_add(r_track->src_nodes[sn_type], 1);
                counter_u64_add(V_pf_status.scounters[SCNT_SRC_NODE_INSERT], 1);
        } else {
-               if (rule->max_src_states &&
+               if (sn_type == PF_SN_LIMIT && rule->max_src_states &&
                    (*sn)->states >= rule->max_src_states) {
                        counter_u64_add(V_pf_status.lcounters[LCNT_SRCSTATES],
                            1);
@@ -1126,7 +1145,7 @@ pf_unlink_src_node(struct pf_ksrc_node *src)
 
        LIST_REMOVE(src, entry);
        if (src->rule)
-               counter_u64_add(src->rule->src_nodes, -1);
+               counter_u64_add(src->rule->src_nodes[src->type], -1);
 }
 
 u_int
@@ -2647,30 +2666,24 @@ pf_purge_expired_src_nodes(void)
 static void
 pf_src_tree_remove_state(struct pf_kstate *s)
 {
-       struct pf_ksrc_node *sn;
        uint32_t timeout;
 
        timeout = s->rule->timeout[PFTM_SRC_NODE] ?
            s->rule->timeout[PFTM_SRC_NODE] :
            V_pf_default_rule.timeout[PFTM_SRC_NODE];
 
-       if (s->src_node != NULL) {
-               sn = s->src_node;
-               PF_SRC_NODE_LOCK(sn);
-               if (s->src.tcp_est)
-                       --sn->conn;
-               if (--sn->states == 0)
-                       sn->expire = time_uptime + timeout;
-               PF_SRC_NODE_UNLOCK(sn);
-       }
-       if (s->nat_src_node != s->src_node && s->nat_src_node != NULL) {
-               sn = s->nat_src_node;
-               PF_SRC_NODE_LOCK(sn);
-               if (--sn->states == 0)
-                       sn->expire = time_uptime + timeout;
-               PF_SRC_NODE_UNLOCK(sn);
+       for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+               if (s->sns[sn_type] == NULL)
+                       continue;
+               PF_SRC_NODE_LOCK(s->sns[sn_type]);
+               if (sn_type == PF_SN_LIMIT && s->src.tcp_est)
+                       --(s->sns[sn_type]->conn);
+               if (--(s->sns[sn_type]->states) == 0)
+                       s->sns[sn_type]->expire = time_uptime + timeout;
+               PF_SRC_NODE_UNLOCK(s->sns[sn_type]);
+               s->sns[sn_type] = NULL;
        }
-       s->src_node = s->nat_src_node = NULL;
+
 }
 
 /*
@@ -5895,7 +5908,7 @@ nextrule:
                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, pool);
+                   &pd->act.rt_kif, NULL, &sn, &snh, pool, PF_SN_ROUTE);
                if (reason != 0)
                        goto cleanup;
        }
@@ -5997,14 +6010,18 @@ pf_create_state(struct pf_krule *r, struct pf_krule 
*nr, struct pf_krule *a,
     struct pf_udp_mapping *udp_mapping)
 {
        struct pf_kstate        *s = NULL;
-       struct pf_ksrc_node     *sn = NULL;
-       struct pf_srchash       *snh = NULL;
-       struct pf_ksrc_node     *nsn = NULL;
-       struct pf_srchash       *nsnh = NULL;
+       struct pf_ksrc_node     *sns[PF_SN_MAX] = { NULL };
+       /*
+        * XXXKS: The hash for PF_SN_LIMIT and PF_SN_ROUTE should be the same
+        *        but for PF_SN_NAT it is different. Don't try optimizing it,
+        *        just store all 3 hashes.
+        */
+       struct pf_srchash       *snhs[PF_SN_MAX] = { NULL };
        struct tcphdr           *th = &pd->hdr.tcp;
        u_int16_t                mss = V_tcp_mssdflt;
        u_short                  reason, sn_reason;
        struct pf_krule_item    *ri;
+       struct pf_kpool         *pool_route = &r->route;
 
        /* check maximums */
        if (r->max_states &&
@@ -6013,18 +6030,26 @@ pf_create_state(struct pf_krule *r, struct pf_krule 
*nr, struct pf_krule *a,
                REASON_SET(&reason, PFRES_MAXSTATES);
                goto csfailed;
        }
-       /* src node for filter rule */
-       if ((r->rule_flag & PFRULE_SRCTRACK ||
-           r->rdr.opts & PF_POOL_STICKYADDR) &&
-           (sn_reason = pf_insert_src_node(&sn, &snh, r, pd->src, pd->af,
-           &pd->act.rt_addr, pd->act.rt_kif)) != 0) {
+       /* 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) {
+           REASON_SET(&reason, sn_reason);
+               goto csfailed;
+       }
+       /* src node for route-to rule */
+       if (TAILQ_EMPTY(&pool_route->list)) /* Backwards compatibility. */
+               pool_route = &r->rdr;
+       if ((pool_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, PF_SN_ROUTE)) != 0) {
                REASON_SET(&reason, sn_reason);
                goto csfailed;
        }
        /* src node for translation rule */
        if (nr != NULL && (nr->rdr.opts & PF_POOL_STICKYADDR) &&
-           (sn_reason = pf_insert_src_node(&nsn, &nsnh, nr, 
&sk->addr[pd->sidx],
-           pd->af, &nk->addr[1], NULL)) != 0 ) {
+           (sn_reason = pf_insert_src_node(sns, snhs, nr, &sk->addr[pd->sidx],
+           pd->af, &nk->addr[1], NULL, PF_SN_NAT)) != 0 ) {
                REASON_SET(&reason, sn_reason);
                goto csfailed;
        }
@@ -6166,13 +6191,11 @@ pf_create_state(struct pf_krule *r, struct pf_krule 
*nr, struct pf_krule *a,
        /*
         * Lock order is important: first state, then source node.
         */
-       if (pf_src_node_exists(&sn, snh)) {
-               s->src_node = sn;
-               PF_HASHROW_UNLOCK(snh);
-       }
-       if (pf_src_node_exists(&nsn, nsnh)) {
-               s->nat_src_node = nsn;
-               PF_HASHROW_UNLOCK(nsnh);
+       for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+               if (pf_src_node_exists(&sns[sn_type], snhs[sn_type])) {
+                       s->sns[sn_type] = sns[sn_type];
+                       PF_HASHROW_UNLOCK(snhs[sn_type]);
+               }
        }
 
        if (tag > 0)
@@ -6223,24 +6246,17 @@ csfailed:
        uma_zfree(V_pf_state_key_z, sk);
        uma_zfree(V_pf_state_key_z, nk);
 
-       if (pf_src_node_exists(&sn, snh)) {
-               if (--sn->states == 0 && sn->expire == 0) {
-                       pf_unlink_src_node(sn);
-                       pf_free_src_node(sn);
-                       counter_u64_add(
-                           V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS], 1);
-               }
-               PF_HASHROW_UNLOCK(snh);
-       }
-
-       if (sn != nsn && pf_src_node_exists(&nsn, nsnh)) {
-               if (--nsn->states == 0 && nsn->expire == 0) {
-                       pf_unlink_src_node(nsn);
-                       pf_free_src_node(nsn);
-                       counter_u64_add(
-                           V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS], 1);
+       for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++) {
+               if (pf_src_node_exists(&sns[sn_type], snhs[sn_type])) {
+                       if (--sns[sn_type]->states == 0 &&
+                           sns[sn_type]->expire == 0) {
+                               pf_unlink_src_node(sns[sn_type]);
+                               pf_free_src_node(sns[sn_type]);
+                               counter_u64_add(
+                                   
V_pf_status.scounters[SCNT_SRC_NODE_REMOVALS], 1);
+                       }
+                       PF_HASHROW_UNLOCK(snhs[sn_type]);
                }
-               PF_HASHROW_UNLOCK(nsnh);
        }
 
 drop:
@@ -6575,7 +6591,7 @@ pf_tcp_track_full(struct pf_kstate **state, struct 
pf_pdesc *pd,
                                pf_set_protostate(*state, pdst,
                                    TCPS_ESTABLISHED);
                                if (src->state == TCPS_ESTABLISHED &&
-                                   (*state)->src_node != NULL &&
+                                   (*state)->sns[PF_SN_LIMIT] != NULL &&
                                    pf_src_connlimit(*state)) {
                                        REASON_SET(reason, PFRES_SRCLIMIT);
                                        return (PF_DROP);
@@ -6746,7 +6762,7 @@ pf_tcp_track_sloppy(struct pf_kstate **state, struct 
pf_pdesc *pd, u_short *reas
                if (dst->state == TCPS_SYN_SENT) {
                        pf_set_protostate(*state, pdst, TCPS_ESTABLISHED);
                        if (src->state == TCPS_ESTABLISHED &&
-                           (*state)->src_node != NULL &&
+                           (*state)->sns[PF_SN_LIMIT] != NULL &&
                            pf_src_connlimit(*state)) {
                                REASON_SET(reason, PFRES_SRCLIMIT);
                                return (PF_DROP);
@@ -6764,7 +6780,7 @@ pf_tcp_track_sloppy(struct pf_kstate **state, struct 
pf_pdesc *pd, u_short *reas
                        pf_set_protostate(*state, PF_PEER_BOTH,
                            TCPS_ESTABLISHED);
                        dst->state = src->state = TCPS_ESTABLISHED;
-                       if ((*state)->src_node != NULL &&
+                       if ((*state)->sns[PF_SN_LIMIT] != NULL &&
                            pf_src_connlimit(*state)) {
                                REASON_SET(reason, PFRES_SRCLIMIT);
                                return (PF_DROP);
@@ -6831,7 +6847,7 @@ pf_synproxy(struct pf_pdesc *pd, struct pf_kstate 
**state, u_short *reason)
                    (ntohl(th->th_seq) != (*state)->src.seqlo + 1)) {
                        REASON_SET(reason, PFRES_SYNPROXY);
                        return (PF_DROP);
-               } else if ((*state)->src_node != NULL &&
+               } else if ((*state)->sns[PF_SN_LIMIT] != NULL &&
                    pf_src_connlimit(*state)) {
                        REASON_SET(reason, PFRES_SRCLIMIT);
                        return (PF_DROP);
@@ -10023,17 +10039,21 @@ pf_counters_inc(int action, struct pf_pdesc *pd,
                                
pf_counter_u64_add_protected(&s->nat_rule->bytes[dirndx],
                                    pd->tot_len);
                        }
-                       if (s->src_node != NULL) {
-                               counter_u64_add(s->src_node->packets[dirndx],
-                                   1);
-                               counter_u64_add(s->src_node->bytes[dirndx],
-                                   pd->tot_len);
-                       }
-                       if (s->nat_src_node != NULL) {
-                               
counter_u64_add(s->nat_src_node->packets[dirndx],
-                                   1);
-                               counter_u64_add(s->nat_src_node->bytes[dirndx],
-                                   pd->tot_len);
+                       /*
+                        * Source nodes are accessed unlocked here.
+                        * But since we are operating with stateful tracking
+                        * and the state is locked, those SNs could not have
+                        * been freed.
+                        */
+                       for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; 
sn_type++) {
+                               if (s->sns[sn_type] != NULL) {
+                                       counter_u64_add(
+                                           s->sns[sn_type]->packets[dirndx],
+                                           1);
+                                       counter_u64_add(
+                                           s->sns[sn_type]->bytes[dirndx],
+                                           pd->tot_len);
+                               }
                        }
                        dirndx = (dir == s->direction) ? 0 : 1;
                        s->packets[dirndx]++;
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
index 45652f174884..dfa86e7f1d6d 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -649,6 +649,12 @@ struct pf_rule {
 #define        PFSTATE_SCRUBMASK 
(PFSTATE_NODF|PFSTATE_RANDOMID|PFSTATE_SCRUB_TCP)
 #define        PFSTATE_SETMASK   (PFSTATE_SETTOS|PFSTATE_SETPRIO)
 
+/* pfctl_state->src_node_flags */
+#define PFSTATE_SRC_NODE_LIMIT         0x01
+#define PFSTATE_SRC_NODE_NAT           0x02
+#define PFSTATE_SRC_NODE_ROUTE         0x04
+#define PFSTATE_SRC_NODE_LIMIT_GLOBAL  0x10
+
 #define PFSTATE_HIWAT          100000  /* default state table size */
 #define PFSTATE_ADAPT_START    60000   /* default adaptive timeout start */
 #define PFSTATE_ADAPT_END      120000  /* default adaptive timeout end */
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index bea2cf1a5331..6553981a1059 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -352,7 +352,8 @@ pfattach_vnet(void)
        }
        V_pf_default_rule.states_cur = counter_u64_alloc(M_WAITOK);
        V_pf_default_rule.states_tot = counter_u64_alloc(M_WAITOK);
-       V_pf_default_rule.src_nodes = counter_u64_alloc(M_WAITOK);
+       for (pf_sn_types_t sn_type = 0; sn_type<PF_SN_MAX; sn_type++)
+               V_pf_default_rule.src_nodes[sn_type] = 
counter_u64_alloc(M_WAITOK);
 
        V_pf_default_rule.timestamp = uma_zalloc_pcpu(pf_timestamp_pcpu_zone,
            M_WAITOK | M_ZERO);
@@ -1854,7 +1855,8 @@ pf_krule_free(struct pf_krule *rule)
        }
        counter_u64_free(rule->states_cur);
        counter_u64_free(rule->states_tot);
-       counter_u64_free(rule->src_nodes);
+       for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++)
+               counter_u64_free(rule->src_nodes[sn_type]);
        uma_zfree_pcpu(pf_timestamp_pcpu_zone, rule->timestamp);
 
        mtx_destroy(&rule->nat.mtx);
@@ -2090,7 +2092,8 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket,
        }
        rule->states_cur = counter_u64_alloc(M_WAITOK);
        rule->states_tot = counter_u64_alloc(M_WAITOK);
-       rule->src_nodes = counter_u64_alloc(M_WAITOK);
+       for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++)
+               rule->src_nodes[sn_type] = counter_u64_alloc(M_WAITOK);
        rule->cuid = uid;
        rule->cpid = pid;
        TAILQ_INIT(&rule->rdr.list);
@@ -3651,7 +3654,8 @@ DIOCGETRULENV_error:
                        }
                        newrule->states_cur = counter_u64_alloc(M_WAITOK);
                        newrule->states_tot = counter_u64_alloc(M_WAITOK);
-                       newrule->src_nodes = counter_u64_alloc(M_WAITOK);
+                       for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; 
sn_type++)
+                               newrule->src_nodes[sn_type] = 
counter_u64_alloc(M_WAITOK);
                        newrule->cuid = td->td_ucred->cr_ruid;
                        newrule->cpid = td->td_proc ? td->td_proc->p_pid : 0;
                        TAILQ_INIT(&newrule->nat.list);
@@ -5672,9 +5676,14 @@ pfsync_state_export(union pfsync_state_union *sp, struct 
pf_kstate *st, int msg_
                            __func__, msg_version);
        }
 
-       if (st->src_node)
+       /*
+        * XXX Why do we bother pfsyncing source node information if source
+        * nodes are not synced? Showing users that there is source tracking
+        * when there is none seems useless.
+        */
+       if (st->sns[PF_SN_LIMIT] != NULL)
                sp->pfs_1301.sync_flags |= PFSYNC_FLAG_SRCNODE;
-       if (st->nat_src_node)
+       if (st->sns[PF_SN_NAT] != NULL || st->sns[PF_SN_ROUTE])
                sp->pfs_1301.sync_flags |= PFSYNC_FLAG_NATSRCNODE;
 
        sp->pfs_1301.id = st->id;
@@ -5738,11 +5747,10 @@ pf_state_export(struct pf_state_export *sp, struct 
pf_kstate *st)
        /* 8 bits for the old libpfctl, 16 bits for the new libpfctl */
        sp->state_flags_compat = st->state_flags;
        sp->state_flags = htons(st->state_flags);
-       if (st->src_node)
+       if (st->sns[PF_SN_LIMIT] != NULL)
                sp->sync_flags |= PFSYNC_FLAG_SRCNODE;
-       if (st->nat_src_node)
+       if (st->sns[PF_SN_NAT] != NULL || st->sns[PF_SN_ROUTE] != NULL)
                sp->sync_flags |= PFSYNC_FLAG_NATSRCNODE;
-
        sp->id = st->id;
        sp->creatorid = st->creatorid;
        pf_state_peer_hton(&st->src, &sp->src);
@@ -6007,10 +6015,13 @@ pf_kill_srcnodes(struct pfioc_src_node_kill *psnk)
 
                PF_HASHROW_LOCK(ih);
                LIST_FOREACH(s, &ih->states, entry) {
-                       if (s->src_node && s->src_node->expire == 1)
-                               s->src_node = NULL;
-                       if (s->nat_src_node && s->nat_src_node->expire == 1)
-                               s->nat_src_node = NULL;
+                       for(pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX;
+                           sn_type++) {
+                               if (s->sns[sn_type] &&
+                                   s->sns[sn_type]->expire == 1) {
+                                       s->sns[sn_type] = NULL;
+                               }
+                       }
                }
                PF_HASHROW_UNLOCK(ih);
        }
@@ -6834,7 +6845,8 @@ pf_unload_vnet(void)
        }
        counter_u64_free(V_pf_default_rule.states_cur);
        counter_u64_free(V_pf_default_rule.states_tot);
-       counter_u64_free(V_pf_default_rule.src_nodes);
+       for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++)
+               counter_u64_free(V_pf_default_rule.src_nodes[sn_type]);
        uma_zfree_pcpu(pf_timestamp_pcpu_zone, V_pf_default_rule.timestamp);
 
        for (int i = 0; i < PFRES_MAX; i++)
diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c
index 23c7ad1c0a66..9c2d7b4c71b6 100644
--- a/sys/netpfil/pf/pf_lb.c
+++ b/sys/netpfil/pf/pf_lb.c
@@ -75,9 +75,11 @@ static void           pf_hash(struct pf_addr *, struct 
pf_addr *,
                            struct pf_poolhashkey *, sa_family_t);
 static struct pf_krule *pf_match_translation(struct pf_pdesc *,
                            int, struct pf_kanchor_stackframe *);
-static int pf_get_sport(struct pf_pdesc *, struct pf_krule *,
-    struct pf_addr *, uint16_t *, uint16_t, uint16_t, struct pf_ksrc_node **,
-    struct pf_srchash **, struct pf_kpool *, struct pf_udp_mapping **);
+static int              pf_get_sport(struct pf_pdesc *, struct pf_krule *,
+                           struct pf_addr *, uint16_t *, uint16_t, uint16_t,
+                           struct pf_ksrc_node **, struct pf_srchash **,
+                           struct pf_kpool *, struct pf_udp_mapping **,
+                           pf_sn_types_t);
 static bool             pf_islinklocal(const sa_family_t, const struct pf_addr 
*);
 
 #define mix(a,b,c) \
@@ -231,7 +233,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
     struct pf_addr *naddr, uint16_t *nport, uint16_t low,
     uint16_t high, struct pf_ksrc_node **sn,
     struct pf_srchash **sh, struct pf_kpool *rpool,
-    struct pf_udp_mapping **udp_mapping)
+    struct pf_udp_mapping **udp_mapping, pf_sn_types_t sn_type)
 {
        struct pf_state_key_cmp key;
        struct pf_addr          init_addr;
@@ -262,7 +264,8 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
                                /* Try to find a src_node as per pf_map_addr(). 
*/
                                if (*sn == NULL && rpool->opts & 
PF_POOL_STICKYADDR &&
                                    (rpool->opts & PF_POOL_TYPEMASK) != 
PF_POOL_NONE)
-                                       *sn = pf_find_src_node(&pd->nsaddr, r, 
pd->af, sh, false);
+                                       *sn = pf_find_src_node(&pd->nsaddr, r,
+                                           pd->af, sh, sn_type, false);
                                if (*sn != NULL)
                                        PF_SRC_NODE_UNLOCK(*sn);
                                return (0);
@@ -276,7 +279,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
        }
 
        if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL, &init_addr,
-           sn, sh, rpool))
+           sn, sh, rpool, sn_type))
                goto failed;
 
        if (pd->proto == IPPROTO_ICMP) {
@@ -400,7 +403,7 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_krule *r,
                         */
                        (*sn) = NULL;
                        if (pf_map_addr_sn(pd->naf, r, &pd->nsaddr, naddr, NULL,
-                           &init_addr, sn, sh, rpool))
+                           &init_addr, sn, sh, rpool, sn_type))
                                return (1);
                        break;
                case PF_POOL_NONE:
@@ -453,14 +456,14 @@ pf_get_mape_sport(struct pf_pdesc *pd, struct pf_krule *r,
                low = (i << ashift) | psmask;
                if (!pf_get_sport(pd, r,
                    naddr, nport, low, low | highmask, sn, sh, &r->rdr,
-                   udp_mapping))
+                   udp_mapping, PF_SN_NAT))
                        return (0);
        }
        for (i = cut - 1; i > 0; i--) {
                low = (i << ashift) | psmask;
                if (!pf_get_sport(pd, r,
                    naddr, nport, low, low | highmask, sn, sh, &r->rdr,
-                   udp_mapping))
+                   udp_mapping, PF_SN_NAT))
                        return (0);
        }
        return (1);
@@ -642,7 +645,8 @@ 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_ksrc_node **sn, struct pf_srchash **sh, struct pf_kpool *rpool)
+    struct pf_ksrc_node **sn, struct pf_srchash **sh, struct pf_kpool *rpool,
+    pf_sn_types_t sn_type)
 {
        u_short                  reason = 0;
 
@@ -655,7 +659,7 @@ 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, false);
+               *sn = pf_find_src_node(saddr, r, af, sh, sn_type, false);
 
        if (*sn != NULL) {
                PF_SRC_NODE_LOCK_ASSERT(*sn);
@@ -780,7 +784,7 @@ pf_get_translation(struct pf_pdesc *pd, int off,
                                goto notrans;
                        }
                } else if (pf_get_sport(pd, r, naddr, nportp, low, high, &sn,
-                   &sh, &r->rdr, udp_mapping)) {
+                   &sh, &r->rdr, udp_mapping, PF_SN_NAT)) {
                        DPFPRINTF(PF_DEBUG_MISC,
                            ("pf: NAT proxy port allocation (%u-%u) failed\n",
                            r->rdr.proxy_port[0], r->rdr.proxy_port[1]));
@@ -868,7 +872,7 @@ pf_get_translation(struct pf_pdesc *pd, int off,
                uint16_t cut, low, high, nport;
 
                reason = pf_map_addr_sn(pd->af, r, &pd->nsaddr, naddr, NULL,
-                   NULL, &sn, &sh, &r->rdr);
+                   NULL, &sn, &sh, &r->rdr, PF_SN_NAT);
                if (reason != 0)
                        goto notrans;
                if ((r->rdr.opts & PF_POOL_TYPEMASK) == PF_POOL_BITMASK)
@@ -1007,7 +1011,8 @@ pf_get_transaddr_af(struct pf_krule *r, struct pf_pdesc 
*pd)
 
        /* get source address and port */
        if (pf_get_sport(pd, r, &nsaddr, &nport,
-           r->nat.proxy_port[0], r->nat.proxy_port[1], &sns, &sh, &r->nat, 
NULL)) {
+           r->nat.proxy_port[0], r->nat.proxy_port[1], &sns, &sh, &r->nat,
+           NULL, PF_SN_NAT)) {
                DPFPRINTF(PF_DEBUG_MISC,
                    ("pf: af-to NAT proxy port allocation (%u-%u) failed",
                    r->nat.proxy_port[0], r->nat.proxy_port[1]));
@@ -1051,7 +1056,7 @@ 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,
-                   &sns, NULL, &r->rdr))
+                   &sns, 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 4cdb16d1fbba..73c39e1f7471 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -186,9 +186,9 @@ dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, 
struct pf_kstate *s,
        nlattr_add_u8(nw, PF_ST_TIMEOUT, s->timeout);
        nlattr_add_u16(nw, PF_ST_STATE_FLAGS, s->state_flags);
        uint8_t sync_flags = 0;
-       if (s->src_node)
+       if (s->sns[PF_SN_LIMIT] != NULL)
                sync_flags |= PFSYNC_FLAG_SRCNODE;
-       if (s->nat_src_node)
+       if (s->sns[PF_SN_NAT] != NULL || s->sns[PF_SN_ROUTE])
                sync_flags |= PFSYNC_FLAG_NATSRCNODE;
        nlattr_add_u8(nw, PF_ST_SYNC_FLAGS, sync_flags);
        nlattr_add_u64(nw, PF_ST_ID, s->id);
@@ -210,6 +210,17 @@ dump_state(struct nlpcb *nlp, const struct nlmsghdr *hdr, 
struct pf_kstate *s,
        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);
+       uint8_t src_node_flags = 0;
+       if (s->sns[PF_SN_LIMIT] != NULL) {
+               src_node_flags |= PFSTATE_SRC_NODE_LIMIT;
+               if (s->sns[PF_SN_LIMIT]->rule == &V_pf_default_rule)
+                       src_node_flags |= PFSTATE_SRC_NODE_LIMIT_GLOBAL;
+       }
+       if (s->sns[PF_SN_NAT] != NULL)
+               src_node_flags |= PFSTATE_SRC_NODE_NAT;
+       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);
 
        if (!dump_state_peer(nw, PF_ST_PEER_SRC, &s->src))
                goto enomem;
@@ -854,6 +865,7 @@ pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate 
*npt)
        struct genlmsghdr               *ghdr_new;
        struct pf_kruleset              *ruleset;
        struct pf_krule                 *rule;
+       u_int64_t                        src_nodes_total = 0;
        int                              rs_num;
        int                              error;
 
@@ -985,7 +997,12 @@ pf_handle_getrule(struct nlmsghdr *hdr, struct nl_pstate 
*npt)
        nlattr_add_u64(nw, PF_RT_TIMESTAMP, pf_get_timestamp(rule));
        nlattr_add_u64(nw, PF_RT_STATES_CUR, 
counter_u64_fetch(rule->states_cur));
        nlattr_add_u64(nw, PF_RT_STATES_TOTAL, 
counter_u64_fetch(rule->states_tot));
-       nlattr_add_u64(nw, PF_RT_SRC_NODES, counter_u64_fetch(rule->src_nodes));
+       for (pf_sn_types_t sn_type=0; sn_type<PF_SN_MAX; sn_type++)
+               src_nodes_total += counter_u64_fetch(rule->src_nodes[sn_type]);
+       nlattr_add_u64(nw, PF_RT_SRC_NODES, src_nodes_total);
+       nlattr_add_u64(nw, PF_RT_SRC_NODES_LIMIT, 
counter_u64_fetch(rule->src_nodes[PF_SN_LIMIT]));
+       nlattr_add_u64(nw, PF_RT_SRC_NODES_NAT, 
counter_u64_fetch(rule->src_nodes[PF_SN_NAT]));
+       nlattr_add_u64(nw, PF_RT_SRC_NODES_ROUTE, 
counter_u64_fetch(rule->src_nodes[PF_SN_ROUTE]));
 
        error = pf_kanchor_copyout(ruleset, rule, anchor_call, 
sizeof(anchor_call));
        MPASS(error == 0);
@@ -1785,6 +1802,8 @@ pf_handle_get_srcnodes(struct nlmsghdr *hdr, struct 
nl_pstate *npt)
*** 252 LINES SKIPPED ***

Reply via email to