From: Liping Zhang <zlpnobody@xxxxxxxxx> First, we should make the global nfnl_cthelper_list become per-net, so different netns's user cthelpers will be linked to the different global lists. Second, when we do the netns cleanup work, we may invoke the nfnl_cthelper_net_exit and nf_conntrack_helper_put in different orders, so we should free the cthelper only when the refcnt is decreased to 0, this is similar to nfnetlink_cttimeout. Signed-off-by: Liping Zhang <zlpnobody@xxxxxxxxx> --- include/net/netfilter/nf_conntrack_helper.h | 10 +++++ include/net/netns/conntrack.h | 3 ++ net/netfilter/nf_conntrack_helper.c | 2 +- net/netfilter/nfnetlink_cthelper.c | 68 ++++++++++++++++++++--------- 4 files changed, 61 insertions(+), 22 deletions(-) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 7ac67c4..a63451f 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -82,6 +82,16 @@ static inline struct net *nf_ct_helper_net(struct nf_conntrack_helper *helper) return read_pnet(&helper->net); } +static inline void nf_ct_helper_put(struct nf_conntrack_helper *helper) +{ + if (refcount_dec_and_test(&helper->refcnt)) { + if (helper->flags & NF_CT_HELPER_F_USERSPACE) { + kfree(helper->expect_policy); + kfree(helper); + } + } +} + struct nf_conntrack_helper *__nf_conntrack_helper_find(struct net *net, const char *name, u16 l3num, u8 protonum); diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h index 244b794..8b1d2f9 100644 --- a/include/net/netns/conntrack.h +++ b/include/net/netns/conntrack.h @@ -111,6 +111,9 @@ struct netns_ct { int sysctl_tstamp; int sysctl_checksum; +#if IS_ENABLED(CONFIG_NF_CT_NETLINK_HELPER) + struct list_head nfnl_cthelper_list; +#endif unsigned int nf_ct_helper_count; struct ct_pcpu __percpu *pcpu_lists; diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 3f3eeb9..248310d 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -201,8 +201,8 @@ EXPORT_SYMBOL_GPL(nf_conntrack_helper_try_module_get); void nf_conntrack_helper_put(struct nf_conntrack_helper *helper) { - refcount_dec(&helper->refcnt); module_put(helper->me); + nf_ct_helper_put(helper); } EXPORT_SYMBOL_GPL(nf_conntrack_helper_put); diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index 90603b1..5c4a45a 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -33,12 +33,10 @@ MODULE_AUTHOR("Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>"); MODULE_DESCRIPTION("nfnl_cthelper: User-space connection tracking helpers"); struct nfnl_cthelper { - struct list_head list; struct nf_conntrack_helper helper; + struct list_head list; }; -static LIST_HEAD(nfnl_cthelper_list); - static int nfnl_userspace_cthelper(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo) @@ -214,7 +212,8 @@ nfnl_cthelper_parse_expect_policy(struct nf_conntrack_helper *helper, static int nfnl_cthelper_create(const struct nlattr * const tb[], - struct nf_conntrack_tuple *tuple) + struct nf_conntrack_tuple *tuple, + struct net *net) { struct nf_conntrack_helper *helper; struct nfnl_cthelper *nfcth; @@ -265,11 +264,11 @@ nfnl_cthelper_create(const struct nlattr * const tb[], } } - ret = nf_conntrack_helper_register(&init_net, helper); + ret = nf_conntrack_helper_register(net, helper); if (ret < 0) goto err2; - list_add_tail(&nfcth->list, &nfnl_cthelper_list); + list_add_tail(&nfcth->list, &net->ct.nfnl_cthelper_list); return 0; err2: kfree(helper->expect_policy); @@ -415,7 +414,7 @@ static int nfnl_cthelper_new(struct net *net, struct sock *nfnl, if (ret < 0) return ret; - list_for_each_entry(nlcth, &nfnl_cthelper_list, list) { + list_for_each_entry(nlcth, &net->ct.nfnl_cthelper_list, list) { cur = &nlcth->helper; if (strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN)) @@ -433,7 +432,7 @@ static int nfnl_cthelper_new(struct net *net, struct sock *nfnl, } if (helper == NULL) - ret = nfnl_cthelper_create(tb, &tuple); + ret = nfnl_cthelper_create(tb, &tuple, net); else ret = nfnl_cthelper_update(tb, helper); @@ -561,6 +560,7 @@ static int nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb) { struct nf_conntrack_helper *cur, *last; + struct net *net = sock_net(skb->sk); rcu_read_lock(); last = (struct nf_conntrack_helper *)cb->args[1]; @@ -568,6 +568,8 @@ nfnl_cthelper_dump_table(struct sk_buff *skb, struct netlink_callback *cb) restart: hlist_for_each_entry_rcu(cur, &nf_ct_helper_hash[cb->args[0]], hnode) { + if (!net_eq(net, nf_ct_helper_net(cur))) + continue; /* skip non-userspace conntrack helpers. */ if (!(cur->flags & NF_CT_HELPER_F_USERSPACE)) @@ -627,7 +629,7 @@ static int nfnl_cthelper_get(struct net *net, struct sock *nfnl, tuple_set = true; } - list_for_each_entry(nlcth, &nfnl_cthelper_list, list) { + list_for_each_entry(nlcth, &net->ct.nfnl_cthelper_list, list) { cur = &nlcth->helper; if (helper_name && strncmp(cur->name, helper_name, NF_CT_HELPER_NAME_LEN)) @@ -687,7 +689,7 @@ static int nfnl_cthelper_del(struct net *net, struct sock *nfnl, } ret = -ENOENT; - list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) { + list_for_each_entry_safe(nlcth, n, &net->ct.nfnl_cthelper_list, list) { cur = &nlcth->helper; j++; @@ -743,34 +745,58 @@ static const struct nfnetlink_subsystem nfnl_cthelper_subsys = { MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_CTHELPER); +static int __net_init nfnl_cthelper_net_init(struct net *net) +{ + INIT_LIST_HEAD(&net->ct.nfnl_cthelper_list); + return 0; +} + +static void __net_exit nfnl_cthelper_net_exit(struct net *net) +{ + struct nf_conntrack_helper *cur; + struct nfnl_cthelper *nlcth, *n; + + list_for_each_entry_safe(nlcth, n, &net->ct.nfnl_cthelper_list, list) { + cur = &nlcth->helper; + + nf_conntrack_helper_unregister(net, cur); + list_del(&nlcth->list); + + nf_ct_helper_put(cur); + } +} + +static struct pernet_operations nfnl_cthelper_net_ops = { + .init = nfnl_cthelper_net_init, + .exit = nfnl_cthelper_net_exit, +}; + static int __init nfnl_cthelper_init(void) { int ret; + BUILD_BUG_ON(offsetof(struct nfnl_cthelper, helper) != 0); + + ret = register_pernet_subsys(&nfnl_cthelper_net_ops); + if (ret < 0) + return ret; + ret = nfnetlink_subsys_register(&nfnl_cthelper_subsys); if (ret < 0) { pr_err("nfnl_cthelper: cannot register with nfnetlink.\n"); goto err_out; } return 0; + err_out: + unregister_pernet_subsys(&nfnl_cthelper_net_ops); return ret; } static void __exit nfnl_cthelper_exit(void) { - struct nf_conntrack_helper *cur; - struct nfnl_cthelper *nlcth, *n; - nfnetlink_subsys_unregister(&nfnl_cthelper_subsys); - - list_for_each_entry_safe(nlcth, n, &nfnl_cthelper_list, list) { - cur = &nlcth->helper; - - nf_conntrack_helper_unregister(&init_net, cur); - kfree(cur->expect_policy); - kfree(nlcth); - } + unregister_pernet_subsys(&nfnl_cthelper_net_ops); } module_init(nfnl_cthelper_init); -- 2.5.5 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html