* [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation for non-first fragments
@ 2026-04-17 18:34 Fernando Fernandez Mancera
2026-04-17 18:34 ` [PATCH 2/4 nf] netfilter: nft_tproxy: skip " Fernando Fernandez Mancera
` (3 more replies)
0 siblings, 4 replies; 7+ messages in thread
From: Fernando Fernandez Mancera @ 2026-04-17 18:34 UTC (permalink / raw)
To: netfilter-devel
Cc: netdev, coreteam, pablo, fw, phil, Fernando Fernandez Mancera
The SCTP chunk matching logic in nft_exthdr relies on SCTP common header
being present at the transport header offset. For fragmented packets at
IP level, only the first fragment would match this condition.
The nft_exthdr could be used in a PREROUTING chain with a priority lower
than -400. This would bypass defragmentation. In addition, it can be use
in stateless environments so it should work on a environment where
defragmentation is not being performed at all.
Add a check for pkt->fragoff to ensure exthdr SCTP only evaluates
unfragmented packets or the first fragment in the stream.
Fixes: 133dc203d77d ("netfilter: nft_exthdr: Support SCTP chunks")
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
net/netfilter/nft_exthdr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
index 7eedf4e3ae9c..8eb708bb8cff 100644
--- a/net/netfilter/nft_exthdr.c
+++ b/net/netfilter/nft_exthdr.c
@@ -376,7 +376,7 @@ static void nft_exthdr_sctp_eval(const struct nft_expr *expr,
const struct sctp_chunkhdr *sch;
struct sctp_chunkhdr _sch;
- if (pkt->tprot != IPPROTO_SCTP)
+ if (pkt->tprot != IPPROTO_SCTP || pkt->fragoff)
goto err;
do {
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/4 nf] netfilter: nft_tproxy: skip evaluation for non-first fragments
2026-04-17 18:34 [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation for non-first fragments Fernando Fernandez Mancera
@ 2026-04-17 18:34 ` Fernando Fernandez Mancera
2026-04-17 18:34 ` [PATCH 3/4 nf] netfilter: nft_osf: " Fernando Fernandez Mancera
` (2 subsequent siblings)
3 siblings, 0 replies; 7+ messages in thread
From: Fernando Fernandez Mancera @ 2026-04-17 18:34 UTC (permalink / raw)
To: netfilter-devel
Cc: netdev, coreteam, pablo, fw, phil, Fernando Fernandez Mancera
The tproxy expression relies on L4 ports to perform socke lookups. For
fragmented packets, every fragment carries the transport protocol used
but only the first fragment contains the L4 header.
As nftables is not evaluating chain priority, a tproxy expression could
be attached to a PREROUTING chain with a priority lower than -400. This
would bypass defragmentation.
Add a check for pkt->fragoff to ensure tproxy only evaluates
unfragmented packets or the first fragment in the stream.
Fixes: 4ed8eb6570a4 ("netfilter: nf_tables: Add native tproxy support")
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
net/netfilter/nft_tproxy.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c
index 50481280abd2..8080cbd878cd 100644
--- a/net/netfilter/nft_tproxy.c
+++ b/net/netfilter/nft_tproxy.c
@@ -30,8 +30,8 @@ static void nft_tproxy_eval_v4(const struct nft_expr *expr,
__be16 tport = 0;
struct sock *sk;
- if (pkt->tprot != IPPROTO_TCP &&
- pkt->tprot != IPPROTO_UDP) {
+ if ((pkt->tprot != IPPROTO_TCP &&
+ pkt->tprot != IPPROTO_UDP) || pkt->fragoff) {
regs->verdict.code = NFT_BREAK;
return;
}
@@ -97,8 +97,8 @@ static void nft_tproxy_eval_v6(const struct nft_expr *expr,
memset(&taddr, 0, sizeof(taddr));
- if (pkt->tprot != IPPROTO_TCP &&
- pkt->tprot != IPPROTO_UDP) {
+ if ((pkt->tprot != IPPROTO_TCP &&
+ pkt->tprot != IPPROTO_UDP) || pkt->fragoff) {
regs->verdict.code = NFT_BREAK;
return;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/4 nf] netfilter: nft_osf: skip evaluation for non-first fragments
2026-04-17 18:34 [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation for non-first fragments Fernando Fernandez Mancera
2026-04-17 18:34 ` [PATCH 2/4 nf] netfilter: nft_tproxy: skip " Fernando Fernandez Mancera
@ 2026-04-17 18:34 ` Fernando Fernandez Mancera
2026-04-17 18:34 ` [PATCH 4/4 nf] netfilter: xtables: fix L4 header parsing " Fernando Fernandez Mancera
2026-04-18 7:49 ` [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation " Pablo Neira Ayuso
3 siblings, 0 replies; 7+ messages in thread
From: Fernando Fernandez Mancera @ 2026-04-17 18:34 UTC (permalink / raw)
To: netfilter-devel
Cc: netdev, coreteam, pablo, fw, phil, Fernando Fernandez Mancera
The osf expression extracts TCP options to match them against
fingerprints. For fragmented packets, every fragment carries the
transport protocol used but only the first fragment contains the TCP
header.
As nftables is not evaluating chain priority, a osf expression could be
attached to a PREROUTING chain with a priority lower than -400. This
would bypass defragmentation. In addition, nft_osf should be able to
work in stateless environments, therefore it can be use in situation
when defragmentation is not being performed.
Add a check for pkt->fragoff to ensure osf only evaluates unfragmented
packets or the first fragment in the stream.
Fixes: b96af92d6eaf ("netfilter: nf_tables: implement Passive OS fingerprint module in nft_osf")
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
net/netfilter/nft_osf.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c
index 1c0b493ef0a9..ceca87e405eb 100644
--- a/net/netfilter/nft_osf.c
+++ b/net/netfilter/nft_osf.c
@@ -28,7 +28,7 @@ static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs,
struct nf_osf_data data;
struct tcphdr _tcph;
- if (pkt->tprot != IPPROTO_TCP) {
+ if (pkt->tprot != IPPROTO_TCP || pkt->fragoff) {
regs->verdict.code = NFT_BREAK;
return;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 4/4 nf] netfilter: xtables: fix L4 header parsing for non-first fragments
2026-04-17 18:34 [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation for non-first fragments Fernando Fernandez Mancera
2026-04-17 18:34 ` [PATCH 2/4 nf] netfilter: nft_tproxy: skip " Fernando Fernandez Mancera
2026-04-17 18:34 ` [PATCH 3/4 nf] netfilter: nft_osf: " Fernando Fernandez Mancera
@ 2026-04-17 18:34 ` Fernando Fernandez Mancera
2026-04-18 7:51 ` Pablo Neira Ayuso
2026-04-18 7:49 ` [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation " Pablo Neira Ayuso
3 siblings, 1 reply; 7+ messages in thread
From: Fernando Fernandez Mancera @ 2026-04-17 18:34 UTC (permalink / raw)
To: netfilter-devel
Cc: netdev, coreteam, pablo, fw, phil, Fernando Fernandez Mancera
The TPROXY target and osf match relies on L4 header to operate. For
fragmented packets, every fragment carries the transport protocol
identifier, but only the first fragment contains the L4 header.
As the 'raw' table can be configured to run at priority -450 (before
defragmentation at -400), the target/match can be reached before
reassembly. In this case, non-first fragments have their payload
incorrectly parsed as a TCP/UDP header.
Add a fragment check to ensure TPROXY/osf only evaluates unfragmented
packets or the first fragment in the stream.
Fixes: 902d6a4c2a4f ("netfilter: nf_defrag: Skip defrag if NOTRACK is set")
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
net/netfilter/xt_TPROXY.c | 8 ++++++--
net/netfilter/xt_osf.c | 3 +++
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c
index e4bea1d346cf..ac4b011ce48c 100644
--- a/net/netfilter/xt_TPROXY.c
+++ b/net/netfilter/xt_TPROXY.c
@@ -40,6 +40,9 @@ tproxy_tg4(struct net *net, struct sk_buff *skb, __be32 laddr, __be16 lport,
struct udphdr _hdr, *hp;
struct sock *sk;
+ if (ip_is_fragment(iph))
+ return NF_DROP;
+
hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
if (hp == NULL)
return NF_DROP;
@@ -106,6 +109,7 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
{
const struct ipv6hdr *iph = ipv6_hdr(skb);
const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
+ unsigned short fragoff = 0;
struct udphdr _hdr, *hp;
struct sock *sk;
const struct in6_addr *laddr;
@@ -113,8 +117,8 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
int thoff = 0;
int tproto;
- tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
- if (tproto < 0)
+ tproto = ipv6_find_hdr(skb, &thoff, -1, &fragoff, NULL);
+ if (tproto < 0 || fragoff)
return NF_DROP;
hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index dc9485854002..889dff4daff0 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -27,6 +27,9 @@
static bool
xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
{
+ if (ip_is_fragment(ip_hdr(skb)))
+ return false;
+
return nf_osf_match(skb, xt_family(p), xt_hooknum(p), xt_in(p),
xt_out(p), p->matchinfo, xt_net(p), nf_osf_fingers);
}
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation for non-first fragments
2026-04-17 18:34 [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation for non-first fragments Fernando Fernandez Mancera
` (2 preceding siblings ...)
2026-04-17 18:34 ` [PATCH 4/4 nf] netfilter: xtables: fix L4 header parsing " Fernando Fernandez Mancera
@ 2026-04-18 7:49 ` Pablo Neira Ayuso
2026-04-18 9:51 ` Fernando Fernandez Mancera
3 siblings, 1 reply; 7+ messages in thread
From: Pablo Neira Ayuso @ 2026-04-18 7:49 UTC (permalink / raw)
To: Fernando Fernandez Mancera; +Cc: netfilter-devel, netdev, coreteam, fw, phil
Hi Fernando,
On Fri, Apr 17, 2026 at 08:34:30PM +0200, Fernando Fernandez Mancera wrote:
> The SCTP chunk matching logic in nft_exthdr relies on SCTP common header
> being present at the transport header offset. For fragmented packets at
> IP level, only the first fragment would match this condition.
>
> The nft_exthdr could be used in a PREROUTING chain with a priority lower
> than -400. This would bypass defragmentation. In addition, it can be use
> in stateless environments so it should work on a environment where
> defragmentation is not being performed at all.
Yes, and stateless filtering is still a valid configuration, ie.
nf_conntrack is not loaded.
> Add a check for pkt->fragoff to ensure exthdr SCTP only evaluates
> unfragmented packets or the first fragment in the stream.
I would suggest to squash the three small patches to check for
pkt->fragoff in one patch. The three expressions have been already
around for a while (backporting the combo patch that makes the same
logical change should be easy) and it is basically the same logical
change.
Thanks!
> Fixes: 133dc203d77d ("netfilter: nft_exthdr: Support SCTP chunks")
> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
> ---
> net/netfilter/nft_exthdr.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
> index 7eedf4e3ae9c..8eb708bb8cff 100644
> --- a/net/netfilter/nft_exthdr.c
> +++ b/net/netfilter/nft_exthdr.c
> @@ -376,7 +376,7 @@ static void nft_exthdr_sctp_eval(const struct nft_expr *expr,
> const struct sctp_chunkhdr *sch;
> struct sctp_chunkhdr _sch;
>
> - if (pkt->tprot != IPPROTO_SCTP)
> + if (pkt->tprot != IPPROTO_SCTP || pkt->fragoff)
> goto err;
>
> do {
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 4/4 nf] netfilter: xtables: fix L4 header parsing for non-first fragments
2026-04-17 18:34 ` [PATCH 4/4 nf] netfilter: xtables: fix L4 header parsing " Fernando Fernandez Mancera
@ 2026-04-18 7:51 ` Pablo Neira Ayuso
0 siblings, 0 replies; 7+ messages in thread
From: Pablo Neira Ayuso @ 2026-04-18 7:51 UTC (permalink / raw)
To: Fernando Fernandez Mancera; +Cc: netfilter-devel, netdev, coreteam, fw, phil
On Fri, Apr 17, 2026 at 08:34:35PM +0200, Fernando Fernandez Mancera wrote:
> The TPROXY target and osf match relies on L4 header to operate. For
> fragmented packets, every fragment carries the transport protocol
> identifier, but only the first fragment contains the L4 header.
>
> As the 'raw' table can be configured to run at priority -450 (before
> defragmentation at -400), the target/match can be reached before
> reassembly. In this case, non-first fragments have their payload
> incorrectly parsed as a TCP/UDP header.
I see, this refers to a misconfiguration scenario.
> Add a fragment check to ensure TPROXY/osf only evaluates unfragmented
> packets or the first fragment in the stream.
LGTM this combo patch for osf and TPROXY in xtables.
Thanks.
> Fixes: 902d6a4c2a4f ("netfilter: nf_defrag: Skip defrag if NOTRACK is set")
> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
> ---
> net/netfilter/xt_TPROXY.c | 8 ++++++--
> net/netfilter/xt_osf.c | 3 +++
> 2 files changed, 9 insertions(+), 2 deletions(-)
>
> diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c
> index e4bea1d346cf..ac4b011ce48c 100644
> --- a/net/netfilter/xt_TPROXY.c
> +++ b/net/netfilter/xt_TPROXY.c
> @@ -40,6 +40,9 @@ tproxy_tg4(struct net *net, struct sk_buff *skb, __be32 laddr, __be16 lport,
> struct udphdr _hdr, *hp;
> struct sock *sk;
>
> + if (ip_is_fragment(iph))
> + return NF_DROP;
> +
> hp = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_hdr), &_hdr);
> if (hp == NULL)
> return NF_DROP;
> @@ -106,6 +109,7 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
> {
> const struct ipv6hdr *iph = ipv6_hdr(skb);
> const struct xt_tproxy_target_info_v1 *tgi = par->targinfo;
> + unsigned short fragoff = 0;
> struct udphdr _hdr, *hp;
> struct sock *sk;
> const struct in6_addr *laddr;
> @@ -113,8 +117,8 @@ tproxy_tg6_v1(struct sk_buff *skb, const struct xt_action_param *par)
> int thoff = 0;
> int tproto;
>
> - tproto = ipv6_find_hdr(skb, &thoff, -1, NULL, NULL);
> - if (tproto < 0)
> + tproto = ipv6_find_hdr(skb, &thoff, -1, &fragoff, NULL);
> + if (tproto < 0 || fragoff)
> return NF_DROP;
>
> hp = skb_header_pointer(skb, thoff, sizeof(_hdr), &_hdr);
> diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
> index dc9485854002..889dff4daff0 100644
> --- a/net/netfilter/xt_osf.c
> +++ b/net/netfilter/xt_osf.c
> @@ -27,6 +27,9 @@
> static bool
> xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p)
> {
> + if (ip_is_fragment(ip_hdr(skb)))
> + return false;
> +
> return nf_osf_match(skb, xt_family(p), xt_hooknum(p), xt_in(p),
> xt_out(p), p->matchinfo, xt_net(p), nf_osf_fingers);
> }
> --
> 2.53.0
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation for non-first fragments
2026-04-18 7:49 ` [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation " Pablo Neira Ayuso
@ 2026-04-18 9:51 ` Fernando Fernandez Mancera
0 siblings, 0 replies; 7+ messages in thread
From: Fernando Fernandez Mancera @ 2026-04-18 9:51 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel, netdev, coreteam, fw, phil
On 4/18/26 9:49 AM, Pablo Neira Ayuso wrote:
> Hi Fernando,
>
> On Fri, Apr 17, 2026 at 08:34:30PM +0200, Fernando Fernandez Mancera wrote:
>> The SCTP chunk matching logic in nft_exthdr relies on SCTP common header
>> being present at the transport header offset. For fragmented packets at
>> IP level, only the first fragment would match this condition.
>>
>> The nft_exthdr could be used in a PREROUTING chain with a priority lower
>> than -400. This would bypass defragmentation. In addition, it can be use
>> in stateless environments so it should work on a environment where
>> defragmentation is not being performed at all.
>
> Yes, and stateless filtering is still a valid configuration, ie.
> nf_conntrack is not loaded.
>
>> Add a check for pkt->fragoff to ensure exthdr SCTP only evaluates
>> unfragmented packets or the first fragment in the stream.
>
> I would suggest to squash the three small patches to check for
> pkt->fragoff in one patch. The three expressions have been already
> around for a while (backporting the combo patch that makes the same
> logical change should be easy) and it is basically the same logical
> change.
>
Hi Pablo,
Thanks for the review! I am not sure about squashing them as they all
have different blamed commits. I find accurate fixes tag quite useful
when handling backports and I guess others do too (also for stable
kernels). Is that convincing?
Anyway, not a big deal if there is a strong preference I will squash them.
Thanks,
Fernando.
> Thanks!
>
>> Fixes: 133dc203d77d ("netfilter: nft_exthdr: Support SCTP chunks")
>> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
>> ---
>> net/netfilter/nft_exthdr.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
>> index 7eedf4e3ae9c..8eb708bb8cff 100644
>> --- a/net/netfilter/nft_exthdr.c
>> +++ b/net/netfilter/nft_exthdr.c
>> @@ -376,7 +376,7 @@ static void nft_exthdr_sctp_eval(const struct nft_expr *expr,
>> const struct sctp_chunkhdr *sch;
>> struct sctp_chunkhdr _sch;
>>
>> - if (pkt->tprot != IPPROTO_SCTP)
>> + if (pkt->tprot != IPPROTO_SCTP || pkt->fragoff)
>> goto err;
>>
>> do {
>> --
>> 2.53.0
>>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-04-18 9:52 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-17 18:34 [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation for non-first fragments Fernando Fernandez Mancera
2026-04-17 18:34 ` [PATCH 2/4 nf] netfilter: nft_tproxy: skip " Fernando Fernandez Mancera
2026-04-17 18:34 ` [PATCH 3/4 nf] netfilter: nft_osf: " Fernando Fernandez Mancera
2026-04-17 18:34 ` [PATCH 4/4 nf] netfilter: xtables: fix L4 header parsing " Fernando Fernandez Mancera
2026-04-18 7:51 ` Pablo Neira Ayuso
2026-04-18 7:49 ` [PATCH 1/4 nf] netfilter: nft_exthdr: skip SCTP chunk evaluation " Pablo Neira Ayuso
2026-04-18 9:51 ` Fernando Fernandez Mancera
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox