Linux Netfilter development
 help / color / mirror / Atom feed
From: Pablo Neira Ayuso <pablo@netfilter.org>
To: Florian Westphal <fw@strlen.de>
Cc: netfilter-devel@vger.kernel.org, Xiang Mei <xmei5@asu.edu>,
	Weiming Shi <bestswngs@gmail.com>
Subject: Re: [PATCH nf] netfilter: x_tables: add late validate callback for nft_compat sake
Date: Sun, 19 Apr 2026 12:50:27 +0200	[thread overview]
Message-ID: <aeSzcx9YmM3usuez@chamomile> (raw)
In-Reply-To: <20260419104509.42196-1-fw@strlen.de>

On Sun, Apr 19, 2026 at 12:45:05PM +0200, Florian Westphal wrote:
> x_tables and nftables are fundamentally different.
> In x_tables, one gets the full ruleset graph via setsockopt().
> ->checkentry() gets called at ruleset validation time.
> 
> In nf_tables, you get a transactional request (rule add in this case)
> in netlink format.  At this time, it is not yet knowm from which
> basechain(s) the new expression is reachable.
>
> In nf_tables, there is one final hook validation pass right before the
> point-of-no-return when the new state is fully known.
>
> However, nft_compat calls the x_tables checkentry functions way too
> early, at expression instantiation time, when we have the netlink
> info available but not the base chain info (not yet known).

There used to be full validation of the table in each transaction in
nf_tables.

What happened?

> At final validation time we lack the additional compat information
> userspace provided to us.  So we need compat_info + base chain info,
> but we never have both.
> 
> Add a new .nft_validate_chain callback to x_tables.
> It is not allowed to have side effects and only performs addititonal
> hook_mask validiation at last possible moment.
> It is called from nft_compat .validate callbacks.
> 
> Fixes: 0ca743a55991 ("netfilter: nf_tables: add compatibility layer for x_tables")
> Reported-by: Xiang Mei <xmei5@asu.edu>
> Cc: Weiming Shi <bestswngs@gmail.com>
> Signed-off-by: Florian Westphal <fw@strlen.de>
> ---
>  Tentative hack to resolve this bug where it originates.
> 
>  include/linux/netfilter/x_tables.h | 15 ++++++++++++++
>  net/netfilter/nft_compat.c         | 32 +++++++++++++++++++++++++++---
>  net/netfilter/xt_TCPMSS.c          | 25 ++++++++++++++++-------
>  net/netfilter/xt_addrtype.c        | 27 +++++++++++++++++++------
>  net/netfilter/xt_devgroup.c        | 31 +++++++++++++++++++----------
>  net/netfilter/xt_physdev.c         | 21 ++++++++++++++++----
>  net/netfilter/xt_policy.c          | 24 ++++++++++++++++++----
>  net/netfilter/xt_set.c             | 22 +++++++++++++++-----
>  8 files changed, 158 insertions(+), 39 deletions(-)
> 
> diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
> index 77c778d84d4c..d1dcef359e61 100644
> --- a/include/linux/netfilter/x_tables.h
> +++ b/include/linux/netfilter/x_tables.h
> @@ -146,6 +146,10 @@ struct xt_match {
>  	/* Called when user tries to insert an entry of this type. */
>  	int (*checkentry)(const struct xt_mtchk_param *);
>  
> +#if IS_ENABLED(CONFIG_NFT_COMPAT)
> +	/* only used by nft_compat, must be pure: no side effects allowed */
> +	bool (*nft_validate_chain)(const void *matchinfo, unsigned int hook_mask);
> +#endif
>  	/* Called when entry of this type deleted. */
>  	void (*destroy)(const struct xt_mtdtor_param *);
>  #ifdef CONFIG_NETFILTER_XTABLES_COMPAT
> @@ -187,6 +191,10 @@ struct xt_target {
>  	/* Should return 0 on success or an error code otherwise (-Exxxx). */
>  	int (*checkentry)(const struct xt_tgchk_param *);
>  
> +#if IS_ENABLED(CONFIG_NFT_COMPAT)
> +	/* only used by nft_compat, must be pure: no side effects allowed */
> +	bool (*nft_validate_chain)(const void *targinfo, unsigned int hook_mask);
> +#endif
>  	/* Called when entry of this type deleted. */
>  	void (*destroy)(const struct xt_tgdtor_param *);
>  #ifdef CONFIG_NETFILTER_XTABLES_COMPAT
> @@ -524,4 +532,11 @@ int xt_compat_check_entry_offsets(const void *base, const char *elems,
>  				  unsigned int next_offset);
>  
>  #endif /* CONFIG_NETFILTER_XTABLES_COMPAT */
> +
> +#if IS_ENABLED(CONFIG_NFT_COMPAT)
> +#define NFT_COMPAT_VALIDATE(fname) .nft_validate_chain = fname,
> +#else
> +#define NFT_COMPAT_VALIDATE(fname)
> +#endif
> +
>  #endif /* _X_TABLES_H */
> diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
> index 27cc983a7cdf..cc4041b1f1d0 100644
> --- a/net/netfilter/nft_compat.c
> +++ b/net/netfilter/nft_compat.c
> @@ -382,6 +382,10 @@ static int nft_target_validate(const struct nft_ctx *ctx,
>  		if (target->hooks && !(hook_mask & target->hooks))
>  			return -EINVAL;
>  
> +		if (target->nft_validate_chain &&
> +		    !target->nft_validate_chain(nft_expr_priv(expr), hook_mask))
> +			return -EINVAL;
> +
>  		ret = nft_compat_chain_validate_dependency(ctx, target->table);
>  		if (ret < 0)
>  			return ret;
> @@ -611,10 +615,11 @@ static int nft_match_large_dump(struct sk_buff *skb,
>  	return __nft_match_dump(skb, e, priv->info);
>  }
>  
> -static int nft_match_validate(const struct nft_ctx *ctx,
> -			      const struct nft_expr *expr)
> +static int __nft_match_validate(const struct nft_ctx *ctx,
> +				const struct nft_expr *expr,
> +				const void *info)
>  {
> -	struct xt_match *match = expr->ops->data;
> +	const struct xt_match *match = expr->ops->data;
>  	unsigned int hook_mask = 0;
>  	int ret;
>  
> @@ -643,6 +648,10 @@ static int nft_match_validate(const struct nft_ctx *ctx,
>  		if (match->hooks && !(hook_mask & match->hooks))
>  			return -EINVAL;
>  
> +		if (match->nft_validate_chain &&
> +		    !match->nft_validate_chain(info, hook_mask))
> +			return -EINVAL;
> +
>  		ret = nft_compat_chain_validate_dependency(ctx, match->table);
>  		if (ret < 0)
>  			return ret;
> @@ -650,6 +659,22 @@ static int nft_match_validate(const struct nft_ctx *ctx,
>  	return 0;
>  }
>  
> +static int nft_match_validate(const struct nft_ctx *ctx,
> +			      const struct nft_expr *expr)
> +{
> +	const void *info = nft_expr_priv(expr);
> +
> +	return __nft_match_validate(ctx, expr, info);
> +}
> +
> +static int nft_match_large_validate(const struct nft_ctx *ctx,
> +			            const struct nft_expr *expr)
> +{
> +	struct nft_xt_match_priv *priv = nft_expr_priv(expr);
> +
> +	return __nft_match_validate(ctx, expr, priv->info);
> +}
> +
>  static int
>  nfnl_compat_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type,
>  		      int event, u16 family, const char *name,
> @@ -838,6 +863,7 @@ nft_match_select_ops(const struct nft_ctx *ctx,
>  		ops->init = nft_match_large_init;
>  		ops->destroy = nft_match_large_destroy;
>  		ops->dump = nft_match_large_dump;
> +		ops->validate = nft_match_large_validate;
>  	}
>  
>  	ops->size = matchsize;
> diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c
> index 116a885adb3c..261115ad4242 100644
> --- a/net/netfilter/xt_TCPMSS.c
> +++ b/net/netfilter/xt_TCPMSS.c
> @@ -260,16 +260,26 @@ static inline bool find_syn_match(const struct xt_entry_match *m)
>  	return false;
>  }
>  
> +static bool tcpmss_validate_chain(const void *tginfo, unsigned int hook_mask)
> +{
> +	const struct xt_tcpmss_info *info = tginfo;
> +
> +	if ((info->mss == XT_TCPMSS_CLAMP_PMTU) &&
> +	   (hook_mask & ~((1 << NF_INET_FORWARD) |
> +			  (1 << NF_INET_LOCAL_OUT) |
> +			  (1 << NF_INET_POST_ROUTING))) != 0)
> +		return false;
> +
> +	return true;
> +}
> +
>  static int tcpmss_tg4_check(const struct xt_tgchk_param *par)
>  {
>  	const struct xt_tcpmss_info *info = par->targinfo;
>  	const struct ipt_entry *e = par->entryinfo;
>  	const struct xt_entry_match *ematch;
>  
> -	if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
> -	    (par->hook_mask & ~((1 << NF_INET_FORWARD) |
> -			   (1 << NF_INET_LOCAL_OUT) |
> -			   (1 << NF_INET_POST_ROUTING))) != 0) {
> +	if (!tcpmss_validate_chain(info, par->hook_mask)) {
>  		pr_info_ratelimited("path-MTU clamping only supported in FORWARD, OUTPUT and POSTROUTING hooks\n");
>  		return -EINVAL;
>  	}
> @@ -291,12 +301,11 @@ static int tcpmss_tg6_check(const struct xt_tgchk_param *par)
>  	const struct xt_entry_match *ematch;
>  
>  	if (info->mss == XT_TCPMSS_CLAMP_PMTU &&
> -	    (par->hook_mask & ~((1 << NF_INET_FORWARD) |
> -			   (1 << NF_INET_LOCAL_OUT) |
> -			   (1 << NF_INET_POST_ROUTING))) != 0) {
> +	    !tcpmss_validate_chain(info, par->hook_mask)) {
>  		pr_info_ratelimited("path-MTU clamping only supported in FORWARD, OUTPUT and POSTROUTING hooks\n");
>  		return -EINVAL;
>  	}
> +
>  	if (par->nft_compat)
>  		return 0;
>  
> @@ -313,6 +322,7 @@ static struct xt_target tcpmss_tg_reg[] __read_mostly = {
>  		.family		= NFPROTO_IPV4,
>  		.name		= "TCPMSS",
>  		.checkentry	= tcpmss_tg4_check,
> +		NFT_COMPAT_VALIDATE(tcpmss_validate_chain)
>  		.target		= tcpmss_tg4,
>  		.targetsize	= sizeof(struct xt_tcpmss_info),
>  		.proto		= IPPROTO_TCP,
> @@ -323,6 +333,7 @@ static struct xt_target tcpmss_tg_reg[] __read_mostly = {
>  		.family		= NFPROTO_IPV6,
>  		.name		= "TCPMSS",
>  		.checkentry	= tcpmss_tg6_check,
> +		NFT_COMPAT_VALIDATE(tcpmss_validate_chain)
>  		.target		= tcpmss_tg6,
>  		.targetsize	= sizeof(struct xt_tcpmss_info),
>  		.proto		= IPPROTO_TCP,
> diff --git a/net/netfilter/xt_addrtype.c b/net/netfilter/xt_addrtype.c
> index a77088943107..bd1391bb0853 100644
> --- a/net/netfilter/xt_addrtype.c
> +++ b/net/netfilter/xt_addrtype.c
> @@ -153,6 +153,21 @@ addrtype_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
>  	return ret;
>  }
>  
> +static bool addrtype_mt_validate(const void *matchinfo, unsigned int hook_mask)
> +{
> +	const struct xt_addrtype_info_v1 *info = matchinfo;
> +
> +	if (hook_mask & ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) &&
> +	    info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT)
> +		return false;
> +
> +	if (hook_mask & ((1 << NF_INET_POST_ROUTING) | (1 << NF_INET_LOCAL_OUT)) &&
> +	    info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN)
> +		return false;
> +
> +	return true;
> +}
> +
>  static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par)
>  {
>  	const char *errmsg = "both incoming and outgoing interface limitation cannot be selected";
> @@ -162,16 +177,14 @@ static int addrtype_mt_checkentry_v1(const struct xt_mtchk_param *par)
>  	    info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT)
>  		goto err;
>  
> -	if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) |
> -	    (1 << NF_INET_LOCAL_IN)) &&
> -	    info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) {
> +	if ((info->flags & XT_ADDRTYPE_LIMIT_IFACE_OUT) &&
> +	    !addrtype_mt_validate(info, par->hook_mask)) {
>  		errmsg = "output interface limitation not valid in PREROUTING and INPUT";
>  		goto err;
>  	}
>  
> -	if (par->hook_mask & ((1 << NF_INET_POST_ROUTING) |
> -	    (1 << NF_INET_LOCAL_OUT)) &&
> -	    info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN) {
> +	if ((info->flags & XT_ADDRTYPE_LIMIT_IFACE_IN) &&
> +	    !addrtype_mt_validate(info, par->hook_mask)) {
>  		errmsg = "input interface limitation not valid in POSTROUTING and OUTPUT";
>  		goto err;
>  	}
> @@ -212,6 +225,7 @@ static struct xt_match addrtype_mt_reg[] __read_mostly = {
>  		.revision	= 1,
>  		.match		= addrtype_mt_v1,
>  		.checkentry	= addrtype_mt_checkentry_v1,
> +		NFT_COMPAT_VALIDATE(addrtype_mt_validate)
>  		.matchsize	= sizeof(struct xt_addrtype_info_v1),
>  		.me		= THIS_MODULE
>  	},
> @@ -222,6 +236,7 @@ static struct xt_match addrtype_mt_reg[] __read_mostly = {
>  		.revision	= 1,
>  		.match		= addrtype_mt_v1,
>  		.checkentry	= addrtype_mt_checkentry_v1,
> +		NFT_COMPAT_VALIDATE(addrtype_mt_validate)
>  		.matchsize	= sizeof(struct xt_addrtype_info_v1),
>  		.me		= THIS_MODULE
>  	},
> diff --git a/net/netfilter/xt_devgroup.c b/net/netfilter/xt_devgroup.c
> index 9520dd00070b..d6ee83554845 100644
> --- a/net/netfilter/xt_devgroup.c
> +++ b/net/netfilter/xt_devgroup.c
> @@ -33,6 +33,25 @@ static bool devgroup_mt(const struct sk_buff *skb, struct xt_action_param *par)
>  	return true;
>  }
>  
> +static bool devgroup_mt_validate(const void *matchinfo, unsigned int hook_mask)
> +{
> +	const struct xt_devgroup_info *info = matchinfo;
> +
> +	if (info->flags & XT_DEVGROUP_MATCH_SRC &&
> +	    hook_mask & ~((1 << NF_INET_PRE_ROUTING) |
> +			  (1 << NF_INET_LOCAL_IN) |
> +			  (1 << NF_INET_FORWARD)))
> +		return false;
> +
> +	if (info->flags & XT_DEVGROUP_MATCH_DST &&
> +	    hook_mask & ~((1 << NF_INET_FORWARD) |
> +			  (1 << NF_INET_LOCAL_OUT) |
> +			  (1 << NF_INET_POST_ROUTING)))
> +		return false;
> +
> +	return true;
> +}
> +
>  static int devgroup_mt_checkentry(const struct xt_mtchk_param *par)
>  {
>  	const struct xt_devgroup_info *info = par->matchinfo;
> @@ -41,16 +60,7 @@ static int devgroup_mt_checkentry(const struct xt_mtchk_param *par)
>  			    XT_DEVGROUP_MATCH_DST | XT_DEVGROUP_INVERT_DST))
>  		return -EINVAL;
>  
> -	if (info->flags & XT_DEVGROUP_MATCH_SRC &&
> -	    par->hook_mask & ~((1 << NF_INET_PRE_ROUTING) |
> -			       (1 << NF_INET_LOCAL_IN) |
> -			       (1 << NF_INET_FORWARD)))
> -		return -EINVAL;
> -
> -	if (info->flags & XT_DEVGROUP_MATCH_DST &&
> -	    par->hook_mask & ~((1 << NF_INET_FORWARD) |
> -			       (1 << NF_INET_LOCAL_OUT) |
> -			       (1 << NF_INET_POST_ROUTING)))
> +	if (!devgroup_mt_validate(info, par->hook_mask))
>  		return -EINVAL;
>  
>  	return 0;
> @@ -60,6 +70,7 @@ static struct xt_match devgroup_mt_reg __read_mostly = {
>  	.name		= "devgroup",
>  	.match		= devgroup_mt,
>  	.checkentry	= devgroup_mt_checkentry,
> +	NFT_COMPAT_VALIDATE(devgroup_mt_validate)
>  	.matchsize	= sizeof(struct xt_devgroup_info),
>  	.family		= NFPROTO_UNSPEC,
>  	.me		= THIS_MODULE
> diff --git a/net/netfilter/xt_physdev.c b/net/netfilter/xt_physdev.c
> index 343e65f377d4..27dcd7fc7427 100644
> --- a/net/netfilter/xt_physdev.c
> +++ b/net/netfilter/xt_physdev.c
> @@ -91,6 +91,20 @@ physdev_mt(const struct sk_buff *skb, struct xt_action_param *par)
>  	return (!!ret ^ !(info->invert & XT_PHYSDEV_OP_OUT));
>  }
>  
> +static bool physdev_mt_validate(const void *matchinfo, unsigned int hook_mask)
> +{
> +	const struct xt_physdev_info *info = matchinfo;
> +
> +	if (info->bitmask & (XT_PHYSDEV_OP_OUT | XT_PHYSDEV_OP_ISOUT) &&
> +	    (!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) ||
> +	     info->invert & XT_PHYSDEV_OP_BRIDGED) &&
> +	     hook_mask & (1 << NF_INET_LOCAL_OUT)) {
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
>  static int physdev_mt_check(const struct xt_mtchk_param *par)
>  {
>  	const struct xt_physdev_info *info = par->matchinfo;
> @@ -99,10 +113,8 @@ static int physdev_mt_check(const struct xt_mtchk_param *par)
>  	if (!(info->bitmask & XT_PHYSDEV_OP_MASK) ||
>  	    info->bitmask & ~XT_PHYSDEV_OP_MASK)
>  		return -EINVAL;
> -	if (info->bitmask & (XT_PHYSDEV_OP_OUT | XT_PHYSDEV_OP_ISOUT) &&
> -	    (!(info->bitmask & XT_PHYSDEV_OP_BRIDGED) ||
> -	     info->invert & XT_PHYSDEV_OP_BRIDGED) &&
> -	    par->hook_mask & (1 << NF_INET_LOCAL_OUT)) {
> +
> +	if (!physdev_mt_validate(info, par->hook_mask)) {
>  		pr_info_ratelimited("--physdev-out and --physdev-is-out only supported in the FORWARD and POSTROUTING chains with bridged traffic\n");
>  		return -EINVAL;
>  	}
> @@ -120,6 +132,7 @@ static struct xt_match physdev_mt_reg __read_mostly = {
>  	.revision   = 0,
>  	.family     = NFPROTO_UNSPEC,
>  	.checkentry = physdev_mt_check,
> +	NFT_COMPAT_VALIDATE(physdev_mt_validate)
>  	.match      = physdev_mt,
>  	.matchsize  = sizeof(struct xt_physdev_info),
>  	.me         = THIS_MODULE,
> diff --git a/net/netfilter/xt_policy.c b/net/netfilter/xt_policy.c
> index cb6e8279010a..826727ca843c 100644
> --- a/net/netfilter/xt_policy.c
> +++ b/net/netfilter/xt_policy.c
> @@ -126,6 +126,20 @@ policy_mt(const struct sk_buff *skb, struct xt_action_param *par)
>  	return ret;
>  }
>  
> +static bool policy_mt_validate(const void *matchinfo, unsigned int hook_mask)
> +{
> +	const struct xt_policy_info *info = matchinfo;
> +
> +	if (hook_mask & ((1 << NF_INET_PRE_ROUTING) |
> +	    (1 << NF_INET_LOCAL_IN)) && info->flags & XT_POLICY_MATCH_OUT)
> +		return false;
> +	if (hook_mask & ((1 << NF_INET_POST_ROUTING) |
> +	    (1 << NF_INET_LOCAL_OUT)) && info->flags & XT_POLICY_MATCH_IN)
> +		return false;
> +
> +	return true;
> +}
> +
>  static int policy_mt_check(const struct xt_mtchk_param *par)
>  {
>  	const struct xt_policy_info *info = par->matchinfo;
> @@ -134,13 +148,13 @@ static int policy_mt_check(const struct xt_mtchk_param *par)
>  	if (!(info->flags & (XT_POLICY_MATCH_IN|XT_POLICY_MATCH_OUT)))
>  		goto err;
>  
> -	if (par->hook_mask & ((1 << NF_INET_PRE_ROUTING) |
> -	    (1 << NF_INET_LOCAL_IN)) && info->flags & XT_POLICY_MATCH_OUT) {
> +	if ((info->flags & XT_POLICY_MATCH_OUT) &&
> +	    !policy_mt_validate(info, par->hook_mask)) {
>  		errmsg = "output policy not valid in PREROUTING and INPUT";
>  		goto err;
>  	}
> -	if (par->hook_mask & ((1 << NF_INET_POST_ROUTING) |
> -	    (1 << NF_INET_LOCAL_OUT)) && info->flags & XT_POLICY_MATCH_IN) {
> +	if ((info->flags & XT_POLICY_MATCH_IN) &&
> +	    !policy_mt_validate(info, par->hook_mask)) {
>  		errmsg = "input policy not valid in POSTROUTING and OUTPUT";
>  		goto err;
>  	}
> @@ -159,6 +173,7 @@ static struct xt_match policy_mt_reg[] __read_mostly = {
>  		.name		= "policy",
>  		.family		= NFPROTO_IPV4,
>  		.checkentry 	= policy_mt_check,
> +		NFT_COMPAT_VALIDATE(policy_mt_validate)
>  		.match		= policy_mt,
>  		.matchsize	= sizeof(struct xt_policy_info),
>  		.me		= THIS_MODULE,
> @@ -167,6 +182,7 @@ static struct xt_match policy_mt_reg[] __read_mostly = {
>  		.name		= "policy",
>  		.family		= NFPROTO_IPV6,
>  		.checkentry	= policy_mt_check,
> +		NFT_COMPAT_VALIDATE(policy_mt_validate)
>  		.match		= policy_mt,
>  		.matchsize	= sizeof(struct xt_policy_info),
>  		.me		= THIS_MODULE,
> diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c
> index 731bc2cafae4..a4ff80fd0223 100644
> --- a/net/netfilter/xt_set.c
> +++ b/net/netfilter/xt_set.c
> @@ -430,6 +430,20 @@ set_target_v3(struct sk_buff *skb, const struct xt_action_param *par)
>  	return XT_CONTINUE;
>  }
>  
> +static bool set_target_v3_validate(const void *targinfo, unsigned int hook_mask)
> +{
> +	const struct xt_set_info_target_v3 *info = targinfo;
> +
> +	if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
> +	     (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
> +	     (hook_mask & ~(1 << NF_INET_FORWARD |
> +			    1 << NF_INET_LOCAL_OUT |
> +			    1 << NF_INET_POST_ROUTING)))
> +		return false;
> +
> +	return true;
> +}
> +
>  static int
>  set_target_v3_checkentry(const struct xt_tgchk_param *par)
>  {
> @@ -464,11 +478,7 @@ set_target_v3_checkentry(const struct xt_tgchk_param *par)
>  			ret = -EINVAL;
>  			goto cleanup_del;
>  		}
> -		if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
> -		     (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
> -		     (par->hook_mask & ~(1 << NF_INET_FORWARD |
> -					 1 << NF_INET_LOCAL_OUT |
> -					 1 << NF_INET_POST_ROUTING))) {
> +		if (!set_target_v3_validate(info, par->hook_mask)) {
>  			pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
>  			ret = -EINVAL;
>  			goto cleanup_del;
> @@ -673,6 +683,7 @@ static struct xt_target set_targets[] __read_mostly = {
>  		.target		= set_target_v3,
>  		.targetsize	= sizeof(struct xt_set_info_target_v3),
>  		.checkentry	= set_target_v3_checkentry,
> +		NFT_COMPAT_VALIDATE(set_target_v3_validate)
>  		.destroy	= set_target_v3_destroy,
>  		.me		= THIS_MODULE
>  	},
> @@ -683,6 +694,7 @@ static struct xt_target set_targets[] __read_mostly = {
>  		.target		= set_target_v3,
>  		.targetsize	= sizeof(struct xt_set_info_target_v3),
>  		.checkentry	= set_target_v3_checkentry,
> +		NFT_COMPAT_VALIDATE(set_target_v3_validate)
>  		.destroy	= set_target_v3_destroy,
>  		.me		= THIS_MODULE
>  	},
> -- 
> 2.53.0
> 
> 

  reply	other threads:[~2026-04-19 10:50 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-19 10:45 [PATCH nf] netfilter: x_tables: add late validate callback for nft_compat sake Florian Westphal
2026-04-19 10:50 ` Pablo Neira Ayuso [this message]
2026-04-19 10:59   ` Florian Westphal
2026-04-19 11:06     ` Pablo Neira Ayuso
2026-04-19 11:19       ` Florian Westphal

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=aeSzcx9YmM3usuez@chamomile \
    --to=pablo@netfilter.org \
    --cc=bestswngs@gmail.com \
    --cc=fw@strlen.de \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=xmei5@asu.edu \
    /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