diff options
author | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2013-04-08 15:05:44 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-04-29 14:08:56 -0400 |
commit | 1feab10d7e6ddb5e13d6a4bd93a1b877390262ec (patch) | |
tree | 7da5d5aba00e9bd207de7b8c493c589c826f8604 /net | |
parent | b0da3905bb1eb0969470f57b18c978f902475c78 (diff) |
netfilter: ipset: Unified hash type generation
Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/ipset/ip_set_hash_gen.h | 1039 |
1 files changed, 1039 insertions, 0 deletions
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h new file mode 100644 index 000000000000..2ba7d4e76cde --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_gen.h | |||
@@ -0,0 +1,1039 @@ | |||
1 | /* Copyright (C) 2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 as | ||
5 | * published by the Free Software Foundation. | ||
6 | */ | ||
7 | |||
8 | #ifndef _IP_SET_HASH_GEN_H | ||
9 | #define _IP_SET_HASH_GEN_H | ||
10 | |||
11 | #include <linux/rcupdate.h> | ||
12 | #include <linux/jhash.h> | ||
13 | #include <linux/netfilter/ipset/ip_set_timeout.h> | ||
14 | #ifndef rcu_dereference_bh | ||
15 | #define rcu_dereference_bh(p) rcu_dereference(p) | ||
16 | #endif | ||
17 | |||
18 | #define CONCAT(a, b) a##b | ||
19 | #define TOKEN(a, b) CONCAT(a, b) | ||
20 | |||
21 | /* Hashing which uses arrays to resolve clashing. The hash table is resized | ||
22 | * (doubled) when searching becomes too long. | ||
23 | * Internally jhash is used with the assumption that the size of the | ||
24 | * stored data is a multiple of sizeof(u32). If storage supports timeout, | ||
25 | * the timeout field must be the last one in the data structure - that field | ||
26 | * is ignored when computing the hash key. | ||
27 | * | ||
28 | * Readers and resizing | ||
29 | * | ||
30 | * Resizing can be triggered by userspace command only, and those | ||
31 | * are serialized by the nfnl mutex. During resizing the set is | ||
32 | * read-locked, so the only possible concurrent operations are | ||
33 | * the kernel side readers. Those must be protected by proper RCU locking. | ||
34 | */ | ||
35 | |||
36 | /* Number of elements to store in an initial array block */ | ||
37 | #define AHASH_INIT_SIZE 4 | ||
38 | /* Max number of elements to store in an array block */ | ||
39 | #define AHASH_MAX_SIZE (3*AHASH_INIT_SIZE) | ||
40 | |||
41 | /* Max number of elements can be tuned */ | ||
42 | #ifdef IP_SET_HASH_WITH_MULTI | ||
43 | #define AHASH_MAX(h) ((h)->ahash_max) | ||
44 | |||
45 | static inline u8 | ||
46 | tune_ahash_max(u8 curr, u32 multi) | ||
47 | { | ||
48 | u32 n; | ||
49 | |||
50 | if (multi < curr) | ||
51 | return curr; | ||
52 | |||
53 | n = curr + AHASH_INIT_SIZE; | ||
54 | /* Currently, at listing one hash bucket must fit into a message. | ||
55 | * Therefore we have a hard limit here. | ||
56 | */ | ||
57 | return n > curr && n <= 64 ? n : curr; | ||
58 | } | ||
59 | #define TUNE_AHASH_MAX(h, multi) \ | ||
60 | ((h)->ahash_max = tune_ahash_max((h)->ahash_max, multi)) | ||
61 | #else | ||
62 | #define AHASH_MAX(h) AHASH_MAX_SIZE | ||
63 | #define TUNE_AHASH_MAX(h, multi) | ||
64 | #endif | ||
65 | |||
66 | /* A hash bucket */ | ||
67 | struct hbucket { | ||
68 | void *value; /* the array of the values */ | ||
69 | u8 size; /* size of the array */ | ||
70 | u8 pos; /* position of the first free entry */ | ||
71 | }; | ||
72 | |||
73 | /* The hash table: the table size stored here in order to make resizing easy */ | ||
74 | struct htable { | ||
75 | u8 htable_bits; /* size of hash table == 2^htable_bits */ | ||
76 | struct hbucket bucket[0]; /* hashtable buckets */ | ||
77 | }; | ||
78 | |||
79 | #define hbucket(h, i) (&((h)->bucket[i])) | ||
80 | |||
81 | /* Book-keeping of the prefixes added to the set */ | ||
82 | struct net_prefixes { | ||
83 | u8 cidr; /* the different cidr values in the set */ | ||
84 | u32 nets; /* number of elements per cidr */ | ||
85 | }; | ||
86 | |||
87 | /* Compute the hash table size */ | ||
88 | static size_t | ||
89 | htable_size(u8 hbits) | ||
90 | { | ||
91 | size_t hsize; | ||
92 | |||
93 | /* We must fit both into u32 in jhash and size_t */ | ||
94 | if (hbits > 31) | ||
95 | return 0; | ||
96 | hsize = jhash_size(hbits); | ||
97 | if ((((size_t)-1) - sizeof(struct htable))/sizeof(struct hbucket) | ||
98 | < hsize) | ||
99 | return 0; | ||
100 | |||
101 | return hsize * sizeof(struct hbucket) + sizeof(struct htable); | ||
102 | } | ||
103 | |||
104 | /* Compute htable_bits from the user input parameter hashsize */ | ||
105 | static u8 | ||
106 | htable_bits(u32 hashsize) | ||
107 | { | ||
108 | /* Assume that hashsize == 2^htable_bits */ | ||
109 | u8 bits = fls(hashsize - 1); | ||
110 | if (jhash_size(bits) != hashsize) | ||
111 | /* Round up to the first 2^n value */ | ||
112 | bits = fls(hashsize); | ||
113 | |||
114 | return bits; | ||
115 | } | ||
116 | |||
117 | /* Destroy the hashtable part of the set */ | ||
118 | static void | ||
119 | ahash_destroy(struct htable *t) | ||
120 | { | ||
121 | struct hbucket *n; | ||
122 | u32 i; | ||
123 | |||
124 | for (i = 0; i < jhash_size(t->htable_bits); i++) { | ||
125 | n = hbucket(t, i); | ||
126 | if (n->size) | ||
127 | /* FIXME: use slab cache */ | ||
128 | kfree(n->value); | ||
129 | } | ||
130 | |||
131 | ip_set_free(t); | ||
132 | } | ||
133 | |||
134 | static int | ||
135 | hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) | ||
136 | { | ||
137 | if (n->pos >= n->size) { | ||
138 | void *tmp; | ||
139 | |||
140 | if (n->size >= ahash_max) | ||
141 | /* Trigger rehashing */ | ||
142 | return -EAGAIN; | ||
143 | |||
144 | tmp = kzalloc((n->size + AHASH_INIT_SIZE) * dsize, | ||
145 | GFP_ATOMIC); | ||
146 | if (!tmp) | ||
147 | return -ENOMEM; | ||
148 | if (n->size) { | ||
149 | memcpy(tmp, n->value, n->size * dsize); | ||
150 | kfree(n->value); | ||
151 | } | ||
152 | n->value = tmp; | ||
153 | n->size += AHASH_INIT_SIZE; | ||
154 | } | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | #ifdef IP_SET_HASH_WITH_NETS | ||
159 | #ifdef IP_SET_HASH_WITH_NETS_PACKED | ||
160 | /* When cidr is packed with nomatch, cidr - 1 is stored in the entry */ | ||
161 | #define CIDR(cidr) (cidr + 1) | ||
162 | #else | ||
163 | #define CIDR(cidr) (cidr) | ||
164 | #endif | ||
165 | |||
166 | #define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) | ||
167 | |||
168 | #ifdef IP_SET_HASH_WITH_MULTI | ||
169 | #define NETS_LENGTH(family) (SET_HOST_MASK(family) + 1) | ||
170 | #else | ||
171 | #define NETS_LENGTH(family) SET_HOST_MASK(family) | ||
172 | #endif | ||
173 | |||
174 | #else | ||
175 | #define NETS_LENGTH(family) 0 | ||
176 | #endif /* IP_SET_HASH_WITH_NETS */ | ||
177 | |||
178 | #define ext_timeout(e, h) \ | ||
179 | (unsigned long *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_TIMEOUT]) | ||
180 | |||
181 | #endif /* _IP_SET_HASH_GEN_H */ | ||
182 | |||
183 | /* Family dependent templates */ | ||
184 | |||
185 | #undef ahash_data | ||
186 | #undef mtype_data_equal | ||
187 | #undef mtype_do_data_match | ||
188 | #undef mtype_data_set_flags | ||
189 | #undef mtype_data_reset_flags | ||
190 | #undef mtype_data_netmask | ||
191 | #undef mtype_data_list | ||
192 | #undef mtype_data_next | ||
193 | #undef mtype_elem | ||
194 | |||
195 | #undef mtype_add_cidr | ||
196 | #undef mtype_del_cidr | ||
197 | #undef mtype_ahash_memsize | ||
198 | #undef mtype_flush | ||
199 | #undef mtype_destroy | ||
200 | #undef mtype_gc_init | ||
201 | #undef mtype_same_set | ||
202 | #undef mtype_kadt | ||
203 | #undef mtype_uadt | ||
204 | #undef mtype | ||
205 | |||
206 | #undef mtype_add | ||
207 | #undef mtype_del | ||
208 | #undef mtype_test_cidrs | ||
209 | #undef mtype_test | ||
210 | #undef mtype_expire | ||
211 | #undef mtype_resize | ||
212 | #undef mtype_head | ||
213 | #undef mtype_list | ||
214 | #undef mtype_gc | ||
215 | #undef mtype_gc_init | ||
216 | #undef mtype_variant | ||
217 | #undef mtype_data_match | ||
218 | |||
219 | #undef HKEY | ||
220 | |||
221 | #define mtype_data_equal TOKEN(MTYPE, _data_equal) | ||
222 | #ifdef IP_SET_HASH_WITH_NETS | ||
223 | #define mtype_do_data_match TOKEN(MTYPE, _do_data_match) | ||
224 | #else | ||
225 | #define mtype_do_data_match(d) 1 | ||
226 | #endif | ||
227 | #define mtype_data_set_flags TOKEN(MTYPE, _data_set_flags) | ||
228 | #define mtype_data_reset_flags TOKEN(MTYPE, _data_reset_flags) | ||
229 | #define mtype_data_netmask TOKEN(MTYPE, _data_netmask) | ||
230 | #define mtype_data_list TOKEN(MTYPE, _data_list) | ||
231 | #define mtype_data_next TOKEN(MTYPE, _data_next) | ||
232 | #define mtype_elem TOKEN(MTYPE, _elem) | ||
233 | #define mtype_add_cidr TOKEN(MTYPE, _add_cidr) | ||
234 | #define mtype_del_cidr TOKEN(MTYPE, _del_cidr) | ||
235 | #define mtype_ahash_memsize TOKEN(MTYPE, _ahash_memsize) | ||
236 | #define mtype_flush TOKEN(MTYPE, _flush) | ||
237 | #define mtype_destroy TOKEN(MTYPE, _destroy) | ||
238 | #define mtype_gc_init TOKEN(MTYPE, _gc_init) | ||
239 | #define mtype_same_set TOKEN(MTYPE, _same_set) | ||
240 | #define mtype_kadt TOKEN(MTYPE, _kadt) | ||
241 | #define mtype_uadt TOKEN(MTYPE, _uadt) | ||
242 | #define mtype MTYPE | ||
243 | |||
244 | #define mtype_elem TOKEN(MTYPE, _elem) | ||
245 | #define mtype_add TOKEN(MTYPE, _add) | ||
246 | #define mtype_del TOKEN(MTYPE, _del) | ||
247 | #define mtype_test_cidrs TOKEN(MTYPE, _test_cidrs) | ||
248 | #define mtype_test TOKEN(MTYPE, _test) | ||
249 | #define mtype_expire TOKEN(MTYPE, _expire) | ||
250 | #define mtype_resize TOKEN(MTYPE, _resize) | ||
251 | #define mtype_head TOKEN(MTYPE, _head) | ||
252 | #define mtype_list TOKEN(MTYPE, _list) | ||
253 | #define mtype_gc TOKEN(MTYPE, _gc) | ||
254 | #define mtype_variant TOKEN(MTYPE, _variant) | ||
255 | #define mtype_data_match TOKEN(MTYPE, _data_match) | ||
256 | |||
257 | #ifndef HKEY_DATALEN | ||
258 | #define HKEY_DATALEN sizeof(struct mtype_elem) | ||
259 | #endif | ||
260 | |||
261 | #define HKEY(data, initval, htable_bits) \ | ||
262 | (jhash2((u32 *)(data), HKEY_DATALEN/sizeof(u32), initval) \ | ||
263 | & jhash_mask(htable_bits)) | ||
264 | |||
265 | #ifndef htype | ||
266 | #define htype HTYPE | ||
267 | |||
268 | /* The generic hash structure */ | ||
269 | struct htype { | ||
270 | struct htable *table; /* the hash table */ | ||
271 | u32 maxelem; /* max elements in the hash */ | ||
272 | u32 elements; /* current element (vs timeout) */ | ||
273 | u32 initval; /* random jhash init value */ | ||
274 | u32 timeout; /* timeout value, if enabled */ | ||
275 | size_t dsize; /* data struct size */ | ||
276 | size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ | ||
277 | struct timer_list gc; /* garbage collection when timeout enabled */ | ||
278 | struct mtype_elem next; /* temporary storage for uadd */ | ||
279 | #ifdef IP_SET_HASH_WITH_MULTI | ||
280 | u8 ahash_max; /* max elements in an array block */ | ||
281 | #endif | ||
282 | #ifdef IP_SET_HASH_WITH_NETMASK | ||
283 | u8 netmask; /* netmask value for subnets to store */ | ||
284 | #endif | ||
285 | #ifdef IP_SET_HASH_WITH_RBTREE | ||
286 | struct rb_root rbtree; | ||
287 | #endif | ||
288 | #ifdef IP_SET_HASH_WITH_NETS | ||
289 | struct net_prefixes nets[0]; /* book-keeping of prefixes */ | ||
290 | #endif | ||
291 | }; | ||
292 | #endif | ||
293 | |||
294 | #ifdef IP_SET_HASH_WITH_NETS | ||
295 | /* Network cidr size book keeping when the hash stores different | ||
296 | * sized networks */ | ||
297 | static void | ||
298 | mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length) | ||
299 | { | ||
300 | int i, j; | ||
301 | |||
302 | /* Add in increasing prefix order, so larger cidr first */ | ||
303 | for (i = 0, j = -1; i < nets_length && h->nets[i].nets; i++) { | ||
304 | if (j != -1) | ||
305 | continue; | ||
306 | else if (h->nets[i].cidr < cidr) | ||
307 | j = i; | ||
308 | else if (h->nets[i].cidr == cidr) { | ||
309 | h->nets[i].nets++; | ||
310 | return; | ||
311 | } | ||
312 | } | ||
313 | if (j != -1) { | ||
314 | for (; i > j; i--) { | ||
315 | h->nets[i].cidr = h->nets[i - 1].cidr; | ||
316 | h->nets[i].nets = h->nets[i - 1].nets; | ||
317 | } | ||
318 | } | ||
319 | h->nets[i].cidr = cidr; | ||
320 | h->nets[i].nets = 1; | ||
321 | } | ||
322 | |||
323 | static void | ||
324 | mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length) | ||
325 | { | ||
326 | u8 i, j; | ||
327 | |||
328 | for (i = 0; i < nets_length - 1 && h->nets[i].cidr != cidr; i++) | ||
329 | ; | ||
330 | h->nets[i].nets--; | ||
331 | |||
332 | if (h->nets[i].nets != 0) | ||
333 | return; | ||
334 | |||
335 | for (j = i; j < nets_length - 1 && h->nets[j].nets; j++) { | ||
336 | h->nets[j].cidr = h->nets[j + 1].cidr; | ||
337 | h->nets[j].nets = h->nets[j + 1].nets; | ||
338 | } | ||
339 | } | ||
340 | #endif | ||
341 | |||
342 | /* Calculate the actual memory size of the set data */ | ||
343 | static size_t | ||
344 | mtype_ahash_memsize(const struct htype *h, u8 nets_length) | ||
345 | { | ||
346 | u32 i; | ||
347 | struct htable *t = h->table; | ||
348 | size_t memsize = sizeof(*h) | ||
349 | + sizeof(*t) | ||
350 | #ifdef IP_SET_HASH_WITH_NETS | ||
351 | + sizeof(struct net_prefixes) * nets_length | ||
352 | #endif | ||
353 | + jhash_size(t->htable_bits) * sizeof(struct hbucket); | ||
354 | |||
355 | for (i = 0; i < jhash_size(t->htable_bits); i++) | ||
356 | memsize += t->bucket[i].size * h->dsize; | ||
357 | |||
358 | return memsize; | ||
359 | } | ||
360 | |||
361 | /* Flush a hash type of set: destroy all elements */ | ||
362 | static void | ||
363 | mtype_flush(struct ip_set *set) | ||
364 | { | ||
365 | struct htype *h = set->data; | ||
366 | struct htable *t = h->table; | ||
367 | struct hbucket *n; | ||
368 | u32 i; | ||
369 | |||
370 | for (i = 0; i < jhash_size(t->htable_bits); i++) { | ||
371 | n = hbucket(t, i); | ||
372 | if (n->size) { | ||
373 | n->size = n->pos = 0; | ||
374 | /* FIXME: use slab cache */ | ||
375 | kfree(n->value); | ||
376 | } | ||
377 | } | ||
378 | #ifdef IP_SET_HASH_WITH_NETS | ||
379 | memset(h->nets, 0, sizeof(struct net_prefixes) | ||
380 | * NETS_LENGTH(set->family)); | ||
381 | #endif | ||
382 | h->elements = 0; | ||
383 | } | ||
384 | |||
385 | /* Destroy a hash type of set */ | ||
386 | static void | ||
387 | mtype_destroy(struct ip_set *set) | ||
388 | { | ||
389 | struct htype *h = set->data; | ||
390 | |||
391 | if (set->extensions & IPSET_EXT_TIMEOUT) | ||
392 | del_timer_sync(&h->gc); | ||
393 | |||
394 | ahash_destroy(h->table); | ||
395 | #ifdef IP_SET_HASH_WITH_RBTREE | ||
396 | rbtree_destroy(&h->rbtree); | ||
397 | #endif | ||
398 | kfree(h); | ||
399 | |||
400 | set->data = NULL; | ||
401 | } | ||
402 | |||
403 | static void | ||
404 | mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) | ||
405 | { | ||
406 | struct htype *h = set->data; | ||
407 | |||
408 | init_timer(&h->gc); | ||
409 | h->gc.data = (unsigned long) set; | ||
410 | h->gc.function = gc; | ||
411 | h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; | ||
412 | add_timer(&h->gc); | ||
413 | pr_debug("gc initialized, run in every %u\n", | ||
414 | IPSET_GC_PERIOD(h->timeout)); | ||
415 | } | ||
416 | |||
417 | static bool | ||
418 | mtype_same_set(const struct ip_set *a, const struct ip_set *b) | ||
419 | { | ||
420 | const struct htype *x = a->data; | ||
421 | const struct htype *y = b->data; | ||
422 | |||
423 | /* Resizing changes htable_bits, so we ignore it */ | ||
424 | return x->maxelem == y->maxelem && | ||
425 | x->timeout == y->timeout && | ||
426 | #ifdef IP_SET_HASH_WITH_NETMASK | ||
427 | x->netmask == y->netmask && | ||
428 | #endif | ||
429 | a->extensions == b->extensions; | ||
430 | } | ||
431 | |||
432 | /* Get the ith element from the array block n */ | ||
433 | #define ahash_data(n, i, dsize) \ | ||
434 | ((struct mtype_elem *)((n)->value + ((i) * (dsize)))) | ||
435 | |||
436 | /* Delete expired elements from the hashtable */ | ||
437 | static void | ||
438 | mtype_expire(struct htype *h, u8 nets_length, size_t dsize) | ||
439 | { | ||
440 | struct htable *t = h->table; | ||
441 | struct hbucket *n; | ||
442 | struct mtype_elem *data; | ||
443 | u32 i; | ||
444 | int j; | ||
445 | |||
446 | for (i = 0; i < jhash_size(t->htable_bits); i++) { | ||
447 | n = hbucket(t, i); | ||
448 | for (j = 0; j < n->pos; j++) { | ||
449 | data = ahash_data(n, j, dsize); | ||
450 | if (ip_set_timeout_expired(ext_timeout(data, h))) { | ||
451 | pr_debug("expired %u/%u\n", i, j); | ||
452 | #ifdef IP_SET_HASH_WITH_NETS | ||
453 | mtype_del_cidr(h, CIDR(data->cidr), | ||
454 | nets_length); | ||
455 | #endif | ||
456 | if (j != n->pos - 1) | ||
457 | /* Not last one */ | ||
458 | memcpy(data, | ||
459 | ahash_data(n, n->pos - 1, dsize), | ||
460 | dsize); | ||
461 | n->pos--; | ||
462 | h->elements--; | ||
463 | } | ||
464 | } | ||
465 | if (n->pos + AHASH_INIT_SIZE < n->size) { | ||
466 | void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) | ||
467 | * dsize, | ||
468 | GFP_ATOMIC); | ||
469 | if (!tmp) | ||
470 | /* Still try to delete expired elements */ | ||
471 | continue; | ||
472 | n->size -= AHASH_INIT_SIZE; | ||
473 | memcpy(tmp, n->value, n->size * dsize); | ||
474 | kfree(n->value); | ||
475 | n->value = tmp; | ||
476 | } | ||
477 | } | ||
478 | } | ||
479 | |||
480 | static void | ||
481 | mtype_gc(unsigned long ul_set) | ||
482 | { | ||
483 | struct ip_set *set = (struct ip_set *) ul_set; | ||
484 | struct htype *h = set->data; | ||
485 | |||
486 | pr_debug("called\n"); | ||
487 | write_lock_bh(&set->lock); | ||
488 | mtype_expire(h, NETS_LENGTH(set->family), h->dsize); | ||
489 | write_unlock_bh(&set->lock); | ||
490 | |||
491 | h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; | ||
492 | add_timer(&h->gc); | ||
493 | } | ||
494 | |||
495 | /* Resize a hash: create a new hash table with doubling the hashsize | ||
496 | * and inserting the elements to it. Repeat until we succeed or | ||
497 | * fail due to memory pressures. */ | ||
498 | static int | ||
499 | mtype_resize(struct ip_set *set, bool retried) | ||
500 | { | ||
501 | struct htype *h = set->data; | ||
502 | struct htable *t, *orig = h->table; | ||
503 | u8 htable_bits = orig->htable_bits; | ||
504 | #ifdef IP_SET_HASH_WITH_NETS | ||
505 | u8 flags; | ||
506 | #endif | ||
507 | struct mtype_elem *data; | ||
508 | struct mtype_elem *d; | ||
509 | struct hbucket *n, *m; | ||
510 | u32 i, j; | ||
511 | int ret; | ||
512 | |||
513 | /* Try to cleanup once */ | ||
514 | if (SET_WITH_TIMEOUT(set) && !retried) { | ||
515 | i = h->elements; | ||
516 | write_lock_bh(&set->lock); | ||
517 | mtype_expire(set->data, NETS_LENGTH(set->family), | ||
518 | h->dsize); | ||
519 | write_unlock_bh(&set->lock); | ||
520 | if (h->elements < i) | ||
521 | return 0; | ||
522 | } | ||
523 | |||
524 | retry: | ||
525 | ret = 0; | ||
526 | htable_bits++; | ||
527 | pr_debug("attempt to resize set %s from %u to %u, t %p\n", | ||
528 | set->name, orig->htable_bits, htable_bits, orig); | ||
529 | if (!htable_bits) { | ||
530 | /* In case we have plenty of memory :-) */ | ||
531 | pr_warning("Cannot increase the hashsize of set %s further\n", | ||
532 | set->name); | ||
533 | return -IPSET_ERR_HASH_FULL; | ||
534 | } | ||
535 | t = ip_set_alloc(sizeof(*t) | ||
536 | + jhash_size(htable_bits) * sizeof(struct hbucket)); | ||
537 | if (!t) | ||
538 | return -ENOMEM; | ||
539 | t->htable_bits = htable_bits; | ||
540 | |||
541 | read_lock_bh(&set->lock); | ||
542 | for (i = 0; i < jhash_size(orig->htable_bits); i++) { | ||
543 | n = hbucket(orig, i); | ||
544 | for (j = 0; j < n->pos; j++) { | ||
545 | data = ahash_data(n, j, h->dsize); | ||
546 | #ifdef IP_SET_HASH_WITH_NETS | ||
547 | flags = 0; | ||
548 | mtype_data_reset_flags(data, &flags); | ||
549 | #endif | ||
550 | m = hbucket(t, HKEY(data, h->initval, htable_bits)); | ||
551 | ret = hbucket_elem_add(m, AHASH_MAX(h), h->dsize); | ||
552 | if (ret < 0) { | ||
553 | #ifdef IP_SET_HASH_WITH_NETS | ||
554 | mtype_data_reset_flags(data, &flags); | ||
555 | #endif | ||
556 | read_unlock_bh(&set->lock); | ||
557 | ahash_destroy(t); | ||
558 | if (ret == -EAGAIN) | ||
559 | goto retry; | ||
560 | return ret; | ||
561 | } | ||
562 | d = ahash_data(m, m->pos++, h->dsize); | ||
563 | memcpy(d, data, h->dsize); | ||
564 | #ifdef IP_SET_HASH_WITH_NETS | ||
565 | mtype_data_reset_flags(d, &flags); | ||
566 | #endif | ||
567 | } | ||
568 | } | ||
569 | |||
570 | rcu_assign_pointer(h->table, t); | ||
571 | read_unlock_bh(&set->lock); | ||
572 | |||
573 | /* Give time to other readers of the set */ | ||
574 | synchronize_rcu_bh(); | ||
575 | |||
576 | pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name, | ||
577 | orig->htable_bits, orig, t->htable_bits, t); | ||
578 | ahash_destroy(orig); | ||
579 | |||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | /* Add an element to a hash and update the internal counters when succeeded, | ||
584 | * otherwise report the proper error code. */ | ||
585 | static int | ||
586 | mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, | ||
587 | struct ip_set_ext *mext, u32 flags) | ||
588 | { | ||
589 | struct htype *h = set->data; | ||
590 | struct htable *t; | ||
591 | const struct mtype_elem *d = value; | ||
592 | struct mtype_elem *data; | ||
593 | struct hbucket *n; | ||
594 | int i, ret = 0; | ||
595 | int j = AHASH_MAX(h) + 1; | ||
596 | bool flag_exist = flags & IPSET_FLAG_EXIST; | ||
597 | u32 key, multi = 0; | ||
598 | |||
599 | if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem) | ||
600 | /* FIXME: when set is full, we slow down here */ | ||
601 | mtype_expire(h, NETS_LENGTH(set->family), h->dsize); | ||
602 | |||
603 | if (h->elements >= h->maxelem) { | ||
604 | if (net_ratelimit()) | ||
605 | pr_warning("Set %s is full, maxelem %u reached\n", | ||
606 | set->name, h->maxelem); | ||
607 | return -IPSET_ERR_HASH_FULL; | ||
608 | } | ||
609 | |||
610 | rcu_read_lock_bh(); | ||
611 | t = rcu_dereference_bh(h->table); | ||
612 | key = HKEY(value, h->initval, t->htable_bits); | ||
613 | n = hbucket(t, key); | ||
614 | for (i = 0; i < n->pos; i++) { | ||
615 | data = ahash_data(n, i, h->dsize); | ||
616 | if (mtype_data_equal(data, d, &multi)) { | ||
617 | if (flag_exist || | ||
618 | (SET_WITH_TIMEOUT(set) && | ||
619 | ip_set_timeout_expired(ext_timeout(data, h)))) { | ||
620 | /* Just the extensions could be overwritten */ | ||
621 | j = i; | ||
622 | goto reuse_slot; | ||
623 | } else { | ||
624 | ret = -IPSET_ERR_EXIST; | ||
625 | goto out; | ||
626 | } | ||
627 | } | ||
628 | /* Reuse first timed out entry */ | ||
629 | if (SET_WITH_TIMEOUT(set) && | ||
630 | ip_set_timeout_expired(ext_timeout(data, h)) && | ||
631 | j != AHASH_MAX(h) + 1) | ||
632 | j = i; | ||
633 | } | ||
634 | reuse_slot: | ||
635 | if (j != AHASH_MAX(h) + 1) { | ||
636 | /* Fill out reused slot */ | ||
637 | data = ahash_data(n, j, h->dsize); | ||
638 | #ifdef IP_SET_HASH_WITH_NETS | ||
639 | mtype_del_cidr(h, CIDR(data->cidr), NETS_LENGTH(set->family)); | ||
640 | mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); | ||
641 | #endif | ||
642 | } else { | ||
643 | /* Use/create a new slot */ | ||
644 | TUNE_AHASH_MAX(h, multi); | ||
645 | ret = hbucket_elem_add(n, AHASH_MAX(h), h->dsize); | ||
646 | if (ret != 0) { | ||
647 | if (ret == -EAGAIN) | ||
648 | mtype_data_next(&h->next, d); | ||
649 | goto out; | ||
650 | } | ||
651 | data = ahash_data(n, n->pos++, h->dsize); | ||
652 | #ifdef IP_SET_HASH_WITH_NETS | ||
653 | mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); | ||
654 | #endif | ||
655 | h->elements++; | ||
656 | } | ||
657 | memcpy(data, d, sizeof(struct mtype_elem)); | ||
658 | #ifdef IP_SET_HASH_WITH_NETS | ||
659 | mtype_data_set_flags(data, flags); | ||
660 | #endif | ||
661 | if (SET_WITH_TIMEOUT(set)) | ||
662 | ip_set_timeout_set(ext_timeout(data, h), ext->timeout); | ||
663 | |||
664 | out: | ||
665 | rcu_read_unlock_bh(); | ||
666 | return ret; | ||
667 | } | ||
668 | |||
669 | /* Delete an element from the hash: swap it with the last element | ||
670 | * and free up space if possible. | ||
671 | */ | ||
672 | static int | ||
673 | mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, | ||
674 | struct ip_set_ext *mext, u32 flags) | ||
675 | { | ||
676 | struct htype *h = set->data; | ||
677 | struct htable *t = h->table; | ||
678 | const struct mtype_elem *d = value; | ||
679 | struct mtype_elem *data; | ||
680 | struct hbucket *n; | ||
681 | int i; | ||
682 | u32 key, multi = 0; | ||
683 | |||
684 | key = HKEY(value, h->initval, t->htable_bits); | ||
685 | n = hbucket(t, key); | ||
686 | for (i = 0; i < n->pos; i++) { | ||
687 | data = ahash_data(n, i, h->dsize); | ||
688 | if (!mtype_data_equal(data, d, &multi)) | ||
689 | continue; | ||
690 | if (SET_WITH_TIMEOUT(set) && | ||
691 | ip_set_timeout_expired(ext_timeout(data, h))) | ||
692 | return -IPSET_ERR_EXIST; | ||
693 | if (i != n->pos - 1) | ||
694 | /* Not last one */ | ||
695 | memcpy(data, ahash_data(n, n->pos - 1, h->dsize), | ||
696 | h->dsize); | ||
697 | |||
698 | n->pos--; | ||
699 | h->elements--; | ||
700 | #ifdef IP_SET_HASH_WITH_NETS | ||
701 | mtype_del_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); | ||
702 | #endif | ||
703 | if (n->pos + AHASH_INIT_SIZE < n->size) { | ||
704 | void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) | ||
705 | * h->dsize, | ||
706 | GFP_ATOMIC); | ||
707 | if (!tmp) | ||
708 | return 0; | ||
709 | n->size -= AHASH_INIT_SIZE; | ||
710 | memcpy(tmp, n->value, n->size * h->dsize); | ||
711 | kfree(n->value); | ||
712 | n->value = tmp; | ||
713 | } | ||
714 | return 0; | ||
715 | } | ||
716 | |||
717 | return -IPSET_ERR_EXIST; | ||
718 | } | ||
719 | |||
720 | static inline int | ||
721 | mtype_data_match(struct mtype_elem *data, const struct ip_set_ext *ext, | ||
722 | struct ip_set_ext *mext, struct ip_set *set, u32 flags) | ||
723 | { | ||
724 | return mtype_do_data_match(data); | ||
725 | } | ||
726 | |||
727 | #ifdef IP_SET_HASH_WITH_NETS | ||
728 | /* Special test function which takes into account the different network | ||
729 | * sizes added to the set */ | ||
730 | static int | ||
731 | mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, | ||
732 | const struct ip_set_ext *ext, | ||
733 | struct ip_set_ext *mext, u32 flags) | ||
734 | { | ||
735 | struct htype *h = set->data; | ||
736 | struct htable *t = h->table; | ||
737 | struct hbucket *n; | ||
738 | struct mtype_elem *data; | ||
739 | int i, j = 0; | ||
740 | u32 key, multi = 0; | ||
741 | u8 nets_length = NETS_LENGTH(set->family); | ||
742 | |||
743 | pr_debug("test by nets\n"); | ||
744 | for (; j < nets_length && h->nets[j].nets && !multi; j++) { | ||
745 | mtype_data_netmask(d, h->nets[j].cidr); | ||
746 | key = HKEY(d, h->initval, t->htable_bits); | ||
747 | n = hbucket(t, key); | ||
748 | for (i = 0; i < n->pos; i++) { | ||
749 | data = ahash_data(n, i, h->dsize); | ||
750 | if (!mtype_data_equal(data, d, &multi)) | ||
751 | continue; | ||
752 | if (SET_WITH_TIMEOUT(set)) { | ||
753 | if (!ip_set_timeout_expired( | ||
754 | ext_timeout(data, h))) | ||
755 | return mtype_data_match(data, ext, | ||
756 | mext, set, | ||
757 | flags); | ||
758 | #ifdef IP_SET_HASH_WITH_MULTI | ||
759 | multi = 0; | ||
760 | #endif | ||
761 | } else | ||
762 | return mtype_data_match(data, ext, | ||
763 | mext, set, flags); | ||
764 | } | ||
765 | } | ||
766 | return 0; | ||
767 | } | ||
768 | #endif | ||
769 | |||
770 | /* Test whether the element is added to the set */ | ||
771 | static int | ||
772 | mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, | ||
773 | struct ip_set_ext *mext, u32 flags) | ||
774 | { | ||
775 | struct htype *h = set->data; | ||
776 | struct htable *t = h->table; | ||
777 | struct mtype_elem *d = value; | ||
778 | struct hbucket *n; | ||
779 | struct mtype_elem *data; | ||
780 | int i; | ||
781 | u32 key, multi = 0; | ||
782 | |||
783 | #ifdef IP_SET_HASH_WITH_NETS | ||
784 | /* If we test an IP address and not a network address, | ||
785 | * try all possible network sizes */ | ||
786 | if (CIDR(d->cidr) == SET_HOST_MASK(set->family)) | ||
787 | return mtype_test_cidrs(set, d, ext, mext, flags); | ||
788 | #endif | ||
789 | |||
790 | key = HKEY(d, h->initval, t->htable_bits); | ||
791 | n = hbucket(t, key); | ||
792 | for (i = 0; i < n->pos; i++) { | ||
793 | data = ahash_data(n, i, h->dsize); | ||
794 | if (mtype_data_equal(data, d, &multi) && | ||
795 | !(SET_WITH_TIMEOUT(set) && | ||
796 | ip_set_timeout_expired(ext_timeout(data, h)))) | ||
797 | return mtype_data_match(data, ext, mext, set, flags); | ||
798 | } | ||
799 | return 0; | ||
800 | } | ||
801 | |||
802 | /* Reply a HEADER request: fill out the header part of the set */ | ||
803 | static int | ||
804 | mtype_head(struct ip_set *set, struct sk_buff *skb) | ||
805 | { | ||
806 | const struct htype *h = set->data; | ||
807 | struct nlattr *nested; | ||
808 | size_t memsize; | ||
809 | |||
810 | read_lock_bh(&set->lock); | ||
811 | memsize = mtype_ahash_memsize(h, NETS_LENGTH(set->family)); | ||
812 | read_unlock_bh(&set->lock); | ||
813 | |||
814 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
815 | if (!nested) | ||
816 | goto nla_put_failure; | ||
817 | if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE, | ||
818 | htonl(jhash_size(h->table->htable_bits))) || | ||
819 | nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) | ||
820 | goto nla_put_failure; | ||
821 | #ifdef IP_SET_HASH_WITH_NETMASK | ||
822 | if (h->netmask != HOST_MASK && | ||
823 | nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask)) | ||
824 | goto nla_put_failure; | ||
825 | #endif | ||
826 | if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || | ||
827 | nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) || | ||
828 | ((set->extensions & IPSET_EXT_TIMEOUT) && | ||
829 | nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout)))) | ||
830 | goto nla_put_failure; | ||
831 | ipset_nest_end(skb, nested); | ||
832 | |||
833 | return 0; | ||
834 | nla_put_failure: | ||
835 | return -EMSGSIZE; | ||
836 | } | ||
837 | |||
838 | /* Reply a LIST/SAVE request: dump the elements of the specified set */ | ||
839 | static int | ||
840 | mtype_list(const struct ip_set *set, | ||
841 | struct sk_buff *skb, struct netlink_callback *cb) | ||
842 | { | ||
843 | const struct htype *h = set->data; | ||
844 | const struct htable *t = h->table; | ||
845 | struct nlattr *atd, *nested; | ||
846 | const struct hbucket *n; | ||
847 | const struct mtype_elem *e; | ||
848 | u32 first = cb->args[2]; | ||
849 | /* We assume that one hash bucket fills into one page */ | ||
850 | void *incomplete; | ||
851 | int i; | ||
852 | |||
853 | atd = ipset_nest_start(skb, IPSET_ATTR_ADT); | ||
854 | if (!atd) | ||
855 | return -EMSGSIZE; | ||
856 | pr_debug("list hash set %s\n", set->name); | ||
857 | for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) { | ||
858 | incomplete = skb_tail_pointer(skb); | ||
859 | n = hbucket(t, cb->args[2]); | ||
860 | pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n); | ||
861 | for (i = 0; i < n->pos; i++) { | ||
862 | e = ahash_data(n, i, h->dsize); | ||
863 | if (SET_WITH_TIMEOUT(set) && | ||
864 | ip_set_timeout_expired(ext_timeout(e, h))) | ||
865 | continue; | ||
866 | pr_debug("list hash %lu hbucket %p i %u, data %p\n", | ||
867 | cb->args[2], n, i, e); | ||
868 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | ||
869 | if (!nested) { | ||
870 | if (cb->args[2] == first) { | ||
871 | nla_nest_cancel(skb, atd); | ||
872 | return -EMSGSIZE; | ||
873 | } else | ||
874 | goto nla_put_failure; | ||
875 | } | ||
876 | if (mtype_data_list(skb, e)) | ||
877 | goto nla_put_failure; | ||
878 | if (SET_WITH_TIMEOUT(set) && | ||
879 | nla_put_net32(skb, IPSET_ATTR_TIMEOUT, | ||
880 | htonl(ip_set_timeout_get( | ||
881 | ext_timeout(e, h))))) | ||
882 | goto nla_put_failure; | ||
883 | ipset_nest_end(skb, nested); | ||
884 | } | ||
885 | } | ||
886 | ipset_nest_end(skb, atd); | ||
887 | /* Set listing finished */ | ||
888 | cb->args[2] = 0; | ||
889 | |||
890 | return 0; | ||
891 | |||
892 | nla_put_failure: | ||
893 | nlmsg_trim(skb, incomplete); | ||
894 | ipset_nest_end(skb, atd); | ||
895 | if (unlikely(first == cb->args[2])) { | ||
896 | pr_warning("Can't list set %s: one bucket does not fit into " | ||
897 | "a message. Please report it!\n", set->name); | ||
898 | cb->args[2] = 0; | ||
899 | return -EMSGSIZE; | ||
900 | } | ||
901 | return 0; | ||
902 | } | ||
903 | |||
904 | static int | ||
905 | TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb, | ||
906 | const struct xt_action_param *par, | ||
907 | enum ipset_adt adt, struct ip_set_adt_opt *opt); | ||
908 | |||
909 | static int | ||
910 | TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[], | ||
911 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried); | ||
912 | |||
913 | static const struct ip_set_type_variant mtype_variant = { | ||
914 | .kadt = mtype_kadt, | ||
915 | .uadt = mtype_uadt, | ||
916 | .adt = { | ||
917 | [IPSET_ADD] = mtype_add, | ||
918 | [IPSET_DEL] = mtype_del, | ||
919 | [IPSET_TEST] = mtype_test, | ||
920 | }, | ||
921 | .destroy = mtype_destroy, | ||
922 | .flush = mtype_flush, | ||
923 | .head = mtype_head, | ||
924 | .list = mtype_list, | ||
925 | .resize = mtype_resize, | ||
926 | .same_set = mtype_same_set, | ||
927 | }; | ||
928 | |||
929 | #ifdef IP_SET_EMIT_CREATE | ||
930 | static int | ||
931 | TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags) | ||
932 | { | ||
933 | u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; | ||
934 | u8 hbits; | ||
935 | #ifdef IP_SET_HASH_WITH_NETMASK | ||
936 | u8 netmask; | ||
937 | #endif | ||
938 | size_t hsize; | ||
939 | struct HTYPE *h; | ||
940 | |||
941 | if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) | ||
942 | return -IPSET_ERR_INVALID_FAMILY; | ||
943 | #ifdef IP_SET_HASH_WITH_NETMASK | ||
944 | netmask = set->family == NFPROTO_IPV4 ? 32 : 128; | ||
945 | pr_debug("Create set %s with family %s\n", | ||
946 | set->name, set->family == NFPROTO_IPV4 ? "inet" : "inet6"); | ||
947 | #endif | ||
948 | |||
949 | if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || | ||
950 | !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || | ||
951 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | ||
952 | !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) | ||
953 | return -IPSET_ERR_PROTOCOL; | ||
954 | |||
955 | if (tb[IPSET_ATTR_HASHSIZE]) { | ||
956 | hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); | ||
957 | if (hashsize < IPSET_MIMINAL_HASHSIZE) | ||
958 | hashsize = IPSET_MIMINAL_HASHSIZE; | ||
959 | } | ||
960 | |||
961 | if (tb[IPSET_ATTR_MAXELEM]) | ||
962 | maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); | ||
963 | |||
964 | #ifdef IP_SET_HASH_WITH_NETMASK | ||
965 | if (tb[IPSET_ATTR_NETMASK]) { | ||
966 | netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]); | ||
967 | |||
968 | if ((set->family == NFPROTO_IPV4 && netmask > 32) || | ||
969 | (set->family == NFPROTO_IPV6 && netmask > 128) || | ||
970 | netmask == 0) | ||
971 | return -IPSET_ERR_INVALID_NETMASK; | ||
972 | } | ||
973 | #endif | ||
974 | |||
975 | hsize = sizeof(*h); | ||
976 | #ifdef IP_SET_HASH_WITH_NETS | ||
977 | hsize += sizeof(struct net_prefixes) * | ||
978 | (set->family == NFPROTO_IPV4 ? 32 : 128); | ||
979 | #endif | ||
980 | h = kzalloc(hsize, GFP_KERNEL); | ||
981 | if (!h) | ||
982 | return -ENOMEM; | ||
983 | |||
984 | h->maxelem = maxelem; | ||
985 | #ifdef IP_SET_HASH_WITH_NETMASK | ||
986 | h->netmask = netmask; | ||
987 | #endif | ||
988 | get_random_bytes(&h->initval, sizeof(h->initval)); | ||
989 | h->timeout = IPSET_NO_TIMEOUT; | ||
990 | |||
991 | hbits = htable_bits(hashsize); | ||
992 | hsize = htable_size(hbits); | ||
993 | if (hsize == 0) { | ||
994 | kfree(h); | ||
995 | return -ENOMEM; | ||
996 | } | ||
997 | h->table = ip_set_alloc(hsize); | ||
998 | if (!h->table) { | ||
999 | kfree(h); | ||
1000 | return -ENOMEM; | ||
1001 | } | ||
1002 | h->table->htable_bits = hbits; | ||
1003 | |||
1004 | set->data = h; | ||
1005 | if (set->family == NFPROTO_IPV4) | ||
1006 | set->variant = &TOKEN(HTYPE, 4_variant); | ||
1007 | else | ||
1008 | set->variant = &TOKEN(HTYPE, 6_variant); | ||
1009 | |||
1010 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
1011 | h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
1012 | set->extensions |= IPSET_EXT_TIMEOUT; | ||
1013 | if (set->family == NFPROTO_IPV4) { | ||
1014 | h->dsize = sizeof(struct TOKEN(HTYPE, 4t_elem)); | ||
1015 | h->offset[IPSET_OFFSET_TIMEOUT] = | ||
1016 | offsetof(struct TOKEN(HTYPE, 4t_elem), | ||
1017 | timeout); | ||
1018 | TOKEN(HTYPE, 4_gc_init)(set, TOKEN(HTYPE, 4_gc)); | ||
1019 | } else { | ||
1020 | h->dsize = sizeof(struct TOKEN(HTYPE, 6t_elem)); | ||
1021 | h->offset[IPSET_OFFSET_TIMEOUT] = | ||
1022 | offsetof(struct TOKEN(HTYPE, 6t_elem), | ||
1023 | timeout); | ||
1024 | TOKEN(HTYPE, 6_gc_init)(set, TOKEN(HTYPE, 6_gc)); | ||
1025 | } | ||
1026 | } else { | ||
1027 | if (set->family == NFPROTO_IPV4) | ||
1028 | h->dsize = sizeof(struct TOKEN(HTYPE, 4_elem)); | ||
1029 | else | ||
1030 | h->dsize = sizeof(struct TOKEN(HTYPE, 6_elem)); | ||
1031 | } | ||
1032 | |||
1033 | pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", | ||
1034 | set->name, jhash_size(h->table->htable_bits), | ||
1035 | h->table->htable_bits, h->maxelem, set->data, h->table); | ||
1036 | |||
1037 | return 0; | ||
1038 | } | ||
1039 | #endif /* IP_SET_EMIT_CREATE */ | ||