xt_set.c revision 6e01781d1c80e2e8263471252a631e86165b15c5
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
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,
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)	\
41struct 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
51static bool
52set_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
62static void
63compat_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
78static int
79set_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
104static void
105set_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
112static unsigned int
113set_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
129static int
130set_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
172static void
173set_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
185static bool
186set_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
199static int
200set_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
222static void
223set_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
230static unsigned int
231set_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
247static int
248set_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
286static void
287set_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
299static unsigned int
300set_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
325static bool
326match_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
343static bool
344set_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
369static 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
444static 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
498static 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
512static 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
518module_init(xt_set_init);
519module_exit(xt_set_fini);
520