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>
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.
11 /* Kernel module which implements the set match and SET target
12 * for netfilter/iptables.
15 #include <linux/module.h>
16 #include <linux/skbuff.h>
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>
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");
32 #ifdef HAVE_CHECKENTRY_BOOL
34 #define CHECK_FAIL(err) 0
37 #else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) */
39 #define CHECK_FAIL(err) (err)
43 #ifdef HAVE_XT_MTCHK_PARAM_STRUCT_NET
44 #define XT_PAR_NET(par) ((par)->net)
46 #define XT_PAR_NET(par) NULL
50 match_set(ip_set_id_t index, const struct sk_buff *skb,
51 const struct xt_action_param *par,
52 struct ip_set_adt_opt *opt, int inv)
54 if (ip_set_test(index, skb, par, opt))
59 #define ADT_OPT(n, f, d, fs, cfs, t, p, b, po, bo) \
60 struct ip_set_adt_opt n = { \
68 .ext.packets_op = po, \
72 /* Revision 0 interface: backward compatible with netfilter/iptables */
75 set_match_v0(const struct sk_buff *skb, CONST struct xt_action_param *par)
77 const struct xt_set_info_match_v0 *info = par->matchinfo;
79 ADT_OPT(opt, XT_FAMILY(par), info->match_set.u.compat.dim,
80 info->match_set.u.compat.flags, 0, UINT_MAX,
83 return match_set(info->match_set.index, skb, par, &opt,
84 info->match_set.u.compat.flags & IPSET_INV_MATCH);
88 compat_flags(struct xt_set_info_v0 *info)
92 /* Fill out compatibility data according to enum ip_set_kopt */
93 info->u.compat.dim = IPSET_DIM_ZERO;
94 if (info->u.flags[0] & IPSET_MATCH_INV)
95 info->u.compat.flags |= IPSET_INV_MATCH;
96 for (i = 0; i < IPSET_DIM_MAX - 1 && info->u.flags[i]; i++) {
98 if (info->u.flags[i] & IPSET_SRC)
99 info->u.compat.flags |= (1 << info->u.compat.dim);
104 set_match_v0_checkentry(const struct xt_mtchk_param *par)
106 struct xt_set_info_match_v0 *info = par->matchinfo;
109 index = ip_set_nfnl_get_byindex(XT_PAR_NET(par), info->match_set.index);
111 if (index == IPSET_INVALID_ID) {
112 pr_warn("Cannot find set identified by id %u to match\n",
113 info->match_set.index);
114 return CHECK_FAIL(-ENOENT);
116 if (info->match_set.u.flags[IPSET_DIM_MAX - 1] != 0) {
117 pr_warn("Protocol error: set match dimension is over the limit!\n");
118 ip_set_nfnl_put(XT_PAR_NET(par), info->match_set.index);
119 return CHECK_FAIL(-ERANGE);
122 /* Fill out compatibility data */
123 compat_flags(&info->match_set);
129 set_match_v0_destroy(const struct xt_mtdtor_param *par)
131 struct xt_set_info_match_v0 *info = par->matchinfo;
133 ip_set_nfnl_put(XT_PAR_NET(par), info->match_set.index);
139 set_match_v1(const struct sk_buff *skb, CONST struct xt_action_param *par)
141 const struct xt_set_info_match_v1 *info = par->matchinfo;
143 ADT_OPT(opt, XT_FAMILY(par), info->match_set.dim,
144 info->match_set.flags, 0, UINT_MAX,
147 if (opt.flags & IPSET_RETURN_NOMATCH)
148 opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
150 return match_set(info->match_set.index, skb, par, &opt,
151 info->match_set.flags & IPSET_INV_MATCH);
155 set_match_v1_checkentry(const struct xt_mtchk_param *par)
157 struct xt_set_info_match_v1 *info = par->matchinfo;
160 index = ip_set_nfnl_get_byindex(XT_PAR_NET(par), info->match_set.index);
162 if (index == IPSET_INVALID_ID) {
163 pr_warn("Cannot find set identified by id %u to match\n",
164 info->match_set.index);
165 return CHECK_FAIL(-ENOENT);
167 if (info->match_set.dim > IPSET_DIM_MAX) {
168 pr_warn("Protocol error: set match dimension is over the limit!\n");
169 ip_set_nfnl_put(XT_PAR_NET(par), info->match_set.index);
170 return CHECK_FAIL(-ERANGE);
177 set_match_v1_destroy(const struct xt_mtdtor_param *par)
179 struct xt_set_info_match_v1 *info = par->matchinfo;
181 ip_set_nfnl_put(XT_PAR_NET(par), info->match_set.index);
184 /* Revision 3 match */
187 set_match_v3(const struct sk_buff *skb, CONST struct xt_action_param *par)
189 const struct xt_set_info_match_v3 *info = par->matchinfo;
191 ADT_OPT(opt, XT_FAMILY(par), info->match_set.dim,
192 info->match_set.flags, info->flags, UINT_MAX,
193 info->packets.value, info->bytes.value,
194 info->packets.op, info->bytes.op);
196 if (info->packets.op != IPSET_COUNTER_NONE ||
197 info->bytes.op != IPSET_COUNTER_NONE)
198 opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
200 return match_set(info->match_set.index, skb, par, &opt,
201 info->match_set.flags & IPSET_INV_MATCH);
204 #define set_match_v3_checkentry set_match_v1_checkentry
205 #define set_match_v3_destroy set_match_v1_destroy
207 /* Revision 4 match */
210 set_match_v4(const struct sk_buff *skb, CONST struct xt_action_param *par)
212 const struct xt_set_info_match_v4 *info = par->matchinfo;
214 ADT_OPT(opt, XT_FAMILY(par), info->match_set.dim,
215 info->match_set.flags, info->flags, UINT_MAX,
216 info->packets.value, info->bytes.value,
217 info->packets.op, info->bytes.op);
219 if (info->packets.op != IPSET_COUNTER_NONE ||
220 info->bytes.op != IPSET_COUNTER_NONE)
221 opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
223 return match_set(info->match_set.index, skb, par, &opt,
224 info->match_set.flags & IPSET_INV_MATCH);
227 #define set_match_v4_checkentry set_match_v1_checkentry
228 #define set_match_v4_destroy set_match_v1_destroy
230 /* Revision 0 interface: backward compatible with netfilter/iptables */
232 #ifdef HAVE_XT_TARGET_PARAM
233 #undef xt_action_param
234 #define xt_action_param xt_target_param
235 #define CAST_TO_MATCH (const struct xt_match_param *)
237 #define CAST_TO_MATCH
241 set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
243 const struct xt_set_info_target_v0 *info = par->targinfo;
245 ADT_OPT(add_opt, XT_FAMILY(par), info->add_set.u.compat.dim,
246 info->add_set.u.compat.flags, 0, UINT_MAX,
248 ADT_OPT(del_opt, XT_FAMILY(par), info->del_set.u.compat.dim,
249 info->del_set.u.compat.flags, 0, UINT_MAX,
252 if (info->add_set.index != IPSET_INVALID_ID)
253 ip_set_add(info->add_set.index, skb, CAST_TO_MATCH par,
255 if (info->del_set.index != IPSET_INVALID_ID)
256 ip_set_del(info->del_set.index, skb, CAST_TO_MATCH par,
263 set_target_v0_checkentry(const struct xt_tgchk_param *par)
265 struct xt_set_info_target_v0 *info = par->targinfo;
268 if (info->add_set.index != IPSET_INVALID_ID) {
269 index = ip_set_nfnl_get_byindex(XT_PAR_NET(par),
270 info->add_set.index);
271 if (index == IPSET_INVALID_ID) {
272 pr_warn("Cannot find add_set index %u as target\n",
273 info->add_set.index);
274 return CHECK_FAIL(-ENOENT);
278 if (info->del_set.index != IPSET_INVALID_ID) {
279 index = ip_set_nfnl_get_byindex(XT_PAR_NET(par),
280 info->del_set.index);
281 if (index == IPSET_INVALID_ID) {
282 pr_warn("Cannot find del_set index %u as target\n",
283 info->del_set.index);
284 if (info->add_set.index != IPSET_INVALID_ID)
285 ip_set_nfnl_put(XT_PAR_NET(par),
286 info->add_set.index);
287 return CHECK_FAIL(-ENOENT);
290 if (info->add_set.u.flags[IPSET_DIM_MAX - 1] != 0 ||
291 info->del_set.u.flags[IPSET_DIM_MAX - 1] != 0) {
292 pr_warn("Protocol error: SET target dimension is over the limit!\n");
293 if (info->add_set.index != IPSET_INVALID_ID)
294 ip_set_nfnl_put(XT_PAR_NET(par), info->add_set.index);
295 if (info->del_set.index != IPSET_INVALID_ID)
296 ip_set_nfnl_put(XT_PAR_NET(par), info->del_set.index);
297 return CHECK_FAIL(-ERANGE);
300 /* Fill out compatibility data */
301 compat_flags(&info->add_set);
302 compat_flags(&info->del_set);
308 set_target_v0_destroy(const struct xt_tgdtor_param *par)
310 const struct xt_set_info_target_v0 *info = par->targinfo;
312 if (info->add_set.index != IPSET_INVALID_ID)
313 ip_set_nfnl_put(XT_PAR_NET(par), info->add_set.index);
314 if (info->del_set.index != IPSET_INVALID_ID)
315 ip_set_nfnl_put(XT_PAR_NET(par), info->del_set.index);
318 /* Revision 1 target */
321 set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
323 const struct xt_set_info_target_v1 *info = par->targinfo;
325 ADT_OPT(add_opt, XT_FAMILY(par), info->add_set.dim,
326 info->add_set.flags, 0, UINT_MAX,
328 ADT_OPT(del_opt, XT_FAMILY(par), info->del_set.dim,
329 info->del_set.flags, 0, UINT_MAX,
332 if (info->add_set.index != IPSET_INVALID_ID)
333 ip_set_add(info->add_set.index, skb, CAST_TO_MATCH par,
335 if (info->del_set.index != IPSET_INVALID_ID)
336 ip_set_del(info->del_set.index, skb, CAST_TO_MATCH par,
343 set_target_v1_checkentry(const struct xt_tgchk_param *par)
345 const struct xt_set_info_target_v1 *info = par->targinfo;
348 if (info->add_set.index != IPSET_INVALID_ID) {
349 index = ip_set_nfnl_get_byindex(XT_PAR_NET(par),
350 info->add_set.index);
351 if (index == IPSET_INVALID_ID) {
352 pr_warn("Cannot find add_set index %u as target\n",
353 info->add_set.index);
354 return CHECK_FAIL(-ENOENT);
358 if (info->del_set.index != IPSET_INVALID_ID) {
359 index = ip_set_nfnl_get_byindex(XT_PAR_NET(par),
360 info->del_set.index);
361 if (index == IPSET_INVALID_ID) {
362 pr_warn("Cannot find del_set index %u as target\n",
363 info->del_set.index);
364 if (info->add_set.index != IPSET_INVALID_ID)
365 ip_set_nfnl_put(XT_PAR_NET(par),
366 info->add_set.index);
367 return CHECK_FAIL(-ENOENT);
370 if (info->add_set.dim > IPSET_DIM_MAX ||
371 info->del_set.dim > IPSET_DIM_MAX) {
372 pr_warn("Protocol error: SET target dimension is over the limit!\n");
373 if (info->add_set.index != IPSET_INVALID_ID)
374 ip_set_nfnl_put(XT_PAR_NET(par), info->add_set.index);
375 if (info->del_set.index != IPSET_INVALID_ID)
376 ip_set_nfnl_put(XT_PAR_NET(par), info->del_set.index);
377 return CHECK_FAIL(-ERANGE);
384 set_target_v1_destroy(const struct xt_tgdtor_param *par)
386 const struct xt_set_info_target_v1 *info = par->targinfo;
388 if (info->add_set.index != IPSET_INVALID_ID)
389 ip_set_nfnl_put(XT_PAR_NET(par), info->add_set.index);
390 if (info->del_set.index != IPSET_INVALID_ID)
391 ip_set_nfnl_put(XT_PAR_NET(par), info->del_set.index);
394 /* Revision 2 target */
397 set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
399 const struct xt_set_info_target_v2 *info = par->targinfo;
401 ADT_OPT(add_opt, XT_FAMILY(par), info->add_set.dim,
402 info->add_set.flags, info->flags, info->timeout,
404 ADT_OPT(del_opt, XT_FAMILY(par), info->del_set.dim,
405 info->del_set.flags, 0, UINT_MAX,
408 /* Normalize to fit into jiffies */
409 if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
410 add_opt.ext.timeout > UINT_MAX / MSEC_PER_SEC)
411 add_opt.ext.timeout = UINT_MAX / MSEC_PER_SEC;
412 if (info->add_set.index != IPSET_INVALID_ID)
413 ip_set_add(info->add_set.index, skb, CAST_TO_MATCH par,
415 if (info->del_set.index != IPSET_INVALID_ID)
416 ip_set_del(info->del_set.index, skb, CAST_TO_MATCH par,
422 #define set_target_v2_checkentry set_target_v1_checkentry
423 #define set_target_v2_destroy set_target_v1_destroy
425 /* Revision 3 target */
427 #define MOPT(opt, member) ((opt).ext.skbinfo.member)
430 set_target_v3(struct sk_buff *skb, const struct xt_action_param *par)
432 const struct xt_set_info_target_v3 *info = par->targinfo;
435 ADT_OPT(add_opt, XT_FAMILY(par), info->add_set.dim,
436 info->add_set.flags, info->flags, info->timeout,
438 ADT_OPT(del_opt, XT_FAMILY(par), info->del_set.dim,
439 info->del_set.flags, 0, UINT_MAX,
441 ADT_OPT(map_opt, XT_FAMILY(par), info->map_set.dim,
442 info->map_set.flags, 0, UINT_MAX,
445 /* Normalize to fit into jiffies */
446 if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
447 add_opt.ext.timeout > UINT_MAX / MSEC_PER_SEC)
448 add_opt.ext.timeout = UINT_MAX / MSEC_PER_SEC;
449 if (info->add_set.index != IPSET_INVALID_ID)
450 ip_set_add(info->add_set.index, skb, CAST_TO_MATCH par,
452 if (info->del_set.index != IPSET_INVALID_ID)
453 ip_set_del(info->del_set.index, skb, CAST_TO_MATCH par,
455 if (info->map_set.index != IPSET_INVALID_ID) {
456 map_opt.cmdflags |= info->flags & (IPSET_FLAG_MAP_SKBMARK |
457 IPSET_FLAG_MAP_SKBPRIO |
458 IPSET_FLAG_MAP_SKBQUEUE);
459 ret = match_set(info->map_set.index, skb, CAST_TO_MATCH par,
461 info->map_set.flags & IPSET_INV_MATCH);
464 if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBMARK)
465 skb->mark = (skb->mark & ~MOPT(map_opt,skbmarkmask))
466 ^ MOPT(map_opt, skbmark);
467 if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBPRIO)
468 skb->priority = MOPT(map_opt, skbprio);
469 if ((map_opt.cmdflags & IPSET_FLAG_MAP_SKBQUEUE) &&
471 skb->dev->real_num_tx_queues > MOPT(map_opt, skbqueue))
472 skb_set_queue_mapping(skb, MOPT(map_opt, skbqueue));
478 set_target_v3_checkentry(const struct xt_tgchk_param *par)
480 const struct xt_set_info_target_v3 *info = par->targinfo;
483 if (info->add_set.index != IPSET_INVALID_ID) {
484 index = ip_set_nfnl_get_byindex(XT_PAR_NET(par),
485 info->add_set.index);
486 if (index == IPSET_INVALID_ID) {
487 pr_warn("Cannot find add_set index %u as target\n",
488 info->add_set.index);
489 return CHECK_FAIL(-ENOENT);
493 if (info->del_set.index != IPSET_INVALID_ID) {
494 index = ip_set_nfnl_get_byindex(XT_PAR_NET(par),
495 info->del_set.index);
496 if (index == IPSET_INVALID_ID) {
497 pr_warn("Cannot find del_set index %u as target\n",
498 info->del_set.index);
499 if (info->add_set.index != IPSET_INVALID_ID)
500 ip_set_nfnl_put(XT_PAR_NET(par),
501 info->add_set.index);
502 return CHECK_FAIL(-ENOENT);
506 if (info->map_set.index != IPSET_INVALID_ID) {
507 if (strncmp(par->table, "mangle", 7)) {
508 pr_warn("--map-set only usable from mangle table\n");
509 return CHECK_FAIL(-EINVAL);
511 if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) |
512 (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) &&
513 (par->hook_mask & ~(1 << NF_INET_FORWARD |
514 1 << NF_INET_LOCAL_OUT |
515 1 << NF_INET_POST_ROUTING))) {
516 pr_warn("mapping of prio or/and queue is allowed only from OUTPUT/FORWARD/POSTROUTING chains\n");
517 return CHECK_FAIL(-EINVAL);
519 index = ip_set_nfnl_get_byindex(XT_PAR_NET(par),
520 info->map_set.index);
521 if (index == IPSET_INVALID_ID) {
522 pr_warn("Cannot find map_set index %u as target\n",
523 info->map_set.index);
524 if (info->add_set.index != IPSET_INVALID_ID)
525 ip_set_nfnl_put(XT_PAR_NET(par),
526 info->add_set.index);
527 if (info->del_set.index != IPSET_INVALID_ID)
528 ip_set_nfnl_put(XT_PAR_NET(par),
529 info->del_set.index);
530 return CHECK_FAIL(-ENOENT);
534 if (info->add_set.dim > IPSET_DIM_MAX ||
535 info->del_set.dim > IPSET_DIM_MAX ||
536 info->map_set.dim > IPSET_DIM_MAX) {
537 pr_warn("Protocol error: SET target dimension is over the limit!\n");
538 if (info->add_set.index != IPSET_INVALID_ID)
539 ip_set_nfnl_put(XT_PAR_NET(par), info->add_set.index);
540 if (info->del_set.index != IPSET_INVALID_ID)
541 ip_set_nfnl_put(XT_PAR_NET(par), info->del_set.index);
542 if (info->map_set.index != IPSET_INVALID_ID)
543 ip_set_nfnl_put(XT_PAR_NET(par), info->map_set.index);
544 return CHECK_FAIL(-ERANGE);
551 set_target_v3_destroy(const struct xt_tgdtor_param *par)
553 const struct xt_set_info_target_v3 *info = par->targinfo;
555 if (info->add_set.index != IPSET_INVALID_ID)
556 ip_set_nfnl_put(XT_PAR_NET(par), info->add_set.index);
557 if (info->del_set.index != IPSET_INVALID_ID)
558 ip_set_nfnl_put(XT_PAR_NET(par), info->del_set.index);
559 if (info->map_set.index != IPSET_INVALID_ID)
560 ip_set_nfnl_put(XT_PAR_NET(par), info->map_set.index);
563 static struct xt_match set_matches[] __read_mostly = {
566 .family = NFPROTO_IPV4,
568 .match = set_match_v0,
569 .matchsize = sizeof(struct xt_set_info_match_v0),
570 .checkentry = set_match_v0_checkentry,
571 .destroy = set_match_v0_destroy,
576 .family = NFPROTO_IPV4,
578 .match = set_match_v1,
579 .matchsize = sizeof(struct xt_set_info_match_v1),
580 .checkentry = set_match_v1_checkentry,
581 .destroy = set_match_v1_destroy,
586 .family = NFPROTO_IPV6,
588 .match = set_match_v1,
589 .matchsize = sizeof(struct xt_set_info_match_v1),
590 .checkentry = set_match_v1_checkentry,
591 .destroy = set_match_v1_destroy,
594 /* --return-nomatch flag support */
597 .family = NFPROTO_IPV4,
599 .match = set_match_v1,
600 .matchsize = sizeof(struct xt_set_info_match_v1),
601 .checkentry = set_match_v1_checkentry,
602 .destroy = set_match_v1_destroy,
607 .family = NFPROTO_IPV6,
609 .match = set_match_v1,
610 .matchsize = sizeof(struct xt_set_info_match_v1),
611 .checkentry = set_match_v1_checkentry,
612 .destroy = set_match_v1_destroy,
615 /* counters support: update, match */
618 .family = NFPROTO_IPV4,
620 .match = set_match_v3,
621 .matchsize = sizeof(struct xt_set_info_match_v3),
622 .checkentry = set_match_v3_checkentry,
623 .destroy = set_match_v3_destroy,
628 .family = NFPROTO_IPV6,
630 .match = set_match_v3,
631 .matchsize = sizeof(struct xt_set_info_match_v3),
632 .checkentry = set_match_v3_checkentry,
633 .destroy = set_match_v3_destroy,
636 /* new revision for counters support: update, match */
639 .family = NFPROTO_IPV4,
641 .match = set_match_v4,
642 .matchsize = sizeof(struct xt_set_info_match_v4),
643 .checkentry = set_match_v4_checkentry,
644 .destroy = set_match_v4_destroy,
649 .family = NFPROTO_IPV6,
651 .match = set_match_v4,
652 .matchsize = sizeof(struct xt_set_info_match_v4),
653 .checkentry = set_match_v4_checkentry,
654 .destroy = set_match_v4_destroy,
659 static struct xt_target set_targets[] __read_mostly = {
663 .family = NFPROTO_IPV4,
664 .target = set_target_v0,
665 .targetsize = sizeof(struct xt_set_info_target_v0),
666 .checkentry = set_target_v0_checkentry,
667 .destroy = set_target_v0_destroy,
673 .family = NFPROTO_IPV4,
674 .target = set_target_v1,
675 .targetsize = sizeof(struct xt_set_info_target_v1),
676 .checkentry = set_target_v1_checkentry,
677 .destroy = set_target_v1_destroy,
683 .family = NFPROTO_IPV6,
684 .target = set_target_v1,
685 .targetsize = sizeof(struct xt_set_info_target_v1),
686 .checkentry = set_target_v1_checkentry,
687 .destroy = set_target_v1_destroy,
690 /* --timeout and --exist flags support */
694 .family = NFPROTO_IPV4,
695 .target = set_target_v2,
696 .targetsize = sizeof(struct xt_set_info_target_v2),
697 .checkentry = set_target_v2_checkentry,
698 .destroy = set_target_v2_destroy,
704 .family = NFPROTO_IPV6,
705 .target = set_target_v2,
706 .targetsize = sizeof(struct xt_set_info_target_v2),
707 .checkentry = set_target_v2_checkentry,
708 .destroy = set_target_v2_destroy,
711 /* --map-set support */
715 .family = NFPROTO_IPV4,
716 .target = set_target_v3,
717 .targetsize = sizeof(struct xt_set_info_target_v3),
718 .checkentry = set_target_v3_checkentry,
719 .destroy = set_target_v3_destroy,
725 .family = NFPROTO_IPV6,
726 .target = set_target_v3,
727 .targetsize = sizeof(struct xt_set_info_target_v3),
728 .checkentry = set_target_v3_checkentry,
729 .destroy = set_target_v3_destroy,
734 static int __init xt_set_init(void)
736 int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
739 ret = xt_register_targets(set_targets,
740 ARRAY_SIZE(set_targets));
742 xt_unregister_matches(set_matches,
743 ARRAY_SIZE(set_matches));
748 static void __exit xt_set_fini(void)
750 xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
751 xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
754 module_init(xt_set_init);
755 module_exit(xt_set_fini);