xt_set.c revision 127f559127f5175e4bec3dab725a34845d956591
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
20MODULE_LICENSE("GPL");
21MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
22MODULE_DESCRIPTION("Xtables: IP set match and target module");
23MODULE_ALIAS("xt_SET");
24MODULE_ALIAS("ipt_set");
25MODULE_ALIAS("ip6t_set");
26MODULE_ALIAS("ipt_SET");
27MODULE_ALIAS("ip6t_SET");
28
29static inline int
30match_set(ip_set_id_t index, const struct sk_buff *skb,
31	  const struct xt_action_param *par,
32	  const struct ip_set_adt_opt *opt, int inv)
33{
34	if (ip_set_test(index, skb, par, opt))
35		inv = !inv;
36	return inv;
37}
38
39#define ADT_OPT(n, f, d, fs, cfs, t)	\
40const struct ip_set_adt_opt n = {	\
41	.family	= f,			\
42	.dim = d,			\
43	.flags = fs,			\
44	.cmdflags = cfs,		\
45	.timeout = t,			\
46}
47#define ADT_MOPT(n, f, d, fs, cfs, t)	\
48struct ip_set_adt_opt n = {		\
49	.family	= f,			\
50	.dim = d,			\
51	.flags = fs,			\
52	.cmdflags = cfs,		\
53	.timeout = t,			\
54}
55
56/* Revision 0 interface: backward compatible with netfilter/iptables */
57
58static bool
59set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
60{
61	const struct xt_set_info_match_v0 *info = par->matchinfo;
62	ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
63		info->match_set.u.compat.flags, 0, UINT_MAX);
64
65	return match_set(info->match_set.index, skb, par, &opt,
66			 info->match_set.u.compat.flags & IPSET_INV_MATCH);
67}
68
69static void
70compat_flags(struct xt_set_info_v0 *info)
71{
72	u_int8_t i;
73
74	/* Fill out compatibility data according to enum ip_set_kopt */
75	info->u.compat.dim = IPSET_DIM_ZERO;
76	if (info->u.flags[0] & IPSET_MATCH_INV)
77		info->u.compat.flags |= IPSET_INV_MATCH;
78	for (i = 0; i < IPSET_DIM_MAX-1 && info->u.flags[i]; i++) {
79		info->u.compat.dim++;
80		if (info->u.flags[i] & IPSET_SRC)
81			info->u.compat.flags |= (1<<info->u.compat.dim);
82	}
83}
84
85static int
86set_match_v0_checkentry(const struct xt_mtchk_param *par)
87{
88	struct xt_set_info_match_v0 *info = par->matchinfo;
89	ip_set_id_t index;
90
91	index = ip_set_nfnl_get_byindex(info->match_set.index);
92
93	if (index == IPSET_INVALID_ID) {
94		pr_warning("Cannot find set indentified by id %u to match\n",
95			   info->match_set.index);
96		return -ENOENT;
97	}
98	if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) {
99		pr_warning("Protocol error: set match dimension "
100			   "is over the limit!\n");
101		ip_set_nfnl_put(info->match_set.index);
102		return -ERANGE;
103	}
104
105	/* Fill out compatibility data */
106	compat_flags(&info->match_set);
107
108	return 0;
109}
110
111static void
112set_match_v0_destroy(const struct xt_mtdtor_param *par)
113{
114	struct xt_set_info_match_v0 *info = par->matchinfo;
115
116	ip_set_nfnl_put(info->match_set.index);
117}
118
119static unsigned int
120set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
121{
122	const struct xt_set_info_target_v0 *info = par->targinfo;
123	ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
124		info->add_set.u.compat.flags, 0, UINT_MAX);
125	ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
126		info->del_set.u.compat.flags, 0, UINT_MAX);
127
128	if (info->add_set.index != IPSET_INVALID_ID)
129		ip_set_add(info->add_set.index, skb, par, &add_opt);
130	if (info->del_set.index != IPSET_INVALID_ID)
131		ip_set_del(info->del_set.index, skb, par, &del_opt);
132
133	return XT_CONTINUE;
134}
135
136static int
137set_target_v0_checkentry(const struct xt_tgchk_param *par)
138{
139	struct xt_set_info_target_v0 *info = par->targinfo;
140	ip_set_id_t index;
141
142	if (info->add_set.index != IPSET_INVALID_ID) {
143		index = ip_set_nfnl_get_byindex(info->add_set.index);
144		if (index == IPSET_INVALID_ID) {
145			pr_warning("Cannot find add_set index %u as target\n",
146				   info->add_set.index);
147			return -ENOENT;
148		}
149	}
150
151	if (info->del_set.index != IPSET_INVALID_ID) {
152		index = ip_set_nfnl_get_byindex(info->del_set.index);
153		if (index == IPSET_INVALID_ID) {
154			pr_warning("Cannot find del_set index %u as target\n",
155				   info->del_set.index);
156			if (info->add_set.index != IPSET_INVALID_ID)
157				ip_set_nfnl_put(info->add_set.index);
158			return -ENOENT;
159		}
160	}
161	if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
162	    info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
163		pr_warning("Protocol error: SET target dimension "
164			   "is over the limit!\n");
165		if (info->add_set.index != IPSET_INVALID_ID)
166			ip_set_nfnl_put(info->add_set.index);
167		if (info->del_set.index != IPSET_INVALID_ID)
168			ip_set_nfnl_put(info->del_set.index);
169		return -ERANGE;
170	}
171
172	/* Fill out compatibility data */
173	compat_flags(&info->add_set);
174	compat_flags(&info->del_set);
175
176	return 0;
177}
178
179static void
180set_target_v0_destroy(const struct xt_tgdtor_param *par)
181{
182	const struct xt_set_info_target_v0 *info = par->targinfo;
183
184	if (info->add_set.index != IPSET_INVALID_ID)
185		ip_set_nfnl_put(info->add_set.index);
186	if (info->del_set.index != IPSET_INVALID_ID)
187		ip_set_nfnl_put(info->del_set.index);
188}
189
190/* Revision 1 match and target */
191
192static bool
193set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
194{
195	const struct xt_set_info_match_v1 *info = par->matchinfo;
196	ADT_OPT(opt, par->family, info->match_set.dim,
197		info->match_set.flags, 0, UINT_MAX);
198
199	return match_set(info->match_set.index, skb, par, &opt,
200			 info->match_set.flags & IPSET_INV_MATCH);
201}
202
203static int
204set_match_v1_checkentry(const struct xt_mtchk_param *par)
205{
206	struct xt_set_info_match_v1 *info = par->matchinfo;
207	ip_set_id_t index;
208
209	index = ip_set_nfnl_get_byindex(info->match_set.index);
210
211	if (index == IPSET_INVALID_ID) {
212		pr_warning("Cannot find set indentified by id %u to match\n",
213			   info->match_set.index);
214		return -ENOENT;
215	}
216	if (info->match_set.dim > IPSET_DIM_MAX) {
217		pr_warning("Protocol error: set match dimension "
218			   "is over the limit!\n");
219		ip_set_nfnl_put(info->match_set.index);
220		return -ERANGE;
221	}
222
223	return 0;
224}
225
226static void
227set_match_v1_destroy(const struct xt_mtdtor_param *par)
228{
229	struct xt_set_info_match_v1 *info = par->matchinfo;
230
231	ip_set_nfnl_put(info->match_set.index);
232}
233
234static unsigned int
235set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
236{
237	const struct xt_set_info_target_v1 *info = par->targinfo;
238	ADT_OPT(add_opt, par->family, info->add_set.dim,
239		info->add_set.flags, 0, UINT_MAX);
240	ADT_OPT(del_opt, par->family, info->del_set.dim,
241		info->del_set.flags, 0, UINT_MAX);
242
243	if (info->add_set.index != IPSET_INVALID_ID)
244		ip_set_add(info->add_set.index, skb, par, &add_opt);
245	if (info->del_set.index != IPSET_INVALID_ID)
246		ip_set_del(info->del_set.index, skb, par, &del_opt);
247
248	return XT_CONTINUE;
249}
250
251static int
252set_target_v1_checkentry(const struct xt_tgchk_param *par)
253{
254	const struct xt_set_info_target_v1 *info = par->targinfo;
255	ip_set_id_t index;
256
257	if (info->add_set.index != IPSET_INVALID_ID) {
258		index = ip_set_nfnl_get_byindex(info->add_set.index);
259		if (index == IPSET_INVALID_ID) {
260			pr_warning("Cannot find add_set index %u as target\n",
261				   info->add_set.index);
262			return -ENOENT;
263		}
264	}
265
266	if (info->del_set.index != IPSET_INVALID_ID) {
267		index = ip_set_nfnl_get_byindex(info->del_set.index);
268		if (index == IPSET_INVALID_ID) {
269			pr_warning("Cannot find del_set index %u as target\n",
270				   info->del_set.index);
271			if (info->add_set.index != IPSET_INVALID_ID)
272				ip_set_nfnl_put(info->add_set.index);
273			return -ENOENT;
274		}
275	}
276	if (info->add_set.dim > IPSET_DIM_MAX ||
277	    info->del_set.dim > IPSET_DIM_MAX) {
278		pr_warning("Protocol error: SET target dimension "
279			   "is over the limit!\n");
280		if (info->add_set.index != IPSET_INVALID_ID)
281			ip_set_nfnl_put(info->add_set.index);
282		if (info->del_set.index != IPSET_INVALID_ID)
283			ip_set_nfnl_put(info->del_set.index);
284		return -ERANGE;
285	}
286
287	return 0;
288}
289
290static void
291set_target_v1_destroy(const struct xt_tgdtor_param *par)
292{
293	const struct xt_set_info_target_v1 *info = par->targinfo;
294
295	if (info->add_set.index != IPSET_INVALID_ID)
296		ip_set_nfnl_put(info->add_set.index);
297	if (info->del_set.index != IPSET_INVALID_ID)
298		ip_set_nfnl_put(info->del_set.index);
299}
300
301/* Revision 2 target */
302
303static unsigned int
304set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
305{
306	const struct xt_set_info_target_v2 *info = par->targinfo;
307	ADT_MOPT(add_opt, par->family, info->add_set.dim,
308		 info->add_set.flags, info->flags, info->timeout);
309	ADT_OPT(del_opt, par->family, info->del_set.dim,
310		info->del_set.flags, 0, UINT_MAX);
311
312	/* Normalize to fit into jiffies */
313	if (add_opt.timeout > UINT_MAX/MSEC_PER_SEC)
314		add_opt.timeout = UINT_MAX/MSEC_PER_SEC;
315	if (info->add_set.index != IPSET_INVALID_ID)
316		ip_set_add(info->add_set.index, skb, par, &add_opt);
317	if (info->del_set.index != IPSET_INVALID_ID)
318		ip_set_del(info->del_set.index, skb, par, &del_opt);
319
320	return XT_CONTINUE;
321}
322
323#define set_target_v2_checkentry	set_target_v1_checkentry
324#define set_target_v2_destroy		set_target_v1_destroy
325
326static struct xt_match set_matches[] __read_mostly = {
327	{
328		.name		= "set",
329		.family		= NFPROTO_IPV4,
330		.revision	= 0,
331		.match		= set_match_v0,
332		.matchsize	= sizeof(struct xt_set_info_match_v0),
333		.checkentry	= set_match_v0_checkentry,
334		.destroy	= set_match_v0_destroy,
335		.me		= THIS_MODULE
336	},
337	{
338		.name		= "set",
339		.family		= NFPROTO_IPV4,
340		.revision	= 1,
341		.match		= set_match_v1,
342		.matchsize	= sizeof(struct xt_set_info_match_v1),
343		.checkentry	= set_match_v1_checkentry,
344		.destroy	= set_match_v1_destroy,
345		.me		= THIS_MODULE
346	},
347	{
348		.name		= "set",
349		.family		= NFPROTO_IPV6,
350		.revision	= 1,
351		.match		= set_match_v1,
352		.matchsize	= sizeof(struct xt_set_info_match_v1),
353		.checkentry	= set_match_v1_checkentry,
354		.destroy	= set_match_v1_destroy,
355		.me		= THIS_MODULE
356	},
357};
358
359static struct xt_target set_targets[] __read_mostly = {
360	{
361		.name		= "SET",
362		.revision	= 0,
363		.family		= NFPROTO_IPV4,
364		.target		= set_target_v0,
365		.targetsize	= sizeof(struct xt_set_info_target_v0),
366		.checkentry	= set_target_v0_checkentry,
367		.destroy	= set_target_v0_destroy,
368		.me		= THIS_MODULE
369	},
370	{
371		.name		= "SET",
372		.revision	= 1,
373		.family		= NFPROTO_IPV4,
374		.target		= set_target_v1,
375		.targetsize	= sizeof(struct xt_set_info_target_v1),
376		.checkentry	= set_target_v1_checkentry,
377		.destroy	= set_target_v1_destroy,
378		.me		= THIS_MODULE
379	},
380	{
381		.name		= "SET",
382		.revision	= 1,
383		.family		= NFPROTO_IPV6,
384		.target		= set_target_v1,
385		.targetsize	= sizeof(struct xt_set_info_target_v1),
386		.checkentry	= set_target_v1_checkentry,
387		.destroy	= set_target_v1_destroy,
388		.me		= THIS_MODULE
389	},
390	{
391		.name		= "SET",
392		.revision	= 2,
393		.family		= NFPROTO_IPV4,
394		.target		= set_target_v2,
395		.targetsize	= sizeof(struct xt_set_info_target_v2),
396		.checkentry	= set_target_v2_checkentry,
397		.destroy	= set_target_v2_destroy,
398		.me		= THIS_MODULE
399	},
400	{
401		.name		= "SET",
402		.revision	= 2,
403		.family		= NFPROTO_IPV6,
404		.target		= set_target_v2,
405		.targetsize	= sizeof(struct xt_set_info_target_v2),
406		.checkentry	= set_target_v2_checkentry,
407		.destroy	= set_target_v2_destroy,
408		.me		= THIS_MODULE
409	},
410};
411
412static int __init xt_set_init(void)
413{
414	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
415
416	if (!ret) {
417		ret = xt_register_targets(set_targets,
418					  ARRAY_SIZE(set_targets));
419		if (ret)
420			xt_unregister_matches(set_matches,
421					      ARRAY_SIZE(set_matches));
422	}
423	return ret;
424}
425
426static void __exit xt_set_fini(void)
427{
428	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
429	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
430}
431
432module_init(xt_set_init);
433module_exit(xt_set_fini);
434