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