aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2013-09-09 08:44:29 -0400
committerJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2013-09-30 15:33:27 -0400
commit40cd63bf33b21ef4d43776b1d49c605f876fe32c (patch)
tree525daadc3b2e86977e469a39c28749ea9303d7e2
parent03c8b234e61a9a3aab8d970b3bf681934ecfe443 (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.h22
-rw-r--r--net/netfilter/ipset/ip_set_bitmap_gen.h38
-rw-r--r--net/netfilter/ipset/ip_set_hash_gen.h71
-rw-r--r--net/netfilter/ipset/ip_set_list_set.c19
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 */
51enum ip_set_extension { 51enum 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 */
70struct ip_set_ext_type { 72struct 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
95struct ip_set;
96
97static inline void
98ip_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
96struct ip_set;
97
98typedef int (*ipset_adtfn)(struct ip_set *set, void *value, 110typedef 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
49static void 50static void
51mtype_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
61static void
50mtype_destroy(struct ip_set *set) 62mtype_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 */
121static void
122ahash_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
137static int 120static int
138hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) 121hbucket_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
353static void
354mtype_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 */
363static void 363static void
364mtype_flush(struct ip_set *set) 364mtype_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 */
389static void
390mtype_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 */
387static void 409static void
388mtype_destroy(struct ip_set *set) 410mtype_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 */
438static void 456static void
439mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) 457mtype_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 }