aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2022-11-22 13:23:27 +0000
committerKristof Provost <kp@FreeBSD.org>2022-11-28 19:19:05 +0000
commit57e047e51c6daf72912332bc95263084f4f0430c (patch)
tree3aa68aa8e994fba038bbc84fdff5c9d94f2ed63c
parentce9f36610ea9ff29d42a2bcfed44b020c2e56dcb (diff)
downloadsrc-57e047e51c6daf72912332bc95263084f4f0430c.tar.gz
src-57e047e51c6daf72912332bc95263084f4f0430c.zip
pf: allow scrub rules without fragment reassemble
scrub rules have defaulted to handling fragments for a long time, but since we removed "fragment crop" and "fragment drop-ovl" in 64b3b4d611 this has become less obvious and more expensive ("reassemble" being the more expensive option, even if it's the one the vast majority of users should be using). Extend the 'scrub' syntax to allow fragment reassembly to be disabled, while retaining the other scrub behaviour (e.g. TTL changes, random-id, ..) using 'scrub fragment no reassemble'. Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D37459
-rw-r--r--sbin/pfctl/parse.y3
-rw-r--r--sbin/pfctl/pfctl_parser.c3
-rw-r--r--sbin/pfctl/tests/files/pf1011.in1
-rw-r--r--sbin/pfctl/tests/files/pf1011.ok1
-rw-r--r--sbin/pfctl/tests/files/pf1012.in1
-rw-r--r--sbin/pfctl/tests/files/pf1012.ok1
-rw-r--r--sbin/pfctl/tests/pfctl_test_list.inc2
-rw-r--r--share/man/man5/pf.conf.55
-rw-r--r--sys/netpfil/pf/pf.h1
-rw-r--r--sys/netpfil/pf/pf_norm.c39
10 files changed, 36 insertions, 21 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y
index 1d0494e6d0b9..166cbae79087 100644
--- a/sbin/pfctl/parse.y
+++ b/sbin/pfctl/parse.y
@@ -1528,7 +1528,8 @@ scrub_opt : NODF {
}
;
-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
index 824e473ea2e9..fca1a7aa7b8f 100644
--- a/sbin/pfctl/pfctl_parser.c
+++ b/sbin/pfctl/pfctl_parser.c
@@ -1131,7 +1131,8 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer
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
index 000000000000..84f0e7204e40
--- /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
index 000000000000..48572b371d8d
--- /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
index 000000000000..9083d1bf5396
--- /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
index 000000000000..b7f1f454fb6a
--- /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
index 0b6d5110034d..0b7d89099efe 100644
--- a/sbin/pfctl/tests/pfctl_test_list.inc
+++ b/sbin/pfctl/tests/pfctl_test_list.inc
@@ -121,3 +121,5 @@ PFCTL_TEST(1007, "Basic ethernet rule")
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
index ed57ad5b1c9f..c7446f53bb49 100644
--- 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 @@ packet, and only the completed packet is passed on to the filter.
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
index cc3063b887c9..3e3ba977285a 100644
--- a/sys/netpfil/pf/pf.h
+++ b/sys/netpfil/pf/pf.h
@@ -598,6 +598,7 @@ struct pf_rule {
/* 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
index 5827fb0b093b..283f6771221c 100644
--- a/sys/netpfil/pf/pf_norm.c
+++ b/sys/netpfil/pf/pf_norm.c
@@ -1115,31 +1115,34 @@ pf_normalize_ip(struct mbuf **m0, int dir, struct pfi_kkif *kif, u_short *reason
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);