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)
This commit is contained in:
Leonid Evdokimov 2024-12-06 13:08:54 +01:00 committed by Franco Fichtner
parent 816c116633
commit d3d3ef2b33
5 changed files with 132 additions and 3 deletions

View file

@ -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
@ -503,6 +503,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 .

View file

@ -238,7 +238,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[] = {

View file

@ -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);
}

View file

@ -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)
{

View file

@ -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"