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