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