From: Pablo Neira Ayuso <pablo@netfilter.org>
To: Matthieu Baerts <matttbe@kernel.org>
Cc: Netfilter Devel <netfilter-devel@vger.kernel.org>,
Netfilter Coreteam <coreteam@netfilter.org>
Subject: Re: Netfilter: match "tcp option" with the same type present multiple times
Date: Wed, 7 Oct 2026 01:29:30 +0200 [thread overview]
Message-ID: <asWEWpVJLk0so8zM@chamomile> (raw)
In-Reply-To: <6365150c-09d0-4a2c-84e3-ffe6e292e317@kernel.org>
Hi Matthieu,
On Wed, Sep 30, 2026 at 08:33:36PM +0200, Matthieu Baerts wrote:
> Hi Pablo,
>
> Thank you for your reply!
>
> On 29/09/2026 14:03, Pablo Neira Ayuso wrote:
> > On Tue, Sep 29, 2026 at 12:40:47PM +0200, Matthieu Baerts wrote:
> >> On 29/09/2026 12:00, Pablo Neira Ayuso wrote:
> >>> On Tue, Sep 29, 2026 at 11:21:15AM +0200, Matthieu Baerts wrote:
> >>>> Hi Pablo,
> >>>>
> >>>> Thank you for your reply!
> >>>>
> >>>> On 28/09/2026 23:52, Pablo Neira Ayuso wrote:
> >>>>> Hi Mattieu,
> >>>>>
> >>>>> On Mon, Sep 28, 2026 at 02:13:16PM +0200, Matthieu Baerts wrote:
> >>>>>> Hello Netfilter devs,
> >>>>>>
> >>>>>> First, thank you for maintaining Netfilter in the kernel and the
> >>>>>> userspace tools.
> >>>>>>
> >>>>>> The MPTCP selftests are switching from IPTables to NFTables, and
> >>>>>> Clashiko reported that this part of a rule wouldn't match anything:
> >>>>>>
> >>>>>> tcp option mptcp subtype remove-addr drop
> >>>>>>
> >>>>>> When an MPTCP REMOVE_ADDR suboption (type 0x30, len >=4, subtype 0x4) is
> >>>>>> added to the TCP options, it is added after an MPTCP DSS option (type
> >>>>>> 0x30, len >= 8, subtype 0x2). In other words, there will be two MPTCP
> >>>>>> (type 30) options in the TCP options. It looks like Netfilter doesn't
> >>>>>> handle that, because it stops processing other TCP options when the
> >>>>>> expected type is found:
> >>>>>>
> >>>>>> net/netfilter/nft_exthdr.c:nft_exthdr_tcp_eval() {
> >>>>>> ...
> >>>>>> for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) {
> >>>>>> optl = optlen(opt, i);
> >>>>>>
> >>>>>> if (priv->type != opt[i])
> >>>>>> continue;
> >>>>>> ...
> >>>>>> return; // <== it will look at the first MPTCP option
> >>>>>> }
> >>>>>> ...
> >>>>>> }
> >>>>>>
> >>>>>> It looks like it shouldn't stop if the wrong subtype is found, but the
> >>>>>> subtype is not compared there if I'm not mistaken. Should there be a fix
> >>>>>> to support this case?
> >>>>>
> >>>>> Would it work for you if you can specify what TCP option you want to
> >>>>> match? ie.
> >>>>>
> >>>>> tcp option[2] mptcp subtype remove-addr drop
> >>>>> ^
> >>>>> |
> >>>>> |
> >>>>> allow to specify a match at a given tcp option
> >>>>
> >>>> In my specific use-case, yes it would work. Some MPTCP sub-options will
> >>>> always be after another one... when the Linux stack is used.
> >>>
> >>> This would require a smaller patch.
> >>
> >> I understand. But I guess that still means adding a new option.
> >>
> >> In my case, it would work, but this might be confusing for people who
> >> want to use this filter: they need to know if a suboption can be used
> >> with another one. I guess they could have multiple rules to check the
> >> presence of a suboption in the first matched type, and in a second one,
> >> but that seems confusing. Or maybe not?
> >
> > You mean, the might want to validate the entire list of suboptions in
> > a particular order, correct? ie. first suboption A then suboption B.
>
> Yes, that's one possibility. But initially, I was thinking about having
> two rules (or a set?) to match "the MPTCP subtype is either in the first
> MPTCP option, or the second one (if any)". (Or nft could always say with
> MPTCP by default, check also for a second MPTCP option, if any)
>
> >>>>> Or would this be too strict for your use-case and you would prefer
> >>>>> that you can match it anywhere?
> >>>>
> >>>> I think it would make more sense to match it anywhere. For example, an
> >>>> MP_RESET can be used alone, or after an MP_FASTCLOSE. Plus some stacks
> >>>> could reorder the options as there is no imposed order.
> >>>
> >>> I think this would require a MPTCP subtype parser, ie. make kernel
> >>> aware of MPTCP subtypes.
> >>
> >> In nft_exthdr_tcp_eval(), why can the comparison not be done there
> >> instead of copying the data in a register, and compare later on? If the
> >> comparison is done directly for a given type and is different, the code
> >> could continue and check for the same type. (Or store in multiple
> >> registers.)
> >
> > In nftables, the fetch then cmp instructions are splitted, ie. you
> > first retrieve then you compare (or make a set lookup with it).
> > Builtin comparison should be possible, but it defeats the integration
> > with the set infrastructure.
>
> OK, I see.
>
> If someone gives ...
>
> tcp option mptcp subtype { mp-capable, mp-join, remove-addr } drop
>
> ... a bitmap could be used, but then that's specific to MPTCP I suppose.
I think this makes sense from user perspective.
The nft_exthdr expression needs iterate over the whole list of tcp
options, then if type is 30, make a set lookup to check if the subtype
value is in the set.
Something like:
- loop
| r1 <- exthdr [ if no more tcp options, break loop ]
| r2 <- lookup(r1) [ if found, break loop ]
but exthdr need to be taugh to resume from the last visited tcp
option. A new loop expression would wrap these two exthdr and lookup
expression (similar to nft_inner) and store the iteration context (ie.
last visited tcp option to continue from there).
exthdr tcp option support needs advertise a new NFT_EXPR_LOOP flag, so
it can be used with within this new nft_loop expression.
Another question: Would you still like to validate that DSS comes
before REMOVE_ADDR?
> >> I don't know if you need something specific to MPTCP, maybe just a way
> >> to check all options with the same type.
> >
> > Going back to this:
> >
> > "When an MPTCP REMOVE_ADDR suboption (type 0x30, len >=4, subtype 0x4) is
> > added to the TCP options, it is added after an MPTCP DSS option (type
> > 0x30, len >= 8, subtype 0x2)"
> >
> > Would you like to check for this particular sequence, right? Then
>
> In my case, no particularly -- I just want to count/drop REMOVE_ADDR to
> catch kernel regressions in the selftests -- but I can.
>
> > position-based solution would work, because this would allow to match
> > strictly:
> >
> > pos = 0, suboption DSS
> > pos = 1, suboption REMOVE_ADDR
> >
> > This ruleset might break the protocol if there is ever a suboption
> > before DSS.
>
> Indeed. Technically, with MPTCP, we could send the REMOVE_ADDR alone
> (without DSS), or before the DSS, or with another one in between if
> there is room.
Position matching is too strict.
You can still make it with the raw expression, but I think the native
matching representation should be more flexible too as you describe.
> > Loose mode is more protocol designer friendly as it does not constrain
> > future updates in the protocol:
> >
> > "please find MP-TCP suboption REMOVE_ADDR for me"
> >
> > but security folks might probably say this is ... loose, because it
> > does not validate DSS before and REMOVE_ADDR might come anywhere.
> > Protocol designer would say that this is good for them because this
> > firewall does not constrain future enhancements of the protocol (still
> > raw expressions are possible, then this last statement does not hold
> > anymore).
> >
> > Which one would you pick? :-)
>
> I would first prefer if people don't drop packets with MPTCP options :-D
>
> Honestly, I'm not sure what people would be interested in doing in
> production, but I guess it would be more: "please match packet with
> MPTCP suboption X, no matter the order".
Yes, as discussed, this is more protocol friendly too in terms of
extensibility.
> If people are interested in a specific packet where MPTCP suboptions
> are in a specific order, with a specific length, I *think* they can
> use a raw payload expression instead, no?
>
> In my case, I just wanted to drop packets with a specific MPTCP
> suboption. It happens that I know exactly which packet I want to block,
> and I can use a raw payload expression, but a simpler rule would be
> welcome :)
Makes sense.
This would need an extension in the kernel as Florian has anticipated.
next prev parent reply other threads:[~2026-10-06 23:29 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-28 12:13 Netfilter: match "tcp option" with the same type present multiple times Matthieu Baerts
2026-09-28 13:13 ` Florian Westphal
2026-09-28 20:26 ` Matthieu Baerts
2026-09-28 21:08 ` Florian Westphal
2026-09-28 20:56 ` Fernando Fernandez Mancera
2026-09-28 21:22 ` Florian Westphal
2026-09-28 21:23 ` Fernando Fernandez Mancera
2026-09-28 20:54 ` Fernando Fernandez Mancera
2026-09-28 21:17 ` Fernando Fernandez Mancera
2026-09-28 21:22 ` Matthieu Baerts
2026-09-28 21:52 ` Pablo Neira Ayuso
2026-09-29 9:21 ` Matthieu Baerts
2026-09-29 10:00 ` Pablo Neira Ayuso
2026-09-29 10:40 ` Matthieu Baerts
2026-09-29 12:03 ` Pablo Neira Ayuso
2026-09-30 18:33 ` Matthieu Baerts
2026-10-06 23:29 ` Pablo Neira Ayuso [this message]
2026-10-07 8:01 ` Fernando Fernandez Mancera
2026-10-07 10:47 ` Pablo Neira Ayuso
2026-10-07 8:17 ` Matthieu Baerts
2026-09-28 21:54 ` Jan Engelhardt
2026-09-29 9:34 ` Matthieu Baerts
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=asWEWpVJLk0so8zM@chamomile \
--to=pablo@netfilter.org \
--cc=coreteam@netfilter.org \
--cc=matttbe@kernel.org \
--cc=netfilter-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox