linux/net/netfilter/ipset/ip_set_list_set.c
<<
>>
Prefs
   1/* Copyright (C) 2008-2013 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/rculist.h>
  13#include <linux/skbuff.h>
  14#include <linux/errno.h>
  15
  16#include <linux/netfilter/ipset/ip_set.h>
  17#include <linux/netfilter/ipset/ip_set_list.h>
  18
  19#define IPSET_TYPE_REV_MIN      0
  20/*                              1    Counters support added */
  21/*                              2    Comments support added */
  22#define IPSET_TYPE_REV_MAX      3 /* skbinfo support added */
  23
  24MODULE_LICENSE("GPL");
  25MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
  26IP_SET_MODULE_DESC("list:set", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
  27MODULE_ALIAS("ip_set_list:set");
  28
  29/* Member elements  */
  30struct set_elem {
  31        struct rcu_head rcu;
  32        struct list_head list;
  33        struct ip_set *set;     /* Sigh, in order to cleanup reference */
  34        ip_set_id_t id;
  35} __aligned(__alignof__(u64));
  36
  37struct set_adt_elem {
  38        ip_set_id_t id;
  39        ip_set_id_t refid;
  40        int before;
  41};
  42
  43/* Type structure */
  44struct list_set {
  45        u32 size;               /* size of set list array */
  46        struct timer_list gc;   /* garbage collection */
  47        struct net *net;        /* namespace */
  48        struct list_head members; /* the set members */
  49};
  50
  51static int
  52list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
  53               const struct xt_action_param *par,
  54               struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
  55{
  56        struct list_set *map = set->data;
  57        struct set_elem *e;
  58        u32 cmdflags = opt->cmdflags;
  59        int ret;
  60
  61        /* Don't lookup sub-counters at all */
  62        opt->cmdflags &= ~IPSET_FLAG_MATCH_COUNTERS;
  63        if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE)
  64                opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE;
  65        list_for_each_entry_rcu(e, &map->members, list) {
  66                if (SET_WITH_TIMEOUT(set) &&
  67                    ip_set_timeout_expired(ext_timeout(e, set)))
  68                        continue;
  69                ret = ip_set_test(e->id, skb, par, opt);
  70                if (ret > 0) {
  71                        if (SET_WITH_COUNTER(set))
  72                                ip_set_update_counter(ext_counter(e, set),
  73                                                      ext, &opt->ext,
  74                                                      cmdflags);
  75                        if (SET_WITH_SKBINFO(set))
  76                                ip_set_get_skbinfo(ext_skbinfo(e, set),
  77                                                   ext, &opt->ext,
  78                                                   cmdflags);
  79                        return ret;
  80                }
  81        }
  82        return 0;
  83}
  84
  85static int
  86list_set_kadd(struct ip_set *set, const struct sk_buff *skb,
  87              const struct xt_action_param *par,
  88              struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
  89{
  90        struct list_set *map = set->data;
  91        struct set_elem *e;
  92        int ret;
  93
  94        list_for_each_entry(e, &map->members, list) {
  95                if (SET_WITH_TIMEOUT(set) &&
  96                    ip_set_timeout_expired(ext_timeout(e, set)))
  97                        continue;
  98                ret = ip_set_add(e->id, skb, par, opt);
  99                if (ret == 0)
 100                        return ret;
 101        }
 102        return 0;
 103}
 104
 105static int
 106list_set_kdel(struct ip_set *set, const struct sk_buff *skb,
 107              const struct xt_action_param *par,
 108              struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
 109{
 110        struct list_set *map = set->data;
 111        struct set_elem *e;
 112        int ret;
 113
 114        list_for_each_entry(e, &map->members, list) {
 115                if (SET_WITH_TIMEOUT(set) &&
 116                    ip_set_timeout_expired(ext_timeout(e, set)))
 117                        continue;
 118                ret = ip_set_del(e->id, skb, par, opt);
 119                if (ret == 0)
 120                        return ret;
 121        }
 122        return 0;
 123}
 124
 125static int
 126list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
 127              const struct xt_action_param *par,
 128              enum ipset_adt adt, struct ip_set_adt_opt *opt)
 129{
 130        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 131        int ret = -EINVAL;
 132
 133        rcu_read_lock();
 134        switch (adt) {
 135        case IPSET_TEST:
 136                ret = list_set_ktest(set, skb, par, opt, &ext);
 137                break;
 138        case IPSET_ADD:
 139                ret = list_set_kadd(set, skb, par, opt, &ext);
 140                break;
 141        case IPSET_DEL:
 142                ret = list_set_kdel(set, skb, par, opt, &ext);
 143                break;
 144        default:
 145                break;
 146        }
 147        rcu_read_unlock();
 148
 149        return ret;
 150}
 151
 152/* Userspace interfaces: we are protected by the nfnl mutex */
 153
 154static void
 155__list_set_del_rcu(struct rcu_head * rcu)
 156{
 157        struct set_elem *e = container_of(rcu, struct set_elem, rcu);
 158        struct ip_set *set = e->set;
 159        struct list_set *map = set->data;
 160
 161        ip_set_put_byindex(map->net, e->id);
 162        ip_set_ext_destroy(set, e);
 163        kfree(e);
 164}
 165
 166static inline void
 167list_set_del(struct ip_set *set, struct set_elem *e)
 168{
 169        list_del_rcu(&e->list);
 170        call_rcu(&e->rcu, __list_set_del_rcu);
 171}
 172
 173static inline void
 174list_set_replace(struct set_elem *e, struct set_elem *old)
 175{
 176        list_replace_rcu(&old->list, &e->list);
 177        call_rcu(&old->rcu, __list_set_del_rcu);
 178}
 179
 180static void
 181set_cleanup_entries(struct ip_set *set)
 182{
 183        struct list_set *map = set->data;
 184        struct set_elem *e, *n;
 185
 186        list_for_each_entry_safe(e, n, &map->members, list)
 187                if (ip_set_timeout_expired(ext_timeout(e, set)))
 188                        list_set_del(set, e);
 189}
 190
 191static int
 192list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 193               struct ip_set_ext *mext, u32 flags)
 194{
 195        struct list_set *map = set->data;
 196        struct set_adt_elem *d = value;
 197        struct set_elem *e, *next, *prev = NULL;
 198        int ret;
 199
 200        list_for_each_entry(e, &map->members, list) {
 201                if (SET_WITH_TIMEOUT(set) &&
 202                    ip_set_timeout_expired(ext_timeout(e, set)))
 203                        continue;
 204                else if (e->id != d->id) {
 205                        prev = e;
 206                        continue;
 207                }
 208
 209                if (d->before == 0) {
 210                        ret = 1;
 211                } else if (d->before > 0) {
 212                        next = list_next_entry(e, list);
 213                        ret = !list_is_last(&e->list, &map->members) &&
 214                              next->id == d->refid;
 215                } else {
 216                        ret = prev && prev->id == d->refid;
 217                }
 218                return ret;
 219        }
 220        return 0;
 221}
 222
 223static void
 224list_set_init_extensions(struct ip_set *set, const struct ip_set_ext *ext,
 225                         struct set_elem *e)
 226{
 227        if (SET_WITH_COUNTER(set))
 228                ip_set_init_counter(ext_counter(e, set), ext);
 229        if (SET_WITH_COMMENT(set))
 230                ip_set_init_comment(ext_comment(e, set), ext);
 231        if (SET_WITH_SKBINFO(set))
 232                ip_set_init_skbinfo(ext_skbinfo(e, set), ext);
 233        /* Update timeout last */
 234        if (SET_WITH_TIMEOUT(set))
 235                ip_set_timeout_set(ext_timeout(e, set), ext->timeout);
 236}
 237
 238static int
 239list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 240              struct ip_set_ext *mext, u32 flags)
 241{
 242        struct list_set *map = set->data;
 243        struct set_adt_elem *d = value;
 244        struct set_elem *e, *n, *prev, *next;
 245        bool flag_exist = flags & IPSET_FLAG_EXIST;
 246
 247        /* Find where to add the new entry */
 248        n = prev = next = NULL;
 249        list_for_each_entry(e, &map->members, list) {
 250                if (SET_WITH_TIMEOUT(set) &&
 251                    ip_set_timeout_expired(ext_timeout(e, set)))
 252                        continue;
 253                else if (d->id == e->id)
 254                        n = e;
 255                else if (d->before == 0 || e->id != d->refid)
 256                        continue;
 257                else if (d->before > 0)
 258                        next = e;
 259                else
 260                        prev = e;
 261        }
 262        /* Re-add already existing element */
 263        if (n) {
 264                if ((d->before > 0 && !next) ||
 265                    (d->before < 0 && !prev))
 266                        return -IPSET_ERR_REF_EXIST;
 267                if (!flag_exist)
 268                        return -IPSET_ERR_EXIST;
 269                /* Update extensions */
 270                ip_set_ext_destroy(set, n);
 271                list_set_init_extensions(set, ext, n);
 272
 273                /* Set is already added to the list */
 274                ip_set_put_byindex(map->net, d->id);
 275                return 0;
 276        }
 277        /* Add new entry */
 278        if (d->before == 0) {
 279                /* Append  */
 280                n = list_empty(&map->members) ? NULL :
 281                    list_last_entry(&map->members, struct set_elem, list);
 282        } else if (d->before > 0) {
 283                /* Insert after next element */
 284                if (!list_is_last(&next->list, &map->members))
 285                        n = list_next_entry(next, list);
 286        } else {
 287                /* Insert before prev element */
 288                if (prev->list.prev != &map->members)
 289                        n = list_prev_entry(prev, list);
 290        }
 291        /* Can we replace a timed out entry? */
 292        if (n &&
 293            !(SET_WITH_TIMEOUT(set) &&
 294              ip_set_timeout_expired(ext_timeout(n, set))))
 295                n =  NULL;
 296
 297        e = kzalloc(set->dsize, GFP_ATOMIC);
 298        if (!e)
 299                return -ENOMEM;
 300        e->id = d->id;
 301        e->set = set;
 302        INIT_LIST_HEAD(&e->list);
 303        list_set_init_extensions(set, ext, e);
 304        if (n)
 305                list_set_replace(e, n);
 306        else if (next)
 307                list_add_tail_rcu(&e->list, &next->list);
 308        else if (prev)
 309                list_add_rcu(&e->list, &prev->list);
 310        else
 311                list_add_tail_rcu(&e->list, &map->members);
 312
 313        return 0;
 314}
 315
 316static int
 317list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 318              struct ip_set_ext *mext, u32 flags)
 319{
 320        struct list_set *map = set->data;
 321        struct set_adt_elem *d = value;
 322        struct set_elem *e, *next, *prev = NULL;
 323
 324        list_for_each_entry(e, &map->members, list) {
 325                if (SET_WITH_TIMEOUT(set) &&
 326                    ip_set_timeout_expired(ext_timeout(e, set)))
 327                        continue;
 328                else if (e->id != d->id) {
 329                        prev = e;
 330                        continue;
 331                }
 332
 333                if (d->before > 0) {
 334                        next = list_next_entry(e, list);
 335                        if (list_is_last(&e->list, &map->members) ||
 336                            next->id != d->refid)
 337                                return -IPSET_ERR_REF_EXIST;
 338                } else if (d->before < 0) {
 339                        if (!prev || prev->id != d->refid)
 340                                return -IPSET_ERR_REF_EXIST;
 341                }
 342                list_set_del(set, e);
 343                return 0;
 344        }
 345        return d->before != 0 ? -IPSET_ERR_REF_EXIST : -IPSET_ERR_EXIST;
 346}
 347
 348static int
 349list_set_uadt(struct ip_set *set, struct nlattr *tb[],
 350              enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 351{
 352        struct list_set *map = set->data;
 353        ipset_adtfn adtfn = set->variant->adt[adt];
 354        struct set_adt_elem e = { .refid = IPSET_INVALID_ID };
 355        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
 356        struct ip_set *s;
 357        int ret = 0;
 358
 359        if (tb[IPSET_ATTR_LINENO])
 360                *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 361
 362        if (unlikely(!tb[IPSET_ATTR_NAME] ||
 363                     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
 364                return -IPSET_ERR_PROTOCOL;
 365
 366        ret = ip_set_get_extensions(set, tb, &ext);
 367        if (ret)
 368                return ret;
 369        e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
 370        if (e.id == IPSET_INVALID_ID)
 371                return -IPSET_ERR_NAME;
 372        /* "Loop detection" */
 373        if (s->type->features & IPSET_TYPE_NAME) {
 374                ret = -IPSET_ERR_LOOP;
 375                goto finish;
 376        }
 377
 378        if (tb[IPSET_ATTR_CADT_FLAGS]) {
 379                u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 380
 381                e.before = f & IPSET_FLAG_BEFORE;
 382        }
 383
 384        if (e.before && !tb[IPSET_ATTR_NAMEREF]) {
 385                ret = -IPSET_ERR_BEFORE;
 386                goto finish;
 387        }
 388
 389        if (tb[IPSET_ATTR_NAMEREF]) {
 390                e.refid = ip_set_get_byname(map->net,
 391                                            nla_data(tb[IPSET_ATTR_NAMEREF]),
 392                                            &s);
 393                if (e.refid == IPSET_INVALID_ID) {
 394                        ret = -IPSET_ERR_NAMEREF;
 395                        goto finish;
 396                }
 397                if (!e.before)
 398                        e.before = -1;
 399        }
 400        if (adt != IPSET_TEST && SET_WITH_TIMEOUT(set))
 401                set_cleanup_entries(set);
 402
 403        ret = adtfn(set, &e, &ext, &ext, flags);
 404
 405finish:
 406        if (e.refid != IPSET_INVALID_ID)
 407                ip_set_put_byindex(map->net, e.refid);
 408        if (adt != IPSET_ADD || ret)
 409                ip_set_put_byindex(map->net, e.id);
 410
 411        return ip_set_eexist(ret, flags) ? 0 : ret;
 412}
 413
 414static void
 415list_set_flush(struct ip_set *set)
 416{
 417        struct list_set *map = set->data;
 418        struct set_elem *e, *n;
 419
 420        list_for_each_entry_safe(e, n, &map->members, list)
 421                list_set_del(set, e);
 422}
 423
 424static void
 425list_set_destroy(struct ip_set *set)
 426{
 427        struct list_set *map = set->data;
 428        struct set_elem *e, *n;
 429
 430        if (SET_WITH_TIMEOUT(set))
 431                del_timer_sync(&map->gc);
 432
 433        list_for_each_entry_safe(e, n, &map->members, list) {
 434                list_del(&e->list);
 435                ip_set_put_byindex(map->net, e->id);
 436                ip_set_ext_destroy(set, e);
 437                kfree(e);
 438        }
 439        kfree(map);
 440
 441        set->data = NULL;
 442}
 443
 444static int
 445list_set_head(struct ip_set *set, struct sk_buff *skb)
 446{
 447        const struct list_set *map = set->data;
 448        struct nlattr *nested;
 449        struct set_elem *e;
 450        u32 n = 0;
 451
 452        rcu_read_lock();
 453        list_for_each_entry_rcu(e, &map->members, list)
 454                n++;
 455        rcu_read_unlock();
 456
 457        nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
 458        if (!nested)
 459                goto nla_put_failure;
 460        if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
 461            nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) ||
 462            nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
 463                          htonl(sizeof(*map) + n * set->dsize)))
 464                goto nla_put_failure;
 465        if (unlikely(ip_set_put_flags(skb, set)))
 466                goto nla_put_failure;
 467        ipset_nest_end(skb, nested);
 468
 469        return 0;
 470nla_put_failure:
 471        return -EMSGSIZE;
 472}
 473
 474static int
 475list_set_list(const struct ip_set *set,
 476              struct sk_buff *skb, struct netlink_callback *cb)
 477{
 478        const struct list_set *map = set->data;
 479        struct nlattr *atd, *nested;
 480        u32 i = 0, first = cb->args[IPSET_CB_ARG0];
 481        struct set_elem *e;
 482        int ret = 0;
 483
 484        atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
 485        if (!atd)
 486                return -EMSGSIZE;
 487
 488        rcu_read_lock();
 489        list_for_each_entry_rcu(e, &map->members, list) {
 490                if (i < first ||
 491                    (SET_WITH_TIMEOUT(set) &&
 492                     ip_set_timeout_expired(ext_timeout(e, set)))) {
 493                        i++;
 494                        continue;
 495                }
 496                nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
 497                if (!nested)
 498                        goto nla_put_failure;
 499                if (nla_put_string(skb, IPSET_ATTR_NAME,
 500                                   ip_set_name_byindex(map->net, e->id)))
 501                        goto nla_put_failure;
 502                if (ip_set_put_extensions(skb, set, e, true))
 503                        goto nla_put_failure;
 504                ipset_nest_end(skb, nested);
 505                i++;
 506        }
 507
 508        ipset_nest_end(skb, atd);
 509        /* Set listing finished */
 510        cb->args[IPSET_CB_ARG0] = 0;
 511        goto out;
 512
 513nla_put_failure:
 514        nla_nest_cancel(skb, nested);
 515        if (unlikely(i == first)) {
 516                nla_nest_cancel(skb, atd);
 517                cb->args[IPSET_CB_ARG0] = 0;
 518                ret = -EMSGSIZE;
 519        } else {
 520                cb->args[IPSET_CB_ARG0] = i;
 521        }
 522        ipset_nest_end(skb, atd);
 523out:
 524        rcu_read_unlock();
 525        return ret;
 526}
 527
 528static bool
 529list_set_same_set(const struct ip_set *a, const struct ip_set *b)
 530{
 531        const struct list_set *x = a->data;
 532        const struct list_set *y = b->data;
 533
 534        return x->size == y->size &&
 535               a->timeout == b->timeout &&
 536               a->extensions == b->extensions;
 537}
 538
 539static const struct ip_set_type_variant set_variant = {
 540        .kadt   = list_set_kadt,
 541        .uadt   = list_set_uadt,
 542        .adt    = {
 543                [IPSET_ADD] = list_set_uadd,
 544                [IPSET_DEL] = list_set_udel,
 545                [IPSET_TEST] = list_set_utest,
 546        },
 547        .destroy = list_set_destroy,
 548        .flush  = list_set_flush,
 549        .head   = list_set_head,
 550        .list   = list_set_list,
 551        .same_set = list_set_same_set,
 552};
 553
 554static void
 555list_set_gc(unsigned long ul_set)
 556{
 557        struct ip_set *set = (struct ip_set *)ul_set;
 558        struct list_set *map = set->data;
 559
 560        spin_lock_bh(&set->lock);
 561        set_cleanup_entries(set);
 562        spin_unlock_bh(&set->lock);
 563
 564        map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
 565        add_timer(&map->gc);
 566}
 567
 568static void
 569list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
 570{
 571        struct list_set *map = set->data;
 572
 573        init_timer(&map->gc);
 574        map->gc.data = (unsigned long)set;
 575        map->gc.function = gc;
 576        map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
 577        add_timer(&map->gc);
 578}
 579
 580/* Create list:set type of sets */
 581
 582static bool
 583init_list_set(struct net *net, struct ip_set *set, u32 size)
 584{
 585        struct list_set *map;
 586
 587        map = kzalloc(sizeof(*map), GFP_KERNEL);
 588        if (!map)
 589                return false;
 590
 591        map->size = size;
 592        map->net = net;
 593        INIT_LIST_HEAD(&map->members);
 594        set->data = map;
 595
 596        return true;
 597}
 598
 599static int
 600list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
 601                u32 flags)
 602{
 603        u32 size = IP_SET_LIST_DEFAULT_SIZE;
 604
 605        if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
 606                     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
 607                     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
 608                return -IPSET_ERR_PROTOCOL;
 609
 610        if (tb[IPSET_ATTR_SIZE])
 611                size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
 612        if (size < IP_SET_LIST_MIN_SIZE)
 613                size = IP_SET_LIST_MIN_SIZE;
 614
 615        set->variant = &set_variant;
 616        set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem),
 617                                     __alignof__(struct set_elem));
 618        if (!init_list_set(net, set, size))
 619                return -ENOMEM;
 620        if (tb[IPSET_ATTR_TIMEOUT]) {
 621                set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
 622                list_set_gc_init(set, list_set_gc);
 623        }
 624        return 0;
 625}
 626
 627static struct ip_set_type list_set_type __read_mostly = {
 628        .name           = "list:set",
 629        .protocol       = IPSET_PROTOCOL,
 630        .features       = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
 631        .dimension      = IPSET_DIM_ONE,
 632        .family         = NFPROTO_UNSPEC,
 633        .revision_min   = IPSET_TYPE_REV_MIN,
 634        .revision_max   = IPSET_TYPE_REV_MAX,
 635        .create         = list_set_create,
 636        .create_policy  = {
 637                [IPSET_ATTR_SIZE]       = { .type = NLA_U32 },
 638                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 639                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
 640        },
 641        .adt_policy     = {
 642                [IPSET_ATTR_NAME]       = { .type = NLA_STRING,
 643                                            .len = IPSET_MAXNAMELEN },
 644                [IPSET_ATTR_NAMEREF]    = { .type = NLA_STRING,
 645                                            .len = IPSET_MAXNAMELEN },
 646                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 647                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
 648                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
 649                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
 650                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
 651                [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING,
 652                                            .len  = IPSET_MAX_COMMENT_SIZE },
 653                [IPSET_ATTR_SKBMARK]    = { .type = NLA_U64 },
 654                [IPSET_ATTR_SKBPRIO]    = { .type = NLA_U32 },
 655                [IPSET_ATTR_SKBQUEUE]   = { .type = NLA_U16 },
 656        },
 657        .me             = THIS_MODULE,
 658};
 659
 660static int __init
 661list_set_init(void)
 662{
 663        return ip_set_type_register(&list_set_type);
 664}
 665
 666static void __exit
 667list_set_fini(void)
 668{
 669        rcu_barrier();
 670        ip_set_type_unregister(&list_set_type);
 671}
 672
 673module_init(list_set_init);
 674module_exit(list_set_fini);
 675