blob: 8371c2bac2e4240eb5c4b3f6abd0faa48f6212ce [file] [log] [blame]
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +01001/* 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
Jozsef Kadlecsik10111a62012-09-21 21:59:32 +020019#define REVISION_MIN 0
20#define REVISION_MAX 0
21
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010022MODULE_LICENSE("GPL");
23MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
Jozsef Kadlecsik10111a62012-09-21 21:59:32 +020024IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010025MODULE_ALIAS("ip_set_list:set");
26
27/* Member elements without and with timeout */
28struct set_elem {
29 ip_set_id_t id;
30};
31
32struct set_telem {
33 ip_set_id_t id;
34 unsigned long timeout;
35};
36
37/* Type structure */
38struct list_set {
39 size_t dsize; /* element size */
40 u32 size; /* size of set list array */
41 u32 timeout; /* timeout value */
42 struct timer_list gc; /* garbage collection */
43 struct set_elem members[0]; /* the set members */
44};
45
46static inline struct set_elem *
47list_set_elem(const struct list_set *map, u32 id)
48{
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +020049 return (struct set_elem *)((void *)map->members + id * map->dsize);
50}
51
52static inline struct set_telem *
53list_set_telem(const struct list_set *map, u32 id)
54{
55 return (struct set_telem *)((void *)map->members + id * map->dsize);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010056}
57
58static inline bool
59list_set_timeout(const struct list_set *map, u32 id)
60{
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +020061 const struct set_telem *elem = list_set_telem(map, id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010062
63 return ip_set_timeout_test(elem->timeout);
64}
65
66static inline bool
67list_set_expired(const struct list_set *map, u32 id)
68{
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +020069 const struct set_telem *elem = list_set_telem(map, id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010070
71 return ip_set_timeout_expired(elem->timeout);
72}
73
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010074/* Set list without and with timeout */
75
76static int
77list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020078 const struct xt_action_param *par,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +020079 enum ipset_adt adt, const struct ip_set_adt_opt *opt)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010080{
81 struct list_set *map = set->data;
82 struct set_elem *elem;
83 u32 i;
84 int ret;
85
86 for (i = 0; i < map->size; i++) {
87 elem = list_set_elem(map, i);
88 if (elem->id == IPSET_INVALID_ID)
89 return 0;
90 if (with_timeout(map->timeout) && list_set_expired(map, i))
91 continue;
92 switch (adt) {
93 case IPSET_TEST:
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020094 ret = ip_set_test(elem->id, skb, par, opt);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010095 if (ret > 0)
96 return ret;
97 break;
98 case IPSET_ADD:
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020099 ret = ip_set_add(elem->id, skb, par, opt);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100100 if (ret == 0)
101 return ret;
102 break;
103 case IPSET_DEL:
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200104 ret = ip_set_del(elem->id, skb, par, opt);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100105 if (ret == 0)
106 return ret;
107 break;
108 default:
109 break;
110 }
111 }
112 return -EINVAL;
113}
114
115static bool
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200116id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100117{
118 const struct set_elem *elem;
119
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200120 if (i < map->size) {
121 elem = list_set_elem(map, i);
122 return elem->id == id;
123 }
124
125 return 0;
126}
127
128static bool
129id_eq_timeout(const struct list_set *map, u32 i, ip_set_id_t id)
130{
131 const struct set_elem *elem;
132
133 if (i < map->size) {
134 elem = list_set_elem(map, i);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100135 return !!(elem->id == id &&
136 !(with_timeout(map->timeout) &&
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200137 list_set_expired(map, i)));
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100138 }
139
140 return 0;
141}
142
143static void
144list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
145{
146 struct set_elem *e;
147
148 for (; i < map->size; i++) {
149 e = list_set_elem(map, i);
150 swap(e->id, id);
151 if (e->id == IPSET_INVALID_ID)
152 break;
153 }
154}
155
156static void
157list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
158 unsigned long timeout)
159{
160 struct set_telem *e;
161
162 for (; i < map->size; i++) {
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200163 e = list_set_telem(map, i);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100164 swap(e->id, id);
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200165 swap(e->timeout, timeout);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100166 if (e->id == IPSET_INVALID_ID)
167 break;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100168 }
169}
170
171static int
172list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
173 unsigned long timeout)
174{
175 const struct set_elem *e = list_set_elem(map, i);
176
177 if (i == map->size - 1 && e->id != IPSET_INVALID_ID)
178 /* Last element replaced: e.g. add new,before,last */
179 ip_set_put_byindex(e->id);
180 if (with_timeout(map->timeout))
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200181 list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100182 else
183 list_elem_add(map, i, id);
184
185 return 0;
186}
187
188static int
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200189list_set_del(struct list_set *map, u32 i)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100190{
191 struct set_elem *a = list_set_elem(map, i), *b;
192
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200193 ip_set_put_byindex(a->id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100194
195 for (; i < map->size - 1; i++) {
196 b = list_set_elem(map, i + 1);
197 a->id = b->id;
198 if (with_timeout(map->timeout))
199 ((struct set_telem *)a)->timeout =
200 ((struct set_telem *)b)->timeout;
201 a = b;
202 if (a->id == IPSET_INVALID_ID)
203 break;
204 }
205 /* Last element */
206 a->id = IPSET_INVALID_ID;
207 return 0;
208}
209
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200210static void
211cleanup_entries(struct list_set *map)
212{
213 struct set_telem *e;
214 u32 i;
215
216 for (i = 0; i < map->size; i++) {
217 e = list_set_telem(map, i);
218 if (e->id != IPSET_INVALID_ID && list_set_expired(map, i))
219 list_set_del(map, i);
220 }
221}
222
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100223static int
224list_set_uadt(struct ip_set *set, struct nlattr *tb[],
Jozsef Kadlecsik3d14b172011-06-16 18:49:17 +0200225 enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100226{
227 struct list_set *map = set->data;
228 bool with_timeout = with_timeout(map->timeout);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200229 bool flag_exist = flags & IPSET_FLAG_EXIST;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100230 int before = 0;
231 u32 timeout = map->timeout;
232 ip_set_id_t id, refid = IPSET_INVALID_ID;
233 const struct set_elem *elem;
234 struct ip_set *s;
235 u32 i;
236 int ret = 0;
237
238 if (unlikely(!tb[IPSET_ATTR_NAME] ||
239 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
240 !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
241 return -IPSET_ERR_PROTOCOL;
242
243 if (tb[IPSET_ATTR_LINENO])
244 *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
245
246 id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
247 if (id == IPSET_INVALID_ID)
248 return -IPSET_ERR_NAME;
249 /* "Loop detection" */
250 if (s->type->features & IPSET_TYPE_NAME) {
251 ret = -IPSET_ERR_LOOP;
252 goto finish;
253 }
254
255 if (tb[IPSET_ATTR_CADT_FLAGS]) {
256 u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
257 before = f & IPSET_FLAG_BEFORE;
258 }
259
260 if (before && !tb[IPSET_ATTR_NAMEREF]) {
261 ret = -IPSET_ERR_BEFORE;
262 goto finish;
263 }
264
265 if (tb[IPSET_ATTR_NAMEREF]) {
266 refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
267 &s);
268 if (refid == IPSET_INVALID_ID) {
269 ret = -IPSET_ERR_NAMEREF;
270 goto finish;
271 }
272 if (!before)
273 before = -1;
274 }
275 if (tb[IPSET_ATTR_TIMEOUT]) {
276 if (!with_timeout) {
277 ret = -IPSET_ERR_TIMEOUT;
278 goto finish;
279 }
280 timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
281 }
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200282 if (with_timeout && adt != IPSET_TEST)
283 cleanup_entries(map);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100284
285 switch (adt) {
286 case IPSET_TEST:
287 for (i = 0; i < map->size && !ret; i++) {
288 elem = list_set_elem(map, i);
289 if (elem->id == IPSET_INVALID_ID ||
290 (before != 0 && i + 1 >= map->size))
291 break;
292 else if (with_timeout && list_set_expired(map, i))
293 continue;
294 else if (before > 0 && elem->id == id)
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200295 ret = id_eq_timeout(map, i + 1, refid);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100296 else if (before < 0 && elem->id == refid)
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200297 ret = id_eq_timeout(map, i + 1, id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100298 else if (before == 0 && elem->id == id)
299 ret = 1;
300 }
301 break;
302 case IPSET_ADD:
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200303 for (i = 0; i < map->size; i++) {
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100304 elem = list_set_elem(map, i);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200305 if (elem->id != id)
306 continue;
307 if (!(with_timeout && flag_exist)) {
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100308 ret = -IPSET_ERR_EXIST;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200309 goto finish;
310 } else {
311 struct set_telem *e = list_set_telem(map, i);
312
313 if ((before > 1 &&
314 !id_eq(map, i + 1, refid)) ||
315 (before < 0 &&
316 (i == 0 || !id_eq(map, i - 1, refid)))) {
Jozsef Kadlecsik483e9ea2011-06-16 18:41:53 +0200317 ret = -IPSET_ERR_EXIST;
318 goto finish;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200319 }
320 e->timeout = ip_set_timeout_set(timeout);
321 ip_set_put_byindex(id);
322 ret = 0;
323 goto finish;
324 }
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100325 }
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100326 ret = -IPSET_ERR_LIST_FULL;
327 for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
328 elem = list_set_elem(map, i);
329 if (elem->id == IPSET_INVALID_ID)
330 ret = before != 0 ? -IPSET_ERR_REF_EXIST
331 : list_set_add(map, i, id, timeout);
332 else if (elem->id != refid)
333 continue;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200334 else if (before > 0)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100335 ret = list_set_add(map, i, id, timeout);
336 else if (i + 1 < map->size)
337 ret = list_set_add(map, i + 1, id, timeout);
338 }
339 break;
340 case IPSET_DEL:
341 ret = -IPSET_ERR_EXIST;
342 for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
343 elem = list_set_elem(map, i);
344 if (elem->id == IPSET_INVALID_ID) {
345 ret = before != 0 ? -IPSET_ERR_REF_EXIST
346 : -IPSET_ERR_EXIST;
347 break;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200348 } else if (elem->id == id &&
349 (before == 0 ||
350 (before > 0 && id_eq(map, i + 1, refid))))
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200351 ret = list_set_del(map, i);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200352 else if (elem->id == refid &&
353 before < 0 && id_eq(map, i + 1, id))
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200354 ret = list_set_del(map, i + 1);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100355 }
356 break;
357 default:
358 break;
359 }
360
361finish:
362 if (refid != IPSET_INVALID_ID)
363 ip_set_put_byindex(refid);
364 if (adt != IPSET_ADD || ret)
365 ip_set_put_byindex(id);
366
367 return ip_set_eexist(ret, flags) ? 0 : ret;
368}
369
370static void
371list_set_flush(struct ip_set *set)
372{
373 struct list_set *map = set->data;
374 struct set_elem *elem;
375 u32 i;
376
377 for (i = 0; i < map->size; i++) {
378 elem = list_set_elem(map, i);
379 if (elem->id != IPSET_INVALID_ID) {
380 ip_set_put_byindex(elem->id);
381 elem->id = IPSET_INVALID_ID;
382 }
383 }
384}
385
386static void
387list_set_destroy(struct ip_set *set)
388{
389 struct list_set *map = set->data;
390
391 if (with_timeout(map->timeout))
392 del_timer_sync(&map->gc);
393 list_set_flush(set);
394 kfree(map);
395
396 set->data = NULL;
397}
398
399static int
400list_set_head(struct ip_set *set, struct sk_buff *skb)
401{
402 const struct list_set *map = set->data;
403 struct nlattr *nested;
404
405 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
406 if (!nested)
407 goto nla_put_failure;
David S. Miller7cf78992012-04-01 19:54:46 -0400408 if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
409 (with_timeout(map->timeout) &&
410 nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
411 nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
412 nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
413 htonl(sizeof(*map) + map->size * map->dsize)))
414 goto nla_put_failure;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100415 ipset_nest_end(skb, nested);
416
417 return 0;
418nla_put_failure:
419 return -EMSGSIZE;
420}
421
422static int
423list_set_list(const struct ip_set *set,
424 struct sk_buff *skb, struct netlink_callback *cb)
425{
426 const struct list_set *map = set->data;
427 struct nlattr *atd, *nested;
428 u32 i, first = cb->args[2];
429 const struct set_elem *e;
430
431 atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
432 if (!atd)
433 return -EMSGSIZE;
434 for (; cb->args[2] < map->size; cb->args[2]++) {
435 i = cb->args[2];
436 e = list_set_elem(map, i);
437 if (e->id == IPSET_INVALID_ID)
438 goto finish;
439 if (with_timeout(map->timeout) && list_set_expired(map, i))
440 continue;
441 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
442 if (!nested) {
443 if (i == first) {
444 nla_nest_cancel(skb, atd);
445 return -EMSGSIZE;
446 } else
447 goto nla_put_failure;
448 }
David S. Miller7cf78992012-04-01 19:54:46 -0400449 if (nla_put_string(skb, IPSET_ATTR_NAME,
450 ip_set_name_byindex(e->id)))
451 goto nla_put_failure;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100452 if (with_timeout(map->timeout)) {
453 const struct set_telem *te =
454 (const struct set_telem *) e;
David S. Miller7cf78992012-04-01 19:54:46 -0400455 __be32 to = htonl(ip_set_timeout_get(te->timeout));
456 if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, to))
457 goto nla_put_failure;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100458 }
459 ipset_nest_end(skb, nested);
460 }
461finish:
462 ipset_nest_end(skb, atd);
463 /* Set listing finished */
464 cb->args[2] = 0;
465 return 0;
466
467nla_put_failure:
468 nla_nest_cancel(skb, nested);
469 ipset_nest_end(skb, atd);
470 if (unlikely(i == first)) {
471 cb->args[2] = 0;
472 return -EMSGSIZE;
473 }
474 return 0;
475}
476
477static bool
478list_set_same_set(const struct ip_set *a, const struct ip_set *b)
479{
480 const struct list_set *x = a->data;
481 const struct list_set *y = b->data;
482
483 return x->size == y->size &&
484 x->timeout == y->timeout;
485}
486
487static const struct ip_set_type_variant list_set = {
488 .kadt = list_set_kadt,
489 .uadt = list_set_uadt,
490 .destroy = list_set_destroy,
491 .flush = list_set_flush,
492 .head = list_set_head,
493 .list = list_set_list,
494 .same_set = list_set_same_set,
495};
496
497static void
498list_set_gc(unsigned long ul_set)
499{
500 struct ip_set *set = (struct ip_set *) ul_set;
501 struct list_set *map = set->data;
Jozsef Kadlecsik2f9f28b2011-04-04 15:19:25 +0200502
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200503 write_lock_bh(&set->lock);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200504 cleanup_entries(map);
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200505 write_unlock_bh(&set->lock);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100506
507 map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
508 add_timer(&map->gc);
509}
510
511static void
512list_set_gc_init(struct ip_set *set)
513{
514 struct list_set *map = set->data;
515
516 init_timer(&map->gc);
517 map->gc.data = (unsigned long) set;
518 map->gc.function = list_set_gc;
519 map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
520 add_timer(&map->gc);
521}
522
523/* Create list:set type of sets */
524
525static bool
526init_list_set(struct ip_set *set, u32 size, size_t dsize,
527 unsigned long timeout)
528{
529 struct list_set *map;
530 struct set_elem *e;
531 u32 i;
532
533 map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
534 if (!map)
535 return false;
536
537 map->size = size;
538 map->dsize = dsize;
539 map->timeout = timeout;
540 set->data = map;
541
542 for (i = 0; i < size; i++) {
543 e = list_set_elem(map, i);
544 e->id = IPSET_INVALID_ID;
545 }
546
547 return true;
548}
549
550static int
551list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
552{
553 u32 size = IP_SET_LIST_DEFAULT_SIZE;
554
555 if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
556 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
557 return -IPSET_ERR_PROTOCOL;
558
559 if (tb[IPSET_ATTR_SIZE])
560 size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
561 if (size < IP_SET_LIST_MIN_SIZE)
562 size = IP_SET_LIST_MIN_SIZE;
563
564 if (tb[IPSET_ATTR_TIMEOUT]) {
565 if (!init_list_set(set, size, sizeof(struct set_telem),
566 ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
567 return -ENOMEM;
568
569 list_set_gc_init(set);
570 } else {
571 if (!init_list_set(set, size, sizeof(struct set_elem),
572 IPSET_NO_TIMEOUT))
573 return -ENOMEM;
574 }
575 set->variant = &list_set;
576 return 0;
577}
578
579static struct ip_set_type list_set_type __read_mostly = {
580 .name = "list:set",
581 .protocol = IPSET_PROTOCOL,
582 .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
583 .dimension = IPSET_DIM_ONE,
Jan Engelhardtc15f1c82012-02-14 00:24:10 +0100584 .family = NFPROTO_UNSPEC,
Jozsef Kadlecsik10111a62012-09-21 21:59:32 +0200585 .revision_min = REVISION_MIN,
586 .revision_max = REVISION_MAX,
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100587 .create = list_set_create,
588 .create_policy = {
589 [IPSET_ATTR_SIZE] = { .type = NLA_U32 },
590 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
591 },
592 .adt_policy = {
593 [IPSET_ATTR_NAME] = { .type = NLA_STRING,
594 .len = IPSET_MAXNAMELEN },
595 [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
596 .len = IPSET_MAXNAMELEN },
597 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
598 [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
599 [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
600 },
601 .me = THIS_MODULE,
602};
603
604static int __init
605list_set_init(void)
606{
607 return ip_set_type_register(&list_set_type);
608}
609
610static void __exit
611list_set_fini(void)
612{
613 ip_set_type_unregister(&list_set_type);
614}
615
616module_init(list_set_init);
617module_exit(list_set_fini);