xref: /linux/net/netfilter/xt_set.c (revision fcee7d82f27d6a8b1ddc5bbefda59b4e441e9bc0)
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_check_hooks(const struct xt_tgchk_param * par)434 set_target_v3_check_hooks(const struct xt_tgchk_param *par)
435 {
436 	const struct xt_set_info_target_v3 *info = par->targinfo;
437 
438 	if (info->map_set.index != IPSET_INVALID_ID) {
439 		if (strncmp(par->table, "mangle", 7)) {
440 			pr_info_ratelimited("--map-set only usable from mangle table\n");
441 			return -EINVAL;
442 		}
443 		if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
444 		     (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
445 		     (par->hook_mask & ~(1 << NF_INET_FORWARD |
446 					 1 << NF_INET_LOCAL_OUT |
447 					 1 << NF_INET_POST_ROUTING))) {
448 			pr_info_ratelimited("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
449 			return -EINVAL;
450 		}
451 	}
452 
453 	return 0;
454 }
455 
456 static int
set_target_v3_checkentry(const struct xt_tgchk_param * par)457 set_target_v3_checkentry(const struct xt_tgchk_param *par)
458 {
459 	const struct xt_set_info_target_v3 *info = par->targinfo;
460 	ip_set_id_t index;
461 	int ret = 0;
462 
463 	if (info->add_set.index != IPSET_INVALID_ID) {
464 		index = ip_set_nfnl_get_byindex(par->net,
465 						info->add_set.index);
466 		if (index == IPSET_INVALID_ID) {
467 			pr_info_ratelimited("Cannot find add_set index %u as target\n",
468 					    info->add_set.index);
469 			return -ENOENT;
470 		}
471 	}
472 
473 	if (info->del_set.index != IPSET_INVALID_ID) {
474 		index = ip_set_nfnl_get_byindex(par->net,
475 						info->del_set.index);
476 		if (index == IPSET_INVALID_ID) {
477 			pr_info_ratelimited("Cannot find del_set index %u as target\n",
478 					    info->del_set.index);
479 			ret = -ENOENT;
480 			goto cleanup_add;
481 		}
482 	}
483 
484 	if (info->map_set.index != IPSET_INVALID_ID) {
485 		index = ip_set_nfnl_get_byindex(par->net,
486 						info->map_set.index);
487 		if (index == IPSET_INVALID_ID) {
488 			pr_info_ratelimited("Cannot find map_set index %u as target\n",
489 					    info->map_set.index);
490 			ret = -ENOENT;
491 			goto cleanup_del;
492 		}
493 	}
494 
495 	if (info->add_set.dim > IPSET_DIM_MAX ||
496 	    info->del_set.dim > IPSET_DIM_MAX ||
497 	    info->map_set.dim > IPSET_DIM_MAX) {
498 		pr_info_ratelimited("SET target dimension over the limit!\n");
499 		ret = -ERANGE;
500 		goto cleanup_mark;
501 	}
502 
503 	return 0;
504 cleanup_mark:
505 	if (info->map_set.index != IPSET_INVALID_ID)
506 		ip_set_nfnl_put(par->net, info->map_set.index);
507 cleanup_del:
508 	if (info->del_set.index != IPSET_INVALID_ID)
509 		ip_set_nfnl_put(par->net, info->del_set.index);
510 cleanup_add:
511 	if (info->add_set.index != IPSET_INVALID_ID)
512 		ip_set_nfnl_put(par->net, info->add_set.index);
513 	return ret;
514 }
515 
516 static void
set_target_v3_destroy(const struct xt_tgdtor_param * par)517 set_target_v3_destroy(const struct xt_tgdtor_param *par)
518 {
519 	const struct xt_set_info_target_v3 *info = par->targinfo;
520 
521 	if (info->add_set.index != IPSET_INVALID_ID)
522 		ip_set_nfnl_put(par->net, info->add_set.index);
523 	if (info->del_set.index != IPSET_INVALID_ID)
524 		ip_set_nfnl_put(par->net, info->del_set.index);
525 	if (info->map_set.index != IPSET_INVALID_ID)
526 		ip_set_nfnl_put(par->net, info->map_set.index);
527 }
528 
529 static struct xt_match set_matches[] __read_mostly = {
530 	{
531 		.name		= "set",
532 		.family		= NFPROTO_IPV4,
533 		.revision	= 0,
534 		.match		= set_match_v0,
535 		.matchsize	= sizeof(struct xt_set_info_match_v0),
536 		.checkentry	= set_match_v0_checkentry,
537 		.destroy	= set_match_v0_destroy,
538 		.me		= THIS_MODULE
539 	},
540 	{
541 		.name		= "set",
542 		.family		= NFPROTO_IPV4,
543 		.revision	= 1,
544 		.match		= set_match_v1,
545 		.matchsize	= sizeof(struct xt_set_info_match_v1),
546 		.checkentry	= set_match_v1_checkentry,
547 		.destroy	= set_match_v1_destroy,
548 		.me		= THIS_MODULE
549 	},
550 	{
551 		.name		= "set",
552 		.family		= NFPROTO_IPV6,
553 		.revision	= 1,
554 		.match		= set_match_v1,
555 		.matchsize	= sizeof(struct xt_set_info_match_v1),
556 		.checkentry	= set_match_v1_checkentry,
557 		.destroy	= set_match_v1_destroy,
558 		.me		= THIS_MODULE
559 	},
560 	/* --return-nomatch flag support */
561 	{
562 		.name		= "set",
563 		.family		= NFPROTO_IPV4,
564 		.revision	= 2,
565 		.match		= set_match_v1,
566 		.matchsize	= sizeof(struct xt_set_info_match_v1),
567 		.checkentry	= set_match_v1_checkentry,
568 		.destroy	= set_match_v1_destroy,
569 		.me		= THIS_MODULE
570 	},
571 	{
572 		.name		= "set",
573 		.family		= NFPROTO_IPV6,
574 		.revision	= 2,
575 		.match		= set_match_v1,
576 		.matchsize	= sizeof(struct xt_set_info_match_v1),
577 		.checkentry	= set_match_v1_checkentry,
578 		.destroy	= set_match_v1_destroy,
579 		.me		= THIS_MODULE
580 	},
581 	/* counters support: update, match */
582 	{
583 		.name		= "set",
584 		.family		= NFPROTO_IPV4,
585 		.revision	= 3,
586 		.match		= set_match_v3,
587 		.matchsize	= sizeof(struct xt_set_info_match_v3),
588 		.checkentry	= set_match_v3_checkentry,
589 		.destroy	= set_match_v3_destroy,
590 		.me		= THIS_MODULE
591 	},
592 	{
593 		.name		= "set",
594 		.family		= NFPROTO_IPV6,
595 		.revision	= 3,
596 		.match		= set_match_v3,
597 		.matchsize	= sizeof(struct xt_set_info_match_v3),
598 		.checkentry	= set_match_v3_checkentry,
599 		.destroy	= set_match_v3_destroy,
600 		.me		= THIS_MODULE
601 	},
602 	/* new revision for counters support: update, match */
603 	{
604 		.name		= "set",
605 		.family		= NFPROTO_IPV4,
606 		.revision	= 4,
607 		.match		= set_match_v4,
608 		.matchsize	= sizeof(struct xt_set_info_match_v4),
609 		.checkentry	= set_match_v4_checkentry,
610 		.destroy	= set_match_v4_destroy,
611 		.me		= THIS_MODULE
612 	},
613 	{
614 		.name		= "set",
615 		.family		= NFPROTO_IPV6,
616 		.revision	= 4,
617 		.match		= set_match_v4,
618 		.matchsize	= sizeof(struct xt_set_info_match_v4),
619 		.checkentry	= set_match_v4_checkentry,
620 		.destroy	= set_match_v4_destroy,
621 		.me		= THIS_MODULE
622 	},
623 };
624 
625 static struct xt_target set_targets[] __read_mostly = {
626 	{
627 		.name		= "SET",
628 		.revision	= 0,
629 		.family		= NFPROTO_IPV4,
630 		.target		= set_target_v0,
631 		.targetsize	= sizeof(struct xt_set_info_target_v0),
632 		.checkentry	= set_target_v0_checkentry,
633 		.destroy	= set_target_v0_destroy,
634 		.me		= THIS_MODULE
635 	},
636 	{
637 		.name		= "SET",
638 		.revision	= 1,
639 		.family		= NFPROTO_IPV4,
640 		.target		= set_target_v1,
641 		.targetsize	= sizeof(struct xt_set_info_target_v1),
642 		.checkentry	= set_target_v1_checkentry,
643 		.destroy	= set_target_v1_destroy,
644 		.me		= THIS_MODULE
645 	},
646 	{
647 		.name		= "SET",
648 		.revision	= 1,
649 		.family		= NFPROTO_IPV6,
650 		.target		= set_target_v1,
651 		.targetsize	= sizeof(struct xt_set_info_target_v1),
652 		.checkentry	= set_target_v1_checkentry,
653 		.destroy	= set_target_v1_destroy,
654 		.me		= THIS_MODULE
655 	},
656 	/* --timeout and --exist flags support */
657 	{
658 		.name		= "SET",
659 		.revision	= 2,
660 		.family		= NFPROTO_IPV4,
661 		.target		= set_target_v2,
662 		.targetsize	= sizeof(struct xt_set_info_target_v2),
663 		.checkentry	= set_target_v2_checkentry,
664 		.destroy	= set_target_v2_destroy,
665 		.me		= THIS_MODULE
666 	},
667 	{
668 		.name		= "SET",
669 		.revision	= 2,
670 		.family		= NFPROTO_IPV6,
671 		.target		= set_target_v2,
672 		.targetsize	= sizeof(struct xt_set_info_target_v2),
673 		.checkentry	= set_target_v2_checkentry,
674 		.destroy	= set_target_v2_destroy,
675 		.me		= THIS_MODULE
676 	},
677 	/* --map-set support */
678 	{
679 		.name		= "SET",
680 		.revision	= 3,
681 		.family		= NFPROTO_IPV4,
682 		.target		= set_target_v3,
683 		.targetsize	= sizeof(struct xt_set_info_target_v3),
684 		.check_hooks	= set_target_v3_check_hooks,
685 		.checkentry	= set_target_v3_checkentry,
686 		.destroy	= set_target_v3_destroy,
687 		.me		= THIS_MODULE
688 	},
689 	{
690 		.name		= "SET",
691 		.revision	= 3,
692 		.family		= NFPROTO_IPV6,
693 		.target		= set_target_v3,
694 		.targetsize	= sizeof(struct xt_set_info_target_v3),
695 		.check_hooks	= set_target_v3_check_hooks,
696 		.checkentry	= set_target_v3_checkentry,
697 		.destroy	= set_target_v3_destroy,
698 		.me		= THIS_MODULE
699 	},
700 };
701 
xt_set_init(void)702 static int __init xt_set_init(void)
703 {
704 	int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
705 
706 	if (!ret) {
707 		ret = xt_register_targets(set_targets,
708 					  ARRAY_SIZE(set_targets));
709 		if (ret)
710 			xt_unregister_matches(set_matches,
711 					      ARRAY_SIZE(set_matches));
712 	}
713 	return ret;
714 }
715 
xt_set_fini(void)716 static void __exit xt_set_fini(void)
717 {
718 	xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
719 	xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
720 }
721 
722 module_init(xt_set_init);
723 module_exit(xt_set_fini);
724