Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 3 Apr 2025 17:00:17 GMT
From:      Kristof Provost <kp@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 7e70d94acd68 - main - pf: allow 'allow-opts' to be used on match rules
Message-ID:  <202504031700.533H0HXR052468@gitrepo.freebsd.org>

next in thread | raw e-mail | index | archive | help
The branch main has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=7e70d94acd68b3ac6b45f49d4ab7a0f7867c3ea7

commit 7e70d94acd68b3ac6b45f49d4ab7a0f7867c3ea7
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2025-04-03 12:58:21 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2025-04-03 16:29:38 +0000

    pf: allow 'allow-opts' to be used on match rules
    
    Allow the 'allow-opts' setting to be set on match rules.
    The intended use case is to simplify dealing with traffic where IP options are
    expected (e.g. IGMP), without allowing them on other traffic, or making the
    ruleset more complex.
    
    Add a test case confirming this works as expected.
    
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
---
 sbin/pfctl/parse.y            |  5 ++--
 sys/net/pfvar.h               |  1 +
 sys/netpfil/pf/pf.c           |  6 ++--
 tests/sys/netpfil/pf/match.sh | 66 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 74 insertions(+), 4 deletions(-)

diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index ea81efd7fd94..1362ae43428c 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -5325,8 +5325,9 @@ filter_consistent(struct pfctl_rule *r, int anchor_call)
 		    r->af == AF_INET ? "inet" : "inet6");
 		problems++;
 	}
-	if (r->allow_opts && r->action != PF_PASS) {
-		yyerror("allow-opts can only be specified for pass rules");
+	if (r->allow_opts && r->action != PF_PASS && r->action != PF_MATCH) {
+		yyerror("allow-opts can only be specified for pass or "
+		    "match rules");
 		problems++;
 	}
 	if (r->rule_flag & PFRULE_FRAGMENT && (r->src.port_op ||
diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h
index 82967dc10d03..c511d61d6cc1 100644
--- a/sys/net/pfvar.h
+++ b/sys/net/pfvar.h
@@ -696,6 +696,7 @@ struct pf_rule_actions {
 	uint8_t		 min_ttl;
 	uint8_t		 set_prio[2];
 	uint8_t		 rt;
+	uint8_t		 allow_opts;
 };
 
 union pf_keth_rule_ptr {
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 775bd016c656..c343512b20dd 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -4890,6 +4890,8 @@ pf_rule_to_actions(struct pf_krule *r, struct pf_rule_actions *a)
 		a->set_prio[0] = r->set_prio[0];
 		a->set_prio[1] = r->set_prio[1];
 	}
+	if (r->allow_opts)
+		a->allow_opts = r->allow_opts;
 }
 
 int
@@ -6102,7 +6104,7 @@ pf_create_state(struct pf_krule *r, struct pf_krule *nr, struct pf_krule *a,
 	memcpy(&s->match_rules, match_rules, sizeof(s->match_rules));
 	memcpy(&s->act, &pd->act, sizeof(struct pf_rule_actions));
 
-	if (r->allow_opts)
+	if (pd->act.allow_opts)
 		s->state_flags |= PFSTATE_ALLOWOPTS;
 	if (r->rule_flag & PFRULE_STATESLOPPY)
 		s->state_flags |= PFSTATE_SLOPPY;
@@ -10473,7 +10475,7 @@ done:
 		goto eat_pkt;
 
 	if (action == PF_PASS && pd.badopts &&
-	    !((s && s->state_flags & PFSTATE_ALLOWOPTS) || r->allow_opts)) {
+	    !((s && s->state_flags & PFSTATE_ALLOWOPTS) || pd.act.allow_opts)) {
 		action = PF_DROP;
 		REASON_SET(&reason, PFRES_IPOPTIONS);
 		pd.act.log = PF_LOG_FORCE;
diff --git a/tests/sys/netpfil/pf/match.sh b/tests/sys/netpfil/pf/match.sh
index 93b908e62f2d..58c1e021310a 100644
--- a/tests/sys/netpfil/pf/match.sh
+++ b/tests/sys/netpfil/pf/match.sh
@@ -26,6 +26,8 @@
 
 . $(atf_get_srcdir)/utils.subr
 
+common_dir=$(atf_get_srcdir)/../common
+
 atf_test_case "dummynet" "cleanup"
 dummynet_head()
 {
@@ -112,8 +114,72 @@ quick_cleanup()
 	pft_cleanup
 }
 
+atf_test_case "allow_opts" "cleanup"
+allow_opts_head()
+{
+	atf_set descr 'Test allowing IP options via match'
+	atf_set require.user root
+	atf_set require.progs python3 scapy
+}
+
+allow_opts_body()
+{
+	pft_init
+
+	epair=$(vnet_mkepair)
+
+	ifconfig ${epair}b 192.0.2.2/24 up
+
+	vnet_mkjail alcatraz ${epair}a
+	jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up
+
+	jexec alcatraz pfctl -e
+	jexec alcatraz pfctl -x loud
+	pft_set_rules alcatraz \
+	    "match proto icmp allow-opts" \
+	    "pass"
+
+	# Sanity check
+	atf_check -s exit:0 -o ignore \
+	    ping -c 1 192.0.2.1
+
+	atf_check -s exit:0 -o ignore \
+	    ${common_dir}/pft_ping.py  \
+	    --sendif ${epair}b \
+	    --to 192.0.2.1 \
+	    --send-nop \
+	    --replyif ${epair}b
+
+	# This doesn't work without 'allow-opts'
+	pft_set_rules alcatraz \
+	    "match proto icmp" \
+	    "pass"
+	atf_check -s exit:1 -o ignore \
+	    ${common_dir}/pft_ping.py  \
+	    --sendif ${epair}b \
+	    --to 192.0.2.1 \
+	    --send-nop \
+	    --replyif ${epair}b
+
+	# Setting it on a pass rule still works.
+	pft_set_rules alcatraz \
+	    "pass allow-opts"
+	atf_check -s exit:0 -o ignore \
+	    ${common_dir}/pft_ping.py  \
+	    --sendif ${epair}b \
+	    --to 192.0.2.1 \
+	    --send-nop \
+	    --replyif ${epair}b
+}
+
+allow_opts_cleanup()
+{
+	pft_cleanup
+}
+
 atf_init_test_cases()
 {
 	atf_add_test_case "dummynet"
 	atf_add_test_case "quick"
+	atf_add_test_case "allow_opts"
 }



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202504031700.533H0HXR052468>