1/* Copyright (C) 2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 as
5 * published by the Free Software Foundation.
6 */
7
8#ifndef __IP_SET_BITMAP_IP_GEN_H
9#define __IP_SET_BITMAP_IP_GEN_H
10
11#define CONCAT(a, b)		a##b
12#define TOKEN(a,b)		CONCAT(a, b)
13
14#define mtype_do_test		TOKEN(MTYPE, _do_test)
15#define mtype_gc_test		TOKEN(MTYPE, _gc_test)
16#define mtype_is_filled		TOKEN(MTYPE, _is_filled)
17#define mtype_do_add		TOKEN(MTYPE, _do_add)
18#define mtype_do_del		TOKEN(MTYPE, _do_del)
19#define mtype_do_list		TOKEN(MTYPE, _do_list)
20#define mtype_do_head		TOKEN(MTYPE, _do_head)
21#define mtype_adt_elem		TOKEN(MTYPE, _adt_elem)
22#define mtype_add_timeout	TOKEN(MTYPE, _add_timeout)
23#define mtype_gc_init		TOKEN(MTYPE, _gc_init)
24#define mtype_kadt		TOKEN(MTYPE, _kadt)
25#define mtype_uadt		TOKEN(MTYPE, _uadt)
26#define mtype_destroy		TOKEN(MTYPE, _destroy)
27#define mtype_flush		TOKEN(MTYPE, _flush)
28#define mtype_head		TOKEN(MTYPE, _head)
29#define mtype_same_set		TOKEN(MTYPE, _same_set)
30#define mtype_elem		TOKEN(MTYPE, _elem)
31#define mtype_test		TOKEN(MTYPE, _test)
32#define mtype_add		TOKEN(MTYPE, _add)
33#define mtype_del		TOKEN(MTYPE, _del)
34#define mtype_list		TOKEN(MTYPE, _list)
35#define mtype_gc		TOKEN(MTYPE, _gc)
36#define mtype			MTYPE
37
38#define ext_timeout(e, m)	\
39	(unsigned long *)((e) + (m)->offset[IPSET_OFFSET_TIMEOUT])
40#define ext_counter(e, m)	\
41	(struct ip_set_counter *)((e) + (m)->offset[IPSET_OFFSET_COUNTER])
42#define get_ext(map, id)	((map)->extensions + (map)->dsize * (id))
43
44static void
45mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set))
46{
47	struct mtype *map = set->data;
48
49	init_timer(&map->gc);
50	map->gc.data = (unsigned long) set;
51	map->gc.function = gc;
52	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
53	add_timer(&map->gc);
54}
55
56static void
57mtype_destroy(struct ip_set *set)
58{
59	struct mtype *map = set->data;
60
61	if (SET_WITH_TIMEOUT(set))
62		del_timer_sync(&map->gc);
63
64	ip_set_free(map->members);
65	if (map->dsize)
66		ip_set_free(map->extensions);
67	kfree(map);
68
69	set->data = NULL;
70}
71
72static void
73mtype_flush(struct ip_set *set)
74{
75	struct mtype *map = set->data;
76
77	memset(map->members, 0, map->memsize);
78}
79
80static int
81mtype_head(struct ip_set *set, struct sk_buff *skb)
82{
83	const struct mtype *map = set->data;
84	struct nlattr *nested;
85
86	nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
87	if (!nested)
88		goto nla_put_failure;
89	if (mtype_do_head(skb, map) ||
90	    nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
91	    nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
92			  htonl(sizeof(*map) +
93				map->memsize +
94				map->dsize * map->elements)) ||
95	    (SET_WITH_TIMEOUT(set) &&
96	     nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
97	    (SET_WITH_COUNTER(set) &&
98	     nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS,
99			   htonl(IPSET_FLAG_WITH_COUNTERS))))
100		goto nla_put_failure;
101	ipset_nest_end(skb, nested);
102
103	return 0;
104nla_put_failure:
105	return -EMSGSIZE;
106}
107
108static int
109mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
110	   struct ip_set_ext *mext, u32 flags)
111{
112	struct mtype *map = set->data;
113	const struct mtype_adt_elem *e = value;
114	void *x = get_ext(map, e->id);
115	int ret = mtype_do_test(e, map);
116
117	if (ret <= 0)
118		return ret;
119	if (SET_WITH_TIMEOUT(set) &&
120	    ip_set_timeout_expired(ext_timeout(x, map)))
121		return 0;
122	if (SET_WITH_COUNTER(set))
123		ip_set_update_counter(ext_counter(x, map), ext, mext, flags);
124	return 1;
125}
126
127static int
128mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
129	  struct ip_set_ext *mext, u32 flags)
130{
131	struct mtype *map = set->data;
132	const struct mtype_adt_elem *e = value;
133	void *x = get_ext(map, e->id);
134	int ret = mtype_do_add(e, map, flags);
135
136	if (ret == IPSET_ADD_FAILED) {
137		if (SET_WITH_TIMEOUT(set) &&
138		    ip_set_timeout_expired(ext_timeout(x, map)))
139			ret = 0;
140		else if (!(flags & IPSET_FLAG_EXIST))
141			return -IPSET_ERR_EXIST;
142	}
143
144	if (SET_WITH_TIMEOUT(set))
145#ifdef IP_SET_BITMAP_STORED_TIMEOUT
146		mtype_add_timeout(ext_timeout(x, map), e, ext, map, ret);
147#else
148		ip_set_timeout_set(ext_timeout(x, map), ext->timeout);
149#endif
150
151	if (SET_WITH_COUNTER(set))
152		ip_set_init_counter(ext_counter(x, map), ext);
153	return 0;
154}
155
156static int
157mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
158	  struct ip_set_ext *mext, u32 flags)
159{
160	struct mtype *map = set->data;
161	const struct mtype_adt_elem *e = value;
162	const void *x = get_ext(map, e->id);
163
164	if (mtype_do_del(e, map) ||
165	    (SET_WITH_TIMEOUT(set) &&
166	     ip_set_timeout_expired(ext_timeout(x, map))))
167		return -IPSET_ERR_EXIST;
168
169	return 0;
170}
171
172static int
173mtype_list(const struct ip_set *set,
174	   struct sk_buff *skb, struct netlink_callback *cb)
175{
176	struct mtype *map = set->data;
177	struct nlattr *adt, *nested;
178	void *x;
179	u32 id, first = cb->args[2];
180
181	adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
182	if (!adt)
183		return -EMSGSIZE;
184	for (; cb->args[2] < map->elements; cb->args[2]++) {
185		id = cb->args[2];
186		x = get_ext(map, id);
187		if (!test_bit(id, map->members) ||
188		    (SET_WITH_TIMEOUT(set) &&
189#ifdef IP_SET_BITMAP_STORED_TIMEOUT
190		     mtype_is_filled((const struct mtype_elem *) x) &&
191#endif
192		     ip_set_timeout_expired(ext_timeout(x, map))))
193			continue;
194		nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
195		if (!nested) {
196			if (id == first) {
197				nla_nest_cancel(skb, adt);
198				return -EMSGSIZE;
199			} else
200				goto nla_put_failure;
201		}
202		if (mtype_do_list(skb, map, id))
203			goto nla_put_failure;
204		if (SET_WITH_TIMEOUT(set)) {
205#ifdef IP_SET_BITMAP_STORED_TIMEOUT
206			if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
207					  htonl(ip_set_timeout_stored(map, id,
208							ext_timeout(x, map)))))
209				goto nla_put_failure;
210#else
211			if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT,
212					  htonl(ip_set_timeout_get(
213							ext_timeout(x, map)))))
214				goto nla_put_failure;
215#endif
216		}
217		if (SET_WITH_COUNTER(set) &&
218		    ip_set_put_counter(skb, ext_counter(x, map)))
219			goto nla_put_failure;
220		ipset_nest_end(skb, nested);
221	}
222	ipset_nest_end(skb, adt);
223
224	/* Set listing finished */
225	cb->args[2] = 0;
226
227	return 0;
228
229nla_put_failure:
230	nla_nest_cancel(skb, nested);
231	ipset_nest_end(skb, adt);
232	if (unlikely(id == first)) {
233		cb->args[2] = 0;
234		return -EMSGSIZE;
235	}
236	return 0;
237}
238
239static void
240mtype_gc(unsigned long ul_set)
241{
242	struct ip_set *set = (struct ip_set *) ul_set;
243	struct mtype *map = set->data;
244	const void *x;
245	u32 id;
246
247	/* We run parallel with other readers (test element)
248	 * but adding/deleting new entries is locked out */
249	read_lock_bh(&set->lock);
250	for (id = 0; id < map->elements; id++)
251		if (mtype_gc_test(id, map)) {
252			x = get_ext(map, id);
253			if (ip_set_timeout_expired(ext_timeout(x, map)))
254				clear_bit(id, map->members);
255		}
256	read_unlock_bh(&set->lock);
257
258	map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
259	add_timer(&map->gc);
260}
261
262static const struct ip_set_type_variant mtype = {
263	.kadt	= mtype_kadt,
264	.uadt	= mtype_uadt,
265	.adt	= {
266		[IPSET_ADD] = mtype_add,
267		[IPSET_DEL] = mtype_del,
268		[IPSET_TEST] = mtype_test,
269	},
270	.destroy = mtype_destroy,
271	.flush	= mtype_flush,
272	.head	= mtype_head,
273	.list	= mtype_list,
274	.same_set = mtype_same_set,
275};
276
277#endif /* __IP_SET_BITMAP_IP_GEN_H */
278