xt_set.c revision a73f89a61f92b364f0b4a3be412b5b70553afc23
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
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	  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)	\
41const 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)	\
49struct 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
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;
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
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");
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
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;
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
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);
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
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
191/* Revision 1 match and target */
192
193static bool
194set_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
204static int
205set_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
227static void
228set_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
235static unsigned int
236set_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
252static int
253set_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
291static void
292set_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
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;
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
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,
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};
360
361static struct xt_target set_targets[] __read_mostly = {
362	{
363		.name		= "SET",
364		.revision	= 0,
365		.family		= NFPROTO_IPV4,
366		.target		= set_target_v0,
367		.targetsize	= sizeof(struct xt_set_info_target_v0),
368		.checkentry	= set_target_v0_checkentry,
369		.destroy	= set_target_v0_destroy,
370		.me		= THIS_MODULE
371	},
372	{
373		.name		= "SET",
374		.revision	= 1,
375		.family		= NFPROTO_IPV4,
376		.target		= set_target_v1,
377		.targetsize	= sizeof(struct xt_set_info_target_v1),
378		.checkentry	= set_target_v1_checkentry,
379		.destroy	= set_target_v1_destroy,
380		.me		= THIS_MODULE
381	},
382	{
383		.name		= "SET",
384		.revision	= 1,
385		.family		= NFPROTO_IPV6,
386		.target		= set_target_v1,
387		.targetsize	= sizeof(struct xt_set_info_target_v1),
388		.checkentry	= set_target_v1_checkentry,
389		.destroy	= set_target_v1_destroy,
390		.me		= THIS_MODULE
391	},
392	{
393		.name		= "SET",
394		.revision	= 2,
395		.family		= NFPROTO_IPV4,
396		.target		= set_target_v2,
397		.targetsize	= sizeof(struct xt_set_info_target_v2),
398		.checkentry	= set_target_v2_checkentry,
399		.destroy	= set_target_v2_destroy,
400		.me		= THIS_MODULE
401	},
402	{
403		.name		= "SET",
404		.revision	= 2,
405		.family		= NFPROTO_IPV6,
406		.target		= set_target_v2,
407		.targetsize	= sizeof(struct xt_set_info_target_v2),
408		.checkentry	= set_target_v2_checkentry,
409		.destroy	= set_target_v2_destroy,
410		.me		= THIS_MODULE
411	},
412};
413
414static int __init xt_set_init(void)
415{
416	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
417
418	if (!ret) {
419		ret = xt_register_targets(set_targets,
420					  ARRAY_SIZE(set_targets));
421		if (ret)
422			xt_unregister_matches(set_matches,
423					      ARRAY_SIZE(set_matches));
424	}
425	return ret;
426}
427
428static void __exit xt_set_fini(void)
429{
430	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
431	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
432}
433
434module_init(xt_set_init);
435module_exit(xt_set_fini);
436