|
@@ -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 */
|