diff options
author | Patrick McHardy <kaber@trash.net> | 2010-02-08 14:18:07 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-08 14:18:07 -0500 |
commit | d696c7bdaa55e2208e56c6f98e6bc1599f34286d (patch) | |
tree | 628782197c21b1e8611a41914865cdba586a1c65 /net/ipv4 | |
parent | 14c7dbe043d01a83a30633ab6b109ba2ac61d9f7 (diff) |
netfilter: nf_conntrack: fix hash resizing with namespaces
As noticed by Jon Masters <jonathan@jonmasters.org>, the conntrack hash
size is global and not per namespace, but modifiable at runtime through
/sys/module/nf_conntrack/hashsize. Changing the hash size will only
resize the hash in the current namespace however, so other namespaces
will use an invalid hash size. This can cause crashes when enlarging
the hashsize, or false negative lookups when shrinking it.
Move the hash size into the per-namespace data and only use the global
hash size to initialize the per-namespace value when instanciating a
new namespace. Additionally restrict hash resizing to init_net for
now as other namespaces are not handled currently.
Cc: stable@kernel.org
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4')
-rw-r--r-- | net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 2 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c | 4 | ||||
-rw-r--r-- | net/ipv4/netfilter/nf_nat_core.c | 22 |
3 files changed, 12 insertions, 16 deletions
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index d171b123a656..d1ea38a7c490 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | |||
@@ -210,7 +210,7 @@ static ctl_table ip_ct_sysctl_table[] = { | |||
210 | }, | 210 | }, |
211 | { | 211 | { |
212 | .procname = "ip_conntrack_buckets", | 212 | .procname = "ip_conntrack_buckets", |
213 | .data = &nf_conntrack_htable_size, | 213 | .data = &init_net.ct.htable_size, |
214 | .maxlen = sizeof(unsigned int), | 214 | .maxlen = sizeof(unsigned int), |
215 | .mode = 0444, | 215 | .mode = 0444, |
216 | .proc_handler = proc_dointvec, | 216 | .proc_handler = proc_dointvec, |
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c index 8668a3defda6..2fb7b76da94f 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c | |||
@@ -32,7 +32,7 @@ static struct hlist_nulls_node *ct_get_first(struct seq_file *seq) | |||
32 | struct hlist_nulls_node *n; | 32 | struct hlist_nulls_node *n; |
33 | 33 | ||
34 | for (st->bucket = 0; | 34 | for (st->bucket = 0; |
35 | st->bucket < nf_conntrack_htable_size; | 35 | st->bucket < net->ct.htable_size; |
36 | st->bucket++) { | 36 | st->bucket++) { |
37 | n = rcu_dereference(net->ct.hash[st->bucket].first); | 37 | n = rcu_dereference(net->ct.hash[st->bucket].first); |
38 | if (!is_a_nulls(n)) | 38 | if (!is_a_nulls(n)) |
@@ -50,7 +50,7 @@ static struct hlist_nulls_node *ct_get_next(struct seq_file *seq, | |||
50 | head = rcu_dereference(head->next); | 50 | head = rcu_dereference(head->next); |
51 | while (is_a_nulls(head)) { | 51 | while (is_a_nulls(head)) { |
52 | if (likely(get_nulls_value(head) == st->bucket)) { | 52 | if (likely(get_nulls_value(head) == st->bucket)) { |
53 | if (++st->bucket >= nf_conntrack_htable_size) | 53 | if (++st->bucket >= net->ct.htable_size) |
54 | return NULL; | 54 | return NULL; |
55 | } | 55 | } |
56 | head = rcu_dereference(net->ct.hash[st->bucket].first); | 56 | head = rcu_dereference(net->ct.hash[st->bucket].first); |
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c index fe1a64479dd0..26066a2327ad 100644 --- a/net/ipv4/netfilter/nf_nat_core.c +++ b/net/ipv4/netfilter/nf_nat_core.c | |||
@@ -35,9 +35,6 @@ static DEFINE_SPINLOCK(nf_nat_lock); | |||
35 | 35 | ||
36 | static struct nf_conntrack_l3proto *l3proto __read_mostly; | 36 | static struct nf_conntrack_l3proto *l3proto __read_mostly; |
37 | 37 | ||
38 | /* Calculated at init based on memory size */ | ||
39 | static unsigned int nf_nat_htable_size __read_mostly; | ||
40 | |||
41 | #define MAX_IP_NAT_PROTO 256 | 38 | #define MAX_IP_NAT_PROTO 256 |
42 | static const struct nf_nat_protocol *nf_nat_protos[MAX_IP_NAT_PROTO] | 39 | static const struct nf_nat_protocol *nf_nat_protos[MAX_IP_NAT_PROTO] |
43 | __read_mostly; | 40 | __read_mostly; |
@@ -72,7 +69,7 @@ EXPORT_SYMBOL_GPL(nf_nat_proto_put); | |||
72 | 69 | ||
73 | /* We keep an extra hash for each conntrack, for fast searching. */ | 70 | /* We keep an extra hash for each conntrack, for fast searching. */ |
74 | static inline unsigned int | 71 | static inline unsigned int |
75 | hash_by_src(const struct nf_conntrack_tuple *tuple) | 72 | hash_by_src(const struct net *net, const struct nf_conntrack_tuple *tuple) |
76 | { | 73 | { |
77 | unsigned int hash; | 74 | unsigned int hash; |
78 | 75 | ||
@@ -80,7 +77,7 @@ hash_by_src(const struct nf_conntrack_tuple *tuple) | |||
80 | hash = jhash_3words((__force u32)tuple->src.u3.ip, | 77 | hash = jhash_3words((__force u32)tuple->src.u3.ip, |
81 | (__force u32)tuple->src.u.all, | 78 | (__force u32)tuple->src.u.all, |
82 | tuple->dst.protonum, 0); | 79 | tuple->dst.protonum, 0); |
83 | return ((u64)hash * nf_nat_htable_size) >> 32; | 80 | return ((u64)hash * net->ipv4.nat_htable_size) >> 32; |
84 | } | 81 | } |
85 | 82 | ||
86 | /* Is this tuple already taken? (not by us) */ | 83 | /* Is this tuple already taken? (not by us) */ |
@@ -147,7 +144,7 @@ find_appropriate_src(struct net *net, | |||
147 | struct nf_conntrack_tuple *result, | 144 | struct nf_conntrack_tuple *result, |
148 | const struct nf_nat_range *range) | 145 | const struct nf_nat_range *range) |
149 | { | 146 | { |
150 | unsigned int h = hash_by_src(tuple); | 147 | unsigned int h = hash_by_src(net, tuple); |
151 | const struct nf_conn_nat *nat; | 148 | const struct nf_conn_nat *nat; |
152 | const struct nf_conn *ct; | 149 | const struct nf_conn *ct; |
153 | const struct hlist_node *n; | 150 | const struct hlist_node *n; |
@@ -330,7 +327,7 @@ nf_nat_setup_info(struct nf_conn *ct, | |||
330 | if (have_to_hash) { | 327 | if (have_to_hash) { |
331 | unsigned int srchash; | 328 | unsigned int srchash; |
332 | 329 | ||
333 | srchash = hash_by_src(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); | 330 | srchash = hash_by_src(net, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); |
334 | spin_lock_bh(&nf_nat_lock); | 331 | spin_lock_bh(&nf_nat_lock); |
335 | /* nf_conntrack_alter_reply might re-allocate exntension aera */ | 332 | /* nf_conntrack_alter_reply might re-allocate exntension aera */ |
336 | nat = nfct_nat(ct); | 333 | nat = nfct_nat(ct); |
@@ -679,8 +676,10 @@ nfnetlink_parse_nat_setup(struct nf_conn *ct, | |||
679 | 676 | ||
680 | static int __net_init nf_nat_net_init(struct net *net) | 677 | static int __net_init nf_nat_net_init(struct net *net) |
681 | { | 678 | { |
682 | net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&nf_nat_htable_size, | 679 | /* Leave them the same for the moment. */ |
683 | &net->ipv4.nat_vmalloced, 0); | 680 | net->ipv4.nat_htable_size = net->ct.htable_size; |
681 | net->ipv4.nat_bysource = nf_ct_alloc_hashtable(&net->ipv4.nat_htable_size, | ||
682 | &net->ipv4.nat_vmalloced, 0); | ||
684 | if (!net->ipv4.nat_bysource) | 683 | if (!net->ipv4.nat_bysource) |
685 | return -ENOMEM; | 684 | return -ENOMEM; |
686 | return 0; | 685 | return 0; |
@@ -703,7 +702,7 @@ static void __net_exit nf_nat_net_exit(struct net *net) | |||
703 | nf_ct_iterate_cleanup(net, &clean_nat, NULL); | 702 | nf_ct_iterate_cleanup(net, &clean_nat, NULL); |
704 | synchronize_rcu(); | 703 | synchronize_rcu(); |
705 | nf_ct_free_hashtable(net->ipv4.nat_bysource, net->ipv4.nat_vmalloced, | 704 | nf_ct_free_hashtable(net->ipv4.nat_bysource, net->ipv4.nat_vmalloced, |
706 | nf_nat_htable_size); | 705 | net->ipv4.nat_htable_size); |
707 | } | 706 | } |
708 | 707 | ||
709 | static struct pernet_operations nf_nat_net_ops = { | 708 | static struct pernet_operations nf_nat_net_ops = { |
@@ -724,9 +723,6 @@ static int __init nf_nat_init(void) | |||
724 | return ret; | 723 | return ret; |
725 | } | 724 | } |
726 | 725 | ||
727 | /* Leave them the same for the moment. */ | ||
728 | nf_nat_htable_size = nf_conntrack_htable_size; | ||
729 | |||
730 | ret = register_pernet_subsys(&nf_nat_net_ops); | 726 | ret = register_pernet_subsys(&nf_nat_net_ops); |
731 | if (ret < 0) | 727 | if (ret < 0) |
732 | goto cleanup_extend; | 728 | goto cleanup_extend; |