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