diff options
author | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2013-09-09 08:44:29 -0400 |
---|---|---|
committer | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2013-09-30 15:33:27 -0400 |
commit | 40cd63bf33b21ef4d43776b1d49c605f876fe32c (patch) | |
tree | 525daadc3b2e86977e469a39c28749ea9303d7e2 | |
parent | 03c8b234e61a9a3aab8d970b3bf681934ecfe443 (diff) |
netfilter: ipset: Support extensions which need a per data destroy function
Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
-rw-r--r-- | include/linux/netfilter/ipset/ip_set.h | 22 | ||||
-rw-r--r-- | net/netfilter/ipset/ip_set_bitmap_gen.h | 38 | ||||
-rw-r--r-- | net/netfilter/ipset/ip_set_hash_gen.h | 71 | ||||
-rw-r--r-- | net/netfilter/ipset/ip_set_list_set.c | 19 |
4 files changed, 107 insertions, 43 deletions
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 66d6bd404d64..6372ee224fe8 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h | |||
@@ -49,11 +49,13 @@ enum ip_set_feature { | |||
49 | 49 | ||
50 | /* Set extensions */ | 50 | /* Set extensions */ |
51 | enum ip_set_extension { | 51 | enum ip_set_extension { |
52 | IPSET_EXT_NONE = 0, | 52 | IPSET_EXT_BIT_TIMEOUT = 0, |
53 | IPSET_EXT_BIT_TIMEOUT = 1, | ||
54 | IPSET_EXT_TIMEOUT = (1 << IPSET_EXT_BIT_TIMEOUT), | 53 | IPSET_EXT_TIMEOUT = (1 << IPSET_EXT_BIT_TIMEOUT), |
55 | IPSET_EXT_BIT_COUNTER = 2, | 54 | IPSET_EXT_BIT_COUNTER = 1, |
56 | IPSET_EXT_COUNTER = (1 << IPSET_EXT_BIT_COUNTER), | 55 | IPSET_EXT_COUNTER = (1 << IPSET_EXT_BIT_COUNTER), |
56 | /* Mark set with an extension which needs to call destroy */ | ||
57 | IPSET_EXT_BIT_DESTROY = 7, | ||
58 | IPSET_EXT_DESTROY = (1 << IPSET_EXT_BIT_DESTROY), | ||
57 | }; | 59 | }; |
58 | 60 | ||
59 | #define SET_WITH_TIMEOUT(s) ((s)->extensions & IPSET_EXT_TIMEOUT) | 61 | #define SET_WITH_TIMEOUT(s) ((s)->extensions & IPSET_EXT_TIMEOUT) |
@@ -68,6 +70,8 @@ enum ip_set_ext_id { | |||
68 | 70 | ||
69 | /* Extension type */ | 71 | /* Extension type */ |
70 | struct ip_set_ext_type { | 72 | struct ip_set_ext_type { |
73 | /* Destroy extension private data (can be NULL) */ | ||
74 | void (*destroy)(void *ext); | ||
71 | enum ip_set_extension type; | 75 | enum ip_set_extension type; |
72 | enum ipset_cadt_flags flag; | 76 | enum ipset_cadt_flags flag; |
73 | /* Size and minimal alignment */ | 77 | /* Size and minimal alignment */ |
@@ -88,13 +92,21 @@ struct ip_set_counter { | |||
88 | atomic64_t packets; | 92 | atomic64_t packets; |
89 | }; | 93 | }; |
90 | 94 | ||
95 | struct ip_set; | ||
96 | |||
97 | static inline void | ||
98 | ip_set_ext_destroy(struct ip_set *set, void *data) | ||
99 | { | ||
100 | /* Check that the extension is enabled for the set and | ||
101 | * call it's destroy function for its extension part in data. | ||
102 | */ | ||
103 | } | ||
104 | |||
91 | #define ext_timeout(e, s) \ | 105 | #define ext_timeout(e, s) \ |
92 | (unsigned long *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_TIMEOUT]) | 106 | (unsigned long *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_TIMEOUT]) |
93 | #define ext_counter(e, s) \ | 107 | #define ext_counter(e, s) \ |
94 | (struct ip_set_counter *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_COUNTER]) | 108 | (struct ip_set_counter *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_COUNTER]) |
95 | 109 | ||
96 | struct ip_set; | ||
97 | |||
98 | typedef int (*ipset_adtfn)(struct ip_set *set, void *value, | 110 | typedef int (*ipset_adtfn)(struct ip_set *set, void *value, |
99 | const struct ip_set_ext *ext, | 111 | const struct ip_set_ext *ext, |
100 | struct ip_set_ext *mext, u32 cmdflags); | 112 | struct ip_set_ext *mext, u32 cmdflags); |
diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index f32ddbca3923..4515fe8b83dd 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h | |||
@@ -12,6 +12,7 @@ | |||
12 | #define mtype_gc_test IPSET_TOKEN(MTYPE, _gc_test) | 12 | #define mtype_gc_test IPSET_TOKEN(MTYPE, _gc_test) |
13 | #define mtype_is_filled IPSET_TOKEN(MTYPE, _is_filled) | 13 | #define mtype_is_filled IPSET_TOKEN(MTYPE, _is_filled) |
14 | #define mtype_do_add IPSET_TOKEN(MTYPE, _do_add) | 14 | #define mtype_do_add IPSET_TOKEN(MTYPE, _do_add) |
15 | #define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup) | ||
15 | #define mtype_do_del IPSET_TOKEN(MTYPE, _do_del) | 16 | #define mtype_do_del IPSET_TOKEN(MTYPE, _do_del) |
16 | #define mtype_do_list IPSET_TOKEN(MTYPE, _do_list) | 17 | #define mtype_do_list IPSET_TOKEN(MTYPE, _do_list) |
17 | #define mtype_do_head IPSET_TOKEN(MTYPE, _do_head) | 18 | #define mtype_do_head IPSET_TOKEN(MTYPE, _do_head) |
@@ -47,6 +48,17 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) | |||
47 | } | 48 | } |
48 | 49 | ||
49 | static void | 50 | static void |
51 | mtype_ext_cleanup(struct ip_set *set) | ||
52 | { | ||
53 | struct mtype *map = set->data; | ||
54 | u32 id; | ||
55 | |||
56 | for (id = 0; id < map->elements; id++) | ||
57 | if (test_bit(id, map->members)) | ||
58 | ip_set_ext_destroy(set, get_ext(set, map, id)); | ||
59 | } | ||
60 | |||
61 | static void | ||
50 | mtype_destroy(struct ip_set *set) | 62 | mtype_destroy(struct ip_set *set) |
51 | { | 63 | { |
52 | struct mtype *map = set->data; | 64 | struct mtype *map = set->data; |
@@ -55,8 +67,11 @@ mtype_destroy(struct ip_set *set) | |||
55 | del_timer_sync(&map->gc); | 67 | del_timer_sync(&map->gc); |
56 | 68 | ||
57 | ip_set_free(map->members); | 69 | ip_set_free(map->members); |
58 | if (set->dsize) | 70 | if (set->dsize) { |
71 | if (set->extensions & IPSET_EXT_DESTROY) | ||
72 | mtype_ext_cleanup(set); | ||
59 | ip_set_free(map->extensions); | 73 | ip_set_free(map->extensions); |
74 | } | ||
60 | kfree(map); | 75 | kfree(map); |
61 | 76 | ||
62 | set->data = NULL; | 77 | set->data = NULL; |
@@ -67,6 +82,8 @@ mtype_flush(struct ip_set *set) | |||
67 | { | 82 | { |
68 | struct mtype *map = set->data; | 83 | struct mtype *map = set->data; |
69 | 84 | ||
85 | if (set->extensions & IPSET_EXT_DESTROY) | ||
86 | mtype_ext_cleanup(set); | ||
70 | memset(map->members, 0, map->memsize); | 87 | memset(map->members, 0, map->memsize); |
71 | } | 88 | } |
72 | 89 | ||
@@ -132,6 +149,8 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, | |||
132 | ret = 0; | 149 | ret = 0; |
133 | else if (!(flags & IPSET_FLAG_EXIST)) | 150 | else if (!(flags & IPSET_FLAG_EXIST)) |
134 | return -IPSET_ERR_EXIST; | 151 | return -IPSET_ERR_EXIST; |
152 | /* Element is re-added, cleanup extensions */ | ||
153 | ip_set_ext_destroy(set, x); | ||
135 | } | 154 | } |
136 | 155 | ||
137 | if (SET_WITH_TIMEOUT(set)) | 156 | if (SET_WITH_TIMEOUT(set)) |
@@ -152,11 +171,14 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, | |||
152 | { | 171 | { |
153 | struct mtype *map = set->data; | 172 | struct mtype *map = set->data; |
154 | const struct mtype_adt_elem *e = value; | 173 | const struct mtype_adt_elem *e = value; |
155 | const void *x = get_ext(set, map, e->id); | 174 | void *x = get_ext(set, map, e->id); |
156 | 175 | ||
157 | if (mtype_do_del(e, map) || | 176 | if (mtype_do_del(e, map)) |
158 | (SET_WITH_TIMEOUT(set) && | 177 | return -IPSET_ERR_EXIST; |
159 | ip_set_timeout_expired(ext_timeout(x, set)))) | 178 | |
179 | ip_set_ext_destroy(set, x); | ||
180 | if (SET_WITH_TIMEOUT(set) && | ||
181 | ip_set_timeout_expired(ext_timeout(x, set))) | ||
160 | return -IPSET_ERR_EXIST; | 182 | return -IPSET_ERR_EXIST; |
161 | 183 | ||
162 | return 0; | 184 | return 0; |
@@ -235,7 +257,7 @@ mtype_gc(unsigned long ul_set) | |||
235 | { | 257 | { |
236 | struct ip_set *set = (struct ip_set *) ul_set; | 258 | struct ip_set *set = (struct ip_set *) ul_set; |
237 | struct mtype *map = set->data; | 259 | struct mtype *map = set->data; |
238 | const void *x; | 260 | void *x; |
239 | u32 id; | 261 | u32 id; |
240 | 262 | ||
241 | /* We run parallel with other readers (test element) | 263 | /* We run parallel with other readers (test element) |
@@ -244,8 +266,10 @@ mtype_gc(unsigned long ul_set) | |||
244 | for (id = 0; id < map->elements; id++) | 266 | for (id = 0; id < map->elements; id++) |
245 | if (mtype_gc_test(id, map, set->dsize)) { | 267 | if (mtype_gc_test(id, map, set->dsize)) { |
246 | x = get_ext(set, map, id); | 268 | x = get_ext(set, map, id); |
247 | if (ip_set_timeout_expired(ext_timeout(x, set))) | 269 | if (ip_set_timeout_expired(ext_timeout(x, set))) { |
248 | clear_bit(id, map->members); | 270 | clear_bit(id, map->members); |
271 | ip_set_ext_destroy(set, x); | ||
272 | } | ||
249 | } | 273 | } |
250 | read_unlock_bh(&set->lock); | 274 | read_unlock_bh(&set->lock); |
251 | 275 | ||
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 3999f1719f69..3c26e5b946f5 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h | |||
@@ -117,23 +117,6 @@ htable_bits(u32 hashsize) | |||
117 | return bits; | 117 | return bits; |
118 | } | 118 | } |
119 | 119 | ||
120 | /* Destroy the hashtable part of the set */ | ||
121 | static void | ||
122 | ahash_destroy(struct htable *t) | ||
123 | { | ||
124 | struct hbucket *n; | ||
125 | u32 i; | ||
126 | |||
127 | for (i = 0; i < jhash_size(t->htable_bits); i++) { | ||
128 | n = hbucket(t, i); | ||
129 | if (n->size) | ||
130 | /* FIXME: use slab cache */ | ||
131 | kfree(n->value); | ||
132 | } | ||
133 | |||
134 | ip_set_free(t); | ||
135 | } | ||
136 | |||
137 | static int | 120 | static int |
138 | hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) | 121 | hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) |
139 | { | 122 | { |
@@ -192,6 +175,8 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) | |||
192 | #undef mtype_data_next | 175 | #undef mtype_data_next |
193 | #undef mtype_elem | 176 | #undef mtype_elem |
194 | 177 | ||
178 | #undef mtype_ahash_destroy | ||
179 | #undef mtype_ext_cleanup | ||
195 | #undef mtype_add_cidr | 180 | #undef mtype_add_cidr |
196 | #undef mtype_del_cidr | 181 | #undef mtype_del_cidr |
197 | #undef mtype_ahash_memsize | 182 | #undef mtype_ahash_memsize |
@@ -230,6 +215,8 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) | |||
230 | #define mtype_data_list IPSET_TOKEN(MTYPE, _data_list) | 215 | #define mtype_data_list IPSET_TOKEN(MTYPE, _data_list) |
231 | #define mtype_data_next IPSET_TOKEN(MTYPE, _data_next) | 216 | #define mtype_data_next IPSET_TOKEN(MTYPE, _data_next) |
232 | #define mtype_elem IPSET_TOKEN(MTYPE, _elem) | 217 | #define mtype_elem IPSET_TOKEN(MTYPE, _elem) |
218 | #define mtype_ahash_destroy IPSET_TOKEN(MTYPE, _ahash_destroy) | ||
219 | #define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup) | ||
233 | #define mtype_add_cidr IPSET_TOKEN(MTYPE, _add_cidr) | 220 | #define mtype_add_cidr IPSET_TOKEN(MTYPE, _add_cidr) |
234 | #define mtype_del_cidr IPSET_TOKEN(MTYPE, _del_cidr) | 221 | #define mtype_del_cidr IPSET_TOKEN(MTYPE, _del_cidr) |
235 | #define mtype_ahash_memsize IPSET_TOKEN(MTYPE, _ahash_memsize) | 222 | #define mtype_ahash_memsize IPSET_TOKEN(MTYPE, _ahash_memsize) |
@@ -359,6 +346,19 @@ mtype_ahash_memsize(const struct htype *h, const struct htable *t, | |||
359 | return memsize; | 346 | return memsize; |
360 | } | 347 | } |
361 | 348 | ||
349 | /* Get the ith element from the array block n */ | ||
350 | #define ahash_data(n, i, dsize) \ | ||
351 | ((struct mtype_elem *)((n)->value + ((i) * (dsize)))) | ||
352 | |||
353 | static void | ||
354 | mtype_ext_cleanup(struct ip_set *set, struct hbucket *n) | ||
355 | { | ||
356 | int i; | ||
357 | |||
358 | for (i = 0; i < n->pos; i++) | ||
359 | ip_set_ext_destroy(set, ahash_data(n, i, set->dsize)); | ||
360 | } | ||
361 | |||
362 | /* Flush a hash type of set: destroy all elements */ | 362 | /* Flush a hash type of set: destroy all elements */ |
363 | static void | 363 | static void |
364 | mtype_flush(struct ip_set *set) | 364 | mtype_flush(struct ip_set *set) |
@@ -372,6 +372,8 @@ mtype_flush(struct ip_set *set) | |||
372 | for (i = 0; i < jhash_size(t->htable_bits); i++) { | 372 | for (i = 0; i < jhash_size(t->htable_bits); i++) { |
373 | n = hbucket(t, i); | 373 | n = hbucket(t, i); |
374 | if (n->size) { | 374 | if (n->size) { |
375 | if (set->extensions & IPSET_EXT_DESTROY) | ||
376 | mtype_ext_cleanup(set, n); | ||
375 | n->size = n->pos = 0; | 377 | n->size = n->pos = 0; |
376 | /* FIXME: use slab cache */ | 378 | /* FIXME: use slab cache */ |
377 | kfree(n->value); | 379 | kfree(n->value); |
@@ -383,6 +385,26 @@ mtype_flush(struct ip_set *set) | |||
383 | h->elements = 0; | 385 | h->elements = 0; |
384 | } | 386 | } |
385 | 387 | ||
388 | /* Destroy the hashtable part of the set */ | ||
389 | static void | ||
390 | mtype_ahash_destroy(struct ip_set *set, struct htable *t) | ||
391 | { | ||
392 | struct hbucket *n; | ||
393 | u32 i; | ||
394 | |||
395 | for (i = 0; i < jhash_size(t->htable_bits); i++) { | ||
396 | n = hbucket(t, i); | ||
397 | if (n->size) { | ||
398 | if (set->extensions & IPSET_EXT_DESTROY) | ||
399 | mtype_ext_cleanup(set, n); | ||
400 | /* FIXME: use slab cache */ | ||
401 | kfree(n->value); | ||
402 | } | ||
403 | } | ||
404 | |||
405 | ip_set_free(t); | ||
406 | } | ||
407 | |||
386 | /* Destroy a hash type of set */ | 408 | /* Destroy a hash type of set */ |
387 | static void | 409 | static void |
388 | mtype_destroy(struct ip_set *set) | 410 | mtype_destroy(struct ip_set *set) |
@@ -392,7 +414,7 @@ mtype_destroy(struct ip_set *set) | |||
392 | if (set->extensions & IPSET_EXT_TIMEOUT) | 414 | if (set->extensions & IPSET_EXT_TIMEOUT) |
393 | del_timer_sync(&h->gc); | 415 | del_timer_sync(&h->gc); |
394 | 416 | ||
395 | ahash_destroy(rcu_dereference_bh_nfnl(h->table)); | 417 | mtype_ahash_destroy(set, rcu_dereference_bh_nfnl(h->table)); |
396 | #ifdef IP_SET_HASH_WITH_RBTREE | 418 | #ifdef IP_SET_HASH_WITH_RBTREE |
397 | rbtree_destroy(&h->rbtree); | 419 | rbtree_destroy(&h->rbtree); |
398 | #endif | 420 | #endif |
@@ -430,10 +452,6 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b) | |||
430 | a->extensions == b->extensions; | 452 | a->extensions == b->extensions; |
431 | } | 453 | } |
432 | 454 | ||
433 | /* Get the ith element from the array block n */ | ||
434 | #define ahash_data(n, i, dsize) \ | ||
435 | ((struct mtype_elem *)((n)->value + ((i) * (dsize)))) | ||
436 | |||
437 | /* Delete expired elements from the hashtable */ | 455 | /* Delete expired elements from the hashtable */ |
438 | static void | 456 | static void |
439 | mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) | 457 | mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) |
@@ -456,6 +474,7 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) | |||
456 | mtype_del_cidr(h, CIDR(data->cidr), | 474 | mtype_del_cidr(h, CIDR(data->cidr), |
457 | nets_length, 0); | 475 | nets_length, 0); |
458 | #endif | 476 | #endif |
477 | ip_set_ext_destroy(set, data); | ||
459 | if (j != n->pos - 1) | 478 | if (j != n->pos - 1) |
460 | /* Not last one */ | 479 | /* Not last one */ |
461 | memcpy(data, | 480 | memcpy(data, |
@@ -557,7 +576,7 @@ retry: | |||
557 | mtype_data_reset_flags(data, &flags); | 576 | mtype_data_reset_flags(data, &flags); |
558 | #endif | 577 | #endif |
559 | read_unlock_bh(&set->lock); | 578 | read_unlock_bh(&set->lock); |
560 | ahash_destroy(t); | 579 | mtype_ahash_destroy(set, t); |
561 | if (ret == -EAGAIN) | 580 | if (ret == -EAGAIN) |
562 | goto retry; | 581 | goto retry; |
563 | return ret; | 582 | return ret; |
@@ -578,7 +597,7 @@ retry: | |||
578 | 597 | ||
579 | pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name, | 598 | pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name, |
580 | orig->htable_bits, orig, t->htable_bits, t); | 599 | orig->htable_bits, orig, t->htable_bits, t); |
581 | ahash_destroy(orig); | 600 | mtype_ahash_destroy(set, orig); |
582 | 601 | ||
583 | return 0; | 602 | return 0; |
584 | } | 603 | } |
@@ -642,6 +661,7 @@ reuse_slot: | |||
642 | mtype_del_cidr(h, CIDR(data->cidr), NLEN(set->family), 0); | 661 | mtype_del_cidr(h, CIDR(data->cidr), NLEN(set->family), 0); |
643 | mtype_add_cidr(h, CIDR(d->cidr), NLEN(set->family), 0); | 662 | mtype_add_cidr(h, CIDR(d->cidr), NLEN(set->family), 0); |
644 | #endif | 663 | #endif |
664 | ip_set_ext_destroy(set, data); | ||
645 | } else { | 665 | } else { |
646 | /* Use/create a new slot */ | 666 | /* Use/create a new slot */ |
647 | TUNE_AHASH_MAX(h, multi); | 667 | TUNE_AHASH_MAX(h, multi); |
@@ -707,6 +727,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, | |||
707 | #ifdef IP_SET_HASH_WITH_NETS | 727 | #ifdef IP_SET_HASH_WITH_NETS |
708 | mtype_del_cidr(h, CIDR(d->cidr), NLEN(set->family), 0); | 728 | mtype_del_cidr(h, CIDR(d->cidr), NLEN(set->family), 0); |
709 | #endif | 729 | #endif |
730 | ip_set_ext_destroy(set, data); | ||
710 | if (n->pos + AHASH_INIT_SIZE < n->size) { | 731 | if (n->pos + AHASH_INIT_SIZE < n->size) { |
711 | void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) | 732 | void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) |
712 | * set->dsize, | 733 | * set->dsize, |
@@ -1033,7 +1054,7 @@ IPSET_TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags) | |||
1033 | rcu_assign_pointer(h->table, t); | 1054 | rcu_assign_pointer(h->table, t); |
1034 | 1055 | ||
1035 | set->data = h; | 1056 | set->data = h; |
1036 | if (set->family == NFPROTO_IPV4) { | 1057 | if (set->family == NFPROTO_IPV4) { |
1037 | set->variant = &IPSET_TOKEN(HTYPE, 4_variant); | 1058 | set->variant = &IPSET_TOKEN(HTYPE, 4_variant); |
1038 | set->dsize = ip_set_elem_len(set, tb, | 1059 | set->dsize = ip_set_elem_len(set, tb, |
1039 | sizeof(struct IPSET_TOKEN(HTYPE, 4_elem))); | 1060 | sizeof(struct IPSET_TOKEN(HTYPE, 4_elem))); |
diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 7fd11c79aff4..e44986af1fc2 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c | |||
@@ -168,16 +168,19 @@ list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d, | |||
168 | struct set_elem *e = list_set_elem(set, map, i); | 168 | struct set_elem *e = list_set_elem(set, map, i); |
169 | 169 | ||
170 | if (e->id != IPSET_INVALID_ID) { | 170 | if (e->id != IPSET_INVALID_ID) { |
171 | if (i == map->size - 1) | 171 | if (i == map->size - 1) { |
172 | /* Last element replaced: e.g. add new,before,last */ | 172 | /* Last element replaced: e.g. add new,before,last */ |
173 | ip_set_put_byindex(e->id); | 173 | ip_set_put_byindex(e->id); |
174 | else { | 174 | ip_set_ext_destroy(set, e); |
175 | } else { | ||
175 | struct set_elem *x = list_set_elem(set, map, | 176 | struct set_elem *x = list_set_elem(set, map, |
176 | map->size - 1); | 177 | map->size - 1); |
177 | 178 | ||
178 | /* Last element pushed off */ | 179 | /* Last element pushed off */ |
179 | if (x->id != IPSET_INVALID_ID) | 180 | if (x->id != IPSET_INVALID_ID) { |
180 | ip_set_put_byindex(x->id); | 181 | ip_set_put_byindex(x->id); |
182 | ip_set_ext_destroy(set, x); | ||
183 | } | ||
181 | memmove(list_set_elem(set, map, i + 1), e, | 184 | memmove(list_set_elem(set, map, i + 1), e, |
182 | set->dsize * (map->size - (i + 1))); | 185 | set->dsize * (map->size - (i + 1))); |
183 | } | 186 | } |
@@ -198,6 +201,7 @@ list_set_del(struct ip_set *set, u32 i) | |||
198 | struct set_elem *e = list_set_elem(set, map, i); | 201 | struct set_elem *e = list_set_elem(set, map, i); |
199 | 202 | ||
200 | ip_set_put_byindex(e->id); | 203 | ip_set_put_byindex(e->id); |
204 | ip_set_ext_destroy(set, e); | ||
201 | 205 | ||
202 | if (i < map->size - 1) | 206 | if (i < map->size - 1) |
203 | memmove(e, list_set_elem(set, map, i + 1), | 207 | memmove(e, list_set_elem(set, map, i + 1), |
@@ -266,14 +270,14 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, | |||
266 | bool flag_exist = flags & IPSET_FLAG_EXIST; | 270 | bool flag_exist = flags & IPSET_FLAG_EXIST; |
267 | u32 i, ret = 0; | 271 | u32 i, ret = 0; |
268 | 272 | ||
273 | if (SET_WITH_TIMEOUT(set)) | ||
274 | set_cleanup_entries(set); | ||
275 | |||
269 | /* Check already added element */ | 276 | /* Check already added element */ |
270 | for (i = 0; i < map->size; i++) { | 277 | for (i = 0; i < map->size; i++) { |
271 | e = list_set_elem(set, map, i); | 278 | e = list_set_elem(set, map, i); |
272 | if (e->id == IPSET_INVALID_ID) | 279 | if (e->id == IPSET_INVALID_ID) |
273 | goto insert; | 280 | goto insert; |
274 | else if (SET_WITH_TIMEOUT(set) && | ||
275 | ip_set_timeout_expired(ext_timeout(e, set))) | ||
276 | continue; | ||
277 | else if (e->id != d->id) | 281 | else if (e->id != d->id) |
278 | continue; | 282 | continue; |
279 | 283 | ||
@@ -286,6 +290,8 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, | |||
286 | /* Can't re-add */ | 290 | /* Can't re-add */ |
287 | return -IPSET_ERR_EXIST; | 291 | return -IPSET_ERR_EXIST; |
288 | /* Update extensions */ | 292 | /* Update extensions */ |
293 | ip_set_ext_destroy(set, e); | ||
294 | |||
289 | if (SET_WITH_TIMEOUT(set)) | 295 | if (SET_WITH_TIMEOUT(set)) |
290 | ip_set_timeout_set(ext_timeout(e, set), ext->timeout); | 296 | ip_set_timeout_set(ext_timeout(e, set), ext->timeout); |
291 | if (SET_WITH_COUNTER(set)) | 297 | if (SET_WITH_COUNTER(set)) |
@@ -423,6 +429,7 @@ list_set_flush(struct ip_set *set) | |||
423 | e = list_set_elem(set, map, i); | 429 | e = list_set_elem(set, map, i); |
424 | if (e->id != IPSET_INVALID_ID) { | 430 | if (e->id != IPSET_INVALID_ID) { |
425 | ip_set_put_byindex(e->id); | 431 | ip_set_put_byindex(e->id); |
432 | ip_set_ext_destroy(set, e); | ||
426 | e->id = IPSET_INVALID_ID; | 433 | e->id = IPSET_INVALID_ID; |
427 | } | 434 | } |
428 | } | 435 | } |