aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJozsef Kadlecsik <kadlec@blackhole.kfki.hu>2011-02-01 09:54:59 -0500
committerPatrick McHardy <kaber@trash.net>2011-02-01 09:54:59 -0500
commitf830837f0eed0f9e371b8fd65169365780814bb1 (patch)
treef83646838333cf2526270aa23320f88171e6fa35
parent21f45020a3084f80fcdd5f056a0c6389f5406399 (diff)
netfilter: ipset: list:set set type support
The module implements the list:set type support in two flavours: without and with timeout. The sets has two sides: for the userspace, they store the names of other (non list:set type of) sets: one can add, delete and test set names. For the kernel, it forms an ordered union of the member sets: the members sets are tried in order when elements are added, deleted and tested and the process stops at the first success. Signed-off-by: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r--include/linux/netfilter/ipset/ip_set_list.h27
-rw-r--r--net/netfilter/ipset/Kconfig10
-rw-r--r--net/netfilter/ipset/Makefile3
-rw-r--r--net/netfilter/ipset/ip_set_list_set.c584
4 files changed, 624 insertions, 0 deletions
diff --git a/include/linux/netfilter/ipset/ip_set_list.h b/include/linux/netfilter/ipset/ip_set_list.h
new file mode 100644
index 000000000000..40a63f302613
--- /dev/null
+++ b/include/linux/netfilter/ipset/ip_set_list.h
@@ -0,0 +1,27 @@
1#ifndef __IP_SET_LIST_H
2#define __IP_SET_LIST_H
3
4/* List type specific error codes */
5enum {
6 /* Set name to be added/deleted/tested does not exist. */
7 IPSET_ERR_NAME = IPSET_ERR_TYPE_SPECIFIC,
8 /* list:set type is not permitted to add */
9 IPSET_ERR_LOOP,
10 /* Missing reference set */
11 IPSET_ERR_BEFORE,
12 /* Reference set does not exist */
13 IPSET_ERR_NAMEREF,
14 /* Set is full */
15 IPSET_ERR_LIST_FULL,
16 /* Reference set is not added to the set */
17 IPSET_ERR_REF_EXIST,
18};
19
20#ifdef __KERNEL__
21
22#define IP_SET_LIST_DEFAULT_SIZE 8
23#define IP_SET_LIST_MIN_SIZE 4
24
25#endif /* __KERNEL__ */
26
27#endif /* __IP_SET_LIST_H */
diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig
index 2512e7b82d12..3b970d343023 100644
--- a/net/netfilter/ipset/Kconfig
+++ b/net/netfilter/ipset/Kconfig
@@ -108,4 +108,14 @@ config IP_SET_HASH_NETPORT
108 108
109 To compile it as a module, choose M here. If unsure, say N. 109 To compile it as a module, choose M here. If unsure, say N.
110 110
111config IP_SET_LIST_SET
112 tristate "list:set set support"
113 depends on IP_SET
114 help
115 This option adds the list:set set type support. In this
116 kind of set one can store the name of other sets and it forms
117 an ordered union of the member sets.
118
119 To compile it as a module, choose M here. If unsure, say N.
120
111endif # IP_SET 121endif # IP_SET
diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile
index fbbebd62bb2a..5adbdab67bd2 100644
--- a/net/netfilter/ipset/Makefile
+++ b/net/netfilter/ipset/Makefile
@@ -19,3 +19,6 @@ obj-$(CONFIG_IP_SET_HASH_IPPORTIP) += ip_set_hash_ipportip.o
19obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o 19obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o
20obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o 20obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o
21obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o 21obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o
22
23# list types
24obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o
diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c
new file mode 100644
index 000000000000..a47c32982f06
--- /dev/null
+++ b/net/netfilter/ipset/ip_set_list_set.c
@@ -0,0 +1,584 @@
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);