The branch stable/13 has been updated by kp:

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

commit 3fa5d13c5be01da5796e0f3617d6da277cc16432
Author:     Leonid Evdokimov <leon+free...@darkk.net.ru>
AuthorDate: 2024-12-06 12:08:54 +0000
Commit:     Kristof Provost <k...@freebsd.org>
CommitDate: 2024-12-24 10:16:50 +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
    
    (cherry picked from commit 5b59b0c61e29f684a019afdd2848ffe2d5604e0c)
---
 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 c1b570f225b1..642ca844023f 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
@@ -495,6 +495,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 1cdcd1d0b912..8399051048b8 100644
--- a/sbin/pfctl/pfctl.c
+++ b/sbin/pfctl/pfctl.c
@@ -227,7 +227,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 7d8c146c1dda..aa27652db2ec 100644
--- a/sbin/pfctl/pfctl_radix.c
+++ b/sbin/pfctl/pfctl_radix.c
@@ -293,7 +293,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 cb04b2b30bac..d645846c7fd0 100644
--- a/sbin/pfctl/pfctl_table.c
+++ b/sbin/pfctl/pfctl_table.c
@@ -61,6 +61,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 *, ...);
@@ -294,6 +295,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;
@@ -485,6 +516,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 9e0990941466..b650884c7c30 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 "network"
        atf_add_test_case "automatic"

Reply via email to