summaryrefslogtreecommitdiffstats
path: root/kernel/include
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2011-05-06 22:08:09 +0200
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2011-05-06 22:08:09 +0200
commit32d1d2fa35c6ff61ce376f9f5eb734132599ff44 (patch)
treece584314e7b447a903d1246abfabf7c1a64d339a /kernel/include
parent896447190b8ced40819cb91250db00bc38423bbf (diff)
Fix adding ranges to hash types
When ranges are added to hash types, the elements may trigger rehashing the set. However, the last successfully added element was not kept track so the adding started again with the first element after the rehashing. Bug reported by Mr Dash Four.
Diffstat (limited to 'kernel/include')
-rw-r--r--kernel/include/linux/netfilter/ipset/ip_set.h2
-rw-r--r--kernel/include/linux/netfilter/ipset/ip_set_ahash.h22
2 files changed, 20 insertions, 4 deletions
diff --git a/kernel/include/linux/netfilter/ipset/ip_set.h b/kernel/include/linux/netfilter/ipset/ip_set.h
index e677c4d..710ba00 100644
--- a/kernel/include/linux/netfilter/ipset/ip_set.h
+++ b/kernel/include/linux/netfilter/ipset/ip_set.h
@@ -244,7 +244,7 @@ struct ip_set_type_variant {
* zero for no match/success to add/delete
* positive for matching element */
int (*uadt)(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags);
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
/* Low level add/del/test functions */
ipset_adtfn adt[IPSET_ADT_MAX];
diff --git a/kernel/include/linux/netfilter/ipset/ip_set_ahash.h b/kernel/include/linux/netfilter/ipset/ip_set_ahash.h
index 690fa69..6a4969d 100644
--- a/kernel/include/linux/netfilter/ipset/ip_set_ahash.h
+++ b/kernel/include/linux/netfilter/ipset/ip_set_ahash.h
@@ -5,6 +5,11 @@
#include <linux/jhash.h>
#include <linux/netfilter/ipset/ip_set_timeout.h>
+#define CONCAT(a, b, c) a##b##c
+#define TOKEN(a, b, c) CONCAT(a, b, c)
+
+#define type_pf_next TOKEN(TYPE, PF, _elem)
+
/* Hashing which uses arrays to resolve clashing. The hash table is resized
* (doubled) when searching becomes too long.
* Internally jhash is used with the assumption that the size of the
@@ -54,6 +59,7 @@ struct ip_set_hash {
u32 initval; /* random jhash init value */
u32 timeout; /* timeout value, if enabled */
struct timer_list gc; /* garbage collection when timeout enabled */
+ struct type_pf_next next; /* temporary storage for uadd */
#ifdef IP_SET_HASH_WITH_NETMASK
u8 netmask; /* netmask value for subnets to store */
#endif
@@ -217,6 +223,7 @@ ip_set_hash_destroy(struct ip_set *set)
#define type_pf_data_netmask TOKEN(TYPE, PF, _data_netmask)
#define type_pf_data_list TOKEN(TYPE, PF, _data_list)
#define type_pf_data_tlist TOKEN(TYPE, PF, _data_tlist)
+#define type_pf_data_next TOKEN(TYPE, PF, _data_next)
#define type_pf_elem TOKEN(TYPE, PF, _elem)
#define type_pf_telem TOKEN(TYPE, PF, _telem)
@@ -346,6 +353,9 @@ retry:
return 0;
}
+static inline void
+type_pf_data_next(struct ip_set_hash *h, const struct type_pf_elem *d);
+
/* Add an element to a hash and update the internal counters when succeeded,
* otherwise report the proper error code. */
static int
@@ -372,8 +382,11 @@ type_pf_add(struct ip_set *set, void *value, u32 timeout, u32 flags)
}
ret = type_pf_elem_add(n, value);
- if (ret != 0)
+ if (ret != 0) {
+ if (ret == -EAGAIN)
+ type_pf_data_next(h, d);
goto out;
+ }
#ifdef IP_SET_HASH_WITH_NETS
add_cidr(h, d->cidr, HOST_MASK);
@@ -589,7 +602,7 @@ type_pf_kadt(struct ip_set *set, const struct sk_buff * skb,
enum ipset_adt adt, const struct ip_set_adt_opt *opt);
static int
type_pf_uadt(struct ip_set *set, struct nlattr *tb[],
- enum ipset_adt adt, u32 *lineno, u32 flags);
+ enum ipset_adt adt, u32 *lineno, u32 flags, bool retried);
static const struct ip_set_type_variant type_pf_variant = {
.kadt = type_pf_kadt,
@@ -821,8 +834,11 @@ type_pf_tadd(struct ip_set *set, void *value, u32 timeout, u32 flags)
goto out;
}
ret = type_pf_elem_tadd(n, d, timeout);
- if (ret != 0)
+ if (ret != 0) {
+ if (ret == -EEXIST)
+ type_pf_data_next(h, d);
goto out;
+ }
#ifdef IP_SET_HASH_WITH_NETS
add_cidr(h, d->cidr, HOST_MASK);