]> git.ipfire.org Git - thirdparty/ipset.git/blame - kernel/net/netfilter/ipset/ip_set_list_set.c
treewide: Convert del_timer*() to timer_shutdown*()
[thirdparty/ipset.git] / kernel / net / netfilter / ipset / ip_set_list_set.c
CommitLineData
6cd9def1 1// SPDX-License-Identifier: GPL-2.0-only
0fdebb3b 2/* Copyright (C) 2008-2013 Jozsef Kadlecsik <kadlec@netfilter.org>
a96e4fca
ESECJK
3 */
4
3fd6b24a 5/* Kernel module implementing an IP set type: the list:set type */
a96e4fca
ESECJK
6
7#include <linux/module.h>
8#include <linux/ip.h>
920ddfa0 9#include <linux/rculist.h>
a96e4fca
ESECJK
10#include <linux/skbuff.h>
11#include <linux/errno.h>
12
4902415c 13#include <linux/netfilter/ipset/ip_set.h>
4902415c 14#include <linux/netfilter/ipset/ip_set_list.h>
a96e4fca 15
76e1fadb 16#define IPSET_TYPE_REV_MIN 0
718080c5 17/* 1 Counters support added */
89798a24
AD
18/* 2 Comments support added */
19#define IPSET_TYPE_REV_MAX 3 /* skbinfo support added */
aec28884 20
3fd6b24a 21MODULE_LICENSE("GPL");
0fdebb3b 22MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
76e1fadb 23IP_SET_MODULE_DESC("list:set", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
3fd6b24a
JK
24MODULE_ALIAS("ip_set_list:set");
25
9b24b406 26/* Member elements */
3fd6b24a 27struct set_elem {
920ddfa0
JK
28 struct rcu_head rcu;
29 struct list_head list;
27265aab 30 struct ip_set *set; /* Sigh, in order to cleanup reference */
3fd6b24a 31 ip_set_id_t id;
c7cf6f3b 32} __aligned(__alignof__(u64));
3fd6b24a 33
9b24b406
JK
34struct set_adt_elem {
35 ip_set_id_t id;
36 ip_set_id_t refid;
37 int before;
38};
39
3fd6b24a
JK
40/* Type structure */
41struct list_set {
3fd6b24a 42 u32 size; /* size of set list array */
3fd6b24a 43 struct timer_list gc; /* garbage collection */
0a81ab50
JK
44#ifdef HAVE_TIMER_SETUP
45 struct ip_set *set; /* attached to this ip_set */
46#endif
bac86fe6 47 struct net *net; /* namespace */
920ddfa0 48 struct list_head members; /* the set members */
3fd6b24a
JK
49};
50
9b24b406
JK
51static int
52list_set_ktest(struct ip_set *set, const struct sk_buff *skb,
53 const struct xt_action_param *par,
54 struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
3fd6b24a 55{
9b24b406 56 struct list_set *map = set->data;
ad92ed77 57 struct ip_set_ext *mext = &opt->ext;
9b24b406 58 struct set_elem *e;
ad92ed77 59 u32 flags = opt->cmdflags;
9b24b406 60 int ret;
9d317732 61
488c1e17
JK
62 /* Don't lookup sub-counters at all */
63 opt->cmdflags &= ~IPSET_FLAG_MATCH_COUNTERS;
64 if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE)
e362d763 65 opt->cmdflags |= IPSET_FLAG_SKIP_COUNTER_UPDATE;
920ddfa0 66 list_for_each_entry_rcu(e, &map->members, list) {
9b24b406 67 ret = ip_set_test(e->id, skb, par, opt);
ad92ed77
JK
68 if (ret <= 0)
69 continue;
70 if (ip_set_match_extensions(set, ext, mext, flags, e))
71 return 1;
9b24b406
JK
72 }
73 return 0;
3fd6b24a
JK
74}
75
9b24b406
JK
76static int
77list_set_kadd(struct ip_set *set, const struct sk_buff *skb,
78 const struct xt_action_param *par,
79 struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
3fd6b24a 80{
9b24b406
JK
81 struct list_set *map = set->data;
82 struct set_elem *e;
9b24b406 83 int ret;
3fd6b24a 84
2ca175d7 85 list_for_each_entry(e, &map->members, list) {
9b24b406 86 if (SET_WITH_TIMEOUT(set) &&
71b092cb 87 ip_set_timeout_expired(ext_timeout(e, set)))
9b24b406
JK
88 continue;
89 ret = ip_set_add(e->id, skb, par, opt);
90 if (ret == 0)
91 return ret;
92 }
93 return 0;
3fd6b24a 94}
a96e4fca 95
a96e4fca 96static int
9b24b406 97list_set_kdel(struct ip_set *set, const struct sk_buff *skb,
000b9b79 98 const struct xt_action_param *par,
9b24b406 99 struct ip_set_adt_opt *opt, const struct ip_set_ext *ext)
a96e4fca 100{
3fd6b24a 101 struct list_set *map = set->data;
9b24b406 102 struct set_elem *e;
3fd6b24a
JK
103 int ret;
104
2ca175d7 105 list_for_each_entry(e, &map->members, list) {
9b24b406 106 if (SET_WITH_TIMEOUT(set) &&
71b092cb 107 ip_set_timeout_expired(ext_timeout(e, set)))
3fd6b24a 108 continue;
9b24b406
JK
109 ret = ip_set_del(e->id, skb, par, opt);
110 if (ret == 0)
111 return ret;
112 }
113 return 0;
114}
115
116static int
117list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
118 const struct xt_action_param *par,
119 enum ipset_adt adt, struct ip_set_adt_opt *opt)
120{
71b092cb 121 struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
920ddfa0 122 int ret = -EINVAL;
9b24b406 123
920ddfa0 124 rcu_read_lock();
9b24b406
JK
125 switch (adt) {
126 case IPSET_TEST:
920ddfa0
JK
127 ret = list_set_ktest(set, skb, par, opt, &ext);
128 break;
9b24b406 129 case IPSET_ADD:
920ddfa0
JK
130 ret = list_set_kadd(set, skb, par, opt, &ext);
131 break;
9b24b406 132 case IPSET_DEL:
920ddfa0
JK
133 ret = list_set_kdel(set, skb, par, opt, &ext);
134 break;
9b24b406
JK
135 default:
136 break;
a96e4fca 137 }
920ddfa0 138 rcu_read_unlock();
3fd6b24a 139
920ddfa0 140 return ret;
9b24b406
JK
141}
142
920ddfa0 143/* Userspace interfaces: we are protected by the nfnl mutex */
bdd51358 144
920ddfa0 145static void
27265aab 146__list_set_del_rcu(struct rcu_head * rcu)
bdd51358 147{
27265aab
JK
148 struct set_elem *e = container_of(rcu, struct set_elem, rcu);
149 struct ip_set *set = e->set;
bdd51358 150
92f6f53b 151 ip_set_ext_destroy(set, e);
27265aab 152 kfree(e);
920ddfa0 153}
3fd6b24a 154
4392230c 155static void
920ddfa0
JK
156list_set_del(struct ip_set *set, struct set_elem *e)
157{
328623aa
SB
158 struct list_set *map = set->data;
159
0c7e18ed 160 set->elements--;
920ddfa0 161 list_del_rcu(&e->list);
328623aa 162 ip_set_put_byindex(map->net, e->id);
27265aab 163 call_rcu(&e->rcu, __list_set_del_rcu);
920ddfa0 164}
9b24b406 165
4392230c 166static void
328623aa 167list_set_replace(struct ip_set *set, struct set_elem *e, struct set_elem *old)
920ddfa0 168{
328623aa
SB
169 struct list_set *map = set->data;
170
920ddfa0 171 list_replace_rcu(&old->list, &e->list);
328623aa 172 ip_set_put_byindex(map->net, old->id);
27265aab 173 call_rcu(&old->rcu, __list_set_del_rcu);
a96e4fca
ESECJK
174}
175
ed3c453a 176static void
9b24b406 177set_cleanup_entries(struct ip_set *set)
3fd6b24a 178{
9b24b406 179 struct list_set *map = set->data;
920ddfa0 180 struct set_elem *e, *n;
3fd6b24a 181
920ddfa0
JK
182 list_for_each_entry_safe(e, n, &map->members, list)
183 if (ip_set_timeout_expired(ext_timeout(e, set)))
184 list_set_del(set, e);
3fd6b24a 185}
9d317732 186
9b24b406
JK
187static int
188list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext,
189 struct ip_set_ext *mext, u32 flags)
3fd6b24a 190{
9b24b406
JK
191 struct list_set *map = set->data;
192 struct set_adt_elem *d = value;
920ddfa0 193 struct set_elem *e, *next, *prev = NULL;
9b24b406 194 int ret;
3fd6b24a 195
920ddfa0
JK
196 list_for_each_entry(e, &map->members, list) {
197 if (SET_WITH_TIMEOUT(set) &&
198 ip_set_timeout_expired(ext_timeout(e, set)))
9b24b406 199 continue;
920ddfa0
JK
200 else if (e->id != d->id) {
201 prev = e;
9b24b406 202 continue;
920ddfa0 203 }
9b24b406 204
9f497f7e 205 if (d->before == 0) {
920ddfa0 206 ret = 1;
9f497f7e 207 } else if (d->before > 0) {
920ddfa0
JK
208 next = list_next_entry(e, list);
209 ret = !list_is_last(&e->list, &map->members) &&
210 next->id == d->refid;
9f497f7e 211 } else {
8bd2debc 212 ret = prev && prev->id == d->refid;
9f497f7e 213 }
9b24b406 214 return ret;
3fd6b24a 215 }
9b24b406 216 return 0;
3fd6b24a 217}
9d317732 218
920ddfa0
JK
219static void
220list_set_init_extensions(struct ip_set *set, const struct ip_set_ext *ext,
221 struct set_elem *e)
222{
223 if (SET_WITH_COUNTER(set))
224 ip_set_init_counter(ext_counter(e, set), ext);
225 if (SET_WITH_COMMENT(set))
5a568889 226 ip_set_init_comment(set, ext_comment(e, set), ext);
920ddfa0
JK
227 if (SET_WITH_SKBINFO(set))
228 ip_set_init_skbinfo(ext_skbinfo(e, set), ext);
229 /* Update timeout last */
230 if (SET_WITH_TIMEOUT(set))
231 ip_set_timeout_set(ext_timeout(e, set), ext->timeout);
232}
9b24b406 233
a96e4fca 234static int
9b24b406
JK
235list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
236 struct ip_set_ext *mext, u32 flags)
a96e4fca 237{
9b24b406
JK
238 struct list_set *map = set->data;
239 struct set_adt_elem *d = value;
920ddfa0 240 struct set_elem *e, *n, *prev, *next;
9b24b406 241 bool flag_exist = flags & IPSET_FLAG_EXIST;
3fd6b24a 242
920ddfa0
JK
243 /* Find where to add the new entry */
244 n = prev = next = NULL;
245 list_for_each_entry(e, &map->members, list) {
246 if (SET_WITH_TIMEOUT(set) &&
247 ip_set_timeout_expired(ext_timeout(e, set)))
9b24b406 248 continue;
920ddfa0
JK
249 else if (d->id == e->id)
250 n = e;
251 else if (d->before == 0 || e->id != d->refid)
252 continue;
253 else if (d->before > 0)
254 next = e;
255 else
256 prev = e;
257 }
4d0d11e6
VP
258
259 /* If before/after is used on an empty set */
260 if ((d->before > 0 && !next) ||
261 (d->before < 0 && !prev))
262 return -IPSET_ERR_REF_EXIST;
263
920ddfa0
JK
264 /* Re-add already existing element */
265 if (n) {
9b24b406 266 if (!flag_exist)
9b24b406
JK
267 return -IPSET_ERR_EXIST;
268 /* Update extensions */
920ddfa0
JK
269 ip_set_ext_destroy(set, n);
270 list_set_init_extensions(set, ext, n);
92f6f53b 271
9b24b406 272 /* Set is already added to the list */
bac86fe6 273 ip_set_put_byindex(map->net, d->id);
9b24b406
JK
274 return 0;
275 }
920ddfa0
JK
276 /* Add new entry */
277 if (d->before == 0) {
278 /* Append */
279 n = list_empty(&map->members) ? NULL :
280 list_last_entry(&map->members, struct set_elem, list);
281 } else if (d->before > 0) {
282 /* Insert after next element */
283 if (!list_is_last(&next->list, &map->members))
284 n = list_next_entry(next, list);
285 } else {
286 /* Insert before prev element */
287 if (prev->list.prev != &map->members)
288 n = list_prev_entry(prev, list);
d34aeb3c 289 }
920ddfa0 290 /* Can we replace a timed out entry? */
8bd2debc 291 if (n &&
920ddfa0
JK
292 !(SET_WITH_TIMEOUT(set) &&
293 ip_set_timeout_expired(ext_timeout(n, set))))
bbd11c89 294 n = NULL;
920ddfa0 295
e3f1193c 296 e = kzalloc(set->dsize, GFP_ATOMIC);
920ddfa0
JK
297 if (!e)
298 return -ENOMEM;
299 e->id = d->id;
27265aab 300 e->set = set;
920ddfa0
JK
301 INIT_LIST_HEAD(&e->list);
302 list_set_init_extensions(set, ext, e);
303 if (n)
328623aa 304 list_set_replace(set, e, n);
920ddfa0
JK
305 else if (next)
306 list_add_tail_rcu(&e->list, &next->list);
307 else if (prev)
308 list_add_rcu(&e->list, &prev->list);
309 else
310 list_add_tail_rcu(&e->list, &map->members);
0c7e18ed 311 set->elements++;
920ddfa0 312
920ddfa0 313 return 0;
a96e4fca
ESECJK
314}
315
3fd6b24a 316static int
9b24b406
JK
317list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext,
318 struct ip_set_ext *mext, u32 flags)
a96e4fca 319{
9b24b406
JK
320 struct list_set *map = set->data;
321 struct set_adt_elem *d = value;
920ddfa0
JK
322 struct set_elem *e, *next, *prev = NULL;
323
324 list_for_each_entry(e, &map->members, list) {
325 if (SET_WITH_TIMEOUT(set) &&
326 ip_set_timeout_expired(ext_timeout(e, set)))
9b24b406 327 continue;
920ddfa0
JK
328 else if (e->id != d->id) {
329 prev = e;
9b24b406 330 continue;
920ddfa0 331 }
9b24b406 332
920ddfa0
JK
333 if (d->before > 0) {
334 next = list_next_entry(e, list);
335 if (list_is_last(&e->list, &map->members) ||
336 next->id != d->refid)
9b24b406 337 return -IPSET_ERR_REF_EXIST;
920ddfa0 338 } else if (d->before < 0) {
8bd2debc 339 if (!prev || prev->id != d->refid)
920ddfa0
JK
340 return -IPSET_ERR_REF_EXIST;
341 }
342 list_set_del(set, e);
343 return 0;
bdd51358 344 }
920ddfa0 345 return d->before != 0 ? -IPSET_ERR_REF_EXIST : -IPSET_ERR_EXIST;
bdd51358
JK
346}
347
a96e4fca 348static int
b84145e4 349list_set_uadt(struct ip_set *set, struct nlattr *tb[],
32d1d2fa 350 enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
a96e4fca 351{
bac86fe6 352 struct list_set *map = set->data;
9b24b406
JK
353 ipset_adtfn adtfn = set->variant->adt[adt];
354 struct set_adt_elem e = { .refid = IPSET_INVALID_ID };
71b092cb 355 struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
a96e4fca 356 struct ip_set *s;
3fd6b24a
JK
357 int ret = 0;
358
1ea77bda
SP
359 if (tb[IPSET_ATTR_LINENO])
360 *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
361
13f42a71 362 if (unlikely(!tb[IPSET_ATTR_NAME] ||
2ca2559e 363 !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
13f42a71
JK
364 return -IPSET_ERR_PROTOCOL;
365
9b24b406
JK
366 ret = ip_set_get_extensions(set, tb, &ext);
367 if (ret)
368 return ret;
bac86fe6 369 e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s);
9b24b406 370 if (e.id == IPSET_INVALID_ID)
13f42a71
JK
371 return -IPSET_ERR_NAME;
372 /* "Loop detection" */
373 if (s->type->features & IPSET_TYPE_NAME) {
374 ret = -IPSET_ERR_LOOP;
375 goto finish;
376 }
3fd6b24a
JK
377
378 if (tb[IPSET_ATTR_CADT_FLAGS]) {
379 u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
920ddfa0 380
9b24b406 381 e.before = f & IPSET_FLAG_BEFORE;
3fd6b24a
JK
382 }
383
9b24b406 384 if (e.before && !tb[IPSET_ATTR_NAMEREF]) {
3fd6b24a 385 ret = -IPSET_ERR_BEFORE;
a96e4fca 386 goto finish;
3fd6b24a 387 }
a96e4fca 388
3fd6b24a 389 if (tb[IPSET_ATTR_NAMEREF]) {
bac86fe6
VL
390 e.refid = ip_set_get_byname(map->net,
391 nla_data(tb[IPSET_ATTR_NAMEREF]),
9b24b406
JK
392 &s);
393 if (e.refid == IPSET_INVALID_ID) {
3fd6b24a 394 ret = -IPSET_ERR_NAMEREF;
a96e4fca
ESECJK
395 goto finish;
396 }
9b24b406
JK
397 if (!e.before)
398 e.before = -1;
3fd6b24a 399 }
9b24b406
JK
400 if (adt != IPSET_TEST && SET_WITH_TIMEOUT(set))
401 set_cleanup_entries(set);
9d317732 402
9b24b406 403 ret = adtfn(set, &e, &ext, &ext, flags);
3fd6b24a 404
a96e4fca 405finish:
9b24b406 406 if (e.refid != IPSET_INVALID_ID)
bac86fe6 407 ip_set_put_byindex(map->net, e.refid);
3fd6b24a 408 if (adt != IPSET_ADD || ret)
bac86fe6 409 ip_set_put_byindex(map->net, e.id);
3fd6b24a 410
020936c8 411 return ip_set_eexist(ret, flags) ? 0 : ret;
a96e4fca
ESECJK
412}
413
3fd6b24a
JK
414static void
415list_set_flush(struct ip_set *set)
a96e4fca 416{
3fd6b24a 417 struct list_set *map = set->data;
920ddfa0
JK
418 struct set_elem *e, *n;
419
420 list_for_each_entry_safe(e, n, &map->members, list)
421 list_set_del(set, e);
0c7e18ed 422 set->elements = 0;
5a568889 423 set->ext_size = 0;
3fd6b24a
JK
424}
425
426static void
427list_set_destroy(struct ip_set *set)
428{
429 struct list_set *map = set->data;
920ddfa0 430 struct set_elem *e, *n;
3fd6b24a 431
920ddfa0
JK
432 list_for_each_entry_safe(e, n, &map->members, list) {
433 list_del(&e->list);
434 ip_set_put_byindex(map->net, e->id);
435 ip_set_ext_destroy(set, e);
436 kfree(e);
437 }
3fd6b24a 438 kfree(map);
9d317732 439
3fd6b24a 440 set->data = NULL;
a96e4fca
ESECJK
441}
442
d897885a
JK
443/* Calculate the actual memory size of the set data */
444static size_t
445list_set_memsize(const struct list_set *map, size_t dsize)
a96e4fca 446{
920ddfa0
JK
447 struct set_elem *e;
448 u32 n = 0;
449
27265aab
JK
450 rcu_read_lock();
451 list_for_each_entry_rcu(e, &map->members, list)
920ddfa0 452 n++;
27265aab 453 rcu_read_unlock();
3fd6b24a 454
905df4e8 455 return (sizeof(*map) + n * dsize);
d897885a
JK
456}
457
458static int
459list_set_head(struct ip_set *set, struct sk_buff *skb)
460{
461 const struct list_set *map = set->data;
462 struct nlattr *nested;
5a568889 463 size_t memsize = list_set_memsize(map, set->dsize) + set->ext_size;
d897885a 464
3fd6b24a
JK
465 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
466 if (!nested)
467 goto nla_put_failure;
1a3d302a 468 if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
4c70c227 469 nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) ||
0c7e18ed
JK
470 nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) ||
471 nla_put_net32(skb, IPSET_ATTR_ELEMENTS, htonl(set->elements)))
1a3d302a 472 goto nla_put_failure;
718080c5
OS
473 if (unlikely(ip_set_put_flags(skb, set)))
474 goto nla_put_failure;
3fd6b24a 475 ipset_nest_end(skb, nested);
9d317732 476
a96e4fca 477 return 0;
3fd6b24a 478nla_put_failure:
fa7f70f8 479 return -EMSGSIZE;
a96e4fca
ESECJK
480}
481
482static int
3850182a 483list_set_list(const struct ip_set *set,
3fd6b24a 484 struct sk_buff *skb, struct netlink_callback *cb)
a96e4fca 485{
3fd6b24a
JK
486 const struct list_set *map = set->data;
487 struct nlattr *atd, *nested;
920ddfa0 488 u32 i = 0, first = cb->args[IPSET_CB_ARG0];
328623aa 489 char name[IPSET_MAXNAMELEN];
920ddfa0 490 struct set_elem *e;
aa458c2d 491 int ret = 0;
3fd6b24a
JK
492
493 atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
494 if (!atd)
fa7f70f8 495 return -EMSGSIZE;
920ddfa0 496
aa458c2d 497 rcu_read_lock();
27265aab
JK
498 list_for_each_entry_rcu(e, &map->members, list) {
499 if (i < first ||
500 (SET_WITH_TIMEOUT(set) &&
501 ip_set_timeout_expired(ext_timeout(e, set)))) {
502 i++;
3fd6b24a 503 continue;
27265aab 504 }
3fd6b24a 505 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
27265aab 506 if (!nested)
9f497f7e 507 goto nla_put_failure;
328623aa
SB
508 ip_set_name_byindex(map->net, e->id, name);
509 if (nla_put_string(skb, IPSET_ATTR_NAME, name))
1a3d302a 510 goto nla_put_failure;
def57acb 511 if (ip_set_put_extensions(skb, set, e, true))
718080c5 512 goto nla_put_failure;
3fd6b24a 513 ipset_nest_end(skb, nested);
27265aab 514 i++;
a96e4fca 515 }
920ddfa0 516
3fd6b24a
JK
517 ipset_nest_end(skb, atd);
518 /* Set listing finished */
a61d7ebe 519 cb->args[IPSET_CB_ARG0] = 0;
aa458c2d 520 goto out;
3fd6b24a
JK
521
522nla_put_failure:
523 nla_nest_cancel(skb, nested);
fa7f70f8 524 if (unlikely(i == first)) {
27265aab 525 nla_nest_cancel(skb, atd);
a61d7ebe 526 cb->args[IPSET_CB_ARG0] = 0;
aa458c2d 527 ret = -EMSGSIZE;
27265aab
JK
528 } else {
529 cb->args[IPSET_CB_ARG0] = i;
3bd458e4 530 ipset_nest_end(skb, atd);
fa7f70f8 531 }
aa458c2d
JK
532out:
533 rcu_read_unlock();
534 return ret;
a96e4fca
ESECJK
535}
536
3fd6b24a
JK
537static bool
538list_set_same_set(const struct ip_set *a, const struct ip_set *b)
a96e4fca 539{
3850182a
JE
540 const struct list_set *x = a->data;
541 const struct list_set *y = b->data;
9d317732 542
c2d28607 543 return x->size == y->size &&
71b092cb 544 a->timeout == b->timeout &&
9b24b406 545 a->extensions == b->extensions;
a96e4fca
ESECJK
546}
547
148fad4d
JK
548static void
549list_set_cancel_gc(struct ip_set *set)
550{
551 struct list_set *map = set->data;
552
553 if (SET_WITH_TIMEOUT(set))
f3e90afa 554 timer_shutdown_sync(&map->gc);
148fad4d
JK
555}
556
9b24b406 557static const struct ip_set_type_variant set_variant = {
3fd6b24a
JK
558 .kadt = list_set_kadt,
559 .uadt = list_set_uadt,
9b24b406
JK
560 .adt = {
561 [IPSET_ADD] = list_set_uadd,
562 [IPSET_DEL] = list_set_udel,
563 [IPSET_TEST] = list_set_utest,
564 },
3fd6b24a
JK
565 .destroy = list_set_destroy,
566 .flush = list_set_flush,
567 .head = list_set_head,
568 .list = list_set_list,
569 .same_set = list_set_same_set,
148fad4d 570 .cancel_gc = list_set_cancel_gc,
3fd6b24a 571};
a96e4fca
ESECJK
572
573static void
0a81ab50 574list_set_gc(GC_ARG)
a96e4fca 575{
0a81ab50 576 INIT_GC_VARS(list_set, map);
d90a2f5b 577
920ddfa0 578 spin_lock_bh(&set->lock);
9b24b406 579 set_cleanup_entries(set);
920ddfa0 580 spin_unlock_bh(&set->lock);
3fd6b24a 581
71b092cb 582 map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ;
3fd6b24a 583 add_timer(&map->gc);
a96e4fca
ESECJK
584}
585
ed3c453a 586static void
0a81ab50 587list_set_gc_init(struct ip_set *set, void (*gc)(GC_ARG))
a96e4fca 588{
3fd6b24a
JK
589 struct list_set *map = set->data;
590
0a81ab50 591 TIMER_SETUP(&map->gc, gc);
b20bcc4d 592 mod_timer(&map->gc, jiffies + IPSET_GC_PERIOD(set->timeout) * HZ);
a96e4fca
ESECJK
593}
594
3fd6b24a
JK
595/* Create list:set type of sets */
596
37fb2c7a 597static bool
bac86fe6 598init_list_set(struct net *net, struct ip_set *set, u32 size)
a96e4fca 599{
3fd6b24a 600 struct list_set *map;
9d317732 601
920ddfa0 602 map = kzalloc(sizeof(*map), GFP_KERNEL);
3fd6b24a 603 if (!map)
37fb2c7a 604 return false;
3fd6b24a
JK
605
606 map->size = size;
bac86fe6 607 map->net = net;
0a81ab50
JK
608#ifdef HAVE_TIMER_SETUP
609 map->set = set;
610#endif
920ddfa0 611 INIT_LIST_HEAD(&map->members);
3fd6b24a
JK
612 set->data = map;
613
37fb2c7a 614 return true;
a96e4fca
ESECJK
615}
616
617static int
bac86fe6
VL
618list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
619 u32 flags)
a96e4fca 620{
37fb2c7a 621 u32 size = IP_SET_LIST_DEFAULT_SIZE;
a96e4fca 622
13f42a71 623 if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
bfd78374
JK
624 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
625 !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
3fd6b24a 626 return -IPSET_ERR_PROTOCOL;
9d317732 627
3fd6b24a
JK
628 if (tb[IPSET_ATTR_SIZE])
629 size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
630 if (size < IP_SET_LIST_MIN_SIZE)
631 size = IP_SET_LIST_MIN_SIZE;
632
9b24b406 633 set->variant = &set_variant;
c7cf6f3b
JK
634 set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem),
635 __alignof__(struct set_elem));
bac86fe6 636 if (!init_list_set(net, set, size))
37fb2c7a
JK
637 return -ENOMEM;
638 if (tb[IPSET_ATTR_TIMEOUT]) {
639 set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
9b24b406 640 list_set_gc_init(set, list_set_gc);
cb76e46e 641 }
3fd6b24a 642 return 0;
a96e4fca
ESECJK
643}
644
7e476b3d 645static struct ip_set_type list_set_type __read_mostly = {
3fd6b24a
JK
646 .name = "list:set",
647 .protocol = IPSET_PROTOCOL,
020936c8 648 .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
3fd6b24a 649 .dimension = IPSET_DIM_ONE,
541e3286 650 .family = NFPROTO_UNSPEC,
76e1fadb
JK
651 .revision_min = IPSET_TYPE_REV_MIN,
652 .revision_max = IPSET_TYPE_REV_MAX,
3fd6b24a 653 .create = list_set_create,
b84145e4
JK
654 .create_policy = {
655 [IPSET_ATTR_SIZE] = { .type = NLA_U32 },
656 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
bfd78374 657 [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
b84145e4
JK
658 },
659 .adt_policy = {
660 [IPSET_ATTR_NAME] = { .type = NLA_STRING,
661 .len = IPSET_MAXNAMELEN },
662 [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
663 .len = IPSET_MAXNAMELEN },
664 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
665 [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
666 [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
bfd78374
JK
667 [IPSET_ATTR_BYTES] = { .type = NLA_U64 },
668 [IPSET_ATTR_PACKETS] = { .type = NLA_U64 },
ae578386
SP
669 [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING,
670 .len = IPSET_MAX_COMMENT_SIZE },
89798a24
AD
671 [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 },
672 [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 },
673 [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 },
b84145e4 674 },
3fd6b24a
JK
675 .me = THIS_MODULE,
676};
a96e4fca 677
3fd6b24a
JK
678static int __init
679list_set_init(void)
680{
681 return ip_set_type_register(&list_set_type);
682}
683
684static void __exit
685list_set_fini(void)
686{
2ca175d7 687 rcu_barrier();
3fd6b24a
JK
688 ip_set_type_unregister(&list_set_type);
689}
a96e4fca 690
3fd6b24a
JK
691module_init(list_set_init);
692module_exit(list_set_fini);