]> Pileus Git - ~andy/linux/blob - net/netfilter/xt_set.c
Merge branch 'x86-reboot-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[~andy/linux] / net / netfilter / xt_set.c
1 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
2  *                         Patrick Schaaf <bof@bof.de>
3  *                         Martin Josefsson <gandalf@wlug.westbo.se>
4  * Copyright (C) 2003-2011 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
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>
19 #include <linux/netfilter/ipset/ip_set_timeout.h>
20
21 MODULE_LICENSE("GPL");
22 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
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
71 compat_flags(struct xt_set_info_v0 *info)
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;
90         ip_set_id_t index;
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;
141         ip_set_id_t index;
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;
208         ip_set_id_t index;
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;
256         ip_set_id_t index;
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 };
360
361 static struct xt_target set_targets[] __read_mostly = {
362         {
363                 .name           = "SET",
364                 .revision       = 0,
365                 .family         = NFPROTO_IPV4,
366                 .target         = set_target_v0,
367                 .targetsize     = sizeof(struct xt_set_info_target_v0),
368                 .checkentry     = set_target_v0_checkentry,
369                 .destroy        = set_target_v0_destroy,
370                 .me             = THIS_MODULE
371         },
372         {
373                 .name           = "SET",
374                 .revision       = 1,
375                 .family         = NFPROTO_IPV4,
376                 .target         = set_target_v1,
377                 .targetsize     = sizeof(struct xt_set_info_target_v1),
378                 .checkentry     = set_target_v1_checkentry,
379                 .destroy        = set_target_v1_destroy,
380                 .me             = THIS_MODULE
381         },
382         {
383                 .name           = "SET",
384                 .revision       = 1,
385                 .family         = NFPROTO_IPV6,
386                 .target         = set_target_v1,
387                 .targetsize     = sizeof(struct xt_set_info_target_v1),
388                 .checkentry     = set_target_v1_checkentry,
389                 .destroy        = set_target_v1_destroy,
390                 .me             = THIS_MODULE
391         },
392         {
393                 .name           = "SET",
394                 .revision       = 2,
395                 .family         = NFPROTO_IPV4,
396                 .target         = set_target_v2,
397                 .targetsize     = sizeof(struct xt_set_info_target_v2),
398                 .checkentry     = set_target_v2_checkentry,
399                 .destroy        = set_target_v2_destroy,
400                 .me             = THIS_MODULE
401         },
402         {
403                 .name           = "SET",
404                 .revision       = 2,
405                 .family         = NFPROTO_IPV6,
406                 .target         = set_target_v2,
407                 .targetsize     = sizeof(struct xt_set_info_target_v2),
408                 .checkentry     = set_target_v2_checkentry,
409                 .destroy        = set_target_v2_destroy,
410                 .me             = THIS_MODULE
411         },
412 };
413
414 static int __init xt_set_init(void)
415 {
416         int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
417
418         if (!ret) {
419                 ret = xt_register_targets(set_targets,
420                                           ARRAY_SIZE(set_targets));
421                 if (ret)
422                         xt_unregister_matches(set_matches,
423                                               ARRAY_SIZE(set_matches));
424         }
425         return ret;
426 }
427
428 static void __exit xt_set_fini(void)
429 {
430         xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
431         xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
432 }
433
434 module_init(xt_set_init);
435 module_exit(xt_set_fini);