Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
xt_set.c
Go to the documentation of this file.
1 /* Copyright (C) 2000-2002 Joakim Axelsson <[email protected]>
2  * Patrick Schaaf <[email protected]>
3  * Martin Josefsson <[email protected]>
4  * Copyright (C) 2003-2011 Jozsef Kadlecsik <[email protected]>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10 
11 /* Kernel module which implements the set match and SET target
12  * for netfilter/iptables. */
13 
14 #include <linux/module.h>
15 #include <linux/skbuff.h>
16 
17 #include <linux/netfilter/x_tables.h>
18 #include <linux/netfilter/xt_set.h>
20 
21 MODULE_LICENSE("GPL");
22 MODULE_AUTHOR("Jozsef Kadlecsik <[email protected]>");
23 MODULE_DESCRIPTION("Xtables: IP set match and target module");
24 MODULE_ALIAS("xt_SET");
25 MODULE_ALIAS("ipt_set");
26 MODULE_ALIAS("ip6t_set");
27 MODULE_ALIAS("ipt_SET");
28 MODULE_ALIAS("ip6t_SET");
29 
30 static inline int
31 match_set(ip_set_id_t index, const struct sk_buff *skb,
32  const struct xt_action_param *par,
33  const struct ip_set_adt_opt *opt, int inv)
34 {
35  if (ip_set_test(index, skb, par, opt))
36  inv = !inv;
37  return inv;
38 }
39 
40 #define ADT_OPT(n, f, d, fs, cfs, t) \
41 const struct ip_set_adt_opt n = { \
42  .family = f, \
43  .dim = d, \
44  .flags = fs, \
45  .cmdflags = cfs, \
46  .timeout = t, \
47 }
48 #define ADT_MOPT(n, f, d, fs, cfs, t) \
49 struct ip_set_adt_opt n = { \
50  .family = f, \
51  .dim = d, \
52  .flags = fs, \
53  .cmdflags = cfs, \
54  .timeout = t, \
55 }
56 
57 /* Revision 0 interface: backward compatible with netfilter/iptables */
58 
59 static bool
60 set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
61 {
62  const struct xt_set_info_match_v0 *info = par->matchinfo;
63  ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
64  info->match_set.u.compat.flags, 0, UINT_MAX);
65 
66  return match_set(info->match_set.index, skb, par, &opt,
67  info->match_set.u.compat.flags & IPSET_INV_MATCH);
68 }
69 
70 static void
72 {
73  u_int8_t i;
74 
75  /* Fill out compatibility data according to enum ip_set_kopt */
76  info->u.compat.dim = IPSET_DIM_ZERO;
77  if (info->u.flags[0] & IPSET_MATCH_INV)
78  info->u.compat.flags |= IPSET_INV_MATCH;
79  for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) {
80  info->u.compat.dim++;
81  if (info->u.flags[i] & IPSET_SRC)
82  info->u.compat.flags |= (1<<info->u.compat.dim);
83  }
84 }
85 
86 static int
87 set_match_v0_checkentry(const struct xt_mtchk_param *par)
88 {
89  struct xt_set_info_match_v0 *info = par->matchinfo;
91 
92  index = ip_set_nfnl_get_byindex(info->match_set.index);
93 
94  if (index == IPSET_INVALID_ID) {
95  pr_warning("Cannot find set indentified by id %u to match\n",
96  info->match_set.index);
97  return -ENOENT;
98  }
99  if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
100  pr_warning("Protocol error: set match dimension "
101  "is over the limit!\n");
102  ip_set_nfnl_put(info->match_set.index);
103  return -ERANGE;
104  }
105 
106  /* Fill out compatibility data */
107  compat_flags(&info->match_set);
108 
109  return 0;
110 }
111 
112 static void
113 set_match_v0_destroy(const struct xt_mtdtor_param *par)
114 {
115  struct xt_set_info_match_v0 *info = par->matchinfo;
116 
117  ip_set_nfnl_put(info->match_set.index);
118 }
119 
120 static unsigned int
121 set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
122 {
123  const struct xt_set_info_target_v0 *info = par->targinfo;
124  ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
125  info->add_set.u.compat.flags, 0, UINT_MAX);
126  ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
127  info->del_set.u.compat.flags, 0, UINT_MAX);
128 
129  if (info->add_set.index != IPSET_INVALID_ID)
130  ip_set_add(info->add_set.index, skb, par, &add_opt);
131  if (info->del_set.index != IPSET_INVALID_ID)
132  ip_set_del(info->del_set.index, skb, par, &del_opt);
133 
134  return XT_CONTINUE;
135 }
136 
137 static int
138 set_target_v0_checkentry(const struct xt_tgchk_param *par)
139 {
140  struct xt_set_info_target_v0 *info = par->targinfo;
142 
143  if (info->add_set.index != IPSET_INVALID_ID) {
144  index = ip_set_nfnl_get_byindex(info->add_set.index);
145  if (index == IPSET_INVALID_ID) {
146  pr_warning("Cannot find add_set index %u as target\n",
147  info->add_set.index);
148  return -ENOENT;
149  }
150  }
151 
152  if (info->del_set.index != IPSET_INVALID_ID) {
153  index = ip_set_nfnl_get_byindex(info->del_set.index);
154  if (index == IPSET_INVALID_ID) {
155  pr_warning("Cannot find del_set index %u as target\n",
156  info->del_set.index);
157  if (info->add_set.index != IPSET_INVALID_ID)
158  ip_set_nfnl_put(info->add_set.index);
159  return -ENOENT;
160  }
161  }
162  if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
163  info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
164  pr_warning("Protocol error: SET target dimension "
165  "is over the limit!\n");
166  if (info->add_set.index != IPSET_INVALID_ID)
167  ip_set_nfnl_put(info->add_set.index);
168  if (info->del_set.index != IPSET_INVALID_ID)
169  ip_set_nfnl_put(info->del_set.index);
170  return -ERANGE;
171  }
172 
173  /* Fill out compatibility data */
174  compat_flags(&info->add_set);
175  compat_flags(&info->del_set);
176 
177  return 0;
178 }
179 
180 static void
181 set_target_v0_destroy(const struct xt_tgdtor_param *par)
182 {
183  const struct xt_set_info_target_v0 *info = par->targinfo;
184 
185  if (info->add_set.index != IPSET_INVALID_ID)
186  ip_set_nfnl_put(info->add_set.index);
187  if (info->del_set.index != IPSET_INVALID_ID)
188  ip_set_nfnl_put(info->del_set.index);
189 }
190 
191 /* Revision 1 match and target */
192 
193 static bool
194 set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
195 {
196  const struct xt_set_info_match_v1 *info = par->matchinfo;
197  ADT_OPT(opt, par->family, info->match_set.dim,
198  info->match_set.flags, 0, UINT_MAX);
199 
200  return match_set(info->match_set.index, skb, par, &opt,
201  info->match_set.flags & IPSET_INV_MATCH);
202 }
203 
204 static int
205 set_match_v1_checkentry(const struct xt_mtchk_param *par)
206 {
207  struct xt_set_info_match_v1 *info = par->matchinfo;
209 
210  index = ip_set_nfnl_get_byindex(info->match_set.index);
211 
212  if (index == IPSET_INVALID_ID) {
213  pr_warning("Cannot find set indentified by id %u to match\n",
214  info->match_set.index);
215  return -ENOENT;
216  }
217  if (info->match_set.dim > IPSET_DIM_MAX) {
218  pr_warning("Protocol error: set match dimension "
219  "is over the limit!\n");
220  ip_set_nfnl_put(info->match_set.index);
221  return -ERANGE;
222  }
223 
224  return 0;
225 }
226 
227 static void
228 set_match_v1_destroy(const struct xt_mtdtor_param *par)
229 {
230  struct xt_set_info_match_v1 *info = par->matchinfo;
231 
232  ip_set_nfnl_put(info->match_set.index);
233 }
234 
235 static unsigned int
236 set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
237 {
238  const struct xt_set_info_target_v1 *info = par->targinfo;
239  ADT_OPT(add_opt, par->family, info->add_set.dim,
240  info->add_set.flags, 0, UINT_MAX);
241  ADT_OPT(del_opt, par->family, info->del_set.dim,
242  info->del_set.flags, 0, UINT_MAX);
243 
244  if (info->add_set.index != IPSET_INVALID_ID)
245  ip_set_add(info->add_set.index, skb, par, &add_opt);
246  if (info->del_set.index != IPSET_INVALID_ID)
247  ip_set_del(info->del_set.index, skb, par, &del_opt);
248 
249  return XT_CONTINUE;
250 }
251 
252 static int
253 set_target_v1_checkentry(const struct xt_tgchk_param *par)
254 {
255  const struct xt_set_info_target_v1 *info = par->targinfo;
257 
258  if (info->add_set.index != IPSET_INVALID_ID) {
259  index = ip_set_nfnl_get_byindex(info->add_set.index);
260  if (index == IPSET_INVALID_ID) {
261  pr_warning("Cannot find add_set index %u as target\n",
262  info->add_set.index);
263  return -ENOENT;
264  }
265  }
266 
267  if (info->del_set.index != IPSET_INVALID_ID) {
268  index = ip_set_nfnl_get_byindex(info->del_set.index);
269  if (index == IPSET_INVALID_ID) {
270  pr_warning("Cannot find del_set index %u as target\n",
271  info->del_set.index);
272  if (info->add_set.index != IPSET_INVALID_ID)
273  ip_set_nfnl_put(info->add_set.index);
274  return -ENOENT;
275  }
276  }
277  if (info->add_set.dim > IPSET_DIM_MAX ||
278  info->del_set.dim > IPSET_DIM_MAX) {
279  pr_warning("Protocol error: SET target dimension "
280  "is over the limit!\n");
281  if (info->add_set.index != IPSET_INVALID_ID)
282  ip_set_nfnl_put(info->add_set.index);
283  if (info->del_set.index != IPSET_INVALID_ID)
284  ip_set_nfnl_put(info->del_set.index);
285  return -ERANGE;
286  }
287 
288  return 0;
289 }
290 
291 static void
292 set_target_v1_destroy(const struct xt_tgdtor_param *par)
293 {
294  const struct xt_set_info_target_v1 *info = par->targinfo;
295 
296  if (info->add_set.index != IPSET_INVALID_ID)
297  ip_set_nfnl_put(info->add_set.index);
298  if (info->del_set.index != IPSET_INVALID_ID)
299  ip_set_nfnl_put(info->del_set.index);
300 }
301 
302 /* Revision 2 target */
303 
304 static unsigned int
305 set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
306 {
307  const struct xt_set_info_target_v2 *info = par->targinfo;
308  ADT_MOPT(add_opt, par->family, info->add_set.dim,
309  info->add_set.flags, info->flags, info->timeout);
310  ADT_OPT(del_opt, par->family, info->del_set.dim,
311  info->del_set.flags, 0, UINT_MAX);
312 
313  /* Normalize to fit into jiffies */
314  if (add_opt.timeout != IPSET_NO_TIMEOUT &&
315  add_opt.timeout > UINT_MAX/MSEC_PER_SEC)
316  add_opt.timeout = UINT_MAX/MSEC_PER_SEC;
317  if (info->add_set.index != IPSET_INVALID_ID)
318  ip_set_add(info->add_set.index, skb, par, &add_opt);
319  if (info->del_set.index != IPSET_INVALID_ID)
320  ip_set_del(info->del_set.index, skb, par, &del_opt);
321 
322  return XT_CONTINUE;
323 }
324 
325 #define set_target_v2_checkentry set_target_v1_checkentry
326 #define set_target_v2_destroy set_target_v1_destroy
327 
328 static struct xt_match set_matches[] __read_mostly = {
329  {
330  .name = "set",
331  .family = NFPROTO_IPV4,
332  .revision = 0,
333  .match = set_match_v0,
334  .matchsize = sizeof(struct xt_set_info_match_v0),
335  .checkentry = set_match_v0_checkentry,
336  .destroy = set_match_v0_destroy,
337  .me = THIS_MODULE
338  },
339  {
340  .name = "set",
341  .family = NFPROTO_IPV4,
342  .revision = 1,
343  .match = set_match_v1,
344  .matchsize = sizeof(struct xt_set_info_match_v1),
345  .checkentry = set_match_v1_checkentry,
346  .destroy = set_match_v1_destroy,
347  .me = THIS_MODULE
348  },
349  {
350  .name = "set",
351  .family = NFPROTO_IPV6,
352  .revision = 1,
353  .match = set_match_v1,
354  .matchsize = sizeof(struct xt_set_info_match_v1),
355  .checkentry = set_match_v1_checkentry,
356  .destroy = set_match_v1_destroy,
357  .me = THIS_MODULE
358  },
359  /* --return-nomatch flag support */
360  {
361  .name = "set",
362  .family = NFPROTO_IPV4,
363  .revision = 2,
364  .match = set_match_v1,
365  .matchsize = sizeof(struct xt_set_info_match_v1),
366  .checkentry = set_match_v1_checkentry,
367  .destroy = set_match_v1_destroy,
368  .me = THIS_MODULE
369  },
370  {
371  .name = "set",
372  .family = NFPROTO_IPV6,
373  .revision = 2,
374  .match = set_match_v1,
375  .matchsize = sizeof(struct xt_set_info_match_v1),
376  .checkentry = set_match_v1_checkentry,
377  .destroy = set_match_v1_destroy,
378  .me = THIS_MODULE
379  },
380 };
381 
382 static struct xt_target set_targets[] __read_mostly = {
383  {
384  .name = "SET",
385  .revision = 0,
386  .family = NFPROTO_IPV4,
387  .target = set_target_v0,
388  .targetsize = sizeof(struct xt_set_info_target_v0),
389  .checkentry = set_target_v0_checkentry,
390  .destroy = set_target_v0_destroy,
391  .me = THIS_MODULE
392  },
393  {
394  .name = "SET",
395  .revision = 1,
396  .family = NFPROTO_IPV4,
397  .target = set_target_v1,
398  .targetsize = sizeof(struct xt_set_info_target_v1),
399  .checkentry = set_target_v1_checkentry,
400  .destroy = set_target_v1_destroy,
401  .me = THIS_MODULE
402  },
403  {
404  .name = "SET",
405  .revision = 1,
406  .family = NFPROTO_IPV6,
407  .target = set_target_v1,
408  .targetsize = sizeof(struct xt_set_info_target_v1),
409  .checkentry = set_target_v1_checkentry,
410  .destroy = set_target_v1_destroy,
411  .me = THIS_MODULE
412  },
413  /* --timeout and --exist flags support */
414  {
415  .name = "SET",
416  .revision = 2,
417  .family = NFPROTO_IPV4,
418  .target = set_target_v2,
419  .targetsize = sizeof(struct xt_set_info_target_v2),
420  .checkentry = set_target_v2_checkentry,
421  .destroy = set_target_v2_destroy,
422  .me = THIS_MODULE
423  },
424  {
425  .name = "SET",
426  .revision = 2,
427  .family = NFPROTO_IPV6,
428  .target = set_target_v2,
429  .targetsize = sizeof(struct xt_set_info_target_v2),
430  .checkentry = set_target_v2_checkentry,
431  .destroy = set_target_v2_destroy,
432  .me = THIS_MODULE
433  },
434 };
435 
436 static int __init xt_set_init(void)
437 {
438  int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
439 
440  if (!ret) {
441  ret = xt_register_targets(set_targets,
442  ARRAY_SIZE(set_targets));
443  if (ret)
444  xt_unregister_matches(set_matches,
445  ARRAY_SIZE(set_matches));
446  }
447  return ret;
448 }
449 
450 static void __exit xt_set_fini(void)
451 {
452  xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
453  xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
454 }
455 
456 module_init(xt_set_init);
457 module_exit(xt_set_fini);