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
CommitLineData
d956798d
JK
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>
d956798d
JK
16
17#include <linux/netfilter/x_tables.h>
18#include <linux/netfilter/xt_set.h>
a73f89a6 19#include <linux/netfilter/ipset/ip_set_timeout.h>
d956798d
JK
20
21MODULE_LICENSE("GPL");
22MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
23MODULE_DESCRIPTION("Xtables: IP set match and target module");
24MODULE_ALIAS("xt_SET");
25MODULE_ALIAS("ipt_set");
26MODULE_ALIAS("ip6t_set");
27MODULE_ALIAS("ipt_SET");
28MODULE_ALIAS("ip6t_SET");
29
30static inline int
31match_set(ip_set_id_t index, const struct sk_buff *skb,
b66554cf 32 const struct xt_action_param *par,
ac8cc925 33 const struct ip_set_adt_opt *opt, int inv)
d956798d 34{
b66554cf 35 if (ip_set_test(index, skb, par, opt))
d956798d
JK
36 inv = !inv;
37 return inv;
38}
39
15b4d93f 40#define ADT_OPT(n, f, d, fs, cfs, t) \
ac8cc925
JK
41const struct ip_set_adt_opt n = { \
42 .family = f, \
43 .dim = d, \
44 .flags = fs, \
45 .cmdflags = cfs, \
46 .timeout = t, \
47}
127f5591
JK
48#define ADT_MOPT(n, f, d, fs, cfs, t) \
49struct ip_set_adt_opt n = { \
50 .family = f, \
51 .dim = d, \
52 .flags = fs, \
53 .cmdflags = cfs, \
54 .timeout = t, \
55}
ac8cc925 56
d956798d
JK
57/* Revision 0 interface: backward compatible with netfilter/iptables */
58
59static bool
60set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
61{
62 const struct xt_set_info_match_v0 *info = par->matchinfo;
ac8cc925
JK
63 ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
64 info->match_set.u.compat.flags, 0, UINT_MAX);
d956798d 65
b66554cf 66 return match_set(info->match_set.index, skb, par, &opt,
d956798d
JK
67 info->match_set.u.compat.flags & IPSET_INV_MATCH);
68}
69
70static void
71compat_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
86static int
87set_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");
eafbd3fd 102 ip_set_nfnl_put(info->match_set.index);
d956798d
JK
103 return -ERANGE;
104 }
105
106 /* Fill out compatibility data */
107 compat_flags(&info->match_set);
108
109 return 0;
110}
111
112static void
113set_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
120static unsigned int
121set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
122{
123 const struct xt_set_info_target_v0 *info = par->targinfo;
ac8cc925
JK
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);
d956798d
JK
128
129 if (info->add_set.index != IPSET_INVALID_ID)
b66554cf 130 ip_set_add(info->add_set.index, skb, par, &add_opt);
d956798d 131 if (info->del_set.index != IPSET_INVALID_ID)
b66554cf 132 ip_set_del(info->del_set.index, skb, par, &del_opt);
d956798d
JK
133
134 return XT_CONTINUE;
135}
136
137static int
138set_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);
eafbd3fd
JK
157 if (info->add_set.index != IPSET_INVALID_ID)
158 ip_set_nfnl_put(info->add_set.index);
d956798d
JK
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");
eafbd3fd
JK
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);
d956798d
JK
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
180static void
181set_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
ac8cc925 191/* Revision 1 match and target */
d956798d
JK
192
193static bool
ac8cc925 194set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
d956798d 195{
ac8cc925
JK
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);
d956798d 199
b66554cf 200 return match_set(info->match_set.index, skb, par, &opt,
d956798d
JK
201 info->match_set.flags & IPSET_INV_MATCH);
202}
203
204static int
ac8cc925 205set_match_v1_checkentry(const struct xt_mtchk_param *par)
d956798d 206{
ac8cc925 207 struct xt_set_info_match_v1 *info = par->matchinfo;
d956798d
JK
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");
eafbd3fd 220 ip_set_nfnl_put(info->match_set.index);
d956798d
JK
221 return -ERANGE;
222 }
223
224 return 0;
225}
226
227static void
ac8cc925 228set_match_v1_destroy(const struct xt_mtdtor_param *par)
d956798d 229{
ac8cc925 230 struct xt_set_info_match_v1 *info = par->matchinfo;
d956798d
JK
231
232 ip_set_nfnl_put(info->match_set.index);
233}
234
235static unsigned int
ac8cc925 236set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
d956798d 237{
ac8cc925
JK
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);
d956798d
JK
243
244 if (info->add_set.index != IPSET_INVALID_ID)
b66554cf 245 ip_set_add(info->add_set.index, skb, par, &add_opt);
d956798d 246 if (info->del_set.index != IPSET_INVALID_ID)
b66554cf 247 ip_set_del(info->del_set.index, skb, par, &del_opt);
d956798d
JK
248
249 return XT_CONTINUE;
250}
251
252static int
ac8cc925 253set_target_v1_checkentry(const struct xt_tgchk_param *par)
d956798d 254{
ac8cc925 255 const struct xt_set_info_target_v1 *info = par->targinfo;
d956798d
JK
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);
eafbd3fd
JK
272 if (info->add_set.index != IPSET_INVALID_ID)
273 ip_set_nfnl_put(info->add_set.index);
d956798d
JK
274 return -ENOENT;
275 }
276 }
277 if (info->add_set.dim > IPSET_DIM_MAX ||
eafbd3fd 278 info->del_set.dim > IPSET_DIM_MAX) {
d956798d
JK
279 pr_warning("Protocol error: SET target dimension "
280 "is over the limit!\n");
eafbd3fd
JK
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);
d956798d
JK
285 return -ERANGE;
286 }
287
288 return 0;
289}
290
291static void
ac8cc925 292set_target_v1_destroy(const struct xt_tgdtor_param *par)
d956798d 293{
ac8cc925 294 const struct xt_set_info_target_v1 *info = par->targinfo;
d956798d
JK
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
ac8cc925
JK
302/* Revision 2 target */
303
304static unsigned int
305set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
306{
307 const struct xt_set_info_target_v2 *info = par->targinfo;
127f5591
JK
308 ADT_MOPT(add_opt, par->family, info->add_set.dim,
309 info->add_set.flags, info->flags, info->timeout);
ac8cc925
JK
310 ADT_OPT(del_opt, par->family, info->del_set.dim,
311 info->del_set.flags, 0, UINT_MAX);
312
127f5591 313 /* Normalize to fit into jiffies */
a73f89a6
JK
314 if (add_opt.timeout != IPSET_NO_TIMEOUT &&
315 add_opt.timeout > UINT_MAX/MSEC_PER_SEC)
127f5591 316 add_opt.timeout = UINT_MAX/MSEC_PER_SEC;
ac8cc925 317 if (info->add_set.index != IPSET_INVALID_ID)
b66554cf 318 ip_set_add(info->add_set.index, skb, par, &add_opt);
ac8cc925 319 if (info->del_set.index != IPSET_INVALID_ID)
b66554cf 320 ip_set_del(info->del_set.index, skb, par, &del_opt);
ac8cc925
JK
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
d956798d
JK
328static 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,
ac8cc925
JK
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,
d956798d
JK
347 .me = THIS_MODULE
348 },
349 {
350 .name = "set",
351 .family = NFPROTO_IPV6,
352 .revision = 1,
ac8cc925
JK
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,
d956798d
JK
357 .me = THIS_MODULE
358 },
3e0304a5
JK
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 },
d956798d
JK
380};
381
382static 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,
ac8cc925
JK
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,
d956798d
JK
401 .me = THIS_MODULE
402 },
403 {
404 .name = "SET",
405 .revision = 1,
406 .family = NFPROTO_IPV6,
ac8cc925
JK
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 },
3e0304a5 413 /* --timeout and --exist flags support */
ac8cc925
JK
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,
d956798d
JK
432 .me = THIS_MODULE
433 },
434};
435
436static 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
450static 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
456module_init(xt_set_init);
457module_exit(xt_set_fini);