Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
[pandora-kernel.git] / net / netfilter / ipset / ip_set_list_set.c
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
19 MODULE_LICENSE("GPL");
20 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
21 MODULE_DESCRIPTION("list:set type of IP sets");
22 MODULE_ALIAS("ip_set_list:set");
23
24 /* Member elements without and with timeout */
25 struct set_elem {
26         ip_set_id_t id;
27 };
28
29 struct set_telem {
30         ip_set_id_t id;
31         unsigned long timeout;
32 };
33
34 /* Type structure */
35 struct 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
43 static inline struct set_elem *
44 list_set_elem(const struct list_set *map, u32 id)
45 {
46         return (struct set_elem *)((char *)map->members + id * map->dsize);
47 }
48
49 static inline bool
50 list_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
58 static inline bool
59 list_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
67 static inline int
68 list_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
76 static int
77 list_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
114 static bool
115 next_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
129 static void
130 list_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
142 static void
143 list_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
157 static int
158 list_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
174 static int
175 list_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
196 static int
197 list_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
322 finish:
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
331 static void
332 list_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
347 static void
348 list_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
360 static int
361 list_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;
379 nla_put_failure:
380         return -EMSGSIZE;
381 }
382
383 static int
384 list_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         }
420 finish:
421         ipset_nest_end(skb, atd);
422         /* Set listing finished */
423         cb->args[2] = 0;
424         return 0;
425
426 nla_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
436 static bool
437 list_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
446 static 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
456 static void
457 list_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
479 static void
480 list_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
493 static bool
494 init_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
518 static int
519 list_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
547 static 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
571 static int __init
572 list_set_init(void)
573 {
574         return ip_set_type_register(&list_set_type);
575 }
576
577 static void __exit
578 list_set_fini(void)
579 {
580         ip_set_type_unregister(&list_set_type);
581 }
582
583 module_init(list_set_init);
584 module_exit(list_set_fini);