xref: /openbmc/linux/net/netfilter/xt_set.c (revision ec2da07c)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu>
3  *                         Patrick Schaaf <bof@bof.de>
4  *                         Martin Josefsson <gandalf@wlug.westbo.se>
5  * Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@netfilter.org>
6  */
7 
8 /* Kernel module which implements the set match and SET target
9  * for netfilter/iptables.
10  */
11 
12 #include <linux/module.h>
13 #include <linux/skbuff.h>
14 
15 #include <linux/netfilter/x_tables.h>
16 #include <linux/netfilter/ipset/ip_set.h>
17 #include <linux/netfilter/ipset/ip_set_timeout.h>
18 #include <uapi/linux/netfilter/xt_set.h>
19 
20 MODULE_LICENSE("GPL");
21 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>");
22 MODULE_DESCRIPTION("Xtables: IP set match and target module");
23 MODULE_ALIAS("xt_SET");
24 MODULE_ALIAS("ipt_set");
25 MODULE_ALIAS("ip6t_set");
26 MODULE_ALIAS("ipt_SET");
27 MODULE_ALIAS("ip6t_SET");
28 
29 static inline int
30 match_set(ip_set_id_t index, const struct sk_buff *skb,
31 	  const struct xt_action_param *par,
32 	  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, p, b, po, bo)	\
40 struct ip_set_adt_opt n = {				\
41 	.family	= f,					\
42 	.dim = d,					\
43 	.flags = fs,					\
44 	.cmdflags = cfs,				\
45 	.ext.timeout = t,				\
46 	.ext.packets = p,				\
47 	.ext.bytes = b,					\
48 	.ext.packets_op = po,				\
49 	.ext.bytes_op = bo,				\
50 }
51 
52 /* Revision 0 interface: backward compatible with netfilter/iptables */
53 
54 static bool
55 set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
56 {
57 	const struct xt_set_info_match_v0 *info = par->matchinfo;
58 
59 	ADT_OPT(opt, xt_family(par), info->match_set.u.compat.dim,
60 		info->match_set.u.compat.flags, 0, UINT_MAX,
61 		0, 0, 0, 0);
62 
63 	return match_set(info->match_set.index, skb, par, &opt,
64 			 info->match_set.u.compat.flags & IPSET_INV_MATCH);
65 }
66 
67 static void
68 compat_flags(struct xt_set_info_v0 *info)
69 {
70 	u_int8_t i;
71 
72 	/* Fill out compatibility data according to enum ip_set_kopt */
73 	info->u.compat.dim = IPSET_DIM_ZERO;
74 	if (info->u.flags[0] & IPSET_MATCH_INV)
75 		info->u.compat.flags |= IPSET_INV_MATCH;
76 	for (i = 0; i < IPSET_DIM_MAX - 1 && info->u.flags[i]; i++) {
77 		info->u.compat.dim++;
78 		if (info->u.flags[i] & IPSET_SRC)
79 			info->u.compat.flags |= (1 << info->u.compat.dim);
80 	}
81 }
82 
83 static int
84 set_match_v0_checkentry(const struct xt_mtchk_param *par)
85 {
86 	struct xt_set_info_match_v0 *info = par->matchinfo;
87 	ip_set_id_t index;
88 
89 	index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
90 
91 	if (index == IPSET_INVALID_ID) {
92 		pr_info_ratelimited("Cannot find set identified by id %u to match\n",
93 				    info->match_set.index);
94 		return -ENOENT;
95 	}
96 	if (info->match_set.u.flags[IPSET_DIM_MAX - 1] != 0) {
97 		pr_info_ratelimited("set match dimension is over the limit!\n");
98 		ip_set_nfnl_put(par->net, info->match_set.index);
99 		return -ERANGE;
100 	}
101 
102 	/* Fill out compatibility data */
103 	compat_flags(&info->match_set);
104 
105 	return 0;
106 }
107 
108 static void
109 set_match_v0_destroy(const struct xt_mtdtor_param *par)
110 {
111 	struct xt_set_info_match_v0 *info = par->matchinfo;
112 
113 	ip_set_nfnl_put(par->net, info->match_set.index);
114 }
115 
116 /* Revision 1 match */
117 
118 static bool
119 set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
120 {
121 	const struct xt_set_info_match_v1 *info = par->matchinfo;
122 
123 	ADT_OPT(opt, xt_family(par), info->match_set.dim,
124 		info->match_set.flags, 0, UINT_MAX,
125 		0, 0, 0, 0);
126 
127 	if (opt.flags & IPSET_RETURN_NOMATCH)
128 		opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
129 
130 	return match_set(info->match_set.index, skb, par, &opt,
131 			 info->match_set.flags & IPSET_INV_MATCH);
132 }
133 
134 static int
135 set_match_v1_checkentry(const struct xt_mtchk_param *par)
136 {
137 	struct xt_set_info_match_v1 *info = par->matchinfo;
138 	ip_set_id_t index;
139 
140 	index = ip_set_nfnl_get_byindex(par->net, info->match_set.index);
141 
142 	if (index == IPSET_INVALID_ID) {
143 		pr_info_ratelimited("Cannot find set identified by id %u to match\n",
144 				    info->match_set.index);
145 		return -ENOENT;
146 	}
147 	if (info->match_set.dim > IPSET_DIM_MAX) {
148 		pr_info_ratelimited("set match dimension is over the limit!\n");
149 		ip_set_nfnl_put(par->net, info->match_set.index);
150 		return -ERANGE;
151 	}
152 
153 	return 0;
154 }
155 
156 static void
157 set_match_v1_destroy(const struct xt_mtdtor_param *par)
158 {
159 	struct xt_set_info_match_v1 *info = par->matchinfo;
160 
161 	ip_set_nfnl_put(par->net, info->match_set.index);
162 }
163 
164 /* Revision 3 match */
165 
166 static bool
167 set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
168 {
169 	const struct xt_set_info_match_v3 *info = par->matchinfo;
170 
171 	ADT_OPT(opt, xt_family(par), info->match_set.dim,
172 		info->match_set.flags, info->flags, UINT_MAX,
173 		info->packets.value, info->bytes.value,
174 		info->packets.op, info->bytes.op);
175 
176 	if (info->packets.op != IPSET_COUNTER_NONE ||
177 	    info->bytes.op != IPSET_COUNTER_NONE)
178 		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
179 
180 	return match_set(info->match_set.index, skb, par, &opt,
181 			 info->match_set.flags & IPSET_INV_MATCH);
182 }
183 
184 #define set_match_v3_checkentry	set_match_v1_checkentry
185 #define set_match_v3_destroy	set_match_v1_destroy
186 
187 /* Revision 4 match */
188 
189 static bool
190 set_match_v4(const struct sk_buff *skb, struct xt_action_param *par)
191 {
192 	const struct xt_set_info_match_v4 *info = par->matchinfo;
193 
194 	ADT_OPT(opt, xt_family(par), info->match_set.dim,
195 		info->match_set.flags, info->flags, UINT_MAX,
196 		info->packets.value, info->bytes.value,
197 		info->packets.op, info->bytes.op);
198 
199 	if (info->packets.op != IPSET_COUNTER_NONE ||
200 	    info->bytes.op != IPSET_COUNTER_NONE)
201 		opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
202 
203 	return match_set(info->match_set.index, skb, par, &opt,
204 			 info->match_set.flags & IPSET_INV_MATCH);
205 }
206 
207 #define set_match_v4_checkentry	set_match_v1_checkentry
208 #define set_match_v4_destroy	set_match_v1_destroy
209 
210 /* Revision 0 interface: backward compatible with netfilter/iptables */
211 
212 static unsigned int
213 set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
214 {
215 	const struct xt_set_info_target_v0 *info = par->targinfo;
216 
217 	ADT_OPT(add_opt, xt_family(par), info->add_set.u.compat.dim,
218 		info->add_set.u.compat.flags, 0, UINT_MAX,
219 		0, 0, 0, 0);
220 	ADT_OPT(del_opt, xt_family(par), info->del_set.u.compat.dim,
221 		info->del_set.u.compat.flags, 0, UINT_MAX,
222 		0, 0, 0, 0);
223 
224 	if (info->add_set.index != IPSET_INVALID_ID)
225 		ip_set_add(info->add_set.index, skb, par, &add_opt);
226 	if (info->del_set.index != IPSET_INVALID_ID)
227 		ip_set_del(info->del_set.index, skb, par, &del_opt);
228 
229 	return XT_CONTINUE;
230 }
231 
232 static int
233 set_target_v0_checkentry(const struct xt_tgchk_param *par)
234 {
235 	struct xt_set_info_target_v0 *info = par->targinfo;
236 	ip_set_id_t index;
237 
238 	if (info->add_set.index != IPSET_INVALID_ID) {
239 		index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
240 		if (index == IPSET_INVALID_ID) {
241 			pr_info_ratelimited("Cannot find add_set index %u as target\n",
242 					    info->add_set.index);
243 			return -ENOENT;
244 		}
245 	}
246 
247 	if (info->del_set.index != IPSET_INVALID_ID) {
248 		index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
249 		if (index == IPSET_INVALID_ID) {
250 			pr_info_ratelimited("Cannot find del_set index %u as target\n",
251 					    info->del_set.index);
252 			if (info->add_set.index != IPSET_INVALID_ID)
253 				ip_set_nfnl_put(par->net, info->add_set.index);
254 			return -ENOENT;
255 		}
256 	}
257 	if (info->add_set.u.flags[IPSET_DIM_MAX - 1] != 0 ||
258 	    info->del_set.u.flags[IPSET_DIM_MAX - 1] != 0) {
259 		pr_info_ratelimited("SET target dimension over the limit!\n");
260 		if (info->add_set.index != IPSET_INVALID_ID)
261 			ip_set_nfnl_put(par->net, info->add_set.index);
262 		if (info->del_set.index != IPSET_INVALID_ID)
263 			ip_set_nfnl_put(par->net, info->del_set.index);
264 		return -ERANGE;
265 	}
266 
267 	/* Fill out compatibility data */
268 	compat_flags(&info->add_set);
269 	compat_flags(&info->del_set);
270 
271 	return 0;
272 }
273 
274 static void
275 set_target_v0_destroy(const struct xt_tgdtor_param *par)
276 {
277 	const struct xt_set_info_target_v0 *info = par->targinfo;
278 
279 	if (info->add_set.index != IPSET_INVALID_ID)
280 		ip_set_nfnl_put(par->net, info->add_set.index);
281 	if (info->del_set.index != IPSET_INVALID_ID)
282 		ip_set_nfnl_put(par->net, info->del_set.index);
283 }
284 
285 /* Revision 1 target */
286 
287 static unsigned int
288 set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
289 {
290 	const struct xt_set_info_target_v1 *info = par->targinfo;
291 
292 	ADT_OPT(add_opt, xt_family(par), info->add_set.dim,
293 		info->add_set.flags, 0, UINT_MAX,
294 		0, 0, 0, 0);
295 	ADT_OPT(del_opt, xt_family(par), info->del_set.dim,
296 		info->del_set.flags, 0, UINT_MAX,
297 		0, 0, 0, 0);
298 
299 	if (info->add_set.index != IPSET_INVALID_ID)
300 		ip_set_add(info->add_set.index, skb, par, &add_opt);
301 	if (info->del_set.index != IPSET_INVALID_ID)
302 		ip_set_del(info->del_set.index, skb, par, &del_opt);
303 
304 	return XT_CONTINUE;
305 }
306 
307 static int
308 set_target_v1_checkentry(const struct xt_tgchk_param *par)
309 {
310 	const struct xt_set_info_target_v1 *info = par->targinfo;
311 	ip_set_id_t index;
312 
313 	if (info->add_set.index != IPSET_INVALID_ID) {
314 		index = ip_set_nfnl_get_byindex(par->net, info->add_set.index);
315 		if (index == IPSET_INVALID_ID) {
316 			pr_info_ratelimited("Cannot find add_set index %u as target\n",
317 					    info->add_set.index);
318 			return -ENOENT;
319 		}
320 	}
321 
322 	if (info->del_set.index != IPSET_INVALID_ID) {
323 		index = ip_set_nfnl_get_byindex(par->net, info->del_set.index);
324 		if (index == IPSET_INVALID_ID) {
325 			pr_info_ratelimited("Cannot find del_set index %u as target\n",
326 					    info->del_set.index);
327 			if (info->add_set.index != IPSET_INVALID_ID)
328 				ip_set_nfnl_put(par->net, info->add_set.index);
329 			return -ENOENT;
330 		}
331 	}
332 	if (info->add_set.dim > IPSET_DIM_MAX ||
333 	    info->del_set.dim > IPSET_DIM_MAX) {
334 		pr_info_ratelimited("SET target dimension over the limit!\n");
335 		if (info->add_set.index != IPSET_INVALID_ID)
336 			ip_set_nfnl_put(par->net, info->add_set.index);
337 		if (info->del_set.index != IPSET_INVALID_ID)
338 			ip_set_nfnl_put(par->net, info->del_set.index);
339 		return -ERANGE;
340 	}
341 
342 	return 0;
343 }
344 
345 static void
346 set_target_v1_destroy(const struct xt_tgdtor_param *par)
347 {
348 	const struct xt_set_info_target_v1 *info = par->targinfo;
349 
350 	if (info->add_set.index != IPSET_INVALID_ID)
351 		ip_set_nfnl_put(par->net, info->add_set.index);
352 	if (info->del_set.index != IPSET_INVALID_ID)
353 		ip_set_nfnl_put(par->net, info->del_set.index);
354 }
355 
356 /* Revision 2 target */
357 
358 static unsigned int
359 set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
360 {
361 	const struct xt_set_info_target_v2 *info = par->targinfo;
362 
363 	ADT_OPT(add_opt, xt_family(par), info->add_set.dim,
364 		info->add_set.flags, info->flags, info->timeout,
365 		0, 0, 0, 0);
366 	ADT_OPT(del_opt, xt_family(par), info->del_set.dim,
367 		info->del_set.flags, 0, UINT_MAX,
368 		0, 0, 0, 0);
369 
370 	/* Normalize to fit into jiffies */
371 	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
372 	    add_opt.ext.timeout > IPSET_MAX_TIMEOUT)
373 		add_opt.ext.timeout = IPSET_MAX_TIMEOUT;
374 	if (info->add_set.index != IPSET_INVALID_ID)
375 		ip_set_add(info->add_set.index, skb, par, &add_opt);
376 	if (info->del_set.index != IPSET_INVALID_ID)
377 		ip_set_del(info->del_set.index, skb, par, &del_opt);
378 
379 	return XT_CONTINUE;
380 }
381 
382 #define set_target_v2_checkentry	set_target_v1_checkentry
383 #define set_target_v2_destroy		set_target_v1_destroy
384 
385 /* Revision 3 target */
386 
387 #define MOPT(opt, member)	((opt).ext.skbinfo.member)
388 
389 static unsigned int
390 set_target_v3(struct sk_buff *skb, const struct xt_action_param *par)
391 {
392 	const struct xt_set_info_target_v3 *info = par->targinfo;
393 	int ret;
394 
395 	ADT_OPT(add_opt, xt_family(par), info->add_set.dim,
396 		info->add_set.flags, info->flags, info->timeout,
397 		0, 0, 0, 0);
398 	ADT_OPT(del_opt, xt_family(par), info->del_set.dim,
399 		info->del_set.flags, 0, UINT_MAX,
400 		0, 0, 0, 0);
401 	ADT_OPT(map_opt, xt_family(par), info->map_set.dim,
402 		info->map_set.flags, 0, UINT_MAX,
403 		0, 0, 0, 0);
404 
405 	/* Normalize to fit into jiffies */
406 	if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
407 	    add_opt.ext.timeout > IPSET_MAX_TIMEOUT)
408 		add_opt.ext.timeout = IPSET_MAX_TIMEOUT;
409 	if (info->add_set.index != IPSET_INVALID_ID)
410 		ip_set_add(info->add_set.index, skb, par, &add_opt);
411 	if (info->del_set.index != IPSET_INVALID_ID)
412 		ip_set_del(info->del_set.index, skb, par, &del_opt);
413 	if (info->map_set.index != IPSET_INVALID_ID) {
414 		map_opt.cmdflags |= info->flags & (IPSET_FLAG_MAP_SKBMARK |
415 						   IPSET_FLAG_MAP_SKBPRIO |
416 						   IPSET_FLAG_MAP_SKBQUEUE);
417 		ret = match_set(info->map_set.index, skb, par, &map_opt,
418 				info->map_set.flags & IPSET_INV_MATCH);
419 		if (!ret)
420 			return XT_CONTINUE;
421 		if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBMARK)
422 			skb->mark = (skb->mark & ~MOPT(map_opt,skbmarkmask))
423 				    ^ MOPT(map_opt, skbmark);
424 		if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBPRIO)
425 			skb->priority = MOPT(map_opt, skbprio);
426 		if ((map_opt.cmdflags & IPSET_FLAG_MAP_SKBQUEUE) &&
427 		    skb->dev &&
428 		    skb->dev->real_num_tx_queues > MOPT(map_opt, skbqueue))
429 			skb_set_queue_mapping(skb, MOPT(map_opt, skbqueue));
430 	}
431 	return XT_CONTINUE;
432 }
433 
434 static int
435 set_target_v3_checkentry(const struct xt_tgchk_param *par)
436 {
437 	const struct xt_set_info_target_v3 *info = par->targinfo;
438 	ip_set_id_t index;
439 	int ret = 0;
440 
441 	if (info->add_set.index != IPSET_INVALID_ID) {
442 		index = ip_set_nfnl_get_byindex(par->net,
443 						info->add_set.index);
444 		if (index == IPSET_INVALID_ID) {
445 			pr_info_ratelimited("Cannot find add_set index %u as target\n",
446 					    info->add_set.index);
447 			return -ENOENT;
448 		}
449 	}
450 
451 	if (info->del_set.index != IPSET_INVALID_ID) {
452 		index = ip_set_nfnl_get_byindex(par->net,
453 						info->del_set.index);
454 		if (index == IPSET_INVALID_ID) {
455 			pr_info_ratelimited("Cannot find del_set index %u as target\n",
456 					    info->del_set.index);
457 			ret = -ENOENT;
458 			goto cleanup_add;
459 		}
460 	}
461 
462 	if (info->map_set.index != IPSET_INVALID_ID) {
463 		if (strncmp(par->table, "mangle", 7)) {
464 			pr_info_ratelimited("--map-set only usable from mangle table\n");
465 			ret = -EINVAL;
466 			goto cleanup_del;
467 		}
468 		if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
469 		     (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
470 		     (par->hook_mask & ~(1 << NF_INET_FORWARD |
471 					 1 << NF_INET_LOCAL_OUT |
472 					 1 << NF_INET_POST_ROUTING))) {
473 			pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
474 			ret = -EINVAL;
475 			goto cleanup_del;
476 		}
477 		index = ip_set_nfnl_get_byindex(par->net,
478 						info->map_set.index);
479 		if (index == IPSET_INVALID_ID) {
480 			pr_info_ratelimited("Cannot find map_set index %u as target\n",
481 					    info->map_set.index);
482 			ret = -ENOENT;
483 			goto cleanup_del;
484 		}
485 	}
486 
487 	if (info->add_set.dim > IPSET_DIM_MAX ||
488 	    info->del_set.dim > IPSET_DIM_MAX ||
489 	    info->map_set.dim > IPSET_DIM_MAX) {
490 		pr_info_ratelimited("SET target dimension over the limit!\n");
491 		ret = -ERANGE;
492 		goto cleanup_mark;
493 	}
494 
495 	return 0;
496 cleanup_mark:
497 	if (info->map_set.index != IPSET_INVALID_ID)
498 		ip_set_nfnl_put(par->net, info->map_set.index);
499 cleanup_del:
500 	if (info->del_set.index != IPSET_INVALID_ID)
501 		ip_set_nfnl_put(par->net, info->del_set.index);
502 cleanup_add:
503 	if (info->add_set.index != IPSET_INVALID_ID)
504 		ip_set_nfnl_put(par->net, info->add_set.index);
505 	return ret;
506 }
507 
508 static void
509 set_target_v3_destroy(const struct xt_tgdtor_param *par)
510 {
511 	const struct xt_set_info_target_v3 *info = par->targinfo;
512 
513 	if (info->add_set.index != IPSET_INVALID_ID)
514 		ip_set_nfnl_put(par->net, info->add_set.index);
515 	if (info->del_set.index != IPSET_INVALID_ID)
516 		ip_set_nfnl_put(par->net, info->del_set.index);
517 	if (info->map_set.index != IPSET_INVALID_ID)
518 		ip_set_nfnl_put(par->net, info->map_set.index);
519 }
520 
521 static struct xt_match set_matches[] __read_mostly = {
522 	{
523 		.name		= "set",
524 		.family		= NFPROTO_IPV4,
525 		.revision	= 0,
526 		.match		= set_match_v0,
527 		.matchsize	= sizeof(struct xt_set_info_match_v0),
528 		.checkentry	= set_match_v0_checkentry,
529 		.destroy	= set_match_v0_destroy,
530 		.me		= THIS_MODULE
531 	},
532 	{
533 		.name		= "set",
534 		.family		= NFPROTO_IPV4,
535 		.revision	= 1,
536 		.match		= set_match_v1,
537 		.matchsize	= sizeof(struct xt_set_info_match_v1),
538 		.checkentry	= set_match_v1_checkentry,
539 		.destroy	= set_match_v1_destroy,
540 		.me		= THIS_MODULE
541 	},
542 	{
543 		.name		= "set",
544 		.family		= NFPROTO_IPV6,
545 		.revision	= 1,
546 		.match		= set_match_v1,
547 		.matchsize	= sizeof(struct xt_set_info_match_v1),
548 		.checkentry	= set_match_v1_checkentry,
549 		.destroy	= set_match_v1_destroy,
550 		.me		= THIS_MODULE
551 	},
552 	/* --return-nomatch flag support */
553 	{
554 		.name		= "set",
555 		.family		= NFPROTO_IPV4,
556 		.revision	= 2,
557 		.match		= set_match_v1,
558 		.matchsize	= sizeof(struct xt_set_info_match_v1),
559 		.checkentry	= set_match_v1_checkentry,
560 		.destroy	= set_match_v1_destroy,
561 		.me		= THIS_MODULE
562 	},
563 	{
564 		.name		= "set",
565 		.family		= NFPROTO_IPV6,
566 		.revision	= 2,
567 		.match		= set_match_v1,
568 		.matchsize	= sizeof(struct xt_set_info_match_v1),
569 		.checkentry	= set_match_v1_checkentry,
570 		.destroy	= set_match_v1_destroy,
571 		.me		= THIS_MODULE
572 	},
573 	/* counters support: update, match */
574 	{
575 		.name		= "set",
576 		.family		= NFPROTO_IPV4,
577 		.revision	= 3,
578 		.match		= set_match_v3,
579 		.matchsize	= sizeof(struct xt_set_info_match_v3),
580 		.checkentry	= set_match_v3_checkentry,
581 		.destroy	= set_match_v3_destroy,
582 		.me		= THIS_MODULE
583 	},
584 	{
585 		.name		= "set",
586 		.family		= NFPROTO_IPV6,
587 		.revision	= 3,
588 		.match		= set_match_v3,
589 		.matchsize	= sizeof(struct xt_set_info_match_v3),
590 		.checkentry	= set_match_v3_checkentry,
591 		.destroy	= set_match_v3_destroy,
592 		.me		= THIS_MODULE
593 	},
594 	/* new revision for counters support: update, match */
595 	{
596 		.name		= "set",
597 		.family		= NFPROTO_IPV4,
598 		.revision	= 4,
599 		.match		= set_match_v4,
600 		.matchsize	= sizeof(struct xt_set_info_match_v4),
601 		.checkentry	= set_match_v4_checkentry,
602 		.destroy	= set_match_v4_destroy,
603 		.me		= THIS_MODULE
604 	},
605 	{
606 		.name		= "set",
607 		.family		= NFPROTO_IPV6,
608 		.revision	= 4,
609 		.match		= set_match_v4,
610 		.matchsize	= sizeof(struct xt_set_info_match_v4),
611 		.checkentry	= set_match_v4_checkentry,
612 		.destroy	= set_match_v4_destroy,
613 		.me		= THIS_MODULE
614 	},
615 };
616 
617 static struct xt_target set_targets[] __read_mostly = {
618 	{
619 		.name		= "SET",
620 		.revision	= 0,
621 		.family		= NFPROTO_IPV4,
622 		.target		= set_target_v0,
623 		.targetsize	= sizeof(struct xt_set_info_target_v0),
624 		.checkentry	= set_target_v0_checkentry,
625 		.destroy	= set_target_v0_destroy,
626 		.me		= THIS_MODULE
627 	},
628 	{
629 		.name		= "SET",
630 		.revision	= 1,
631 		.family		= NFPROTO_IPV4,
632 		.target		= set_target_v1,
633 		.targetsize	= sizeof(struct xt_set_info_target_v1),
634 		.checkentry	= set_target_v1_checkentry,
635 		.destroy	= set_target_v1_destroy,
636 		.me		= THIS_MODULE
637 	},
638 	{
639 		.name		= "SET",
640 		.revision	= 1,
641 		.family		= NFPROTO_IPV6,
642 		.target		= set_target_v1,
643 		.targetsize	= sizeof(struct xt_set_info_target_v1),
644 		.checkentry	= set_target_v1_checkentry,
645 		.destroy	= set_target_v1_destroy,
646 		.me		= THIS_MODULE
647 	},
648 	/* --timeout and --exist flags support */
649 	{
650 		.name		= "SET",
651 		.revision	= 2,
652 		.family		= NFPROTO_IPV4,
653 		.target		= set_target_v2,
654 		.targetsize	= sizeof(struct xt_set_info_target_v2),
655 		.checkentry	= set_target_v2_checkentry,
656 		.destroy	= set_target_v2_destroy,
657 		.me		= THIS_MODULE
658 	},
659 	{
660 		.name		= "SET",
661 		.revision	= 2,
662 		.family		= NFPROTO_IPV6,
663 		.target		= set_target_v2,
664 		.targetsize	= sizeof(struct xt_set_info_target_v2),
665 		.checkentry	= set_target_v2_checkentry,
666 		.destroy	= set_target_v2_destroy,
667 		.me		= THIS_MODULE
668 	},
669 	/* --map-set support */
670 	{
671 		.name		= "SET",
672 		.revision	= 3,
673 		.family		= NFPROTO_IPV4,
674 		.target		= set_target_v3,
675 		.targetsize	= sizeof(struct xt_set_info_target_v3),
676 		.checkentry	= set_target_v3_checkentry,
677 		.destroy	= set_target_v3_destroy,
678 		.me		= THIS_MODULE
679 	},
680 	{
681 		.name		= "SET",
682 		.revision	= 3,
683 		.family		= NFPROTO_IPV6,
684 		.target		= set_target_v3,
685 		.targetsize	= sizeof(struct xt_set_info_target_v3),
686 		.checkentry	= set_target_v3_checkentry,
687 		.destroy	= set_target_v3_destroy,
688 		.me		= THIS_MODULE
689 	},
690 };
691 
692 static int __init xt_set_init(void)
693 {
694 	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
695 
696 	if (!ret) {
697 		ret = xt_register_targets(set_targets,
698 					  ARRAY_SIZE(set_targets));
699 		if (ret)
700 			xt_unregister_matches(set_matches,
701 					      ARRAY_SIZE(set_matches));
702 	}
703 	return ret;
704 }
705 
706 static void __exit xt_set_fini(void)
707 {
708 	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
709 	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
710 }
711 
712 module_init(xt_set_init);
713 module_exit(xt_set_fini);
714