git: 00a793def743 - main - pf: Fix fallout from the STATE_LOOKUP macro removal
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 27 Aug 2026 13:33:30 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=00a793def743b5be8fcbdd39fc1dfa61534ed66c
commit 00a793def743b5be8fcbdd39fc1dfa61534ed66c
Author: Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2026-08-27 13:02:37 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-08-27 13:33:10 +0000
pf: Fix fallout from the STATE_LOOKUP macro removal
Commit 8572367b6814 ("pf: remove STATE_LOOKUP") introduced two seemingly
unintentional changes with respect to divert(4)-injected packets (i.e.,
the PACKET_LOOPED case): we no longer return the matching state, and
direct callers of pf_find_state() now treat matches of diverted packets
the same as having no matching state at all.
This seems inadvertent, and breaks certain rulesets which use divert-to.
Fix them, and add a regression test case.
Fixes: 8572367b6814 ("pf: remove STATE_LOOKUP")
Reviewed by: kp
MFC after: 2 weeks
Sponsored by: OPNsense
Sponsored by: Klara, Inc.
Differential Revision: https://reviews.freebsd.org/D59015
---
sys/netpfil/pf/pf.c | 17 +++------
tests/sys/netpfil/pf/route_to.sh | 82 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 88 insertions(+), 11 deletions(-)
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 9f19f8203cd5..92b441d1306a 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -2231,14 +2231,9 @@ out:
return (PF_DROP);
}
}
- if (PACKET_LOOPED(pd)) {
- PF_STATE_UNLOCK(s);
- return (PF_PASS);
- }
*state = s;
-
- return (PF_MATCH);
+ return (PACKET_LOOPED(pd) ? PF_PASS : PF_MATCH);
}
/*
@@ -8560,7 +8555,7 @@ pf_icmp_state_lookup(struct pf_state_key_cmp *key, struct pf_pdesc *pd,
return (PF_DROP);
action = pf_find_state(pd, key, state);
- if (action != PF_MATCH)
+ if (action != PF_MATCH && action != PF_PASS)
return (action);
if ((*state)->state_flags & PFSTATE_SLOPPY)
@@ -8885,7 +8880,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
key.port[pd2.didx] = th->th_dport;
action = pf_find_state(&pd2, &key, state);
- if (action != PF_MATCH)
+ if (action != PF_MATCH && action != PF_PASS)
return (action);
if (pd->dir == (*state)->direction) {
@@ -9080,7 +9075,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
key.port[pd2.didx] = uh->uh_dport;
action = pf_find_state(&pd2, &key, state);
- if (action != PF_MATCH)
+ if (action != PF_MATCH && action != PF_PASS)
return (action);
/* translate source/destination address, if necessary */
@@ -9212,7 +9207,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
key.port[pd2.didx] = sh->dest_port;
action = pf_find_state(&pd2, &key, state);
- if (action != PF_MATCH)
+ if (action != PF_MATCH && action != PF_PASS)
return (action);
if (pd->dir == (*state)->direction) {
@@ -9598,7 +9593,7 @@ pf_test_state_icmp(struct pf_kstate **state, struct pf_pdesc *pd,
key.port[0] = key.port[1] = 0;
action = pf_find_state(&pd2, &key, state);
- if (action != PF_MATCH)
+ if (action != PF_MATCH && action != PF_PASS)
return (action);
/* translate source/destination address, if necessary */
diff --git a/tests/sys/netpfil/pf/route_to.sh b/tests/sys/netpfil/pf/route_to.sh
index 1caf2367a9e3..3a3e44f0975d 100644
--- a/tests/sys/netpfil/pf/route_to.sh
+++ b/tests/sys/netpfil/pf/route_to.sh
@@ -1981,6 +1981,87 @@ mcast_v6_local_cleanup()
pft_cleanup
}
+atf_test_case "divert_to" "cleanup"
+divert_to_head()
+{
+ atf_set descr 'Test that route-to is applied to divert-injected packets'
+ atf_set require.user root
+ atf_set require.kmods ipdivert
+}
+divert_to_body()
+{
+ pft_init
+
+ # Set up our topology:
+ #
+ # client <--> firewall <--> gateway <--> server
+ #
+ # The firewall has no default route. route-to forces TCP port 8080
+ # traffic via epair_wan to the gateway; without it the firewall has no
+ # route to 10.4.4.2 and drops the packet.
+
+ epair_cl=$(vnet_mkepair)
+ epair_wan=$(vnet_mkepair)
+ epair_srv=$(vnet_mkepair)
+
+ vnet_mkjail client ${epair_cl}a
+ vnet_mkjail firewall ${epair_cl}b ${epair_wan}a
+ vnet_mkjail gateway ${epair_wan}b ${epair_srv}a
+ vnet_mkjail server ${epair_srv}b
+
+ # Client
+ atf_check jexec client ifconfig ${epair_cl}a 10.1.1.2/24 up
+ atf_check -o ignore jexec client route add default 10.1.1.1
+
+ # Firewall: no default route; route-to sends traffic out via epair_wan
+ atf_check jexec firewall ifconfig ${epair_cl}b 10.1.1.1/24 up
+ atf_check jexec firewall ifconfig ${epair_wan}a 10.3.3.1/30 up
+ atf_check -o ignore jexec firewall sysctl net.inet.ip.forwarding=1
+
+ # Gateway: routes between firewall and server
+ atf_check jexec gateway ifconfig ${epair_wan}b 10.3.3.2/30 up
+ atf_check jexec gateway ifconfig ${epair_srv}a 10.4.4.1/24 up
+ atf_check -o ignore jexec gateway sysctl net.inet.ip.forwarding=1
+ atf_check -o ignore jexec gateway route add -net 10.1.1.0/24 10.3.3.1
+
+ # Server
+ atf_check jexec server ifconfig ${epair_srv}b 10.4.4.2/24 up
+ atf_check -o ignore jexec server route add default 10.4.4.1
+
+ atf_check -o ignore jexec client ping -c 1 10.1.1.1
+
+ jexec server nc -l 8080 > $(pwd)/out &
+ server_pid=$!
+
+ # Firewall pf: divert the flow and route-to via wan
+ jexec firewall pfctl -e
+ pft_set_rules firewall \
+ "pass in quick on ${epair_cl}b \
+ route-to (${epair_wan}a 10.3.3.2) \
+ inet proto tcp from 10.1.1.0/24 to 10.4.4.2 port 8080 \
+ keep state divert-to 8000" \
+ "pass in all" \
+ "pass out all"
+
+ # Divert daemon: receives the SYN and re-injects it
+ jexec firewall ${common_dir}/divapp 8000 divert-back &
+ divapp_pid=$!
+ sleep 1
+
+ # The connection must succeed: route-to must be applied to the
+ # re-injected packet for it to reach the server.
+ echo "hello there" >$(pwd)/in
+ atf_check jexec client nc -N -w 3 10.4.4.2 8080 < $(pwd)/in
+
+ wait $divapp_pid
+ wait $server_pid
+ atf_check -o match:"hello there" cat $(pwd)/out
+}
+divert_to_cleanup()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "v4"
@@ -2008,6 +2089,7 @@ atf_init_test_cases()
atf_add_test_case "mcast_v4_local"
atf_add_test_case "mcast_v6_forwarded"
atf_add_test_case "mcast_v6_local"
+ atf_add_test_case "divert_to"
# Tests for pf_map_addr() without prefer-ipv6-nexthop
atf_add_test_case "table_loop"
atf_add_test_case "roundrobin"