Commit f830837f0eed0f9e371b8fd65169365780814bb1
Committed by
Patrick McHardy
1 parent
21f45020a3
netfilter: ipset: list:set set type support
The module implements the list:set type support in two flavours: without and with timeout. The sets has two sides: for the userspace, they store the names of other (non list:set type of) sets: one can add, delete and test set names. For the kernel, it forms an ordered union of the member sets: the members sets are tried in order when elements are added, deleted and tested and the process stops at the first success. Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> Signed-off-by: Patrick McHardy <kaber@trash.net>
Showing 4 changed files with 624 additions and 0 deletions Side-by-side Diff
include/linux/netfilter/ipset/ip_set_list.h
1 | +#ifndef __IP_SET_LIST_H | |
2 | +#define __IP_SET_LIST_H | |
3 | + | |
4 | +/* List type specific error codes */ | |
5 | +enum { | |
6 | + /* Set name to be added/deleted/tested does not exist. */ | |
7 | + IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC, | |
8 | + /* list:set type is not permitted to add */ | |
9 | + IPSET_ERR_LOOP, | |
10 | + /* Missing reference set */ | |
11 | + IPSET_ERR_BEFORE, | |
12 | + /* Reference set does not exist */ | |
13 | + IPSET_ERR_NAMEREF, | |
14 | + /* Set is full */ | |
15 | + IPSET_ERR_LIST_FULL, | |
16 | + /* Reference set is not added to the set */ | |
17 | + IPSET_ERR_REF_EXIST, | |
18 | +}; | |
19 | + | |
20 | +#ifdef __KERNEL__ | |
21 | + | |
22 | +#define IP_SET_LIST_DEFAULT_SIZE 8 | |
23 | +#define IP_SET_LIST_MIN_SIZE 4 | |
24 | + | |
25 | +#endif /* __KERNEL__ */ | |
26 | + | |
27 | +#endif /* __IP_SET_LIST_H */ |
net/netfilter/ipset/Kconfig
... | ... | @@ -108,5 +108,15 @@ |
108 | 108 | |
109 | 109 | To compile it as a module, choose M here. If unsure, say N. |
110 | 110 | |
111 | +config IP_SET_LIST_SET | |
112 | + tristate "list:set set support" | |
113 | + depends on IP_SET | |
114 | + help | |
115 | + This option adds the list:set set type support. In this | |
116 | + kind of set one can store the name of other sets and it forms | |
117 | + an ordered union of the member sets. | |
118 | + | |
119 | + To compile it as a module, choose M here. If unsure, say N. | |
120 | + | |
111 | 121 | endif # IP_SET |
net/netfilter/ipset/Makefile
net/netfilter/ipset/ip_set_list_set.c
1 | +/* Copyright (C) 2008-2011 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 | +/* Kernel module implementing an IP set type: the list:set type */ | |
9 | + | |
10 | +#include <linux/module.h> | |
11 | +#include <linux/ip.h> | |
12 | +#include <linux/skbuff.h> | |
13 | +#include <linux/errno.h> | |
14 | + | |
15 | +#include <linux/netfilter/ipset/ip_set.h> | |
16 | +#include <linux/netfilter/ipset/ip_set_timeout.h> | |
17 | +#include <linux/netfilter/ipset/ip_set_list.h> | |
18 | + | |
19 | +MODULE_LICENSE("GPL"); | |
20 | +MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |
21 | +MODULE_DESCRIPTION("list:set type of IP sets"); | |
22 | +MODULE_ALIAS("ip_set_list:set"); | |
23 | + | |
24 | +/* Member elements without and with timeout */ | |
25 | +struct set_elem { | |
26 | + ip_set_id_t id; | |
27 | +}; | |
28 | + | |
29 | +struct set_telem { | |
30 | + ip_set_id_t id; | |
31 | + unsigned long timeout; | |
32 | +}; | |
33 | + | |
34 | +/* Type structure */ | |
35 | +struct list_set { | |
36 | + size_t dsize; /* element size */ | |
37 | + u32 size; /* size of set list array */ | |
38 | + u32 timeout; /* timeout value */ | |
39 | + struct timer_list gc; /* garbage collection */ | |
40 | + struct set_elem members[0]; /* the set members */ | |
41 | +}; | |
42 | + | |
43 | +static inline struct set_elem * | |
44 | +list_set_elem(const struct list_set *map, u32 id) | |
45 | +{ | |
46 | + return (struct set_elem *)((char *)map->members + id * map->dsize); | |
47 | +} | |
48 | + | |
49 | +static inline bool | |
50 | +list_set_timeout(const struct list_set *map, u32 id) | |
51 | +{ | |
52 | + const struct set_telem *elem = | |
53 | + (const struct set_telem *) list_set_elem(map, id); | |
54 | + | |
55 | + return ip_set_timeout_test(elem->timeout); | |
56 | +} | |
57 | + | |
58 | +static inline bool | |
59 | +list_set_expired(const struct list_set *map, u32 id) | |
60 | +{ | |
61 | + const struct set_telem *elem = | |
62 | + (const struct set_telem *) list_set_elem(map, id); | |
63 | + | |
64 | + return ip_set_timeout_expired(elem->timeout); | |
65 | +} | |
66 | + | |
67 | +static inline int | |
68 | +list_set_exist(const struct set_telem *elem) | |
69 | +{ | |
70 | + return elem->id != IPSET_INVALID_ID && | |
71 | + !ip_set_timeout_expired(elem->timeout); | |
72 | +} | |
73 | + | |
74 | +/* Set list without and with timeout */ | |
75 | + | |
76 | +static int | |
77 | +list_set_kadt(struct ip_set *set, const struct sk_buff *skb, | |
78 | + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) | |
79 | +{ | |
80 | + struct list_set *map = set->data; | |
81 | + struct set_elem *elem; | |
82 | + u32 i; | |
83 | + int ret; | |
84 | + | |
85 | + for (i = 0; i < map->size; i++) { | |
86 | + elem = list_set_elem(map, i); | |
87 | + if (elem->id == IPSET_INVALID_ID) | |
88 | + return 0; | |
89 | + if (with_timeout(map->timeout) && list_set_expired(map, i)) | |
90 | + continue; | |
91 | + switch (adt) { | |
92 | + case IPSET_TEST: | |
93 | + ret = ip_set_test(elem->id, skb, pf, dim, flags); | |
94 | + if (ret > 0) | |
95 | + return ret; | |
96 | + break; | |
97 | + case IPSET_ADD: | |
98 | + ret = ip_set_add(elem->id, skb, pf, dim, flags); | |
99 | + if (ret == 0) | |
100 | + return ret; | |
101 | + break; | |
102 | + case IPSET_DEL: | |
103 | + ret = ip_set_del(elem->id, skb, pf, dim, flags); | |
104 | + if (ret == 0) | |
105 | + return ret; | |
106 | + break; | |
107 | + default: | |
108 | + break; | |
109 | + } | |
110 | + } | |
111 | + return -EINVAL; | |
112 | +} | |
113 | + | |
114 | +static bool | |
115 | +next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id) | |
116 | +{ | |
117 | + const struct set_elem *elem; | |
118 | + | |
119 | + if (i + 1 < map->size) { | |
120 | + elem = list_set_elem(map, i + 1); | |
121 | + return !!(elem->id == id && | |
122 | + !(with_timeout(map->timeout) && | |
123 | + list_set_expired(map, i + 1))); | |
124 | + } | |
125 | + | |
126 | + return 0; | |
127 | +} | |
128 | + | |
129 | +static void | |
130 | +list_elem_add(struct list_set *map, u32 i, ip_set_id_t id) | |
131 | +{ | |
132 | + struct set_elem *e; | |
133 | + | |
134 | + for (; i < map->size; i++) { | |
135 | + e = list_set_elem(map, i); | |
136 | + swap(e->id, id); | |
137 | + if (e->id == IPSET_INVALID_ID) | |
138 | + break; | |
139 | + } | |
140 | +} | |
141 | + | |
142 | +static void | |
143 | +list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id, | |
144 | + unsigned long timeout) | |
145 | +{ | |
146 | + struct set_telem *e; | |
147 | + | |
148 | + for (; i < map->size; i++) { | |
149 | + e = (struct set_telem *)list_set_elem(map, i); | |
150 | + swap(e->id, id); | |
151 | + if (e->id == IPSET_INVALID_ID) | |
152 | + break; | |
153 | + swap(e->timeout, timeout); | |
154 | + } | |
155 | +} | |
156 | + | |
157 | +static int | |
158 | +list_set_add(struct list_set *map, u32 i, ip_set_id_t id, | |
159 | + unsigned long timeout) | |
160 | +{ | |
161 | + const struct set_elem *e = list_set_elem(map, i); | |
162 | + | |
163 | + if (i == map->size - 1 && e->id != IPSET_INVALID_ID) | |
164 | + /* Last element replaced: e.g. add new,before,last */ | |
165 | + ip_set_put_byindex(e->id); | |
166 | + if (with_timeout(map->timeout)) | |
167 | + list_elem_tadd(map, i, id, timeout); | |
168 | + else | |
169 | + list_elem_add(map, i, id); | |
170 | + | |
171 | + return 0; | |
172 | +} | |
173 | + | |
174 | +static int | |
175 | +list_set_del(struct list_set *map, ip_set_id_t id, u32 i) | |
176 | +{ | |
177 | + struct set_elem *a = list_set_elem(map, i), *b; | |
178 | + | |
179 | + ip_set_put_byindex(id); | |
180 | + | |
181 | + for (; i < map->size - 1; i++) { | |
182 | + b = list_set_elem(map, i + 1); | |
183 | + a->id = b->id; | |
184 | + if (with_timeout(map->timeout)) | |
185 | + ((struct set_telem *)a)->timeout = | |
186 | + ((struct set_telem *)b)->timeout; | |
187 | + a = b; | |
188 | + if (a->id == IPSET_INVALID_ID) | |
189 | + break; | |
190 | + } | |
191 | + /* Last element */ | |
192 | + a->id = IPSET_INVALID_ID; | |
193 | + return 0; | |
194 | +} | |
195 | + | |
196 | +static int | |
197 | +list_set_uadt(struct ip_set *set, struct nlattr *tb[], | |
198 | + enum ipset_adt adt, u32 *lineno, u32 flags) | |
199 | +{ | |
200 | + struct list_set *map = set->data; | |
201 | + bool with_timeout = with_timeout(map->timeout); | |
202 | + int before = 0; | |
203 | + u32 timeout = map->timeout; | |
204 | + ip_set_id_t id, refid = IPSET_INVALID_ID; | |
205 | + const struct set_elem *elem; | |
206 | + struct ip_set *s; | |
207 | + u32 i; | |
208 | + int ret = 0; | |
209 | + | |
210 | + if (unlikely(!tb[IPSET_ATTR_NAME] || | |
211 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || | |
212 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) | |
213 | + return -IPSET_ERR_PROTOCOL; | |
214 | + | |
215 | + if (tb[IPSET_ATTR_LINENO]) | |
216 | + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | |
217 | + | |
218 | + id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); | |
219 | + if (id == IPSET_INVALID_ID) | |
220 | + return -IPSET_ERR_NAME; | |
221 | + /* "Loop detection" */ | |
222 | + if (s->type->features & IPSET_TYPE_NAME) { | |
223 | + ret = -IPSET_ERR_LOOP; | |
224 | + goto finish; | |
225 | + } | |
226 | + | |
227 | + if (tb[IPSET_ATTR_CADT_FLAGS]) { | |
228 | + u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); | |
229 | + before = f & IPSET_FLAG_BEFORE; | |
230 | + } | |
231 | + | |
232 | + if (before && !tb[IPSET_ATTR_NAMEREF]) { | |
233 | + ret = -IPSET_ERR_BEFORE; | |
234 | + goto finish; | |
235 | + } | |
236 | + | |
237 | + if (tb[IPSET_ATTR_NAMEREF]) { | |
238 | + refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), | |
239 | + &s); | |
240 | + if (refid == IPSET_INVALID_ID) { | |
241 | + ret = -IPSET_ERR_NAMEREF; | |
242 | + goto finish; | |
243 | + } | |
244 | + if (!before) | |
245 | + before = -1; | |
246 | + } | |
247 | + if (tb[IPSET_ATTR_TIMEOUT]) { | |
248 | + if (!with_timeout) { | |
249 | + ret = -IPSET_ERR_TIMEOUT; | |
250 | + goto finish; | |
251 | + } | |
252 | + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | |
253 | + } | |
254 | + | |
255 | + switch (adt) { | |
256 | + case IPSET_TEST: | |
257 | + for (i = 0; i < map->size && !ret; i++) { | |
258 | + elem = list_set_elem(map, i); | |
259 | + if (elem->id == IPSET_INVALID_ID || | |
260 | + (before != 0 && i + 1 >= map->size)) | |
261 | + break; | |
262 | + else if (with_timeout && list_set_expired(map, i)) | |
263 | + continue; | |
264 | + else if (before > 0 && elem->id == id) | |
265 | + ret = next_id_eq(map, i, refid); | |
266 | + else if (before < 0 && elem->id == refid) | |
267 | + ret = next_id_eq(map, i, id); | |
268 | + else if (before == 0 && elem->id == id) | |
269 | + ret = 1; | |
270 | + } | |
271 | + break; | |
272 | + case IPSET_ADD: | |
273 | + for (i = 0; i < map->size && !ret; i++) { | |
274 | + elem = list_set_elem(map, i); | |
275 | + if (elem->id == id && | |
276 | + !(with_timeout && list_set_expired(map, i))) | |
277 | + ret = -IPSET_ERR_EXIST; | |
278 | + } | |
279 | + if (ret == -IPSET_ERR_EXIST) | |
280 | + break; | |
281 | + ret = -IPSET_ERR_LIST_FULL; | |
282 | + for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { | |
283 | + elem = list_set_elem(map, i); | |
284 | + if (elem->id == IPSET_INVALID_ID) | |
285 | + ret = before != 0 ? -IPSET_ERR_REF_EXIST | |
286 | + : list_set_add(map, i, id, timeout); | |
287 | + else if (elem->id != refid) | |
288 | + continue; | |
289 | + else if (with_timeout && list_set_expired(map, i)) | |
290 | + ret = -IPSET_ERR_REF_EXIST; | |
291 | + else if (before) | |
292 | + ret = list_set_add(map, i, id, timeout); | |
293 | + else if (i + 1 < map->size) | |
294 | + ret = list_set_add(map, i + 1, id, timeout); | |
295 | + } | |
296 | + break; | |
297 | + case IPSET_DEL: | |
298 | + ret = -IPSET_ERR_EXIST; | |
299 | + for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) { | |
300 | + elem = list_set_elem(map, i); | |
301 | + if (elem->id == IPSET_INVALID_ID) { | |
302 | + ret = before != 0 ? -IPSET_ERR_REF_EXIST | |
303 | + : -IPSET_ERR_EXIST; | |
304 | + break; | |
305 | + } else if (with_timeout && list_set_expired(map, i)) | |
306 | + continue; | |
307 | + else if (elem->id == id && | |
308 | + (before == 0 || | |
309 | + (before > 0 && | |
310 | + next_id_eq(map, i, refid)))) | |
311 | + ret = list_set_del(map, id, i); | |
312 | + else if (before < 0 && | |
313 | + elem->id == refid && | |
314 | + next_id_eq(map, i, id)) | |
315 | + ret = list_set_del(map, id, i + 1); | |
316 | + } | |
317 | + break; | |
318 | + default: | |
319 | + break; | |
320 | + } | |
321 | + | |
322 | +finish: | |
323 | + if (refid != IPSET_INVALID_ID) | |
324 | + ip_set_put_byindex(refid); | |
325 | + if (adt != IPSET_ADD || ret) | |
326 | + ip_set_put_byindex(id); | |
327 | + | |
328 | + return ip_set_eexist(ret, flags) ? 0 : ret; | |
329 | +} | |
330 | + | |
331 | +static void | |
332 | +list_set_flush(struct ip_set *set) | |
333 | +{ | |
334 | + struct list_set *map = set->data; | |
335 | + struct set_elem *elem; | |
336 | + u32 i; | |
337 | + | |
338 | + for (i = 0; i < map->size; i++) { | |
339 | + elem = list_set_elem(map, i); | |
340 | + if (elem->id != IPSET_INVALID_ID) { | |
341 | + ip_set_put_byindex(elem->id); | |
342 | + elem->id = IPSET_INVALID_ID; | |
343 | + } | |
344 | + } | |
345 | +} | |
346 | + | |
347 | +static void | |
348 | +list_set_destroy(struct ip_set *set) | |
349 | +{ | |
350 | + struct list_set *map = set->data; | |
351 | + | |
352 | + if (with_timeout(map->timeout)) | |
353 | + del_timer_sync(&map->gc); | |
354 | + list_set_flush(set); | |
355 | + kfree(map); | |
356 | + | |
357 | + set->data = NULL; | |
358 | +} | |
359 | + | |
360 | +static int | |
361 | +list_set_head(struct ip_set *set, struct sk_buff *skb) | |
362 | +{ | |
363 | + const struct list_set *map = set->data; | |
364 | + struct nlattr *nested; | |
365 | + | |
366 | + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | |
367 | + if (!nested) | |
368 | + goto nla_put_failure; | |
369 | + NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size)); | |
370 | + if (with_timeout(map->timeout)) | |
371 | + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); | |
372 | + NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, | |
373 | + htonl(atomic_read(&set->ref) - 1)); | |
374 | + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, | |
375 | + htonl(sizeof(*map) + map->size * map->dsize)); | |
376 | + ipset_nest_end(skb, nested); | |
377 | + | |
378 | + return 0; | |
379 | +nla_put_failure: | |
380 | + return -EMSGSIZE; | |
381 | +} | |
382 | + | |
383 | +static int | |
384 | +list_set_list(const struct ip_set *set, | |
385 | + struct sk_buff *skb, struct netlink_callback *cb) | |
386 | +{ | |
387 | + const struct list_set *map = set->data; | |
388 | + struct nlattr *atd, *nested; | |
389 | + u32 i, first = cb->args[2]; | |
390 | + const struct set_elem *e; | |
391 | + | |
392 | + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); | |
393 | + if (!atd) | |
394 | + return -EMSGSIZE; | |
395 | + for (; cb->args[2] < map->size; cb->args[2]++) { | |
396 | + i = cb->args[2]; | |
397 | + e = list_set_elem(map, i); | |
398 | + if (e->id == IPSET_INVALID_ID) | |
399 | + goto finish; | |
400 | + if (with_timeout(map->timeout) && list_set_expired(map, i)) | |
401 | + continue; | |
402 | + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | |
403 | + if (!nested) { | |
404 | + if (i == first) { | |
405 | + nla_nest_cancel(skb, atd); | |
406 | + return -EMSGSIZE; | |
407 | + } else | |
408 | + goto nla_put_failure; | |
409 | + } | |
410 | + NLA_PUT_STRING(skb, IPSET_ATTR_NAME, | |
411 | + ip_set_name_byindex(e->id)); | |
412 | + if (with_timeout(map->timeout)) { | |
413 | + const struct set_telem *te = | |
414 | + (const struct set_telem *) e; | |
415 | + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, | |
416 | + htonl(ip_set_timeout_get(te->timeout))); | |
417 | + } | |
418 | + ipset_nest_end(skb, nested); | |
419 | + } | |
420 | +finish: | |
421 | + ipset_nest_end(skb, atd); | |
422 | + /* Set listing finished */ | |
423 | + cb->args[2] = 0; | |
424 | + return 0; | |
425 | + | |
426 | +nla_put_failure: | |
427 | + nla_nest_cancel(skb, nested); | |
428 | + ipset_nest_end(skb, atd); | |
429 | + if (unlikely(i == first)) { | |
430 | + cb->args[2] = 0; | |
431 | + return -EMSGSIZE; | |
432 | + } | |
433 | + return 0; | |
434 | +} | |
435 | + | |
436 | +static bool | |
437 | +list_set_same_set(const struct ip_set *a, const struct ip_set *b) | |
438 | +{ | |
439 | + const struct list_set *x = a->data; | |
440 | + const struct list_set *y = b->data; | |
441 | + | |
442 | + return x->size == y->size && | |
443 | + x->timeout == y->timeout; | |
444 | +} | |
445 | + | |
446 | +static const struct ip_set_type_variant list_set = { | |
447 | + .kadt = list_set_kadt, | |
448 | + .uadt = list_set_uadt, | |
449 | + .destroy = list_set_destroy, | |
450 | + .flush = list_set_flush, | |
451 | + .head = list_set_head, | |
452 | + .list = list_set_list, | |
453 | + .same_set = list_set_same_set, | |
454 | +}; | |
455 | + | |
456 | +static void | |
457 | +list_set_gc(unsigned long ul_set) | |
458 | +{ | |
459 | + struct ip_set *set = (struct ip_set *) ul_set; | |
460 | + struct list_set *map = set->data; | |
461 | + struct set_telem *e; | |
462 | + u32 i; | |
463 | + | |
464 | + /* We run parallel with other readers (test element) | |
465 | + * but adding/deleting new entries is locked out */ | |
466 | + read_lock_bh(&set->lock); | |
467 | + for (i = map->size - 1; i >= 0; i--) { | |
468 | + e = (struct set_telem *) list_set_elem(map, i); | |
469 | + if (e->id != IPSET_INVALID_ID && | |
470 | + list_set_expired(map, i)) | |
471 | + list_set_del(map, e->id, i); | |
472 | + } | |
473 | + read_unlock_bh(&set->lock); | |
474 | + | |
475 | + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | |
476 | + add_timer(&map->gc); | |
477 | +} | |
478 | + | |
479 | +static void | |
480 | +list_set_gc_init(struct ip_set *set) | |
481 | +{ | |
482 | + struct list_set *map = set->data; | |
483 | + | |
484 | + init_timer(&map->gc); | |
485 | + map->gc.data = (unsigned long) set; | |
486 | + map->gc.function = list_set_gc; | |
487 | + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | |
488 | + add_timer(&map->gc); | |
489 | +} | |
490 | + | |
491 | +/* Create list:set type of sets */ | |
492 | + | |
493 | +static bool | |
494 | +init_list_set(struct ip_set *set, u32 size, size_t dsize, | |
495 | + unsigned long timeout) | |
496 | +{ | |
497 | + struct list_set *map; | |
498 | + struct set_elem *e; | |
499 | + u32 i; | |
500 | + | |
501 | + map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL); | |
502 | + if (!map) | |
503 | + return false; | |
504 | + | |
505 | + map->size = size; | |
506 | + map->dsize = dsize; | |
507 | + map->timeout = timeout; | |
508 | + set->data = map; | |
509 | + | |
510 | + for (i = 0; i < size; i++) { | |
511 | + e = list_set_elem(map, i); | |
512 | + e->id = IPSET_INVALID_ID; | |
513 | + } | |
514 | + | |
515 | + return true; | |
516 | +} | |
517 | + | |
518 | +static int | |
519 | +list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | |
520 | +{ | |
521 | + u32 size = IP_SET_LIST_DEFAULT_SIZE; | |
522 | + | |
523 | + if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) || | |
524 | + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | |
525 | + return -IPSET_ERR_PROTOCOL; | |
526 | + | |
527 | + if (tb[IPSET_ATTR_SIZE]) | |
528 | + size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]); | |
529 | + if (size < IP_SET_LIST_MIN_SIZE) | |
530 | + size = IP_SET_LIST_MIN_SIZE; | |
531 | + | |
532 | + if (tb[IPSET_ATTR_TIMEOUT]) { | |
533 | + if (!init_list_set(set, size, sizeof(struct set_telem), | |
534 | + ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]))) | |
535 | + return -ENOMEM; | |
536 | + | |
537 | + list_set_gc_init(set); | |
538 | + } else { | |
539 | + if (!init_list_set(set, size, sizeof(struct set_elem), | |
540 | + IPSET_NO_TIMEOUT)) | |
541 | + return -ENOMEM; | |
542 | + } | |
543 | + set->variant = &list_set; | |
544 | + return 0; | |
545 | +} | |
546 | + | |
547 | +static struct ip_set_type list_set_type __read_mostly = { | |
548 | + .name = "list:set", | |
549 | + .protocol = IPSET_PROTOCOL, | |
550 | + .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, | |
551 | + .dimension = IPSET_DIM_ONE, | |
552 | + .family = AF_UNSPEC, | |
553 | + .revision = 0, | |
554 | + .create = list_set_create, | |
555 | + .create_policy = { | |
556 | + [IPSET_ATTR_SIZE] = { .type = NLA_U32 }, | |
557 | + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
558 | + }, | |
559 | + .adt_policy = { | |
560 | + [IPSET_ATTR_NAME] = { .type = NLA_STRING, | |
561 | + .len = IPSET_MAXNAMELEN }, | |
562 | + [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING, | |
563 | + .len = IPSET_MAXNAMELEN }, | |
564 | + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, | |
565 | + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, | |
566 | + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, | |
567 | + }, | |
568 | + .me = THIS_MODULE, | |
569 | +}; | |
570 | + | |
571 | +static int __init | |
572 | +list_set_init(void) | |
573 | +{ | |
574 | + return ip_set_type_register(&list_set_type); | |
575 | +} | |
576 | + | |
577 | +static void __exit | |
578 | +list_set_fini(void) | |
579 | +{ | |
580 | + ip_set_type_unregister(&list_set_type); | |
581 | +} | |
582 | + | |
583 | +module_init(list_set_init); | |
584 | +module_exit(list_set_fini); |