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