git: 5b59b0c61e29 - main - pfctl: add -T `reset` to touch pfras_tzero only for non-zero entries
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 09 Dec 2024 09:39:27 UTC
The branch main has been updated by kp:
URL: https://cgit.FreeBSD.org/src/commit/?id=5b59b0c61e29f684a019afdd2848ffe2d5604e0c
commit 5b59b0c61e29f684a019afdd2848ffe2d5604e0c
Author: Leonid Evdokimov <leon+freebsd@darkk.net.ru>
AuthorDate: 2024-12-06 12:08:54 +0000
Commit: Kristof Provost <kp@FreeBSD.org>
CommitDate: 2024-12-09 09:36:34 +0000
pfctl: add -T `reset` to touch pfras_tzero only for non-zero entries
This will make it easier for scripts to detect idle hosts in tables.
PR: 282984
Reviewed by: kp
MFC after: 2 weeks
---
sbin/pfctl/pfctl.8 | 7 +++-
sbin/pfctl/pfctl.c | 2 +-
sbin/pfctl/pfctl_radix.c | 2 +-
sbin/pfctl/pfctl_table.c | 44 ++++++++++++++++++++++++
tests/sys/netpfil/pf/table.sh | 80 +++++++++++++++++++++++++++++++++++++++++++
5 files changed, 132 insertions(+), 3 deletions(-)
diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8
index 45a6ea525694..00fbda042b7c 100644
--- a/sbin/pfctl/pfctl.8
+++ b/sbin/pfctl/pfctl.8
@@ -24,7 +24,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd November 20, 2024
+.Dd November 25, 2024
.Dt PFCTL 8
.Os
.Sh NAME
@@ -508,6 +508,11 @@ Show the content (addresses) of a table.
Test if the given addresses match a table.
.It Fl T Cm zero Op Ar address ...
Clear all the statistics of a table, or only for specified addresses.
+.It Fl T Cm reset
+Clear statistics only for addresses with non-zero statistics. Addresses
+with counter values at zero and their
+.Dq Cleared
+timestamp are left untouched.
.It Fl T Cm load
Load only the table definitions from
.Xr pf.conf 5 .
diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c
index 555c5181eac8..aa3db4619972 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -239,7 +239,7 @@ static const char * const showopt_list[] = {
static const char * const tblcmdopt_list[] = {
"kill", "flush", "add", "delete", "load", "replace", "show",
- "test", "zero", "expire", NULL
+ "test", "zero", "expire", "reset", NULL
};
static const char * const debugopt_list[] = {
diff --git a/sbin/pfctl/pfctl_radix.c b/sbin/pfctl/pfctl_radix.c
index 749e8b4affc9..9bea219a7d81 100644
--- a/sbin/pfctl/pfctl_radix.c
+++ b/sbin/pfctl/pfctl_radix.c
@@ -292,7 +292,7 @@ pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size,
{
struct pfioc_table io;
- if (size < 0 || (size && !tbl) || addr == NULL) {
+ if (size < 0 || !tbl || (size && !addr)) {
errno = EINVAL;
return (-1);
}
diff --git a/sbin/pfctl/pfctl_table.c b/sbin/pfctl/pfctl_table.c
index 25d9b87d8718..aac031ce26b2 100644
--- a/sbin/pfctl/pfctl_table.c
+++ b/sbin/pfctl/pfctl_table.c
@@ -60,6 +60,7 @@ static void print_table(struct pfr_table *, int, int);
static void print_tstats(struct pfr_tstats *, int);
static int load_addr(struct pfr_buffer *, int, char *[], char *, int);
static void print_addrx(struct pfr_addr *, struct pfr_addr *, int);
+static int nonzero_astats(struct pfr_astats *);
static void print_astats(struct pfr_astats *, int);
static void radix_perror(void);
static void xprintf(int, const char *, ...);
@@ -293,6 +294,36 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
+ } else if (!strcmp(command, "reset")) {
+ struct pfr_astats *as;
+
+ b.pfrb_type = PFRB_ASTATS;
+ b2.pfrb_type = PFRB_ADDRS;
+ if (argc || file != NULL)
+ usage();
+ do {
+ pfr_buf_grow(&b, b.pfrb_size);
+ b.pfrb_size = b.pfrb_msize;
+ RVTEST(pfr_get_astats(&table, b.pfrb_caddr,
+ &b.pfrb_size, flags));
+ } while (b.pfrb_size > b.pfrb_msize);
+ PFRB_FOREACH(as, &b) {
+ as->pfras_a.pfra_fback = 0;
+ if (nonzero_astats(as))
+ if (pfr_buf_add(&b2, &as->pfras_a))
+ err(1, "duplicate buffer");
+ }
+
+ if (opts & PF_OPT_VERBOSE)
+ flags |= PFR_FLAG_FEEDBACK;
+ RVTEST(pfr_clr_astats(&table, b2.pfrb_caddr, b2.pfrb_size,
+ &nzero, flags));
+ xprintf(opts, "%d/%d stats cleared", nzero, b.pfrb_size);
+ if (opts & PF_OPT_VERBOSE)
+ PFRB_FOREACH(a, &b2)
+ if ((opts & PF_OPT_VERBOSE2) || a->pfra_fback)
+ print_addrx(a, NULL,
+ opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "show")) {
b.pfrb_type = (opts & PF_OPT_VERBOSE) ?
PFRB_ASTATS : PFRB_ADDRS;
@@ -484,6 +515,19 @@ print_addrx(struct pfr_addr *ad, struct pfr_addr *rad, int dns)
printf("\n");
}
+int
+nonzero_astats(struct pfr_astats *as)
+{
+ uint64_t s = 0;
+
+ for (int dir = 0; dir < PFR_DIR_MAX; dir++)
+ for (int op = 0; op < PFR_OP_ADDR_MAX; op++)
+ s |= as->pfras_packets[dir][op] |
+ as->pfras_bytes[dir][op];
+
+ return (!!s);
+}
+
void
print_astats(struct pfr_astats *as, int dns)
{
diff --git a/tests/sys/netpfil/pf/table.sh b/tests/sys/netpfil/pf/table.sh
index 828d76a998be..62c9d66d80ce 100644
--- a/tests/sys/netpfil/pf/table.sh
+++ b/tests/sys/netpfil/pf/table.sh
@@ -165,6 +165,85 @@ zero_one_cleanup()
pft_cleanup
}
+atf_test_case "reset_nonzero" "cleanup"
+reset_nonzero_head()
+{
+ atf_set descr 'Test zeroing an address with non-zero counters'
+ atf_set require.user root
+}
+
+reset_nonzero_body()
+{
+ epair_send=$(vnet_mkepair)
+ ifconfig ${epair_send}a 192.0.2.1/24 up
+ ifconfig ${epair_send}a inet alias 192.0.2.3/24
+
+ vnet_mkjail alcatraz ${epair_send}b
+ jexec alcatraz ifconfig ${epair_send}b 192.0.2.2/24 up
+ jexec alcatraz pfctl -e
+
+ pft_set_rules alcatraz \
+ "table <foo> counters { 192.0.2.1, 192.0.2.3 }" \
+ "table <bar> counters { }" \
+ "block all" \
+ "pass in from <foo> to any" \
+ "pass out from any to <foo>" \
+ "pass on notReallyAnIf from <bar> to <bar>" \
+ "set skip on lo"
+
+ # Nonexisting table can't be reset, following `-T show`.
+ atf_check -o ignore \
+ -s not-exit:0 \
+ -e inline:"pfctl: Table does not exist.\n" \
+ jexec alcatraz pfctl -t nonexistent -T reset
+
+ atf_check -o ignore \
+ -s exit:0 \
+ -e inline:"0/0 stats cleared.\n" \
+ jexec alcatraz pfctl -t bar -T reset
+
+ # No-op is a valid operation.
+ atf_check -s exit:0 \
+ -e inline:"0/2 stats cleared.\n" \
+ jexec alcatraz pfctl -t foo -T reset
+
+ atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2
+
+ atf_check -s exit:0 -e ignore \
+ -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -vvT show
+
+ local clrd uniq
+ clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared`
+ uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared`
+ atf_check_equal "$clrd" 2
+ atf_check_equal "$uniq" 1 # time they were added
+
+ atf_check -s exit:0 -e ignore \
+ -e inline:"1/2 stats cleared.\n" \
+ jexec alcatraz pfctl -t foo -T reset
+
+ clrd=`jexec alcatraz pfctl -t foo -vvT show | grep -c Cleared`
+ uniq=`jexec alcatraz pfctl -t foo -vvT show | sort -u | grep -c Cleared`
+ atf_check_equal "$clrd" 2
+ atf_check_equal "$uniq" 2 # 192.0.2.3 should get new timestamp
+
+ atf_check -s exit:0 -e ignore \
+ -o not-match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o not-match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
+ -o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ -o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
+ jexec alcatraz pfctl -t foo -vvT show
+}
+
+reset_nonzero_cleanup()
+{
+ pft_cleanup
+}
+
atf_test_case "pr251414" "cleanup"
pr251414_head()
{
@@ -381,6 +460,7 @@ atf_init_test_cases()
atf_add_test_case "v4_counters"
atf_add_test_case "v6_counters"
atf_add_test_case "zero_one"
+ atf_add_test_case "reset_nonzero"
atf_add_test_case "pr251414"
atf_add_test_case "automatic"
atf_add_test_case "network"