Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
[GitHub/mt8127/android_kernel_alcatel_ttab.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 #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 /* --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);