]> git.ipfire.org Git - thirdparty/ipset.git/blame - kernel/net/netfilter/ipset/ip_set_bitmap_gen.h
netfilter: ipset: fix race condition between swap/destroy and kernel side add/del...
[thirdparty/ipset.git] / kernel / net / netfilter / ipset / ip_set_bitmap_gen.h
CommitLineData
6cd9def1 1/* SPDX-License-Identifier: GPL-2.0-only */
0fdebb3b 2/* Copyright (C) 2013 Jozsef Kadlecsik <kadlec@netfilter.org>
e2f4b810
JK
3 */
4
5#ifndef __IP_SET_BITMAP_IP_GEN_H
6#define __IP_SET_BITMAP_IP_GEN_H
7
76e1fadb
JK
8#define mtype_do_test IPSET_TOKEN(MTYPE, _do_test)
9#define mtype_gc_test IPSET_TOKEN(MTYPE, _gc_test)
10#define mtype_is_filled IPSET_TOKEN(MTYPE, _is_filled)
11#define mtype_do_add IPSET_TOKEN(MTYPE, _do_add)
92f6f53b 12#define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup)
76e1fadb
JK
13#define mtype_do_del IPSET_TOKEN(MTYPE, _do_del)
14#define mtype_do_list IPSET_TOKEN(MTYPE, _do_list)
15#define mtype_do_head IPSET_TOKEN(MTYPE, _do_head)
16#define mtype_adt_elem IPSET_TOKEN(MTYPE, _adt_elem)
17#define mtype_add_timeout IPSET_TOKEN(MTYPE, _add_timeout)
18#define mtype_gc_init IPSET_TOKEN(MTYPE, _gc_init)
19#define mtype_kadt IPSET_TOKEN(MTYPE, _kadt)
20#define mtype_uadt IPSET_TOKEN(MTYPE, _uadt)
21#define mtype_destroy IPSET_TOKEN(MTYPE, _destroy)
d897885a 22#define mtype_memsize IPSET_TOKEN(MTYPE, _memsize)
76e1fadb
JK
23#define mtype_flush IPSET_TOKEN(MTYPE, _flush)
24#define mtype_head IPSET_TOKEN(MTYPE, _head)
25#define mtype_same_set IPSET_TOKEN(MTYPE, _same_set)
26#define mtype_elem IPSET_TOKEN(MTYPE, _elem)
27#define mtype_test IPSET_TOKEN(MTYPE, _test)
28#define mtype_add IPSET_TOKEN(MTYPE, _add)
29#define mtype_del IPSET_TOKEN(MTYPE, _del)
30#define mtype_list IPSET_TOKEN(MTYPE, _list)
31#define mtype_gc IPSET_TOKEN(MTYPE, _gc)
148fad4d 32#define mtype_cancel_gc IPSET_TOKEN(MTYPE, _cancel_gc)
e2f4b810
JK
33#define mtype MTYPE
34
c7cf6f3b 35#define get_ext(set, map, id) ((map)->extensions + ((set)->dsize * (id)))
e2f4b810
JK
36
37static void
0a81ab50 38mtype_gc_init(struct ip_set *set, void (*gc)(GC_ARG))
e2f4b810
JK
39{
40 struct mtype *map = set->data;
41
0a81ab50 42 TIMER_SETUP(&map->gc, gc);
8f6237f6 43 mod_timer(&map->gc, jiffies + IPSET_GC_PERIOD(set->timeout) * HZ);
e2f4b810
JK
44}
45
92f6f53b
JK
46static void
47mtype_ext_cleanup(struct ip_set *set)
48{
49 struct mtype *map = set->data;
50 u32 id;
51
52 for (id = 0; id < map->elements; id++)
53 if (test_bit(id, map->members))
54 ip_set_ext_destroy(set, get_ext(set, map, id));
55}
56
e2f4b810
JK
57static void
58mtype_destroy(struct ip_set *set)
59{
60 struct mtype *map = set->data;
61
c7cf6f3b
JK
62 if (set->dsize && set->extensions & IPSET_EXT_DESTROY)
63 mtype_ext_cleanup(set);
d5bfa437 64 ip_set_free(map->members);
c7cf6f3b 65 ip_set_free(map);
e2f4b810
JK
66
67 set->data = NULL;
68}
69
70static void
71mtype_flush(struct ip_set *set)
72{
73 struct mtype *map = set->data;
74
92f6f53b
JK
75 if (set->extensions & IPSET_EXT_DESTROY)
76 mtype_ext_cleanup(set);
b661f385 77 bitmap_zero(map->members, map->elements);
0c7e18ed 78 set->elements = 0;
5a568889 79 set->ext_size = 0;
e2f4b810
JK
80}
81
d897885a
JK
82/* Calculate the actual memory size of the set data */
83static size_t
b8251a4a 84mtype_memsize(const struct mtype *map, size_t dsize)
d897885a 85{
b8251a4a
JK
86 size_t memsize = sizeof(*map) +
87 map->memsize +
88 map->elements * dsize;
89 return memsize;
d897885a
JK
90}
91
e2f4b810
JK
92static int
93mtype_head(struct ip_set *set, struct sk_buff *skb)
94{
95 const struct mtype *map = set->data;
96 struct nlattr *nested;
b8251a4a 97 size_t memsize = mtype_memsize(map, set->dsize) + set->ext_size;
e2f4b810
JK
98
99 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
100 if (!nested)
101 goto nla_put_failure;
102 if (mtype_do_head(skb, map) ||
4c70c227 103 nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) ||
0c7e18ed
JK
104 nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) ||
105 nla_put_net32(skb, IPSET_ATTR_ELEMENTS, htonl(set->elements)))
9af0e20d
OS
106 goto nla_put_failure;
107 if (unlikely(ip_set_put_flags(skb, set)))
e2f4b810
JK
108 goto nla_put_failure;
109 ipset_nest_end(skb, nested);
110
111 return 0;
112nla_put_failure:
113 return -EMSGSIZE;
114}
115
116static int
117mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
118 struct ip_set_ext *mext, u32 flags)
119{
120 struct mtype *map = set->data;
121 const struct mtype_adt_elem *e = value;
71b092cb
JK
122 void *x = get_ext(set, map, e->id);
123 int ret = mtype_do_test(e, map, set->dsize);
e2f4b810
JK
124
125 if (ret <= 0)
126 return ret;
ad92ed77 127 return ip_set_match_extensions(set, ext, mext, flags, x);
e2f4b810
JK
128}
129
130static int
131mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
132 struct ip_set_ext *mext, u32 flags)
133{
134 struct mtype *map = set->data;
135 const struct mtype_adt_elem *e = value;
71b092cb
JK
136 void *x = get_ext(set, map, e->id);
137 int ret = mtype_do_add(e, map, flags, set->dsize);
e2f4b810
JK
138
139 if (ret == IPSET_ADD_FAILED) {
140 if (SET_WITH_TIMEOUT(set) &&
e91b76ec 141 ip_set_timeout_expired(ext_timeout(x, set))) {
0c7e18ed 142 set->elements--;
e2f4b810 143 ret = 0;
e91b76ec
JK
144 } else if (!(flags & IPSET_FLAG_EXIST)) {
145 set_bit(e->id, map->members);
e2f4b810 146 return -IPSET_ERR_EXIST;
e91b76ec 147 }
92f6f53b
JK
148 /* Element is re-added, cleanup extensions */
149 ip_set_ext_destroy(set, x);
e2f4b810 150 }
0c7e18ed
JK
151 if (ret > 0)
152 set->elements--;
e2f4b810
JK
153
154 if (SET_WITH_TIMEOUT(set))
155#ifdef IP_SET_BITMAP_STORED_TIMEOUT
71b092cb 156 mtype_add_timeout(ext_timeout(x, set), e, ext, set, map, ret);
e2f4b810 157#else
71b092cb 158 ip_set_timeout_set(ext_timeout(x, set), ext->timeout);
e2f4b810
JK
159#endif
160
9272b57d 161 if (SET_WITH_COUNTER(set))
71b092cb 162 ip_set_init_counter(ext_counter(x, set), ext);
9af0e20d 163 if (SET_WITH_COMMENT(set))
5a568889 164 ip_set_init_comment(set, ext_comment(x, set), ext);
b9653a47
AD
165 if (SET_WITH_SKBINFO(set))
166 ip_set_init_skbinfo(ext_skbinfo(x, set), ext);
e91b76ec
JK
167
168 /* Activate element */
169 set_bit(e->id, map->members);
0c7e18ed 170 set->elements++;
e91b76ec 171
e2f4b810
JK
172 return 0;
173}
174
175static int
176mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
177 struct ip_set_ext *mext, u32 flags)
178{
179 struct mtype *map = set->data;
180 const struct mtype_adt_elem *e = value;
92f6f53b 181 void *x = get_ext(set, map, e->id);
e2f4b810 182
92f6f53b
JK
183 if (mtype_do_del(e, map))
184 return -IPSET_ERR_EXIST;
185
186 ip_set_ext_destroy(set, x);
0c7e18ed 187 set->elements--;
92f6f53b
JK
188 if (SET_WITH_TIMEOUT(set) &&
189 ip_set_timeout_expired(ext_timeout(x, set)))
e2f4b810
JK
190 return -IPSET_ERR_EXIST;
191
192 return 0;
193}
194
def57acb 195#ifndef IP_SET_BITMAP_STORED_TIMEOUT
4392230c 196static bool
def57acb
JK
197mtype_is_filled(const struct mtype_elem *x)
198{
199 return true;
200}
201#endif
202
e2f4b810
JK
203static int
204mtype_list(const struct ip_set *set,
205 struct sk_buff *skb, struct netlink_callback *cb)
206{
207 struct mtype *map = set->data;
208 struct nlattr *adt, *nested;
209 void *x;
a61d7ebe 210 u32 id, first = cb->args[IPSET_CB_ARG0];
aa458c2d 211 int ret = 0;
e2f4b810
JK
212
213 adt = ipset_nest_start(skb, IPSET_ATTR_ADT);
214 if (!adt)
215 return -EMSGSIZE;
aa458c2d
JK
216 /* Extensions may be replaced */
217 rcu_read_lock();
a61d7ebe
JK
218 for (; cb->args[IPSET_CB_ARG0] < map->elements;
219 cb->args[IPSET_CB_ARG0]++) {
56004fb7 220 cond_resched_rcu();
a61d7ebe 221 id = cb->args[IPSET_CB_ARG0];
71b092cb 222 x = get_ext(set, map, id);
e2f4b810
JK
223 if (!test_bit(id, map->members) ||
224 (SET_WITH_TIMEOUT(set) &&
225#ifdef IP_SET_BITMAP_STORED_TIMEOUT
c4c74ae6 226 mtype_is_filled(x) &&
e2f4b810 227#endif
68ade830 228 ip_set_timeout_expired(ext_timeout(x, set))))
e2f4b810
JK
229 continue;
230 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
231 if (!nested) {
232 if (id == first) {
233 nla_nest_cancel(skb, adt);
aa458c2d
JK
234 ret = -EMSGSIZE;
235 goto out;
9f497f7e
JK
236 }
237
238 goto nla_put_failure;
e2f4b810 239 }
71b092cb 240 if (mtype_do_list(skb, map, id, set->dsize))
e2f4b810 241 goto nla_put_failure;
cb410bbb 242 if (ip_set_put_extensions(skb, set, x, mtype_is_filled(x)))
9af0e20d 243 goto nla_put_failure;
e2f4b810
JK
244 ipset_nest_end(skb, nested);
245 }
246 ipset_nest_end(skb, adt);
247
248 /* Set listing finished */
a61d7ebe 249 cb->args[IPSET_CB_ARG0] = 0;
e2f4b810 250
aa458c2d 251 goto out;
e2f4b810
JK
252
253nla_put_failure:
254 nla_nest_cancel(skb, nested);
e2f4b810 255 if (unlikely(id == first)) {
a61d7ebe 256 cb->args[IPSET_CB_ARG0] = 0;
aa458c2d 257 ret = -EMSGSIZE;
e2f4b810 258 }
5ebed3ba 259 ipset_nest_end(skb, adt);
aa458c2d
JK
260out:
261 rcu_read_unlock();
262 return ret;
e2f4b810
JK
263}
264
265static void
0a81ab50 266mtype_gc(GC_ARG)
e2f4b810 267{
0a81ab50 268 INIT_GC_VARS(mtype, map);
92f6f53b 269 void *x;
e2f4b810
JK
270 u32 id;
271
272 /* We run parallel with other readers (test element)
9f497f7e
JK
273 * but adding/deleting new entries is locked out
274 */
920ddfa0 275 spin_lock_bh(&set->lock);
e2f4b810 276 for (id = 0; id < map->elements; id++)
71b092cb
JK
277 if (mtype_gc_test(id, map, set->dsize)) {
278 x = get_ext(set, map, id);
92f6f53b 279 if (ip_set_timeout_expired(ext_timeout(x, set))) {
e2f4b810 280 clear_bit(id, map->members);
92f6f53b 281 ip_set_ext_destroy(set, x);
0c7e18ed 282 set->elements--;
92f6f53b 283 }
e2f4b810 284 }
920ddfa0 285 spin_unlock_bh(&set->lock);
e2f4b810 286
71b092cb 287 map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
e2f4b810
JK
288 add_timer(&map->gc);
289}
290
148fad4d
JK
291static void
292mtype_cancel_gc(struct ip_set *set)
293{
294 struct mtype *map = set->data;
295
296 if (SET_WITH_TIMEOUT(set))
297 del_timer_sync(&map->gc);
298}
299
e2f4b810
JK
300static const struct ip_set_type_variant mtype = {
301 .kadt = mtype_kadt,
302 .uadt = mtype_uadt,
303 .adt = {
304 [IPSET_ADD] = mtype_add,
305 [IPSET_DEL] = mtype_del,
306 [IPSET_TEST] = mtype_test,
307 },
308 .destroy = mtype_destroy,
309 .flush = mtype_flush,
310 .head = mtype_head,
311 .list = mtype_list,
312 .same_set = mtype_same_set,
148fad4d 313 .cancel_gc = mtype_cancel_gc,
e2f4b810
JK
314};
315
316#endif /* __IP_SET_BITMAP_IP_GEN_H */