netfilter: ipset: Unified bitmap type generation
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Mon, 8 Apr 2013 19:00:52 +0000 (21:00 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 29 Apr 2013 18:08:54 +0000 (20:08 +0200)
Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/linux/netfilter/ipset/ip_set_bitmap.h
net/netfilter/ipset/ip_set_bitmap_gen.h [new file with mode: 0644]

index 1a30646..5e4662a 100644 (file)
@@ -5,6 +5,12 @@
 
 #define IPSET_BITMAP_MAX_RANGE 0x0000FFFF
 
+enum {
+       IPSET_ADD_FAILED = 1,
+       IPSET_ADD_STORE_PLAIN_TIMEOUT,
+       IPSET_ADD_START_STORED_TIMEOUT,
+};
+
 /* Common functions */
 
 static inline u32
diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h
new file mode 100644 (file)
index 0000000..b993159
--- /dev/null
@@ -0,0 +1,265 @@
+/* Copyright (C) 2013 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.
+ */
+
+#ifndef __IP_SET_BITMAP_IP_GEN_H
+#define __IP_SET_BITMAP_IP_GEN_H
+
+#define CONCAT(a, b)           a##b
+#define TOKEN(a,b)             CONCAT(a, b)
+
+#define mtype_do_test          TOKEN(MTYPE, _do_test)
+#define mtype_gc_test          TOKEN(MTYPE, _gc_test)
+#define mtype_is_filled                TOKEN(MTYPE, _is_filled)
+#define mtype_do_add           TOKEN(MTYPE, _do_add)
+#define mtype_do_del           TOKEN(MTYPE, _do_del)
+#define mtype_do_list          TOKEN(MTYPE, _do_list)
+#define mtype_do_head          TOKEN(MTYPE, _do_head)
+#define mtype_adt_elem         TOKEN(MTYPE, _adt_elem)
+#define mtype_add_timeout      TOKEN(MTYPE, _add_timeout)
+#define mtype_gc_init          TOKEN(MTYPE, _gc_init)
+#define mtype_kadt             TOKEN(MTYPE, _kadt)
+#define mtype_uadt             TOKEN(MTYPE, _uadt)
+#define mtype_destroy          TOKEN(MTYPE, _destroy)
+#define mtype_flush            TOKEN(MTYPE, _flush)
+#define mtype_head             TOKEN(MTYPE, _head)
+#define mtype_same_set         TOKEN(MTYPE, _same_set)
+#define mtype_elem             TOKEN(MTYPE, _elem)
+#define mtype_test             TOKEN(MTYPE, _test)
+#define mtype_add              TOKEN(MTYPE, _add)
+#define mtype_del              TOKEN(MTYPE, _del)
+#define mtype_list             TOKEN(MTYPE, _list)
+#define mtype_gc               TOKEN(MTYPE, _gc)
+#define mtype                  MTYPE
+
+#define ext_timeout(e, m)      \
+       (unsigned long *)((e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
+#define get_ext(map, id)       ((map)->extensions + (map)->dsize * (id))
+
+static void
+mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
+{
+       struct mtype *map = set->data;
+
+       init_timer(&map->gc);
+       map->gc.data = (unsigned long) set;
+       map->gc.function = gc;
+       map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+       add_timer(&map->gc);
+}
+
+static void
+mtype_destroy(struct ip_set *set)
+{
+       struct mtype *map = set->data;
+
+       if (SET_WITH_TIMEOUT(set))
+               del_timer_sync(&map->gc);
+
+       ip_set_free(map->members);
+       if (map->dsize)
+               ip_set_free(map->extensions);
+       kfree(map);
+
+       set->data = NULL;
+}
+
+static void
+mtype_flush(struct ip_set *set)
+{
+       struct mtype *map = set->data;
+
+       memset(map->members, 0, map->memsize);
+}
+
+static int
+mtype_head(struct ip_set *set, struct sk_buff *skb)
+{
+       const struct mtype *map = set->data;
+       struct nlattr *nested;
+
+       nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+       if (!nested)
+               goto nla_put_failure;
+       if (mtype_do_head(skb, map) ||
+           nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
+           nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
+                         htonl(sizeof(*map) +
+                               map->memsize +
+                               map->dsize * map->elements)) ||
+           (SET_WITH_TIMEOUT(set) &&
+            nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))))
+               goto nla_put_failure;
+       ipset_nest_end(skb, nested);
+
+       return 0;
+nla_put_failure:
+       return -EMSGSIZE;
+}
+
+static int
+mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+          struct ip_set_ext *mext, u32 flags)
+{
+       struct mtype *map = set->data;
+       const struct mtype_adt_elem *e = value;
+       void *x = get_ext(map, e->id);
+       int ret = mtype_do_test(e, map);
+
+       if (ret <= 0)
+               return ret;
+       if (SET_WITH_TIMEOUT(set) &&
+           ip_set_timeout_expired(ext_timeout(x, map)))
+               return 0;
+       return 1;
+}
+
+static int
+mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+         struct ip_set_ext *mext, u32 flags)
+{
+       struct mtype *map = set->data;
+       const struct mtype_adt_elem *e = value;
+       void *x = get_ext(map, e->id);
+       int ret = mtype_do_add(e, map, flags);
+
+       if (ret == IPSET_ADD_FAILED) {
+               if (SET_WITH_TIMEOUT(set) &&
+                   ip_set_timeout_expired(ext_timeout(x, map)))
+                       ret = 0;
+               else if (!(flags & IPSET_FLAG_EXIST))
+                       return -IPSET_ERR_EXIST;
+       }
+
+       if (SET_WITH_TIMEOUT(set))
+#ifdef IP_SET_BITMAP_STORED_TIMEOUT
+               mtype_add_timeout(ext_timeout(x, map), e, ext, map, ret);
+#else
+               ip_set_timeout_set(ext_timeout(x, map), ext->timeout);
+#endif
+
+       return 0;
+}
+
+static int
+mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
+         struct ip_set_ext *mext, u32 flags)
+{
+       struct mtype *map = set->data;
+       const struct mtype_adt_elem *e = value;
+       const void *x = get_ext(map, e->id);
+
+       if (mtype_do_del(e, map) ||
+           (SET_WITH_TIMEOUT(set) &&
+            ip_set_timeout_expired(ext_timeout(x, map))))
+               return -IPSET_ERR_EXIST;
+
+       return 0;
+}
+
+static int
+mtype_list(const struct ip_set *set,
+          struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct mtype *map = set->data;
+       struct nlattr *adt, *nested;
+       void *x;
+       u32 id, first = cb->args[2];
+
+       adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
+       if (!adt)
+               return -EMSGSIZE;
+       for (; cb->args[2] < map->elements; cb->args[2]++) {
+               id = cb->args[2];
+               x = get_ext(map, id);
+               if (!test_bit(id, map->members) ||
+                   (SET_WITH_TIMEOUT(set) &&
+#ifdef IP_SET_BITMAP_STORED_TIMEOUT
+                    mtype_is_filled((const struct mtype_elem *) x) &&
+#endif
+                    ip_set_timeout_expired(ext_timeout(x, map))))
+                       continue;
+               nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
+               if (!nested) {
+                       if (id == first) {
+                               nla_nest_cancel(skb, adt);
+                               return -EMSGSIZE;
+                       } else
+                               goto nla_put_failure;
+               }
+               if (mtype_do_list(skb, map, id))
+                       goto nla_put_failure;
+               if (SET_WITH_TIMEOUT(set)) {
+#ifdef IP_SET_BITMAP_STORED_TIMEOUT
+                       if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+                                         htonl(ip_set_timeout_stored(map, id,
+                                                       ext_timeout(x, map)))))
+                               goto nla_put_failure;
+#else
+                       if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
+                                         htonl(ip_set_timeout_get(
+                                                       ext_timeout(x, map)))))
+                               goto nla_put_failure;
+#endif
+               }
+               ipset_nest_end(skb, nested);
+       }
+       ipset_nest_end(skb, adt);
+
+       /* Set listing finished */
+       cb->args[2] = 0;
+
+       return 0;
+
+nla_put_failure:
+       nla_nest_cancel(skb, nested);
+       ipset_nest_end(skb, adt);
+       if (unlikely(id == first)) {
+               cb->args[2] = 0;
+               return -EMSGSIZE;
+       }
+       return 0;
+}
+
+static void
+mtype_gc(unsigned long ul_set)
+{
+       struct ip_set *set = (struct ip_set *) ul_set;
+       struct mtype *map = set->data;
+       const void *x;
+       u32 id;
+
+       /* We run parallel with other readers (test element)
+        * but adding/deleting new entries is locked out */
+       read_lock_bh(&set->lock);
+       for (id = 0; id < map->elements; id++)
+               if (mtype_gc_test(id, map)) {
+                       x = get_ext(map, id);
+                       if (ip_set_timeout_expired(ext_timeout(x, map)))
+                               clear_bit(id, map->members);
+               }
+       read_unlock_bh(&set->lock);
+
+       map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
+       add_timer(&map->gc);
+}
+
+static const struct ip_set_type_variant mtype = {
+       .kadt   = mtype_kadt,
+       .uadt   = mtype_uadt,
+       .adt    = {
+               [IPSET_ADD] = mtype_add,
+               [IPSET_DEL] = mtype_del,
+               [IPSET_TEST] = mtype_test,
+       },
+       .destroy = mtype_destroy,
+       .flush  = mtype_flush,
+       .head   = mtype_head,
+       .list   = mtype_list,
+       .same_set = mtype_same_set,
+};
+
+#endif /* __IP_SET_BITMAP_IP_GEN_H */