summaryrefslogtreecommitdiffstats
path: root/kernel/ip_set_list_set.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/ip_set_list_set.c')
-rw-r--r--kernel/ip_set_list_set.c731
1 files changed, 498 insertions, 233 deletions
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 <kadlec@blackhole.kfki.hu>
+/* Copyright (C) 2008-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
*
* 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 <linux/netfilter/ip_set_kernel.h>
#include <linux/module.h>
#include <linux/ip.h>
#include <linux/skbuff.h>
#include <linux/errno.h>
-#include <linux/netfilter_ipv4/ip_set.h>
-#include <linux/netfilter_ipv4/ip_set_bitmaps.h>
-#include <linux/netfilter_ipv4/ip_set_setlist.h>
+#include <linux/netfilter/ip_set.h>
+#include <linux/netfilter/ip_set_timeout.h>
+#include <linux/netfilter/ip_set_list.h>
-/*
- * before ==> index, ref
- * after ==> ref, index
- */
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
+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 <kadlec@blackhole.kfki.hu>");
-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);