git: 16303d2ba6b0 - main - pf: improve source node error handling

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Wed, 03 May 2023 09:59:27 UTC
The branch main has been updated by kp:

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

commit 16303d2ba6b099afa8ec8f2e97deca2785caa082
Author:     Kajetan Staszkiewicz <vegeta@tuxpowered.net>
AuthorDate: 2023-05-03 08:31:05 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2023-05-03 08:31:05 +0000

    pf: improve source node error handling
    
    Functions manipulating source nodes can fail due to various reasons like
    memory allocation errors, hitting configured limits or lack of
    redirection targets. Ensure those errors are properly caught and
    propagated in the code. Increase the error counters not only when
    parsing the main ruleset but the NAT ruleset too.
    
    Cherry-picked from development of D39880
    
    Reviewed by:    kp
    Sponsored by:   InnoGames GmbH
    Differential Revision:  https://reviews.freebsd.org/D39940
---
 sys/net/pfvar.h        |  2 +-
 sys/netpfil/pf/pf.c    | 50 ++++++++++++++++++++++++++++++--------------------
 sys/netpfil/pf/pf_lb.c | 47 ++++++++++++++++++++++++++++-------------------
 3 files changed, 59 insertions(+), 40 deletions(-)

diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 412453f8dd25..f72c3980438d 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -2403,7 +2403,7 @@ int			 pf_step_out_of_keth_anchor(struct pf_keth_anchor_stackframe *,
 			    struct pf_keth_rule **, struct pf_keth_rule **,
 			    int *);
 
-int			 pf_map_addr(u_int8_t, struct pf_krule *,
+u_short			 pf_map_addr(u_int8_t, struct pf_krule *,
 			    struct pf_addr *, struct pf_addr *,
 			    struct pf_addr *, struct pf_ksrc_node **);
 struct pf_krule		*pf_get_translation(struct pf_pdesc *, struct mbuf *,
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index e5f2a5ea57a0..ee17df0c866f 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -329,7 +329,7 @@ static struct pf_kstate	*pf_find_state(struct pfi_kkif *,
 			    struct pf_state_key_cmp *, u_int);
 static int		 pf_src_connlimit(struct pf_kstate **);
 static void		 pf_overload_task(void *v, int pending);
-static int		 pf_insert_src_node(struct pf_ksrc_node **,
+static u_short		 pf_insert_src_node(struct pf_ksrc_node **,
 			    struct pf_krule *, struct pf_addr *, sa_family_t);
 static u_int		 pf_purge_expired_states(u_int, int);
 static void		 pf_purge_unlinked_rules(void);
@@ -865,11 +865,12 @@ pf_free_src_node(struct pf_ksrc_node *sn)
 	uma_zfree(V_pf_sources_z, sn);
 }
 
-static int
+static u_short
 pf_insert_src_node(struct pf_ksrc_node **sn, struct pf_krule *rule,
     struct pf_addr *src, sa_family_t af)
 {
-	struct pf_srchash *sh = NULL;
+	u_short			 reason = 0;
+	struct pf_srchash	*sh = NULL;
 
 	KASSERT((rule->rule_flag & PFRULE_SRCTRACK ||
 	    rule->rpool.opts & PF_POOL_STICKYADDR),
@@ -881,15 +882,19 @@ pf_insert_src_node(struct pf_ksrc_node **sn, struct pf_krule *rule,
 	if (*sn == NULL) {
 		PF_HASHROW_ASSERT(sh);
 
-		if (!rule->max_src_nodes ||
-		    counter_u64_fetch(rule->src_nodes) < rule->max_src_nodes)
-			(*sn) = uma_zalloc(V_pf_sources_z, M_NOWAIT | M_ZERO);
-		else
-			counter_u64_add(V_pf_status.lcounters[LCNT_SRCNODES],
-			    1);
+		if (rule->max_src_nodes &&
+		    counter_u64_fetch(rule->src_nodes) >= rule->max_src_nodes) {
+			counter_u64_add(V_pf_status.lcounters[LCNT_SRCNODES], 1);
+			PF_HASHROW_UNLOCK(sh);
+			reason = PFRES_SRCLIMIT;
+			goto done;
+		}
+
+		(*sn) = uma_zalloc(V_pf_sources_z, M_NOWAIT | M_ZERO);
 		if ((*sn) == NULL) {
 			PF_HASHROW_UNLOCK(sh);
-			return (-1);
+			reason = PFRES_MEMORY;
+			goto done;
 		}
 
 		for (int i = 0; i < 2; i++) {
@@ -899,7 +904,8 @@ pf_insert_src_node(struct pf_ksrc_node **sn, struct pf_krule *rule,
 			if ((*sn)->bytes[i] == NULL || (*sn)->packets[i] == NULL) {
 				pf_free_src_node(*sn);
 				PF_HASHROW_UNLOCK(sh);
-				return (-1);
+				reason = PFRES_MEMORY;
+				goto done;
 			}
 		}
 
@@ -926,10 +932,12 @@ pf_insert_src_node(struct pf_ksrc_node **sn, struct pf_krule *rule,
 		    (*sn)->states >= rule->max_src_states) {
 			counter_u64_add(V_pf_status.lcounters[LCNT_SRCSTATES],
 			    1);
-			return (-1);
+			reason = PFRES_SRCLIMIT;
+			goto done;
 		}
 	}
-	return (0);
+done:
+	return (reason);
 }
 
 void
@@ -4581,7 +4589,7 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
 	struct pf_ksrc_node	*sn = NULL;
 	struct tcphdr		*th = &pd->hdr.tcp;
 	u_int16_t		 mss = V_tcp_mssdflt;
-	u_short			 reason;
+	u_short			 reason, sn_reason;
 
 	/* check maximums */
 	if (r->max_states &&
@@ -4593,14 +4601,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) &&
-	    pf_insert_src_node(&sn, r, pd->src, pd->af) != 0) {
-		REASON_SET(&reason, PFRES_SRCLIMIT);
+	    (sn_reason = pf_insert_src_node(&sn, r, pd->src, pd->af)) != 0) {
+		REASON_SET(&reason, sn_reason);
 		goto csfailed;
 	}
 	/* src node for translation rule */
 	if (nr != NULL && (nr->rpool.opts & PF_POOL_STICKYADDR) &&
-	    pf_insert_src_node(&nsn, nr, &sk->addr[pd->sidx], pd->af)) {
-		REASON_SET(&reason, PFRES_SRCLIMIT);
+	    (sn_reason = pf_insert_src_node(&nsn, nr, &sk->addr[pd->sidx],
+	    pd->af)) != 0 ) {
+		REASON_SET(&reason, sn_reason);
 		goto csfailed;
 	}
 	s = pf_alloc_state(M_NOWAIT);
@@ -4689,8 +4698,9 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
 	}
 
 	if (r->rt) {
-		if (pf_map_addr(pd->af, r, pd->src, &s->rt_addr, NULL, &sn)) {
-			REASON_SET(&reason, PFRES_MAPFAILED);
+		/* pf_map_addr increases the reason counters */
+		if ((reason = pf_map_addr(pd->af, r, pd->src, &s->rt_addr, NULL,
+		    &sn)) != 0) {
 			pf_src_tree_remove_state(s);
 			s->timeout = PFTM_UNLINKED;
 			STATE_DEC_COUNTERS(s);
diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c
index ca8593a1c37d..a2f0224f842c 100644
--- a/sys/netpfil/pf/pf_lb.c
+++ b/sys/netpfil/pf/pf_lb.c
@@ -342,10 +342,11 @@ pf_get_mape_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r,
 	return (1);
 }
 
-int
+u_short
 pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
     struct pf_addr *naddr, struct pf_addr *init_addr, struct pf_ksrc_node **sn)
 {
+	u_short			 reason = 0;
 	struct pf_kpool		*rpool = &r->rpool;
 	struct pf_addr		*raddr = NULL, *rmask = NULL;
 	struct pf_srchash	*sh = NULL;
@@ -364,8 +365,10 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 		/* 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))
-			return (1);
+		if (PF_AEQ(naddr, &(*sn)->raddr, af)) {
+			reason = PFRES_MAPFAILED;
+			goto done;
+		}
 
 		PF_ACPY(naddr, &(*sn)->raddr, af);
 		if (V_pf_status.debug >= PF_DEBUG_NOISY) {
@@ -375,15 +378,15 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 			pf_print_host(naddr, 0, af);
 			printf("\n");
 		}
-		return (0);
+		goto done;
 	}
 
 	mtx_lock(&rpool->mtx);
 	/* Find the route using chosen algorithm. Store the found route
 	   in src_node if it was given or found. */
 	if (rpool->cur->addr.type == PF_ADDR_NOROUTE) {
-		mtx_unlock(&rpool->mtx);
-		return (1);
+		reason = PFRES_MAPFAILED;
+		goto done_pool_mtx;
 	}
 	if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
 		switch (af) {
@@ -392,8 +395,8 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 			if (rpool->cur->addr.p.dyn->pfid_acnt4 < 1 &&
 			    (rpool->opts & PF_POOL_TYPEMASK) !=
 			    PF_POOL_ROUNDROBIN) {
-				mtx_unlock(&rpool->mtx);
-				return (1);
+				reason = PFRES_MAPFAILED;
+				goto done_pool_mtx;
 			}
 			raddr = &rpool->cur->addr.p.dyn->pfid_addr4;
 			rmask = &rpool->cur->addr.p.dyn->pfid_mask4;
@@ -404,8 +407,8 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 			if (rpool->cur->addr.p.dyn->pfid_acnt6 < 1 &&
 			    (rpool->opts & PF_POOL_TYPEMASK) !=
 			    PF_POOL_ROUNDROBIN) {
-				mtx_unlock(&rpool->mtx);
-				return (1);
+				reason = PFRES_MAPFAILED;
+				goto done_pool_mtx;
 			}
 			raddr = &rpool->cur->addr.p.dyn->pfid_addr6;
 			rmask = &rpool->cur->addr.p.dyn->pfid_mask6;
@@ -414,8 +417,8 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 		}
 	} else if (rpool->cur->addr.type == PF_ADDR_TABLE) {
 		if ((rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_ROUNDROBIN) {
-			mtx_unlock(&rpool->mtx);
-			return (1); /* unsupported */
+			reason = PFRES_MAPFAILED;
+			goto done_pool_mtx; /* unsupported */
 		}
 	} else {
 		raddr = &rpool->cur->addr.v.a.addr;
@@ -503,8 +506,8 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 				/* table contains no address of type 'af' */
 				if (rpool->cur != acur)
 					goto try_next;
-				mtx_unlock(&rpool->mtx);
-				return (1);
+				reason = PFRES_MAPFAILED;
+				goto done_pool_mtx;
 			}
 		} else if (rpool->cur->addr.type == PF_ADDR_DYNIFTL) {
 			rpool->tblidx = -1;
@@ -513,8 +516,8 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 				/* table contains no address of type 'af' */
 				if (rpool->cur != acur)
 					goto try_next;
-				mtx_unlock(&rpool->mtx);
-				return (1);
+				reason = PFRES_MAPFAILED;
+				goto done_pool_mtx;
 			}
 		} else {
 			raddr = &rpool->cur->addr.v.a.addr;
@@ -533,8 +536,6 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 	if (*sn != NULL)
 		PF_ACPY(&(*sn)->raddr, naddr, af);
 
-	mtx_unlock(&rpool->mtx);
-
 	if (V_pf_status.debug >= PF_DEBUG_NOISY &&
 	    (rpool->opts & PF_POOL_TYPEMASK) != PF_POOL_NONE) {
 		printf("pf_map_addr: selected address ");
@@ -542,7 +543,15 @@ pf_map_addr(sa_family_t af, struct pf_krule *r, struct pf_addr *saddr,
 		printf("\n");
 	}
 
-	return (0);
+done_pool_mtx:
+	mtx_unlock(&rpool->mtx);
+
+done:
+	if (reason) {
+		counter_u64_add(V_pf_status.counters[reason], 1);
+	}
+
+	return (reason);
 }
 
 struct pf_krule *