/* Copyright (C) 2013 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. */ #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 */