Commit f830837f0eed0f9e371b8fd65169365780814bb1

Authored by Jozsef Kadlecsik
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
... ... @@ -19,4 +19,7 @@
19 19 obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o
20 20 obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o
21 21 obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o
  22 +
  23 +# list types
  24 +obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o
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);