Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ip_set_list_set.c
Go to the documentation of this file.
1 /* Copyright (C) 2008-2011 Jozsef Kadlecsik <[email protected]>
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>
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 <[email protected]>");
25 MODULE_ALIAS("ip_set_list:set");
26 
27 /* Member elements without and with timeout */
28 struct set_elem {
30 };
31 
32 struct set_telem {
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 (i == map->size - 1 && e->id != IPSET_INVALID_ID)
178  /* Last element replaced: e.g. add new,before,last */
180  if (with_timeout(map->timeout))
181  list_elem_tadd(map, i, id, ip_set_timeout_set(timeout));
182  else
183  list_elem_add(map, i, id);
184 
185  return 0;
186 }
187 
188 static int
189 list_set_del(struct list_set *map, u32 i)
190 {
191  struct set_elem *a = list_set_elem(map, i), *b;
192 
194 
195  for (; i < map->size - 1; i++) {
196  b = list_set_elem(map, i + 1);
197  a->id = b->id;
198  if (with_timeout(map->timeout))
199  ((struct set_telem *)a)->timeout =
200  ((struct set_telem *)b)->timeout;
201  a = b;
202  if (a->id == IPSET_INVALID_ID)
203  break;
204  }
205  /* Last element */
206  a->id = IPSET_INVALID_ID;
207  return 0;
208 }
209 
210 static void
211 cleanup_entries(struct list_set *map)
212 {
213  struct set_telem *e;
214  u32 i;
215 
216  for (i = 0; i < map->size; i++) {
217  e = list_set_telem(map, i);
218  if (e->id != IPSET_INVALID_ID && list_set_expired(map, i))
219  list_set_del(map, i);
220  }
221 }
222 
223 static int
224 list_set_uadt(struct ip_set *set, struct nlattr *tb[],
225  enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
226 {
227  struct list_set *map = set->data;
228  bool with_timeout = with_timeout(map->timeout);
229  bool flag_exist = flags & IPSET_FLAG_EXIST;
230  int before = 0;
231  u32 timeout = map->timeout;
233  const struct set_elem *elem;
234  struct ip_set *s;
235  u32 i;
236  int ret = 0;
237 
238  if (unlikely(!tb[IPSET_ATTR_NAME] ||
239  !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
240  !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
241  return -IPSET_ERR_PROTOCOL;
242 
243  if (tb[IPSET_ATTR_LINENO])
244  *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
245 
246  id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
247  if (id == IPSET_INVALID_ID)
248  return -IPSET_ERR_NAME;
249  /* "Loop detection" */
250  if (s->type->features & IPSET_TYPE_NAME) {
251  ret = -IPSET_ERR_LOOP;
252  goto finish;
253  }
254 
255  if (tb[IPSET_ATTR_CADT_FLAGS]) {
256  u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
257  before = f & IPSET_FLAG_BEFORE;
258  }
259 
260  if (before && !tb[IPSET_ATTR_NAMEREF]) {
261  ret = -IPSET_ERR_BEFORE;
262  goto finish;
263  }
264 
265  if (tb[IPSET_ATTR_NAMEREF]) {
266  refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
267  &s);
268  if (refid == IPSET_INVALID_ID) {
269  ret = -IPSET_ERR_NAMEREF;
270  goto finish;
271  }
272  if (!before)
273  before = -1;
274  }
275  if (tb[IPSET_ATTR_TIMEOUT]) {
276  if (!with_timeout) {
277  ret = -IPSET_ERR_TIMEOUT;
278  goto finish;
279  }
280  timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
281  }
282  if (with_timeout && adt != IPSET_TEST)
283  cleanup_entries(map);
284 
285  switch (adt) {
286  case IPSET_TEST:
287  for (i = 0; i < map->size && !ret; i++) {
288  elem = list_set_elem(map, i);
289  if (elem->id == IPSET_INVALID_ID ||
290  (before != 0 && i + 1 >= map->size))
291  break;
292  else if (with_timeout && list_set_expired(map, i))
293  continue;
294  else if (before > 0 && elem->id == id)
295  ret = id_eq_timeout(map, i + 1, refid);
296  else if (before < 0 && elem->id == refid)
297  ret = id_eq_timeout(map, i + 1, id);
298  else if (before == 0 && elem->id == id)
299  ret = 1;
300  }
301  break;
302  case IPSET_ADD:
303  for (i = 0; i < map->size; i++) {
304  elem = list_set_elem(map, i);
305  if (elem->id != id)
306  continue;
307  if (!(with_timeout && flag_exist)) {
308  ret = -IPSET_ERR_EXIST;
309  goto finish;
310  } else {
311  struct set_telem *e = list_set_telem(map, i);
312 
313  if ((before > 1 &&
314  !id_eq(map, i + 1, refid)) ||
315  (before < 0 &&
316  (i == 0 || !id_eq(map, i - 1, refid)))) {
317  ret = -IPSET_ERR_EXIST;
318  goto finish;
319  }
320  e->timeout = ip_set_timeout_set(timeout);
321  ip_set_put_byindex(id);
322  ret = 0;
323  goto finish;
324  }
325  }
326  ret = -IPSET_ERR_LIST_FULL;
327  for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
328  elem = list_set_elem(map, i);
329  if (elem->id == IPSET_INVALID_ID)
330  ret = before != 0 ? -IPSET_ERR_REF_EXIST
331  : list_set_add(map, i, id, timeout);
332  else if (elem->id != refid)
333  continue;
334  else if (before > 0)
335  ret = list_set_add(map, i, id, timeout);
336  else if (i + 1 < map->size)
337  ret = list_set_add(map, i + 1, id, timeout);
338  }
339  break;
340  case IPSET_DEL:
341  ret = -IPSET_ERR_EXIST;
342  for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
343  elem = list_set_elem(map, i);
344  if (elem->id == IPSET_INVALID_ID) {
345  ret = before != 0 ? -IPSET_ERR_REF_EXIST
346  : -IPSET_ERR_EXIST;
347  break;
348  } else if (elem->id == id &&
349  (before == 0 ||
350  (before > 0 && id_eq(map, i + 1, refid))))
351  ret = list_set_del(map, i);
352  else if (elem->id == refid &&
353  before < 0 && id_eq(map, i + 1, id))
354  ret = list_set_del(map, i + 1);
355  }
356  break;
357  default:
358  break;
359  }
360 
361 finish:
362  if (refid != IPSET_INVALID_ID)
363  ip_set_put_byindex(refid);
364  if (adt != IPSET_ADD || ret)
365  ip_set_put_byindex(id);
366 
367  return ip_set_eexist(ret, flags) ? 0 : ret;
368 }
369 
370 static void
371 list_set_flush(struct ip_set *set)
372 {
373  struct list_set *map = set->data;
374  struct set_elem *elem;
375  u32 i;
376 
377  for (i = 0; i < map->size; i++) {
378  elem = list_set_elem(map, i);
379  if (elem->id != IPSET_INVALID_ID) {
380  ip_set_put_byindex(elem->id);
381  elem->id = IPSET_INVALID_ID;
382  }
383  }
384 }
385 
386 static void
387 list_set_destroy(struct ip_set *set)
388 {
389  struct list_set *map = set->data;
390 
391  if (with_timeout(map->timeout))
392  del_timer_sync(&map->gc);
393  list_set_flush(set);
394  kfree(map);
395 
396  set->data = NULL;
397 }
398 
399 static int
400 list_set_head(struct ip_set *set, struct sk_buff *skb)
401 {
402  const struct list_set *map = set->data;
403  struct nlattr *nested;
404 
405  nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
406  if (!nested)
407  goto nla_put_failure;
408  if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) ||
409  (with_timeout(map->timeout) &&
410  nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) ||
411  nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) ||
412  nla_put_net32(skb, IPSET_ATTR_MEMSIZE,
413  htonl(sizeof(*map) + map->size * map->dsize)))
414  goto nla_put_failure;
415  ipset_nest_end(skb, nested);
416 
417  return 0;
418 nla_put_failure:
419  return -EMSGSIZE;
420 }
421 
422 static int
423 list_set_list(const struct ip_set *set,
424  struct sk_buff *skb, struct netlink_callback *cb)
425 {
426  const struct list_set *map = set->data;
427  struct nlattr *atd, *nested;
428  u32 i, first = cb->args[2];
429  const struct set_elem *e;
430 
431  atd = ipset_nest_start(skb, IPSET_ATTR_ADT);
432  if (!atd)
433  return -EMSGSIZE;
434  for (; cb->args[2] < map->size; cb->args[2]++) {
435  i = cb->args[2];
436  e = list_set_elem(map, i);
437  if (e->id == IPSET_INVALID_ID)
438  goto finish;
439  if (with_timeout(map->timeout) && list_set_expired(map, i))
440  continue;
441  nested = ipset_nest_start(skb, IPSET_ATTR_DATA);
442  if (!nested) {
443  if (i == first) {
444  nla_nest_cancel(skb, atd);
445  return -EMSGSIZE;
446  } else
447  goto nla_put_failure;
448  }
449  if (nla_put_string(skb, IPSET_ATTR_NAME,
450  ip_set_name_byindex(e->id)))
451  goto nla_put_failure;
452  if (with_timeout(map->timeout)) {
453  const struct set_telem *te =
454  (const struct set_telem *) e;
455  __be32 to = htonl(ip_set_timeout_get(te->timeout));
456  if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, to))
457  goto nla_put_failure;
458  }
459  ipset_nest_end(skb, nested);
460  }
461 finish:
462  ipset_nest_end(skb, atd);
463  /* Set listing finished */
464  cb->args[2] = 0;
465  return 0;
466 
467 nla_put_failure:
468  nla_nest_cancel(skb, nested);
469  ipset_nest_end(skb, atd);
470  if (unlikely(i == first)) {
471  cb->args[2] = 0;
472  return -EMSGSIZE;
473  }
474  return 0;
475 }
476 
477 static bool
478 list_set_same_set(const struct ip_set *a, const struct ip_set *b)
479 {
480  const struct list_set *x = a->data;
481  const struct list_set *y = b->data;
482 
483  return x->size == y->size &&
484  x->timeout == y->timeout;
485 }
486 
487 static const struct ip_set_type_variant list_set = {
488  .kadt = list_set_kadt,
489  .uadt = list_set_uadt,
490  .destroy = list_set_destroy,
491  .flush = list_set_flush,
492  .head = list_set_head,
493  .list = list_set_list,
494  .same_set = list_set_same_set,
495 };
496 
497 static void
498 list_set_gc(unsigned long ul_set)
499 {
500  struct ip_set *set = (struct ip_set *) ul_set;
501  struct list_set *map = set->data;
502 
503  write_lock_bh(&set->lock);
504  cleanup_entries(map);
505  write_unlock_bh(&set->lock);
506 
507  map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
508  add_timer(&map->gc);
509 }
510 
511 static void
512 list_set_gc_init(struct ip_set *set)
513 {
514  struct list_set *map = set->data;
515 
516  init_timer(&map->gc);
517  map->gc.data = (unsigned long) set;
518  map->gc.function = list_set_gc;
519  map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ;
520  add_timer(&map->gc);
521 }
522 
523 /* Create list:set type of sets */
524 
525 static bool
526 init_list_set(struct ip_set *set, u32 size, size_t dsize,
527  unsigned long timeout)
528 {
529  struct list_set *map;
530  struct set_elem *e;
531  u32 i;
532 
533  map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL);
534  if (!map)
535  return false;
536 
537  map->size = size;
538  map->dsize = dsize;
539  map->timeout = timeout;
540  set->data = map;
541 
542  for (i = 0; i < size; i++) {
543  e = list_set_elem(map, i);
544  e->id = IPSET_INVALID_ID;
545  }
546 
547  return true;
548 }
549 
550 static int
551 list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags)
552 {
554 
555  if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) ||
556  !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT)))
557  return -IPSET_ERR_PROTOCOL;
558 
559  if (tb[IPSET_ATTR_SIZE])
560  size = ip_set_get_h32(tb[IPSET_ATTR_SIZE]);
561  if (size < IP_SET_LIST_MIN_SIZE)
562  size = IP_SET_LIST_MIN_SIZE;
563 
564  if (tb[IPSET_ATTR_TIMEOUT]) {
565  if (!init_list_set(set, size, sizeof(struct set_telem),
566  ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT])))
567  return -ENOMEM;
568 
569  list_set_gc_init(set);
570  } else {
571  if (!init_list_set(set, size, sizeof(struct set_elem),
572  IPSET_NO_TIMEOUT))
573  return -ENOMEM;
574  }
575  set->variant = &list_set;
576  return 0;
577 }
578 
579 static struct ip_set_type list_set_type __read_mostly = {
580  .name = "list:set",
581  .protocol = IPSET_PROTOCOL,
582  .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST,
583  .dimension = IPSET_DIM_ONE,
584  .family = NFPROTO_UNSPEC,
585  .revision_min = REVISION_MIN,
586  .revision_max = REVISION_MAX,
587  .create = list_set_create,
588  .create_policy = {
589  [IPSET_ATTR_SIZE] = { .type = NLA_U32 },
590  [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
591  },
592  .adt_policy = {
593  [IPSET_ATTR_NAME] = { .type = NLA_STRING,
594  .len = IPSET_MAXNAMELEN },
595  [IPSET_ATTR_NAMEREF] = { .type = NLA_STRING,
596  .len = IPSET_MAXNAMELEN },
597  [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 },
598  [IPSET_ATTR_LINENO] = { .type = NLA_U32 },
599  [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
600  },
601  .me = THIS_MODULE,
602 };
603 
604 static int __init
605 list_set_init(void)
606 {
607  return ip_set_type_register(&list_set_type);
608 }
609 
610 static void __exit
611 list_set_fini(void)
612 {
613  ip_set_type_unregister(&list_set_type);
614 }
615 
616 module_init(list_set_init);
617 module_exit(list_set_fini);