diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -1528,7 +1528,8 @@ } ; -fragcache : FRAGMENT REASSEMBLE { $$ = 0; /* default */ } +fragcache : FRAGMENT REASSEMBLE { $$ = 0; /* default */ } + | FRAGMENT NO REASSEMBLE { $$ = PFRULE_FRAGMENT_NOREASS; } | FRAGMENT FRAGCROP { $$ = 0; } | FRAGMENT FRAGDROP { $$ = 0; } ; diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -1131,7 +1131,8 @@ if (r->rule_flag & PFRULE_REASSEMBLE_TCP) printf(" reassemble tcp"); - printf(" fragment reassemble"); + printf(" fragment %sreassemble", + r->rule_flag & PFRULE_FRAGMENT_NOREASS ? "no " : ""); } i = 0; while (r->label[i][0]) diff --git a/sbin/pfctl/tests/files/pf1011.in b/sbin/pfctl/tests/files/pf1011.in new file mode 100644 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1011.in @@ -0,0 +1 @@ +scrub fragment no reassemble diff --git a/sbin/pfctl/tests/files/pf1011.ok b/sbin/pfctl/tests/files/pf1011.ok new file mode 100644 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1011.ok @@ -0,0 +1 @@ +scrub all fragment no reassemble diff --git a/sbin/pfctl/tests/files/pf1012.in b/sbin/pfctl/tests/files/pf1012.in new file mode 100644 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1012.in @@ -0,0 +1 @@ +scrub diff --git a/sbin/pfctl/tests/files/pf1012.ok b/sbin/pfctl/tests/files/pf1012.ok new file mode 100644 --- /dev/null +++ b/sbin/pfctl/tests/files/pf1012.ok @@ -0,0 +1 @@ +scrub all fragment reassemble diff --git a/sbin/pfctl/tests/pfctl_test_list.inc b/sbin/pfctl/tests/pfctl_test_list.inc --- a/sbin/pfctl/tests/pfctl_test_list.inc +++ b/sbin/pfctl/tests/pfctl_test_list.inc @@ -121,3 +121,5 @@ PFCTL_TEST(1008, "Ethernet rule with mask length") PFCTL_TEST(1009, "Ethernet rule with mask") PFCTL_TEST(1010, "POM_STICKYADDRESS test") +PFCTL_TEST(1011, "Test disabling scrub fragment reassemble") +PFCTL_TEST(1012, "Test scrub fragment reassemble is default") diff --git a/share/man/man5/pf.conf.5 b/share/man/man5/pf.conf.5 --- a/share/man/man5/pf.conf.5 +++ b/share/man/man5/pf.conf.5 @@ -28,7 +28,7 @@ .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd October 28, 2022 +.Dd November 24, 2022 .Dt PF.CONF 5 .Os .Sh NAME @@ -832,6 +832,9 @@ The advantage is that filter rules have to deal only with complete packets, and can ignore fragments. The drawback of caching fragments is the additional memory cost. +This is the default behaviour unless no fragment reassemble is specified. +.It Ar no fragment reassemble +Do not reassemble fragments. .It Ar reassemble tcp Statefully normalizes TCP connections. .Ar scrub reassemble tcp diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h --- a/sys/netpfil/pf/pf.h +++ b/sys/netpfil/pf/pf.h @@ -598,6 +598,7 @@ /* scrub flags */ #define PFRULE_NODF 0x0100 +#define PFRULE_FRAGMENT_NOREASS 0x0200 #define PFRULE_RANDOMID 0x0800 #define PFRULE_REASSEMBLE_TCP 0x1000 #define PFRULE_SET_TOS 0x2000 diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c --- a/sys/netpfil/pf/pf_norm.c +++ b/sys/netpfil/pf/pf_norm.c @@ -1115,31 +1115,34 @@ DPFPRINTF(("max packet %d\n", fragoff + ip_len)); goto bad; } - max = fragoff + ip_len; - /* Fully buffer all of the fragments - * Might return a completely reassembled mbuf, or NULL */ - PF_FRAG_LOCK(); - DPFPRINTF(("reass frag %d @ %d-%d\n", h->ip_id, fragoff, max)); - verdict = pf_reassemble(m0, h, dir, reason); - PF_FRAG_UNLOCK(); + if (! (r->rule_flag & PFRULE_FRAGMENT_NOREASS)) { + max = fragoff + ip_len; - if (verdict != PF_PASS) - return (PF_DROP); + /* Fully buffer all of the fragments + * Might return a completely reassembled mbuf, or NULL */ + PF_FRAG_LOCK(); + DPFPRINTF(("reass frag %d @ %d-%d\n", h->ip_id, fragoff, max)); + verdict = pf_reassemble(m0, h, dir, reason); + PF_FRAG_UNLOCK(); - m = *m0; - if (m == NULL) - return (PF_DROP); + if (verdict != PF_PASS) + return (PF_DROP); + + m = *m0; + if (m == NULL) + return (PF_DROP); - h = mtod(m, struct ip *); + h = mtod(m, struct ip *); no_fragment: - /* At this point, only IP_DF is allowed in ip_off */ - if (h->ip_off & ~htons(IP_DF)) { - u_int16_t ip_off = h->ip_off; + /* At this point, only IP_DF is allowed in ip_off */ + if (h->ip_off & ~htons(IP_DF)) { + u_int16_t ip_off = h->ip_off; - h->ip_off &= htons(IP_DF); - h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_off, h->ip_off, 0); + h->ip_off &= htons(IP_DF); + h->ip_sum = pf_cksum_fixup(h->ip_sum, ip_off, h->ip_off, 0); + } } pf_scrub_ip(&m, r->rule_flag, r->min_ttl, r->set_tos);