Linux Netfilter development
 help / color / mirror / Atom feed
From: Pablo Neira Ayuso <pablo@netfilter.org>
To: Florian Westphal <fw@strlen.de>
Cc: netfilter-devel@vger.kernel.org
Subject: Re: [PATCH nf] netfilter: conntrack: add dead flag to helpers
Date: Wed, 13 May 2026 17:38:40 +0200	[thread overview]
Message-ID: <agSbAFn3wN-sU6uV@chamomile> (raw)
In-Reply-To: <agSY1PyVKRhf4zDc@chamomile>

On Wed, May 13, 2026 at 05:29:27PM +0200, Pablo Neira Ayuso wrote:
> On Wed, May 13, 2026 at 12:05:02PM +0200, Florian Westphal wrote:
> > Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> > > Add a new NF_CT_HELPER_F_DEAD helper flag to notify the packet path that
> > > this helper is going away. Thus, helpers are effectively disabled and no
> > > new expectations are created while removing the expectations created by
> > > this helper as well as unhelping the existing conntrack entries.
> > > 
> > > Add the check for NF_CT_HELPER_F_DEAD in the packet path to:
> > > - Conntrack confirmation path which invokes the helper callback.
> > > - Propagation of helper to conntrack via expectation.
> > > - OVS ct helper invocation.
> > > 
> > > Fixes: 12f7a505331e ("netfilter: add user-space connection tracking helper infrastructure")
> > > Reported-by: Florian Westphal <fw@strlen.de>
> > > Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
> > > ---
> > >  include/net/netfilter/nf_conntrack_helper.h | 6 ++++++
> > >  net/netfilter/nf_conntrack_core.c           | 2 +-
> > >  net/netfilter/nf_conntrack_helper.c         | 5 ++++-
> > >  net/netfilter/nf_conntrack_ovs.c            | 3 +++
> > >  net/netfilter/nf_conntrack_proto.c          | 2 +-
> > >  5 files changed, 15 insertions(+), 3 deletions(-)
> > > 
> > > diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h
> > > index de2f956abf34..1faa42efe42e 100644
> > > --- a/include/net/netfilter/nf_conntrack_helper.h
> > > +++ b/include/net/netfilter/nf_conntrack_helper.h
> > > @@ -25,6 +25,7 @@ struct module;
> > >  enum nf_ct_helper_flags {
> > >  	NF_CT_HELPER_F_USERSPACE	= (1 << 0),
> > >  	NF_CT_HELPER_F_CONFIGURED	= (1 << 1),
> > > +	NF_CT_HELPER_F_DEAD		= (1 << 2),
> > >  };
> > >  
> > >  #define NF_CT_HELPER_NAME_LEN	16
> > > @@ -63,6 +64,11 @@ struct nf_conntrack_helper {
> > >  	char nat_mod_name[NF_CT_HELPER_NAME_LEN];
> > >  };
> > >  
> > > +static inline bool nf_ct_helper_alive(const struct nf_conntrack_helper *helper)
> > > +{
> > > +	return likely(!(helper->flags & NF_CT_HELPER_F_DEAD));
> > > +}
> > > +
> > >  /* Must be kept in sync with the classes defined by helpers */
> > >  #define NF_CT_MAX_EXPECT_CLASSES	4
> > >  
> > > diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
> > > index 8ba5b22a1eef..d54da6babcfe 100644
> > > --- a/net/netfilter/nf_conntrack_core.c
> > > +++ b/net/netfilter/nf_conntrack_core.c
> > > @@ -1818,7 +1818,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
> > >  			/* exp->master safe, refcnt bumped in nf_ct_find_expectation */
> > >  			ct->master = exp->master;
> > >  			assign_helper = rcu_dereference(exp->assign_helper);
> > > -			if (assign_helper) {
> > > +			if (assign_helper && nf_ct_helper_alive(assign_helper)) {
> > 
> > At this time, the new ct isn't in any hash.  As-is, I don't think this
> > will guarantee such nfct canot escape.  See below.
> > 
> > >  				help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
> > >  				if (help)
> > >  					rcu_assign_pointer(help->helper, assign_helper);
> > > diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
> > > index b594cd244fe1..b3752ccca75e 100644
> > > --- a/net/netfilter/nf_conntrack_helper.c
> > > +++ b/net/netfilter/nf_conntrack_helper.c
> > > @@ -415,8 +415,11 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
> > >  	nf_ct_helper_count--;
> > >  	mutex_unlock(&nf_ct_helper_mutex);
> > >  
> > > +	me->flags |= NF_CT_HELPER_F_DEAD;
> > > +
> > 
> > Does this need to be toggled while under lock?
> > I don't think synchronize_rcu() is a barrier.
> > 
> > Also, it looks like this can be racing with nfnl_cthelper_update().
> > We probably need to add some new lock, or reuse existing one like
> > nf_ct_helper_mutex, or expectation spinlock.
> > 
> > >  	/* Make sure every nothing is still using the helper unless its a
> > > -	 * connection in the hash.
> > > +	 * connection in the hash, no more expectations are created after
> > > +	 * this rcu grace period.
> > >  	 */
> > >  	synchronize_rcu();
> > 
> > ... that makes things wait until we leave rcu protection.
> > I think we should also drop nfqueued packets here to make sure
> > they can't be reinjected.
> 
> See below, at the end of this email.
> 
> > Also, should __nf_ct_expect_check() also call nf_ct_helper_alive()
> > and refuse insertion of such exp into the table?
> > 
> > That would give following unreg sequence:
> > 1. Unlink from hash
> > 2. set flag -> prevent concurrent nf_ct_expect_related() from
> >    adding more expectations to the exp table
> 
> My understanding is that during the rcu grace period, packets
> might keep walking over the helper function and create an
> expectations. These expectations will be destroyed by
> nf_ct_expect_iterate_destroy(). 
> 
> But after the rcu grace period, new packets will start seeing the
> helper dead flag, hence skipping the helper logic / no new expectation
> is created. And the existing expectation cannot be reached, because
> _find_expect() is disabled.
> 
> > 3. synchronize_rcu() -> all skbs that had this helper have
> >    left RCU protection
> > 4. nf_ct_expect_iterate_destroy() removes all not-yet-found
> >    exp entries from table
> > 5. nf_ct_iterate_destroy() -> clear exp from nf_conn's that are
> >    *in conntrack table*
> > 
> > That still means we could have a NEW conntrack queued via nfqueue.
> > I think we also need to toss nfqueued packets after step 3) and
> > need to refuse queueing to userspace if the flag is set (-> drop).
> 
> Those conntrack entries would now have help->helper == NULL because of
> the unhelp call.

Oh, wait, _NEW_ conntracks are unreachable, so yes, that can happen.

Tossing nfqueued packets here is convenient when helper goes away.

> > We could have several nfqueue back to back:
> > -t mangle -A PREROUTING -j NFQUEUE
> > -t mangle -A FORWARD -j NFQUEUE
> > 
> > ... and each synchronize_rcu() might advance skb to next
> > 'queue' instead of nf_confirm().
> > 
> > But I think that this a good direction, I think its better than my
> > rather destructive temporarily-block-all-exps idea.
> 
> Thanks for your feedback, let me know if I still don't see anything
> obvious.

  reply	other threads:[~2026-05-13 15:38 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-12 20:58 [PATCH nf] netfilter: conntrack: add dead flag to helpers Pablo Neira Ayuso
2026-05-13 10:05 ` Florian Westphal
2026-05-13 15:29   ` Pablo Neira Ayuso
2026-05-13 15:38     ` Pablo Neira Ayuso [this message]
2026-05-13 15:52       ` Pablo Neira Ayuso

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=agSbAFn3wN-sU6uV@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