Netdev List
 help / color / mirror / Atom feed
* [PATCH net 00/12] Netfilter/IPVS fixes for net
@ 2026-09-03  0:41 Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 01/12] ipvs: reject invalid states in connection template sync records Pablo Neira Ayuso
                   ` (11 more replies)
  0 siblings, 12 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

Hi,

The following patchset contains Netfilter/IPVS fixes for net:

1) Reject malformed messages in IPVS sync, from Kyle Zeng.

2) Fix possible stale infoleak in IPVS sync, also from Kyle Zeng.

3) Out-of-bound read in the SIP conntrack helper, from
   Joas Antonio dos Santos.

4) UaF on cttimeout module removal, from Chengfeng Ye.

5) Unregister nf_loggers before netns teardown to fix UaF,
   also from Chengfeng Ye.

6) Limit IPVS cache growth for LBLCR and LBLC schedulers,
   from Zhiling Zou.

7) Restrict checksum offset to known supported protocols in
   nft_payload, from Florian Westphal.

8) Fix race in nfnetlink_log due to concurrent instance destruction,
   from Florian Westphal.

9) Hold nfnl mutex from event notifier path of nfnetlink_queue to deal
   with race between close() and UNBIND request on same portid, also
   from Florian.

10) Remove arp_table 32bit compat interface, this is already off in
    many distributions, from Florian Westphal.

11) Set IP6T_F_PROTO flag is e->ipv6.proto is set on to deal with
    insufficient validation of xtables extensions when used from
    legacy ip6tables, from Florian.

12) Set on the NLM_F_DUMP_FILTERED flag when all is filtering out
    in ctnetlink, from Ilya Maximets.

Please, pull these changes from:

  git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git nf-26-09-03

Thanks.

----------------------------------------------------------------

The following changes since commit 1d2929d0850fff683b8aff051275945e65f082c8:

  net: psp: do not inherit the Rx association on clone (2026-09-01 15:12:24 +0200)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf.git tags/nf-26-09-03

for you to fetch changes up to 5dfd63737fb19c6b179799d3ae784673152f61eb:

  netfilter: report NLM_F_DUMP_FILTERED when all is filtered out (2026-09-03 01:39:28 +0200)

----------------------------------------------------------------
netfilter pull request 26-09-03

----------------------------------------------------------------
Chengfeng Ye (2):
      netfilter: cttimeout: prevent UAF during module unload
      netfilter: nf_log: unregister loggers before per-net teardown

Florian Westphal (5):
      netfilter: nft_payload: restrict checksum offsets to known values
      netfilter: nfnetlink_log: cope with concurrent instance destruction
      netfilter: nfnetlink_queue: hold nfnl mutex in event notifier
      netfilter: arp_tables: remove the 32bit compat interface
      netfilter: ip6_tables: set F_PROTO when proto value is nonzero

Ilya Maximets (1):
      netfilter: report NLM_F_DUMP_FILTERED when all is filtered out

Joas Antonio dos Santos (1):
      netfilter: nf_conntrack_sip: fix OOB read in sip_skip_whitespace()

Kyle Zeng (2):
      ipvs: reject invalid states in connection template sync records
      ipvs: fix reversed sequence option serialization

Zhiling Zou (1):
      ipvs: bound LBLCR and LBLC cache growth

 include/linux/netfilter_arp/arp_tables.h |  19 --
 net/ipv4/netfilter/arp_tables.c          | 472 +------------------------------
 net/ipv6/netfilter/ip6_tables.c          |   5 +
 net/netfilter/ipvs/ip_vs_lblc.c          |   3 +
 net/netfilter/ipvs/ip_vs_lblcr.c         |   3 +
 net/netfilter/ipvs/ip_vs_sync.c          |  20 +-
 net/netfilter/nf_conntrack_netlink.c     |   2 +
 net/netfilter/nf_conntrack_sip.c         |   2 +-
 net/netfilter/nf_log_syslog.c            |   2 +-
 net/netfilter/nfnetlink_cttimeout.c      |   2 +-
 net/netfilter/nfnetlink_log.c            |  15 +-
 net/netfilter/nfnetlink_queue.c          |   8 +-
 net/netfilter/nft_payload.c              |  36 ++-
 13 files changed, 73 insertions(+), 516 deletions(-)

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH net 01/12] ipvs: reject invalid states in connection template sync records
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 02/12] ipvs: fix reversed sequence option serialization Pablo Neira Ayuso
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Kyle Zeng <kylebot@openai.com>

IPVS sync receivers validate protocol states before creating or updating a
connection. For connection templates, however, they only log states outside
the template state range and still store the value in the connection.

A template can be returned by ordinary connection lookup. TCP and SCTP then
use the invalid state as an index into their transition tables.

Reject invalid template states in both sync protocol versions before
looking up or modifying a connection. The version 1 path handles both
IPv4 and IPv6 records.

Fixes: 275411430f89 ("ipvs: add assured state for conn templates")
Cc: stable@vger.kernel.org
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Kyle Zeng <kylebot@openai.com>
Acked-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipvs/ip_vs_sync.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index ea5fdd4f4ce7..1deb063cd72c 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -999,10 +999,10 @@ static void ip_vs_process_message_v0(struct netns_ipvs *ipvs, const char *buffer
 					pp->name, state);
 				continue;
 			}
-		} else {
-			if (state >= IP_VS_CTPL_S_LAST)
-				IP_VS_DBG(7, "BACKUP v0, Invalid tpl state %u\n",
-					  state);
+		} else if (state >= IP_VS_CTPL_S_LAST) {
+			IP_VS_DBG(7, "BACKUP v0, Invalid tpl state %u\n",
+				  state);
+			continue;
 		}
 
 		ip_vs_conn_fill_param(ipvs, AF_INET, s->protocol,
@@ -1159,10 +1159,10 @@ static inline int ip_vs_proc_sync_conn(struct netns_ipvs *ipvs, __u8 *p, __u8 *m
 			retc = 40;
 			goto out;
 		}
-	} else {
-		if (state >= IP_VS_CTPL_S_LAST)
-			IP_VS_DBG(7, "BACKUP, Invalid tpl state %u\n",
-				  state);
+	} else if (state >= IP_VS_CTPL_S_LAST) {
+		IP_VS_DBG(7, "BACKUP, Invalid tpl state %u\n", state);
+		retc = 40;
+		goto out;
 	}
 	if (ip_vs_conn_fill_param_sync(ipvs, af, s, &param, pe_data,
 				       pe_data_len, pe_name, pe_name_len)) {
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 02/12] ipvs: fix reversed sequence option serialization
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 01/12] ipvs: reject invalid states in connection template sync records Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 03/12] netfilter: nf_conntrack_sip: fix OOB read in sip_skip_whitespace() Pablo Neira Ayuso
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Kyle Zeng <kylebot@openai.com>

hton_seq() expects the host-order source first and the unaligned
network-order destination second. The version 1 sync sender passes these
arguments in reverse for both sequence blocks. This leaves 24 bytes of the
kmalloc-backed message unwritten. It may disclose stale heap data and
replace the live connection sequence state with values read from the
buffer.

Pass the connection sequence state as the source and the message payload as
the destination for both blocks.

Fixes: 986a07579533 ("IPVS: Backup, Change sending to Version 1 format")
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Kyle Zeng <kylebot@openai.com>
Acked-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipvs/ip_vs_sync.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 1deb063cd72c..5383aeafb0ae 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -747,9 +747,9 @@ void ip_vs_sync_conn(struct netns_ipvs *ipvs, struct ip_vs_conn *cp, int pkts)
 	if (cp->flags & IP_VS_CONN_F_SEQ_MASK) {
 		*(p++) = IPVS_OPT_SEQ_DATA;
 		*(p++) = sizeof(struct ip_vs_sync_conn_options);
-		hton_seq((struct ip_vs_seq *)p, &cp->in_seq);
+		hton_seq(&cp->in_seq, (struct ip_vs_seq *)p);
 		p += sizeof(struct ip_vs_seq);
-		hton_seq((struct ip_vs_seq *)p, &cp->out_seq);
+		hton_seq(&cp->out_seq, (struct ip_vs_seq *)p);
 		p += sizeof(struct ip_vs_seq);
 	}
 	/* Handle pe data */
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 03/12] netfilter: nf_conntrack_sip: fix OOB read in sip_skip_whitespace()
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 01/12] ipvs: reject invalid states in connection template sync records Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 02/12] ipvs: fix reversed sequence option serialization Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 04/12] netfilter: cttimeout: prevent UAF during module unload Pablo Neira Ayuso
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Joas Antonio dos Santos <joasantonio108@gmail.com>

sip_skip_whitespace() returns dptr unchanged when its own loop
exhausts the buffer (dptr == limit), instead of NULL like its sibling
sip_follow_continuation() returns on its own "no more data" path.

ct_sip_get_header() only checks for NULL after calling it:

  dptr = sip_skip_whitespace(dptr, limit);
  if (dptr == NULL)
          break;
  if (*dptr != ':' || ++dptr >= limit)
          break;

so a recognized header name followed only by spaces/tabs running to
the exact end of the SIP payload, with no colon, makes the very next
statement read one byte past the buffer.

Make both "no more data" outcomes return NULL, matching the
convention sip_follow_continuation() already uses and that both
existing callers already check for.

Fixes: ea45f12a2766d ("[NETFILTER]: nf_conntrack_sip: parse SIP headers properly")
Signed-off-by: Joas Antonio dos Santos <joasantonio108@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_conntrack_sip.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 3ccf34fc1c53..64bc440b1181 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -423,7 +423,7 @@ static const char *sip_skip_whitespace(const char *dptr, const char *limit)
 		dptr = sip_follow_continuation(dptr, limit);
 		break;
 	}
-	return dptr;
+	return dptr < limit ? dptr : NULL;
 }
 
 /* Search within a SIP header value, dealing with continuation lines */
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 04/12] netfilter: cttimeout: prevent UAF during module unload
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
                   ` (2 preceding siblings ...)
  2026-09-03  0:41 ` [PATCH net 03/12] netfilter: nf_conntrack_sip: fix OOB read in sip_skip_whitespace() Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 05/12] netfilter: nf_log: unregister loggers before per-net teardown Pablo Neira Ayuso
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Chengfeng Ye <nicoyip.dev@gmail.com>

nf_ct_set_timeout() protects the timeout hook dereference and policy lookup
with rcu_read_lock(). cttimeout_exit(), however, unregisters the per-net
operations before it clears the hook.

This allows the following interleaving:

  CPU 0                              CPU 1
  cttimeout_exit()                   nf_ct_set_timeout()
    unregister_pernet_subsys()         rcu_read_lock()
      kfree(pernet)                     h = nf_ct_timeout_hook
                                        h->timeout_find_get()
                                          nfct_timeout_pernet()

The hook still points to ctnl_timeout_find_get() when CPU 1 looks up the
already freed per-net timeout list. KASAN reported:

  BUG: KASAN: slab-use-after-free in ctnl_timeout_find_get
  Read of size 8 by task poc/90
  Call Trace:
   ctnl_timeout_find_get+0x271/0x2a0 [nfnetlink_cttimeout]
   nf_ct_set_timeout+0x7b/0x3c0
   xt_ct_tg_check+0x724/0xb20
   xt_check_target+0x234/0xa90
   do_ipt_set_ctl+0x570/0x1270
  Allocated by task 89:
   __kmalloc_noprof+0x16e/0x460
   ops_init+0x6d/0x420
   register_pernet_operations+0x2f6/0x670
  Freed by task 91:
   kfree+0x131/0x390
   ops_undo_list+0x3d4/0x730
   unregister_pernet_operations+0x232/0x490
   unregister_pernet_subsys+0x1c/0x30
   cttimeout_exit+0x52/0x970 [nfnetlink_cttimeout]

Clear the hook and wait for existing readers before unregistering the
per-net operations. This blocks new policy lookups and ensures readers that
observed the hook finish before the per-net storage is freed.

Fixes: ebfbe67568a7 ("netfilter: cttimeout: use net_generic infra")
Cc: stable@vger.kernel.org
Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nfnetlink_cttimeout.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c
index 66c2016f6049..132c02ac7c4e 100644
--- a/net/netfilter/nfnetlink_cttimeout.c
+++ b/net/netfilter/nfnetlink_cttimeout.c
@@ -652,9 +652,9 @@ static void __exit cttimeout_exit(void)
 {
 	nfnetlink_subsys_unregister(&cttimeout_subsys);
 
-	unregister_pernet_subsys(&cttimeout_ops);
 	RCU_INIT_POINTER(nf_ct_timeout_hook, NULL);
 	synchronize_net();
+	unregister_pernet_subsys(&cttimeout_ops);
 }
 
 module_init(cttimeout_init);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 05/12] netfilter: nf_log: unregister loggers before per-net teardown
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
                   ` (3 preceding siblings ...)
  2026-09-03  0:41 ` [PATCH net 04/12] netfilter: cttimeout: prevent UAF during module unload Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 06/12] ipvs: bound LBLCR and LBLC cache growth Pablo Neira Ayuso
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Chengfeng Ye <nicoyip.dev@gmail.com>

nf_log_syslog and nfnetlink_log unregister their per-network namespace
operations before unregistering their global logger backends. This
leaves a window where a sysctl or netlink writer can rebind the still-
registered logger after the per-net pre-exit callback cleared the old
selection.

The race looks like this:

  CPU 0                                 CPU 1
  ----                                  ----
  unregister_pernet_subsys()
    nf_log_unset(net, logger)
      net->nf.nf_loggers[pf] = NULL

                                        lock nf_log_mutex
                                        find logger in loggers[][]
                                        net->nf.nf_loggers[pf] = logger
                                        unlock nf_log_mutex

  nf_log_unregister(logger)
    lock nf_log_mutex
    loggers[pf][type] = NULL
    unlock nf_log_mutex
    synchronize_rcu()
  module exit returns
  module core frees backend memory

Later, a sysctl read or packet logging operation can dereference the
stale per-net logger pointer.

Fix this by unregistering the global logger backends before tearing down
per-net state. Once the global registrations are gone, later writers can
no longer rebind the logger. unregister_pernet_subsys() already waits
for an RCU grace period after the pre-exit callback clears the per-net
selection, while nf_log_unregister() continues to cover readers of the
global logger table.

Apply this ordering fix to both nf_log backends that combine per-net
teardown with global logger registration.

Fixes: 5b023fc8d8e0 ("netfilter: enable per netns support for nf_loggers")
Cc: stable@vger.kernel.org
Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_log_syslog.c | 2 +-
 net/netfilter/nfnetlink_log.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/netfilter/nf_log_syslog.c b/net/netfilter/nf_log_syslog.c
index f24288088c0d..c3fd398ffcd7 100644
--- a/net/netfilter/nf_log_syslog.c
+++ b/net/netfilter/nf_log_syslog.c
@@ -1073,12 +1073,12 @@ static int __init nf_log_syslog_init(void)
 
 static void __exit nf_log_syslog_exit(void)
 {
-	unregister_pernet_subsys(&nf_log_syslog_net_ops);
 	nf_log_unregister(&nf_ip_logger);
 	nf_log_unregister(&nf_arp_logger);
 	nf_log_unregister(&nf_ip6_logger);
 	nf_log_unregister(&nf_netdev_logger);
 	nf_log_unregister(&nf_bridge_logger);
+	unregister_pernet_subsys(&nf_log_syslog_net_ops);
 }
 
 module_init(nf_log_syslog_init);
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 6c7fa2ed34f5..9d7fec570abe 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -1233,8 +1233,8 @@ static void __exit nfnetlink_log_fini(void)
 {
 	nfnetlink_subsys_unregister(&nfulnl_subsys);
 	netlink_unregister_notifier(&nfulnl_rtnl_notifier);
-	unregister_pernet_subsys(&nfnl_log_net_ops);
 	nf_log_unregister(&nfulnl_logger);
+	unregister_pernet_subsys(&nfnl_log_net_ops);
 }
 
 MODULE_DESCRIPTION("netfilter userspace logging");
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 06/12] ipvs: bound LBLCR and LBLC cache growth
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
                   ` (4 preceding siblings ...)
  2026-09-03  0:41 ` [PATCH net 05/12] netfilter: nf_log: unregister loggers before per-net teardown Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 07/12] netfilter: nft_payload: restrict checksum offsets to known values Pablo Neira Ayuso
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Zhiling Zou <zhilinz@nebusec.ai>

ip_vs_lblcr_new() and ip_vs_lblc_new() create cache entries for
every previously unseen destination address. The table max_size only
tells the periodic collector to reclaim entries after the cache has
already exceeded the limit. It does not reclaim entries that the
attacker continues to use.

Reject new cache entries once either table reaches max_size * 3 / 2.
The extra headroom lets the periodic collector catch up while the
existing scheduler fallback continues to use the selected destination
when cache creation fails. New traffic therefore stays serviceable
without growing the tables further.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Reported-by: Vega <vega@nebusec.ai>
Suggested-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Zhiling Zou <zhilinz@nebusec.ai>
Acked-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/ipvs/ip_vs_lblc.c  | 3 +++
 net/netfilter/ipvs/ip_vs_lblcr.c | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c
index 693bcc82ccb7..8180a7ba9f53 100644
--- a/net/netfilter/ipvs/ip_vs_lblc.c
+++ b/net/netfilter/ipvs/ip_vs_lblc.c
@@ -204,6 +204,9 @@ ip_vs_lblc_new(struct ip_vs_lblc_table *tbl, const union nf_inet_addr *daddr,
 			return en;
 		ip_vs_lblc_del(en);
 	}
+	if (atomic_read(&tbl->entries) >= tbl->max_size * 3 / 2)
+		return NULL;
+
 	en = kmalloc_obj(*en, GFP_ATOMIC);
 	if (!en)
 		return NULL;
diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c
index f53f05ceea36..858393b1d2d1 100644
--- a/net/netfilter/ipvs/ip_vs_lblcr.c
+++ b/net/netfilter/ipvs/ip_vs_lblcr.c
@@ -363,6 +363,9 @@ ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr,
 
 	en = ip_vs_lblcr_get(af, tbl, daddr);
 	if (!en) {
+		if (atomic_read(&tbl->entries) >= tbl->max_size * 3 / 2)
+			return NULL;
+
 		en = kmalloc_obj(*en, GFP_ATOMIC);
 		if (!en)
 			return NULL;
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 07/12] netfilter: nft_payload: restrict checksum offsets to known values
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
                   ` (5 preceding siblings ...)
  2026-09-03  0:41 ` [PATCH net 06/12] ipvs: bound LBLCR and LBLC cache growth Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 08/12] netfilter: nfnetlink_log: cope with concurrent instance destruction Pablo Neira Ayuso
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Florian Westphal <fw@strlen.de>

We need to prevent userspace from corrupting e.g. tcp->doff, because
many locations in conntrack and conntrack helpers rely on
nf_conntrack_in() having validated the packet headers.
nft_payload allows to alter headers later which invalidates this
assumption.

The 'Fixes' commit restricts writes to safe fields, but there is
another side channel: the checksum location.

Restrict this too.  Reported via sashiko/gemini.

Fixes: 112e447d17f7 ("netfilter: validate L4 headers after userspace packet writes")
Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nft_payload.c | 36 +++++++++++++++++++++++++++++-------
 1 file changed, 29 insertions(+), 7 deletions(-)

diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
index e315d35f73d4..70f70a65e327 100644
--- a/net/netfilter/nft_payload.c
+++ b/net/netfilter/nft_payload.c
@@ -1008,11 +1008,13 @@ static bool nft_payload_validate_inet_csum_offset(const struct nft_ctx *ctx,
 		if (priv->csum_flags) /* makes no sense, asks for "re-update" of L4 checksum */
 			return false;
 
-		/* no further check here; offset can't be negative so bogus
-		 * offsets can corrupt L4 or payload but not l3 headers.
-		 * We already allow arbitrary l4/inner payload writes.
-		 */
-		return true;
+		/* Validate csum_offset is one of the supported transport header checksums */
+		if (priv->csum_offset == offsetof(struct tcphdr, check) ||
+		    priv->csum_offset == offsetof(struct udphdr, check) ||
+		    priv->csum_offset == offsetof(struct icmp6hdr, icmp6_cksum))
+			return true;
+
+		return false;
 	case NFT_PAYLOAD_INNER_HEADER:
 		return true;
 	case NFT_PAYLOAD_TUN_HEADER:
@@ -1046,6 +1048,25 @@ static bool nft_payload_csum_nh_write_ok(const struct nft_payload_set *priv,
 	return false;
 }
 
+static bool nft_payload_csum_th_write_ok(const struct nft_payload_set *priv,
+					 const struct nft_pktinfo *pkt)
+{
+	if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
+		return false;
+
+	switch (pkt->tprot) {
+	case IPPROTO_TCP:
+		return priv->csum_offset == offsetof(struct tcphdr, check);
+	case IPPROTO_UDP:
+	case IPPROTO_UDPLITE:
+		return priv->csum_offset == offsetof(struct udphdr, check);
+	case IPPROTO_ICMPV6:
+		return priv->csum_offset == offsetof(struct icmp6hdr, icmp6_cksum);
+	}
+
+	return false;
+}
+
 static bool nft_payload_csum_write_ok(const struct nft_pktinfo *pkt,
 				      const struct nft_payload_set *priv)
 {
@@ -1055,9 +1076,10 @@ static bool nft_payload_csum_write_ok(const struct nft_pktinfo *pkt,
 	case NFT_PAYLOAD_NETWORK_HEADER:
 		return nft_payload_csum_nh_write_ok(priv, pkt);
 	case NFT_PAYLOAD_TRANSPORT_HEADER:
+		return nft_payload_csum_th_write_ok(priv, pkt);
 	case NFT_PAYLOAD_INNER_HEADER:
-		/* neither offsets are validated, offsets cannot be
-		 * negative so real l3 headers cannot be mangled.
+		/* offset is not validated, offset cannot be
+		 * negative so real l3/l4 headers cannot be mangled.
 		 */
 		return true;
 	case NFT_PAYLOAD_TUN_HEADER:
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 08/12] netfilter: nfnetlink_log: cope with concurrent instance destruction
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
                   ` (6 preceding siblings ...)
  2026-09-03  0:41 ` [PATCH net 07/12] netfilter: nft_payload: restrict checksum offsets to known values Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 09/12] netfilter: nfnetlink_queue: hold nfnl mutex in event notifier Pablo Neira Ayuso
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Florian Westphal <fw@strlen.de>

Instances are refcounted. However, only memory release happens on the
1 -> 0 transition; the unlink from hashes can occur with any refcount.

Uncooperative userspace can force a situation where a queue is pending
for destruction from netlink event while a different socket with same
portid processes an UNBIND request.

With right timing, this will unhash the instance again:

Oops: general protection fault, [..]
Call Trace:
 <TASK>
 nfulnl_recv_config+0x31a/0xd50
 nfnetlink_rcv_msg+0x7c2/0xeb0

Fixes: 0597f2680d66 ("[NETFILTER]: Add new "nfnetlink_log" userspace packet logging facility")
Reported-by: Eulgyu Kim <eulgyukim@snu.ac.kr>
Reported-by: Jaeyoung Chung <jjy600901@snu.ac.kr>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nfnetlink_log.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 9d7fec570abe..d923f2cb1398 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -228,13 +228,18 @@ static void __nfulnl_flush(struct nfulnl_instance *inst);
 static void
 __instance_destroy(struct nfulnl_instance *inst)
 {
+	spin_lock(&inst->lock);
+	if (inst->copy_mode == NFULNL_COPY_DISABLED) {
+		/* attempt to UNBIND a queue already pending
+		 * destruction via netlink close event. Ignore.
+		 */
+		spin_unlock(&inst->lock);
+		return;
+	}
+
 	/* first pull it out of the global list */
 	hlist_del_rcu(&inst->hlist);
 
-	/* then flush all pending packets from skb */
-
-	spin_lock(&inst->lock);
-
 	/* lockless readers wont be able to use us */
 	inst->copy_mode = NFULNL_COPY_DISABLED;
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 09/12] netfilter: nfnetlink_queue: hold nfnl mutex in event notifier
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
                   ` (7 preceding siblings ...)
  2026-09-03  0:41 ` [PATCH net 08/12] netfilter: nfnetlink_log: cope with concurrent instance destruction Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 10/12] netfilter: arp_tables: remove the 32bit compat interface Pablo Neira Ayuso
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Florian Westphal <fw@strlen.de>

We must serialize the release notifier and the config netlink function.
A concurrent thread can issue close() which can call the release function
while unrelated socket processes UNBIND request for same portid:

Oops: general protection fault, [..]
RIP: 0010:__instance_destroy+0x60/0x210 [nfnetlink_queue]
Call Trace:
 nfqnl_recv_config+0x9b0/0xdc0 [nfnetlink_queue]
 nfnetlink_rcv_msg+0x7c2/0xeb0
 ? __pfx_nfnetlink_rcv_msg+0x10/0x10

After this, parallel UNBIND and URELEASE events are impossible.

This change isn't nice, but its the shortest fix given instances
are not refcounted and the nfnetlink config callback drops the
rcu read lock early due to need for sleeping allocations.

Fixes: 7af4cc3fa158 ("[NETFILTER]: Add "nfnetlink_queue" netfilter queue handler over nfnetlink")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nfnetlink_queue.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index c727668b0c5b..a3bc00280051 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -1593,6 +1593,7 @@ nfqnl_rcv_nl_event(struct notifier_block *this,
 	if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) {
 		int i;
 
+		nfnl_lock(NFNL_SUBSYS_QUEUE);
 		/* destroy all instances for this portid */
 		spin_lock(&q->instances_lock);
 		for (i = 0; i < INSTANCE_BUCKETS; i++) {
@@ -1606,6 +1607,7 @@ nfqnl_rcv_nl_event(struct notifier_block *this,
 			}
 		}
 		spin_unlock(&q->instances_lock);
+		nfnl_unlock(NFNL_SUBSYS_QUEUE);
 	}
 	return NOTIFY_DONE;
 }
@@ -1925,9 +1927,9 @@ static int nfqnl_recv_config(struct sk_buff *skb, const struct nfnl_info *info,
 
 	/* Lookup queue under RCU. After peer_portid check (or for new queue
 	 * in BIND case), the queue is owned by the socket sending this message.
-	 * A socket cannot simultaneously send a message and close, so while
-	 * processing this CONFIG message, nfqnl_rcv_nl_event() (triggered by
-	 * socket close) cannot destroy this queue. Safe to use without RCU.
+	 * nfqnl_rcv_nl_event() will block on the nfnl subsys mutex that is
+	 * held by the caller, so the queue cannot be destroyed in parallel,
+	 * even after we drop the RCU read lock.
 	 */
 	rcu_read_lock();
 	queue = instance_lookup(q, queue_num);
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 10/12] netfilter: arp_tables: remove the 32bit compat interface
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
                   ` (8 preceding siblings ...)
  2026-09-03  0:41 ` [PATCH net 09/12] netfilter: nfnetlink_queue: hold nfnl mutex in event notifier Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 11/12] netfilter: ip6_tables: set F_PROTO when proto value is nonzero Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 12/12] netfilter: report NLM_F_DUMP_FILTERED when all is filtered out Pablo Neira Ayuso
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Florian Westphal <fw@strlen.de>

This feature is required to use 32bit arptables binary on 64bit kernels.
It's already off in many distributions including Debian and Fedora for
many years.

Zap arptables first, it's the most esoteric of the 4 flavors.

Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter_arp/arp_tables.h |  19 -
 net/ipv4/netfilter/arp_tables.c          | 472 +----------------------
 2 files changed, 3 insertions(+), 488 deletions(-)

diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h
index 05631a25e622..8b8d472eff34 100644
--- a/include/linux/netfilter_arp/arp_tables.h
+++ b/include/linux/netfilter_arp/arp_tables.h
@@ -56,23 +56,4 @@ void arpt_unregister_table(struct net *net, const char *name);
 extern unsigned int arpt_do_table(void *priv, struct sk_buff *skb,
 				  const struct nf_hook_state *state);
 
-#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
-#include <net/compat.h>
-
-struct compat_arpt_entry {
-	struct arpt_arp arp;
-	__u16 target_offset;
-	__u16 next_offset;
-	compat_uint_t comefrom;
-	struct compat_xt_counters counters;
-	unsigned char elems[];
-};
-
-static inline struct xt_entry_target *
-compat_arpt_get_target(struct compat_arpt_entry *e)
-{
-	return (void *)e + e->target_offset;
-}
-
-#endif /* CONFIG_COMPAT */
 #endif /* _ARPTABLES_H */
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index a87e07e80d0d..db307fa49f3f 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -23,7 +23,6 @@
 #include <linux/init.h>
 #include <linux/mutex.h>
 #include <linux/err.h>
-#include <net/compat.h>
 #include <net/sock.h>
 #include <linux/uaccess.h>
 
@@ -724,80 +723,6 @@ static int copy_entries_to_user(unsigned int total_size,
 	return ret;
 }
 
-#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
-static void compat_standard_from_user(void *dst, const void *src)
-{
-	int v = *(compat_int_t *)src;
-
-	if (v > 0)
-		v += xt_compat_calc_jump(NFPROTO_ARP, v);
-	memcpy(dst, &v, sizeof(v));
-}
-
-static int compat_standard_to_user(void __user *dst, const void *src)
-{
-	compat_int_t cv = *(int *)src;
-
-	if (cv > 0)
-		cv -= xt_compat_calc_jump(NFPROTO_ARP, cv);
-	return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
-}
-
-static int compat_calc_entry(const struct arpt_entry *e,
-			     const struct xt_table_info *info,
-			     const void *base, struct xt_table_info *newinfo)
-{
-	const struct xt_entry_target *t;
-	unsigned int entry_offset;
-	int off, i, ret;
-
-	off = sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry);
-	entry_offset = (void *)e - base;
-
-	t = arpt_get_target_c(e);
-	off += xt_compat_target_offset(t->u.kernel.target);
-	newinfo->size -= off;
-	ret = xt_compat_add_offset(NFPROTO_ARP, entry_offset, off);
-	if (ret)
-		return ret;
-
-	for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
-		if (info->hook_entry[i] &&
-		    (e < (struct arpt_entry *)(base + info->hook_entry[i])))
-			newinfo->hook_entry[i] -= off;
-		if (info->underflow[i] &&
-		    (e < (struct arpt_entry *)(base + info->underflow[i])))
-			newinfo->underflow[i] -= off;
-	}
-	return 0;
-}
-
-static int compat_table_info(const struct xt_table_info *info,
-			     struct xt_table_info *newinfo)
-{
-	struct arpt_entry *iter;
-	const void *loc_cpu_entry;
-	int ret;
-
-	if (!newinfo || !info)
-		return -EINVAL;
-
-	/* we dont care about newinfo->entries */
-	memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
-	newinfo->initial_entries = 0;
-	loc_cpu_entry = info->entries;
-	ret = xt_compat_init_offsets(NFPROTO_ARP, info->number);
-	if (ret)
-		return ret;
-	xt_entry_foreach(iter, loc_cpu_entry, info->size) {
-		ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
-		if (ret != 0)
-			return ret;
-	}
-	return 0;
-}
-#endif
-
 static int get_info(struct net *net, void __user *user, const int *len)
 {
 	char name[XT_TABLE_MAXNAMELEN];
@@ -811,23 +736,11 @@ static int get_info(struct net *net, void __user *user, const int *len)
 		return -EFAULT;
 
 	name[XT_TABLE_MAXNAMELEN-1] = '\0';
-#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
-	if (in_compat_syscall())
-		xt_compat_lock(NFPROTO_ARP);
-#endif
 	t = xt_request_find_table_lock(net, NFPROTO_ARP, name);
 	if (!IS_ERR(t)) {
 		struct arpt_getinfo info;
 		const struct xt_table_info *private = t->private;
-#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
-		struct xt_table_info tmp;
 
-		if (in_compat_syscall()) {
-			ret = compat_table_info(private, &tmp);
-			xt_compat_flush_offsets(NFPROTO_ARP);
-			private = &tmp;
-		}
-#endif
 		memset(&info, 0, sizeof(info));
 		info.valid_hooks = t->valid_hooks;
 		memcpy(info.hook_entry, private->hook_entry,
@@ -846,10 +759,7 @@ static int get_info(struct net *net, void __user *user, const int *len)
 		module_put(t->me);
 	} else
 		ret = PTR_ERR(t);
-#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
-	if (in_compat_syscall())
-		xt_compat_unlock(NFPROTO_ARP);
-#endif
+
 	return ret;
 }
 
@@ -1059,367 +969,6 @@ static int do_add_counters(struct net *net, sockptr_t arg, unsigned int len)
 	return ret;
 }
 
-#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
-struct compat_arpt_replace {
-	char				name[XT_TABLE_MAXNAMELEN];
-	u32				valid_hooks;
-	u32				num_entries;
-	u32				size;
-	u32				hook_entry[NF_ARP_NUMHOOKS];
-	u32				underflow[NF_ARP_NUMHOOKS];
-	u32				num_counters;
-	compat_uptr_t			counters;
-	struct compat_arpt_entry	entries[];
-};
-
-static inline void compat_release_entry(struct compat_arpt_entry *e)
-{
-	struct xt_entry_target *t;
-
-	t = compat_arpt_get_target(e);
-	module_put(t->u.kernel.target->me);
-}
-
-static int
-check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
-				  struct xt_table_info *newinfo,
-				  unsigned int *size,
-				  const unsigned char *base,
-				  const unsigned char *limit)
-{
-	struct xt_entry_target *t;
-	struct xt_target *target;
-	unsigned int entry_offset;
-	int ret, off;
-
-	if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 ||
-	    (unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit ||
-	    (unsigned char *)e + e->next_offset > limit)
-		return -EINVAL;
-
-	if (e->next_offset < sizeof(struct compat_arpt_entry) +
-			     sizeof(struct compat_xt_entry_target))
-		return -EINVAL;
-
-	if (!arp_checkentry(&e->arp))
-		return -EINVAL;
-
-	ret = xt_compat_check_entry_offsets(e, e->elems, e->target_offset,
-					    e->next_offset);
-	if (ret)
-		return ret;
-
-	off = sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry);
-	entry_offset = (void *)e - (void *)base;
-
-	t = compat_arpt_get_target(e);
-	target = xt_request_find_target(NFPROTO_ARP, t->u.user.name,
-					t->u.user.revision);
-	if (IS_ERR(target)) {
-		ret = PTR_ERR(target);
-		goto out;
-	}
-	t->u.kernel.target = target;
-
-	off += xt_compat_target_offset(target);
-	*size += off;
-	ret = xt_compat_add_offset(NFPROTO_ARP, entry_offset, off);
-	if (ret)
-		goto release_target;
-
-	return 0;
-
-release_target:
-	module_put(t->u.kernel.target->me);
-out:
-	return ret;
-}
-
-static void
-compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr,
-			    unsigned int *size,
-			    struct xt_table_info *newinfo, unsigned char *base)
-{
-	struct xt_entry_target *t;
-	struct arpt_entry *de;
-	unsigned int origsize;
-	int h;
-
-	origsize = *size;
-	de = *dstptr;
-	memcpy(de, e, sizeof(struct arpt_entry));
-	memcpy(&de->counters, &e->counters, sizeof(e->counters));
-
-	*dstptr += sizeof(struct arpt_entry);
-	*size += sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry);
-
-	de->target_offset = e->target_offset - (origsize - *size);
-	t = compat_arpt_get_target(e);
-	xt_compat_target_from_user(t, dstptr, size);
-
-	de->next_offset = e->next_offset - (origsize - *size);
-	for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
-		if ((unsigned char *)de - base < newinfo->hook_entry[h])
-			newinfo->hook_entry[h] -= origsize - *size;
-		if ((unsigned char *)de - base < newinfo->underflow[h])
-			newinfo->underflow[h] -= origsize - *size;
-	}
-}
-
-static int translate_compat_table(struct net *net,
-				  struct xt_table_info **pinfo,
-				  void **pentry0,
-				  const struct compat_arpt_replace *compatr)
-{
-	unsigned int i, j;
-	struct xt_table_info *newinfo, *info;
-	void *pos, *entry0, *entry1;
-	struct compat_arpt_entry *iter0;
-	struct arpt_replace repl;
-	unsigned int size;
-	int ret;
-
-	info = *pinfo;
-	entry0 = *pentry0;
-	size = compatr->size;
-	info->number = compatr->num_entries;
-
-	j = 0;
-	xt_compat_lock(NFPROTO_ARP);
-	ret = xt_compat_init_offsets(NFPROTO_ARP, compatr->num_entries);
-	if (ret)
-		goto out_unlock;
-	/* Walk through entries, checking offsets. */
-	xt_entry_foreach(iter0, entry0, compatr->size) {
-		ret = check_compat_entry_size_and_hooks(iter0, info, &size,
-							entry0,
-							entry0 + compatr->size);
-		if (ret != 0)
-			goto out_unlock;
-		++j;
-	}
-
-	ret = -EINVAL;
-	if (j != compatr->num_entries)
-		goto out_unlock;
-
-	ret = -ENOMEM;
-	newinfo = xt_alloc_table_info(size);
-	if (!newinfo)
-		goto out_unlock;
-
-	memset(newinfo->entries, 0, size);
-
-	newinfo->number = compatr->num_entries;
-	for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
-		newinfo->hook_entry[i] = compatr->hook_entry[i];
-		newinfo->underflow[i] = compatr->underflow[i];
-	}
-	entry1 = newinfo->entries;
-	pos = entry1;
-	size = compatr->size;
-	xt_entry_foreach(iter0, entry0, compatr->size)
-		compat_copy_entry_from_user(iter0, &pos, &size,
-					    newinfo, entry1);
-
-	/* all module references in entry0 are now gone */
-
-	xt_compat_flush_offsets(NFPROTO_ARP);
-	xt_compat_unlock(NFPROTO_ARP);
-
-	memcpy(&repl, compatr, sizeof(*compatr));
-
-	for (i = 0; i < NF_ARP_NUMHOOKS; i++) {
-		repl.hook_entry[i] = newinfo->hook_entry[i];
-		repl.underflow[i] = newinfo->underflow[i];
-	}
-
-	repl.num_counters = 0;
-	repl.counters = NULL;
-	repl.size = newinfo->size;
-	ret = translate_table(net, newinfo, entry1, &repl);
-	if (ret)
-		goto free_newinfo;
-
-	*pinfo = newinfo;
-	*pentry0 = entry1;
-	xt_free_table_info(info);
-	return 0;
-
-free_newinfo:
-	xt_free_table_info(newinfo);
-	return ret;
-out_unlock:
-	xt_compat_flush_offsets(NFPROTO_ARP);
-	xt_compat_unlock(NFPROTO_ARP);
-	xt_entry_foreach(iter0, entry0, compatr->size) {
-		if (j-- == 0)
-			break;
-		compat_release_entry(iter0);
-	}
-	return ret;
-}
-
-static int compat_do_replace(struct net *net, sockptr_t arg, unsigned int len)
-{
-	int ret;
-	struct compat_arpt_replace tmp;
-	struct xt_table_info *newinfo;
-	void *loc_cpu_entry;
-	struct arpt_entry *iter;
-
-	if (len < sizeof(tmp))
-		return -EINVAL;
-	if (copy_from_sockptr(&tmp, arg, sizeof(tmp)) != 0)
-		return -EFAULT;
-
-	/* overflow check */
-	if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
-		return -ENOMEM;
-	if (tmp.num_counters == 0)
-		return -EINVAL;
-	if ((u64)len < (u64)tmp.size + sizeof(tmp))
-		return -EINVAL;
-
-	tmp.name[sizeof(tmp.name)-1] = 0;
-
-	newinfo = xt_alloc_table_info(tmp.size);
-	if (!newinfo)
-		return -ENOMEM;
-
-	loc_cpu_entry = newinfo->entries;
-	if (copy_from_sockptr_offset(loc_cpu_entry, arg, sizeof(tmp),
-			tmp.size) != 0) {
-		ret = -EFAULT;
-		goto free_newinfo;
-	}
-
-	ret = translate_compat_table(net, &newinfo, &loc_cpu_entry, &tmp);
-	if (ret != 0)
-		goto free_newinfo;
-
-	ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
-			   tmp.num_counters, compat_ptr(tmp.counters));
-	if (ret)
-		goto free_newinfo_untrans;
-	return 0;
-
- free_newinfo_untrans:
-	xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
-		cleanup_entry(iter, net);
- free_newinfo:
-	xt_free_table_info(newinfo);
-	return ret;
-}
-
-static int compat_copy_entry_to_user(struct arpt_entry *e, void __user **dstptr,
-				     compat_uint_t *size,
-				     struct xt_counters *counters,
-				     unsigned int i)
-{
-	struct xt_entry_target *t;
-	struct compat_arpt_entry __user *ce;
-	u_int16_t target_offset, next_offset;
-	compat_uint_t origsize;
-	int ret;
-
-	origsize = *size;
-	ce = *dstptr;
-	if (copy_to_user(ce, e, offsetof(struct compat_arpt_entry, counters)) ||
-	    copy_to_user(&ce->counters, &counters[i], sizeof(counters[i])))
-		return -EFAULT;
-
-	*dstptr += sizeof(struct compat_arpt_entry);
-	*size -= sizeof(struct arpt_entry) - sizeof(struct compat_arpt_entry);
-
-	target_offset = e->target_offset - (origsize - *size);
-
-	t = arpt_get_target(e);
-	ret = xt_compat_target_to_user(t, dstptr, size);
-	if (ret)
-		return ret;
-	next_offset = e->next_offset - (origsize - *size);
-	if (put_user(target_offset, &ce->target_offset) != 0 ||
-	    put_user(next_offset, &ce->next_offset) != 0)
-		return -EFAULT;
-	return 0;
-}
-
-static int compat_copy_entries_to_user(unsigned int total_size,
-				       struct xt_table *table,
-				       void __user *userptr)
-{
-	struct xt_counters *counters;
-	const struct xt_table_info *private = table->private;
-	void __user *pos;
-	unsigned int size;
-	int ret = 0;
-	unsigned int i = 0;
-	struct arpt_entry *iter;
-
-	counters = alloc_counters(table);
-	if (IS_ERR(counters))
-		return PTR_ERR(counters);
-
-	pos = userptr;
-	size = total_size;
-	xt_entry_foreach(iter, private->entries, total_size) {
-		ret = compat_copy_entry_to_user(iter, &pos,
-						&size, counters, i++);
-		if (ret != 0)
-			break;
-	}
-	vfree(counters);
-	return ret;
-}
-
-struct compat_arpt_get_entries {
-	char name[XT_TABLE_MAXNAMELEN];
-	compat_uint_t size;
-	struct compat_arpt_entry entrytable[];
-};
-
-static int compat_get_entries(struct net *net,
-			      struct compat_arpt_get_entries __user *uptr,
-			      int *len)
-{
-	int ret;
-	struct compat_arpt_get_entries get;
-	struct xt_table *t;
-
-	if (*len < sizeof(get))
-		return -EINVAL;
-	if (copy_from_user(&get, uptr, sizeof(get)) != 0)
-		return -EFAULT;
-	if (*len != sizeof(struct compat_arpt_get_entries) + get.size)
-		return -EINVAL;
-
-	get.name[sizeof(get.name) - 1] = '\0';
-
-	xt_compat_lock(NFPROTO_ARP);
-	t = xt_find_table_lock(net, NFPROTO_ARP, get.name);
-	if (!IS_ERR(t)) {
-		const struct xt_table_info *private = t->private;
-		struct xt_table_info info;
-
-		ret = compat_table_info(private, &info);
-		if (!ret && get.size == info.size) {
-			ret = compat_copy_entries_to_user(private->size,
-							  t, uptr->entrytable);
-		} else if (!ret)
-			ret = -EAGAIN;
-
-		xt_compat_flush_offsets(NFPROTO_ARP);
-		module_put(t->me);
-		xt_table_unlock(t);
-	} else
-		ret = PTR_ERR(t);
-
-	xt_compat_unlock(NFPROTO_ARP);
-	return ret;
-}
-#endif
-
 static int do_arpt_set_ctl(struct sock *sk, int cmd, sockptr_t arg,
 		unsigned int len)
 {
@@ -1432,12 +981,7 @@ static int do_arpt_set_ctl(struct sock *sk, int cmd, sockptr_t arg,
 
 	switch (cmd) {
 	case ARPT_SO_SET_REPLACE:
-#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
-		if (in_compat_syscall())
-			ret = compat_do_replace(sock_net(sk), arg, len);
-		else
-#endif
-			ret = do_replace(sock_net(sk), arg, len);
+		ret = do_replace(sock_net(sk), arg, len);
 		break;
 
 	case ARPT_SO_SET_ADD_COUNTERS:
@@ -1466,12 +1010,7 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len
 		break;
 
 	case ARPT_SO_GET_ENTRIES:
-#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
-		if (in_compat_syscall())
-			ret = compat_get_entries(sock_net(sk), user, len);
-		else
-#endif
-			ret = get_entries(sock_net(sk), user, len);
+		ret = get_entries(sock_net(sk), user, len);
 		break;
 
 	case ARPT_SO_GET_REVISION_TARGET: {
@@ -1568,11 +1107,6 @@ static struct xt_target arpt_builtin_tg[] __read_mostly = {
 		.name             = XT_STANDARD_TARGET,
 		.targetsize       = sizeof(int),
 		.family           = NFPROTO_ARP,
-#ifdef CONFIG_NETFILTER_XTABLES_COMPAT
-		.compatsize       = sizeof(compat_int_t),
-		.compat_from_user = compat_standard_from_user,
-		.compat_to_user   = compat_standard_to_user,
-#endif
 	},
 	{
 		.name             = XT_ERROR_TARGET,
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 11/12] netfilter: ip6_tables: set F_PROTO when proto value is nonzero
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
                   ` (9 preceding siblings ...)
  2026-09-03  0:41 ` [PATCH net 10/12] netfilter: arp_tables: remove the 32bit compat interface Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  2026-09-03  0:41 ` [PATCH net 12/12] netfilter: report NLM_F_DUMP_FILTERED when all is filtered out Pablo Neira Ayuso
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Florian Westphal <fw@strlen.de>

The ip6tables traverser doesn't search the extension header chain unless
userspace did set the IP6T_F_PROTO flag.

This also means that userspace that sets the e->ipv6.proto flag can bypass
the protocol check for the rule by not setting this flag.

That in turn means that all ip6_tables modules and targets that want to
reject rules without '-p' flag MUST also check for that flag.

Not all do, likely because they got copied from iptables which lacks
this flag (no extension headers).

Instead of fixing up all the relevant targets, emulate ip6tables behaviour
in the kernel (like nft_compat.c) and set the flag if the protocol is set.

Reported-by: Zhiling Zou <zhilinz@nebusec.ai>
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/ipv6/netfilter/ip6_tables.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index f42fb96ef64b..313c4aac377a 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -647,6 +647,11 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
 	/* Clear counters and comefrom */
 	e->counters = ((struct xt_counters) { 0, 0 });
 	e->comefrom = 0;
+
+	/* set F_PROTO, else ip6_packet_match won't do the right thing. */
+	if (e->ipv6.proto)
+		e->ipv6.flags |= IP6T_F_PROTO;
+
 	return 0;
 }
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH net 12/12] netfilter: report NLM_F_DUMP_FILTERED when all is filtered out
  2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
                   ` (10 preceding siblings ...)
  2026-09-03  0:41 ` [PATCH net 11/12] netfilter: ip6_tables: set F_PROTO when proto value is nonzero Pablo Neira Ayuso
@ 2026-09-03  0:41 ` Pablo Neira Ayuso
  11 siblings, 0 replies; 13+ messages in thread
From: Pablo Neira Ayuso @ 2026-09-03  0:41 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, kuba, pabeni, edumazet, horms, fw, ja

From: Ilya Maximets <i.maximets@ovn.org>

NLM_F_DUMP_FILTERED is only set on data elements in the conntrack dump.
But when everything is filtered out it is confusing for the user space,
since the flag is not reported anymore and it looks like the table was
empty, which may or may not be the case.

'answer_flags' were introduced precisely for this use case, and the
conntrack dump should set the flag in there in case the filtering was
applied.

This is important, for example, to be able to tell if the filters are
supported or not by the kernel without modifying the kernel state.

With the proper reporting of NLM_F_DUMP_FILTERED on NLMSG_DONE, an
application in user space can just try and dump with an arbitrary
filter without worrying that there could be no matching entry.  The
reported flag will signal that the filtering was applied and therefore
supported.

Fixes: cb8aa9a3affb ("netfilter: ctnetlink: add kernel side filtering for dump")
Cc: stable@vger.kernel.org
Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
Reviewed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_conntrack_netlink.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 9b4e29557ec3..579ada063b1b 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1077,6 +1077,8 @@ static int ctnetlink_start(struct netlink_callback *cb)
 	}
 
 	cb->data = filter;
+	if (filter)
+		cb->answer_flags = NLM_F_DUMP_FILTERED;
 	return 0;
 }
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-09-03  0:42 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-03  0:41 [PATCH net 00/12] Netfilter/IPVS fixes for net Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 01/12] ipvs: reject invalid states in connection template sync records Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 02/12] ipvs: fix reversed sequence option serialization Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 03/12] netfilter: nf_conntrack_sip: fix OOB read in sip_skip_whitespace() Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 04/12] netfilter: cttimeout: prevent UAF during module unload Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 05/12] netfilter: nf_log: unregister loggers before per-net teardown Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 06/12] ipvs: bound LBLCR and LBLC cache growth Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 07/12] netfilter: nft_payload: restrict checksum offsets to known values Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 08/12] netfilter: nfnetlink_log: cope with concurrent instance destruction Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 09/12] netfilter: nfnetlink_queue: hold nfnl mutex in event notifier Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 10/12] netfilter: arp_tables: remove the 32bit compat interface Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 11/12] netfilter: ip6_tables: set F_PROTO when proto value is nonzero Pablo Neira Ayuso
2026-09-03  0:41 ` [PATCH net 12/12] netfilter: report NLM_F_DUMP_FILTERED when all is filtered out Pablo Neira Ayuso

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox