xref: /openbmc/linux/net/netfilter/xt_set.c (revision 8b036556)
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 
21 MODULE_LICENSE("GPL");
22 MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
23 MODULE_DESCRIPTION("Xtables: IP set match and target module");
24 MODULE_ALIAS("xt_SET");
25 MODULE_ALIAS("ipt_set");
26 MODULE_ALIAS("ip6t_set");
27 MODULE_ALIAS("ipt_SET");
28 MODULE_ALIAS("ip6t_SET");
29 
30 static inline int
31 match_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)	\
41 struct 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 
51 static bool
52 set_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 
62 static void
63 compat_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 
78 static int
79 set_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 
103 static void
104 set_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 
113 static bool
114 set_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 
127 static int
128 set_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 
149 static void
150 set_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 
159 static bool
160 match_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 
177 static bool
178 set_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 0;
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 
205 static bool
206 match_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 
223 static bool
224 set_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 0;
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 
251 static unsigned int
252 set_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 
268 static int
269 set_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 
310 static void
311 set_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 
323 static unsigned int
324 set_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 
340 static int
341 set_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 
378 static void
379 set_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 
391 static unsigned int
392 set_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 
417 static unsigned int
418 set_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 
460 static int
461 set_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 
535 static void
536 set_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 
549 static 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 
645 static 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 
720 static 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 
734 static 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 
740 module_init(xt_set_init);
741 module_exit(xt_set_fini);
742