Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 15 Sep 2025 11:54:24 GMT
From:      Kristof Provost <kp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: c00aca9a714e - main - pf: Show pf fragment reassembly counters.
Message-ID:  <202509151154.58FBsOMZ025408@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by kp:

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

commit c00aca9a714ee3cdb867d4014898ec4e345465a5
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2025-08-21 08:09:13 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2025-09-15 09:32:34 +0000

    pf: Show pf fragment reassembly counters.
    
    Framgent count and statistics are stored in struct pf_status.  From
    there pfctl(8) and systat(1) collect and show them.  Note that pfctl
    -s info needs the -v switch to show fragments.
    
    input claudio@; OK henning@
    
    Obtained from:  OpenBSD, bluhm <bluhm@openbsd.org>, 19e99d0613
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 lib/libpfctl/libpfctl.c   |  3 +++
 lib/libpfctl/libpfctl.h   |  2 ++
 sbin/pfctl/pfctl_parser.c | 14 ++++++++++++++
 sys/net/pfvar.h           |  2 ++
 sys/netpfil/pf/pf.h       |  6 ++++++
 sys/netpfil/pf/pf_ioctl.c |  6 ++++++
 sys/netpfil/pf/pf_nl.c    |  3 +++
 sys/netpfil/pf/pf_nl.h    |  2 ++
 sys/netpfil/pf/pf_norm.c  | 12 ++++++++++++
 9 files changed, 50 insertions(+)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 190ee46baf21..b96b973ddc7c 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -391,6 +391,8 @@ static const struct snl_attr_parser ap_getstatus[] = {
 	{ .type = PF_GS_CHKSUM, .off = _OUT(pf_chksum), .arg_u32 = PF_MD5_DIGEST_LENGTH, .cb = snl_attr_get_bytes },
 	{ .type = PF_GS_BCOUNTERS, .off = _OUT(bcounters), .arg_u32 = 2 * 2, .cb = snl_attr_get_uint64_array },
 	{ .type = PF_GS_PCOUNTERS, .off = _OUT(pcounters), .arg_u32 = 2 * 2 * 2, .cb = snl_attr_get_uint64_array },
+	{ .type = PF_GS_NCOUNTERS, .off = _OUT(ncounters), .cb = snl_attr_get_counters },
+	{ .type = PF_GS_FRAGMENTS, .off = _OUT(fragments), .cb = snl_attr_get_uint64 },
 };
 SNL_DECLARE_PARSER(getstatus_parser, struct genlmsghdr, snl_f_p_empty, ap_getstatus);
 #undef _OUT
@@ -429,6 +431,7 @@ pfctl_get_status_h(struct pfctl_handle *h)
 	TAILQ_INIT(&status->lcounters);
 	TAILQ_INIT(&status->fcounters);
 	TAILQ_INIT(&status->scounters);
+	TAILQ_INIT(&status->ncounters);
 
 	while ((hdr = snl_read_reply_multi(&h->ss, seq_id, &e)) != NULL) {
 		if (! snl_parse_nlmsg(&h->ss, hdr, &getstatus_parser, status))
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index da16d5179ec0..dd76cab163b5 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -62,6 +62,8 @@ struct pfctl_status {
 	struct pfctl_status_counters	 lcounters;
 	struct pfctl_status_counters	 fcounters;
 	struct pfctl_status_counters	 scounters;
+	struct pfctl_status_counters	 ncounters;
+	uint64_t	fragments;
 	uint64_t	pcounters[2][2][2];
 	uint64_t	bcounters[2][2];
 };
diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c
index a2b1e4d0841d..9609e880584f 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -614,6 +614,20 @@ print_status(struct pfctl_status *s, struct pfctl_syncookies *cookies, int opts)
 				printf("%14s\n", "");
 		}
 	}
+	if (opts & PF_OPT_VERBOSE) {
+		printf("Fragments\n");
+		printf("  %-25s %14ju %14s\n", "current entries",
+		    s->fragments, "");
+		TAILQ_FOREACH(c, &s->ncounters, entry) {
+			printf("  %-25s %14ju ", c->name,
+			    c->counter);
+			if (runtime > 0)
+				printf("%14.1f/s\n",
+				    (double)c->counter / (double)runtime);
+			else
+				printf("%14s\n", "");
+		}
+	}
 	printf("Counters\n");
 	TAILQ_FOREACH(c, &s->counters, entry) {
 		printf("  %-25s %14ju ", c->name, c->counter);
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index e6fb1c2c3e1b..af207d6ece24 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -1751,6 +1751,7 @@ struct pf_kstatus {
 	counter_u64_t	lcounters[KLCNT_MAX]; /* limit counters */
 	struct pf_counter_u64	fcounters[FCNT_MAX]; /* state operation counters */
 	counter_u64_t	scounters[SCNT_MAX]; /* src_node operation counters */
+	counter_u64_t	ncounters[NCNT_MAX];
 	uint32_t	states;
 	uint32_t	src_nodes;
 	uint32_t	running;
@@ -2440,6 +2441,7 @@ int	pf_match_port(u_int8_t, u_int16_t, u_int16_t, u_int16_t);
 
 void	pf_normalize_init(void);
 void	pf_normalize_cleanup(void);
+uint64_t pf_normalize_get_frag_count(void);
 int	pf_normalize_tcp(struct pf_pdesc *);
 void	pf_normalize_tcp_cleanup(struct pf_kstate *);
 int	pf_normalize_tcp_init(struct pf_pdesc *,
diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h
index 8edd5a5110a1..54ffdbed3de5 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -247,6 +247,12 @@ enum	{ PF_ADDR_ADDRMASK, PF_ADDR_NOROUTE, PF_ADDR_DYNIFTL,
 #define SCNT_SRC_NODE_REMOVALS	2
 #define SCNT_MAX		3
 
+/* fragment counters */
+#define NCNT_FRAG_SEARCH	0
+#define NCNT_FRAG_INSERT	1
+#define NCNT_FRAG_REMOVALS	2
+#define NCNT_MAX		3
+
 #define	PF_TABLE_NAME_SIZE	32
 #define	PF_QNAME_SIZE		64
 
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 06c40a03f575..5bfbb2c83f0e 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -421,6 +421,8 @@ pfattach_vnet(void)
 		pf_counter_u64_init(&V_pf_status.fcounters[i], M_WAITOK);
 	for (int i = 0; i < SCNT_MAX; i++)
 		V_pf_status.scounters[i] = counter_u64_alloc(M_WAITOK);
+	for (int i = 0; i < NCNT_MAX; i++)
+		V_pf_status.ncounters[i] = counter_u64_alloc(M_WAITOK);
 
 	if (swi_add(&V_pf_swi_ie, "pf send", pf_intr, curvnet, SWI_NET,
 	    INTR_MPSAFE, &V_pf_swi_cookie) != 0)
@@ -2508,6 +2510,8 @@ pf_ioctl_clear_status(void)
 		pf_counter_u64_zero(&V_pf_status.fcounters[i]);
 	for (int i = 0; i < SCNT_MAX; i++)
 		counter_u64_zero(V_pf_status.scounters[i]);
+	for (int i = 0; i < NCNT_MAX; i++)
+		counter_u64_zero(V_pf_status.ncounters[i]);
 	for (int i = 0; i < KLCNT_MAX; i++)
 		counter_u64_zero(V_pf_status.lcounters[i]);
 	V_pf_status.since = time_uptime;
@@ -6949,6 +6953,8 @@ pf_unload_vnet(void)
 		pf_counter_u64_deinit(&V_pf_status.fcounters[i]);
 	for (int i = 0; i < SCNT_MAX; i++)
 		counter_u64_free(V_pf_status.scounters[i]);
+	for (int i = 0; i < NCNT_MAX; i++)
+		counter_u64_free(V_pf_status.ncounters[i]);
 
 	rm_destroy(&V_pf_rules_lock);
 	sx_destroy(&V_pf_ioctl_lock);
diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c
index 73f018db0266..5c8f56ea4567 100644
--- a/sys/netpfil/pf/pf_nl.c
+++ b/sys/netpfil/pf/pf_nl.c
@@ -1234,6 +1234,9 @@ pf_handle_get_status(struct nlmsghdr *hdr, struct nl_pstate *npt)
 	    V_pf_status.fcounters);
 	nlattr_add_counters(nw, PF_GS_SCOUNTERS, SCNT_MAX, pf_fcounter,
 	    V_pf_status.scounters);
+	nlattr_add_counters(nw, PF_GS_NCOUNTERS, NCNT_MAX, pf_fcounter,
+	    V_pf_status.ncounters);
+	nlattr_add_u64(nw, PF_GS_FRAGMENTS, pf_normalize_get_frag_count());
 
 	pfi_update_status(V_pf_status.ifname, &s);
 	nlattr_add_u64_array(nw, PF_GS_BCOUNTERS, 2 * 2, (uint64_t *)s.bcounters);
diff --git a/sys/netpfil/pf/pf_nl.h b/sys/netpfil/pf/pf_nl.h
index b60d3d4797c6..b769421bbfcc 100644
--- a/sys/netpfil/pf/pf_nl.h
+++ b/sys/netpfil/pf/pf_nl.h
@@ -352,6 +352,8 @@ enum pf_get_status_types_t {
 	PF_GS_CHKSUM		= 14, /* byte array */
 	PF_GS_PCOUNTERS		= 15, /* u64 array */
 	PF_GS_BCOUNTERS		= 16, /* u64 array */
+	PF_GS_NCOUNTERS		= 17, /* nested, */
+	PF_GS_FRAGMENTS		= 18, /* u64, */
 };
 
 enum pf_natlook_types_t {
diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c
index 56074bedbc40..53010222dd07 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -211,6 +211,12 @@ pf_normalize_cleanup(void)
 	mtx_destroy(&V_pf_frag_mtx);
 }
 
+uint64_t
+pf_normalize_get_frag_count(void)
+{
+	return (uma_zone_get_cur(V_pf_frent_z));
+}
+
 static int
 pf_frnode_compare(struct pf_frnode *a, struct pf_frnode *b)
 {
@@ -314,6 +320,7 @@ pf_free_fragment(struct pf_fragment *frag)
 	/* Free all fragment entries */
 	while ((frent = TAILQ_FIRST(&frag->fr_queue)) != NULL) {
 		TAILQ_REMOVE(&frag->fr_queue, frent, fr_next);
+		counter_u64_add(V_pf_status.ncounters[NCNT_FRAG_REMOVALS], 1);
 
 		m_freem(frent->fe_m);
 		uma_zfree(V_pf_frent_z, frent);
@@ -331,6 +338,7 @@ pf_find_fragment(struct pf_frnode *key, uint32_t id)
 	PF_FRAG_ASSERT();
 
 	frnode = RB_FIND(pf_frnode_tree, &V_pf_frnode_tree, key);
+	counter_u64_add(V_pf_status.ncounters[NCNT_FRAG_SEARCH], 1);
 	if (frnode == NULL)
 		return (NULL);
 	MPASS(frnode->fn_fragments >= 1);
@@ -438,6 +446,7 @@ pf_frent_insert(struct pf_fragment *frag, struct pf_frent *frent,
 		    ("overlapping fragment"));
 		TAILQ_INSERT_AFTER(&frag->fr_queue, prev, frent, fr_next);
 	}
+	counter_u64_add(V_pf_status.ncounters[NCNT_FRAG_INSERT], 1);
 
 	if (frag->fr_firstoff[index] == NULL) {
 		KASSERT(prev == NULL || pf_frent_index(prev) < index,
@@ -496,6 +505,7 @@ pf_frent_remove(struct pf_fragment *frag, struct pf_frent *frent)
 	}
 
 	TAILQ_REMOVE(&frag->fr_queue, frent, fr_next);
+	counter_u64_add(V_pf_status.ncounters[NCNT_FRAG_REMOVALS], 1);
 
 	KASSERT(frag->fr_entries[index] > 0, ("No fragments remaining"));
 	frag->fr_entries[index]--;
@@ -768,6 +778,7 @@ pf_join_fragment(struct pf_fragment *frag)
 
 	frent = TAILQ_FIRST(&frag->fr_queue);
 	TAILQ_REMOVE(&frag->fr_queue, frent, fr_next);
+	counter_u64_add(V_pf_status.ncounters[NCNT_FRAG_REMOVALS], 1);
 
 	m = frent->fe_m;
 	if ((frent->fe_hdrlen + frent->fe_len) < m->m_pkthdr.len)
@@ -775,6 +786,7 @@ pf_join_fragment(struct pf_fragment *frag)
 	uma_zfree(V_pf_frent_z, frent);
 	while ((frent = TAILQ_FIRST(&frag->fr_queue)) != NULL) {
 		TAILQ_REMOVE(&frag->fr_queue, frent, fr_next);
+		counter_u64_add(V_pf_status.ncounters[NCNT_FRAG_REMOVALS], 1);
 
 		m2 = frent->fe_m;
 		/* Strip off ip header. */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202509151154.58FBsOMZ025408>