* [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 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-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 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