Commit 226c0c0e authored by Pablo Neira Ayuso's avatar Pablo Neira Ayuso Committed by Patrick McHardy

netfilter: ctnetlink: helper modules load-on-demand support

This patch adds module loading for helpers via ctnetlink.

* Creation path: We support explicit and implicit helper assignation. For
  the explicit case, we try to load the module. If the module is correctly
  loaded and the helper is present, we return EAGAIN to re-start the
  creation. Otherwise, we return EOPNOTSUPP.
* Update path: release the spin lock, load the module and check. If it is
  present, then return EAGAIN to re-start the update.

This patch provides a refactorized function to lookup-and-set the
connection tracking helper. The function removes the exported symbol
__nf_ct_helper_find as it has not clients anymore.
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
parent 4dc06f96
...@@ -38,9 +38,6 @@ struct nf_conntrack_helper ...@@ -38,9 +38,6 @@ struct nf_conntrack_helper
unsigned int expect_class_max; unsigned int expect_class_max;
}; };
extern struct nf_conntrack_helper *
__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple);
extern struct nf_conntrack_helper * extern struct nf_conntrack_helper *
__nf_conntrack_helper_find_byname(const char *name); __nf_conntrack_helper_find_byname(const char *name);
...@@ -49,6 +46,8 @@ extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *); ...@@ -49,6 +46,8 @@ extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *);
extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp); extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp);
extern int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags);
static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct) static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct)
{ {
return nf_ct_ext_find(ct, NF_CT_EXT_HELPER); return nf_ct_ext_find(ct, NF_CT_EXT_HELPER);
......
...@@ -588,14 +588,7 @@ init_conntrack(struct net *net, ...@@ -588,14 +588,7 @@ init_conntrack(struct net *net,
nf_conntrack_get(&ct->master->ct_general); nf_conntrack_get(&ct->master->ct_general);
NF_CT_STAT_INC(net, expect_new); NF_CT_STAT_INC(net, expect_new);
} else { } else {
struct nf_conntrack_helper *helper; __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
helper = __nf_ct_helper_find(&repl_tuple);
if (helper) {
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (help)
rcu_assign_pointer(help->helper, helper);
}
NF_CT_STAT_INC(net, new); NF_CT_STAT_INC(net, new);
} }
...@@ -772,7 +765,6 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, ...@@ -772,7 +765,6 @@ void nf_conntrack_alter_reply(struct nf_conn *ct,
const struct nf_conntrack_tuple *newreply) const struct nf_conntrack_tuple *newreply)
{ {
struct nf_conn_help *help = nfct_help(ct); struct nf_conn_help *help = nfct_help(ct);
struct nf_conntrack_helper *helper;
/* Should be unconfirmed, so not in hash table yet */ /* Should be unconfirmed, so not in hash table yet */
NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); NF_CT_ASSERT(!nf_ct_is_confirmed(ct));
...@@ -785,23 +777,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, ...@@ -785,23 +777,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct,
return; return;
rcu_read_lock(); rcu_read_lock();
helper = __nf_ct_helper_find(newreply); __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
if (helper == NULL) {
if (help)
rcu_assign_pointer(help->helper, NULL);
goto out;
}
if (help == NULL) {
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (help == NULL)
goto out;
} else {
memset(&help->help, 0, sizeof(help->help));
}
rcu_assign_pointer(help->helper, helper);
out:
rcu_read_unlock(); rcu_read_unlock();
} }
EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply); EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply);
......
...@@ -44,7 +44,7 @@ static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple) ...@@ -44,7 +44,7 @@ static unsigned int helper_hash(const struct nf_conntrack_tuple *tuple)
(__force __u16)tuple->src.u.all) % nf_ct_helper_hsize; (__force __u16)tuple->src.u.all) % nf_ct_helper_hsize;
} }
struct nf_conntrack_helper * static struct nf_conntrack_helper *
__nf_ct_helper_find(const struct nf_conntrack_tuple *tuple) __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
{ {
struct nf_conntrack_helper *helper; struct nf_conntrack_helper *helper;
...@@ -62,7 +62,6 @@ __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple) ...@@ -62,7 +62,6 @@ __nf_ct_helper_find(const struct nf_conntrack_tuple *tuple)
} }
return NULL; return NULL;
} }
EXPORT_SYMBOL_GPL(__nf_ct_helper_find);
struct nf_conntrack_helper * struct nf_conntrack_helper *
__nf_conntrack_helper_find_byname(const char *name) __nf_conntrack_helper_find_byname(const char *name)
...@@ -94,6 +93,35 @@ struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp) ...@@ -94,6 +93,35 @@ struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, gfp_t gfp)
} }
EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add); EXPORT_SYMBOL_GPL(nf_ct_helper_ext_add);
int __nf_ct_try_assign_helper(struct nf_conn *ct, gfp_t flags)
{
int ret = 0;
struct nf_conntrack_helper *helper;
struct nf_conn_help *help = nfct_help(ct);
helper = __nf_ct_helper_find(&ct->tuplehash[IP_CT_DIR_REPLY].tuple);
if (helper == NULL) {
if (help)
rcu_assign_pointer(help->helper, NULL);
goto out;
}
if (help == NULL) {
help = nf_ct_helper_ext_add(ct, flags);
if (help == NULL) {
ret = -ENOMEM;
goto out;
}
} else {
memset(&help->help, 0, sizeof(help->help));
}
rcu_assign_pointer(help->helper, helper);
out:
return ret;
}
EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
static inline int unhelp(struct nf_conntrack_tuple_hash *i, static inline int unhelp(struct nf_conntrack_tuple_hash *i,
const struct nf_conntrack_helper *me) const struct nf_conntrack_helper *me)
{ {
......
...@@ -917,8 +917,22 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[]) ...@@ -917,8 +917,22 @@ ctnetlink_change_helper(struct nf_conn *ct, struct nlattr *cda[])
} }
helper = __nf_conntrack_helper_find_byname(helpname); helper = __nf_conntrack_helper_find_byname(helpname);
if (helper == NULL) if (helper == NULL) {
#ifdef CONFIG_MODULES
spin_unlock_bh(&nf_conntrack_lock);
if (request_module("nfct-helper-%s", helpname) < 0) {
spin_lock_bh(&nf_conntrack_lock);
return -EOPNOTSUPP;
}
spin_lock_bh(&nf_conntrack_lock);
helper = __nf_conntrack_helper_find_byname(helpname);
if (helper)
return -EAGAIN;
#endif
return -EOPNOTSUPP; return -EOPNOTSUPP;
}
if (help) { if (help) {
if (help->helper == helper) if (help->helper == helper)
...@@ -1082,7 +1096,6 @@ ctnetlink_create_conntrack(struct nlattr *cda[], ...@@ -1082,7 +1096,6 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
{ {
struct nf_conn *ct; struct nf_conn *ct;
int err = -EINVAL; int err = -EINVAL;
struct nf_conn_help *help;
struct nf_conntrack_helper *helper; struct nf_conntrack_helper *helper;
ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_KERNEL); ct = nf_conntrack_alloc(&init_net, otuple, rtuple, GFP_KERNEL);
...@@ -1097,17 +1110,56 @@ ctnetlink_create_conntrack(struct nlattr *cda[], ...@@ -1097,17 +1110,56 @@ ctnetlink_create_conntrack(struct nlattr *cda[],
ct->status |= IPS_CONFIRMED; ct->status |= IPS_CONFIRMED;
rcu_read_lock(); rcu_read_lock();
helper = __nf_ct_helper_find(rtuple); if (cda[CTA_HELP]) {
char *helpname;
err = ctnetlink_parse_help(cda[CTA_HELP], &helpname);
if (err < 0) {
rcu_read_unlock();
goto err;
}
helper = __nf_conntrack_helper_find_byname(helpname);
if (helper == NULL) {
rcu_read_unlock();
#ifdef CONFIG_MODULES
if (request_module("nfct-helper-%s", helpname) < 0) {
err = -EOPNOTSUPP;
goto err;
}
rcu_read_lock();
helper = __nf_conntrack_helper_find_byname(helpname);
if (helper) { if (helper) {
rcu_read_unlock();
err = -EAGAIN;
goto err;
}
rcu_read_unlock();
#endif
err = -EOPNOTSUPP;
goto err;
} else {
struct nf_conn_help *help;
help = nf_ct_helper_ext_add(ct, GFP_ATOMIC); help = nf_ct_helper_ext_add(ct, GFP_ATOMIC);
if (help == NULL) { if (help == NULL) {
rcu_read_unlock(); rcu_read_unlock();
err = -ENOMEM; err = -ENOMEM;
goto err; goto err;
} }
/* not in hash table yet so not strictly necessary */ /* not in hash table yet so not strictly necessary */
rcu_assign_pointer(help->helper, helper); rcu_assign_pointer(help->helper, helper);
} }
} else {
/* try an implicit helper assignation */
err = __nf_ct_try_assign_helper(ct, GFP_ATOMIC);
if (err < 0) {
rcu_read_unlock();
goto err;
}
}
if (cda[CTA_STATUS]) { if (cda[CTA_STATUS]) {
err = ctnetlink_change_status(ct, cda); err = ctnetlink_change_status(ct, cda);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment