aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2012-12-24 07:09:25 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2012-12-26 12:14:44 -0500
commit09181842b000344b1205801df3aa5b726c03cc62 (patch)
tree49d9df6bbf35300fa3a8d1e3c4f0cbbbe07a383e /net
parent10db9069eb5c60195170a4119bdbcbce69a4945f (diff)
netfilter: xt_hashlimit: fix race that results in duplicated entries
Two packets may race to create the same entry in the hashtable, double check if this packet lost race. This double checking only happens in the path of the packet that creates the hashtable for first time. Note that, with this patch, no packet drops occur if the race happens. Reported-by: Feng Gao <gfree.wind@gmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/xt_hashlimit.c25
1 files changed, 21 insertions, 4 deletions
diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
index 26a668a84aa2..cc430f926a85 100644
--- a/net/netfilter/xt_hashlimit.c
+++ b/net/netfilter/xt_hashlimit.c
@@ -157,11 +157,22 @@ dsthash_find(const struct xt_hashlimit_htable *ht,
157/* allocate dsthash_ent, initialize dst, put in htable and lock it */ 157/* allocate dsthash_ent, initialize dst, put in htable and lock it */
158static struct dsthash_ent * 158static struct dsthash_ent *
159dsthash_alloc_init(struct xt_hashlimit_htable *ht, 159dsthash_alloc_init(struct xt_hashlimit_htable *ht,
160 const struct dsthash_dst *dst) 160 const struct dsthash_dst *dst, bool *race)
161{ 161{
162 struct dsthash_ent *ent; 162 struct dsthash_ent *ent;
163 163
164 spin_lock(&ht->lock); 164 spin_lock(&ht->lock);
165
166 /* Two or more packets may race to create the same entry in the
167 * hashtable, double check if this packet lost race.
168 */
169 ent = dsthash_find(ht, dst);
170 if (ent != NULL) {
171 spin_unlock(&ht->lock);
172 *race = true;
173 return ent;
174 }
175
165 /* initialize hash with random val at the time we allocate 176 /* initialize hash with random val at the time we allocate
166 * the first hashtable entry */ 177 * the first hashtable entry */
167 if (unlikely(!ht->rnd_initialized)) { 178 if (unlikely(!ht->rnd_initialized)) {
@@ -585,6 +596,7 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
585 unsigned long now = jiffies; 596 unsigned long now = jiffies;
586 struct dsthash_ent *dh; 597 struct dsthash_ent *dh;
587 struct dsthash_dst dst; 598 struct dsthash_dst dst;
599 bool race = false;
588 u32 cost; 600 u32 cost;
589 601
590 if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0) 602 if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0)
@@ -593,13 +605,18 @@ hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
593 rcu_read_lock_bh(); 605 rcu_read_lock_bh();
594 dh = dsthash_find(hinfo, &dst); 606 dh = dsthash_find(hinfo, &dst);
595 if (dh == NULL) { 607 if (dh == NULL) {
596 dh = dsthash_alloc_init(hinfo, &dst); 608 dh = dsthash_alloc_init(hinfo, &dst, &race);
597 if (dh == NULL) { 609 if (dh == NULL) {
598 rcu_read_unlock_bh(); 610 rcu_read_unlock_bh();
599 goto hotdrop; 611 goto hotdrop;
612 } else if (race) {
613 /* Already got an entry, update expiration timeout */
614 dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire);
615 rateinfo_recalc(dh, now, hinfo->cfg.mode);
616 } else {
617 dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire);
618 rateinfo_init(dh, hinfo);
600 } 619 }
601 dh->expires = jiffies + msecs_to_jiffies(hinfo->cfg.expire);
602 rateinfo_init(dh, hinfo);
603 } else { 620 } else {
604 /* update expiration timeout */ 621 /* update expiration timeout */
605 dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire); 622 dh->expires = now + msecs_to_jiffies(hinfo->cfg.expire);