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(par->net, info->match_set.index);
85
86	if (index == IPSET_INVALID_ID) {
87		pr_warn("Cannot find set identified 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_warn("Protocol error: set match dimension is over the limit!\n");
93		ip_set_nfnl_put(par->net, info->match_set.index);
94		return -ERANGE;
95	}
96
97	/* Fill out compatibility data */
98	compat_flags(&info->match_set);
99
100	return 0;
101}
102
103static void
104set_match_v0_destroy(const struct xt_mtdtor_param *par)
105{
106	struct xt_set_info_match_v0 *info = par->matchinfo;
107
108	ip_set_nfnl_put(par->net, info->match_set.index);
109}
110
111/* Revision 1 match */
112
113static bool
114set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
115{
116	const struct xt_set_info_match_v1 *info = par->matchinfo;
117	ADT_OPT(opt, par->family, info->match_set.dim,
118		info->match_set.flags, 0, UINT_MAX);
119
120	if (opt.flags & IPSET_RETURN_NOMATCH)
121		opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
122
123	return match_set(info->match_set.index, skb, par, &opt,
124			 info->match_set.flags & IPSET_INV_MATCH);
125}
126
127static int
128set_match_v1_checkentry(const struct xt_mtchk_param *par)
129{
130	struct xt_set_info_match_v1 *info = par->matchinfo;
131	ip_set_id_t index;
132
133	index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
134
135	if (index == IPSET_INVALID_ID) {
136		pr_warn("Cannot find set identified by id %u to match\n",
137			info->match_set.index);
138		return -ENOENT;
139	}
140	if (info->match_set.dim > IPSET_DIM_MAX) {
141		pr_warn("Protocol error: set match dimension is over the limit!\n");
142		ip_set_nfnl_put(par->net, info->match_set.index);
143		return -ERANGE;
144	}
145
146	return 0;
147}
148
149static void
150set_match_v1_destroy(const struct xt_mtdtor_param *par)
151{
152	struct xt_set_info_match_v1 *info = par->matchinfo;
153
154	ip_set_nfnl_put(par->net, info->match_set.index);
155}
156
157/* Revision 3 match */
158
159static bool
160match_counter0(u64 counter, const struct ip_set_counter_match0 *info)
161{
162	switch (info->op) {
163	case IPSET_COUNTER_NONE:
164		return true;
165	case IPSET_COUNTER_EQ:
166		return counter == info->value;
167	case IPSET_COUNTER_NE:
168		return counter != info->value;
169	case IPSET_COUNTER_LT:
170		return counter < info->value;
171	case IPSET_COUNTER_GT:
172		return counter > info->value;
173	}
174	return false;
175}
176
177static bool
178set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
179{
180	const struct xt_set_info_match_v3 *info = par->matchinfo;
181	ADT_OPT(opt, par->family, info->match_set.dim,
182		info->match_set.flags, info->flags, UINT_MAX);
183	int ret;
184
185	if (info->packets.op != IPSET_COUNTER_NONE ||
186	    info->bytes.op != IPSET_COUNTER_NONE)
187		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
188
189	ret = match_set(info->match_set.index, skb, par, &opt,
190			info->match_set.flags & IPSET_INV_MATCH);
191
192	if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
193		return ret;
194
195	if (!match_counter0(opt.ext.packets, &info->packets))
196		return false;
197	return match_counter0(opt.ext.bytes, &info->bytes);
198}
199
200#define set_match_v3_checkentry	set_match_v1_checkentry
201#define set_match_v3_destroy	set_match_v1_destroy
202
203/* Revision 4 match */
204
205static bool
206match_counter(u64 counter, const struct ip_set_counter_match *info)
207{
208	switch (info->op) {
209	case IPSET_COUNTER_NONE:
210		return true;
211	case IPSET_COUNTER_EQ:
212		return counter == info->value;
213	case IPSET_COUNTER_NE:
214		return counter != info->value;
215	case IPSET_COUNTER_LT:
216		return counter < info->value;
217	case IPSET_COUNTER_GT:
218		return counter > info->value;
219	}
220	return false;
221}
222
223static bool
224set_match_v4(const struct sk_buff *skb, struct xt_action_param *par)
225{
226	const struct xt_set_info_match_v4 *info = par->matchinfo;
227	ADT_OPT(opt, par->family, info->match_set.dim,
228		info->match_set.flags, info->flags, UINT_MAX);
229	int ret;
230
231	if (info->packets.op != IPSET_COUNTER_NONE ||
232	    info->bytes.op != IPSET_COUNTER_NONE)
233		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
234
235	ret = match_set(info->match_set.index, skb, par, &opt,
236			info->match_set.flags & IPSET_INV_MATCH);
237
238	if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
239		return ret;
240
241	if (!match_counter(opt.ext.packets, &info->packets))
242		return false;
243	return match_counter(opt.ext.bytes, &info->bytes);
244}
245
246#define set_match_v4_checkentry	set_match_v1_checkentry
247#define set_match_v4_destroy	set_match_v1_destroy
248
249/* Revision 0 interface: backward compatible with netfilter/iptables */
250
251static unsigned int
252set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
253{
254	const struct xt_set_info_target_v0 *info = par->targinfo;
255	ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
256		info->add_set.u.compat.flags, 0, UINT_MAX);
257	ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
258		info->del_set.u.compat.flags, 0, UINT_MAX);
259
260	if (info->add_set.index != IPSET_INVALID_ID)
261		ip_set_add(info->add_set.index, skb, par, &add_opt);
262	if (info->del_set.index != IPSET_INVALID_ID)
263		ip_set_del(info->del_set.index, skb, par, &del_opt);
264
265	return XT_CONTINUE;
266}
267
268static int
269set_target_v0_checkentry(const struct xt_tgchk_param *par)
270{
271	struct xt_set_info_target_v0 *info = par->targinfo;
272	ip_set_id_t index;
273
274	if (info->add_set.index != IPSET_INVALID_ID) {
275		index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
276		if (index == IPSET_INVALID_ID) {
277			pr_warn("Cannot find add_set index %u as target\n",
278				info->add_set.index);
279			return -ENOENT;
280		}
281	}
282
283	if (info->del_set.index != IPSET_INVALID_ID) {
284		index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
285		if (index == IPSET_INVALID_ID) {
286			pr_warn("Cannot find del_set index %u as target\n",
287				info->del_set.index);
288			if (info->add_set.index != IPSET_INVALID_ID)
289				ip_set_nfnl_put(par->net, info->add_set.index);
290			return -ENOENT;
291		}
292	}
293	if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
294	    info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
295		pr_warn("Protocol error: SET target dimension is over the limit!\n");
296		if (info->add_set.index != IPSET_INVALID_ID)
297			ip_set_nfnl_put(par->net, info->add_set.index);
298		if (info->del_set.index != IPSET_INVALID_ID)
299			ip_set_nfnl_put(par->net, info->del_set.index);
300		return -ERANGE;
301	}
302
303	/* Fill out compatibility data */
304	compat_flags(&info->add_set);
305	compat_flags(&info->del_set);
306
307	return 0;
308}
309
310static void
311set_target_v0_destroy(const struct xt_tgdtor_param *par)
312{
313	const struct xt_set_info_target_v0 *info = par->targinfo;
314
315	if (info->add_set.index != IPSET_INVALID_ID)
316		ip_set_nfnl_put(par->net, info->add_set.index);
317	if (info->del_set.index != IPSET_INVALID_ID)
318		ip_set_nfnl_put(par->net, info->del_set.index);
319}
320
321/* Revision 1 target */
322
323static unsigned int
324set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
325{
326	const struct xt_set_info_target_v1 *info = par->targinfo;
327	ADT_OPT(add_opt, par->family, info->add_set.dim,
328		info->add_set.flags, 0, UINT_MAX);
329	ADT_OPT(del_opt, par->family, info->del_set.dim,
330		info->del_set.flags, 0, UINT_MAX);
331
332	if (info->add_set.index != IPSET_INVALID_ID)
333		ip_set_add(info->add_set.index, skb, par, &add_opt);
334	if (info->del_set.index != IPSET_INVALID_ID)
335		ip_set_del(info->del_set.index, skb, par, &del_opt);
336
337	return XT_CONTINUE;
338}
339
340static int
341set_target_v1_checkentry(const struct xt_tgchk_param *par)
342{
343	const struct xt_set_info_target_v1 *info = par->targinfo;
344	ip_set_id_t index;
345
346	if (info->add_set.index != IPSET_INVALID_ID) {
347		index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
348		if (index == IPSET_INVALID_ID) {
349			pr_warn("Cannot find add_set index %u as target\n",
350				info->add_set.index);
351			return -ENOENT;
352		}
353	}
354
355	if (info->del_set.index != IPSET_INVALID_ID) {
356		index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
357		if (index == IPSET_INVALID_ID) {
358			pr_warn("Cannot find del_set index %u as target\n",
359				info->del_set.index);
360			if (info->add_set.index != IPSET_INVALID_ID)
361				ip_set_nfnl_put(par->net, info->add_set.index);
362			return -ENOENT;
363		}
364	}
365	if (info->add_set.dim > IPSET_DIM_MAX ||
366	    info->del_set.dim > IPSET_DIM_MAX) {
367		pr_warn("Protocol error: SET target dimension is over the limit!\n");
368		if (info->add_set.index != IPSET_INVALID_ID)
369			ip_set_nfnl_put(par->net, info->add_set.index);
370		if (info->del_set.index != IPSET_INVALID_ID)
371			ip_set_nfnl_put(par->net, info->del_set.index);
372		return -ERANGE;
373	}
374
375	return 0;
376}
377
378static void
379set_target_v1_destroy(const struct xt_tgdtor_param *par)
380{
381	const struct xt_set_info_target_v1 *info = par->targinfo;
382
383	if (info->add_set.index != IPSET_INVALID_ID)
384		ip_set_nfnl_put(par->net, info->add_set.index);
385	if (info->del_set.index != IPSET_INVALID_ID)
386		ip_set_nfnl_put(par->net, info->del_set.index);
387}
388
389/* Revision 2 target */
390
391static unsigned int
392set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
393{
394	const struct xt_set_info_target_v2 *info = par->targinfo;
395	ADT_OPT(add_opt, par->family, info->add_set.dim,
396		info->add_set.flags, info->flags, info->timeout);
397	ADT_OPT(del_opt, par->family, info->del_set.dim,
398		info->del_set.flags, 0, UINT_MAX);
399
400	/* Normalize to fit into jiffies */
401	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
402	    add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC)
403		add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC;
404	if (info->add_set.index != IPSET_INVALID_ID)
405		ip_set_add(info->add_set.index, skb, par, &add_opt);
406	if (info->del_set.index != IPSET_INVALID_ID)
407		ip_set_del(info->del_set.index, skb, par, &del_opt);
408
409	return XT_CONTINUE;
410}
411
412#define set_target_v2_checkentry	set_target_v1_checkentry
413#define set_target_v2_destroy		set_target_v1_destroy
414
415/* Revision 3 target */
416
417static unsigned int
418set_target_v3(struct sk_buff *skb, const struct xt_action_param *par)
419{
420	const struct xt_set_info_target_v3 *info = par->targinfo;
421	ADT_OPT(add_opt, par->family, info->add_set.dim,
422		info->add_set.flags, info->flags, info->timeout);
423	ADT_OPT(del_opt, par->family, info->del_set.dim,
424		info->del_set.flags, 0, UINT_MAX);
425	ADT_OPT(map_opt, par->family, info->map_set.dim,
426		info->map_set.flags, 0, UINT_MAX);
427
428	int ret;
429
430	/* Normalize to fit into jiffies */
431	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
432	    add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC)
433		add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC;
434	if (info->add_set.index != IPSET_INVALID_ID)
435		ip_set_add(info->add_set.index, skb, par, &add_opt);
436	if (info->del_set.index != IPSET_INVALID_ID)
437		ip_set_del(info->del_set.index, skb, par, &del_opt);
438	if (info->map_set.index != IPSET_INVALID_ID) {
439		map_opt.cmdflags |= info->flags & (IPSET_FLAG_MAP_SKBMARK |
440						   IPSET_FLAG_MAP_SKBPRIO |
441						   IPSET_FLAG_MAP_SKBQUEUE);
442		ret = match_set(info->map_set.index, skb, par, &map_opt,
443				info->map_set.flags & IPSET_INV_MATCH);
444		if (!ret)
445			return XT_CONTINUE;
446		if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBMARK)
447			skb->mark = (skb->mark & ~(map_opt.ext.skbmarkmask))
448				    ^ (map_opt.ext.skbmark);
449		if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBPRIO)
450			skb->priority = map_opt.ext.skbprio;
451		if ((map_opt.cmdflags & IPSET_FLAG_MAP_SKBQUEUE) &&
452		    skb->dev &&
453		    skb->dev->real_num_tx_queues > map_opt.ext.skbqueue)
454			skb_set_queue_mapping(skb, map_opt.ext.skbqueue);
455	}
456	return XT_CONTINUE;
457}
458
459
460static int
461set_target_v3_checkentry(const struct xt_tgchk_param *par)
462{
463	const struct xt_set_info_target_v3 *info = par->targinfo;
464	ip_set_id_t index;
465
466	if (info->add_set.index != IPSET_INVALID_ID) {
467		index = ip_set_nfnl_get_byindex(par->net,
468						info->add_set.index);
469		if (index == IPSET_INVALID_ID) {
470			pr_warn("Cannot find add_set index %u as target\n",
471				info->add_set.index);
472			return -ENOENT;
473		}
474	}
475
476	if (info->del_set.index != IPSET_INVALID_ID) {
477		index = ip_set_nfnl_get_byindex(par->net,
478						info->del_set.index);
479		if (index == IPSET_INVALID_ID) {
480			pr_warn("Cannot find del_set index %u as target\n",
481				info->del_set.index);
482			if (info->add_set.index != IPSET_INVALID_ID)
483				ip_set_nfnl_put(par->net,
484						info->add_set.index);
485			return -ENOENT;
486		}
487	}
488
489	if (info->map_set.index != IPSET_INVALID_ID) {
490		if (strncmp(par->table, "mangle", 7)) {
491			pr_warn("--map-set only usable from mangle table\n");
492			return -EINVAL;
493		}
494		if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
495		     (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
496		     !(par->hook_mask & (1 << NF_INET_FORWARD |
497					 1 << NF_INET_LOCAL_OUT |
498					 1 << NF_INET_POST_ROUTING))) {
499			pr_warn("mapping of prio or/and queue is allowed only"
500				"from OUTPUT/FORWARD/POSTROUTING chains\n");
501			return -EINVAL;
502		}
503		index = ip_set_nfnl_get_byindex(par->net,
504						info->map_set.index);
505		if (index == IPSET_INVALID_ID) {
506			pr_warn("Cannot find map_set index %u as target\n",
507				info->map_set.index);
508			if (info->add_set.index != IPSET_INVALID_ID)
509				ip_set_nfnl_put(par->net,
510						info->add_set.index);
511			if (info->del_set.index != IPSET_INVALID_ID)
512				ip_set_nfnl_put(par->net,
513						info->del_set.index);
514			return -ENOENT;
515		}
516	}
517
518	if (info->add_set.dim > IPSET_DIM_MAX ||
519	    info->del_set.dim > IPSET_DIM_MAX ||
520	    info->map_set.dim > IPSET_DIM_MAX) {
521		pr_warn("Protocol error: SET target dimension "
522			"is over the limit!\n");
523		if (info->add_set.index != IPSET_INVALID_ID)
524			ip_set_nfnl_put(par->net, info->add_set.index);
525		if (info->del_set.index != IPSET_INVALID_ID)
526			ip_set_nfnl_put(par->net, info->del_set.index);
527		if (info->map_set.index != IPSET_INVALID_ID)
528			ip_set_nfnl_put(par->net, info->map_set.index);
529		return -ERANGE;
530	}
531
532	return 0;
533}
534
535static void
536set_target_v3_destroy(const struct xt_tgdtor_param *par)
537{
538	const struct xt_set_info_target_v3 *info = par->targinfo;
539
540	if (info->add_set.index != IPSET_INVALID_ID)
541		ip_set_nfnl_put(par->net, info->add_set.index);
542	if (info->del_set.index != IPSET_INVALID_ID)
543		ip_set_nfnl_put(par->net, info->del_set.index);
544	if (info->map_set.index != IPSET_INVALID_ID)
545		ip_set_nfnl_put(par->net, info->map_set.index);
546}
547
548
549static struct xt_match set_matches[] __read_mostly = {
550	{
551		.name		= "set",
552		.family		= NFPROTO_IPV4,
553		.revision	= 0,
554		.match		= set_match_v0,
555		.matchsize	= sizeof(struct xt_set_info_match_v0),
556		.checkentry	= set_match_v0_checkentry,
557		.destroy	= set_match_v0_destroy,
558		.me		= THIS_MODULE
559	},
560	{
561		.name		= "set",
562		.family		= NFPROTO_IPV4,
563		.revision	= 1,
564		.match		= set_match_v1,
565		.matchsize	= sizeof(struct xt_set_info_match_v1),
566		.checkentry	= set_match_v1_checkentry,
567		.destroy	= set_match_v1_destroy,
568		.me		= THIS_MODULE
569	},
570	{
571		.name		= "set",
572		.family		= NFPROTO_IPV6,
573		.revision	= 1,
574		.match		= set_match_v1,
575		.matchsize	= sizeof(struct xt_set_info_match_v1),
576		.checkentry	= set_match_v1_checkentry,
577		.destroy	= set_match_v1_destroy,
578		.me		= THIS_MODULE
579	},
580	/* --return-nomatch flag support */
581	{
582		.name		= "set",
583		.family		= NFPROTO_IPV4,
584		.revision	= 2,
585		.match		= set_match_v1,
586		.matchsize	= sizeof(struct xt_set_info_match_v1),
587		.checkentry	= set_match_v1_checkentry,
588		.destroy	= set_match_v1_destroy,
589		.me		= THIS_MODULE
590	},
591	{
592		.name		= "set",
593		.family		= NFPROTO_IPV6,
594		.revision	= 2,
595		.match		= set_match_v1,
596		.matchsize	= sizeof(struct xt_set_info_match_v1),
597		.checkentry	= set_match_v1_checkentry,
598		.destroy	= set_match_v1_destroy,
599		.me		= THIS_MODULE
600	},
601	/* counters support: update, match */
602	{
603		.name		= "set",
604		.family		= NFPROTO_IPV4,
605		.revision	= 3,
606		.match		= set_match_v3,
607		.matchsize	= sizeof(struct xt_set_info_match_v3),
608		.checkentry	= set_match_v3_checkentry,
609		.destroy	= set_match_v3_destroy,
610		.me		= THIS_MODULE
611	},
612	{
613		.name		= "set",
614		.family		= NFPROTO_IPV6,
615		.revision	= 3,
616		.match		= set_match_v3,
617		.matchsize	= sizeof(struct xt_set_info_match_v3),
618		.checkentry	= set_match_v3_checkentry,
619		.destroy	= set_match_v3_destroy,
620		.me		= THIS_MODULE
621	},
622	/* new revision for counters support: update, match */
623	{
624		.name		= "set",
625		.family		= NFPROTO_IPV4,
626		.revision	= 4,
627		.match		= set_match_v4,
628		.matchsize	= sizeof(struct xt_set_info_match_v4),
629		.checkentry	= set_match_v4_checkentry,
630		.destroy	= set_match_v4_destroy,
631		.me		= THIS_MODULE
632	},
633	{
634		.name		= "set",
635		.family		= NFPROTO_IPV6,
636		.revision	= 4,
637		.match		= set_match_v4,
638		.matchsize	= sizeof(struct xt_set_info_match_v4),
639		.checkentry	= set_match_v4_checkentry,
640		.destroy	= set_match_v4_destroy,
641		.me		= THIS_MODULE
642	},
643};
644
645static struct xt_target set_targets[] __read_mostly = {
646	{
647		.name		= "SET",
648		.revision	= 0,
649		.family		= NFPROTO_IPV4,
650		.target		= set_target_v0,
651		.targetsize	= sizeof(struct xt_set_info_target_v0),
652		.checkentry	= set_target_v0_checkentry,
653		.destroy	= set_target_v0_destroy,
654		.me		= THIS_MODULE
655	},
656	{
657		.name		= "SET",
658		.revision	= 1,
659		.family		= NFPROTO_IPV4,
660		.target		= set_target_v1,
661		.targetsize	= sizeof(struct xt_set_info_target_v1),
662		.checkentry	= set_target_v1_checkentry,
663		.destroy	= set_target_v1_destroy,
664		.me		= THIS_MODULE
665	},
666	{
667		.name		= "SET",
668		.revision	= 1,
669		.family		= NFPROTO_IPV6,
670		.target		= set_target_v1,
671		.targetsize	= sizeof(struct xt_set_info_target_v1),
672		.checkentry	= set_target_v1_checkentry,
673		.destroy	= set_target_v1_destroy,
674		.me		= THIS_MODULE
675	},
676	/* --timeout and --exist flags support */
677	{
678		.name		= "SET",
679		.revision	= 2,
680		.family		= NFPROTO_IPV4,
681		.target		= set_target_v2,
682		.targetsize	= sizeof(struct xt_set_info_target_v2),
683		.checkentry	= set_target_v2_checkentry,
684		.destroy	= set_target_v2_destroy,
685		.me		= THIS_MODULE
686	},
687	{
688		.name		= "SET",
689		.revision	= 2,
690		.family		= NFPROTO_IPV6,
691		.target		= set_target_v2,
692		.targetsize	= sizeof(struct xt_set_info_target_v2),
693		.checkentry	= set_target_v2_checkentry,
694		.destroy	= set_target_v2_destroy,
695		.me		= THIS_MODULE
696	},
697	/* --map-set support */
698	{
699		.name		= "SET",
700		.revision	= 3,
701		.family		= NFPROTO_IPV4,
702		.target		= set_target_v3,
703		.targetsize	= sizeof(struct xt_set_info_target_v3),
704		.checkentry	= set_target_v3_checkentry,
705		.destroy	= set_target_v3_destroy,
706		.me		= THIS_MODULE
707	},
708	{
709		.name		= "SET",
710		.revision	= 3,
711		.family		= NFPROTO_IPV6,
712		.target		= set_target_v3,
713		.targetsize	= sizeof(struct xt_set_info_target_v3),
714		.checkentry	= set_target_v3_checkentry,
715		.destroy	= set_target_v3_destroy,
716		.me		= THIS_MODULE
717	},
718};
719
720static int __init xt_set_init(void)
721{
722	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
723
724	if (!ret) {
725		ret = xt_register_targets(set_targets,
726					  ARRAY_SIZE(set_targets));
727		if (ret)
728			xt_unregister_matches(set_matches,
729					      ARRAY_SIZE(set_matches));
730	}
731	return ret;
732}
733
734static void __exit xt_set_fini(void)
735{
736	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
737	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
738}
739
740module_init(xt_set_init);
741module_exit(xt_set_fini);
742