diff options
author | Patrick McHardy <kaber@trash.net> | 2007-07-08 01:33:47 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-07-11 01:17:59 -0400 |
commit | a71c085562bcc99e8b711cab4222bff1f6e955da (patch) | |
tree | 7de563d406e8e9e44065b53c664f837f97f8b3fe /net | |
parent | e9c1b084e17ca225b6be731b819308ee0f9e04b8 (diff) |
[NETFILTER]: nf_conntrack: use hashtable for expectations
Currently all expectations are kept on a global list that
- needs to be searched for every new conncetion
- needs to be walked for evicting expectations when a master connection
has reached its limit
- needs to be walked on connection destruction for connections that
have open expectations
This is obviously not good, especially when considering helpers like
H.323 that register *lots* of expectations and can set up permanent
expectations, but it also allows for an easy DoS against firewalls
using connection tracking helpers.
Use a hashtable for expectations to avoid incurring the search overhead
for every new connection. The default hash size is 1/256 of the conntrack
hash table size, this can be overriden using a module parameter.
This patch only introduces the hash table for expectation lookups and
keeps other users to reduce the noise, the following patches will get
rid of it completely.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/nf_conntrack_expect.c | 72 |
1 files changed, 66 insertions, 6 deletions
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index ad197bccc7ca..0696f87aaef1 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/err.h> | 19 | #include <linux/err.h> |
20 | #include <linux/percpu.h> | 20 | #include <linux/percpu.h> |
21 | #include <linux/kernel.h> | 21 | #include <linux/kernel.h> |
22 | #include <linux/jhash.h> | ||
22 | 23 | ||
23 | #include <net/netfilter/nf_conntrack.h> | 24 | #include <net/netfilter/nf_conntrack.h> |
24 | #include <net/netfilter/nf_conntrack_core.h> | 25 | #include <net/netfilter/nf_conntrack_core.h> |
@@ -29,6 +30,17 @@ | |||
29 | LIST_HEAD(nf_ct_expect_list); | 30 | LIST_HEAD(nf_ct_expect_list); |
30 | EXPORT_SYMBOL_GPL(nf_ct_expect_list); | 31 | EXPORT_SYMBOL_GPL(nf_ct_expect_list); |
31 | 32 | ||
33 | struct hlist_head *nf_ct_expect_hash __read_mostly; | ||
34 | EXPORT_SYMBOL_GPL(nf_ct_expect_hash); | ||
35 | |||
36 | unsigned int nf_ct_expect_hsize __read_mostly; | ||
37 | EXPORT_SYMBOL_GPL(nf_ct_expect_hsize); | ||
38 | |||
39 | static unsigned int nf_ct_expect_hash_rnd __read_mostly; | ||
40 | static unsigned int nf_ct_expect_count; | ||
41 | static int nf_ct_expect_hash_rnd_initted __read_mostly; | ||
42 | static int nf_ct_expect_vmalloc; | ||
43 | |||
32 | static struct kmem_cache *nf_ct_expect_cachep __read_mostly; | 44 | static struct kmem_cache *nf_ct_expect_cachep __read_mostly; |
33 | static unsigned int nf_ct_expect_next_id; | 45 | static unsigned int nf_ct_expect_next_id; |
34 | 46 | ||
@@ -41,6 +53,9 @@ void nf_ct_unlink_expect(struct nf_conntrack_expect *exp) | |||
41 | NF_CT_ASSERT(!timer_pending(&exp->timeout)); | 53 | NF_CT_ASSERT(!timer_pending(&exp->timeout)); |
42 | 54 | ||
43 | list_del(&exp->list); | 55 | list_del(&exp->list); |
56 | hlist_del(&exp->hnode); | ||
57 | nf_ct_expect_count--; | ||
58 | |||
44 | NF_CT_STAT_INC(expect_delete); | 59 | NF_CT_STAT_INC(expect_delete); |
45 | master_help->expecting--; | 60 | master_help->expecting--; |
46 | nf_ct_expect_put(exp); | 61 | nf_ct_expect_put(exp); |
@@ -57,12 +72,31 @@ static void nf_ct_expectation_timed_out(unsigned long ul_expect) | |||
57 | nf_ct_expect_put(exp); | 72 | nf_ct_expect_put(exp); |
58 | } | 73 | } |
59 | 74 | ||
75 | static unsigned int nf_ct_expect_dst_hash(const struct nf_conntrack_tuple *tuple) | ||
76 | { | ||
77 | if (unlikely(!nf_ct_expect_hash_rnd_initted)) { | ||
78 | get_random_bytes(&nf_ct_expect_hash_rnd, 4); | ||
79 | nf_ct_expect_hash_rnd_initted = 1; | ||
80 | } | ||
81 | |||
82 | return jhash2(tuple->dst.u3.all, ARRAY_SIZE(tuple->dst.u3.all), | ||
83 | (((tuple->dst.protonum ^ tuple->src.l3num) << 16) | | ||
84 | tuple->dst.u.all) ^ nf_ct_expect_hash_rnd) % | ||
85 | nf_ct_expect_hsize; | ||
86 | } | ||
87 | |||
60 | struct nf_conntrack_expect * | 88 | struct nf_conntrack_expect * |
61 | __nf_ct_expect_find(const struct nf_conntrack_tuple *tuple) | 89 | __nf_ct_expect_find(const struct nf_conntrack_tuple *tuple) |
62 | { | 90 | { |
63 | struct nf_conntrack_expect *i; | 91 | struct nf_conntrack_expect *i; |
92 | struct hlist_node *n; | ||
93 | unsigned int h; | ||
94 | |||
95 | if (!nf_ct_expect_count) | ||
96 | return NULL; | ||
64 | 97 | ||
65 | list_for_each_entry(i, &nf_ct_expect_list, list) { | 98 | h = nf_ct_expect_dst_hash(tuple); |
99 | hlist_for_each_entry(i, n, &nf_ct_expect_hash[h], hnode) { | ||
66 | if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)) | 100 | if (nf_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask)) |
67 | return i; | 101 | return i; |
68 | } | 102 | } |
@@ -252,10 +286,14 @@ EXPORT_SYMBOL_GPL(nf_ct_expect_put); | |||
252 | static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) | 286 | static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) |
253 | { | 287 | { |
254 | struct nf_conn_help *master_help = nfct_help(exp->master); | 288 | struct nf_conn_help *master_help = nfct_help(exp->master); |
289 | unsigned int h = nf_ct_expect_dst_hash(&exp->tuple); | ||
255 | 290 | ||
256 | atomic_inc(&exp->use); | 291 | atomic_inc(&exp->use); |
257 | master_help->expecting++; | 292 | master_help->expecting++; |
293 | |||
258 | list_add(&exp->list, &nf_ct_expect_list); | 294 | list_add(&exp->list, &nf_ct_expect_list); |
295 | hlist_add_head(&exp->hnode, &nf_ct_expect_hash[h]); | ||
296 | nf_ct_expect_count++; | ||
259 | 297 | ||
260 | setup_timer(&exp->timeout, nf_ct_expectation_timed_out, | 298 | setup_timer(&exp->timeout, nf_ct_expectation_timed_out, |
261 | (unsigned long)exp); | 299 | (unsigned long)exp); |
@@ -300,6 +338,8 @@ int nf_ct_expect_related(struct nf_conntrack_expect *expect) | |||
300 | struct nf_conntrack_expect *i; | 338 | struct nf_conntrack_expect *i; |
301 | struct nf_conn *master = expect->master; | 339 | struct nf_conn *master = expect->master; |
302 | struct nf_conn_help *master_help = nfct_help(master); | 340 | struct nf_conn_help *master_help = nfct_help(master); |
341 | struct hlist_node *n; | ||
342 | unsigned int h; | ||
303 | int ret; | 343 | int ret; |
304 | 344 | ||
305 | NF_CT_ASSERT(master_help); | 345 | NF_CT_ASSERT(master_help); |
@@ -309,7 +349,8 @@ int nf_ct_expect_related(struct nf_conntrack_expect *expect) | |||
309 | ret = -ESHUTDOWN; | 349 | ret = -ESHUTDOWN; |
310 | goto out; | 350 | goto out; |
311 | } | 351 | } |
312 | list_for_each_entry(i, &nf_ct_expect_list, list) { | 352 | h = nf_ct_expect_dst_hash(&expect->tuple); |
353 | hlist_for_each_entry(i, n, &nf_ct_expect_hash[h], hnode) { | ||
313 | if (expect_matches(i, expect)) { | 354 | if (expect_matches(i, expect)) { |
314 | /* Refresh timer: if it's dying, ignore.. */ | 355 | /* Refresh timer: if it's dying, ignore.. */ |
315 | if (refresh_timer(i)) { | 356 | if (refresh_timer(i)) { |
@@ -433,24 +474,41 @@ static void exp_proc_remove(void) | |||
433 | #endif /* CONFIG_PROC_FS */ | 474 | #endif /* CONFIG_PROC_FS */ |
434 | } | 475 | } |
435 | 476 | ||
477 | module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0600); | ||
478 | |||
436 | int __init nf_conntrack_expect_init(void) | 479 | int __init nf_conntrack_expect_init(void) |
437 | { | 480 | { |
438 | int err; | 481 | int err = -ENOMEM; |
482 | |||
483 | if (!nf_ct_expect_hsize) { | ||
484 | nf_ct_expect_hsize = nf_conntrack_htable_size / 256; | ||
485 | if (!nf_ct_expect_hsize) | ||
486 | nf_ct_expect_hsize = 1; | ||
487 | } | ||
488 | |||
489 | nf_ct_expect_hash = nf_ct_alloc_hashtable(&nf_ct_expect_hsize, | ||
490 | &nf_ct_expect_vmalloc); | ||
491 | if (nf_ct_expect_hash == NULL) | ||
492 | goto err1; | ||
439 | 493 | ||
440 | nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect", | 494 | nf_ct_expect_cachep = kmem_cache_create("nf_conntrack_expect", |
441 | sizeof(struct nf_conntrack_expect), | 495 | sizeof(struct nf_conntrack_expect), |
442 | 0, 0, NULL, NULL); | 496 | 0, 0, NULL, NULL); |
443 | if (!nf_ct_expect_cachep) | 497 | if (!nf_ct_expect_cachep) |
444 | return -ENOMEM; | 498 | goto err2; |
445 | 499 | ||
446 | err = exp_proc_init(); | 500 | err = exp_proc_init(); |
447 | if (err < 0) | 501 | if (err < 0) |
448 | goto err1; | 502 | goto err3; |
449 | 503 | ||
450 | return 0; | 504 | return 0; |
451 | 505 | ||
452 | err1: | 506 | err3: |
507 | nf_ct_free_hashtable(nf_ct_expect_hash, nf_ct_expect_vmalloc, | ||
508 | nf_ct_expect_hsize); | ||
509 | err2: | ||
453 | kmem_cache_destroy(nf_ct_expect_cachep); | 510 | kmem_cache_destroy(nf_ct_expect_cachep); |
511 | err1: | ||
454 | return err; | 512 | return err; |
455 | } | 513 | } |
456 | 514 | ||
@@ -458,4 +516,6 @@ void nf_conntrack_expect_fini(void) | |||
458 | { | 516 | { |
459 | exp_proc_remove(); | 517 | exp_proc_remove(); |
460 | kmem_cache_destroy(nf_ct_expect_cachep); | 518 | kmem_cache_destroy(nf_ct_expect_cachep); |
519 | nf_ct_free_hashtable(nf_ct_expect_hash, nf_ct_expect_vmalloc, | ||
520 | nf_ct_expect_hsize); | ||
461 | } | 521 | } |