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
This commit is contained in:
Kristof Provost 2022-11-22 14:23:27 +01:00
parent ce9f36610e
commit 57e047e51c
10 changed files with 36 additions and 21 deletions

View file

@ -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; }
;

View file

@ -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])

View file

@ -0,0 +1 @@
scrub fragment no reassemble

View file

@ -0,0 +1 @@
scrub all fragment no reassemble

View file

@ -0,0 +1 @@
scrub

View file

@ -0,0 +1 @@
scrub all fragment reassemble

View file

@ -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")

View file

@ -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

View file

@ -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

View file

@ -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);
h = mtod(m, struct ip *);
m = *m0;
if (m == NULL)
return (PF_DROP);
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);