From: Pablo Neira Ayuso <pablo@netfilter.org>
To: netfilter-devel@vger.kernel.org
Cc: fw@strlen.de
Subject: Re: [PATCH nf,v3] netfilter: nfnetlink_queue: hash queue instance by portid too
Date: Wed, 23 Sep 2026 22:04:05 +0200 [thread overview]
Message-ID: <arQwtSh6BNUZ8BsJ@chamomile> (raw)
In-Reply-To: <20260923184047.48492-1-pablo@netfilter.org>
On Wed, Sep 23, 2026 at 08:40:46PM +0200, Pablo Neira Ayuso wrote:
> 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.
It seems I cannot fix this in this way. I forgot same socket can be
bound to several queues.
> Fixes: 7af4cc3fa158 ("[NETFILTER]: Add "nfnetlink_queue" netfilter queue handler over nfnetlink")
> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> ---
> v3: - use siphash instead of lazy hash
> - fix incorrect hlist and wrong instance_lookup_by_portid()
>
> net/netfilter/nfnetlink_queue.c | 36 +++++++++++++++++++++++++++++++--
> 1 file changed, 34 insertions(+), 2 deletions(-)
>
> diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
> index c727668b0c5b..a2086c192063 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;
>
> @@ -96,11 +97,13 @@ typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long);
>
> static struct workqueue_struct *nfq_cleanup_wq __read_mostly;
> static unsigned int nfnl_queue_net_id __read_mostly;
> +static siphash_aligned_key_t nfnl_queue_hash_key;
>
> #define INSTANCE_BUCKETS 16
> 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 +116,15 @@ 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)
> +{
> + u64 hash = siphash_1u32(portid, &nfnl_queue_hash_key);
> +
> + net_get_random_once(&nfnl_queue_hash_key, sizeof(nfnl_queue_hash_key));
> +
> + return reciprocal_scale(hash, 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 +148,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_portid[instance_portid_hashfn(portid)];
> + hlist_for_each_entry(inst, head, hlist_portid) {
> + 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 +186,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) ||
> + unlikely(instance_lookup_by_portid(q, portid))) {
> err = -EEXIST;
> goto out_unlock;
> }
> @@ -172,6 +199,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 +237,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);
> @@ -2119,8 +2149,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
>
>
prev parent reply other threads:[~2026-09-23 20:04 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-23 18:40 [PATCH nf,v3] netfilter: nfnetlink_queue: hash queue instance by portid too Pablo Neira Ayuso
2026-09-23 20:04 ` Pablo Neira Ayuso [this message]
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=arQwtSh6BNUZ8BsJ@chamomile \
--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