@@ -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,13 @@ 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)
+{
+ unsigned int helper_flags = READ_ONCE(helper->flags);
+
+ 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
@@ -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)) {
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (help)
rcu_assign_pointer(help->helper, assign_helper);
@@ -418,8 +418,11 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me)
nf_ct_helper_count--;
mutex_unlock(&nf_ct_helper_mutex);
+ WRITE_ONCE(me->flags, me->flags | NF_CT_HELPER_F_DEAD);
+
/* 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();
@@ -28,6 +28,9 @@ int nf_ct_helper(struct sk_buff *skb, struct nf_conn *ct,
if (!helper)
return NF_ACCEPT;
+ if (!nf_ct_helper_alive(helper))
+ return NF_ACCEPT;
+
if (helper->tuple.src.l3num != NFPROTO_UNSPEC &&
helper->tuple.src.l3num != proto)
return NF_ACCEPT;
@@ -174,7 +174,7 @@ unsigned int nf_confirm(void *priv,
/* rcu_read_lock()ed by nf_hook */
helper = rcu_dereference(help->helper);
- if (helper) {
+ if (helper && nf_ct_helper_alive(helper)) {
ret = helper->help(skb,
protoff,
ct, ctinfo);
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. nf_conntrack_helper_unregister() is only called under nfnl_mutex when unregistering the helper, else by helper module which already owns this helper from the module removal path, therefore, concurrent update of helper flags via nfnetlink_cthelper is not possible, but packet path and netlink dump path can read these flags locklessly. 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> --- v4: no changes AI does not understand ct->gen_id check in __nf_ct_ext_find() ensure access to stale helper area is not possible. include/net/netfilter/nf_conntrack_helper.h | 8 ++++++++ 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, 17 insertions(+), 3 deletions(-)