From 3fd6b24ace319b139ec3c4e3031a5f05d21e304e Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Tue, 15 Jun 2010 13:30:55 +0200 Subject: ipset 5 in an almost ready state - milestone Reworked protocol and internal interfaces, missing set types added, backward compatibility verified, lots of tests added (and thanks to the tests, bugs fixed), even the manpage is rewritten ;-). Countless changes everywhere... The missing bits before announcing ipset 5: - net namespace support - new iptables/ip6tables extension library - iptables/ip6tables match and target tests (backward/forward compatibility) - tests on catching syntax errors --- kernel/ip_set_list_set.c | 731 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 498 insertions(+), 233 deletions(-) (limited to 'kernel/ip_set_list_set.c') diff --git a/kernel/ip_set_list_set.c b/kernel/ip_set_list_set.c index 3cfdae8..ce6c4d1 100644 --- a/kernel/ip_set_list_set.c +++ b/kernel/ip_set_list_set.c @@ -1,324 +1,589 @@ -/* Copyright (C) 2008 Jozsef Kadlecsik +/* Copyright (C) 2008-2010 Jozsef Kadlecsik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -/* Kernel module implementing an IP set type: the setlist type */ +/* Kernel module implementing an IP set type: the list:set type */ +#include #include #include #include #include -#include -#include -#include +#include +#include +#include -/* - * before ==> index, ref - * after ==> ref, index - */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("list:set type of IP sets"); +MODULE_ALIAS("ip_set_list:set"); + +/* Member elements without and with timeout */ +struct set_elem { + ip_set_id_t id; +}; + +struct set_telem { + ip_set_id_t id; + unsigned long timeout; +}; + +/* Type structure */ +struct list_set { + size_t dsize; /* element size */ + u32 size; /* size of set list array */ + u32 timeout; /* timeout value */ + struct timer_list gc; /* garbage collection */ + struct set_elem members[0]; /* the set members */ +}; + +static inline struct set_elem * +list_set_elem(const struct list_set *map, u32 id) +{ + return (struct set_elem *)((char *)map->members + id * map->dsize); +} + +static inline bool +list_set_timeout(const struct list_set *map, u32 id) +{ + const struct set_telem *elem = + (const struct set_telem *) list_set_elem(map, id); + + return ip_set_timeout_test(elem->timeout); +} + +static inline bool +list_set_expired(const struct list_set *map, u32 id) +{ + const struct set_telem *elem = + (const struct set_telem *) list_set_elem(map, id); + + return ip_set_timeout_expired(elem->timeout); +} static inline int -next_index_eq(const struct ip_set_setlist *map, int i, ip_set_id_t index) +list_set_exist(const struct set_telem *elem) { - return i < map->size && map->index[i] == index; + return elem->id != IPSET_INVALID_ID + && !ip_set_timeout_expired(elem->timeout); } +/* Set list without and with timeout */ + static int -setlist_utest(struct ip_set *set, const void *data, u_int32_t size) +list_set_kadt(struct ip_set *set, const struct sk_buff *skb, + enum ipset_adt adt, u8 pf, u8 dim, u8 flags) { - const struct ip_set_setlist *map = set->data; - const struct ip_set_req_setlist *req = data; - ip_set_id_t index, ref = IP_SET_INVALID_ID; - int i, res = 0; - struct ip_set *s; - - if (req->before && req->ref[0] == '\0') - return 0; - - index = __ip_set_get_byname(req->name, &s); - if (index == IP_SET_INVALID_ID) - return 0; - if (req->ref[0] != '\0') { - ref = __ip_set_get_byname(req->ref, &s); - if (ref == IP_SET_INVALID_ID) - goto finish; - } - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID; i++) { - if (req->before && map->index[i] == index) { - res = next_index_eq(map, i + 1, ref); - break; - } else if (!req->before) { - if ((ref == IP_SET_INVALID_ID - && map->index[i] == index) - || (map->index[i] == ref - && next_index_eq(map, i + 1, index))) { - res = 1; - break; - } + struct list_set *map = set->data; + struct set_elem *elem; + u32 i; + int ret; + + for (i = 0; i < map->size; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID) + return 0; + if (with_timeout(map->timeout) && list_set_expired(map, i)) + continue; + switch (adt) { + case IPSET_TEST: + ret = ip_set_test(elem->id, skb, pf, dim, flags); + if (ret > 0) + return ret; + break; + case IPSET_ADD: + ret = ip_set_add(elem->id, skb, pf, dim, flags); + if (ret == 0) + return ret; + break; + case IPSET_DEL: + ret = ip_set_del(elem->id, skb, pf, dim, flags); + if (ret == 0) + return ret; + break; + default: + break; } } - if (ref != IP_SET_INVALID_ID) - __ip_set_put_byindex(ref); -finish: - __ip_set_put_byindex(index); - return res; + return -EINVAL; +} + +static const struct nla_policy +list_set_adt_policy[IPSET_ATTR_ADT_MAX+1] __read_mostly = { + [IPSET_ATTR_NAME] = { .type = NLA_STRING, + .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING, + .len = IPSET_MAXNAMELEN }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, +}; + +static inline bool +next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id) +{ + const struct set_elem *elem; + + if (i + 1 < map->size) { + elem = list_set_elem(map, i + 1); + return !!(elem->id == id + && !(with_timeout(map->timeout) + && list_set_expired(map, i + 1))); + } + + return 0; } +static inline void +list_elem_add(struct list_set *map, u32 i, ip_set_id_t id) +{ + struct set_elem *e; + + for (; i < map->size; i++) { + e = list_set_elem(map, i); + swap(e->id, id); + if (e->id == IPSET_INVALID_ID) + break; + } +} + +static inline void +list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id, + unsigned long timeout) +{ + struct set_telem *e; + + for (; i < map->size; i++) { + e = (struct set_telem *)list_set_elem(map, i); + swap(e->id, id); + if (e->id == IPSET_INVALID_ID) + break; + swap(e->timeout, timeout); + } +} + static int -setlist_ktest(struct ip_set *set, - const struct sk_buff *skb, - const u_int32_t *flags) +list_set_add(struct list_set *map, u32 i, ip_set_id_t id, + unsigned long timeout) { - struct ip_set_setlist *map = set->data; - int i, res = 0; + struct set_elem *e = list_set_elem(map, i); + + if (i == map->size - 1 && e->id != IPSET_INVALID_ID) + /* Last element replaced: e.g. add new,before,last */ + ip_set_put_byindex(e->id); + if (with_timeout(map->timeout)) + list_elem_tadd(map, i, id, timeout); + else + list_elem_add(map, i, id); - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID - && res == 0; i++) - res = ip_set_testip_kernel(map->index[i], skb, flags); - return res; + return 0; } -static inline int -insert_setlist(struct ip_set_setlist *map, int i, ip_set_id_t index) +static int +list_set_del(struct list_set *map, ip_set_id_t id, u32 i) { - ip_set_id_t tmp; - int j; + struct set_elem *a = list_set_elem(map, i), *b; - DP("i: %u, last %u\n", i, map->index[map->size - 1]); - if (i >= map->size || map->index[map->size - 1] != IP_SET_INVALID_ID) - return -ERANGE; - - for (j = i; j < map->size - && index != IP_SET_INVALID_ID; j++) { - tmp = map->index[j]; - map->index[j] = index; - index = tmp; + ip_set_put_byindex(id); + + for (; i < map->size - 1; i++) { + b = list_set_elem(map, i + 1); + a->id = b->id; + if (with_timeout(map->timeout)) + ((struct set_telem *)a)->timeout = + ((struct set_telem *)b)->timeout; + a = b; + if (a->id == IPSET_INVALID_ID) + break; } + /* Last element */ + a->id = IPSET_INVALID_ID; return 0; } static int -setlist_uadd(struct ip_set *set, const void *data, u_int32_t size) +list_set_uadt(struct ip_set *set, struct nlattr *head, int len, + enum ipset_adt adt, u32 *lineno, u32 flags) { - struct ip_set_setlist *map = set->data; - const struct ip_set_req_setlist *req = data; - ip_set_id_t index, ref = IP_SET_INVALID_ID; - int i, res = -ERANGE; + struct list_set *map = set->data; + struct nlattr *tb[IPSET_ATTR_ADT_MAX]; + bool eexist = flags & IPSET_FLAG_EXIST, + with_timeout = with_timeout(map->timeout); + int before = 0; + u32 timeout = map->timeout; + ip_set_id_t id, refid = IPSET_INVALID_ID; + struct set_elem *elem; struct ip_set *s; - - if (req->before && req->ref[0] == '\0') - return -EINVAL; - - index = __ip_set_get_byname(req->name, &s); - if (index == IP_SET_INVALID_ID) - return -EEXIST; - /* "Loop detection" */ - if (strcmp(s->type->typename, "setlist") == 0) + u32 i; + int ret = 0; + + if (nla_parse(tb, IPSET_ATTR_ADT_MAX, head, len, + list_set_adt_policy)) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_NAME]) { + id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); + if (id == IPSET_INVALID_ID) + return -IPSET_ERR_NAME; + /* "Loop detection" */ + if (s->type->features & IPSET_TYPE_NAME) { + ret = -IPSET_ERR_LOOP; + goto finish; + } + } else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_CADT_FLAGS]) { + u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + before = f & IPSET_FLAG_BEFORE; + } + + if (before && !tb[IPSET_ATTR_NAMEREF]) { + ret = -IPSET_ERR_BEFORE; goto finish; + } - if (req->ref[0] != '\0') { - ref = __ip_set_get_byname(req->ref, &s); - if (ref == IP_SET_INVALID_ID) { - res = -EEXIST; + if (tb[IPSET_ATTR_NAMEREF]) { + refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), &s); + if (refid == IPSET_INVALID_ID) { + ret = -IPSET_ERR_NAMEREF; goto finish; } + if (!before) + before = -1; } - for (i = 0; i < map->size; i++) { - if (map->index[i] != ref) - continue; - if (req->before) - res = insert_setlist(map, i, index); - else - res = insert_setlist(map, - ref == IP_SET_INVALID_ID ? i : i + 1, - index); + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!with_timeout) { + ret = -IPSET_ERR_TIMEOUT; + goto finish; + } + timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); + } + + switch (adt) { + case IPSET_TEST: + for (i = 0; i < map->size && !ret; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID + || (before != 0 && i + 1 >= map->size)) + break; + else if (with_timeout && list_set_expired(map, i)) + continue; + else if (before > 0 && elem->id == id) + ret = next_id_eq(map, i, refid); + else if (before < 0 && elem->id == refid) + ret = next_id_eq(map, i, id); + else if (before == 0 && elem->id == id) + ret = 1; + } + break; + case IPSET_ADD: + for (i = 0; i < map->size && !ret; i++) { + elem = list_set_elem(map, i); + if (elem->id == id + && !(with_timeout && list_set_expired(map, i))) + ret = -IPSET_ERR_EXIST; + } + if (ret == -IPSET_ERR_EXIST) + break; + ret = -IPSET_ERR_LIST_FULL; + for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID) + ret = before != 0 ? -IPSET_ERR_REF_EXIST + : list_set_add(map, i, id, timeout); + else if (elem->id != refid) + continue; + else if (with_timeout && list_set_expired(map, i)) + ret = -IPSET_ERR_REF_EXIST; + else if (before) + ret = list_set_add(map, i, id, timeout); + else if (i + 1 < map->size) + ret = list_set_add(map, i + 1, id, timeout); + } + break; + case IPSET_DEL: + ret = -IPSET_ERR_EXIST; + for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) { + elem = list_set_elem(map, i); + if (elem->id == IPSET_INVALID_ID) { + ret = before != 0 ? -IPSET_ERR_REF_EXIST + : -IPSET_ERR_EXIST; + break; + } else if (with_timeout && list_set_expired(map, i)) + continue; + else if (elem->id == id + && (before == 0 + || (before > 0 && next_id_eq(map, i, refid)))) + ret = list_set_del(map, id, i); + else if (before < 0 && elem->id == refid + && next_id_eq(map, i, id)) + ret = list_set_del(map, id, i + 1); + } + break; + default: break; } - if (ref != IP_SET_INVALID_ID) - __ip_set_put_byindex(ref); - /* In case of success, we keep the reference to the set */ + finish: - if (res != 0) - __ip_set_put_byindex(index); - return res; + if (refid != IPSET_INVALID_ID) + ip_set_put_byindex(refid); + if (adt != IPSET_ADD || ret) + ip_set_put_byindex(id); + + if (ret && !(ret == -IPSET_ERR_EXIST && eexist)) { + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + return ret; + } + return ret; } -static int -setlist_kadd(struct ip_set *set, - const struct sk_buff *skb, - const u_int32_t *flags) +static void +list_set_flush(struct ip_set *set) { - struct ip_set_setlist *map = set->data; - int i, res = -EINVAL; + struct list_set *map = set->data; + struct set_elem *elem; + u32 i; + + for (i = 0; i < map->size; i++) { + elem = list_set_elem(map, i); + if (elem->id != IPSET_INVALID_ID) { + ip_set_put_byindex(elem->id); + elem->id = IPSET_INVALID_ID; + } + } +} + +static void +list_set_destroy(struct ip_set *set) +{ + struct list_set *map = set->data; + + if (with_timeout(map->timeout)) + del_timer_sync(&map->gc); + list_set_flush(set); + kfree(map); - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID - && res != 0; i++) - res = ip_set_addip_kernel(map->index[i], skb, flags); - return res; + set->data = NULL; } -static inline int -unshift_setlist(struct ip_set_setlist *map, int i) +static int +list_set_head(struct ip_set *set, struct sk_buff *skb) { - int j; + const struct list_set *map = set->data; + struct nlattr *nested; + + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) + goto nla_put_failure; + NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size)); + if (with_timeout(map->timeout)) + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout)); + NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES, + htonl(atomic_read(&set->ref) - 1)); + NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE, + htonl(sizeof(*map) + map->size * map->dsize)); + ipset_nest_end(skb, nested); - for (j = i; j < map->size - 1; j++) - map->index[j] = map->index[j+1]; - map->index[map->size-1] = IP_SET_INVALID_ID; return 0; +nla_put_failure: + return -EFAULT; } static int -setlist_udel(struct ip_set *set, const void *data, u_int32_t size) +list_set_list(struct ip_set *set, + struct sk_buff *skb, struct netlink_callback *cb) { - struct ip_set_setlist *map = set->data; - const struct ip_set_req_setlist *req = data; - ip_set_id_t index, ref = IP_SET_INVALID_ID; - int i, res = -EEXIST; - struct ip_set *s; - - if (req->before && req->ref[0] == '\0') - return -EINVAL; - - index = __ip_set_get_byname(req->name, &s); - if (index == IP_SET_INVALID_ID) - return -EEXIST; - if (req->ref[0] != '\0') { - ref = __ip_set_get_byname(req->ref, &s); - if (ref == IP_SET_INVALID_ID) + const struct list_set *map = set->data; + struct nlattr *atd, *nested; + u32 i, first = cb->args[2]; + const struct set_elem *e; + + atd = ipset_nest_start(skb, IPSET_ATTR_ADT); + if (!atd) + return -EFAULT; + for (; cb->args[2] < map->size; cb->args[2]++) { + i = cb->args[2]; + e = list_set_elem(map, i); + if (e->id == IPSET_INVALID_ID) goto finish; - } - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID; i++) { - if (req->before) { - if (map->index[i] == index - && next_index_eq(map, i + 1, ref)) { - res = unshift_setlist(map, i); - break; - } - } else if (ref == IP_SET_INVALID_ID) { - if (map->index[i] == index) { - res = unshift_setlist(map, i); - break; - } - } else if (map->index[i] == ref - && next_index_eq(map, i + 1, index)) { - res = unshift_setlist(map, i + 1); - break; + if (with_timeout(map->timeout) && list_set_expired(map, i)) + continue; + nested = ipset_nest_start(skb, IPSET_ATTR_DATA); + if (!nested) { + if (i == first) { + nla_nest_cancel(skb, atd); + return -EFAULT; + } else + goto nla_put_failure; } + NLA_PUT_STRING(skb, IPSET_ATTR_NAME, + ip_set_name_byindex(e->id)); + if (with_timeout(map->timeout)) { + const struct set_telem *te = + (const struct set_telem *) e; + NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, + htonl(ip_set_timeout_get(te->timeout))); + } + ipset_nest_end(skb, nested); } - if (ref != IP_SET_INVALID_ID) - __ip_set_put_byindex(ref); finish: - __ip_set_put_byindex(index); - /* In case of success, release the reference to the set */ - if (res == 0) - __ip_set_put_byindex(index); - return res; + ipset_nest_end(skb, atd); + /* Set listing finished */ + cb->args[2] = 0; + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nested); + ipset_nest_end(skb, atd); + return 0; } -static int -setlist_kdel(struct ip_set *set, - const struct sk_buff *skb, - const u_int32_t *flags) +static bool +list_set_same_set(const struct ip_set *a, const struct ip_set *b) { - struct ip_set_setlist *map = set->data; - int i, res = -EINVAL; + struct list_set *x = a->data; + struct list_set *y = b->data; - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID - && res != 0; i++) - res = ip_set_delip_kernel(map->index[i], skb, flags); - return res; + return x->size == y->size + && x->timeout == y->timeout; } -static int -setlist_create(struct ip_set *set, const void *data, u_int32_t size) -{ - struct ip_set_setlist *map; - const struct ip_set_req_setlist_create *req = data; - int i; - - map = kmalloc(sizeof(struct ip_set_setlist) + - req->size * sizeof(ip_set_id_t), GFP_KERNEL); - if (!map) - return -ENOMEM; - map->size = req->size; - for (i = 0; i < map->size; i++) - map->index[i] = IP_SET_INVALID_ID; - - set->data = map; - return 0; -} +static const struct ip_set_type_variant list_set __read_mostly = { + .kadt = list_set_kadt, + .uadt = list_set_uadt, + .destroy = list_set_destroy, + .flush = list_set_flush, + .head = list_set_head, + .list = list_set_list, + .same_set = list_set_same_set, +}; static void -setlist_destroy(struct ip_set *set) +list_set_gc(unsigned long ul_set) { - struct ip_set_setlist *map = set->data; - int i; - - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID; i++) - __ip_set_put_byindex(map->index[i]); + struct ip_set *set = (struct ip_set *) ul_set; + struct list_set *map = set->data; + struct set_telem *e; + u32 i; - kfree(map); - set->data = NULL; + /* We run parallel with other readers (test element) + * but adding/deleting new entries is locked out */ + read_lock_bh(&set->lock); + for (i = map->size - 1; i >= 0; i--) { + e = (struct set_telem *) list_set_elem(map, i); + if (e->id != IPSET_INVALID_ID + && list_set_expired(map, i)) + list_set_del(map, e->id, i); + } + read_unlock_bh(&set->lock); + + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + add_timer(&map->gc); } -static void -setlist_flush(struct ip_set *set) +static inline void +list_set_gc_init(struct ip_set *set) { - struct ip_set_setlist *map = set->data; - int i; - - for (i = 0; i < map->size - && map->index[i] != IP_SET_INVALID_ID; i++) { - __ip_set_put_byindex(map->index[i]); - map->index[i] = IP_SET_INVALID_ID; - } + struct list_set *map = set->data; + + init_timer(&map->gc); + map->gc.data = (unsigned long) set; + map->gc.function = list_set_gc; + map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + add_timer(&map->gc); } -static void -setlist_list_header(const struct ip_set *set, void *data) +/* Create list:set type of sets */ + +static const struct nla_policy +list_set_create_policy[IPSET_ATTR_CREATE_MAX+1] __read_mostly = { + [IPSET_ATTR_SIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, +}; + +static inline bool +init_list_set(struct ip_set *set, u32 size, size_t dsize, + unsigned long timeout) { - const struct ip_set_setlist *map = set->data; - struct ip_set_req_setlist_create *header = data; + struct list_set *map; + struct set_elem *e; + u32 i; - header->size = map->size; + map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL); + if (!map) + return false; + + map->size = size; + map->dsize = dsize; + map->timeout = timeout; + set->data = map; + + for (i = 0; i < size; i++) { + e = list_set_elem(map, i); + e->id = IPSET_INVALID_ID; + } + + return true; } static int -setlist_list_members_size(const struct ip_set *set, char dont_align) +list_set_create(struct ip_set *set, struct nlattr *head, int len, + u32 flags) { - const struct ip_set_setlist *map = set->data; - - return map->size * IPSET_VALIGN(sizeof(ip_set_id_t), dont_align); -} + struct nlattr *tb[IPSET_ATTR_CREATE_MAX]; + u32 size = IP_SET_LIST_DEFAULT_SIZE; -static void -setlist_list_members(const struct ip_set *set, void *data, char dont_align) -{ - struct ip_set_setlist *map = set->data; - ip_set_id_t *d; - int i; + if (nla_parse(tb, IPSET_ATTR_CREATE_MAX, head, len, + list_set_create_policy)) + return -IPSET_ERR_PROTOCOL; - for (i = 0; i < map->size; i++) { - d = data + i * IPSET_VALIGN(sizeof(ip_set_id_t), dont_align); - *d = ip_set_id(map->index[i]); + if (tb[IPSET_ATTR_SIZE]) + size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]); + if (size < IP_SET_LIST_MIN_SIZE) + size = IP_SET_LIST_MIN_SIZE; + + if (tb[IPSET_ATTR_TIMEOUT]) { + if (!init_list_set(set, size, sizeof(struct set_telem), + ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]))) + return -ENOMEM; + + list_set_gc_init(set); + } else { + if (!init_list_set(set, size, sizeof(struct set_elem), + IPSET_NO_TIMEOUT)) + return -ENOMEM; } + set->variant = &list_set; + return 0; } -IP_SET_TYPE(setlist, IPSET_TYPE_SETNAME | IPSET_DATA_SINGLE) +static struct ip_set_type list_set_type = { + .name = "list:set", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_NAME, + .dimension = IPSET_DIM_ONE, + .family = AF_UNSPEC, + .revision = 0, + .create = list_set_create, + .me = THIS_MODULE, +}; -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jozsef Kadlecsik "); -MODULE_DESCRIPTION("setlist type of IP sets"); +static int __init +list_set_init(void) +{ + return ip_set_type_register(&list_set_type); +} + +static void __exit +list_set_fini(void) +{ + ip_set_type_unregister(&list_set_type); +} -REGISTER_MODULE(setlist) +module_init(list_set_init); +module_exit(list_set_fini); -- cgit v1.2.3