* [PATCH nf-next RFC] netfilter: nft_exthdr: add inner option type search for MPTCP
@ 2026-10-08 17:40 Fernando Fernandez Mancera
2026-10-09 10:22 ` Matthieu Baerts
2026-11-13 10:04 ` Pablo Neira Ayuso
0 siblings, 2 replies; 5+ messages in thread
From: Fernando Fernandez Mancera @ 2026-10-08 17:40 UTC (permalink / raw)
To: netfilter-devel
Cc: coreteam, pablo, fw, phil, matttbe, Fernando Fernandez Mancera
MPTCP carries each of its suboptions as a separate TCP option of kind
30. The suboption type is stored in the upper four bits of the third
byte of the option. A single packet can therefore carry several kind 30
options, for example a dss followed by an mp-prio.
nft_exthdr_tcp_eval() stops at the first option whose kind equals
NFTA_EXTHDR_TYPE. A rule such as:
tcp option mptcp subtype mp-prio drop
only ever sees the first MPTCP suboption. If that is a dss, the subtype
comparison fails and the rule never matches, even though an mp-prio is
present later in the same header.
Add NFTA_EXTHDR_INNER_TYPE. When present, the expression keeps walking
the options of the requested kind until one carries the requested inner
type. If the end of the options area is reached without a match, the
expression behaves as if the option was not found: NFT_BREAK, or 0 when
NFT_EXTHDR_F_PRESENT is set.
See the generated bytecodes below:
- Bytecode for matching mp-join without using NFT_EXTHDR_F_PRESENT flag.
ip mptcp_exthdr input 3 2
[ exthdr load tcpopt 1b @ 30 + 2 inner 1 => reg 1 ]
[ bitwise reg 1 = ( reg 1 & 0xf0 ) ^ 0x00 ]
[ cmp eq reg 1 0x10 ]
- Bytecode for matching mp-join using the NFT_EXTHDR_F_PRESENT flag.
ip mptcp_exthdr input 8 7
[ exthdr load tcpopt 1b @ 30 + 0 present inner 1 => reg 1 ]
[ cmp eq reg 1 0x01 ]
Note that while using the present flag is more efficient, if we decide
to use it in nftables command line tool it would change the behavior
when using old kernels reverting from false negatives to false
positives.
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
Note: I have tested this with a libnftnl patch too and the code needs to
be polished for the final version as more checks are required on init
functions.
---
include/uapi/linux/netfilter/nf_tables.h | 2 +
net/netfilter/nft_exthdr.c | 50 +++++++++++++++++++-----
2 files changed, 43 insertions(+), 9 deletions(-)
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index 0b708153469c..99c977c72641 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -910,6 +910,7 @@ enum nft_exthdr_op {
* @NFTA_EXTHDR_FLAGS: extension header flags (NLA_U32)
* @NFTA_EXTHDR_OP: option match type (NLA_U32)
* @NFTA_EXTHDR_SREG: source register (NLA_U32: nft_registers)
+ * @NFTA_EXTHDR_INNER_TYPE: header inner type (NLA_U32)
*/
enum nft_exthdr_attributes {
NFTA_EXTHDR_UNSPEC,
@@ -920,6 +921,7 @@ enum nft_exthdr_attributes {
NFTA_EXTHDR_FLAGS,
NFTA_EXTHDR_OP,
NFTA_EXTHDR_SREG,
+ NFTA_EXTHDR_INNER_TYPE,
__NFTA_EXTHDR_MAX
};
#define NFTA_EXTHDR_MAX (__NFTA_EXTHDR_MAX - 1)
diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
index 861bab93f435..984b54d2c6dd 100644
--- a/net/netfilter/nft_exthdr.c
+++ b/net/netfilter/nft_exthdr.c
@@ -24,6 +24,8 @@ struct nft_exthdr {
u8 dreg;
u8 sreg;
u8 flags;
+ u8 inner_type;
+ bool has_inner;
};
static unsigned int optlen(const u8 *opt, unsigned int offset)
@@ -207,7 +209,15 @@ static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
if (priv->type != opt[i])
continue;
- if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
+ if (i + optl > tcphdr_len)
+ goto err;
+
+ if (priv->has_inner && priv->type == TCPOPT_MPTCP) {
+ if (!(optl >= 3 && (opt[i + 2] >> 4) == priv->inner_type))
+ continue;
+ }
+
+ if (priv->len + priv->offset > optl)
goto err;
offset = i + priv->offset;
@@ -507,14 +517,15 @@ static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
[NFTA_EXTHDR_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_EXTHDR_F_PRESENT),
[NFTA_EXTHDR_OP] = NLA_POLICY_MAX(NLA_BE32, 255),
[NFTA_EXTHDR_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
+ [NFTA_EXTHDR_INNER_TYPE] = NLA_POLICY_MAX(NLA_BE32, 255),
};
static int nft_exthdr_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
+ u32 offset, len, inner_type, flags = 0, op = NFT_EXTHDR_OP_IPV6;
struct nft_exthdr *priv = nft_expr_priv(expr);
- u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
int err;
if (!tb[NFTA_EXTHDR_DREG] ||
@@ -546,14 +557,27 @@ static int nft_exthdr_init(const struct nft_ctx *ctx,
return err;
}
+ if (tb[NFTA_EXTHDR_INNER_TYPE]) {
+ if (op != NFT_EXTHDR_OP_TCPOPT)
+ return -EOPNOTSUPP;
+
+ err = nft_parse_u32_check(tb[NFTA_EXTHDR_INNER_TYPE],
+ U8_MAX, &inner_type);
+ if (err < 0)
+ return err;
+
+ priv->has_inner = true;
+ }
+
if ((flags & NFT_EXTHDR_F_PRESENT) && len != 1)
return -EINVAL;
- priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
- priv->offset = offset;
- priv->len = len;
- priv->flags = flags;
- priv->op = op;
+ priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
+ priv->offset = offset;
+ priv->len = len;
+ priv->flags = flags;
+ priv->op = op;
+ priv->inner_type = inner_type;
return nft_parse_register_store(ctx, tb[NFTA_EXTHDR_DREG],
&priv->dreg, NULL, NFT_DATA_VALUE,
@@ -574,7 +598,9 @@ static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,
!tb[NFTA_EXTHDR_LEN])
return -EINVAL;
- if (tb[NFTA_EXTHDR_DREG] || tb[NFTA_EXTHDR_FLAGS])
+ if (tb[NFTA_EXTHDR_DREG] ||
+ tb[NFTA_EXTHDR_FLAGS] ||
+ tb[NFTA_EXTHDR_INNER_TYPE])
return -EINVAL;
err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
@@ -615,7 +641,8 @@ static int nft_exthdr_tcp_strip_init(const struct nft_ctx *ctx,
tb[NFTA_EXTHDR_DREG] ||
tb[NFTA_EXTHDR_FLAGS] ||
tb[NFTA_EXTHDR_OFFSET] ||
- tb[NFTA_EXTHDR_LEN])
+ tb[NFTA_EXTHDR_LEN] ||
+ tb[NFTA_EXTHDR_INNER_TYPE])
return -EINVAL;
if (!tb[NFTA_EXTHDR_TYPE])
@@ -679,6 +706,11 @@ static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *
goto nla_put_failure;
if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op)))
goto nla_put_failure;
+ if (priv->has_inner) {
+ if (nla_put_be32(skb, NFTA_EXTHDR_INNER_TYPE, htonl(priv->inner_type)))
+ goto nla_put_failure;
+ }
+
return 0;
nla_put_failure:
--
2.55.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH nf-next RFC] netfilter: nft_exthdr: add inner option type search for MPTCP
2026-10-08 17:40 [PATCH nf-next RFC] netfilter: nft_exthdr: add inner option type search for MPTCP Fernando Fernandez Mancera
@ 2026-10-09 10:22 ` Matthieu Baerts
2026-10-09 13:05 ` Fernando Fernandez Mancera
2026-11-13 10:04 ` Pablo Neira Ayuso
1 sibling, 1 reply; 5+ messages in thread
From: Matthieu Baerts @ 2026-10-09 10:22 UTC (permalink / raw)
To: Fernando Fernandez Mancera, netfilter-devel; +Cc: coreteam, pablo, fw, phil
Hi Fernando,
On 08/10/2026 19:40, Fernando Fernandez Mancera wrote:
> MPTCP carries each of its suboptions as a separate TCP option of kind
> 30. The suboption type is stored in the upper four bits of the third
> byte of the option. A single packet can therefore carry several kind 30
> options, for example a dss followed by an mp-prio.
>
> nft_exthdr_tcp_eval() stops at the first option whose kind equals
> NFTA_EXTHDR_TYPE. A rule such as:
>
> tcp option mptcp subtype mp-prio drop
>
> only ever sees the first MPTCP suboption. If that is a dss, the subtype
> comparison fails and the rule never matches, even though an mp-prio is
> present later in the same header.
Thank you for working on this!
> Add NFTA_EXTHDR_INNER_TYPE. When present, the expression keeps walking
> the options of the requested kind until one carries the requested inner
> type. If the end of the options area is reached without a match, the
> expression behaves as if the option was not found: NFT_BREAK, or 0 when
> NFT_EXTHDR_F_PRESENT is set.
>
> See the generated bytecodes below:
>
> - Bytecode for matching mp-join without using NFT_EXTHDR_F_PRESENT flag.
>
> ip mptcp_exthdr input 3 2
> [ exthdr load tcpopt 1b @ 30 + 2 inner 1 => reg 1 ]
> [ bitwise reg 1 = ( reg 1 & 0xf0 ) ^ 0x00 ]
> [ cmp eq reg 1 0x10 ]
>
> - Bytecode for matching mp-join using the NFT_EXTHDR_F_PRESENT flag.
>
> ip mptcp_exthdr input 8 7
> [ exthdr load tcpopt 1b @ 30 + 0 present inner 1 => reg 1 ]
> [ cmp eq reg 1 0x01 ]
>
> Note that while using the present flag is more efficient, if we decide
> to use it in nftables command line tool it would change the behavior
> when using old kernels reverting from false negatives to false
> positives.
Could nft use NFTA_EXTHDR_INNER_TYPE if the kernel supports it, and
fallback to the previous method if not?
Or maybe better to have a new option that will explicitly fail if the
kernel doesn't support it, not to miss packets on older kernel versions?
(...)
> diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
> index 861bab93f435..984b54d2c6dd 100644
> --- a/net/netfilter/nft_exthdr.c
> +++ b/net/netfilter/nft_exthdr.c
(...)
> @@ -207,7 +209,15 @@ static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
> if (priv->type != opt[i])
> continue;
>
> - if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
> + if (i + optl > tcphdr_len)
> + goto err;
> +
> + if (priv->has_inner && priv->type == TCPOPT_MPTCP) {
Why does it need to be limited to MPTCP?
(I understand it is probably easier like that, and that's fine for my
use-case, but I'm just thinking about extensions that could be done only
via the userspace.)
> + if (!(optl >= 3 && (opt[i + 2] >> 4) == priv->inner_type))
Can you not use priv->offset and priv->len not to have something
specific to MPTCP?
But then, you need to keep the safety check around them before this. And
you probably need to check that the length is <= 8 at parsing time.
(...)
> @@ -679,6 +706,11 @@ static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *
> goto nla_put_failure;
> if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op)))
> goto nla_put_failure;
> + if (priv->has_inner) {
> + if (nla_put_be32(skb, NFTA_EXTHDR_INNER_TYPE, htonl(priv->inner_type)))
(double spaces before htonl())
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH nf-next RFC] netfilter: nft_exthdr: add inner option type search for MPTCP
2026-10-09 10:22 ` Matthieu Baerts
@ 2026-10-09 13:05 ` Fernando Fernandez Mancera
2026-11-13 10:07 ` Pablo Neira Ayuso
0 siblings, 1 reply; 5+ messages in thread
From: Fernando Fernandez Mancera @ 2026-10-09 13:05 UTC (permalink / raw)
To: Matthieu Baerts, netfilter-devel; +Cc: coreteam, pablo, fw, phil
On 10/9/26 12:22 PM, Matthieu Baerts wrote:
> Hi Fernando,
>
> On 08/10/2026 19:40, Fernando Fernandez Mancera wrote:
>> MPTCP carries each of its suboptions as a separate TCP option of kind
>> 30. The suboption type is stored in the upper four bits of the third
>> byte of the option. A single packet can therefore carry several kind 30
>> options, for example a dss followed by an mp-prio.
>>
>> nft_exthdr_tcp_eval() stops at the first option whose kind equals
>> NFTA_EXTHDR_TYPE. A rule such as:
>>
>> tcp option mptcp subtype mp-prio drop
>>
>> only ever sees the first MPTCP suboption. If that is a dss, the subtype
>> comparison fails and the rule never matches, even though an mp-prio is
>> present later in the same header.
>
> Thank you for working on this!
>
>> Add NFTA_EXTHDR_INNER_TYPE. When present, the expression keeps walking
>> the options of the requested kind until one carries the requested inner
>> type. If the end of the options area is reached without a match, the
>> expression behaves as if the option was not found: NFT_BREAK, or 0 when
>> NFT_EXTHDR_F_PRESENT is set.
>>
>> See the generated bytecodes below:
>>
>> - Bytecode for matching mp-join without using NFT_EXTHDR_F_PRESENT flag.
>>
>> ip mptcp_exthdr input 3 2
>> [ exthdr load tcpopt 1b @ 30 + 2 inner 1 => reg 1 ]
>> [ bitwise reg 1 = ( reg 1 & 0xf0 ) ^ 0x00 ]
>> [ cmp eq reg 1 0x10 ]
>>
>> - Bytecode for matching mp-join using the NFT_EXTHDR_F_PRESENT flag.
>>
>> ip mptcp_exthdr input 8 7
>> [ exthdr load tcpopt 1b @ 30 + 0 present inner 1 => reg 1 ]
>> [ cmp eq reg 1 0x01 ]
>>
>> Note that while using the present flag is more efficient, if we decide
>> to use it in nftables command line tool it would change the behavior
>> when using old kernels reverting from false negatives to false
>> positives.
>
> Could nft use NFTA_EXTHDR_INNER_TYPE if the kernel supports it, and
> fallback to the previous method if not?
>
> Or maybe better to have a new option that will explicitly fail if the
> kernel doesn't support it, not to miss packets on older kernel versions?
>
Well, I was expecting that nft will use the same syntax and only the
underlying bytecode would change. If we want to preserve backward
compatibility, then we should go with the first option. That is, not
using the MFT_EXTHDR_F_PRESENT flag. In such case, the compatibility
will be preserved for older kernels.
It just adds a bitwise operation overhead which is also the current
situation.
> (...)
>
>> diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
>> index 861bab93f435..984b54d2c6dd 100644
>> --- a/net/netfilter/nft_exthdr.c
>> +++ b/net/netfilter/nft_exthdr.c
>
> (...)
>
>> @@ -207,7 +209,15 @@ static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
>> if (priv->type != opt[i])
>> continue;
>>
>> - if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
>> + if (i + optl > tcphdr_len)
>> + goto err;
>> +
>> + if (priv->has_inner && priv->type == TCPOPT_MPTCP) {
>
> Why does it need to be limited to MPTCP?
>
> (I understand it is probably easier like that, and that's fine for my
> use-case, but I'm just thinking about extensions that could be done only
> via the userspace.)
>
>> + if (!(optl >= 3 && (opt[i + 2] >> 4) == priv->inner_type))
>
> Can you not use priv->offset and priv->len not to have something
> specific to MPTCP?
>
> But then, you need to keep the safety check around them before this. And
> you probably need to check that the length is <= 8 at parsing time.
>
We could allow that but I do not have an usecase right now. If we do
have one, please let me know. This new _INNER_TYPE option can be also
used for SCTP for example, but that would need to be implemented later.
For now, I would stick to MPTCP although the option is flexible enough
to be expanded in the future.
> (...)
>
>> @@ -679,6 +706,11 @@ static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *
>> goto nla_put_failure;
>> if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op)))
>> goto nla_put_failure;
>> + if (priv->has_inner) {
>> + if (nla_put_be32(skb, NFTA_EXTHDR_INNER_TYPE, htonl(priv->inner_type)))
>
> (double spaces before htonl())
Oops. Yes, code needs some improvement for example when validating the
values during expression initialization, but I want to focus on the idea
itself for this RFC.
Thanks for the comments Matthieu!
>
> Cheers,
> Matt
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH nf-next RFC] netfilter: nft_exthdr: add inner option type search for MPTCP
2026-10-09 13:05 ` Fernando Fernandez Mancera
@ 2026-11-13 10:07 ` Pablo Neira Ayuso
0 siblings, 0 replies; 5+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13 10:07 UTC (permalink / raw)
To: Fernando Fernandez Mancera
Cc: Matthieu Baerts, netfilter-devel, coreteam, fw, phil
One more comment.
On Fri, Oct 09, 2026 at 03:05:34PM +0200, Fernando Fernandez Mancera wrote:
> On 10/9/26 12:22 PM, Matthieu Baerts wrote:
[...]
> >
> > > + if (!(optl >= 3 && (opt[i + 2] >> 4) == priv->inner_type))
> >
> > Can you not use priv->offset and priv->len not to have something
> > specific to MPTCP?
> >
> > But then, you need to keep the safety check around them before this. And
> > you probably need to check that the length is <= 8 at parsing time.
> >
>
> We could allow that but I do not have an usecase right now. If we do have
> one, please let me know. This new _INNER_TYPE option can be also used for
> SCTP for example, but that would need to be implemented later.
Would it be easier to add the SCTP support later if this is all
generic? Or generalizing it as Matthieu suggest is too much work?
This will require a new kernel patch in the future. Allowing for
userspace updates to support new feature is convenient.
This is not a deal breaker, let me know.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH nf-next RFC] netfilter: nft_exthdr: add inner option type search for MPTCP
2026-10-08 17:40 [PATCH nf-next RFC] netfilter: nft_exthdr: add inner option type search for MPTCP Fernando Fernandez Mancera
2026-10-09 10:22 ` Matthieu Baerts
@ 2026-11-13 10:04 ` Pablo Neira Ayuso
1 sibling, 0 replies; 5+ messages in thread
From: Pablo Neira Ayuso @ 2026-11-13 10:04 UTC (permalink / raw)
To: Fernando Fernandez Mancera; +Cc: netfilter-devel, coreteam, fw, phil, matttbe
On Thu, Oct 08, 2026 at 07:40:30PM +0200, Fernando Fernandez Mancera wrote:
> MPTCP carries each of its suboptions as a separate TCP option of kind
> 30. The suboption type is stored in the upper four bits of the third
> byte of the option. A single packet can therefore carry several kind 30
> options, for example a dss followed by an mp-prio.
>
> nft_exthdr_tcp_eval() stops at the first option whose kind equals
> NFTA_EXTHDR_TYPE. A rule such as:
>
> tcp option mptcp subtype mp-prio drop
>
> only ever sees the first MPTCP suboption. If that is a dss, the subtype
> comparison fails and the rule never matches, even though an mp-prio is
> present later in the same header.
>
> Add NFTA_EXTHDR_INNER_TYPE. When present, the expression keeps walking
> the options of the requested kind until one carries the requested inner
> type. If the end of the options area is reached without a match, the
> expression behaves as if the option was not found: NFT_BREAK, or 0 when
> NFT_EXTHDR_F_PRESENT is set.
>
> See the generated bytecodes below:
>
> - Bytecode for matching mp-join without using NFT_EXTHDR_F_PRESENT flag.
>
> ip mptcp_exthdr input 3 2
> [ exthdr load tcpopt 1b @ 30 + 2 inner 1 => reg 1 ]
> [ bitwise reg 1 = ( reg 1 & 0xf0 ) ^ 0x00 ]
> [ cmp eq reg 1 0x10 ]
>
> - Bytecode for matching mp-join using the NFT_EXTHDR_F_PRESENT flag.
>
> ip mptcp_exthdr input 8 7
> [ exthdr load tcpopt 1b @ 30 + 0 present inner 1 => reg 1 ]
> [ cmp eq reg 1 0x01 ]
>
> Note that while using the present flag is more efficient, if we decide
> to use it in nftables command line tool it would change the behavior
> when using old kernels reverting from false negatives to false
> positives.
Could you add a new NFT_EXTHDR_F_INNER_TYPE flag so this fails on
older kernels as Matthieu suggest?
For simplicity, the idea would be to set on this new flag if
the new NFTA_EXTHDR_INNER_TYPE attribute is used.
These are flags, setting NFT_EXTHDR_F_PRESENT and the new
NFT_EXTHDR_F_INNER_TYPE should be fine. This flag is an artifact that
would be only used in the control plane, so older kernels will reject
this rule since they do not support it.
> Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
> ---
> Note: I have tested this with a libnftnl patch too and the code needs to
> be polished for the final version as more checks are required on init
> functions.
> ---
> include/uapi/linux/netfilter/nf_tables.h | 2 +
> net/netfilter/nft_exthdr.c | 50 +++++++++++++++++++-----
> 2 files changed, 43 insertions(+), 9 deletions(-)
>
> diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
> index 0b708153469c..99c977c72641 100644
> --- a/include/uapi/linux/netfilter/nf_tables.h
> +++ b/include/uapi/linux/netfilter/nf_tables.h
> @@ -910,6 +910,7 @@ enum nft_exthdr_op {
> * @NFTA_EXTHDR_FLAGS: extension header flags (NLA_U32)
> * @NFTA_EXTHDR_OP: option match type (NLA_U32)
> * @NFTA_EXTHDR_SREG: source register (NLA_U32: nft_registers)
> + * @NFTA_EXTHDR_INNER_TYPE: header inner type (NLA_U32)
> */
> enum nft_exthdr_attributes {
> NFTA_EXTHDR_UNSPEC,
> @@ -920,6 +921,7 @@ enum nft_exthdr_attributes {
> NFTA_EXTHDR_FLAGS,
> NFTA_EXTHDR_OP,
> NFTA_EXTHDR_SREG,
> + NFTA_EXTHDR_INNER_TYPE,
> __NFTA_EXTHDR_MAX
> };
> #define NFTA_EXTHDR_MAX (__NFTA_EXTHDR_MAX - 1)
> diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c
> index 861bab93f435..984b54d2c6dd 100644
> --- a/net/netfilter/nft_exthdr.c
> +++ b/net/netfilter/nft_exthdr.c
> @@ -24,6 +24,8 @@ struct nft_exthdr {
> u8 dreg;
> u8 sreg;
> u8 flags;
> + u8 inner_type;
> + bool has_inner;
> };
>
> static unsigned int optlen(const u8 *opt, unsigned int offset)
> @@ -207,7 +209,15 @@ static void nft_exthdr_tcp_eval(const struct nft_expr *expr,
> if (priv->type != opt[i])
> continue;
>
> - if (i + optl > tcphdr_len || priv->len + priv->offset > optl)
> + if (i + optl > tcphdr_len)
> + goto err;
> +
> + if (priv->has_inner && priv->type == TCPOPT_MPTCP) {
> + if (!(optl >= 3 && (opt[i + 2] >> 4) == priv->inner_type))
> + continue;
> + }
> +
> + if (priv->len + priv->offset > optl)
> goto err;
>
> offset = i + priv->offset;
> @@ -507,14 +517,15 @@ static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = {
> [NFTA_EXTHDR_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_EXTHDR_F_PRESENT),
> [NFTA_EXTHDR_OP] = NLA_POLICY_MAX(NLA_BE32, 255),
> [NFTA_EXTHDR_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX),
> + [NFTA_EXTHDR_INNER_TYPE] = NLA_POLICY_MAX(NLA_BE32, 255),
> };
>
> static int nft_exthdr_init(const struct nft_ctx *ctx,
> const struct nft_expr *expr,
> const struct nlattr * const tb[])
> {
> + u32 offset, len, inner_type, flags = 0, op = NFT_EXTHDR_OP_IPV6;
> struct nft_exthdr *priv = nft_expr_priv(expr);
> - u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6;
> int err;
>
> if (!tb[NFTA_EXTHDR_DREG] ||
> @@ -546,14 +557,27 @@ static int nft_exthdr_init(const struct nft_ctx *ctx,
> return err;
> }
>
> + if (tb[NFTA_EXTHDR_INNER_TYPE]) {
> + if (op != NFT_EXTHDR_OP_TCPOPT)
> + return -EOPNOTSUPP;
> +
> + err = nft_parse_u32_check(tb[NFTA_EXTHDR_INNER_TYPE],
> + U8_MAX, &inner_type);
> + if (err < 0)
> + return err;
> +
> + priv->has_inner = true;
> + }
> +
> if ((flags & NFT_EXTHDR_F_PRESENT) && len != 1)
> return -EINVAL;
>
> - priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
> - priv->offset = offset;
> - priv->len = len;
> - priv->flags = flags;
> - priv->op = op;
> + priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]);
> + priv->offset = offset;
> + priv->len = len;
> + priv->flags = flags;
> + priv->op = op;
> + priv->inner_type = inner_type;
>
> return nft_parse_register_store(ctx, tb[NFTA_EXTHDR_DREG],
> &priv->dreg, NULL, NFT_DATA_VALUE,
> @@ -574,7 +598,9 @@ static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx,
> !tb[NFTA_EXTHDR_LEN])
> return -EINVAL;
>
> - if (tb[NFTA_EXTHDR_DREG] || tb[NFTA_EXTHDR_FLAGS])
> + if (tb[NFTA_EXTHDR_DREG] ||
> + tb[NFTA_EXTHDR_FLAGS] ||
> + tb[NFTA_EXTHDR_INNER_TYPE])
> return -EINVAL;
>
> err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset);
> @@ -615,7 +641,8 @@ static int nft_exthdr_tcp_strip_init(const struct nft_ctx *ctx,
> tb[NFTA_EXTHDR_DREG] ||
> tb[NFTA_EXTHDR_FLAGS] ||
> tb[NFTA_EXTHDR_OFFSET] ||
> - tb[NFTA_EXTHDR_LEN])
> + tb[NFTA_EXTHDR_LEN] ||
> + tb[NFTA_EXTHDR_INNER_TYPE])
> return -EINVAL;
>
> if (!tb[NFTA_EXTHDR_TYPE])
> @@ -679,6 +706,11 @@ static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *
> goto nla_put_failure;
> if (nla_put_be32(skb, NFTA_EXTHDR_OP, htonl(priv->op)))
> goto nla_put_failure;
> + if (priv->has_inner) {
> + if (nla_put_be32(skb, NFTA_EXTHDR_INNER_TYPE, htonl(priv->inner_type)))
> + goto nla_put_failure;
> + }
> +
> return 0;
>
> nla_put_failure:
> --
> 2.55.0
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-13 10:07 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-08 17:40 [PATCH nf-next RFC] netfilter: nft_exthdr: add inner option type search for MPTCP Fernando Fernandez Mancera
2026-10-09 10:22 ` Matthieu Baerts
2026-10-09 13:05 ` Fernando Fernandez Mancera
2026-11-13 10:07 ` Pablo Neira Ayuso
2026-11-13 10:04 ` Pablo Neira Ayuso
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.