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"