From 6e6f2b9b3375cc0e6b8567d31ae7d3b2d910582f Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Tue, 3 Mar 2026 18:15:25 +0800 Subject: [PATCH 01/12] netfilter: use function typedefs for __rcu NAT helper hook pointers After commit 07919126ecfc ("netfilter: annotate NAT helper hook pointers with __rcu"), sparse can warn about type/address-space mismatches when RCU-dereferencing NAT helper hook function pointers. The hooks are __rcu-annotated and accessed via rcu_dereference(), but the combination of complex function pointer declarators and the WRITE_ONCE() machinery used by RCU_INIT_POINTER()/rcu_assign_pointer() can confuse sparse and trigger false positives. Introduce typedefs for the NAT helper function types, so __rcu applies to a simple "fn_t __rcu *" pointer form. Also replace local typeof(hook) variables with "fn_t *" to avoid propagating __rcu address space into temporaries. No functional change intended. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202603022359.3dGE9fwI-lkp@intel.com/ Signed-off-by: Sun Jian Signed-off-by: Florian Westphal --- include/linux/netfilter/nf_conntrack_amanda.h | 15 +++++++++------ include/linux/netfilter/nf_conntrack_ftp.h | 17 ++++++++++------- include/linux/netfilter/nf_conntrack_irc.h | 15 +++++++++------ include/linux/netfilter/nf_conntrack_snmp.h | 11 +++++++---- include/linux/netfilter/nf_conntrack_tftp.h | 9 ++++++--- net/netfilter/nf_conntrack_amanda.c | 10 ++-------- net/netfilter/nf_conntrack_ftp.c | 10 ++-------- net/netfilter/nf_conntrack_irc.c | 10 ++-------- net/netfilter/nf_conntrack_snmp.c | 7 ++----- net/netfilter/nf_conntrack_tftp.c | 7 ++----- 10 files changed, 51 insertions(+), 60 deletions(-) diff --git a/include/linux/netfilter/nf_conntrack_amanda.h b/include/linux/netfilter/nf_conntrack_amanda.h index dfe89f38d1f7..1719987e8fd8 100644 --- a/include/linux/netfilter/nf_conntrack_amanda.h +++ b/include/linux/netfilter/nf_conntrack_amanda.h @@ -7,10 +7,13 @@ #include #include -extern unsigned int (__rcu *nf_nat_amanda_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int matchoff, - unsigned int matchlen, - struct nf_conntrack_expect *exp); +typedef unsigned int +nf_nat_amanda_hook_fn(struct sk_buff *skb, + enum ip_conntrack_info ctinfo, + unsigned int protoff, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack_expect *exp); + +extern nf_nat_amanda_hook_fn __rcu *nf_nat_amanda_hook; #endif /* _NF_CONNTRACK_AMANDA_H */ diff --git a/include/linux/netfilter/nf_conntrack_ftp.h b/include/linux/netfilter/nf_conntrack_ftp.h index f31292642035..7b62446ccec4 100644 --- a/include/linux/netfilter/nf_conntrack_ftp.h +++ b/include/linux/netfilter/nf_conntrack_ftp.h @@ -26,11 +26,14 @@ struct nf_ct_ftp_master { /* For NAT to hook in when we find a packet which describes what other * connection we should expect. */ -extern unsigned int (__rcu *nf_nat_ftp_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - enum nf_ct_ftp_type type, - unsigned int protoff, - unsigned int matchoff, - unsigned int matchlen, - struct nf_conntrack_expect *exp); +typedef unsigned int +nf_nat_ftp_hook_fn(struct sk_buff *skb, + enum ip_conntrack_info ctinfo, + enum nf_ct_ftp_type type, + unsigned int protoff, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack_expect *exp); + +extern nf_nat_ftp_hook_fn __rcu *nf_nat_ftp_hook; #endif /* _NF_CONNTRACK_FTP_H */ diff --git a/include/linux/netfilter/nf_conntrack_irc.h b/include/linux/netfilter/nf_conntrack_irc.h index 4f3ca5621998..ce07250afb4e 100644 --- a/include/linux/netfilter/nf_conntrack_irc.h +++ b/include/linux/netfilter/nf_conntrack_irc.h @@ -8,11 +8,14 @@ #define IRC_PORT 6667 -extern unsigned int (__rcu *nf_nat_irc_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int matchoff, - unsigned int matchlen, - struct nf_conntrack_expect *exp); +typedef unsigned int +nf_nat_irc_hook_fn(struct sk_buff *skb, + enum ip_conntrack_info ctinfo, + unsigned int protoff, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack_expect *exp); + +extern nf_nat_irc_hook_fn __rcu *nf_nat_irc_hook; #endif /* _NF_CONNTRACK_IRC_H */ diff --git a/include/linux/netfilter/nf_conntrack_snmp.h b/include/linux/netfilter/nf_conntrack_snmp.h index 99107e4f5234..bb39f04a9977 100644 --- a/include/linux/netfilter/nf_conntrack_snmp.h +++ b/include/linux/netfilter/nf_conntrack_snmp.h @@ -5,9 +5,12 @@ #include #include -extern int (__rcu *nf_nat_snmp_hook)(struct sk_buff *skb, - unsigned int protoff, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo); +typedef int +nf_nat_snmp_hook_fn(struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo); + +extern nf_nat_snmp_hook_fn __rcu *nf_nat_snmp_hook; #endif /* _NF_CONNTRACK_SNMP_H */ diff --git a/include/linux/netfilter/nf_conntrack_tftp.h b/include/linux/netfilter/nf_conntrack_tftp.h index 1490b68dd7d1..90b334bbce3c 100644 --- a/include/linux/netfilter/nf_conntrack_tftp.h +++ b/include/linux/netfilter/nf_conntrack_tftp.h @@ -19,8 +19,11 @@ struct tftphdr { #define TFTP_OPCODE_ACK 4 #define TFTP_OPCODE_ERROR 5 -extern unsigned int (__rcu *nf_nat_tftp_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - struct nf_conntrack_expect *exp); +typedef unsigned int +nf_nat_tftp_hook_fn(struct sk_buff *skb, + enum ip_conntrack_info ctinfo, + struct nf_conntrack_expect *exp); + +extern nf_nat_tftp_hook_fn __rcu *nf_nat_tftp_hook; #endif /* _NF_CONNTRACK_TFTP_H */ diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index c0132559f6af..d2c09e8dd872 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -37,13 +37,7 @@ MODULE_PARM_DESC(master_timeout, "timeout for the master connection"); module_param(ts_algo, charp, 0400); MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)"); -unsigned int (__rcu *nf_nat_amanda_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int matchoff, - unsigned int matchlen, - struct nf_conntrack_expect *exp) - __read_mostly; +nf_nat_amanda_hook_fn __rcu *nf_nat_amanda_hook __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_amanda_hook); enum amanda_strings { @@ -98,7 +92,7 @@ static int amanda_help(struct sk_buff *skb, u_int16_t len; __be16 port; int ret = NF_ACCEPT; - typeof(nf_nat_amanda_hook) nf_nat_amanda; + nf_nat_amanda_hook_fn *nf_nat_amanda; /* Only look at packets from the Amanda server */ if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 5e00f9123c38..de83bf9e6c61 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -43,13 +43,7 @@ module_param_array(ports, ushort, &ports_c, 0400); static bool loose; module_param(loose, bool, 0600); -unsigned int (__rcu *nf_nat_ftp_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - enum nf_ct_ftp_type type, - unsigned int protoff, - unsigned int matchoff, - unsigned int matchlen, - struct nf_conntrack_expect *exp); +nf_nat_ftp_hook_fn __rcu *nf_nat_ftp_hook; EXPORT_SYMBOL_GPL(nf_nat_ftp_hook); static int try_rfc959(const char *, size_t, struct nf_conntrack_man *, @@ -385,7 +379,7 @@ static int help(struct sk_buff *skb, struct nf_conntrack_man cmd = {}; unsigned int i; int found = 0, ends_in_nl; - typeof(nf_nat_ftp_hook) nf_nat_ftp; + nf_nat_ftp_hook_fn *nf_nat_ftp; /* Until there's been traffic both ways, don't look in packets. */ if (ctinfo != IP_CT_ESTABLISHED && diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index b8e6d724acd1..522183b9a604 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -30,13 +30,7 @@ static unsigned int dcc_timeout __read_mostly = 300; static char *irc_buffer; static DEFINE_SPINLOCK(irc_buffer_lock); -unsigned int (__rcu *nf_nat_irc_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int matchoff, - unsigned int matchlen, - struct nf_conntrack_expect *exp) - __read_mostly; +nf_nat_irc_hook_fn __rcu *nf_nat_irc_hook __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_irc_hook); #define HELPER_NAME "irc" @@ -122,7 +116,7 @@ static int help(struct sk_buff *skb, unsigned int protoff, __be16 port; int i, ret = NF_ACCEPT; char *addr_beg_p, *addr_end_p; - typeof(nf_nat_irc_hook) nf_nat_irc; + nf_nat_irc_hook_fn *nf_nat_irc; unsigned int datalen; /* If packet is coming from IRC server */ diff --git a/net/netfilter/nf_conntrack_snmp.c b/net/netfilter/nf_conntrack_snmp.c index 387dd6e58f88..7b7eed43c54f 100644 --- a/net/netfilter/nf_conntrack_snmp.c +++ b/net/netfilter/nf_conntrack_snmp.c @@ -25,17 +25,14 @@ static unsigned int timeout __read_mostly = 30; module_param(timeout, uint, 0400); MODULE_PARM_DESC(timeout, "timeout for master connection/replies in seconds"); -int (__rcu *nf_nat_snmp_hook)(struct sk_buff *skb, - unsigned int protoff, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo); +nf_nat_snmp_hook_fn __rcu *nf_nat_snmp_hook; EXPORT_SYMBOL_GPL(nf_nat_snmp_hook); static int snmp_conntrack_help(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo) { - typeof(nf_nat_snmp_hook) nf_nat_snmp; + nf_nat_snmp_hook_fn *nf_nat_snmp; nf_conntrack_broadcast_help(skb, ct, ctinfo, timeout); diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c index 89e9914e5d03..a2e6833a0bf7 100644 --- a/net/netfilter/nf_conntrack_tftp.c +++ b/net/netfilter/nf_conntrack_tftp.c @@ -32,10 +32,7 @@ static unsigned int ports_c; module_param_array(ports, ushort, &ports_c, 0400); MODULE_PARM_DESC(ports, "Port numbers of TFTP servers"); -unsigned int (__rcu *nf_nat_tftp_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - struct nf_conntrack_expect *exp) - __read_mostly; +nf_nat_tftp_hook_fn __rcu *nf_nat_tftp_hook __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_tftp_hook); static int tftp_help(struct sk_buff *skb, @@ -48,7 +45,7 @@ static int tftp_help(struct sk_buff *skb, struct nf_conntrack_expect *exp; struct nf_conntrack_tuple *tuple; unsigned int ret = NF_ACCEPT; - typeof(nf_nat_tftp_hook) nf_nat_tftp; + nf_nat_tftp_hook_fn *nf_nat_tftp; tfh = skb_header_pointer(skb, protoff + sizeof(struct udphdr), sizeof(_tftph), &_tftph); From 1f290c497cb644dd3b52e69b2eaa24a5ffb66094 Mon Sep 17 00:00:00 2001 From: Jelle van der Waa Date: Mon, 9 Mar 2026 21:29:33 +0100 Subject: [PATCH 02/12] netfilter: nf_tables: Fix typo in enum description Fix the spelling of "options". Signed-off-by: Jelle van der Waa Signed-off-by: Florian Westphal --- include/uapi/linux/netfilter/nf_tables.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 45c71f7d21c2..dca9e72b0558 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -884,7 +884,7 @@ enum nft_exthdr_flags { * @NFT_EXTHDR_OP_TCPOPT: match against tcp options * @NFT_EXTHDR_OP_IPV4: match against ipv4 options * @NFT_EXTHDR_OP_SCTP: match against sctp chunks - * @NFT_EXTHDR_OP_DCCP: match against dccp otions + * @NFT_EXTHDR_OP_DCCP: match against dccp options */ enum nft_exthdr_op { NFT_EXTHDR_OP_IPV6, From 7970d6aaf710db166de98c5356a260089896fae5 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 10 Mar 2026 16:05:51 +0100 Subject: [PATCH 03/12] netfilter: nf_conntrack_sip: remove net variable shadowing net is already set, derived from nf_conn. I don't see how the device could be living in a different netns than the conntrack entry. Remove the extra variable and re-use existing one. Signed-off-by: Florian Westphal --- net/netfilter/nf_conntrack_sip.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 939502ff7c87..182cfb119448 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -869,9 +869,8 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, saddr = &ct->tuplehash[!dir].tuple.src.u3; } else if (sip_external_media) { struct net_device *dev = skb_dst(skb)->dev; - struct net *net = dev_net(dev); - struct flowi fl; struct dst_entry *dst = NULL; + struct flowi fl; memset(&fl, 0, sizeof(fl)); From 606bd17ef0de1b8f4227cb070308ddfd702979dc Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 11 Mar 2026 10:53:15 +0100 Subject: [PATCH 04/12] netfilter: add deprecation warning for dccp support Add a deprecation warning for the xt_dccp match and the nft exthdr code. Signed-off-by: Florian Westphal --- net/netfilter/nft_exthdr.c | 3 +++ net/netfilter/xt_dccp.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index 5f01269a49bd..14d4ad7f518c 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -796,6 +796,9 @@ nft_exthdr_select_ops(const struct nft_ctx *ctx, break; #ifdef CONFIG_NFT_EXTHDR_DCCP case NFT_EXTHDR_OP_DCCP: + pr_warn_once("The dccp option matching is deprecated and scheduled to be removed in 2027.\n" + "Please contact the netfilter-devel mailing list or update your nftables rules.\n"); + if (tb[NFTA_EXTHDR_DREG]) return &nft_exthdr_dccp_ops; break; diff --git a/net/netfilter/xt_dccp.c b/net/netfilter/xt_dccp.c index 037ab93e25d0..3db81e041af9 100644 --- a/net/netfilter/xt_dccp.c +++ b/net/netfilter/xt_dccp.c @@ -159,6 +159,9 @@ static int __init dccp_mt_init(void) { int ret; + pr_warn_once("The DCCP match is deprecated and scheduled to be removed in 2027.\n" + "Please contact the netfilter-devel mailing list or update your iptables rules\n"); + /* doff is 8 bits, so the maximum option size is (4*256). Don't put * this in BSS since DaveM is worried about locked TLB's for kernel * BSS. */ From 390a57dd61af837fcf5ad0681267890bd6cdd594 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 12 Mar 2026 18:53:41 +0100 Subject: [PATCH 05/12] netfilter: nf_conntrack_h323: remove unreliable debug code in decode_octstr The debug code (not enabled in any build) reads up to 6 octets of the inpt buffer, but does so without bound checks. Zap this. Signed-off-by: Florian Westphal --- net/netfilter/nf_conntrack_h323_asn1.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/net/netfilter/nf_conntrack_h323_asn1.c b/net/netfilter/nf_conntrack_h323_asn1.c index 7b1497ed97d2..09e0f724644f 100644 --- a/net/netfilter/nf_conntrack_h323_asn1.c +++ b/net/netfilter/nf_conntrack_h323_asn1.c @@ -21,7 +21,6 @@ #if H323_TRACE #define TAB_SIZE 4 -#define IFTHEN(cond, act) if(cond){act;} #ifdef __KERNEL__ #define PRINT printk #else @@ -29,7 +28,6 @@ #endif #define FNAME(name) name, #else -#define IFTHEN(cond, act) #define PRINT(fmt, args...) #define FNAME(name) #endif @@ -445,11 +443,6 @@ static int decode_octstr(struct bitstr *bs, const struct field_t *f, BYTE_ALIGN(bs); if (base && (f->attr & DECODE)) { /* The IP Address */ - IFTHEN(f->lb == 4, - PRINT(" = %d.%d.%d.%d:%d", - bs->cur[0], bs->cur[1], - bs->cur[2], bs->cur[3], - bs->cur[4] * 256 + bs->cur[5])); *((unsigned int *)(base + f->offset)) = bs->cur - bs->buf; } From 66b75e6bbeeb489156a74534561bbbd360843a73 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 10 Mar 2026 00:26:46 +0100 Subject: [PATCH 06/12] netfilter: add more netlink-based policy range checks These spots either already check the attribute range manually before use or the consuming functions tolerate unexpected values. Nevertheless, add more range checks via netlink policy so we gain more users and avoid possible re-use in other places that might not have the required manual checks. This also improves error reporting: netlink core can generate extack errors. Signed-off-by: Florian Westphal --- net/netfilter/ipset/ip_set_core.c | 2 +- net/netfilter/nf_tables_api.c | 20 +++++++++++++++----- net/netfilter/nfnetlink_acct.c | 2 +- net/netfilter/nfnetlink_cthelper.c | 2 +- net/netfilter/nfnetlink_hook.c | 2 +- net/netfilter/nfnetlink_log.c | 4 +++- net/netfilter/nfnetlink_osf.c | 2 +- net/netfilter/nfnetlink_queue.c | 2 +- net/netfilter/nft_compat.c | 2 +- net/netfilter/nft_connlimit.c | 2 +- net/netfilter/nft_ct.c | 2 +- net/netfilter/nft_dynset.c | 3 ++- net/netfilter/nft_exthdr.c | 2 +- net/netfilter/nft_inner.c | 2 +- net/netfilter/nft_limit.c | 2 +- net/netfilter/nft_log.c | 2 +- net/netfilter/nft_osf.c | 2 +- net/netfilter/nft_payload.c | 2 +- net/netfilter/nft_queue.c | 2 +- net/netfilter/nft_quota.c | 2 +- net/netfilter/nft_synproxy.c | 4 ++-- net/netfilter/nft_tunnel.c | 4 ++-- net/netfilter/nft_xfrm.c | 4 ++-- 23 files changed, 43 insertions(+), 30 deletions(-) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index d0c9fe59c67d..c5a26236a0bb 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -985,7 +985,7 @@ static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = { .len = IPSET_MAXNAMELEN - 1 }, [IPSET_ATTR_TYPENAME] = { .type = NLA_NUL_STRING, .len = IPSET_MAXNAMELEN - 1}, - [IPSET_ATTR_REVISION] = { .type = NLA_U8 }, + [IPSET_ATTR_REVISION] = NLA_POLICY_MAX(NLA_U8, IPSET_REVISION_MAX), [IPSET_ATTR_FAMILY] = { .type = NLA_U8 }, [IPSET_ATTR_DATA] = { .type = NLA_NESTED }, }; diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 8ed8d5384b97..8537b94653d3 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1112,7 +1112,7 @@ static __be16 nft_base_seq_be16(const struct net *net) static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { [NFTA_TABLE_NAME] = { .type = NLA_STRING, .len = NFT_TABLE_MAXNAMELEN - 1 }, - [NFTA_TABLE_FLAGS] = { .type = NLA_U32 }, + [NFTA_TABLE_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_TABLE_F_MASK), [NFTA_TABLE_HANDLE] = { .type = NLA_U64 }, [NFTA_TABLE_USERDATA] = { .type = NLA_BINARY, .len = NFT_USERDATA_MAXLEN } @@ -1878,7 +1878,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = { [NFTA_CHAIN_TYPE] = { .type = NLA_STRING, .len = NFT_MODULE_AUTOLOAD_LIMIT }, [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED }, - [NFTA_CHAIN_FLAGS] = { .type = NLA_U32 }, + [NFTA_CHAIN_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_CHAIN_FLAGS), [NFTA_CHAIN_ID] = { .type = NLA_U32 }, [NFTA_CHAIN_USERDATA] = { .type = NLA_BINARY, .len = NFT_USERDATA_MAXLEN }, @@ -4597,7 +4597,16 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { .len = NFT_TABLE_MAXNAMELEN - 1 }, [NFTA_SET_NAME] = { .type = NLA_STRING, .len = NFT_SET_MAXNAMELEN - 1 }, - [NFTA_SET_FLAGS] = { .type = NLA_U32 }, + [NFTA_SET_FLAGS] = NLA_POLICY_MASK(NLA_BE32, + NFT_SET_ANONYMOUS | + NFT_SET_CONSTANT | + NFT_SET_INTERVAL | + NFT_SET_MAP | + NFT_SET_TIMEOUT | + NFT_SET_EVAL | + NFT_SET_OBJECT | + NFT_SET_CONCAT | + NFT_SET_EXPR), [NFTA_SET_KEY_TYPE] = { .type = NLA_U32 }, [NFTA_SET_KEY_LEN] = { .type = NLA_U32 }, [NFTA_SET_DATA_TYPE] = { .type = NLA_U32 }, @@ -5929,7 +5938,8 @@ const struct nft_set_ext_type nft_set_ext_types[] = { static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = { [NFTA_SET_ELEM_KEY] = { .type = NLA_NESTED }, [NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED }, - [NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 }, + [NFTA_SET_ELEM_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_SET_ELEM_INTERVAL_END | + NFT_SET_ELEM_CATCHALL), [NFTA_SET_ELEM_TIMEOUT] = { .type = NLA_U64 }, [NFTA_SET_ELEM_EXPIRATION] = { .type = NLA_U64 }, [NFTA_SET_ELEM_USERDATA] = { .type = NLA_BINARY, @@ -8649,7 +8659,7 @@ static const struct nla_policy nft_flowtable_policy[NFTA_FLOWTABLE_MAX + 1] = { .len = NFT_NAME_MAXLEN - 1 }, [NFTA_FLOWTABLE_HOOK] = { .type = NLA_NESTED }, [NFTA_FLOWTABLE_HANDLE] = { .type = NLA_U64 }, - [NFTA_FLOWTABLE_FLAGS] = { .type = NLA_U32 }, + [NFTA_FLOWTABLE_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_FLOWTABLE_MASK), }; struct nft_flowtable *nft_flowtable_lookup(const struct net *net, diff --git a/net/netfilter/nfnetlink_acct.c b/net/netfilter/nfnetlink_acct.c index 2bfaa773d82f..8ff1e0ad5cb0 100644 --- a/net/netfilter/nfnetlink_acct.c +++ b/net/netfilter/nfnetlink_acct.c @@ -373,7 +373,7 @@ static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, [NFACCT_BYTES] = { .type = NLA_U64 }, [NFACCT_PKTS] = { .type = NLA_U64 }, - [NFACCT_FLAGS] = { .type = NLA_U32 }, + [NFACCT_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFACCT_F_QUOTA), [NFACCT_QUOTA] = { .type = NLA_U64 }, [NFACCT_FILTER] = {.type = NLA_NESTED }, }; diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index d545fa459455..0d16ad82d70c 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -165,7 +165,7 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy, static const struct nla_policy nfnl_cthelper_expect_policy_set[NFCTH_POLICY_SET_MAX+1] = { - [NFCTH_POLICY_SET_NUM] = { .type = NLA_U32, }, + [NFCTH_POLICY_SET_NUM] = NLA_POLICY_MAX(NLA_BE32, NF_CT_MAX_EXPECT_CLASSES), }; static int diff --git a/net/netfilter/nfnetlink_hook.c b/net/netfilter/nfnetlink_hook.c index 531706982859..5623c18fcd12 100644 --- a/net/netfilter/nfnetlink_hook.c +++ b/net/netfilter/nfnetlink_hook.c @@ -24,7 +24,7 @@ #include static const struct nla_policy nfnl_hook_nla_policy[NFNLA_HOOK_MAX + 1] = { - [NFNLA_HOOK_HOOKNUM] = { .type = NLA_U32 }, + [NFNLA_HOOK_HOOKNUM] = NLA_POLICY_MAX(NLA_BE32, 255), [NFNLA_HOOK_PRIORITY] = { .type = NLA_U32 }, [NFNLA_HOOK_DEV] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 91aa210b3e53..9497ebeedd55 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -879,7 +879,9 @@ static const struct nla_policy nfula_cfg_policy[NFULA_CFG_MAX+1] = { [NFULA_CFG_TIMEOUT] = { .type = NLA_U32 }, [NFULA_CFG_QTHRESH] = { .type = NLA_U32 }, [NFULA_CFG_NLBUFSIZ] = { .type = NLA_U32 }, - [NFULA_CFG_FLAGS] = { .type = NLA_U16 }, + [NFULA_CFG_FLAGS] = NLA_POLICY_MASK(NLA_BE16, NFULNL_CFG_F_SEQ | + NFULNL_CFG_F_SEQ_GLOBAL | + NFULNL_CFG_F_CONNTRACK), }; static int nfulnl_recv_config(struct sk_buff *skb, const struct nfnl_info *info, diff --git a/net/netfilter/nfnetlink_osf.c b/net/netfilter/nfnetlink_osf.c index 45d9ad231a92..d64ce21c7b55 100644 --- a/net/netfilter/nfnetlink_osf.c +++ b/net/netfilter/nfnetlink_osf.c @@ -293,7 +293,7 @@ bool nf_osf_find(const struct sk_buff *skb, EXPORT_SYMBOL_GPL(nf_osf_find); static const struct nla_policy nfnl_osf_policy[OSF_ATTR_MAX + 1] = { - [OSF_ATTR_FINGER] = { .len = sizeof(struct nf_osf_user_finger) }, + [OSF_ATTR_FINGER] = NLA_POLICY_EXACT_LEN(sizeof(struct nf_osf_user_finger)), }; static int nfnl_osf_add_callback(struct sk_buff *skb, diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 2aa2380d976a..ac0c19233681 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1599,7 +1599,7 @@ static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = { [NFQA_CFG_PARAMS] = { .len = sizeof(struct nfqnl_msg_config_params) }, [NFQA_CFG_QUEUE_MAXLEN] = { .type = NLA_U32 }, [NFQA_CFG_MASK] = { .type = NLA_U32 }, - [NFQA_CFG_FLAGS] = { .type = NLA_U32 }, + [NFQA_CFG_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFQA_CFG_F_MAX - 1), }; static const struct nf_queue_handler nfqh = { diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c index 53a614a0e3cd..decc725a33c2 100644 --- a/net/netfilter/nft_compat.c +++ b/net/netfilter/nft_compat.c @@ -195,7 +195,7 @@ static void target_compat_from_user(struct xt_target *t, void *in, void *out) static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1] = { [NFTA_RULE_COMPAT_PROTO] = { .type = NLA_U32 }, - [NFTA_RULE_COMPAT_FLAGS] = { .type = NLA_U32 }, + [NFTA_RULE_COMPAT_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_RULE_COMPAT_F_MASK), }; static int nft_parse_compat(const struct nlattr *attr, u16 *proto, bool *inv) diff --git a/net/netfilter/nft_connlimit.c b/net/netfilter/nft_connlimit.c index 09ac4f77e389..46b31d78abc6 100644 --- a/net/netfilter/nft_connlimit.c +++ b/net/netfilter/nft_connlimit.c @@ -159,7 +159,7 @@ static int nft_connlimit_obj_dump(struct sk_buff *skb, static const struct nla_policy nft_connlimit_policy[NFTA_CONNLIMIT_MAX + 1] = { [NFTA_CONNLIMIT_COUNT] = { .type = NLA_U32 }, - [NFTA_CONNLIMIT_FLAGS] = { .type = NLA_U32 }, + [NFTA_CONNLIMIT_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_CONNLIMIT_F_INV), }; static struct nft_object_type nft_connlimit_obj_type; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index a8fcb4b6ea1a..00dabd985883 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -338,7 +338,7 @@ static void nft_ct_set_eval(const struct nft_expr *expr, static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { [NFTA_CT_DREG] = { .type = NLA_U32 }, [NFTA_CT_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_CT_DIRECTION] = { .type = NLA_U8 }, + [NFTA_CT_DIRECTION] = NLA_POLICY_MAX(NLA_U8, IP_CT_DIR_REPLY), [NFTA_CT_SREG] = { .type = NLA_U32 }, }; diff --git a/net/netfilter/nft_dynset.c b/net/netfilter/nft_dynset.c index 57bf94ae8724..ee9d3e7b1ecf 100644 --- a/net/netfilter/nft_dynset.c +++ b/net/netfilter/nft_dynset.c @@ -163,7 +163,8 @@ static const struct nla_policy nft_dynset_policy[NFTA_DYNSET_MAX + 1] = { [NFTA_DYNSET_SREG_DATA] = { .type = NLA_U32 }, [NFTA_DYNSET_TIMEOUT] = { .type = NLA_U64 }, [NFTA_DYNSET_EXPR] = { .type = NLA_NESTED }, - [NFTA_DYNSET_FLAGS] = { .type = NLA_U32 }, + [NFTA_DYNSET_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_DYNSET_F_INV | + NFT_DYNSET_F_EXPR), [NFTA_DYNSET_EXPRESSIONS] = { .type = NLA_NESTED }, }; diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index 14d4ad7f518c..b997307d94f9 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -490,7 +490,7 @@ static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = { [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 }, [NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 }, [NFTA_EXTHDR_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_EXTHDR_FLAGS] = { .type = NLA_U32 }, + [NFTA_EXTHDR_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_EXTHDR_F_PRESENT), [NFTA_EXTHDR_OP] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_EXTHDR_SREG] = { .type = NLA_U32 }, }; diff --git a/net/netfilter/nft_inner.c b/net/netfilter/nft_inner.c index c4569d4b9228..03ffb1159fc1 100644 --- a/net/netfilter/nft_inner.c +++ b/net/netfilter/nft_inner.c @@ -321,7 +321,7 @@ static void nft_inner_eval(const struct nft_expr *expr, struct nft_regs *regs, static const struct nla_policy nft_inner_policy[NFTA_INNER_MAX + 1] = { [NFTA_INNER_NUM] = { .type = NLA_U32 }, - [NFTA_INNER_FLAGS] = { .type = NLA_U32 }, + [NFTA_INNER_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_INNER_MASK), [NFTA_INNER_HDRSIZE] = { .type = NLA_U32 }, [NFTA_INNER_TYPE] = { .type = NLA_U32 }, [NFTA_INNER_EXPR] = { .type = NLA_NESTED }, diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c index f6830621c471..167d99b1447f 100644 --- a/net/netfilter/nft_limit.c +++ b/net/netfilter/nft_limit.c @@ -189,7 +189,7 @@ static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = { [NFTA_LIMIT_UNIT] = { .type = NLA_U64 }, [NFTA_LIMIT_BURST] = { .type = NLA_U32 }, [NFTA_LIMIT_TYPE] = { .type = NLA_U32 }, - [NFTA_LIMIT_FLAGS] = { .type = NLA_U32 }, + [NFTA_LIMIT_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_LIMIT_F_INV), }; static int nft_limit_pkts_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c index da0c0d1c9cea..0d868eea6257 100644 --- a/net/netfilter/nft_log.c +++ b/net/netfilter/nft_log.c @@ -69,7 +69,7 @@ static const struct nla_policy nft_log_policy[NFTA_LOG_MAX + 1] = { [NFTA_LOG_SNAPLEN] = { .type = NLA_U32 }, [NFTA_LOG_QTHRESHOLD] = { .type = NLA_U16 }, [NFTA_LOG_LEVEL] = { .type = NLA_U32 }, - [NFTA_LOG_FLAGS] = { .type = NLA_U32 }, + [NFTA_LOG_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NF_LOG_MASK), }; static int nft_log_modprobe(struct net *net, enum nf_log_type t) diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c index 39ccd67ed265..b2f44bc6bd3f 100644 --- a/net/netfilter/nft_osf.c +++ b/net/netfilter/nft_osf.c @@ -14,7 +14,7 @@ struct nft_osf { static const struct nla_policy nft_osf_policy[NFTA_OSF_MAX + 1] = { [NFTA_OSF_DREG] = { .type = NLA_U32 }, [NFTA_OSF_TTL] = { .type = NLA_U8 }, - [NFTA_OSF_FLAGS] = { .type = NLA_U32 }, + [NFTA_OSF_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_OSF_F_VERSION), }; static void nft_osf_eval(const struct nft_expr *expr, struct nft_regs *regs, diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 973d56af03ff..91b62083d942 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -216,7 +216,7 @@ static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = { [NFTA_PAYLOAD_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_PAYLOAD_CSUM_TYPE] = { .type = NLA_U32 }, [NFTA_PAYLOAD_CSUM_OFFSET] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_PAYLOAD_CSUM_FLAGS] = { .type = NLA_U32 }, + [NFTA_PAYLOAD_CSUM_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_PAYLOAD_L4CSUM_PSEUDOHDR), }; static int nft_payload_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_queue.c b/net/netfilter/nft_queue.c index 8eb13a02942e..b83d209db886 100644 --- a/net/netfilter/nft_queue.c +++ b/net/netfilter/nft_queue.c @@ -95,7 +95,7 @@ static int nft_queue_validate(const struct nft_ctx *ctx, static const struct nla_policy nft_queue_policy[NFTA_QUEUE_MAX + 1] = { [NFTA_QUEUE_NUM] = { .type = NLA_U16 }, [NFTA_QUEUE_TOTAL] = { .type = NLA_U16 }, - [NFTA_QUEUE_FLAGS] = { .type = NLA_U16 }, + [NFTA_QUEUE_FLAGS] = NLA_POLICY_MASK(NLA_BE16, NFT_QUEUE_FLAG_MASK), [NFTA_QUEUE_SREG_QNUM] = { .type = NLA_U32 }, }; diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index 3be788e5223c..6ed7c4409706 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -46,7 +46,7 @@ static inline void nft_quota_do_eval(struct nft_quota *priv, static const struct nla_policy nft_quota_policy[NFTA_QUOTA_MAX + 1] = { [NFTA_QUOTA_BYTES] = { .type = NLA_U64 }, - [NFTA_QUOTA_FLAGS] = { .type = NLA_U32 }, + [NFTA_QUOTA_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_QUOTA_F_INV), [NFTA_QUOTA_CONSUMED] = { .type = NLA_U64 }, }; diff --git a/net/netfilter/nft_synproxy.c b/net/netfilter/nft_synproxy.c index 8e452a874969..7641f249614c 100644 --- a/net/netfilter/nft_synproxy.c +++ b/net/netfilter/nft_synproxy.c @@ -17,8 +17,8 @@ struct nft_synproxy { static const struct nla_policy nft_synproxy_policy[NFTA_SYNPROXY_MAX + 1] = { [NFTA_SYNPROXY_MSS] = { .type = NLA_U16 }, - [NFTA_SYNPROXY_WSCALE] = { .type = NLA_U8 }, - [NFTA_SYNPROXY_FLAGS] = { .type = NLA_U32 }, + [NFTA_SYNPROXY_WSCALE] = NLA_POLICY_MAX(NLA_U8, TCP_MAX_WSCALE), + [NFTA_SYNPROXY_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NF_SYNPROXY_OPT_MASK), }; static void nft_synproxy_tcp_options(struct synproxy_options *opts, diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index f5cadba91417..65d06300f48a 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -68,7 +68,7 @@ static void nft_tunnel_get_eval(const struct nft_expr *expr, static const struct nla_policy nft_tunnel_policy[NFTA_TUNNEL_MAX + 1] = { [NFTA_TUNNEL_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_TUNNEL_DREG] = { .type = NLA_U32 }, - [NFTA_TUNNEL_MODE] = NLA_POLICY_MAX(NLA_BE32, 255), + [NFTA_TUNNEL_MODE] = NLA_POLICY_MAX(NLA_BE32, NFT_TUNNEL_MODE_MAX), }; static int nft_tunnel_get_init(const struct nft_ctx *ctx, @@ -408,7 +408,7 @@ static const struct nla_policy nft_tunnel_key_policy[NFTA_TUNNEL_KEY_MAX + 1] = [NFTA_TUNNEL_KEY_IP] = { .type = NLA_NESTED, }, [NFTA_TUNNEL_KEY_IP6] = { .type = NLA_NESTED, }, [NFTA_TUNNEL_KEY_ID] = { .type = NLA_U32, }, - [NFTA_TUNNEL_KEY_FLAGS] = { .type = NLA_U32, }, + [NFTA_TUNNEL_KEY_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_TUNNEL_F_MASK), [NFTA_TUNNEL_KEY_TOS] = { .type = NLA_U8, }, [NFTA_TUNNEL_KEY_TTL] = { .type = NLA_U8, }, [NFTA_TUNNEL_KEY_SPORT] = { .type = NLA_U16, }, diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c index 7ffe6a2690d1..6858cd2d16a4 100644 --- a/net/netfilter/nft_xfrm.c +++ b/net/netfilter/nft_xfrm.c @@ -17,8 +17,8 @@ static const struct nla_policy nft_xfrm_policy[NFTA_XFRM_MAX + 1] = { [NFTA_XFRM_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_XFRM_DIR] = { .type = NLA_U8 }, - [NFTA_XFRM_SPNUM] = NLA_POLICY_MAX(NLA_BE32, 255), + [NFTA_XFRM_DIR] = NLA_POLICY_MAX(NLA_U8, XFRM_POLICY_OUT), + [NFTA_XFRM_SPNUM] = NLA_POLICY_MAX(NLA_BE32, XFRM_MAX_DEPTH - 1), [NFTA_XFRM_DREG] = { .type = NLA_U32 }, }; From 8e57338c3601d0cde806bd7e70c377109106c983 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 13 Mar 2026 13:12:30 +0100 Subject: [PATCH 07/12] netfilter: nf_tables: add netlink policy based cap on registers Should have no effect in practice; all of these use the nft_parse_register_load/store apis which is mandatory anyway due to the need to further validate the register load/store, e.g. that the size argument doesn't result in out-of-bounds load/store. OTOH this is a simple method to reject obviously wrong input at earlier stage. Signed-off-by: Florian Westphal --- include/uapi/linux/netfilter/nf_tables.h | 4 ++++ net/netfilter/nft_bitwise.c | 6 +++--- net/netfilter/nft_byteorder.c | 4 ++-- net/netfilter/nft_cmp.c | 2 +- net/netfilter/nft_ct.c | 4 ++-- net/netfilter/nft_exthdr.c | 4 ++-- net/netfilter/nft_fib.c | 2 +- net/netfilter/nft_hash.c | 4 ++-- net/netfilter/nft_immediate.c | 2 +- net/netfilter/nft_lookup.c | 4 ++-- net/netfilter/nft_meta.c | 4 ++-- net/netfilter/nft_numgen.c | 2 +- net/netfilter/nft_objref.c | 2 +- net/netfilter/nft_osf.c | 2 +- net/netfilter/nft_payload.c | 4 ++-- net/netfilter/nft_range.c | 2 +- net/netfilter/nft_rt.c | 2 +- net/netfilter/nft_socket.c | 2 +- net/netfilter/nft_tunnel.c | 2 +- net/netfilter/nft_xfrm.c | 2 +- 20 files changed, 32 insertions(+), 28 deletions(-) diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index dca9e72b0558..0b708153469c 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -46,6 +46,10 @@ enum nft_registers { }; #define NFT_REG_MAX (__NFT_REG_MAX - 1) +#ifdef __KERNEL__ +#define NFT_REG32_MAX NFT_REG32_15 +#endif + #define NFT_REG_SIZE 16 #define NFT_REG32_SIZE 4 #define NFT_REG32_COUNT (NFT_REG32_15 - NFT_REG32_00 + 1) diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c index a4ff781f334d..13808e9cd999 100644 --- a/net/netfilter/nft_bitwise.c +++ b/net/netfilter/nft_bitwise.c @@ -125,9 +125,9 @@ void nft_bitwise_eval(const struct nft_expr *expr, } static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = { - [NFTA_BITWISE_SREG] = { .type = NLA_U32 }, - [NFTA_BITWISE_SREG2] = { .type = NLA_U32 }, - [NFTA_BITWISE_DREG] = { .type = NLA_U32 }, + [NFTA_BITWISE_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_BITWISE_SREG2] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_BITWISE_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_BITWISE_LEN] = { .type = NLA_U32 }, [NFTA_BITWISE_MASK] = { .type = NLA_NESTED }, [NFTA_BITWISE_XOR] = { .type = NLA_NESTED }, diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c index 744878773dac..e00dddfa2fc0 100644 --- a/net/netfilter/nft_byteorder.c +++ b/net/netfilter/nft_byteorder.c @@ -87,8 +87,8 @@ void nft_byteorder_eval(const struct nft_expr *expr, } static const struct nla_policy nft_byteorder_policy[NFTA_BYTEORDER_MAX + 1] = { - [NFTA_BYTEORDER_SREG] = { .type = NLA_U32 }, - [NFTA_BYTEORDER_DREG] = { .type = NLA_U32 }, + [NFTA_BYTEORDER_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_BYTEORDER_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_BYTEORDER_OP] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_BYTEORDER_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_BYTEORDER_SIZE] = NLA_POLICY_MAX(NLA_BE32, 255), diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index b61dc9c3383e..e085c2a00b70 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c @@ -64,7 +64,7 @@ void nft_cmp_eval(const struct nft_expr *expr, } static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = { - [NFTA_CMP_SREG] = { .type = NLA_U32 }, + [NFTA_CMP_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_CMP_OP] = { .type = NLA_U32 }, [NFTA_CMP_DATA] = { .type = NLA_NESTED }, }; diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 00dabd985883..afa7142c529a 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -336,10 +336,10 @@ static void nft_ct_set_eval(const struct nft_expr *expr, } static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { - [NFTA_CT_DREG] = { .type = NLA_U32 }, + [NFTA_CT_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_CT_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_CT_DIRECTION] = NLA_POLICY_MAX(NLA_U8, IP_CT_DIR_REPLY), - [NFTA_CT_SREG] = { .type = NLA_U32 }, + [NFTA_CT_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), }; #ifdef CONFIG_NF_CONNTRACK_ZONES diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index b997307d94f9..0407d6f708ae 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -486,13 +486,13 @@ static void nft_exthdr_dccp_eval(const struct nft_expr *expr, #endif static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = { - [NFTA_EXTHDR_DREG] = { .type = NLA_U32 }, + [NFTA_EXTHDR_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 }, [NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 }, [NFTA_EXTHDR_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_EXTHDR_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_EXTHDR_F_PRESENT), [NFTA_EXTHDR_OP] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_EXTHDR_SREG] = { .type = NLA_U32 }, + [NFTA_EXTHDR_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), }; static int nft_exthdr_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index f7dc0e54375f..327a5f33659c 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -19,7 +19,7 @@ NFTA_FIB_F_PRESENT) const struct nla_policy nft_fib_policy[NFTA_FIB_MAX + 1] = { - [NFTA_FIB_DREG] = { .type = NLA_U32 }, + [NFTA_FIB_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_FIB_RESULT] = { .type = NLA_U32 }, [NFTA_FIB_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFTA_FIB_F_ALL), diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c index 1cf41e0a0e0c..3bacc9b53789 100644 --- a/net/netfilter/nft_hash.c +++ b/net/netfilter/nft_hash.c @@ -58,8 +58,8 @@ static void nft_symhash_eval(const struct nft_expr *expr, } static const struct nla_policy nft_hash_policy[NFTA_HASH_MAX + 1] = { - [NFTA_HASH_SREG] = { .type = NLA_U32 }, - [NFTA_HASH_DREG] = { .type = NLA_U32 }, + [NFTA_HASH_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_HASH_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_HASH_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_HASH_MODULUS] = { .type = NLA_U32 }, [NFTA_HASH_SEED] = { .type = NLA_U32 }, diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index 37c29947b380..1b733c7b1b0e 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -25,7 +25,7 @@ void nft_immediate_eval(const struct nft_expr *expr, } static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = { - [NFTA_IMMEDIATE_DREG] = { .type = NLA_U32 }, + [NFTA_IMMEDIATE_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_IMMEDIATE_DATA] = { .type = NLA_NESTED }, }; diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c index e4e619027542..9fafe5afc490 100644 --- a/net/netfilter/nft_lookup.c +++ b/net/netfilter/nft_lookup.c @@ -125,8 +125,8 @@ static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = { [NFTA_LOOKUP_SET] = { .type = NLA_STRING, .len = NFT_SET_MAXNAMELEN - 1 }, [NFTA_LOOKUP_SET_ID] = { .type = NLA_U32 }, - [NFTA_LOOKUP_SREG] = { .type = NLA_U32 }, - [NFTA_LOOKUP_DREG] = { .type = NLA_U32 }, + [NFTA_LOOKUP_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_LOOKUP_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_LOOKUP_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_LOOKUP_F_INV), }; diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index d0df6cf374d1..7478063339d4 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -460,9 +460,9 @@ void nft_meta_set_eval(const struct nft_expr *expr, EXPORT_SYMBOL_GPL(nft_meta_set_eval); const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { - [NFTA_META_DREG] = { .type = NLA_U32 }, + [NFTA_META_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_META_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_META_SREG] = { .type = NLA_U32 }, + [NFTA_META_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), }; EXPORT_SYMBOL_GPL(nft_meta_policy); diff --git a/net/netfilter/nft_numgen.c b/net/netfilter/nft_numgen.c index 4d69b3399195..b0c802370159 100644 --- a/net/netfilter/nft_numgen.c +++ b/net/netfilter/nft_numgen.c @@ -43,7 +43,7 @@ static void nft_ng_inc_eval(const struct nft_expr *expr, } static const struct nla_policy nft_ng_policy[NFTA_NG_MAX + 1] = { - [NFTA_NG_DREG] = { .type = NLA_U32 }, + [NFTA_NG_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_NG_MODULUS] = { .type = NLA_U32 }, [NFTA_NG_TYPE] = { .type = NLA_U32 }, [NFTA_NG_OFFSET] = { .type = NLA_U32 }, diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c index 633cce69568f..249ded517446 100644 --- a/net/netfilter/nft_objref.c +++ b/net/netfilter/nft_objref.c @@ -265,7 +265,7 @@ static const struct nla_policy nft_objref_policy[NFTA_OBJREF_MAX + 1] = { [NFTA_OBJREF_IMM_NAME] = { .type = NLA_STRING, .len = NFT_OBJ_MAXNAMELEN - 1 }, [NFTA_OBJREF_IMM_TYPE] = { .type = NLA_U32 }, - [NFTA_OBJREF_SET_SREG] = { .type = NLA_U32 }, + [NFTA_OBJREF_SET_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_OBJREF_SET_NAME] = { .type = NLA_STRING, .len = NFT_SET_MAXNAMELEN - 1 }, [NFTA_OBJREF_SET_ID] = { .type = NLA_U32 }, diff --git a/net/netfilter/nft_osf.c b/net/netfilter/nft_osf.c index b2f44bc6bd3f..18003433476c 100644 --- a/net/netfilter/nft_osf.c +++ b/net/netfilter/nft_osf.c @@ -12,7 +12,7 @@ struct nft_osf { }; static const struct nla_policy nft_osf_policy[NFTA_OSF_MAX + 1] = { - [NFTA_OSF_DREG] = { .type = NLA_U32 }, + [NFTA_OSF_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_OSF_TTL] = { .type = NLA_U8 }, [NFTA_OSF_FLAGS] = NLA_POLICY_MASK(NLA_BE32, NFT_OSF_F_VERSION), }; diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 91b62083d942..3fa3c6c835be 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -209,8 +209,8 @@ void nft_payload_eval(const struct nft_expr *expr, } static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = { - [NFTA_PAYLOAD_SREG] = { .type = NLA_U32 }, - [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 }, + [NFTA_PAYLOAD_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), + [NFTA_PAYLOAD_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 }, [NFTA_PAYLOAD_OFFSET] = { .type = NLA_BE32 }, [NFTA_PAYLOAD_LEN] = NLA_POLICY_MAX(NLA_BE32, 255), diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c index cbb02644b836..f8a1641afccf 100644 --- a/net/netfilter/nft_range.c +++ b/net/netfilter/nft_range.c @@ -41,7 +41,7 @@ void nft_range_eval(const struct nft_expr *expr, } static const struct nla_policy nft_range_policy[NFTA_RANGE_MAX + 1] = { - [NFTA_RANGE_SREG] = { .type = NLA_U32 }, + [NFTA_RANGE_SREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_RANGE_OP] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_RANGE_FROM_DATA] = { .type = NLA_NESTED }, [NFTA_RANGE_TO_DATA] = { .type = NLA_NESTED }, diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index ad527f3596c0..e23cd4759851 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -103,7 +103,7 @@ void nft_rt_get_eval(const struct nft_expr *expr, } static const struct nla_policy nft_rt_policy[NFTA_RT_MAX + 1] = { - [NFTA_RT_DREG] = { .type = NLA_U32 }, + [NFTA_RT_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_RT_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), }; diff --git a/net/netfilter/nft_socket.c b/net/netfilter/nft_socket.c index c55a1310226a..a146a45d7531 100644 --- a/net/netfilter/nft_socket.c +++ b/net/netfilter/nft_socket.c @@ -163,7 +163,7 @@ static void nft_socket_eval(const struct nft_expr *expr, static const struct nla_policy nft_socket_policy[NFTA_SOCKET_MAX + 1] = { [NFTA_SOCKET_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_SOCKET_DREG] = { .type = NLA_U32 }, + [NFTA_SOCKET_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_SOCKET_LEVEL] = NLA_POLICY_MAX(NLA_BE32, 255), }; diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c index 65d06300f48a..0b987bc2132a 100644 --- a/net/netfilter/nft_tunnel.c +++ b/net/netfilter/nft_tunnel.c @@ -67,7 +67,7 @@ static void nft_tunnel_get_eval(const struct nft_expr *expr, static const struct nla_policy nft_tunnel_policy[NFTA_TUNNEL_MAX + 1] = { [NFTA_TUNNEL_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), - [NFTA_TUNNEL_DREG] = { .type = NLA_U32 }, + [NFTA_TUNNEL_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), [NFTA_TUNNEL_MODE] = NLA_POLICY_MAX(NLA_BE32, NFT_TUNNEL_MODE_MAX), }; diff --git a/net/netfilter/nft_xfrm.c b/net/netfilter/nft_xfrm.c index 6858cd2d16a4..65a75d88e5f0 100644 --- a/net/netfilter/nft_xfrm.c +++ b/net/netfilter/nft_xfrm.c @@ -19,7 +19,7 @@ static const struct nla_policy nft_xfrm_policy[NFTA_XFRM_MAX + 1] = { [NFTA_XFRM_KEY] = NLA_POLICY_MAX(NLA_BE32, 255), [NFTA_XFRM_DIR] = NLA_POLICY_MAX(NLA_U8, XFRM_POLICY_OUT), [NFTA_XFRM_SPNUM] = NLA_POLICY_MAX(NLA_BE32, XFRM_MAX_DEPTH - 1), - [NFTA_XFRM_DREG] = { .type = NLA_U32 }, + [NFTA_XFRM_DREG] = NLA_POLICY_MAX(NLA_BE32, NFT_REG32_MAX), }; struct nft_xfrm { From 04e1ca21a5e3f84595c546b481b7bc2b5c3c5fbd Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 17 Mar 2026 15:50:08 +0100 Subject: [PATCH 08/12] netfilter: nft_set_pipapo: increment data in one step Since commit e807b13cb3e3 ("nft_set_pipapo: Generalise group size for buckets") there is no longer a need to increment the data pointer in two steps. Switch to a single invocation of NFT_PIPAPO_GROUPS_PADDED_SIZE() helper, like the avx2 implementation. [ Stefano: Improve commit message ] Reviewed-by: Stefano Brivio Signed-off-by: Florian Westphal --- net/netfilter/nft_set_pipapo.c | 4 +--- net/netfilter/nft_set_pipapo.h | 3 --- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 7fd24e0cc428..50d4a4f04309 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -452,8 +452,6 @@ static struct nft_pipapo_elem *pipapo_get_slow(const struct nft_pipapo_match *m, pipapo_and_field_buckets_4bit(f, res_map, data); NFT_PIPAPO_GROUP_BITS_ARE_8_OR_4; - data += f->groups / NFT_PIPAPO_GROUPS_PER_BYTE(f); - /* Now populate the bitmap for the next field, unless this is * the last field, in which case return the matched 'ext' * pointer if any. @@ -498,7 +496,7 @@ static struct nft_pipapo_elem *pipapo_get_slow(const struct nft_pipapo_match *m, map_index = !map_index; swap(res_map, fill_map); - data += NFT_PIPAPO_GROUPS_PADDING(f); + data += NFT_PIPAPO_GROUPS_PADDED_SIZE(f); } __local_unlock_nested_bh(&scratch->bh_lock); diff --git a/net/netfilter/nft_set_pipapo.h b/net/netfilter/nft_set_pipapo.h index 9aee9a9eaeb7..b82abb03576e 100644 --- a/net/netfilter/nft_set_pipapo.h +++ b/net/netfilter/nft_set_pipapo.h @@ -42,9 +42,6 @@ /* Fields are padded to 32 bits in input registers */ #define NFT_PIPAPO_GROUPS_PADDED_SIZE(f) \ (round_up((f)->groups / NFT_PIPAPO_GROUPS_PER_BYTE(f), sizeof(u32))) -#define NFT_PIPAPO_GROUPS_PADDING(f) \ - (NFT_PIPAPO_GROUPS_PADDED_SIZE(f) - (f)->groups / \ - NFT_PIPAPO_GROUPS_PER_BYTE(f)) /* Number of buckets given by 2 ^ n, with n bucket bits */ #define NFT_PIPAPO_BUCKETS(bb) (1 << (bb)) From a3f1e6a19a5dccb7a29d941b3acee0a3974974f4 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 17 Mar 2026 15:09:18 +0100 Subject: [PATCH 09/12] netfilter: nft_set_pipapo_avx2: remove redundant loop in lookup_slow nft_pipapo_avx2_lookup_slow will never be used in reality, because the common sizes are handled by avx2 optimized versions. However, nft_pipapo_avx2_lookup_slow loops over the data just like the avx2 functions. However, _slow doesn't need to do that. As-is, first loop sets all the right result bits and the next iterations boil down to 'x = x & x'. Remove the loop. Reviewed-by: Stefano Brivio Signed-off-by: Florian Westphal --- net/netfilter/nft_set_pipapo_avx2.c | 32 ++++++++--------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/net/netfilter/nft_set_pipapo_avx2.c b/net/netfilter/nft_set_pipapo_avx2.c index 6395982e4d95..dad265807b8b 100644 --- a/net/netfilter/nft_set_pipapo_avx2.c +++ b/net/netfilter/nft_set_pipapo_avx2.c @@ -1041,7 +1041,6 @@ static int nft_pipapo_avx2_lookup_8b_16(unsigned long *map, unsigned long *fill, * @map: Previous match result, used as initial bitmap * @fill: Destination bitmap to be filled with current match result * @f: Field, containing lookup and mapping tables - * @offset: Ignore buckets before the given index, no bits are filled there * @pkt: Packet data, pointer to input nftables register * @first: If this is the first field, don't source previous result * @last: Last field: stop at the first match and return bit index @@ -1056,32 +1055,19 @@ static int nft_pipapo_avx2_lookup_8b_16(unsigned long *map, unsigned long *fill, static int nft_pipapo_avx2_lookup_slow(const struct nft_pipapo_match *mdata, unsigned long *map, unsigned long *fill, const struct nft_pipapo_field *f, - int offset, const u8 *pkt, + const u8 *pkt, bool first, bool last) { - unsigned long bsize = f->bsize; - int i, ret = -1, b; - if (first) pipapo_resmap_init(mdata, map); - for (i = offset; i < bsize; i++) { - if (f->bb == 8) - pipapo_and_field_buckets_8bit(f, map, pkt); - else - pipapo_and_field_buckets_4bit(f, map, pkt); - NFT_PIPAPO_GROUP_BITS_ARE_8_OR_4; + if (f->bb == 8) + pipapo_and_field_buckets_8bit(f, map, pkt); + else + pipapo_and_field_buckets_4bit(f, map, pkt); + NFT_PIPAPO_GROUP_BITS_ARE_8_OR_4; - b = pipapo_refill(map, bsize, f->rules, fill, f->mt, last); - - if (last) - return b; - - if (ret == -1) - ret = b / XSAVE_YMM_SIZE; - } - - return ret; + return pipapo_refill(map, f->bsize, f->rules, fill, f->mt, last); } /** @@ -1201,7 +1187,7 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, NFT_SET_PIPAPO_AVX2_LOOKUP(8, 16); } else { ret = nft_pipapo_avx2_lookup_slow(m, res, fill, f, - ret, data, + data, first, last); } } else { @@ -1217,7 +1203,7 @@ struct nft_pipapo_elem *pipapo_get_avx2(const struct nft_pipapo_match *m, NFT_SET_PIPAPO_AVX2_LOOKUP(4, 32); } else { ret = nft_pipapo_avx2_lookup_slow(m, res, fill, f, - ret, data, + data, first, last); } } From 3785091c6c16a1ce4a5e0460881fc81ed8d2c8a1 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 22 Mar 2026 23:51:47 +0100 Subject: [PATCH 10/12] netfilter: nft_meta: add double-tagged vlan and pppoe support Currently: add rule netdev x y ip saddr 1.1.1.1 does not work with neither double-tagged vlan nor pppoe packets. This is because the network and transport header offset are not pointing to the IP and transport protocol headers in the stack. This patch expands NFT_META_PROTOCOL and NFT_META_L4PROTO to parse double-tagged vlan and pppoe packets so matching network and transport header fields becomes possible with the existing userspace generated bytecode. Note that this parser only supports double-tagged vlan which is composed of vlan offload + vlan header in the skb payload area for simplicity. NFT_META_PROTOCOL is used by bridge and netdev family as an implicit dependency in the bytecode to match on network header fields. Similarly, there is also NFT_META_L4PROTO, which is also used as an implicit dependency when matching on the transport protocol header fields. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- include/net/netfilter/nf_tables.h | 4 ++ include/net/netfilter/nf_tables_ipv4.h | 17 +++++--- include/net/netfilter/nf_tables_ipv6.h | 16 +++++--- net/netfilter/nf_tables_core.c | 2 +- net/netfilter/nft_meta.c | 54 +++++++++++++++++++++++++- net/netfilter/nft_payload.c | 2 +- 6 files changed, 82 insertions(+), 13 deletions(-) diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 66cedcfa338e..2c0173d9309c 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -31,7 +31,9 @@ struct nft_pktinfo { const struct nf_hook_state *state; u8 flags; u8 tprot; + __be16 ethertype; u16 fragoff; + u16 nhoff; u16 thoff; u16 inneroff; }; @@ -83,6 +85,8 @@ static inline void nft_set_pktinfo_unspec(struct nft_pktinfo *pkt) { pkt->flags = 0; pkt->tprot = 0; + pkt->ethertype = pkt->skb->protocol; + pkt->nhoff = 0; pkt->thoff = 0; pkt->fragoff = 0; } diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h index fcf967286e37..e715405a73cb 100644 --- a/include/net/netfilter/nf_tables_ipv4.h +++ b/include/net/netfilter/nf_tables_ipv4.h @@ -12,16 +12,19 @@ static inline void nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt) ip = ip_hdr(pkt->skb); pkt->flags = NFT_PKTINFO_L4PROTO; pkt->tprot = ip->protocol; + pkt->ethertype = pkt->skb->protocol; + pkt->nhoff = 0; pkt->thoff = ip_hdrlen(pkt->skb); pkt->fragoff = ntohs(ip->frag_off) & IP_OFFSET; } -static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt) +static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt, + int nhoff) { struct iphdr *iph, _iph; u32 len, thoff, skb_len; - iph = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb), + iph = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb) + nhoff, sizeof(*iph), &_iph); if (!iph) return -1; @@ -31,7 +34,7 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt) len = iph_totlen(pkt->skb, iph); thoff = iph->ihl * 4; - skb_len = pkt->skb->len - skb_network_offset(pkt->skb); + skb_len = pkt->skb->len - skb_network_offset(pkt->skb) - nhoff; if (skb_len < len) return -1; @@ -42,7 +45,9 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt) pkt->flags = NFT_PKTINFO_L4PROTO; pkt->tprot = iph->protocol; - pkt->thoff = skb_network_offset(pkt->skb) + thoff; + pkt->ethertype = pkt->skb->protocol; + pkt->nhoff = nhoff; + pkt->thoff = skb_network_offset(pkt->skb) + nhoff + thoff; pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET; return 0; @@ -50,7 +55,7 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt) static inline void nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt) { - if (__nft_set_pktinfo_ipv4_validate(pkt) < 0) + if (__nft_set_pktinfo_ipv4_validate(pkt, 0) < 0) nft_set_pktinfo_unspec(pkt); } @@ -78,6 +83,8 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt) } pkt->flags = NFT_PKTINFO_L4PROTO; + pkt->ethertype = pkt->skb->protocol; + pkt->nhoff = 0; pkt->tprot = iph->protocol; pkt->thoff = thoff; pkt->fragoff = ntohs(iph->frag_off) & IP_OFFSET; diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h index c53ac00bb974..d7b8c559b795 100644 --- a/include/net/netfilter/nf_tables_ipv6.h +++ b/include/net/netfilter/nf_tables_ipv6.h @@ -20,21 +20,23 @@ static inline void nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt) pkt->flags = NFT_PKTINFO_L4PROTO; pkt->tprot = protohdr; + pkt->ethertype = pkt->skb->protocol; + pkt->nhoff = 0; pkt->thoff = thoff; pkt->fragoff = frag_off; } -static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt) +static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt, int nhoff) { #if IS_ENABLED(CONFIG_IPV6) unsigned int flags = IP6_FH_F_AUTH; struct ipv6hdr *ip6h, _ip6h; - unsigned int thoff = 0; + unsigned int thoff = nhoff; unsigned short frag_off; u32 pkt_len, skb_len; int protohdr; - ip6h = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb), + ip6h = skb_header_pointer(pkt->skb, skb_network_offset(pkt->skb) + nhoff, sizeof(*ip6h), &_ip6h); if (!ip6h) return -1; @@ -43,7 +45,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt) return -1; pkt_len = ipv6_payload_len(pkt->skb, ip6h); - skb_len = pkt->skb->len - skb_network_offset(pkt->skb); + skb_len = pkt->skb->len - skb_network_offset(pkt->skb) - nhoff; if (pkt_len + sizeof(*ip6h) > skb_len) return -1; @@ -53,6 +55,8 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt) pkt->flags = NFT_PKTINFO_L4PROTO; pkt->tprot = protohdr; + pkt->ethertype = pkt->skb->protocol; + pkt->nhoff = nhoff; pkt->thoff = thoff; pkt->fragoff = frag_off; @@ -64,7 +68,7 @@ static inline int __nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt) static inline void nft_set_pktinfo_ipv6_validate(struct nft_pktinfo *pkt) { - if (__nft_set_pktinfo_ipv6_validate(pkt) < 0) + if (__nft_set_pktinfo_ipv6_validate(pkt, 0) < 0) nft_set_pktinfo_unspec(pkt); } @@ -99,6 +103,8 @@ static inline int nft_set_pktinfo_ipv6_ingress(struct nft_pktinfo *pkt) pkt->flags = NFT_PKTINFO_L4PROTO; pkt->tprot = protohdr; + pkt->ethertype = pkt->skb->protocol; + pkt->nhoff = 0; pkt->thoff = thoff; pkt->fragoff = frag_off; diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 6557a4018c09..5ddd5b6e135f 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -151,7 +151,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr, unsigned char *ptr; if (priv->base == NFT_PAYLOAD_NETWORK_HEADER) - ptr = skb_network_header(skb); + ptr = skb_network_header(skb) + pkt->nhoff; else { if (!(pkt->flags & NFT_PKTINFO_L4PROTO)) return false; diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c index 7478063339d4..5b25851381e5 100644 --- a/net/netfilter/nft_meta.c +++ b/net/netfilter/nft_meta.c @@ -23,6 +23,8 @@ #include /* for TCP_TIME_WAIT */ #include #include +#include +#include #include #include @@ -309,6 +311,54 @@ nft_meta_get_eval_sdifname(u32 *dest, const struct nft_pktinfo *pkt) nft_meta_store_ifname(dest, dev); } +static void nft_meta_pktinfo_may_update(struct nft_pktinfo *pkt) +{ + struct sk_buff *skb = pkt->skb; + struct vlan_ethhdr *veth; + __be16 ethertype; + int nhoff; + + /* Is this an IP packet? Then, skip. */ + if (pkt->flags) + return; + + /* ... else maybe an IP packet over PPPoE or Q-in-Q? */ + switch (skb->protocol) { + case htons(ETH_P_8021Q): + if (!pskb_may_pull(skb, skb_mac_offset(skb) + sizeof(*veth))) + return; + + veth = (struct vlan_ethhdr *)skb_mac_header(skb); + nhoff = VLAN_HLEN; + ethertype = veth->h_vlan_encapsulated_proto; + break; + case htons(ETH_P_PPP_SES): + if (!nf_flow_pppoe_proto(skb, ðertype)) + return; + + nhoff = PPPOE_SES_HLEN; + break; + default: + return; + } + + nhoff += skb_network_offset(skb); + switch (ethertype) { + case htons(ETH_P_IP): + if (__nft_set_pktinfo_ipv4_validate(pkt, nhoff)) + nft_set_pktinfo_unspec(pkt); + break; + case htons(ETH_P_IPV6): + if (__nft_set_pktinfo_ipv6_validate(pkt, nhoff)) + nft_set_pktinfo_unspec(pkt); + break; + default: + break; + } + + pkt->ethertype = ethertype; +} + void nft_meta_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -322,12 +372,14 @@ void nft_meta_get_eval(const struct nft_expr *expr, *dest = skb->len; break; case NFT_META_PROTOCOL: - nft_reg_store16(dest, (__force u16)skb->protocol); + nft_meta_pktinfo_may_update((struct nft_pktinfo *)pkt); + nft_reg_store16(dest, (__force u16)pkt->ethertype); break; case NFT_META_NFPROTO: nft_reg_store8(dest, nft_pf(pkt)); break; case NFT_META_L4PROTO: + nft_meta_pktinfo_may_update((struct nft_pktinfo *)pkt); if (!(pkt->flags & NFT_PKTINFO_L4PROTO)) goto err; nft_reg_store8(dest, pkt->tprot); diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 3fa3c6c835be..01e13e5255a9 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -183,7 +183,7 @@ void nft_payload_eval(const struct nft_expr *expr, offset = skb_mac_header(skb) - skb->data; break; case NFT_PAYLOAD_NETWORK_HEADER: - offset = skb_network_offset(skb); + offset = skb_network_offset(skb) + pkt->nhoff; break; case NFT_PAYLOAD_TRANSPORT_HEADER: if (!(pkt->flags & NFT_PKTINFO_L4PROTO) || pkt->fragoff) From f33fad8dbfff7c35c22abb3a7305173d005ac362 Mon Sep 17 00:00:00 2001 From: David Laight Date: Thu, 26 Mar 2026 20:18:19 +0000 Subject: [PATCH 11/12] netfilter: nf_conntrack_h323: Correct indentation when H323_TRACE defined The trace lines are indented using PRINT("%*.s", xx, " "). Userspace will treat this as "%*.0s" and will output no characters when 'xx' is zero, the kernel treats it as "%*s" and will output a single ' ' - which is probably what is intended. Change all the formats to "%*s" removing the default precision. This gives a single space indent when level is zero. Signed-off-by: David Laight Reviewed-by: Petr Mladek Signed-off-by: Florian Westphal --- net/netfilter/nf_conntrack_h323_asn1.c | 38 +++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/net/netfilter/nf_conntrack_h323_asn1.c b/net/netfilter/nf_conntrack_h323_asn1.c index 09e0f724644f..6830c9da3507 100644 --- a/net/netfilter/nf_conntrack_h323_asn1.c +++ b/net/netfilter/nf_conntrack_h323_asn1.c @@ -274,7 +274,7 @@ static unsigned int get_uint(struct bitstr *bs, int b) static int decode_nul(struct bitstr *bs, const struct field_t *f, char *base, int level) { - PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name); return H323_ERROR_NONE; } @@ -282,7 +282,7 @@ static int decode_nul(struct bitstr *bs, const struct field_t *f, static int decode_bool(struct bitstr *bs, const struct field_t *f, char *base, int level) { - PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name); INC_BIT(bs); if (nf_h323_error_boundary(bs, 0, 0)) @@ -295,7 +295,7 @@ static int decode_oid(struct bitstr *bs, const struct field_t *f, { int len; - PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name); BYTE_ALIGN(bs); if (nf_h323_error_boundary(bs, 1, 0)) @@ -314,7 +314,7 @@ static int decode_int(struct bitstr *bs, const struct field_t *f, { unsigned int len; - PRINT("%*.s%s", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s", level * TAB_SIZE, " ", f->name); switch (f->sz) { case BYTE: /* Range == 256 */ @@ -361,7 +361,7 @@ static int decode_int(struct bitstr *bs, const struct field_t *f, static int decode_enum(struct bitstr *bs, const struct field_t *f, char *base, int level) { - PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name); if ((f->attr & EXT) && get_bit(bs)) { INC_BITS(bs, 7); @@ -379,7 +379,7 @@ static int decode_bitstr(struct bitstr *bs, const struct field_t *f, { unsigned int len; - PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name); BYTE_ALIGN(bs); switch (f->sz) { @@ -415,7 +415,7 @@ static int decode_numstr(struct bitstr *bs, const struct field_t *f, { unsigned int len; - PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name); /* 2 <= Range <= 255 */ if (nf_h323_error_boundary(bs, 0, f->sz)) @@ -435,7 +435,7 @@ static int decode_octstr(struct bitstr *bs, const struct field_t *f, { unsigned int len; - PRINT("%*.s%s", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s", level * TAB_SIZE, " ", f->name); switch (f->sz) { case FIXD: /* Range == 1 */ @@ -483,7 +483,7 @@ static int decode_bmpstr(struct bitstr *bs, const struct field_t *f, { unsigned int len; - PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name); switch (f->sz) { case BYTE: /* Range == 256 */ @@ -515,7 +515,7 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f, const struct field_t *son; unsigned char *beg = NULL; - PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name); /* Decode? */ base = (base && (f->attr & DECODE)) ? base + f->offset : NULL; @@ -537,7 +537,7 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f, /* Decode the root components */ for (i = opt = 0, son = f->fields; i < f->lb; i++, son++) { if (son->attr & STOP) { - PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ", + PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ", son->name); return H323_ERROR_STOP; } @@ -555,7 +555,7 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f, if (nf_h323_error_boundary(bs, len, 0)) return H323_ERROR_BOUND; if (!base || !(son->attr & DECODE)) { - PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, + PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ", son->name); bs->cur += len; continue; @@ -608,7 +608,7 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f, } if (son->attr & STOP) { - PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ", + PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ", son->name); return H323_ERROR_STOP; } @@ -622,7 +622,7 @@ static int decode_seq(struct bitstr *bs, const struct field_t *f, if (nf_h323_error_boundary(bs, len, 0)) return H323_ERROR_BOUND; if (!base || !(son->attr & DECODE)) { - PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ", + PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ", son->name); bs->cur += len; continue; @@ -648,7 +648,7 @@ static int decode_seqof(struct bitstr *bs, const struct field_t *f, const struct field_t *son; unsigned char *beg = NULL; - PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name); /* Decode? */ base = (base && (f->attr & DECODE)) ? base + f->offset : NULL; @@ -703,7 +703,7 @@ static int decode_seqof(struct bitstr *bs, const struct field_t *f, if (nf_h323_error_boundary(bs, len, 0)) return H323_ERROR_BOUND; if (!base || !(son->attr & DECODE)) { - PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, + PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ", son->name); bs->cur += len; continue; @@ -744,7 +744,7 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f, const struct field_t *son; unsigned char *beg = NULL; - PRINT("%*.s%s\n", level * TAB_SIZE, " ", f->name); + PRINT("%*s%s\n", level * TAB_SIZE, " ", f->name); /* Decode? */ base = (base && (f->attr & DECODE)) ? base + f->offset : NULL; @@ -785,7 +785,7 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f, /* Transfer to son level */ son = &f->fields[type]; if (son->attr & STOP) { - PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ", son->name); + PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ", son->name); return H323_ERROR_STOP; } @@ -797,7 +797,7 @@ static int decode_choice(struct bitstr *bs, const struct field_t *f, if (nf_h323_error_boundary(bs, len, 0)) return H323_ERROR_BOUND; if (!base || !(son->attr & DECODE)) { - PRINT("%*.s%s\n", (level + 1) * TAB_SIZE, " ", + PRINT("%*s%s\n", (level + 1) * TAB_SIZE, " ", son->name); bs->cur += len; return H323_ERROR_NONE; From c6f85577584b5f8414141ae389e974b8ca6a698b Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 30 Mar 2026 11:04:02 +0200 Subject: [PATCH 12/12] netfilter: nf_tables_offload: add nft_flow_action_entry_next() and use it Add a new helper function to retrieve the next action entry in flow rule, check if the maximum number of actions is reached, bail out in such case. Replace existing opencoded iteration on the action array by this helper function. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- include/net/netfilter/nf_tables_offload.h | 10 ++++++++++ net/netfilter/nf_dup_netdev.c | 5 ++++- net/netfilter/nft_immediate.c | 4 +++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/include/net/netfilter/nf_tables_offload.h b/include/net/netfilter/nf_tables_offload.h index 3568b6a2f5f0..14c427891ee6 100644 --- a/include/net/netfilter/nf_tables_offload.h +++ b/include/net/netfilter/nf_tables_offload.h @@ -67,6 +67,16 @@ struct nft_flow_rule { struct flow_rule *rule; }; +static inline struct flow_action_entry * +nft_flow_action_entry_next(struct nft_offload_ctx *ctx, + struct nft_flow_rule *flow) +{ + if (unlikely(ctx->num_actions >= flow->rule->action.num_entries)) + return NULL; + + return &flow->rule->action.entries[ctx->num_actions++]; +} + void nft_flow_rule_set_addr_type(struct nft_flow_rule *flow, enum flow_dissector_key_id addr_type); diff --git a/net/netfilter/nf_dup_netdev.c b/net/netfilter/nf_dup_netdev.c index fab8b9011098..e348fb90b8dc 100644 --- a/net/netfilter/nf_dup_netdev.c +++ b/net/netfilter/nf_dup_netdev.c @@ -95,7 +95,10 @@ int nft_fwd_dup_netdev_offload(struct nft_offload_ctx *ctx, if (!dev) return -EOPNOTSUPP; - entry = &flow->rule->action.entries[ctx->num_actions++]; + entry = nft_flow_action_entry_next(ctx, flow); + if (!entry) + return -E2BIG; + entry->id = id; entry->dev = dev; diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index 1b733c7b1b0e..d00eb2eb30e4 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c @@ -279,7 +279,9 @@ static int nft_immediate_offload_verdict(struct nft_offload_ctx *ctx, struct flow_action_entry *entry; const struct nft_data *data; - entry = &flow->rule->action.entries[ctx->num_actions++]; + entry = nft_flow_action_entry_next(ctx, flow); + if (!entry) + return -E2BIG; data = &priv->data; switch (data->verdict.code) {