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