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