diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/xt_hashlimit.c | 25 |
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 */ |
158 | static struct dsthash_ent * | 158 | static struct dsthash_ent * |
159 | dsthash_alloc_init(struct xt_hashlimit_htable *ht, | 159 | dsthash_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); |