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