Linux Netfilter development
 help / color / mirror / Atom feed
From: Pablo Neira Ayuso <pablo@netfilter.org>
To: netfilter-devel@vger.kernel.org
Cc: fw@strlen.de
Subject: [PATCH nf-next] netfilter: nfnetlink_queue: hash queue instance by portid too
Date: Thu, 17 Sep 2026 00:00:47 +0200	[thread overview]
Message-ID: <20260916220047.95751-1-pablo@netfilter.org> (raw)

netlink_release() calls netlink_remove() before delivering the
NETLINK_URELEASE event. And a new queue instance can be created before
delivering such NETLINK_URELEASE event. This allows for two different
queue instances with the same portid to co-exist for a little while.
Since the instance that is going away is destroyed by portid via netlink
notifier, both the newly created instance and the one that is going away
with the same portid are destroyed.

Fixes: 7af4cc3fa158 ("[NETFILTER]: Add "nfnetlink_queue" netfilter queue handler over nfnetlink")
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
@Florian: this is the rare race that Clashiko uncover with your fix.
          This is a pre-existing issue. I have to check if nfnetlink_log
	  is also affected.

 net/netfilter/nfnetlink_queue.c | 31 +++++++++++++++++++++++++++++--
 1 file changed, 29 insertions(+), 2 deletions(-)

diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index a3bc00280051..cbb586231fbd 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -69,6 +69,7 @@
 
 struct nfqnl_instance {
 	struct hlist_node hlist;		/* global list of queues */
+	struct hlist_node hlist_portid;		/* global list of queues, by portid */
 	struct rhashtable nfqnl_packet_map;
 	struct rcu_work	rwork;
 
@@ -101,6 +102,7 @@ static unsigned int nfnl_queue_net_id __read_mostly;
 struct nfnl_queue_net {
 	spinlock_t instances_lock;
 	struct hlist_head instance_table[INSTANCE_BUCKETS];
+	struct hlist_head instance_table_portid[INSTANCE_BUCKETS];
 };
 
 static struct nfnl_queue_net *nfnl_queue_pernet(struct net *net)
@@ -113,6 +115,11 @@ static inline u_int8_t instance_hashfn(u_int16_t queue_num)
 	return ((queue_num >> 8) ^ queue_num) % INSTANCE_BUCKETS;
 }
 
+static inline u8 instance_portid_hashfn(u32 portid)
+{
+	return portid % INSTANCE_BUCKETS;
+}
+
 static const struct rhashtable_params nfqnl_rhashtable_params = {
 	.head_offset = offsetof(struct nf_queue_entry, hash_node),
 	.key_offset = offsetof(struct nf_queue_entry, id),
@@ -136,6 +143,20 @@ instance_lookup(struct nfnl_queue_net *q, u_int16_t queue_num)
 	return NULL;
 }
 
+static struct nfqnl_instance *
+instance_lookup_by_portid(struct nfnl_queue_net *q, u32 portid)
+{
+	struct nfqnl_instance *inst;
+	struct hlist_head *head;
+
+	head = &q->instance_table[instance_portid_hashfn(portid)];
+	hlist_for_each_entry(inst, head, hlist) {
+		if (inst->peer_portid == portid)
+			return inst;
+	}
+	return NULL;
+}
+
 static struct nfqnl_instance *
 instance_create(struct nfnl_queue_net *q, u_int16_t queue_num, u32 portid)
 {
@@ -160,7 +181,8 @@ instance_create(struct nfnl_queue_net *q, u_int16_t queue_num, u32 portid)
 		goto out_free;
 
 	spin_lock(&q->instances_lock);
-	if (instance_lookup(q, queue_num)) {
+	if (instance_lookup(q, queue_num) ||
+	    instance_lookup_by_portid(q, queue_num)) {
 		err = -EEXIST;
 		goto out_unlock;
 	}
@@ -172,6 +194,8 @@ instance_create(struct nfnl_queue_net *q, u_int16_t queue_num, u32 portid)
 
 	h = instance_hashfn(queue_num);
 	hlist_add_head_rcu(&inst->hlist, &q->instance_table[h]);
+	h = instance_portid_hashfn(portid);
+	hlist_add_head(&inst->hlist_portid, &q->instance_table_portid[h]);
 
 	spin_unlock(&q->instances_lock);
 
@@ -208,6 +232,7 @@ static void
 __instance_destroy(struct nfqnl_instance *inst)
 {
 	hlist_del_rcu(&inst->hlist);
+	hlist_del(&inst->hlist_portid);
 
 	INIT_RCU_WORK(&inst->rwork, instance_destroy_work);
 	queue_rcu_work(nfq_cleanup_wq, &inst->rwork);
@@ -2121,8 +2146,10 @@ static int __net_init nfnl_queue_net_init(struct net *net)
 	unsigned int i;
 	struct nfnl_queue_net *q = nfnl_queue_pernet(net);
 
-	for (i = 0; i < INSTANCE_BUCKETS; i++)
+	for (i = 0; i < INSTANCE_BUCKETS; i++) {
 		INIT_HLIST_HEAD(&q->instance_table[i]);
+		INIT_HLIST_HEAD(&q->instance_table_portid[i]);
+	}
 
 	spin_lock_init(&q->instances_lock);
 
-- 
2.47.3


             reply	other threads:[~2026-09-16 22:00 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-16 22:00 Pablo Neira Ayuso [this message]
2026-09-17  4:36 ` [PATCH nf-next] netfilter: nfnetlink_queue: hash queue instance by portid too Florian Westphal
2026-09-17  8:54   ` Pablo Neira Ayuso
2026-09-17  9:30     ` Florian Westphal

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260916220047.95751-1-pablo@netfilter.org \
    --to=pablo@netfilter.org \
    --cc=fw@strlen.de \
    --cc=netfilter-devel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox