diff options
-rw-r--r-- | net/netfilter/xt_hashlimit.c | 52 |
1 files changed, 18 insertions, 34 deletions
diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index fb7fcb773a3f..017c95966aa8 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c | |||
@@ -90,7 +90,7 @@ struct dsthash_ent { | |||
90 | 90 | ||
91 | struct xt_hashlimit_htable { | 91 | struct xt_hashlimit_htable { |
92 | struct hlist_node node; /* global list of all htables */ | 92 | struct hlist_node node; /* global list of all htables */ |
93 | atomic_t use; | 93 | int use; |
94 | u_int8_t family; | 94 | u_int8_t family; |
95 | bool rnd_initialized; | 95 | bool rnd_initialized; |
96 | 96 | ||
@@ -109,8 +109,7 @@ struct xt_hashlimit_htable { | |||
109 | struct hlist_head hash[0]; /* hashtable itself */ | 109 | struct hlist_head hash[0]; /* hashtable itself */ |
110 | }; | 110 | }; |
111 | 111 | ||
112 | static DEFINE_SPINLOCK(hashlimit_lock); /* protects htables list */ | 112 | static DEFINE_MUTEX(hashlimit_mutex); /* protects htables list */ |
113 | static DEFINE_MUTEX(hlimit_mutex); /* additional checkentry protection */ | ||
114 | static struct kmem_cache *hashlimit_cachep __read_mostly; | 113 | static struct kmem_cache *hashlimit_cachep __read_mostly; |
115 | 114 | ||
116 | static inline bool dst_cmp(const struct dsthash_ent *ent, | 115 | static inline bool dst_cmp(const struct dsthash_ent *ent, |
@@ -244,7 +243,7 @@ static int htable_create_v0(struct net *net, struct xt_hashlimit_info *minfo, u_ | |||
244 | for (i = 0; i < hinfo->cfg.size; i++) | 243 | for (i = 0; i < hinfo->cfg.size; i++) |
245 | INIT_HLIST_HEAD(&hinfo->hash[i]); | 244 | INIT_HLIST_HEAD(&hinfo->hash[i]); |
246 | 245 | ||
247 | atomic_set(&hinfo->use, 1); | 246 | hinfo->use = 1; |
248 | hinfo->count = 0; | 247 | hinfo->count = 0; |
249 | hinfo->family = family; | 248 | hinfo->family = family; |
250 | hinfo->rnd_initialized = false; | 249 | hinfo->rnd_initialized = false; |
@@ -263,9 +262,9 @@ static int htable_create_v0(struct net *net, struct xt_hashlimit_info *minfo, u_ | |||
263 | hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); | 262 | hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); |
264 | add_timer(&hinfo->timer); | 263 | add_timer(&hinfo->timer); |
265 | 264 | ||
266 | spin_lock_bh(&hashlimit_lock); | 265 | mutex_lock(&hashlimit_mutex); |
267 | hlist_add_head(&hinfo->node, &hashlimit_net->htables); | 266 | hlist_add_head(&hinfo->node, &hashlimit_net->htables); |
268 | spin_unlock_bh(&hashlimit_lock); | 267 | mutex_unlock(&hashlimit_mutex); |
269 | 268 | ||
270 | return 0; | 269 | return 0; |
271 | } | 270 | } |
@@ -308,7 +307,7 @@ static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo, | |||
308 | for (i = 0; i < hinfo->cfg.size; i++) | 307 | for (i = 0; i < hinfo->cfg.size; i++) |
309 | INIT_HLIST_HEAD(&hinfo->hash[i]); | 308 | INIT_HLIST_HEAD(&hinfo->hash[i]); |
310 | 309 | ||
311 | atomic_set(&hinfo->use, 1); | 310 | hinfo->use = 1; |
312 | hinfo->count = 0; | 311 | hinfo->count = 0; |
313 | hinfo->family = family; | 312 | hinfo->family = family; |
314 | hinfo->rnd_initialized = false; | 313 | hinfo->rnd_initialized = false; |
@@ -328,9 +327,9 @@ static int htable_create(struct net *net, struct xt_hashlimit_mtinfo1 *minfo, | |||
328 | hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); | 327 | hinfo->timer.expires = jiffies + msecs_to_jiffies(hinfo->cfg.gc_interval); |
329 | add_timer(&hinfo->timer); | 328 | add_timer(&hinfo->timer); |
330 | 329 | ||
331 | spin_lock_bh(&hashlimit_lock); | 330 | mutex_lock(&hashlimit_mutex); |
332 | hlist_add_head(&hinfo->node, &hashlimit_net->htables); | 331 | hlist_add_head(&hinfo->node, &hashlimit_net->htables); |
333 | spin_unlock_bh(&hashlimit_lock); | 332 | mutex_unlock(&hashlimit_mutex); |
334 | 333 | ||
335 | return 0; | 334 | return 0; |
336 | } | 335 | } |
@@ -402,27 +401,24 @@ static struct xt_hashlimit_htable *htable_find_get(struct net *net, | |||
402 | struct xt_hashlimit_htable *hinfo; | 401 | struct xt_hashlimit_htable *hinfo; |
403 | struct hlist_node *pos; | 402 | struct hlist_node *pos; |
404 | 403 | ||
405 | spin_lock_bh(&hashlimit_lock); | ||
406 | hlist_for_each_entry(hinfo, pos, &hashlimit_net->htables, node) { | 404 | hlist_for_each_entry(hinfo, pos, &hashlimit_net->htables, node) { |
407 | if (!strcmp(name, hinfo->pde->name) && | 405 | if (!strcmp(name, hinfo->pde->name) && |
408 | hinfo->family == family) { | 406 | hinfo->family == family) { |
409 | atomic_inc(&hinfo->use); | 407 | hinfo->use++; |
410 | spin_unlock_bh(&hashlimit_lock); | ||
411 | return hinfo; | 408 | return hinfo; |
412 | } | 409 | } |
413 | } | 410 | } |
414 | spin_unlock_bh(&hashlimit_lock); | ||
415 | return NULL; | 411 | return NULL; |
416 | } | 412 | } |
417 | 413 | ||
418 | static void htable_put(struct xt_hashlimit_htable *hinfo) | 414 | static void htable_put(struct xt_hashlimit_htable *hinfo) |
419 | { | 415 | { |
420 | if (atomic_dec_and_test(&hinfo->use)) { | 416 | mutex_lock(&hashlimit_mutex); |
421 | spin_lock_bh(&hashlimit_lock); | 417 | if (--hinfo->use == 0) { |
422 | hlist_del(&hinfo->node); | 418 | hlist_del(&hinfo->node); |
423 | spin_unlock_bh(&hashlimit_lock); | ||
424 | htable_destroy(hinfo); | 419 | htable_destroy(hinfo); |
425 | } | 420 | } |
421 | mutex_unlock(&hashlimit_mutex); | ||
426 | } | 422 | } |
427 | 423 | ||
428 | /* The algorithm used is the Simple Token Bucket Filter (TBF) | 424 | /* The algorithm used is the Simple Token Bucket Filter (TBF) |
@@ -710,19 +706,13 @@ static bool hashlimit_mt_check_v0(const struct xt_mtchk_param *par) | |||
710 | if (r->name[sizeof(r->name) - 1] != '\0') | 706 | if (r->name[sizeof(r->name) - 1] != '\0') |
711 | return false; | 707 | return false; |
712 | 708 | ||
713 | /* This is the best we've got: We cannot release and re-grab lock, | 709 | mutex_lock(&hashlimit_mutex); |
714 | * since checkentry() is called before x_tables.c grabs xt_mutex. | ||
715 | * We also cannot grab the hashtable spinlock, since htable_create will | ||
716 | * call vmalloc, and that can sleep. And we cannot just re-search | ||
717 | * the list of htable's in htable_create(), since then we would | ||
718 | * create duplicate proc files. -HW */ | ||
719 | mutex_lock(&hlimit_mutex); | ||
720 | r->hinfo = htable_find_get(net, r->name, par->match->family); | 710 | r->hinfo = htable_find_get(net, r->name, par->match->family); |
721 | if (!r->hinfo && htable_create_v0(net, r, par->match->family) != 0) { | 711 | if (!r->hinfo && htable_create_v0(net, r, par->match->family) != 0) { |
722 | mutex_unlock(&hlimit_mutex); | 712 | mutex_unlock(&hashlimit_mutex); |
723 | return false; | 713 | return false; |
724 | } | 714 | } |
725 | mutex_unlock(&hlimit_mutex); | 715 | mutex_unlock(&hashlimit_mutex); |
726 | 716 | ||
727 | return true; | 717 | return true; |
728 | } | 718 | } |
@@ -752,19 +742,13 @@ static bool hashlimit_mt_check(const struct xt_mtchk_param *par) | |||
752 | return false; | 742 | return false; |
753 | } | 743 | } |
754 | 744 | ||
755 | /* This is the best we've got: We cannot release and re-grab lock, | 745 | mutex_lock(&hashlimit_mutex); |
756 | * since checkentry() is called before x_tables.c grabs xt_mutex. | ||
757 | * We also cannot grab the hashtable spinlock, since htable_create will | ||
758 | * call vmalloc, and that can sleep. And we cannot just re-search | ||
759 | * the list of htable's in htable_create(), since then we would | ||
760 | * create duplicate proc files. -HW */ | ||
761 | mutex_lock(&hlimit_mutex); | ||
762 | info->hinfo = htable_find_get(net, info->name, par->match->family); | 746 | info->hinfo = htable_find_get(net, info->name, par->match->family); |
763 | if (!info->hinfo && htable_create(net, info, par->match->family) != 0) { | 747 | if (!info->hinfo && htable_create(net, info, par->match->family) != 0) { |
764 | mutex_unlock(&hlimit_mutex); | 748 | mutex_unlock(&hashlimit_mutex); |
765 | return false; | 749 | return false; |
766 | } | 750 | } |
767 | mutex_unlock(&hlimit_mutex); | 751 | mutex_unlock(&hashlimit_mutex); |
768 | return true; | 752 | return true; |
769 | } | 753 | } |
770 | 754 | ||