git: 65b20771713c - main - pf tests: Simplify handling of pfctl -s

From: Kajetan Staszkiewicz <ks_at_FreeBSD.org>
Date: Tue, 12 Nov 2024 17:20:27 UTC
The branch main has been updated by ks:

URL: https://cgit.FreeBSD.org/src/commit/?id=65b20771713c7ec4d46fb5af4a16353209658d79

commit 65b20771713c7ec4d46fb5af4a16353209658d79
Author:     Kajetan Staszkiewicz <ks@FreeBSD.org>
AuthorDate: 2024-11-12 17:17:12 +0000
Commit:     Kajetan Staszkiewicz <ks@FreeBSD.org>
CommitDate: 2024-11-12 17:17:12 +0000

    pf tests: Simplify handling of pfctl -s
    
    Some pf tests check the output of pfctl -s[sSr] to find if relevant
    states, source nodes or rules exist and if their counters have proper
    values. The output is multiple lines per entry and contains varying
    amount of whitespace. This makes parsing it rather hard.
    
    Provide a function for standardization of output of pfctl -s[sSr] which
    converts the output to a single line per entry and reduces whitespace
    always to a single space. Adjust existing tests to make use of this
    function.
    
    Revieved by:            kp
    Approved by:            kp (mentor)
    Differental Revision:   https://reviews.freebsd.org/D47435
---
 tests/sys/netpfil/pf/fragmentation_pass.sh |  5 ++---
 tests/sys/netpfil/pf/src_track.sh          | 24 ++++++++++++------------
 tests/sys/netpfil/pf/utils.subr            | 10 ++++++++++
 3 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/tests/sys/netpfil/pf/fragmentation_pass.sh b/tests/sys/netpfil/pf/fragmentation_pass.sh
index 99d2c827b239..94a25cc41988 100644
--- a/tests/sys/netpfil/pf/fragmentation_pass.sh
+++ b/tests/sys/netpfil/pf/fragmentation_pass.sh
@@ -580,13 +580,12 @@ dummynet_fragmented_body()
 	ping_dummy_check_request exit:0 --ping-type=udp --send-length=10000 --send-frag-length=1280
 
 	rules=$(mktemp) || exit 1
-	jexec router pfctl -qvsr > $rules
+	jexec router pfctl -qvsr | normalize_pfctl_s > $rules
 
 	# Count that fragmented packets have hit the rule only once and that
 	# they have not created states. There is no stateful firewall support
 	# for fragmented packets.
-	grep -A2 'pass in on epair0b inet proto udp all keep state dnpipe(1, 1)' $rules |
-		grep -qE 'Packets: 8\s+Bytes: 10168\s+States: 0\s+' ||
+	grep -qE 'pass in on epair0b inet proto udp all keep state dnpipe\(1, 1\) .* Packets: 8 Bytes: 10168 States: 0 ' $rules ||
 		atf_fail "Fragmented packets not counted correctly"
 }
 
diff --git a/tests/sys/netpfil/pf/src_track.sh b/tests/sys/netpfil/pf/src_track.sh
index 9d0ca690d344..5349e61ec76b 100755
--- a/tests/sys/netpfil/pf/src_track.sh
+++ b/tests/sys/netpfil/pf/src_track.sh
@@ -165,16 +165,16 @@ max_src_conn_rule_body()
 	ping_server_check_reply exit:0 --ping-type=tcp3way --send-sport=4205 --fromaddr 2001:db8:44::2
 
 	states=$(mktemp) || exit 1
-	jexec router pfctl -qss | grep 'tcp 2001:db8:43::2\[9\] <-' > $states
+	jexec router pfctl -qss | normalize_pfctl_s | grep 'tcp 2001:db8:43::2\[9\] <-' > $states
 
-	grep -qE '2001:db8:44::1\[4201\]\s+ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4201 not found or not established"
-	grep -qE '2001:db8:44::1\[4202\]\s+ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4202 not found or not established"
-	grep -qE '2001:db8:44::1\[4203\]\s+ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4203 not found or not established"
-	grep -qE '2001:db8:44::2\[4205\]\s+ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4205 not found or not established"
+	grep -qE '2001:db8:44::1\[4201\] ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4201 not found or not established"
+	grep -qE '2001:db8:44::1\[4202\] ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4202 not found or not established"
+	grep -qE '2001:db8:44::1\[4203\] ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4203 not found or not established"
+	grep -qE '2001:db8:44::2\[4205\] ESTABLISHED:ESTABLISHED' $states || atf_fail "State for port 4205 not found or not established"
 
 	if (
-		grep -qE '2001:db8:44::1\[4204\]\s+' $states &&
-		! grep -qE '2001:db8:44::1\[4204\]\s+CLOSED:CLOSED' $states
+		grep -qE '2001:db8:44::1\[4204\] ' $states &&
+		! grep -qE '2001:db8:44::1\[4204\] CLOSED:CLOSED' $states
 	); then
 		atf_fail "State for port 4204 found but not closed"
 	fi
@@ -234,13 +234,13 @@ max_src_states_rule_body()
 	# We will check the resulting source nodes, though.
 	# Order of source nodes in output is not guaranteed, find each one separately.
 	nodes=$(mktemp) || exit 1
-	jexec router pfctl -qvsS  > $nodes
+	jexec router pfctl -qvsS | normalize_pfctl_s > $nodes
 	for node_regexp in \
-		'2001:db8:44::1 -> :: \( states 2, connections 2, rate [0-9/\.]+s \)\s+age [0-9:]+, 6 pkts, [0-9]+ bytes, filter rule 3' \
-		'2001:db8:44::1 -> :: \( states 2, connections 2, rate [0-9/\.]+s \)\s+age [0-9:]+, 6 pkts, [0-9]+ bytes, filter rule 4' \
-		'2001:db8:44::2 -> :: \( states 2, connections 2, rate [0-9/\.]+s \)\s+age [0-9:]+, 6 pkts, [0-9]+ bytes, filter rule 4' \
+		'2001:db8:44::1 -> :: \( states 2, connections 2, rate [0-9/\.]+s \) age [0-9:]+, 6 pkts, [0-9]+ bytes, filter rule 3$' \
+		'2001:db8:44::1 -> :: \( states 2, connections 2, rate [0-9/\.]+s \) age [0-9:]+, 6 pkts, [0-9]+ bytes, filter rule 4$' \
+		'2001:db8:44::2 -> :: \( states 2, connections 2, rate [0-9/\.]+s \) age [0-9:]+, 6 pkts, [0-9]+ bytes, filter rule 4$' \
 	; do
-		cat $nodes | tr '\n' ' ' | grep -qE "$node_regexp" || atf_fail "Source nodes not matching expected output"
+		grep -qE "$node_regexp" $nodes || atf_fail "Source nodes not matching expected output"
 	done
 
 	# Check if limit counters have been properly set.
diff --git a/tests/sys/netpfil/pf/utils.subr b/tests/sys/netpfil/pf/utils.subr
index c854ad5e69d8..6af10e80390d 100644
--- a/tests/sys/netpfil/pf/utils.subr
+++ b/tests/sys/netpfil/pf/utils.subr
@@ -301,3 +301,13 @@ ping_server_check_reply()
 	    --replyif ${epair_tester}a \
 	$params
 }
+
+normalize_pfctl_s()
+{
+	# `pfctl -s[rsS]` output is divided into sections. Each rule, state or
+	# source node starts with the beginning of a line and next lines with leading
+	# spaces are various parameters of said rule, state or source node.
+	# Convert it into a single line per entry, and remove multiple spaces,
+	# so that regular expressions for matching them in tests can be simpler.
+	awk '{ if ($0 ~ /^[^ ]/ && NR > 1) print(""); gsub(/ +/, " ", $0); printf("%s", $0); } END {print("");}'
+}