diff options
author | Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 2013-04-04 06:21:02 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-04-29 14:08:58 -0400 |
commit | 7d47d972b5d154e143bb24a795af92bbb3c95532 (patch) | |
tree | 139f7fff8cc9cbfd1cad78fab2858516eb1c7c17 /net | |
parent | 5d50e1d88336a9334348a338731c6a7bc4823d08 (diff) |
netfilter: ipset: list:set type using the extension interface
Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/ipset/ip_set_list_set.c | 547 |
1 files changed, 301 insertions, 246 deletions
diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 09c744aa8982..919eefe713d5 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* Copyright (C) 2008-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | 1 | /* Copyright (C) 2008-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> |
2 | * | 2 | * |
3 | * This program is free software; you can redistribute it and/or modify | 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 | 4 | * it under the terms of the GNU General Public License version 2 as |
@@ -13,7 +13,6 @@ | |||
13 | #include <linux/errno.h> | 13 | #include <linux/errno.h> |
14 | 14 | ||
15 | #include <linux/netfilter/ipset/ip_set.h> | 15 | #include <linux/netfilter/ipset/ip_set.h> |
16 | #include <linux/netfilter/ipset/ip_set_timeout.h> | ||
17 | #include <linux/netfilter/ipset/ip_set_list.h> | 16 | #include <linux/netfilter/ipset/ip_set_list.h> |
18 | 17 | ||
19 | #define REVISION_MIN 0 | 18 | #define REVISION_MIN 0 |
@@ -24,19 +23,28 @@ MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); | |||
24 | IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX); | 23 | IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX); |
25 | MODULE_ALIAS("ip_set_list:set"); | 24 | MODULE_ALIAS("ip_set_list:set"); |
26 | 25 | ||
27 | /* Member elements without and with timeout */ | 26 | /* Member elements */ |
28 | struct set_elem { | 27 | struct set_elem { |
29 | ip_set_id_t id; | 28 | ip_set_id_t id; |
30 | }; | 29 | }; |
31 | 30 | ||
32 | struct set_telem { | 31 | struct sett_elem { |
33 | ip_set_id_t id; | 32 | struct { |
33 | ip_set_id_t id; | ||
34 | } __attribute__ ((aligned)); | ||
34 | unsigned long timeout; | 35 | unsigned long timeout; |
35 | }; | 36 | }; |
36 | 37 | ||
38 | struct set_adt_elem { | ||
39 | ip_set_id_t id; | ||
40 | ip_set_id_t refid; | ||
41 | int before; | ||
42 | }; | ||
43 | |||
37 | /* Type structure */ | 44 | /* Type structure */ |
38 | struct list_set { | 45 | struct list_set { |
39 | size_t dsize; /* element size */ | 46 | size_t dsize; /* element size */ |
47 | size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ | ||
40 | u32 size; /* size of set list array */ | 48 | u32 size; /* size of set list array */ |
41 | u32 timeout; /* timeout value */ | 49 | u32 timeout; /* timeout value */ |
42 | struct timer_list gc; /* garbage collection */ | 50 | struct timer_list gc; /* garbage collection */ |
@@ -49,179 +57,296 @@ list_set_elem(const struct list_set *map, u32 id) | |||
49 | return (struct set_elem *)((void *)map->members + id * map->dsize); | 57 | return (struct set_elem *)((void *)map->members + id * map->dsize); |
50 | } | 58 | } |
51 | 59 | ||
52 | static inline struct set_telem * | 60 | #define ext_timeout(e, m) \ |
53 | list_set_telem(const struct list_set *map, u32 id) | 61 | (unsigned long *)((void *)(e) + (m)->offset[IPSET_OFFSET_TIMEOUT]) |
54 | { | ||
55 | return (struct set_telem *)((void *)map->members + id * map->dsize); | ||
56 | } | ||
57 | 62 | ||
58 | static inline bool | 63 | static int |
59 | list_set_timeout(const struct list_set *map, u32 id) | 64 | list_set_ktest(struct ip_set *set, const struct sk_buff *skb, |
65 | const struct xt_action_param *par, | ||
66 | struct ip_set_adt_opt *opt, const struct ip_set_ext *ext) | ||
60 | { | 67 | { |
61 | const struct set_telem *elem = list_set_telem(map, id); | 68 | struct list_set *map = set->data; |
69 | struct set_elem *e; | ||
70 | u32 i; | ||
71 | int ret; | ||
62 | 72 | ||
63 | return ip_set_timeout_test(elem->timeout); | 73 | for (i = 0; i < map->size; i++) { |
74 | e = list_set_elem(map, i); | ||
75 | if (e->id == IPSET_INVALID_ID) | ||
76 | return 0; | ||
77 | if (SET_WITH_TIMEOUT(set) && | ||
78 | ip_set_timeout_expired(ext_timeout(e, map))) | ||
79 | continue; | ||
80 | ret = ip_set_test(e->id, skb, par, opt); | ||
81 | if (ret > 0) | ||
82 | return ret; | ||
83 | } | ||
84 | return 0; | ||
64 | } | 85 | } |
65 | 86 | ||
66 | static inline bool | 87 | static int |
67 | list_set_expired(const struct list_set *map, u32 id) | 88 | list_set_kadd(struct ip_set *set, const struct sk_buff *skb, |
89 | const struct xt_action_param *par, | ||
90 | struct ip_set_adt_opt *opt, const struct ip_set_ext *ext) | ||
68 | { | 91 | { |
69 | const struct set_telem *elem = list_set_telem(map, id); | 92 | struct list_set *map = set->data; |
93 | struct set_elem *e; | ||
94 | u32 i; | ||
95 | int ret; | ||
70 | 96 | ||
71 | return ip_set_timeout_expired(elem->timeout); | 97 | for (i = 0; i < map->size; i++) { |
98 | e = list_set_elem(map, i); | ||
99 | if (e->id == IPSET_INVALID_ID) | ||
100 | return 0; | ||
101 | if (SET_WITH_TIMEOUT(set) && | ||
102 | ip_set_timeout_expired(ext_timeout(e, map))) | ||
103 | continue; | ||
104 | ret = ip_set_add(e->id, skb, par, opt); | ||
105 | if (ret == 0) | ||
106 | return ret; | ||
107 | } | ||
108 | return 0; | ||
72 | } | 109 | } |
73 | 110 | ||
74 | /* Set list without and with timeout */ | ||
75 | |||
76 | static int | 111 | static int |
77 | list_set_kadt(struct ip_set *set, const struct sk_buff *skb, | 112 | list_set_kdel(struct ip_set *set, const struct sk_buff *skb, |
78 | const struct xt_action_param *par, | 113 | const struct xt_action_param *par, |
79 | enum ipset_adt adt, const struct ip_set_adt_opt *opt) | 114 | struct ip_set_adt_opt *opt, const struct ip_set_ext *ext) |
80 | { | 115 | { |
81 | struct list_set *map = set->data; | 116 | struct list_set *map = set->data; |
82 | struct set_elem *elem; | 117 | struct set_elem *e; |
83 | u32 i; | 118 | u32 i; |
84 | int ret; | 119 | int ret; |
85 | 120 | ||
86 | for (i = 0; i < map->size; i++) { | 121 | for (i = 0; i < map->size; i++) { |
87 | elem = list_set_elem(map, i); | 122 | e = list_set_elem(map, i); |
88 | if (elem->id == IPSET_INVALID_ID) | 123 | if (e->id == IPSET_INVALID_ID) |
89 | return 0; | 124 | return 0; |
90 | if (with_timeout(map->timeout) && list_set_expired(map, i)) | 125 | if (SET_WITH_TIMEOUT(set) && |
126 | ip_set_timeout_expired(ext_timeout(e, map))) | ||
91 | continue; | 127 | continue; |
92 | switch (adt) { | 128 | ret = ip_set_del(e->id, skb, par, opt); |
93 | case IPSET_TEST: | 129 | if (ret == 0) |
94 | ret = ip_set_test(elem->id, skb, par, opt); | 130 | return ret; |
95 | if (ret > 0) | 131 | } |
96 | return ret; | 132 | return 0; |
97 | break; | 133 | } |
98 | case IPSET_ADD: | 134 | |
99 | ret = ip_set_add(elem->id, skb, par, opt); | 135 | static int |
100 | if (ret == 0) | 136 | list_set_kadt(struct ip_set *set, const struct sk_buff *skb, |
101 | return ret; | 137 | const struct xt_action_param *par, |
102 | break; | 138 | enum ipset_adt adt, struct ip_set_adt_opt *opt) |
103 | case IPSET_DEL: | 139 | { |
104 | ret = ip_set_del(elem->id, skb, par, opt); | 140 | struct list_set *map = set->data; |
105 | if (ret == 0) | 141 | struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); |
106 | return ret; | 142 | |
107 | break; | 143 | switch (adt) { |
108 | default: | 144 | case IPSET_TEST: |
109 | break; | 145 | return list_set_ktest(set, skb, par, opt, &ext); |
110 | } | 146 | case IPSET_ADD: |
147 | return list_set_kadd(set, skb, par, opt, &ext); | ||
148 | case IPSET_DEL: | ||
149 | return list_set_kdel(set, skb, par, opt, &ext); | ||
150 | default: | ||
151 | break; | ||
111 | } | 152 | } |
112 | return -EINVAL; | 153 | return -EINVAL; |
113 | } | 154 | } |
114 | 155 | ||
115 | static bool | 156 | static bool |
116 | id_eq(const struct list_set *map, u32 i, ip_set_id_t id) | 157 | id_eq(const struct ip_set *set, u32 i, ip_set_id_t id) |
117 | { | 158 | { |
118 | const struct set_elem *elem; | 159 | const struct list_set *map = set->data; |
160 | const struct set_elem *e; | ||
161 | |||
162 | if (i >= map->size) | ||
163 | return 0; | ||
119 | 164 | ||
120 | if (i < map->size) { | 165 | e = list_set_elem(map, i); |
121 | elem = list_set_elem(map, i); | 166 | return !!(e->id == id && |
122 | return elem->id == id; | 167 | !(SET_WITH_TIMEOUT(set) && |
168 | ip_set_timeout_expired(ext_timeout(e, map)))); | ||
169 | } | ||
170 | |||
171 | static int | ||
172 | list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d, | ||
173 | const struct ip_set_ext *ext) | ||
174 | { | ||
175 | struct list_set *map = set->data; | ||
176 | struct set_elem *e = list_set_elem(map, i); | ||
177 | |||
178 | if (e->id != IPSET_INVALID_ID) { | ||
179 | if (i == map->size - 1) | ||
180 | /* Last element replaced: e.g. add new,before,last */ | ||
181 | ip_set_put_byindex(e->id); | ||
182 | else { | ||
183 | struct set_elem *x = list_set_elem(map, map->size - 1); | ||
184 | |||
185 | /* Last element pushed off */ | ||
186 | if (x->id != IPSET_INVALID_ID) | ||
187 | ip_set_put_byindex(x->id); | ||
188 | memmove(list_set_elem(map, i + 1), e, | ||
189 | map->dsize * (map->size - (i + 1))); | ||
190 | } | ||
123 | } | 191 | } |
124 | 192 | ||
193 | e->id = d->id; | ||
194 | if (SET_WITH_TIMEOUT(set)) | ||
195 | ip_set_timeout_set(ext_timeout(e, map), ext->timeout); | ||
125 | return 0; | 196 | return 0; |
126 | } | 197 | } |
127 | 198 | ||
128 | static bool | 199 | static int |
129 | id_eq_timeout(const struct list_set *map, u32 i, ip_set_id_t id) | 200 | list_set_del(struct ip_set *set, u32 i) |
130 | { | 201 | { |
131 | const struct set_elem *elem; | 202 | struct list_set *map = set->data; |
203 | struct set_elem *e = list_set_elem(map, i); | ||
132 | 204 | ||
133 | if (i < map->size) { | 205 | ip_set_put_byindex(e->id); |
134 | elem = list_set_elem(map, i); | ||
135 | return !!(elem->id == id && | ||
136 | !(with_timeout(map->timeout) && | ||
137 | list_set_expired(map, i))); | ||
138 | } | ||
139 | 206 | ||
207 | if (i < map->size - 1) | ||
208 | memmove(e, list_set_elem(map, i + 1), | ||
209 | map->dsize * (map->size - (i + 1))); | ||
210 | |||
211 | /* Last element */ | ||
212 | e = list_set_elem(map, map->size - 1); | ||
213 | e->id = IPSET_INVALID_ID; | ||
140 | return 0; | 214 | return 0; |
141 | } | 215 | } |
142 | 216 | ||
143 | static void | 217 | static void |
144 | list_elem_add(struct list_set *map, u32 i, ip_set_id_t id) | 218 | set_cleanup_entries(struct ip_set *set) |
145 | { | 219 | { |
220 | struct list_set *map = set->data; | ||
146 | struct set_elem *e; | 221 | struct set_elem *e; |
222 | u32 i; | ||
147 | 223 | ||
148 | for (; i < map->size; i++) { | 224 | for (i = 0; i < map->size; i++) { |
149 | e = list_set_elem(map, i); | 225 | e = list_set_elem(map, i); |
150 | swap(e->id, id); | 226 | if (e->id != IPSET_INVALID_ID && |
151 | if (e->id == IPSET_INVALID_ID) | 227 | ip_set_timeout_expired(ext_timeout(e, map))) |
152 | break; | 228 | list_set_del(set, i); |
153 | } | 229 | } |
154 | } | 230 | } |
155 | 231 | ||
156 | static void | 232 | static int |
157 | list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id, | 233 | list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext, |
158 | unsigned long timeout) | 234 | struct ip_set_ext *mext, u32 flags) |
159 | { | 235 | { |
160 | struct set_telem *e; | 236 | struct list_set *map = set->data; |
237 | struct set_adt_elem *d = value; | ||
238 | struct set_elem *e; | ||
239 | u32 i; | ||
240 | int ret; | ||
161 | 241 | ||
162 | for (; i < map->size; i++) { | 242 | for (i = 0; i < map->size; i++) { |
163 | e = list_set_telem(map, i); | 243 | e = list_set_elem(map, i); |
164 | swap(e->id, id); | ||
165 | swap(e->timeout, timeout); | ||
166 | if (e->id == IPSET_INVALID_ID) | 244 | if (e->id == IPSET_INVALID_ID) |
167 | break; | 245 | return 0; |
246 | else if (SET_WITH_TIMEOUT(set) && | ||
247 | ip_set_timeout_expired(ext_timeout(e, map))) | ||
248 | continue; | ||
249 | else if (e->id != d->id) | ||
250 | continue; | ||
251 | |||
252 | if (d->before == 0) | ||
253 | return 1; | ||
254 | else if (d->before > 0) | ||
255 | ret = id_eq(set, i + 1, d->refid); | ||
256 | else | ||
257 | ret = i > 0 && id_eq(set, i - 1, d->refid); | ||
258 | return ret; | ||
168 | } | 259 | } |
260 | return 0; | ||
169 | } | 261 | } |
170 | 262 | ||
263 | |||
171 | static int | 264 | static int |
172 | list_set_add(struct list_set *map, u32 i, ip_set_id_t id, | 265 | list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, |
173 | unsigned long timeout) | 266 | struct ip_set_ext *mext, u32 flags) |
174 | { | 267 | { |
175 | const struct set_elem *e = list_set_elem(map, i); | 268 | struct list_set *map = set->data; |
269 | struct set_adt_elem *d = value; | ||
270 | struct set_elem *e; | ||
271 | bool flag_exist = flags & IPSET_FLAG_EXIST; | ||
272 | u32 i, ret = 0; | ||
176 | 273 | ||
177 | if (e->id != IPSET_INVALID_ID) { | 274 | /* Check already added element */ |
178 | const struct set_elem *x = list_set_elem(map, map->size - 1); | 275 | for (i = 0; i < map->size; i++) { |
276 | e = list_set_elem(map, i); | ||
277 | if (e->id == IPSET_INVALID_ID) | ||
278 | goto insert; | ||
279 | else if (SET_WITH_TIMEOUT(set) && | ||
280 | ip_set_timeout_expired(ext_timeout(e, map))) | ||
281 | continue; | ||
282 | else if (e->id != d->id) | ||
283 | continue; | ||
179 | 284 | ||
180 | /* Last element replaced or pushed off */ | 285 | if ((d->before > 1 && !id_eq(set, i + 1, d->refid)) || |
181 | if (x->id != IPSET_INVALID_ID) | 286 | (d->before < 0 && |
182 | ip_set_put_byindex(x->id); | 287 | (i == 0 || !id_eq(set, i - 1, d->refid)))) |
288 | /* Before/after doesn't match */ | ||
289 | return -IPSET_ERR_REF_EXIST; | ||
290 | if (!flag_exist) | ||
291 | /* Can't re-add */ | ||
292 | return -IPSET_ERR_EXIST; | ||
293 | /* Update extensions */ | ||
294 | if (SET_WITH_TIMEOUT(set)) | ||
295 | ip_set_timeout_set(ext_timeout(e, map), ext->timeout); | ||
296 | /* Set is already added to the list */ | ||
297 | ip_set_put_byindex(d->id); | ||
298 | return 0; | ||
299 | } | ||
300 | insert: | ||
301 | ret = -IPSET_ERR_LIST_FULL; | ||
302 | for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { | ||
303 | e = list_set_elem(map, i); | ||
304 | if (e->id == IPSET_INVALID_ID) | ||
305 | ret = d->before != 0 ? -IPSET_ERR_REF_EXIST | ||
306 | : list_set_add(set, i, d, ext); | ||
307 | else if (e->id != d->refid) | ||
308 | continue; | ||
309 | else if (d->before > 0) | ||
310 | ret = list_set_add(set, i, d, ext); | ||
311 | else if (i + 1 < map->size) | ||
312 | ret = list_set_add(set, i + 1, d, ext); | ||
183 | } | 313 | } |
184 | if (with_timeout(map->timeout)) | ||
185 | list_elem_tadd(map, i, id, ip_set_timeout_set(timeout)); | ||
186 | else | ||
187 | list_elem_add(map, i, id); | ||
188 | 314 | ||
189 | return 0; | 315 | return ret; |
190 | } | 316 | } |
191 | 317 | ||
192 | static int | 318 | static int |
193 | list_set_del(struct list_set *map, u32 i) | 319 | list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext, |
320 | struct ip_set_ext *mext, u32 flags) | ||
194 | { | 321 | { |
195 | struct set_elem *a = list_set_elem(map, i), *b; | 322 | struct list_set *map = set->data; |
196 | 323 | struct set_adt_elem *d = value; | |
197 | ip_set_put_byindex(a->id); | 324 | struct set_elem *e; |
198 | |||
199 | for (; i < map->size - 1; i++) { | ||
200 | b = list_set_elem(map, i + 1); | ||
201 | a->id = b->id; | ||
202 | if (with_timeout(map->timeout)) | ||
203 | ((struct set_telem *)a)->timeout = | ||
204 | ((struct set_telem *)b)->timeout; | ||
205 | a = b; | ||
206 | if (a->id == IPSET_INVALID_ID) | ||
207 | break; | ||
208 | } | ||
209 | /* Last element */ | ||
210 | a->id = IPSET_INVALID_ID; | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static void | ||
215 | cleanup_entries(struct list_set *map) | ||
216 | { | ||
217 | struct set_telem *e; | ||
218 | u32 i; | 325 | u32 i; |
219 | 326 | ||
220 | for (i = 0; i < map->size; i++) { | 327 | for (i = 0; i < map->size; i++) { |
221 | e = list_set_telem(map, i); | 328 | e = list_set_elem(map, i); |
222 | if (e->id != IPSET_INVALID_ID && list_set_expired(map, i)) | 329 | if (e->id == IPSET_INVALID_ID) |
223 | list_set_del(map, i); | 330 | return d->before != 0 ? -IPSET_ERR_REF_EXIST |
331 | : -IPSET_ERR_EXIST; | ||
332 | else if (SET_WITH_TIMEOUT(set) && | ||
333 | ip_set_timeout_expired(ext_timeout(e, map))) | ||
334 | continue; | ||
335 | else if (e->id != d->id) | ||
336 | continue; | ||
337 | |||
338 | if (d->before == 0) | ||
339 | return list_set_del(set, i); | ||
340 | else if (d->before > 0) { | ||
341 | if (!id_eq(set, i + 1, d->refid)) | ||
342 | return -IPSET_ERR_REF_EXIST; | ||
343 | return list_set_del(set, i); | ||
344 | } else if (i == 0 || !id_eq(set, i - 1, d->refid)) | ||
345 | return -IPSET_ERR_REF_EXIST; | ||
346 | else | ||
347 | return list_set_del(set, i); | ||
224 | } | 348 | } |
349 | return -IPSET_ERR_EXIST; | ||
225 | } | 350 | } |
226 | 351 | ||
227 | static int | 352 | static int |
@@ -229,14 +354,10 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], | |||
229 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) | 354 | enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) |
230 | { | 355 | { |
231 | struct list_set *map = set->data; | 356 | struct list_set *map = set->data; |
232 | bool with_timeout = with_timeout(map->timeout); | 357 | ipset_adtfn adtfn = set->variant->adt[adt]; |
233 | bool flag_exist = flags & IPSET_FLAG_EXIST; | 358 | struct set_adt_elem e = { .refid = IPSET_INVALID_ID }; |
234 | int before = 0; | 359 | struct ip_set_ext ext = IP_SET_INIT_UEXT(map); |
235 | u32 timeout = map->timeout; | ||
236 | ip_set_id_t id, refid = IPSET_INVALID_ID; | ||
237 | const struct set_elem *elem; | ||
238 | struct ip_set *s; | 360 | struct ip_set *s; |
239 | u32 i; | ||
240 | int ret = 0; | 361 | int ret = 0; |
241 | 362 | ||
242 | if (unlikely(!tb[IPSET_ATTR_NAME] || | 363 | if (unlikely(!tb[IPSET_ATTR_NAME] || |
@@ -247,8 +368,11 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], | |||
247 | if (tb[IPSET_ATTR_LINENO]) | 368 | if (tb[IPSET_ATTR_LINENO]) |
248 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); | 369 | *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); |
249 | 370 | ||
250 | id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); | 371 | ret = ip_set_get_extensions(set, tb, &ext); |
251 | if (id == IPSET_INVALID_ID) | 372 | if (ret) |
373 | return ret; | ||
374 | e.id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); | ||
375 | if (e.id == IPSET_INVALID_ID) | ||
252 | return -IPSET_ERR_NAME; | 376 | return -IPSET_ERR_NAME; |
253 | /* "Loop detection" */ | 377 | /* "Loop detection" */ |
254 | if (s->type->features & IPSET_TYPE_NAME) { | 378 | if (s->type->features & IPSET_TYPE_NAME) { |
@@ -258,115 +382,34 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], | |||
258 | 382 | ||
259 | if (tb[IPSET_ATTR_CADT_FLAGS]) { | 383 | if (tb[IPSET_ATTR_CADT_FLAGS]) { |
260 | u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); | 384 | u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); |
261 | before = f & IPSET_FLAG_BEFORE; | 385 | e.before = f & IPSET_FLAG_BEFORE; |
262 | } | 386 | } |
263 | 387 | ||
264 | if (before && !tb[IPSET_ATTR_NAMEREF]) { | 388 | if (e.before && !tb[IPSET_ATTR_NAMEREF]) { |
265 | ret = -IPSET_ERR_BEFORE; | 389 | ret = -IPSET_ERR_BEFORE; |
266 | goto finish; | 390 | goto finish; |
267 | } | 391 | } |
268 | 392 | ||
269 | if (tb[IPSET_ATTR_NAMEREF]) { | 393 | if (tb[IPSET_ATTR_NAMEREF]) { |
270 | refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), | 394 | e.refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), |
271 | &s); | 395 | &s); |
272 | if (refid == IPSET_INVALID_ID) { | 396 | if (e.refid == IPSET_INVALID_ID) { |
273 | ret = -IPSET_ERR_NAMEREF; | 397 | ret = -IPSET_ERR_NAMEREF; |
274 | goto finish; | 398 | goto finish; |
275 | } | 399 | } |
276 | if (!before) | 400 | if (!e.before) |
277 | before = -1; | 401 | e.before = -1; |
278 | } | ||
279 | if (tb[IPSET_ATTR_TIMEOUT]) { | ||
280 | if (!with_timeout) { | ||
281 | ret = -IPSET_ERR_TIMEOUT; | ||
282 | goto finish; | ||
283 | } | ||
284 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
285 | } | 402 | } |
286 | if (with_timeout && adt != IPSET_TEST) | 403 | if (adt != IPSET_TEST && SET_WITH_TIMEOUT(set)) |
287 | cleanup_entries(map); | 404 | set_cleanup_entries(set); |
288 | 405 | ||
289 | switch (adt) { | 406 | ret = adtfn(set, &e, &ext, &ext, flags); |
290 | case IPSET_TEST: | ||
291 | for (i = 0; i < map->size && !ret; i++) { | ||
292 | elem = list_set_elem(map, i); | ||
293 | if (elem->id == IPSET_INVALID_ID || | ||
294 | (before != 0 && i + 1 >= map->size)) | ||
295 | break; | ||
296 | else if (with_timeout && list_set_expired(map, i)) | ||
297 | continue; | ||
298 | else if (before > 0 && elem->id == id) | ||
299 | ret = id_eq_timeout(map, i + 1, refid); | ||
300 | else if (before < 0 && elem->id == refid) | ||
301 | ret = id_eq_timeout(map, i + 1, id); | ||
302 | else if (before == 0 && elem->id == id) | ||
303 | ret = 1; | ||
304 | } | ||
305 | break; | ||
306 | case IPSET_ADD: | ||
307 | for (i = 0; i < map->size; i++) { | ||
308 | elem = list_set_elem(map, i); | ||
309 | if (elem->id != id) | ||
310 | continue; | ||
311 | if (!(with_timeout && flag_exist)) { | ||
312 | ret = -IPSET_ERR_EXIST; | ||
313 | goto finish; | ||
314 | } else { | ||
315 | struct set_telem *e = list_set_telem(map, i); | ||
316 | |||
317 | if ((before > 1 && | ||
318 | !id_eq(map, i + 1, refid)) || | ||
319 | (before < 0 && | ||
320 | (i == 0 || !id_eq(map, i - 1, refid)))) { | ||
321 | ret = -IPSET_ERR_EXIST; | ||
322 | goto finish; | ||
323 | } | ||
324 | e->timeout = ip_set_timeout_set(timeout); | ||
325 | ip_set_put_byindex(id); | ||
326 | ret = 0; | ||
327 | goto finish; | ||
328 | } | ||
329 | } | ||
330 | ret = -IPSET_ERR_LIST_FULL; | ||
331 | for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { | ||
332 | elem = list_set_elem(map, i); | ||
333 | if (elem->id == IPSET_INVALID_ID) | ||
334 | ret = before != 0 ? -IPSET_ERR_REF_EXIST | ||
335 | : list_set_add(map, i, id, timeout); | ||
336 | else if (elem->id != refid) | ||
337 | continue; | ||
338 | else if (before > 0) | ||
339 | ret = list_set_add(map, i, id, timeout); | ||
340 | else if (i + 1 < map->size) | ||
341 | ret = list_set_add(map, i + 1, id, timeout); | ||
342 | } | ||
343 | break; | ||
344 | case IPSET_DEL: | ||
345 | ret = -IPSET_ERR_EXIST; | ||
346 | for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) { | ||
347 | elem = list_set_elem(map, i); | ||
348 | if (elem->id == IPSET_INVALID_ID) { | ||
349 | ret = before != 0 ? -IPSET_ERR_REF_EXIST | ||
350 | : -IPSET_ERR_EXIST; | ||
351 | break; | ||
352 | } else if (elem->id == id && | ||
353 | (before == 0 || | ||
354 | (before > 0 && id_eq(map, i + 1, refid)))) | ||
355 | ret = list_set_del(map, i); | ||
356 | else if (elem->id == refid && | ||
357 | before < 0 && id_eq(map, i + 1, id)) | ||
358 | ret = list_set_del(map, i + 1); | ||
359 | } | ||
360 | break; | ||
361 | default: | ||
362 | break; | ||
363 | } | ||
364 | 407 | ||
365 | finish: | 408 | finish: |
366 | if (refid != IPSET_INVALID_ID) | 409 | if (e.refid != IPSET_INVALID_ID) |
367 | ip_set_put_byindex(refid); | 410 | ip_set_put_byindex(e.refid); |
368 | if (adt != IPSET_ADD || ret) | 411 | if (adt != IPSET_ADD || ret) |
369 | ip_set_put_byindex(id); | 412 | ip_set_put_byindex(e.id); |
370 | 413 | ||
371 | return ip_set_eexist(ret, flags) ? 0 : ret; | 414 | return ip_set_eexist(ret, flags) ? 0 : ret; |
372 | } | 415 | } |
@@ -375,14 +418,14 @@ static void | |||
375 | list_set_flush(struct ip_set *set) | 418 | list_set_flush(struct ip_set *set) |
376 | { | 419 | { |
377 | struct list_set *map = set->data; | 420 | struct list_set *map = set->data; |
378 | struct set_elem *elem; | 421 | struct set_elem *e; |
379 | u32 i; | 422 | u32 i; |
380 | 423 | ||
381 | for (i = 0; i < map->size; i++) { | 424 | for (i = 0; i < map->size; i++) { |
382 | elem = list_set_elem(map, i); | 425 | e = list_set_elem(map, i); |
383 | if (elem->id != IPSET_INVALID_ID) { | 426 | if (e->id != IPSET_INVALID_ID) { |
384 | ip_set_put_byindex(elem->id); | 427 | ip_set_put_byindex(e->id); |
385 | elem->id = IPSET_INVALID_ID; | 428 | e->id = IPSET_INVALID_ID; |
386 | } | 429 | } |
387 | } | 430 | } |
388 | } | 431 | } |
@@ -392,7 +435,7 @@ list_set_destroy(struct ip_set *set) | |||
392 | { | 435 | { |
393 | struct list_set *map = set->data; | 436 | struct list_set *map = set->data; |
394 | 437 | ||
395 | if (with_timeout(map->timeout)) | 438 | if (SET_WITH_TIMEOUT(set)) |
396 | del_timer_sync(&map->gc); | 439 | del_timer_sync(&map->gc); |
397 | list_set_flush(set); | 440 | list_set_flush(set); |
398 | kfree(map); | 441 | kfree(map); |
@@ -410,7 +453,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb) | |||
410 | if (!nested) | 453 | if (!nested) |
411 | goto nla_put_failure; | 454 | goto nla_put_failure; |
412 | if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) || | 455 | if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) || |
413 | (with_timeout(map->timeout) && | 456 | (SET_WITH_TIMEOUT(set) && |
414 | nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) || | 457 | nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) || |
415 | nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || | 458 | nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || |
416 | nla_put_net32(skb, IPSET_ATTR_MEMSIZE, | 459 | nla_put_net32(skb, IPSET_ATTR_MEMSIZE, |
@@ -440,7 +483,8 @@ list_set_list(const struct ip_set *set, | |||
440 | e = list_set_elem(map, i); | 483 | e = list_set_elem(map, i); |
441 | if (e->id == IPSET_INVALID_ID) | 484 | if (e->id == IPSET_INVALID_ID) |
442 | goto finish; | 485 | goto finish; |
443 | if (with_timeout(map->timeout) && list_set_expired(map, i)) | 486 | if (SET_WITH_TIMEOUT(set) && |
487 | ip_set_timeout_expired(ext_timeout(e, map))) | ||
444 | continue; | 488 | continue; |
445 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); | 489 | nested = ipset_nest_start(skb, IPSET_ATTR_DATA); |
446 | if (!nested) { | 490 | if (!nested) { |
@@ -453,13 +497,11 @@ list_set_list(const struct ip_set *set, | |||
453 | if (nla_put_string(skb, IPSET_ATTR_NAME, | 497 | if (nla_put_string(skb, IPSET_ATTR_NAME, |
454 | ip_set_name_byindex(e->id))) | 498 | ip_set_name_byindex(e->id))) |
455 | goto nla_put_failure; | 499 | goto nla_put_failure; |
456 | if (with_timeout(map->timeout)) { | 500 | if (SET_WITH_TIMEOUT(set) && |
457 | const struct set_telem *te = | 501 | nla_put_net32(skb, IPSET_ATTR_TIMEOUT, |
458 | (const struct set_telem *) e; | 502 | htonl(ip_set_timeout_get( |
459 | __be32 to = htonl(ip_set_timeout_get(te->timeout)); | 503 | ext_timeout(e, map))))) |
460 | if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, to)) | 504 | goto nla_put_failure; |
461 | goto nla_put_failure; | ||
462 | } | ||
463 | ipset_nest_end(skb, nested); | 505 | ipset_nest_end(skb, nested); |
464 | } | 506 | } |
465 | finish: | 507 | finish: |
@@ -485,12 +527,18 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b) | |||
485 | const struct list_set *y = b->data; | 527 | const struct list_set *y = b->data; |
486 | 528 | ||
487 | return x->size == y->size && | 529 | return x->size == y->size && |
488 | x->timeout == y->timeout; | 530 | x->timeout == y->timeout && |
531 | a->extensions == b->extensions; | ||
489 | } | 532 | } |
490 | 533 | ||
491 | static const struct ip_set_type_variant list_set = { | 534 | static const struct ip_set_type_variant set_variant = { |
492 | .kadt = list_set_kadt, | 535 | .kadt = list_set_kadt, |
493 | .uadt = list_set_uadt, | 536 | .uadt = list_set_uadt, |
537 | .adt = { | ||
538 | [IPSET_ADD] = list_set_uadd, | ||
539 | [IPSET_DEL] = list_set_udel, | ||
540 | [IPSET_TEST] = list_set_utest, | ||
541 | }, | ||
494 | .destroy = list_set_destroy, | 542 | .destroy = list_set_destroy, |
495 | .flush = list_set_flush, | 543 | .flush = list_set_flush, |
496 | .head = list_set_head, | 544 | .head = list_set_head, |
@@ -505,7 +553,7 @@ list_set_gc(unsigned long ul_set) | |||
505 | struct list_set *map = set->data; | 553 | struct list_set *map = set->data; |
506 | 554 | ||
507 | write_lock_bh(&set->lock); | 555 | write_lock_bh(&set->lock); |
508 | cleanup_entries(map); | 556 | set_cleanup_entries(set); |
509 | write_unlock_bh(&set->lock); | 557 | write_unlock_bh(&set->lock); |
510 | 558 | ||
511 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | 559 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; |
@@ -513,20 +561,20 @@ list_set_gc(unsigned long ul_set) | |||
513 | } | 561 | } |
514 | 562 | ||
515 | static void | 563 | static void |
516 | list_set_gc_init(struct ip_set *set) | 564 | list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) |
517 | { | 565 | { |
518 | struct list_set *map = set->data; | 566 | struct list_set *map = set->data; |
519 | 567 | ||
520 | init_timer(&map->gc); | 568 | init_timer(&map->gc); |
521 | map->gc.data = (unsigned long) set; | 569 | map->gc.data = (unsigned long) set; |
522 | map->gc.function = list_set_gc; | 570 | map->gc.function = gc; |
523 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; | 571 | map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; |
524 | add_timer(&map->gc); | 572 | add_timer(&map->gc); |
525 | } | 573 | } |
526 | 574 | ||
527 | /* Create list:set type of sets */ | 575 | /* Create list:set type of sets */ |
528 | 576 | ||
529 | static bool | 577 | static struct list_set * |
530 | init_list_set(struct ip_set *set, u32 size, size_t dsize, | 578 | init_list_set(struct ip_set *set, u32 size, size_t dsize, |
531 | unsigned long timeout) | 579 | unsigned long timeout) |
532 | { | 580 | { |
@@ -536,7 +584,7 @@ init_list_set(struct ip_set *set, u32 size, size_t dsize, | |||
536 | 584 | ||
537 | map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL); | 585 | map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL); |
538 | if (!map) | 586 | if (!map) |
539 | return false; | 587 | return NULL; |
540 | 588 | ||
541 | map->size = size; | 589 | map->size = size; |
542 | map->dsize = dsize; | 590 | map->dsize = dsize; |
@@ -548,13 +596,15 @@ init_list_set(struct ip_set *set, u32 size, size_t dsize, | |||
548 | e->id = IPSET_INVALID_ID; | 596 | e->id = IPSET_INVALID_ID; |
549 | } | 597 | } |
550 | 598 | ||
551 | return true; | 599 | return map; |
552 | } | 600 | } |
553 | 601 | ||
554 | static int | 602 | static int |
555 | list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | 603 | list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags) |
556 | { | 604 | { |
605 | struct list_set *map; | ||
557 | u32 size = IP_SET_LIST_DEFAULT_SIZE; | 606 | u32 size = IP_SET_LIST_DEFAULT_SIZE; |
607 | unsigned long timeout = 0; | ||
558 | 608 | ||
559 | if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) || | 609 | if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) || |
560 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) | 610 | !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) |
@@ -565,18 +615,23 @@ list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags) | |||
565 | if (size < IP_SET_LIST_MIN_SIZE) | 615 | if (size < IP_SET_LIST_MIN_SIZE) |
566 | size = IP_SET_LIST_MIN_SIZE; | 616 | size = IP_SET_LIST_MIN_SIZE; |
567 | 617 | ||
618 | if (tb[IPSET_ATTR_TIMEOUT]) | ||
619 | timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); | ||
620 | set->variant = &set_variant; | ||
568 | if (tb[IPSET_ATTR_TIMEOUT]) { | 621 | if (tb[IPSET_ATTR_TIMEOUT]) { |
569 | if (!init_list_set(set, size, sizeof(struct set_telem), | 622 | map = init_list_set(set, size, |
570 | ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]))) | 623 | sizeof(struct sett_elem), timeout); |
624 | if (!map) | ||
571 | return -ENOMEM; | 625 | return -ENOMEM; |
572 | 626 | set->extensions |= IPSET_EXT_TIMEOUT; | |
573 | list_set_gc_init(set); | 627 | map->offset[IPSET_OFFSET_TIMEOUT] = |
628 | offsetof(struct sett_elem, timeout); | ||
629 | list_set_gc_init(set, list_set_gc); | ||
574 | } else { | 630 | } else { |
575 | if (!init_list_set(set, size, sizeof(struct set_elem), | 631 | map = init_list_set(set, size, sizeof(struct set_elem), 0); |
576 | IPSET_NO_TIMEOUT)) | 632 | if (!map) |
577 | return -ENOMEM; | 633 | return -ENOMEM; |
578 | } | 634 | } |
579 | set->variant = &list_set; | ||
580 | return 0; | 635 | return 0; |
581 | } | 636 | } |
582 | 637 | ||