• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
match_set(ip_set_id_t index,const struct sk_buff * skb,const struct xt_action_param * par,struct ip_set_adt_opt * opt,int inv)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
set_match_v0(const struct sk_buff * skb,struct xt_action_param * par)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
compat_flags(struct xt_set_info_v0 * info)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
set_match_v0_checkentry(const struct xt_mtchk_param * par)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
set_match_v0_destroy(const struct xt_mtdtor_param * par)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
set_target_v0(struct sk_buff * skb,const struct xt_action_param * par)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
set_target_v0_checkentry(const struct xt_tgchk_param * par)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
set_target_v0_destroy(const struct xt_tgdtor_param * par)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
set_match_v1(const struct sk_buff * skb,struct xt_action_param * par)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 	if (opt.flags & IPSET_RETURN_NOMATCH)
193 		opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
194 
195 	return match_set(info->match_set.index, skb, par, &opt,
196 			 info->match_set.flags & IPSET_INV_MATCH);
197 }
198 
199 static int
set_match_v1_checkentry(const struct xt_mtchk_param * par)200 set_match_v1_checkentry(const struct xt_mtchk_param *par)
201 {
202 	struct xt_set_info_match_v1 *info = par->matchinfo;
203 	ip_set_id_t index;
204 
205 	index = ip_set_nfnl_get_byindex(info->match_set.index);
206 
207 	if (index == IPSET_INVALID_ID) {
208 		pr_warning("Cannot find set indentified by id %u to match\n",
209 			   info->match_set.index);
210 		return -ENOENT;
211 	}
212 	if (info->match_set.dim > IPSET_DIM_MAX) {
213 		pr_warning("Protocol error: set match dimension "
214 			   "is over the limit!\n");
215 		ip_set_nfnl_put(info->match_set.index);
216 		return -ERANGE;
217 	}
218 
219 	return 0;
220 }
221 
222 static void
set_match_v1_destroy(const struct xt_mtdtor_param * par)223 set_match_v1_destroy(const struct xt_mtdtor_param *par)
224 {
225 	struct xt_set_info_match_v1 *info = par->matchinfo;
226 
227 	ip_set_nfnl_put(info->match_set.index);
228 }
229 
230 static unsigned int
set_target_v1(struct sk_buff * skb,const struct xt_action_param * par)231 set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
232 {
233 	const struct xt_set_info_target_v1 *info = par->targinfo;
234 	ADT_OPT(add_opt, par->family, info->add_set.dim,
235 		info->add_set.flags, 0, UINT_MAX);
236 	ADT_OPT(del_opt, par->family, info->del_set.dim,
237 		info->del_set.flags, 0, UINT_MAX);
238 
239 	if (info->add_set.index != IPSET_INVALID_ID)
240 		ip_set_add(info->add_set.index, skb, par, &add_opt);
241 	if (info->del_set.index != IPSET_INVALID_ID)
242 		ip_set_del(info->del_set.index, skb, par, &del_opt);
243 
244 	return XT_CONTINUE;
245 }
246 
247 static int
set_target_v1_checkentry(const struct xt_tgchk_param * par)248 set_target_v1_checkentry(const struct xt_tgchk_param *par)
249 {
250 	const struct xt_set_info_target_v1 *info = par->targinfo;
251 	ip_set_id_t index;
252 
253 	if (info->add_set.index != IPSET_INVALID_ID) {
254 		index = ip_set_nfnl_get_byindex(info->add_set.index);
255 		if (index == IPSET_INVALID_ID) {
256 			pr_warning("Cannot find add_set index %u as target\n",
257 				   info->add_set.index);
258 			return -ENOENT;
259 		}
260 	}
261 
262 	if (info->del_set.index != IPSET_INVALID_ID) {
263 		index = ip_set_nfnl_get_byindex(info->del_set.index);
264 		if (index == IPSET_INVALID_ID) {
265 			pr_warning("Cannot find del_set index %u as target\n",
266 				   info->del_set.index);
267 			if (info->add_set.index != IPSET_INVALID_ID)
268 				ip_set_nfnl_put(info->add_set.index);
269 			return -ENOENT;
270 		}
271 	}
272 	if (info->add_set.dim > IPSET_DIM_MAX ||
273 	    info->del_set.dim > IPSET_DIM_MAX) {
274 		pr_warning("Protocol error: SET target dimension "
275 			   "is over the limit!\n");
276 		if (info->add_set.index != IPSET_INVALID_ID)
277 			ip_set_nfnl_put(info->add_set.index);
278 		if (info->del_set.index != IPSET_INVALID_ID)
279 			ip_set_nfnl_put(info->del_set.index);
280 		return -ERANGE;
281 	}
282 
283 	return 0;
284 }
285 
286 static void
set_target_v1_destroy(const struct xt_tgdtor_param * par)287 set_target_v1_destroy(const struct xt_tgdtor_param *par)
288 {
289 	const struct xt_set_info_target_v1 *info = par->targinfo;
290 
291 	if (info->add_set.index != IPSET_INVALID_ID)
292 		ip_set_nfnl_put(info->add_set.index);
293 	if (info->del_set.index != IPSET_INVALID_ID)
294 		ip_set_nfnl_put(info->del_set.index);
295 }
296 
297 /* Revision 2 target */
298 
299 static unsigned int
set_target_v2(struct sk_buff * skb,const struct xt_action_param * par)300 set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
301 {
302 	const struct xt_set_info_target_v2 *info = par->targinfo;
303 	ADT_OPT(add_opt, par->family, info->add_set.dim,
304 		info->add_set.flags, info->flags, info->timeout);
305 	ADT_OPT(del_opt, par->family, info->del_set.dim,
306 		info->del_set.flags, 0, UINT_MAX);
307 
308 	/* Normalize to fit into jiffies */
309 	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
310 	    add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC)
311 		add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC;
312 	if (info->add_set.index != IPSET_INVALID_ID)
313 		ip_set_add(info->add_set.index, skb, par, &add_opt);
314 	if (info->del_set.index != IPSET_INVALID_ID)
315 		ip_set_del(info->del_set.index, skb, par, &del_opt);
316 
317 	return XT_CONTINUE;
318 }
319 
320 #define set_target_v2_checkentry	set_target_v1_checkentry
321 #define set_target_v2_destroy		set_target_v1_destroy
322 
323 /* Revision 3 match */
324 
325 static bool
match_counter(u64 counter,const struct ip_set_counter_match * info)326 match_counter(u64 counter, const struct ip_set_counter_match *info)
327 {
328 	switch (info->op) {
329 	case IPSET_COUNTER_NONE:
330 		return true;
331 	case IPSET_COUNTER_EQ:
332 		return counter == info->value;
333 	case IPSET_COUNTER_NE:
334 		return counter != info->value;
335 	case IPSET_COUNTER_LT:
336 		return counter < info->value;
337 	case IPSET_COUNTER_GT:
338 		return counter > info->value;
339 	}
340 	return false;
341 }
342 
343 static bool
set_match_v3(const struct sk_buff * skb,struct xt_action_param * par)344 set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
345 {
346 	const struct xt_set_info_match_v3 *info = par->matchinfo;
347 	ADT_OPT(opt, par->family, info->match_set.dim,
348 		info->match_set.flags, info->flags, UINT_MAX);
349 	int ret;
350 
351 	if (info->packets.op != IPSET_COUNTER_NONE ||
352 	    info->bytes.op != IPSET_COUNTER_NONE)
353 		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
354 
355 	ret = match_set(info->match_set.index, skb, par, &opt,
356 			info->match_set.flags & IPSET_INV_MATCH);
357 
358 	if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
359 		return ret;
360 
361 	if (!match_counter(opt.ext.packets, &info->packets))
362 		return 0;
363 	return match_counter(opt.ext.bytes, &info->bytes);
364 }
365 
366 #define set_match_v3_checkentry	set_match_v1_checkentry
367 #define set_match_v3_destroy	set_match_v1_destroy
368 
369 static struct xt_match set_matches[] __read_mostly = {
370 	{
371 		.name		= "set",
372 		.family		= NFPROTO_IPV4,
373 		.revision	= 0,
374 		.match		= set_match_v0,
375 		.matchsize	= sizeof(struct xt_set_info_match_v0),
376 		.checkentry	= set_match_v0_checkentry,
377 		.destroy	= set_match_v0_destroy,
378 		.me		= THIS_MODULE
379 	},
380 	{
381 		.name		= "set",
382 		.family		= NFPROTO_IPV4,
383 		.revision	= 1,
384 		.match		= set_match_v1,
385 		.matchsize	= sizeof(struct xt_set_info_match_v1),
386 		.checkentry	= set_match_v1_checkentry,
387 		.destroy	= set_match_v1_destroy,
388 		.me		= THIS_MODULE
389 	},
390 	{
391 		.name		= "set",
392 		.family		= NFPROTO_IPV6,
393 		.revision	= 1,
394 		.match		= set_match_v1,
395 		.matchsize	= sizeof(struct xt_set_info_match_v1),
396 		.checkentry	= set_match_v1_checkentry,
397 		.destroy	= set_match_v1_destroy,
398 		.me		= THIS_MODULE
399 	},
400 	/* --return-nomatch flag support */
401 	{
402 		.name		= "set",
403 		.family		= NFPROTO_IPV4,
404 		.revision	= 2,
405 		.match		= set_match_v1,
406 		.matchsize	= sizeof(struct xt_set_info_match_v1),
407 		.checkentry	= set_match_v1_checkentry,
408 		.destroy	= set_match_v1_destroy,
409 		.me		= THIS_MODULE
410 	},
411 	{
412 		.name		= "set",
413 		.family		= NFPROTO_IPV6,
414 		.revision	= 2,
415 		.match		= set_match_v1,
416 		.matchsize	= sizeof(struct xt_set_info_match_v1),
417 		.checkentry	= set_match_v1_checkentry,
418 		.destroy	= set_match_v1_destroy,
419 		.me		= THIS_MODULE
420 	},
421 	/* counters support: update, match */
422 	{
423 		.name		= "set",
424 		.family		= NFPROTO_IPV4,
425 		.revision	= 3,
426 		.match		= set_match_v3,
427 		.matchsize	= sizeof(struct xt_set_info_match_v3),
428 		.checkentry	= set_match_v3_checkentry,
429 		.destroy	= set_match_v3_destroy,
430 		.me		= THIS_MODULE
431 	},
432 	{
433 		.name		= "set",
434 		.family		= NFPROTO_IPV6,
435 		.revision	= 3,
436 		.match		= set_match_v3,
437 		.matchsize	= sizeof(struct xt_set_info_match_v3),
438 		.checkentry	= set_match_v3_checkentry,
439 		.destroy	= set_match_v3_destroy,
440 		.me		= THIS_MODULE
441 	},
442 };
443 
444 static struct xt_target set_targets[] __read_mostly = {
445 	{
446 		.name		= "SET",
447 		.revision	= 0,
448 		.family		= NFPROTO_IPV4,
449 		.target		= set_target_v0,
450 		.targetsize	= sizeof(struct xt_set_info_target_v0),
451 		.checkentry	= set_target_v0_checkentry,
452 		.destroy	= set_target_v0_destroy,
453 		.me		= THIS_MODULE
454 	},
455 	{
456 		.name		= "SET",
457 		.revision	= 1,
458 		.family		= NFPROTO_IPV4,
459 		.target		= set_target_v1,
460 		.targetsize	= sizeof(struct xt_set_info_target_v1),
461 		.checkentry	= set_target_v1_checkentry,
462 		.destroy	= set_target_v1_destroy,
463 		.me		= THIS_MODULE
464 	},
465 	{
466 		.name		= "SET",
467 		.revision	= 1,
468 		.family		= NFPROTO_IPV6,
469 		.target		= set_target_v1,
470 		.targetsize	= sizeof(struct xt_set_info_target_v1),
471 		.checkentry	= set_target_v1_checkentry,
472 		.destroy	= set_target_v1_destroy,
473 		.me		= THIS_MODULE
474 	},
475 	/* --timeout and --exist flags support */
476 	{
477 		.name		= "SET",
478 		.revision	= 2,
479 		.family		= NFPROTO_IPV4,
480 		.target		= set_target_v2,
481 		.targetsize	= sizeof(struct xt_set_info_target_v2),
482 		.checkentry	= set_target_v2_checkentry,
483 		.destroy	= set_target_v2_destroy,
484 		.me		= THIS_MODULE
485 	},
486 	{
487 		.name		= "SET",
488 		.revision	= 2,
489 		.family		= NFPROTO_IPV6,
490 		.target		= set_target_v2,
491 		.targetsize	= sizeof(struct xt_set_info_target_v2),
492 		.checkentry	= set_target_v2_checkentry,
493 		.destroy	= set_target_v2_destroy,
494 		.me		= THIS_MODULE
495 	},
496 };
497 
xt_set_init(void)498 static int __init xt_set_init(void)
499 {
500 	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
501 
502 	if (!ret) {
503 		ret = xt_register_targets(set_targets,
504 					  ARRAY_SIZE(set_targets));
505 		if (ret)
506 			xt_unregister_matches(set_matches,
507 					      ARRAY_SIZE(set_matches));
508 	}
509 	return ret;
510 }
511 
xt_set_fini(void)512 static void __exit xt_set_fini(void)
513 {
514 	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
515 	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
516 }
517 
518 module_init(xt_set_init);
519 module_exit(xt_set_fini);
520