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