pf: Log the intended action when a NAT rule matches a packet

When a packet matches a binat/nat/rdr rule, pf logs the match.  The log
metadata includes the rule's action on the packet, e.g., PF_PASS.  NAT
rules have their own actions: PF_BINAT, PF_NAT, PF_RDR.

Before commit 948e8413ab ("pflog: pass the action to pflog directly"),
pflog_packet() would obtain the action from the rule definition, whereas
after that commit the action is passed as a parameter.  When a NAT rule
matches, we want to log the rule action, but after that commit, PF_PASS
is hard-coded.  Restore the previous behaviour.

Add a regression test which installs a redirect, logs packets matching
the redirect rule, and verifies that the corresponding pflog entry
includes the correct action.

Reviewed by:	kp
Fixes:		948e8413ab ("pflog: pass the action to pflog directly")
MFC after:	2 weeks
Sponsored by:	Klara, Inc.
Sponsored by:	OPNsense
Differential Revision:	https://reviews.freebsd.org/D48911
This commit is contained in:
Franco Fichtner
2025-02-14 15:14:27 +00:00
committed by Mark Johnston
parent b968644127
commit 685fb42538
2 changed files with 60 additions and 1 deletions
+1 -1
View File
@@ -5535,7 +5535,7 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm,
KASSERT(nk != NULL, ("%s: null nk", __func__));
if (nr->log) {
PFLOG_PACKET(PF_PASS, PFRES_MATCH, nr, a,
PFLOG_PACKET(nr->action, PFRES_MATCH, nr, a,
ruleset, pd, 1);
}
+59
View File
@@ -264,10 +264,69 @@ unspecified_v6_cleanup()
pft_cleanup
}
atf_test_case "rdr_action" "cleanup"
rdr_head()
{
atf_set descr 'Ensure that NAT rule actions are logged correctly'
atf_set require.user root
}
rdr_action_body()
{
j="pflog:rdr_action"
epair_c=$(vnet_mkepair)
epair_srv=$(vnet_mkepair)
vnet_mkjail ${j}srv ${epair_srv}a
vnet_mkjail ${j}gw ${epair_srv}b ${epair_c}a
vnet_mkjail ${j}c ${epair_c}b
jexec ${j}srv ifconfig ${epair_srv}a 198.51.100.1/24 up
# No default route in srv jail, to ensure we're NAT-ing
jexec ${j}gw ifconfig ${epair_srv}b 198.51.100.2/24 up
jexec ${j}gw ifconfig ${epair_c}a 192.0.2.1/24 up
jexec ${j}gw sysctl net.inet.ip.forwarding=1
jexec ${j}c ifconfig ${epair_c}b 192.0.2.2/24 up
jexec ${j}c route add default 192.0.2.1
jexec ${j}gw pfctl -e
jexec ${j}gw ifconfig pflog0 up
pft_set_rules ${j}gw \
"rdr log on ${epair_srv}b proto tcp from 198.51.100.0/24 to any port 1234 -> 192.0.2.2 port 1234" \
"block quick inet6" \
"pass in log"
jexec ${j}gw tcpdump -n -e -ttt --immediate-mode -l -U -i pflog0 >> ${PWD}/pflog.txt &
sleep 1 # Wait for tcpdump to start
# send a SYN to catch in the log
jexec ${j}srv nc -N -w 0 198.51.100.2 1234
echo "Log"
cat ${PWD}/pflog.txt
# log line generated for rdr hit (pre-NAT)
atf_check -o match:".*.*rule 0/0\(match\): rdr in on ${epair_srv}b: 198.51.100.1.[0-9]* > 198.51.100.2.1234: Flags \[S\].*" \
cat pflog.txt
# log line generated for pass hit (post-NAT)
atf_check -o match:".*.*rule 1/0\(match\): pass in on ${epair_srv}b: 198.51.100.1.[0-9]* > 192.0.2.2.1234: Flags \[S\].*" \
cat pflog.txt
# only two log lines shall be written
atf_check -o match:2 grep -c . pflog.txt
}
rdr_action_cleanup()
{
pft_cleanup
}
atf_init_test_cases()
{
atf_add_test_case "malformed"
atf_add_test_case "matches"
atf_add_test_case "rdr_action"
atf_add_test_case "state_max"
atf_add_test_case "unspecified_v4"
atf_add_test_case "unspecified_v6"