]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - net/netfilter/ipset/ip_set_list_set.c
netfilter: ipset: list:set set type support
[mirror_ubuntu-hirsute-kernel.git] / net / netfilter / ipset / ip_set_list_set.c
CommitLineData
f830837f
JK
1/* Copyright (C) 2008-2011 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/* Kernel module implementing an IP set type: the list:set type */
9
10#include <linux/module.h>
11#include <linux/ip.h>
12#include <linux/skbuff.h>
13#include <linux/errno.h>
14
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>
18
19MODULE_LICENSE("GPL");
20MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
21MODULE_DESCRIPTION("list:set type of IP sets");
22MODULE_ALIAS("ip_set_list:set");
23
24/* Member elements without and with timeout */
25struct set_elem {
26 ip_set_id_t id;
27};
28
29struct set_telem {
30 ip_set_id_t id;
31 unsigned long timeout;
32};
33
34/* Type structure */
35struct list_set {
36 size_t dsize; /* element size */
37 u32 size; /* size of set list array */
38 u32 timeout; /* timeout value */
39 struct timer_list gc; /* garbage collection */
40 struct set_elem members[0]; /* the set members */
41};
42
43static inline struct set_elem *
44list_set_elem(const struct list_set *map, u32 id)
45{
46 return (struct set_elem *)((char *)map->members + id * map->dsize);
47}
48
49static inline bool
50list_set_timeout(const struct list_set *map, u32 id)
51{
52 const struct set_telem *elem =
53 (const struct set_telem *) list_set_elem(map, id);
54
55 return ip_set_timeout_test(elem->timeout);
56}
57
58static inline bool
59list_set_expired(const struct list_set *map, u32 id)
60{
61 const struct set_telem *elem =
62 (const struct set_telem *) list_set_elem(map, id);
63
64 return ip_set_timeout_expired(elem->timeout);
65}
66
67static inline int
68list_set_exist(const struct set_telem *elem)
69{
70 return elem->id != IPSET_INVALID_ID &&
71 !ip_set_timeout_expired(elem->timeout);
72}
73
74/* Set list without and with timeout */
75
76static int
77list_set_kadt(struct ip_set *set, const struct sk_buff *skb,
78 enum ipset_adt adt, u8 pf, u8 dim, u8 flags)
79{
80 struct list_set *map = set->data;
81 struct set_elem *elem;
82 u32 i;
83 int ret;
84
85 for (i = 0; i < map->size; i++) {
86 elem = list_set_elem(map, i);
87 if (elem->id == IPSET_INVALID_ID)
88 return 0;
89 if (with_timeout(map->timeout) && list_set_expired(map, i))
90 continue;
91 switch (adt) {
92 case IPSET_TEST:
93 ret = ip_set_test(elem->id, skb, pf, dim, flags);
94 if (ret > 0)
95 return ret;
96 break;
97 case IPSET_ADD:
98 ret = ip_set_add(elem->id, skb, pf, dim, flags);
99 if (ret == 0)
100 return ret;
101 break;
102 case IPSET_DEL:
103 ret = ip_set_del(elem->id, skb, pf, dim, flags);
104 if (ret == 0)
105 return ret;
106 break;
107 default:
108 break;
109 }
110 }
111 return -EINVAL;
112}
113
114static bool
115next_id_eq(const struct list_set *map, u32 i, ip_set_id_t id)
116{
117 const struct set_elem *elem;
118
119 if (i + 1 < map->size) {
120 elem = list_set_elem(map, i + 1);
121 return !!(elem->id == id &&
122 !(with_timeout(map->timeout) &&
123 list_set_expired(map, i + 1)));
124 }
125
126 return 0;
127}
128
129static void
130list_elem_add(struct list_set *map, u32 i, ip_set_id_t id)
131{
132 struct set_elem *e;
133
134 for (; i < map->size; i++) {
135 e = list_set_elem(map, i);
136 swap(e->id, id);
137 if (e->id == IPSET_INVALID_ID)
138 break;
139 }
140}
141
142static void
143list_elem_tadd(struct list_set *map, u32 i, ip_set_id_t id,
144 unsigned long timeout)
145{
146 struct set_telem *e;
147
148 for (; i < map->size; i++) {
149 e = (struct set_telem *)list_set_elem(map, i);
150 swap(e->id, id);
151 if (e->id == IPSET_INVALID_ID)
152 break;
153 swap(e->timeout, timeout);
154 }
155}
156
157static int
158list_set_add(struct list_set *map, u32 i, ip_set_id_t id,
159 unsigned long timeout)
160{
161 const struct set_elem *e = list_set_elem(map, i);
162
163 if (i == map->size - 1 && e->id != IPSET_INVALID_ID)
164 /* Last element replaced: e.g. add new,before,last */
165 ip_set_put_byindex(e->id);
166 if (with_timeout(map->timeout))
167 list_elem_tadd(map, i, id, timeout);
168 else
169 list_elem_add(map, i, id);
170
171 return 0;
172}
173
174static int
175list_set_del(struct list_set *map, ip_set_id_t id, u32 i)
176{
177 struct set_elem *a = list_set_elem(map, i), *b;
178
179 ip_set_put_byindex(id);
180
181 for (; i < map->size - 1; i++) {
182 b = list_set_elem(map, i + 1);
183 a->id = b->id;
184 if (with_timeout(map->timeout))
185 ((struct set_telem *)a)->timeout =
186 ((struct set_telem *)b)->timeout;
187 a = b;
188 if (a->id == IPSET_INVALID_ID)
189 break;
190 }
191 /* Last element */
192 a->id = IPSET_INVALID_ID;
193 return 0;
194}
195
196static int
197list_set_uadt(struct ip_set *set, struct nlattr *tb[],
198 enum ipset_adt adt, u32 *lineno, u32 flags)
199{
200 struct list_set *map = set->data;
201 bool with_timeout = with_timeout(map->timeout);
202 int before = 0;
203 u32 timeout = map->timeout;
204 ip_set_id_t id, refid = IPSET_INVALID_ID;
205 const struct set_elem *elem;
206 struct ip_set *s;
207 u32 i;
208 int ret = 0;
209
210 if (unlikely(!tb[IPSET_ATTR_NAME] ||
211 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
212 !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
213 return -IPSET_ERR_PROTOCOL;
214
215 if (tb[IPSET_ATTR_LINENO])
216 *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
217
218 id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
219 if (id == IPSET_INVALID_ID)
220 return -IPSET_ERR_NAME;
221 /* "Loop detection" */
222 if (s->type->features & IPSET_TYPE_NAME) {
223 ret = -IPSET_ERR_LOOP;
224 goto finish;
225 }
226
227 if (tb[IPSET_ATTR_CADT_FLAGS]) {
228 u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
229 before = f & IPSET_FLAG_BEFORE;
230 }
231
232 if (before && !tb[IPSET_ATTR_NAMEREF]) {
233 ret = -IPSET_ERR_BEFORE;
234 goto finish;
235 }
236
237 if (tb[IPSET_ATTR_NAMEREF]) {
238 refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
239 &s);
240 if (refid == IPSET_INVALID_ID) {
241 ret = -IPSET_ERR_NAMEREF;
242 goto finish;
243 }
244 if (!before)
245 before = -1;
246 }
247 if (tb[IPSET_ATTR_TIMEOUT]) {
248 if (!with_timeout) {
249 ret = -IPSET_ERR_TIMEOUT;
250 goto finish;
251 }
252 timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
253 }
254
255 switch (adt) {
256 case IPSET_TEST:
257 for (i = 0; i < map->size && !ret; i++) {
258 elem = list_set_elem(map, i);
259 if (elem->id == IPSET_INVALID_ID ||
260 (before != 0 && i + 1 >= map->size))
261 break;
262 else if (with_timeout && list_set_expired(map, i))
263 continue;
264 else if (before > 0 && elem->id == id)
265 ret = next_id_eq(map, i, refid);
266 else if (before < 0 && elem->id == refid)
267 ret = next_id_eq(map, i, id);
268 else if (before == 0 && elem->id == id)
269 ret = 1;
270 }
271 break;
272 case IPSET_ADD:
273 for (i = 0; i < map->size && !ret; i++) {
274 elem = list_set_elem(map, i);
275 if (elem->id == id &&
276 !(with_timeout && list_set_expired(map, i)))
277 ret = -IPSET_ERR_EXIST;
278 }
279 if (ret == -IPSET_ERR_EXIST)
280 break;
281 ret = -IPSET_ERR_LIST_FULL;
282 for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
283 elem = list_set_elem(map, i);
284 if (elem->id == IPSET_INVALID_ID)
285 ret = before != 0 ? -IPSET_ERR_REF_EXIST
286 : list_set_add(map, i, id, timeout);
287 else if (elem->id != refid)
288 continue;
289 else if (with_timeout && list_set_expired(map, i))
290 ret = -IPSET_ERR_REF_EXIST;
291 else if (before)
292 ret = list_set_add(map, i, id, timeout);
293 else if (i + 1 < map->size)
294 ret = list_set_add(map, i + 1, id, timeout);
295 }
296 break;
297 case IPSET_DEL:
298 ret = -IPSET_ERR_EXIST;
299 for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
300 elem = list_set_elem(map, i);
301 if (elem->id == IPSET_INVALID_ID) {
302 ret = before != 0 ? -IPSET_ERR_REF_EXIST
303 : -IPSET_ERR_EXIST;
304 break;
305 } else if (with_timeout && list_set_expired(map, i))
306 continue;
307 else if (elem->id == id &&
308 (before == 0 ||
309 (before > 0 &&
310 next_id_eq(map, i, refid))))
311 ret = list_set_del(map, id, i);
312 else if (before < 0 &&
313 elem->id == refid &&
314 next_id_eq(map, i, id))
315 ret = list_set_del(map, id, i + 1);
316 }
317 break;
318 default:
319 break;
320 }
321
322finish:
323 if (refid != IPSET_INVALID_ID)
324 ip_set_put_byindex(refid);
325 if (adt != IPSET_ADD || ret)
326 ip_set_put_byindex(id);
327
328 return ip_set_eexist(ret, flags) ? 0 : ret;
329}
330
331static void
332list_set_flush(struct ip_set *set)
333{
334 struct list_set *map = set->data;
335 struct set_elem *elem;
336 u32 i;
337
338 for (i = 0; i < map->size; i++) {
339 elem = list_set_elem(map, i);
340 if (elem->id != IPSET_INVALID_ID) {
341 ip_set_put_byindex(elem->id);
342 elem->id = IPSET_INVALID_ID;
343 }
344 }
345}
346
347static void
348list_set_destroy(struct ip_set *set)
349{
350 struct list_set *map = set->data;
351
352 if (with_timeout(map->timeout))
353 del_timer_sync(&map->gc);
354 list_set_flush(set);
355 kfree(map);
356
357 set->data = NULL;
358}
359
360static int
361list_set_head(struct ip_set *set, struct sk_buff *skb)
362{
363 const struct list_set *map = set->data;
364 struct nlattr *nested;
365
366 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
367 if (!nested)
368 goto nla_put_failure;
369 NLA_PUT_NET32(skb, IPSET_ATTR_SIZE, htonl(map->size));
370 if (with_timeout(map->timeout))
371 NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout));
372 NLA_PUT_NET32(skb, IPSET_ATTR_REFERENCES,
373 htonl(atomic_read(&set->ref) - 1));
374 NLA_PUT_NET32(skb, IPSET_ATTR_MEMSIZE,
375 htonl(sizeof(*map) + map->size * map->dsize));
376 ipset_nest_end(skb, nested);
377
378 return 0;
379nla_put_failure:
380 return -EMSGSIZE;
381}
382
383static int
384list_set_list(const struct ip_set *set,
385 struct sk_buff *skb, struct netlink_callback *cb)
386{
387 const struct list_set *map = set->data;
388 struct nlattr *atd, *nested;
389 u32 i, first = cb->args[2];
390 const struct set_elem *e;
391
392 atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
393 if (!atd)
394 return -EMSGSIZE;
395 for (; cb->args[2] < map->size; cb->args[2]++) {
396 i = cb->args[2];
397 e = list_set_elem(map, i);
398 if (e->id == IPSET_INVALID_ID)
399 goto finish;
400 if (with_timeout(map->timeout) && list_set_expired(map, i))
401 continue;
402 nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
403 if (!nested) {
404 if (i == first) {
405 nla_nest_cancel(skb, atd);
406 return -EMSGSIZE;
407 } else
408 goto nla_put_failure;
409 }
410 NLA_PUT_STRING(skb, IPSET_ATTR_NAME,
411 ip_set_name_byindex(e->id));
412 if (with_timeout(map->timeout)) {
413 const struct set_telem *te =
414 (const struct set_telem *) e;
415 NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT,
416 htonl(ip_set_timeout_get(te->timeout)));
417 }
418 ipset_nest_end(skb, nested);
419 }
420finish:
421 ipset_nest_end(skb, atd);
422 /* Set listing finished */
423 cb->args[2] = 0;
424 return 0;
425
426nla_put_failure:
427 nla_nest_cancel(skb, nested);
428 ipset_nest_end(skb, atd);
429 if (unlikely(i == first)) {
430 cb->args[2] = 0;
431 return -EMSGSIZE;
432 }
433 return 0;
434}
435
436static bool
437list_set_same_set(const struct ip_set *a, const struct ip_set *b)
438{
439 const struct list_set *x = a->data;
440 const struct list_set *y = b->data;
441
442 return x->size == y->size &&
443 x->timeout == y->timeout;
444}
445
446static const struct ip_set_type_variant list_set = {
447 .kadt = list_set_kadt,
448 .uadt = list_set_uadt,
449 .destroy = list_set_destroy,
450 .flush = list_set_flush,
451 .head = list_set_head,
452 .list = list_set_list,
453 .same_set = list_set_same_set,
454};
455
456static void
457list_set_gc(unsigned long ul_set)
458{
459 struct ip_set *set = (struct ip_set *) ul_set;
460 struct list_set *map = set->data;
461 struct set_telem *e;
462 u32 i;
463
464 /* We run parallel with other readers (test element)
465 * but adding/deleting new entries is locked out */
466 read_lock_bh(&set->lock);
467 for (i = map->size - 1; i >= 0; i--) {
468 e = (struct set_telem *) list_set_elem(map, i);
469 if (e->id != IPSET_INVALID_ID &&
470 list_set_expired(map, i))
471 list_set_del(map, e->id, i);
472 }
473 read_unlock_bh(&set->lock);
474
475 map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
476 add_timer(&map->gc);
477}
478
479static void
480list_set_gc_init(struct ip_set *set)
481{
482 struct list_set *map = set->data;
483
484 init_timer(&map->gc);
485 map->gc.data = (unsigned long) set;
486 map->gc.function = list_set_gc;
487 map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
488 add_timer(&map->gc);
489}
490
491/* Create list:set type of sets */
492
493static bool
494init_list_set(struct ip_set *set, u32 size, size_t dsize,
495 unsigned long timeout)
496{
497 struct list_set *map;
498 struct set_elem *e;
499 u32 i;
500
501 map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
502 if (!map)
503 return false;
504
505 map->size = size;
506 map->dsize = dsize;
507 map->timeout = timeout;
508 set->data = map;
509
510 for (i = 0; i < size; i++) {
511 e = list_set_elem(map, i);
512 e->id = IPSET_INVALID_ID;
513 }
514
515 return true;
516}
517
518static int
519list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
520{
521 u32 size = IP_SET_LIST_DEFAULT_SIZE;
522
523 if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
524 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
525 return -IPSET_ERR_PROTOCOL;
526
527 if (tb[IPSET_ATTR_SIZE])
528 size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
529 if (size < IP_SET_LIST_MIN_SIZE)
530 size = IP_SET_LIST_MIN_SIZE;
531
532 if (tb[IPSET_ATTR_TIMEOUT]) {
533 if (!init_list_set(set, size, sizeof(struct set_telem),
534 ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
535 return -ENOMEM;
536
537 list_set_gc_init(set);
538 } else {
539 if (!init_list_set(set, size, sizeof(struct set_elem),
540 IPSET_NO_TIMEOUT))
541 return -ENOMEM;
542 }
543 set->variant = &list_set;
544 return 0;
545}
546
547static struct ip_set_type list_set_type __read_mostly = {
548 .name = "list:set",
549 .protocol = IPSET_PROTOCOL,
550 .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
551 .dimension = IPSET_DIM_ONE,
552 .family = AF_UNSPEC,
553 .revision = 0,
554 .create = list_set_create,
555 .create_policy = {
556 [IPSET_ATTR_SIZE] = { .type = NLA_U32 },
557 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
558 },
559 .adt_policy = {
560 [IPSET_ATTR_NAME] = { .type = NLA_STRING,
561 .len = IPSET_MAXNAMELEN },
562 [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
563 .len = IPSET_MAXNAMELEN },
564 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
565 [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
566 [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
567 },
568 .me = THIS_MODULE,
569};
570
571static int __init
572list_set_init(void)
573{
574 return ip_set_type_register(&list_set_type);
575}
576
577static void __exit
578list_set_fini(void)
579{
580 ip_set_type_unregister(&list_set_type);
581}
582
583module_init(list_set_init);
584module_exit(list_set_fini);