netfilter: nf_conntrack: move initialization out of pernet operations

nf_conntrack initialization and cleanup codes happens in pernet
operations function. This task should be done in module_init/exit.
We can't use init_net to identify if it's the right time to initialize
or cleanup since we cannot make assumption on the order netns are
created/destroyed.

Signed-off-by: Gao feng <gaofeng@cn.fujitsu.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
Gao feng 2013-01-21 22:10:24 +00:00 committed by Pablo Neira Ayuso
parent 8a454ab95e
commit f94161c1bb
3 changed files with 77 additions and 83 deletions

View File

@ -25,12 +25,16 @@ extern unsigned int nf_conntrack_in(struct net *net,
unsigned int hooknum, unsigned int hooknum,
struct sk_buff *skb); struct sk_buff *skb);
extern int nf_conntrack_init(struct net *net); extern int nf_conntrack_init_net(struct net *net);
extern void nf_conntrack_cleanup(struct net *net); extern void nf_conntrack_cleanup_net(struct net *net);
extern int nf_conntrack_proto_init(struct net *net); extern int nf_conntrack_proto_init(struct net *net);
extern void nf_conntrack_proto_fini(struct net *net); extern void nf_conntrack_proto_fini(struct net *net);
extern int nf_conntrack_init_start(void);
extern void nf_conntrack_cleanup_start(void);
extern void nf_conntrack_init_end(void);
extern void nf_conntrack_cleanup_end(void); extern void nf_conntrack_cleanup_end(void);
extern bool extern bool

View File

@ -1334,8 +1334,14 @@ static int untrack_refs(void)
return cnt; return cnt;
} }
static void nf_conntrack_cleanup_init_net(void) void nf_conntrack_cleanup_start(void)
{ {
RCU_INIT_POINTER(ip_ct_attach, NULL);
}
void nf_conntrack_cleanup_end(void)
{
RCU_INIT_POINTER(nf_ct_destroy, NULL);
while (untrack_refs() > 0) while (untrack_refs() > 0)
schedule(); schedule();
@ -1344,8 +1350,18 @@ static void nf_conntrack_cleanup_init_net(void)
#endif #endif
} }
static void nf_conntrack_cleanup_net(struct net *net) /*
* Mishearing the voices in his head, our hero wonders how he's
* supposed to kill the mall.
*/
void nf_conntrack_cleanup_net(struct net *net)
{ {
/*
* This makes sure all current packets have passed through
* netfilter framework. Roll on, two-stage module
* delete...
*/
synchronize_net();
i_see_dead_people: i_see_dead_people:
nf_ct_iterate_cleanup(net, kill_all, NULL); nf_ct_iterate_cleanup(net, kill_all, NULL);
nf_ct_release_dying_list(net); nf_ct_release_dying_list(net);
@ -1355,6 +1371,7 @@ static void nf_conntrack_cleanup_net(struct net *net)
} }
nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size); nf_ct_free_hashtable(net->ct.hash, net->ct.htable_size);
nf_conntrack_proto_fini(net);
nf_conntrack_labels_fini(net); nf_conntrack_labels_fini(net);
nf_conntrack_helper_fini(net); nf_conntrack_helper_fini(net);
nf_conntrack_timeout_fini(net); nf_conntrack_timeout_fini(net);
@ -1367,27 +1384,6 @@ static void nf_conntrack_cleanup_net(struct net *net)
free_percpu(net->ct.stat); free_percpu(net->ct.stat);
} }
/* Mishearing the voices in his head, our hero wonders how he's
supposed to kill the mall. */
void nf_conntrack_cleanup(struct net *net)
{
if (net_eq(net, &init_net))
RCU_INIT_POINTER(ip_ct_attach, NULL);
/* This makes sure all current packets have passed through
netfilter framework. Roll on, two-stage module
delete... */
synchronize_net();
nf_conntrack_proto_fini(net);
nf_conntrack_cleanup_net(net);
}
void nf_conntrack_cleanup_end(void)
{
RCU_INIT_POINTER(nf_ct_destroy, NULL);
nf_conntrack_cleanup_init_net();
}
void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)
{ {
struct hlist_nulls_head *hash; struct hlist_nulls_head *hash;
@ -1478,7 +1474,7 @@ void nf_ct_untracked_status_or(unsigned long bits)
} }
EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or); EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or);
static int nf_conntrack_init_init_net(void) int nf_conntrack_init_start(void)
{ {
int max_factor = 8; int max_factor = 8;
int ret, cpu; int ret, cpu;
@ -1526,6 +1522,16 @@ err_extend:
return ret; return ret;
} }
void nf_conntrack_init_end(void)
{
/* For use by REJECT target */
RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach);
RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack);
/* Howto get NAT offsets */
RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
}
/* /*
* We need to use special "null" values, not used in hash table * We need to use special "null" values, not used in hash table
*/ */
@ -1533,7 +1539,7 @@ err_extend:
#define DYING_NULLS_VAL ((1<<30)+1) #define DYING_NULLS_VAL ((1<<30)+1)
#define TEMPLATE_NULLS_VAL ((1<<30)+2) #define TEMPLATE_NULLS_VAL ((1<<30)+2)
static int nf_conntrack_init_net(struct net *net) int nf_conntrack_init_net(struct net *net)
{ {
int ret; int ret;
@ -1592,8 +1598,13 @@ static int nf_conntrack_init_net(struct net *net)
if (ret < 0) if (ret < 0)
goto err_labels; goto err_labels;
ret = nf_conntrack_proto_init(net);
if (ret < 0)
goto err_proto;
return 0; return 0;
err_proto:
nf_conntrack_labels_fini(net);
err_labels: err_labels:
nf_conntrack_helper_fini(net); nf_conntrack_helper_fini(net);
err_helper: err_helper:
@ -1622,38 +1633,3 @@ s16 (*nf_ct_nat_offset)(const struct nf_conn *ct,
enum ip_conntrack_dir dir, enum ip_conntrack_dir dir,
u32 seq); u32 seq);
EXPORT_SYMBOL_GPL(nf_ct_nat_offset); EXPORT_SYMBOL_GPL(nf_ct_nat_offset);
int nf_conntrack_init(struct net *net)
{
int ret;
if (net_eq(net, &init_net)) {
ret = nf_conntrack_init_init_net();
if (ret < 0)
goto out_init_net;
}
ret = nf_conntrack_proto_init(net);
if (ret < 0)
goto out_proto;
ret = nf_conntrack_init_net(net);
if (ret < 0)
goto out_net;
if (net_eq(net, &init_net)) {
/* For use by REJECT target */
RCU_INIT_POINTER(ip_ct_attach, nf_conntrack_attach);
RCU_INIT_POINTER(nf_ct_destroy, destroy_conntrack);
/* Howto get NAT offsets */
RCU_INIT_POINTER(nf_ct_nat_offset, NULL);
}
return 0;
out_net:
nf_conntrack_proto_fini(net);
out_proto:
if (net_eq(net, &init_net))
nf_conntrack_cleanup_init_net();
out_init_net:
return ret;
}

View File

@ -472,13 +472,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net)
{ {
struct ctl_table *table; struct ctl_table *table;
if (net_eq(net, &init_net)) {
nf_ct_netfilter_header =
register_net_sysctl(&init_net, "net", nf_ct_netfilter_table);
if (!nf_ct_netfilter_header)
goto out;
}
table = kmemdup(nf_ct_sysctl_table, sizeof(nf_ct_sysctl_table), table = kmemdup(nf_ct_sysctl_table, sizeof(nf_ct_sysctl_table),
GFP_KERNEL); GFP_KERNEL);
if (!table) if (!table)
@ -502,10 +495,6 @@ static int nf_conntrack_standalone_init_sysctl(struct net *net)
out_unregister_netfilter: out_unregister_netfilter:
kfree(table); kfree(table);
out_kmemdup: out_kmemdup:
if (net_eq(net, &init_net))
unregister_net_sysctl_table(nf_ct_netfilter_header);
out:
printk(KERN_ERR "nf_conntrack: can't register to sysctl.\n");
return -ENOMEM; return -ENOMEM;
} }
@ -513,8 +502,6 @@ static void nf_conntrack_standalone_fini_sysctl(struct net *net)
{ {
struct ctl_table *table; struct ctl_table *table;
if (net_eq(net, &init_net))
unregister_net_sysctl_table(nf_ct_netfilter_header);
table = net->ct.sysctl_header->ctl_table_arg; table = net->ct.sysctl_header->ctl_table_arg;
unregister_net_sysctl_table(net->ct.sysctl_header); unregister_net_sysctl_table(net->ct.sysctl_header);
kfree(table); kfree(table);
@ -530,51 +517,78 @@ static void nf_conntrack_standalone_fini_sysctl(struct net *net)
} }
#endif /* CONFIG_SYSCTL */ #endif /* CONFIG_SYSCTL */
static int nf_conntrack_net_init(struct net *net) static int nf_conntrack_pernet_init(struct net *net)
{ {
int ret; int ret;
ret = nf_conntrack_init(net); ret = nf_conntrack_init_net(net);
if (ret < 0) if (ret < 0)
goto out_init; goto out_init;
ret = nf_conntrack_standalone_init_proc(net); ret = nf_conntrack_standalone_init_proc(net);
if (ret < 0) if (ret < 0)
goto out_proc; goto out_proc;
net->ct.sysctl_checksum = 1; net->ct.sysctl_checksum = 1;
net->ct.sysctl_log_invalid = 0; net->ct.sysctl_log_invalid = 0;
ret = nf_conntrack_standalone_init_sysctl(net); ret = nf_conntrack_standalone_init_sysctl(net);
if (ret < 0) if (ret < 0)
goto out_sysctl; goto out_sysctl;
return 0; return 0;
out_sysctl: out_sysctl:
nf_conntrack_standalone_fini_proc(net); nf_conntrack_standalone_fini_proc(net);
out_proc: out_proc:
nf_conntrack_cleanup(net); nf_conntrack_cleanup_net(net);
out_init: out_init:
return ret; return ret;
} }
static void nf_conntrack_net_exit(struct net *net) static void nf_conntrack_pernet_exit(struct net *net)
{ {
nf_conntrack_standalone_fini_sysctl(net); nf_conntrack_standalone_fini_sysctl(net);
nf_conntrack_standalone_fini_proc(net); nf_conntrack_standalone_fini_proc(net);
nf_conntrack_cleanup(net); nf_conntrack_cleanup_net(net);
} }
static struct pernet_operations nf_conntrack_net_ops = { static struct pernet_operations nf_conntrack_net_ops = {
.init = nf_conntrack_net_init, .init = nf_conntrack_pernet_init,
.exit = nf_conntrack_net_exit, .exit = nf_conntrack_pernet_exit,
}; };
static int __init nf_conntrack_standalone_init(void) static int __init nf_conntrack_standalone_init(void)
{ {
return register_pernet_subsys(&nf_conntrack_net_ops); int ret = nf_conntrack_init_start();
if (ret < 0)
goto out_start;
nf_ct_netfilter_header =
register_net_sysctl(&init_net, "net", nf_ct_netfilter_table);
if (!nf_ct_netfilter_header)
goto out_sysctl;
ret = register_pernet_subsys(&nf_conntrack_net_ops);
if (ret < 0)
goto out_pernet;
nf_conntrack_init_end();
return 0;
out_pernet:
unregister_net_sysctl_table(nf_ct_netfilter_header);
out_sysctl:
pr_err("nf_conntrack: can't register to sysctl.\n");
nf_conntrack_cleanup_end();
out_start:
return ret;
} }
static void __exit nf_conntrack_standalone_fini(void) static void __exit nf_conntrack_standalone_fini(void)
{ {
nf_conntrack_cleanup_start();
unregister_pernet_subsys(&nf_conntrack_net_ops); unregister_pernet_subsys(&nf_conntrack_net_ops);
unregister_net_sysctl_table(nf_ct_netfilter_header);
nf_conntrack_cleanup_end(); nf_conntrack_cleanup_end();
} }