git: 0abcc1d2d33a - main - pf: Add per-rule timestamps for rule and eth_rule

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Fri, 22 Apr 2022 17:53:55 UTC
The branch main has been updated by kp:

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

commit 0abcc1d2d33aef2333dab28e1ec1858cf45b314a
Author:     Reid Linnemann <rlinnemann@netgate.com>
AuthorDate: 2022-04-22 11:54:51 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2022-04-22 17:53:20 +0000

    pf: Add per-rule timestamps for rule and eth_rule
    
    Similar to ipfw rule timestamps, these timestamps internally are
    uint32_t snaps of the system time in seconds. The timestamp is CPU local
    and updated each time a rule or a state associated with a rule or state
    is matched.
    
    Reviewed by:    kp
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D34970
---
 lib/libpfctl/libpfctl.c   | 10 +++++++++-
 lib/libpfctl/libpfctl.h   |  2 ++
 sbin/pfctl/pfctl.c        | 23 +++++++++++++++++++++++
 sys/net/pfvar.h           | 22 ++++++++++++++++++++++
 sys/netpfil/pf/pf.c       |  3 +++
 sys/netpfil/pf/pf_ioctl.c |  9 +++++++++
 sys/netpfil/pf/pf_nv.c    |  2 ++
 7 files changed, 70 insertions(+), 1 deletion(-)

diff --git a/lib/libpfctl/libpfctl.c b/lib/libpfctl/libpfctl.c
index 89123bd80ad5..6a883a814902 100644
--- a/lib/libpfctl/libpfctl.c
+++ b/lib/libpfctl/libpfctl.c
@@ -512,6 +512,10 @@ pf_nvrule_to_rule(const nvlist_t *nvl, struct pfctl_rule *rule)
 	pf_nvuint_64_array(nvl, "packets", 2, rule->packets, NULL);
 	pf_nvuint_64_array(nvl, "bytes", 2, rule->bytes, NULL);
 
+	if (nvlist_exists_number(nvl, "timestamp")) {
+		rule->last_active_timestamp = nvlist_get_number(nvl, "timestamp");
+	}
+
 	rule->os_fingerprint = nvlist_get_number(nvl, "os_fingerprint");
 
 	rule->rtableid = nvlist_get_number(nvl, "rtableid");
@@ -642,6 +646,10 @@ pfctl_nveth_rule_to_eth_rule(const nvlist_t *nvl, struct pfctl_eth_rule *rule)
 	rule->bytes[0] = nvlist_get_number(nvl, "bytes-in");
 	rule->bytes[1] = nvlist_get_number(nvl, "bytes-out");
 
+	if (nvlist_exists_number(nvl, "timestamp")) {
+		rule->last_active_timestamp = nvlist_get_number(nvl, "timestamp");
+	}
+
 	strlcpy(rule->qname, nvlist_get_string(nvl, "qname"), PF_QNAME_SIZE);
 	strlcpy(rule->tagname, nvlist_get_string(nvl, "tagname"),
 	    PF_TAG_NAME_SIZE);
@@ -737,7 +745,7 @@ pfctl_get_eth_rule(int dev, uint32_t nr, uint32_t ticket,
 	nvlist_add_number(nvl, "nr", nr);
 	nvlist_add_bool(nvl, "clear", clear);
 
-	if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULE, 2048, &nvl)) != 0)
+	if ((ret = pfctl_do_ioctl(dev, DIOCGETETHRULE, 4096, &nvl)) != 0)
 		return (ret);
 
 	pfctl_nveth_rule_to_eth_rule(nvl, rule);
diff --git a/lib/libpfctl/libpfctl.h b/lib/libpfctl/libpfctl.h
index 440ca2fe0d10..ab4b6a27c787 100644
--- a/lib/libpfctl/libpfctl.h
+++ b/lib/libpfctl/libpfctl.h
@@ -99,6 +99,7 @@ struct pfctl_eth_rule {
 	uint64_t		 evaluations;
 	uint64_t		 packets[2];
 	uint64_t		 bytes[2];
+	uint32_t		 last_active_timestamp;
 
 	/* Action */
 	char			 qname[PF_QNAME_SIZE];
@@ -171,6 +172,7 @@ struct pfctl_rule {
 	uint64_t		 evaluations;
 	uint64_t		 packets[2];
 	uint64_t		 bytes[2];
+	uint32_t		 last_active_timestamp;
 
 	struct pfi_kif		*kif;
 	struct pfctl_anchor	*anchor;
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 150a375b316a..a1f8e5fedd4c 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -1016,6 +1016,18 @@ pfctl_print_eth_rule_counters(struct pfctl_eth_rule *rule, int opts)
 			    (unsigned long long)(rule->bytes[0] +
 			    rule->bytes[1]));
 	}
+	if (opts & PF_OPT_VERBOSE2) {
+		char timestr[30];
+
+		if (rule->last_active_timestamp != 0) {
+			time_t last_active = rule->last_active_timestamp;
+			bcopy(ctime(&last_active), timestr, sizeof(timestr));
+			*strchr(timestr, '\n') = '\0';
+		} else {
+			snprintf(timestr, sizeof(timestr), "N/A");
+		}
+		printf("  [ Last Active Time: %s ]\n", timestr);
+	}
 }
 
 void
@@ -1055,6 +1067,17 @@ pfctl_print_rule_counters(struct pfctl_rule *rule, int opts)
 			    (unsigned)rule->cuid, (unsigned)rule->cpid,
 			    (uintmax_t)rule->states_tot);
 	}
+	if (opts & PF_OPT_VERBOSE2) {
+		char timestr[30];
+		if (rule->last_active_timestamp != 0) {
+			time_t last_active = rule->last_active_timestamp;
+			bcopy(ctime(&last_active), timestr, sizeof(timestr));
+			*strchr(timestr, '\n') = '\0';
+		} else {
+			snprintf(timestr, sizeof(timestr), "N/A");
+		}
+		printf("  [ Last Active Time: %s ]\n", timestr);
+	}
 }
 
 void
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index adcf69905698..4ad758fe439b 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -286,6 +286,26 @@ pf_counter_u64_zero(struct pf_counter_u64 *pfcu64)
 }
 #endif
 
+#define pf_get_timestamp(prule)({					\
+	uint32_t _ts = 0;						\
+	uint32_t __ts;							\
+	int cpu;							\
+	CPU_FOREACH(cpu) {						\
+		__ts = *zpcpu_get_cpu(prule->timestamp, cpu);		\
+		if (__ts > _ts)						\
+			_ts = __ts;					\
+	}								\
+	_ts;								\
+})
+
+#define pf_update_timestamp(prule)					\
+	do {								\
+		critical_enter();					\
+		*zpcpu_get((prule)->timestamp) = time_second;		\
+		critical_exit();					\
+	} while (0)
+
+
 SYSCTL_DECL(_net_pf);
 MALLOC_DECLARE(M_PFHASH);
 
@@ -657,6 +677,7 @@ struct pf_keth_rule {
 	counter_u64_t		 evaluations;
 	counter_u64_t		 packets[2];
 	counter_u64_t		 bytes[2];
+	uint32_t		*timestamp;
 
 	/* Action */
 	char			 qname[PF_QNAME_SIZE];
@@ -696,6 +717,7 @@ struct pf_krule {
 	struct pf_counter_u64	 evaluations;
 	struct pf_counter_u64	 packets[2];
 	struct pf_counter_u64	 bytes[2];
+	uint32_t		*timestamp;
 
 	struct pfi_kkif		*kif;
 	struct pf_kanchor	*anchor;
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index d27e6f30f945..5eee9dcf5b91 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -3971,6 +3971,7 @@ pf_test_eth_rule(int dir, struct pfi_kkif *kif, struct mbuf **m0)
 	/* Execute action. */
 	counter_u64_add(r->packets[dir == PF_OUT], 1);
 	counter_u64_add(r->bytes[dir == PF_OUT], m_length(m, NULL));
+	pf_update_timestamp(r);
 
 	/* Shortcut. Don't tag if we're just going to drop anyway. */
 	if (r->action == PF_DROP) {
@@ -7198,6 +7199,8 @@ done:
 		dirndx = (dir == PF_OUT);
 		pf_counter_u64_add_protected(&r->packets[dirndx], 1);
 		pf_counter_u64_add_protected(&r->bytes[dirndx], pd.tot_len);
+		pf_update_timestamp(r);
+
 		if (a != NULL) {
 			pf_counter_u64_add_protected(&a->packets[dirndx], 1);
 			pf_counter_u64_add_protected(&a->bytes[dirndx], pd.tot_len);
diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c
index 86cbf7aa3808..65839d1d31d9 100644
--- a/sys/netpfil/pf/pf_ioctl.c
+++ b/sys/netpfil/pf/pf_ioctl.c
@@ -344,6 +344,8 @@ pfattach_vnet(void)
 	V_pf_default_rule.states_tot = counter_u64_alloc(M_WAITOK);
 	V_pf_default_rule.src_nodes = counter_u64_alloc(M_WAITOK);
 
+	V_pf_default_rule.timestamp = uma_zalloc_pcpu(pcpu_zone_4, M_WAITOK | M_ZERO);
+
 #ifdef PF_WANT_32_TO_64_COUNTER
 	V_pf_kifmarker = malloc(sizeof(*V_pf_kifmarker), PFI_MTYPE, M_WAITOK | M_ZERO);
 	V_pf_rulemarker = malloc(sizeof(*V_pf_rulemarker), M_PFRULE, M_WAITOK | M_ZERO);
@@ -530,6 +532,7 @@ pf_free_eth_rule(struct pf_keth_rule *rule)
 		counter_u64_free(rule->packets[i]);
 		counter_u64_free(rule->bytes[i]);
 	}
+	uma_zfree_pcpu(pcpu_zone_4, rule->timestamp);
 	pf_keth_anchor_remove(rule);
 
 	free(rule, M_PFRULE);
@@ -1801,6 +1804,7 @@ 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);
+	uma_zfree_pcpu(pcpu_zone_4, rule->timestamp);
 
 	mtx_destroy(&rule->rpool.mtx);
 	free(rule, M_PFRULE);
@@ -2130,6 +2134,7 @@ 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);
+	rule->timestamp = uma_zalloc_pcpu(pcpu_zone_4, M_WAITOK | M_ZERO);
 	rule->cuid = td->td_ucred->cr_ruid;
 	rule->cpid = td->td_proc ? td->td_proc->p_pid : 0;
 	TAILQ_INIT(&rule->rpool.list);
@@ -2832,6 +2837,7 @@ DIOCGETETHRULE_error:
 		rule = malloc(sizeof(*rule), M_PFRULE, M_WAITOK);
 		if (rule == NULL)
 			ERROUT(ENOMEM);
+		rule->timestamp = NULL;
 
 		error = pf_nveth_rule_to_keth_rule(nvl, rule);
 		if (error != 0)
@@ -2844,6 +2850,8 @@ DIOCGETETHRULE_error:
 			rule->packets[i] = counter_u64_alloc(M_WAITOK);
 			rule->bytes[i] = counter_u64_alloc(M_WAITOK);
 		}
+		rule->timestamp = uma_zalloc_pcpu(pcpu_zone_4,
+		    M_WAITOK | M_ZERO);
 
 		PF_RULES_WLOCK();
 
@@ -6697,6 +6705,7 @@ 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);
+	uma_zfree_pcpu(pcpu_zone_4, V_pf_default_rule.timestamp);
 
 	for (int i = 0; i < PFRES_MAX; i++)
 		counter_u64_free(V_pf_status.counters[i]);
diff --git a/sys/netpfil/pf/pf_nv.c b/sys/netpfil/pf/pf_nv.c
index 4dcb5441f835..5476b59d859f 100644
--- a/sys/netpfil/pf/pf_nv.c
+++ b/sys/netpfil/pf/pf_nv.c
@@ -737,6 +737,7 @@ pf_krule_to_nvrule(struct pf_krule *rule)
 		nvlist_append_number_array(nvl, "bytes",
 		    pf_counter_u64_fetch(&rule->bytes[i]));
 	}
+	nvlist_add_number(nvl, "timestamp", pf_get_timestamp(rule));
 
 	nvlist_add_number(nvl, "os_fingerprint", rule->os_fingerprint);
 
@@ -1098,6 +1099,7 @@ pf_keth_rule_to_nveth_rule(const struct pf_keth_rule *krule)
 	nvlist_add_number(nvl, "bytes-out",
 	    counter_u64_fetch(krule->bytes[1]));
 
+	nvlist_add_number(nvl, "timestamp", pf_get_timestamp(krule));
 	nvlist_add_string(nvl, "qname", krule->qname);
 	nvlist_add_string(nvl, "tagname", krule->tagname);