blob: f05e9eb863dcf8402941c58770358915a5e1dc2f [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 Kadlecsikac8cc922011-06-16 18:42:40 +020075 enum ipset_adt adt, const struct ip_set_adt_opt *opt)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010076{
77 struct list_set *map = set->data;
78 struct set_elem *elem;
79 u32 i;
80 int ret;
81
82 for (i = 0; i < map->size; i++) {
83 elem = list_set_elem(map, i);
84 if (elem->id == IPSET_INVALID_ID)
85 return 0;
86 if (with_timeout(map->timeout) && list_set_expired(map, i))
87 continue;
88 switch (adt) {
89 case IPSET_TEST:
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +020090 ret = ip_set_test(elem->id, skb, opt);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010091 if (ret > 0)
92 return ret;
93 break;
94 case IPSET_ADD:
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +020095 ret = ip_set_add(elem->id, skb, opt);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +010096 if (ret == 0)
97 return ret;
98 break;
99 case IPSET_DEL:
Jozsef Kadlecsikac8cc922011-06-16 18:42:40 +0200100 ret = ip_set_del(elem->id, skb, opt);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100101 if (ret == 0)
102 return ret;
103 break;
104 default:
105 break;
106 }
107 }
108 return -EINVAL;
109}
110
111static bool
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200112id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100113{
114 const struct set_elem *elem;
115
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200116 if (i < map->size) {
117 elem = list_set_elem(map, i);
118 return elem->id == id;
119 }
120
121 return 0;
122}
123
124static bool
125id_eq_timeout(const struct list_set *map, u32 i, ip_set_id_t id)
126{
127 const struct set_elem *elem;
128
129 if (i < map->size) {
130 elem = list_set_elem(map, i);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100131 return !!(elem->id == id &&
132 !(with_timeout(map->timeout) &&
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200133 list_set_expired(map, i)));
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100134 }
135
136 return 0;
137}
138
139static void
140list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
141{
142 struct set_elem *e;
143
144 for (; i < map->size; i++) {
145 e = list_set_elem(map, i);
146 swap(e->id, id);
147 if (e->id == IPSET_INVALID_ID)
148 break;
149 }
150}
151
152static void
153list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
154 unsigned long timeout)
155{
156 struct set_telem *e;
157
158 for (; i < map->size; i++) {
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200159 e = list_set_telem(map, i);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100160 swap(e->id, id);
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200161 swap(e->timeout, timeout);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100162 if (e->id == IPSET_INVALID_ID)
163 break;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100164 }
165}
166
167static int
168list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
169 unsigned long timeout)
170{
171 const struct set_elem *e = list_set_elem(map, i);
172
173 if (i == map->size - 1 && e->id != IPSET_INVALID_ID)
174 /* Last element replaced: e.g. add new,before,last */
175 ip_set_put_byindex(e->id);
176 if (with_timeout(map->timeout))
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200177 list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100178 else
179 list_elem_add(map, i, id);
180
181 return 0;
182}
183
184static int
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200185list_set_del(struct list_set *map, u32 i)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100186{
187 struct set_elem *a = list_set_elem(map, i), *b;
188
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200189 ip_set_put_byindex(a->id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100190
191 for (; i < map->size - 1; i++) {
192 b = list_set_elem(map, i + 1);
193 a->id = b->id;
194 if (with_timeout(map->timeout))
195 ((struct set_telem *)a)->timeout =
196 ((struct set_telem *)b)->timeout;
197 a = b;
198 if (a->id == IPSET_INVALID_ID)
199 break;
200 }
201 /* Last element */
202 a->id = IPSET_INVALID_ID;
203 return 0;
204}
205
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200206static void
207cleanup_entries(struct list_set *map)
208{
209 struct set_telem *e;
210 u32 i;
211
212 for (i = 0; i < map->size; i++) {
213 e = list_set_telem(map, i);
214 if (e->id != IPSET_INVALID_ID && list_set_expired(map, i))
215 list_set_del(map, i);
216 }
217}
218
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100219static int
220list_set_uadt(struct ip_set *set, struct nlattr *tb[],
221 enum ipset_adt adt, u32 *lineno, u32 flags)
222{
223 struct list_set *map = set->data;
224 bool with_timeout = with_timeout(map->timeout);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200225 bool flag_exist = flags & IPSET_FLAG_EXIST;
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100226 int before = 0;
227 u32 timeout = map->timeout;
228 ip_set_id_t id, refid = IPSET_INVALID_ID;
229 const struct set_elem *elem;
230 struct ip_set *s;
231 u32 i;
232 int ret = 0;
233
234 if (unlikely(!tb[IPSET_ATTR_NAME] ||
235 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
236 !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
237 return -IPSET_ERR_PROTOCOL;
238
239 if (tb[IPSET_ATTR_LINENO])
240 *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
241
242 id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
243 if (id == IPSET_INVALID_ID)
244 return -IPSET_ERR_NAME;
245 /* "Loop detection" */
246 if (s->type->features & IPSET_TYPE_NAME) {
247 ret = -IPSET_ERR_LOOP;
248 goto finish;
249 }
250
251 if (tb[IPSET_ATTR_CADT_FLAGS]) {
252 u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
253 before = f & IPSET_FLAG_BEFORE;
254 }
255
256 if (before && !tb[IPSET_ATTR_NAMEREF]) {
257 ret = -IPSET_ERR_BEFORE;
258 goto finish;
259 }
260
261 if (tb[IPSET_ATTR_NAMEREF]) {
262 refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
263 &s);
264 if (refid == IPSET_INVALID_ID) {
265 ret = -IPSET_ERR_NAMEREF;
266 goto finish;
267 }
268 if (!before)
269 before = -1;
270 }
271 if (tb[IPSET_ATTR_TIMEOUT]) {
272 if (!with_timeout) {
273 ret = -IPSET_ERR_TIMEOUT;
274 goto finish;
275 }
276 timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
277 }
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200278 if (with_timeout && adt != IPSET_TEST)
279 cleanup_entries(map);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100280
281 switch (adt) {
282 case IPSET_TEST:
283 for (i = 0; i < map->size && !ret; i++) {
284 elem = list_set_elem(map, i);
285 if (elem->id == IPSET_INVALID_ID ||
286 (before != 0 && i + 1 >= map->size))
287 break;
288 else if (with_timeout && list_set_expired(map, i))
289 continue;
290 else if (before > 0 && elem->id == id)
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200291 ret = id_eq_timeout(map, i + 1, refid);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100292 else if (before < 0 && elem->id == refid)
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200293 ret = id_eq_timeout(map, i + 1, id);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100294 else if (before == 0 && elem->id == id)
295 ret = 1;
296 }
297 break;
298 case IPSET_ADD:
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200299 for (i = 0; i < map->size; i++) {
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100300 elem = list_set_elem(map, i);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200301 if (elem->id != id)
302 continue;
303 if (!(with_timeout && flag_exist)) {
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100304 ret = -IPSET_ERR_EXIST;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200305 goto finish;
306 } else {
307 struct set_telem *e = list_set_telem(map, i);
308
309 if ((before > 1 &&
310 !id_eq(map, i + 1, refid)) ||
311 (before < 0 &&
312 (i == 0 || !id_eq(map, i - 1, refid)))) {
Jozsef Kadlecsik483e9ea2011-06-16 18:41:53 +0200313 ret = -IPSET_ERR_EXIST;
314 goto finish;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200315 }
316 e->timeout = ip_set_timeout_set(timeout);
317 ip_set_put_byindex(id);
318 ret = 0;
319 goto finish;
320 }
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100321 }
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100322 ret = -IPSET_ERR_LIST_FULL;
323 for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
324 elem = list_set_elem(map, i);
325 if (elem->id == IPSET_INVALID_ID)
326 ret = before != 0 ? -IPSET_ERR_REF_EXIST
327 : list_set_add(map, i, id, timeout);
328 else if (elem->id != refid)
329 continue;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200330 else if (before > 0)
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100331 ret = list_set_add(map, i, id, timeout);
332 else if (i + 1 < map->size)
333 ret = list_set_add(map, i + 1, id, timeout);
334 }
335 break;
336 case IPSET_DEL:
337 ret = -IPSET_ERR_EXIST;
338 for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
339 elem = list_set_elem(map, i);
340 if (elem->id == IPSET_INVALID_ID) {
341 ret = before != 0 ? -IPSET_ERR_REF_EXIST
342 : -IPSET_ERR_EXIST;
343 break;
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200344 } else if (elem->id == id &&
345 (before == 0 ||
346 (before > 0 && id_eq(map, i + 1, refid))))
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200347 ret = list_set_del(map, i);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200348 else if (elem->id == refid &&
349 before < 0 && id_eq(map, i + 1, id))
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200350 ret = list_set_del(map, i + 1);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100351 }
352 break;
353 default:
354 break;
355 }
356
357finish:
358 if (refid != IPSET_INVALID_ID)
359 ip_set_put_byindex(refid);
360 if (adt != IPSET_ADD || ret)
361 ip_set_put_byindex(id);
362
363 return ip_set_eexist(ret, flags) ? 0 : ret;
364}
365
366static void
367list_set_flush(struct ip_set *set)
368{
369 struct list_set *map = set->data;
370 struct set_elem *elem;
371 u32 i;
372
373 for (i = 0; i < map->size; i++) {
374 elem = list_set_elem(map, i);
375 if (elem->id != IPSET_INVALID_ID) {
376 ip_set_put_byindex(elem->id);
377 elem->id = IPSET_INVALID_ID;
378 }
379 }
380}
381
382static void
383list_set_destroy(struct ip_set *set)
384{
385 struct list_set *map = set->data;
386
387 if (with_timeout(map->timeout))
388 del_timer_sync(&map->gc);
389 list_set_flush(set);
390 kfree(map);
391
392 set->data = NULL;
393}
394
395static int
396list_set_head(struct ip_set *set, struct sk_buff *skb)
397{
398 const struct list_set *map = set->data;
399 struct nlattr *nested;
400
401 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
402 if (!nested)
403 goto nla_put_failure;
404 NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size));
405 if (with_timeout(map->timeout))
406 NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
Jozsef Kadlecsik2f9f28b2011-04-04 15:19:25 +0200407 NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1));
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100408 NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
409 htonl(sizeof(*map) + map->size * map->dsize));
410 ipset_nest_end(skb, nested);
411
412 return 0;
413nla_put_failure:
414 return -EMSGSIZE;
415}
416
417static int
418list_set_list(const struct ip_set *set,
419 struct sk_buff *skb, struct netlink_callback *cb)
420{
421 const struct list_set *map = set->data;
422 struct nlattr *atd, *nested;
423 u32 i, first = cb->args[2];
424 const struct set_elem *e;
425
426 atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
427 if (!atd)
428 return -EMSGSIZE;
429 for (; cb->args[2] < map->size; cb->args[2]++) {
430 i = cb->args[2];
431 e = list_set_elem(map, i);
432 if (e->id == IPSET_INVALID_ID)
433 goto finish;
434 if (with_timeout(map->timeout) && list_set_expired(map, i))
435 continue;
436 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
437 if (!nested) {
438 if (i == first) {
439 nla_nest_cancel(skb, atd);
440 return -EMSGSIZE;
441 } else
442 goto nla_put_failure;
443 }
444 NLA_PUT_STRING(skb, IPSET_ATTR_NAME,
445 ip_set_name_byindex(e->id));
446 if (with_timeout(map->timeout)) {
447 const struct set_telem *te =
448 (const struct set_telem *) e;
449 NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
450 htonl(ip_set_timeout_get(te->timeout)));
451 }
452 ipset_nest_end(skb, nested);
453 }
454finish:
455 ipset_nest_end(skb, atd);
456 /* Set listing finished */
457 cb->args[2] = 0;
458 return 0;
459
460nla_put_failure:
461 nla_nest_cancel(skb, nested);
462 ipset_nest_end(skb, atd);
463 if (unlikely(i == first)) {
464 cb->args[2] = 0;
465 return -EMSGSIZE;
466 }
467 return 0;
468}
469
470static bool
471list_set_same_set(const struct ip_set *a, const struct ip_set *b)
472{
473 const struct list_set *x = a->data;
474 const struct list_set *y = b->data;
475
476 return x->size == y->size &&
477 x->timeout == y->timeout;
478}
479
480static const struct ip_set_type_variant list_set = {
481 .kadt = list_set_kadt,
482 .uadt = list_set_uadt,
483 .destroy = list_set_destroy,
484 .flush = list_set_flush,
485 .head = list_set_head,
486 .list = list_set_list,
487 .same_set = list_set_same_set,
488};
489
490static void
491list_set_gc(unsigned long ul_set)
492{
493 struct ip_set *set = (struct ip_set *) ul_set;
494 struct list_set *map = set->data;
Jozsef Kadlecsik2f9f28b2011-04-04 15:19:25 +0200495
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200496 write_lock_bh(&set->lock);
Jozsef Kadlecsik54162192011-06-16 18:40:55 +0200497 cleanup_entries(map);
Jozsef Kadlecsik512d06b2011-04-04 15:18:45 +0200498 write_unlock_bh(&set->lock);
Jozsef Kadlecsikf8308372011-02-01 15:54:59 +0100499
500 map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
501 add_timer(&map->gc);
502}
503
504static void
505list_set_gc_init(struct ip_set *set)
506{
507 struct list_set *map = set->data;
508
509 init_timer(&map->gc);
510 map->gc.data = (unsigned long) set;
511 map->gc.function = list_set_gc;
512 map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
513 add_timer(&map->gc);
514}
515
516/* Create list:set type of sets */
517
518static bool
519init_list_set(struct ip_set *set, u32 size, size_t dsize,
520 unsigned long timeout)
521{
522 struct list_set *map;
523 struct set_elem *e;
524 u32 i;
525
526 map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
527 if (!map)
528 return false;
529
530 map->size = size;
531 map->dsize = dsize;
532 map->timeout = timeout;
533 set->data = map;
534
535 for (i = 0; i < size; i++) {
536 e = list_set_elem(map, i);
537 e->id = IPSET_INVALID_ID;
538 }
539
540 return true;
541}
542
543static int
544list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
545{
546 u32 size = IP_SET_LIST_DEFAULT_SIZE;
547
548 if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
549 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
550 return -IPSET_ERR_PROTOCOL;
551
552 if (tb[IPSET_ATTR_SIZE])
553 size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
554 if (size < IP_SET_LIST_MIN_SIZE)
555 size = IP_SET_LIST_MIN_SIZE;
556
557 if (tb[IPSET_ATTR_TIMEOUT]) {
558 if (!init_list_set(set, size, sizeof(struct set_telem),
559 ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
560 return -ENOMEM;
561
562 list_set_gc_init(set);
563 } else {
564 if (!init_list_set(set, size, sizeof(struct set_elem),
565 IPSET_NO_TIMEOUT))
566 return -ENOMEM;
567 }
568 set->variant = &list_set;
569 return 0;
570}
571
572static struct ip_set_type list_set_type __read_mostly = {
573 .name = "list:set",
574 .protocol = IPSET_PROTOCOL,
575 .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
576 .dimension = IPSET_DIM_ONE,
577 .family = AF_UNSPEC,
578 .revision = 0,
579 .create = list_set_create,
580 .create_policy = {
581 [IPSET_ATTR_SIZE] = { .type = NLA_U32 },
582 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
583 },
584 .adt_policy = {
585 [IPSET_ATTR_NAME] = { .type = NLA_STRING,
586 .len = IPSET_MAXNAMELEN },
587 [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
588 .len = IPSET_MAXNAMELEN },
589 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
590 [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
591 [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
592 },
593 .me = THIS_MODULE,
594};
595
596static int __init
597list_set_init(void)
598{
599 return ip_set_type_register(&list_set_type);
600}
601
602static void __exit
603list_set_fini(void)
604{
605 ip_set_type_unregister(&list_set_type);
606}
607
608module_init(list_set_init);
609module_exit(list_set_fini);