Skip to content

Commit

Permalink
pfctl: clear statistic for specified addresses
Browse files Browse the repository at this point in the history
The ioctl DIOCRCLRASTATS provides the functionality of clearing stats
not only for the whole table for for addresses stored in that table. The
functionality was missing from pfctl, though. Add it now.

PR:		282877
Obtained from:	OpenBSD, kirill <[email protected]>, e496dff3a7
MFC after:	3 weeks

(cherry picked from commit 6463b6b)
  • Loading branch information
kprovost committed Dec 14, 2024
1 parent 8176157 commit 642ade0
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 5 deletions.
6 changes: 3 additions & 3 deletions sbin/pfctl/pfctl.8
Original file line number Diff line number Diff line change
Expand Up @@ -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 July 23, 2024
.Dd November 20, 2024
.Dt PFCTL 8
.Os
.Sh NAME
Expand Down Expand Up @@ -493,8 +493,8 @@ Automatically create a nonexisting table.
Show the content (addresses) of a table.
.It Fl T Cm test
Test if the given addresses match a table.
.It Fl T Cm zero
Clear all the statistics of 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 load
Load only the table definitions from
.Xr pf.conf 5 .
Expand Down
1 change: 1 addition & 0 deletions sbin/pfctl/pfctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ int pfr_del_tables(struct pfr_table *, int, int *, int);
int pfr_get_tables(struct pfr_table *, struct pfr_table *, int *, int);
int pfr_get_tstats(struct pfr_table *, struct pfr_tstats *, int *, int);
int pfr_clr_tstats(struct pfr_table *, int, int *, int);
int pfr_clr_astats(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_clr_addrs(struct pfr_table *, int *, int);
int pfr_add_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
int pfr_del_addrs(struct pfr_table *, struct pfr_addr *, int, int *, int);
Expand Down
23 changes: 23 additions & 0 deletions sbin/pfctl/pfctl_radix.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,29 @@ pfr_get_astats(struct pfr_table *tbl, struct pfr_astats *addr, int *size,
return (0);
}

int
pfr_clr_astats(struct pfr_table *tbl, struct pfr_addr *addr, int size,
int *nzero, int flags)
{
struct pfioc_table io;

if (size < 0 || (size && !tbl) || addr == NULL) {
errno = EINVAL;
return (-1);
}
bzero(&io, sizeof io);
io.pfrio_flags = flags;
io.pfrio_table = *tbl;
io.pfrio_buffer = addr;
io.pfrio_esize = sizeof(*addr);
io.pfrio_size = size;
if (ioctl(dev, DIOCRCLRASTATS, &io) == -1)
return (-1);
if (nzero)
*nzero = io.pfrio_nzero;
return (0);
}

int
pfr_clr_tstats(struct pfr_table *tbl, int size, int *nzero, int flags)
{
Expand Down
17 changes: 15 additions & 2 deletions sbin/pfctl/pfctl_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,22 @@ pfctl_table(int argc, char *argv[], char *tname, const char *command,
}
if (nmatch < b.pfrb_size)
rv = 2;
} else if (!strcmp(command, "zero") && (argc || file != NULL)) {
b.pfrb_type = PFRB_ADDRS;
if (load_addr(&b, argc, argv, file, 0))
goto _error;
if (opts & PF_OPT_VERBOSE)
flags |= PFR_FLAG_FEEDBACK;
RVTEST(pfr_clr_astats(&table, b.pfrb_caddr, b.pfrb_size,
&nzero, flags));
xprintf(opts, "%d/%d addresses cleared", nzero, b.pfrb_size);
if (opts & PF_OPT_VERBOSE)
PFRB_FOREACH(a, &b)
if (opts & PF_OPT_VERBOSE2 ||
a->pfra_fback != PFR_FB_NONE)
print_addrx(a, NULL,
opts & PF_OPT_USEDNS);
} else if (!strcmp(command, "zero")) {
if (argc || file != NULL)
usage();
flags |= PFR_FLAG_ADDRSTOO;
RVTEST(pfr_clr_tstats(&table, 1, &nzero, flags));
xprintf(opts, "%d table/stats cleared", nzero);
Expand Down
57 changes: 57 additions & 0 deletions tests/sys/netpfil/pf/table.sh
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,62 @@ v6_counters_cleanup()
pft_cleanup
}

atf_test_case "zero_one" "cleanup"
zero_one_head()
{
atf_set descr 'Test zeroing a single address in a table'
atf_set require.user root
}

zero_one_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 }" \
"block all" \
"pass in from <foo> to any" \
"pass out from any to <foo>" \
"set skip on lo"

atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.1 192.0.2.2
atf_check -s exit:0 -o ignore ping -c 3 -S 192.0.2.3 192.0.2.2

jexec alcatraz pfctl -t foo -T show -vv

atf_check -s exit:0 -e ignore \
-o match:'In/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
-o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
-o match:'Out/Block:.*'"$TABLE_STATS_ZERO_REGEXP" \
-o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
jexec alcatraz pfctl -t foo -T show -vv

atf_check -s exit:0 -e ignore \
jexec alcatraz pfctl -t foo -T zero 192.0.2.3

# We now have a zeroed and a non-zeroed counter, so both patterns
# should match
atf_check -s exit:0 -e ignore \
-o match:'In/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
-o match:'Out/Pass:.*'"$TABLE_STATS_NONZERO_REGEXP" \
jexec alcatraz pfctl -t foo -T show -vv
atf_check -s exit:0 -e ignore \
-o match:'In/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
-o match:'Out/Pass:.*'"$TABLE_STATS_ZERO_REGEXP" \
jexec alcatraz pfctl -t foo -T show -vv
}

zero_one_cleanup()
{
pft_cleanup
}

atf_test_case "pr251414" "cleanup"
pr251414_head()
{
Expand Down Expand Up @@ -324,6 +380,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 "pr251414"
atf_add_test_case "network"
atf_add_test_case "automatic"
Expand Down

0 comments on commit 642ade0

Please sign in to comment.