blob: 7e095f9005f01f301cefe757533981e787b82ad5 [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
19MODULE_LICENSE("GPL");
20MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
21MODULE_DESCRIPTION("list:set type of IP sets");
22MODULE_ALIAS("ip_set_list:set");
23
24/* Member elements without and with timeout */
25struct set_elem {
26 ip_set_id_t id;
27};
28
29struct set_telem {
30 ip_set_id_t id;
31 unsigned long timeout;
32};
33
34/* Type structure */
35struct 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
43static inline struct set_elem *
44list_set_elem(const struct list_set *map, u32 id)
45{
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +020046 return (struct set_elem *)((void *)map->members + id * map->dsize);
47}
48
49static inline struct set_telem *
50list_set_telem(const struct list_set *map, u32 id)
51{
52 return (struct set_telem *)((void *)map->members + id * map->dsize);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010053}
54
55static inline bool
56list_set_timeout(const struct list_set *map, u32 id)
57{
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +020058 const struct set_telem *elem = list_set_telem(map, id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010059
60 return ip_set_timeout_test(elem->timeout);
61}
62
63static inline bool
64list_set_expired(const struct list_set *map, u32 id)
65{
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +020066 const struct set_telem *elem = list_set_telem(map, id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010067
68 return ip_set_timeout_expired(elem->timeout);
69}
70
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010071/* Set list without and with timeout */
72
73static int
74list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020075 const struct xt_action_param *par,
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +020076 enum ipset_adt adt, const struct ip_set_adt_opt *opt)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010077{
78 struct list_set *map = set->data;
79 struct set_elem *elem;
80 u32 i;
81 int ret;
82
83 for (i = 0; i < map->size; i++) {
84 elem = list_set_elem(map, i);
85 if (elem->id == IPSET_INVALID_ID)
86 return 0;
87 if (with_timeout(map->timeout) && list_set_expired(map, i))
88 continue;
89 switch (adt) {
90 case IPSET_TEST:
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020091 ret = ip_set_test(elem->id, skb, par, opt);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010092 if (ret > 0)
93 return ret;
94 break;
95 case IPSET_ADD:
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +020096 ret = ip_set_add(elem->id, skb, par, opt);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010097 if (ret == 0)
98 return ret;
99 break;
100 case IPSET_DEL:
Jozsef Kadlecsikb66554c2011-06-16 18:56:47 +0200101 ret = ip_set_del(elem->id, skb, par, opt);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100102 if (ret == 0)
103 return ret;
104 break;
105 default:
106 break;
107 }
108 }
109 return -EINVAL;
110}
111
112static bool
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200113id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100114{
115 const struct set_elem *elem;
116
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200117 if (i < map->size) {
118 elem = list_set_elem(map, i);
119 return elem->id == id;
120 }
121
122 return 0;
123}
124
125static bool
126id_eq_timeout(const struct list_set *map, u32 i, ip_set_id_t id)
127{
128 const struct set_elem *elem;
129
130 if (i < map->size) {
131 elem = list_set_elem(map, i);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100132 return !!(elem->id == id &&
133 !(with_timeout(map->timeout) &&
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200134 list_set_expired(map, i)));
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100135 }
136
137 return 0;
138}
139
140static void
141list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
142{
143 struct set_elem *e;
144
145 for (; i < map->size; i++) {
146 e = list_set_elem(map, i);
147 swap(e->id, id);
148 if (e->id == IPSET_INVALID_ID)
149 break;
150 }
151}
152
153static void
154list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
155 unsigned long timeout)
156{
157 struct set_telem *e;
158
159 for (; i < map->size; i++) {
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200160 e = list_set_telem(map, i);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100161 swap(e->id, id);
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200162 swap(e->timeout, timeout);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100163 if (e->id == IPSET_INVALID_ID)
164 break;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100165 }
166}
167
168static int
169list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
170 unsigned long timeout)
171{
172 const struct set_elem *e = list_set_elem(map, i);
173
174 if (i == map->size - 1 && e->id != IPSET_INVALID_ID)
175 /* Last element replaced: e.g. add new,before,last */
176 ip_set_put_byindex(e->id);
177 if (with_timeout(map->timeout))
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200178 list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100179 else
180 list_elem_add(map, i, id);
181
182 return 0;
183}
184
185static int
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200186list_set_del(struct list_set *map, u32 i)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100187{
188 struct set_elem *a = list_set_elem(map, i), *b;
189
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200190 ip_set_put_byindex(a->id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100191
192 for (; i < map->size - 1; i++) {
193 b = list_set_elem(map, i + 1);
194 a->id = b->id;
195 if (with_timeout(map->timeout))
196 ((struct set_telem *)a)->timeout =
197 ((struct set_telem *)b)->timeout;
198 a = b;
199 if (a->id == IPSET_INVALID_ID)
200 break;
201 }
202 /* Last element */
203 a->id = IPSET_INVALID_ID;
204 return 0;
205}
206
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200207static void
208cleanup_entries(struct list_set *map)
209{
210 struct set_telem *e;
211 u32 i;
212
213 for (i = 0; i < map->size; i++) {
214 e = list_set_telem(map, i);
215 if (e->id != IPSET_INVALID_ID && list_set_expired(map, i))
216 list_set_del(map, i);
217 }
218}
219
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100220static int
221list_set_uadt(struct ip_set *set, struct nlattr *tb[],
Jozsef Kadlecsik3d14b172011-06-16 18:49:17 +0200222 enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100223{
224 struct list_set *map = set->data;
225 bool with_timeout = with_timeout(map->timeout);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200226 bool flag_exist = flags & IPSET_FLAG_EXIST;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100227 int before = 0;
228 u32 timeout = map->timeout;
229 ip_set_id_t id, refid = IPSET_INVALID_ID;
230 const struct set_elem *elem;
231 struct ip_set *s;
232 u32 i;
233 int ret = 0;
234
235 if (unlikely(!tb[IPSET_ATTR_NAME] ||
236 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
237 !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
238 return -IPSET_ERR_PROTOCOL;
239
240 if (tb[IPSET_ATTR_LINENO])
241 *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
242
243 id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
244 if (id == IPSET_INVALID_ID)
245 return -IPSET_ERR_NAME;
246 /* "Loop detection" */
247 if (s->type->features & IPSET_TYPE_NAME) {
248 ret = -IPSET_ERR_LOOP;
249 goto finish;
250 }
251
252 if (tb[IPSET_ATTR_CADT_FLAGS]) {
253 u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
254 before = f & IPSET_FLAG_BEFORE;
255 }
256
257 if (before && !tb[IPSET_ATTR_NAMEREF]) {
258 ret = -IPSET_ERR_BEFORE;
259 goto finish;
260 }
261
262 if (tb[IPSET_ATTR_NAMEREF]) {
263 refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
264 &s);
265 if (refid == IPSET_INVALID_ID) {
266 ret = -IPSET_ERR_NAMEREF;
267 goto finish;
268 }
269 if (!before)
270 before = -1;
271 }
272 if (tb[IPSET_ATTR_TIMEOUT]) {
273 if (!with_timeout) {
274 ret = -IPSET_ERR_TIMEOUT;
275 goto finish;
276 }
277 timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
278 }
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200279 if (with_timeout && adt != IPSET_TEST)
280 cleanup_entries(map);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100281
282 switch (adt) {
283 case IPSET_TEST:
284 for (i = 0; i < map->size && !ret; i++) {
285 elem = list_set_elem(map, i);
286 if (elem->id == IPSET_INVALID_ID ||
287 (before != 0 && i + 1 >= map->size))
288 break;
289 else if (with_timeout && list_set_expired(map, i))
290 continue;
291 else if (before > 0 && elem->id == id)
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200292 ret = id_eq_timeout(map, i + 1, refid);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100293 else if (before < 0 && elem->id == refid)
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200294 ret = id_eq_timeout(map, i + 1, id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100295 else if (before == 0 && elem->id == id)
296 ret = 1;
297 }
298 break;
299 case IPSET_ADD:
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200300 for (i = 0; i < map->size; i++) {
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100301 elem = list_set_elem(map, i);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200302 if (elem->id != id)
303 continue;
304 if (!(with_timeout && flag_exist)) {
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100305 ret = -IPSET_ERR_EXIST;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200306 goto finish;
307 } else {
308 struct set_telem *e = list_set_telem(map, i);
309
310 if ((before > 1 &&
311 !id_eq(map, i + 1, refid)) ||
312 (before < 0 &&
313 (i == 0 || !id_eq(map, i - 1, refid)))) {
Jozsef Kadlecsik483e9ea2011-06-16 18:41:53 +0200314 ret = -IPSET_ERR_EXIST;
315 goto finish;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200316 }
317 e->timeout = ip_set_timeout_set(timeout);
318 ip_set_put_byindex(id);
319 ret = 0;
320 goto finish;
321 }
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100322 }
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100323 ret = -IPSET_ERR_LIST_FULL;
324 for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
325 elem = list_set_elem(map, i);
326 if (elem->id == IPSET_INVALID_ID)
327 ret = before != 0 ? -IPSET_ERR_REF_EXIST
328 : list_set_add(map, i, id, timeout);
329 else if (elem->id != refid)
330 continue;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200331 else if (before > 0)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100332 ret = list_set_add(map, i, id, timeout);
333 else if (i + 1 < map->size)
334 ret = list_set_add(map, i + 1, id, timeout);
335 }
336 break;
337 case IPSET_DEL:
338 ret = -IPSET_ERR_EXIST;
339 for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
340 elem = list_set_elem(map, i);
341 if (elem->id == IPSET_INVALID_ID) {
342 ret = before != 0 ? -IPSET_ERR_REF_EXIST
343 : -IPSET_ERR_EXIST;
344 break;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200345 } else if (elem->id == id &&
346 (before == 0 ||
347 (before > 0 && id_eq(map, i + 1, refid))))
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200348 ret = list_set_del(map, i);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200349 else if (elem->id == refid &&
350 before < 0 && id_eq(map, i + 1, id))
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200351 ret = list_set_del(map, i + 1);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100352 }
353 break;
354 default:
355 break;
356 }
357
358finish:
359 if (refid != IPSET_INVALID_ID)
360 ip_set_put_byindex(refid);
361 if (adt != IPSET_ADD || ret)
362 ip_set_put_byindex(id);
363
364 return ip_set_eexist(ret, flags) ? 0 : ret;
365}
366
367static void
368list_set_flush(struct ip_set *set)
369{
370 struct list_set *map = set->data;
371 struct set_elem *elem;
372 u32 i;
373
374 for (i = 0; i < map->size; i++) {
375 elem = list_set_elem(map, i);
376 if (elem->id != IPSET_INVALID_ID) {
377 ip_set_put_byindex(elem->id);
378 elem->id = IPSET_INVALID_ID;
379 }
380 }
381}
382
383static void
384list_set_destroy(struct ip_set *set)
385{
386 struct list_set *map = set->data;
387
388 if (with_timeout(map->timeout))
389 del_timer_sync(&map->gc);
390 list_set_flush(set);
391 kfree(map);
392
393 set->data = NULL;
394}
395
396static int
397list_set_head(struct ip_set *set, struct sk_buff *skb)
398{
399 const struct list_set *map = set->data;
400 struct nlattr *nested;
401
402 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
403 if (!nested)
404 goto nla_put_failure;
405 NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size));
406 if (with_timeout(map->timeout))
407 NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
Jozsef Kadlecsik2f9f28b2011-04-04 15:19:25 +0200408 NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100409 NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
410 htonl(sizeof(*map) + map->size * map->dsize));
411 ipset_nest_end(skb, nested);
412
413 return 0;
414nla_put_failure:
415 return -EMSGSIZE;
416}
417
418static int
419list_set_list(const struct ip_set *set,
420 struct sk_buff *skb, struct netlink_callback *cb)
421{
422 const struct list_set *map = set->data;
423 struct nlattr *atd, *nested;
424 u32 i, first = cb->args[2];
425 const struct set_elem *e;
426
427 atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
428 if (!atd)
429 return -EMSGSIZE;
430 for (; cb->args[2] < map->size; cb->args[2]++) {
431 i = cb->args[2];
432 e = list_set_elem(map, i);
433 if (e->id == IPSET_INVALID_ID)
434 goto finish;
435 if (with_timeout(map->timeout) && list_set_expired(map, i))
436 continue;
437 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
438 if (!nested) {
439 if (i == first) {
440 nla_nest_cancel(skb, atd);
441 return -EMSGSIZE;
442 } else
443 goto nla_put_failure;
444 }
445 NLA_PUT_STRING(skb, IPSET_ATTR_NAME,
446 ip_set_name_byindex(e->id));
447 if (with_timeout(map->timeout)) {
448 const struct set_telem *te =
449 (const struct set_telem *) e;
450 NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
451 htonl(ip_set_timeout_get(te->timeout)));
452 }
453 ipset_nest_end(skb, nested);
454 }
455finish:
456 ipset_nest_end(skb, atd);
457 /* Set listing finished */
458 cb->args[2] = 0;
459 return 0;
460
461nla_put_failure:
462 nla_nest_cancel(skb, nested);
463 ipset_nest_end(skb, atd);
464 if (unlikely(i == first)) {
465 cb->args[2] = 0;
466 return -EMSGSIZE;
467 }
468 return 0;
469}
470
471static bool
472list_set_same_set(const struct ip_set *a, const struct ip_set *b)
473{
474 const struct list_set *x = a->data;
475 const struct list_set *y = b->data;
476
477 return x->size == y->size &&
478 x->timeout == y->timeout;
479}
480
481static const struct ip_set_type_variant list_set = {
482 .kadt = list_set_kadt,
483 .uadt = list_set_uadt,
484 .destroy = list_set_destroy,
485 .flush = list_set_flush,
486 .head = list_set_head,
487 .list = list_set_list,
488 .same_set = list_set_same_set,
489};
490
491static void
492list_set_gc(unsigned long ul_set)
493{
494 struct ip_set *set = (struct ip_set *) ul_set;
495 struct list_set *map = set->data;
Jozsef Kadlecsik2f9f28b2011-04-04 15:19:25 +0200496
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200497 write_lock_bh(&set->lock);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200498 cleanup_entries(map);
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200499 write_unlock_bh(&set->lock);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100500
501 map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
502 add_timer(&map->gc);
503}
504
505static void
506list_set_gc_init(struct ip_set *set)
507{
508 struct list_set *map = set->data;
509
510 init_timer(&map->gc);
511 map->gc.data = (unsigned long) set;
512 map->gc.function = list_set_gc;
513 map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
514 add_timer(&map->gc);
515}
516
517/* Create list:set type of sets */
518
519static bool
520init_list_set(struct ip_set *set, u32 size, size_t dsize,
521 unsigned long timeout)
522{
523 struct list_set *map;
524 struct set_elem *e;
525 u32 i;
526
527 map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
528 if (!map)
529 return false;
530
531 map->size = size;
532 map->dsize = dsize;
533 map->timeout = timeout;
534 set->data = map;
535
536 for (i = 0; i < size; i++) {
537 e = list_set_elem(map, i);
538 e->id = IPSET_INVALID_ID;
539 }
540
541 return true;
542}
543
544static int
545list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
546{
547 u32 size = IP_SET_LIST_DEFAULT_SIZE;
548
549 if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
550 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
551 return -IPSET_ERR_PROTOCOL;
552
553 if (tb[IPSET_ATTR_SIZE])
554 size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
555 if (size < IP_SET_LIST_MIN_SIZE)
556 size = IP_SET_LIST_MIN_SIZE;
557
558 if (tb[IPSET_ATTR_TIMEOUT]) {
559 if (!init_list_set(set, size, sizeof(struct set_telem),
560 ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
561 return -ENOMEM;
562
563 list_set_gc_init(set);
564 } else {
565 if (!init_list_set(set, size, sizeof(struct set_elem),
566 IPSET_NO_TIMEOUT))
567 return -ENOMEM;
568 }
569 set->variant = &list_set;
570 return 0;
571}
572
573static struct ip_set_type list_set_type __read_mostly = {
574 .name = "list:set",
575 .protocol = IPSET_PROTOCOL,
576 .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
577 .dimension = IPSET_DIM_ONE,
Jan Engelhardtc15f1c82012-02-14 00:24:10 +0100578 .family = NFPROTO_UNSPEC,
Jozsef Kadlecsikf1e00b32011-06-16 18:51:41 +0200579 .revision_min = 0,
580 .revision_max = 0,
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100581 .create = list_set_create,
582 .create_policy = {
583 [IPSET_ATTR_SIZE] = { .type = NLA_U32 },
584 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
585 },
586 .adt_policy = {
587 [IPSET_ATTR_NAME] = { .type = NLA_STRING,
588 .len = IPSET_MAXNAMELEN },
589 [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
590 .len = IPSET_MAXNAMELEN },
591 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
592 [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
593 [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
594 },
595 .me = THIS_MODULE,
596};
597
598static int __init
599list_set_init(void)
600{
601 return ip_set_type_register(&list_set_type);
602}
603
604static void __exit
605list_set_fini(void)
606{
607 ip_set_type_unregister(&list_set_type);
608}
609
610module_init(list_set_init);
611module_exit(list_set_fini);