at v3.18-rc4 674 lines 19 kB view raw
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 21MODULE_LICENSE("GPL"); 22MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); 23MODULE_DESCRIPTION("Xtables: IP set match and target module"); 24MODULE_ALIAS("xt_SET"); 25MODULE_ALIAS("ipt_set"); 26MODULE_ALIAS("ip6t_set"); 27MODULE_ALIAS("ipt_SET"); 28MODULE_ALIAS("ip6t_SET"); 29 30static inline int 31match_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) \ 41struct 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 51static bool 52set_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 62static void 63compat_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 78static int 79set_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 103static void 104set_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 113static bool 114set_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 127static int 128set_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 149static void 150set_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 159static bool 160match_counter(u64 counter, const struct ip_set_counter_match *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 177static bool 178set_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_counter(opt.ext.packets, &info->packets)) 196 return 0; 197 return match_counter(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 0 interface: backward compatible with netfilter/iptables */ 204 205static unsigned int 206set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) 207{ 208 const struct xt_set_info_target_v0 *info = par->targinfo; 209 ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim, 210 info->add_set.u.compat.flags, 0, UINT_MAX); 211 ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim, 212 info->del_set.u.compat.flags, 0, UINT_MAX); 213 214 if (info->add_set.index != IPSET_INVALID_ID) 215 ip_set_add(info->add_set.index, skb, par, &add_opt); 216 if (info->del_set.index != IPSET_INVALID_ID) 217 ip_set_del(info->del_set.index, skb, par, &del_opt); 218 219 return XT_CONTINUE; 220} 221 222static int 223set_target_v0_checkentry(const struct xt_tgchk_param *par) 224{ 225 struct xt_set_info_target_v0 *info = par->targinfo; 226 ip_set_id_t index; 227 228 if (info->add_set.index != IPSET_INVALID_ID) { 229 index = ip_set_nfnl_get_byindex(par->net, info->add_set.index); 230 if (index == IPSET_INVALID_ID) { 231 pr_warn("Cannot find add_set index %u as target\n", 232 info->add_set.index); 233 return -ENOENT; 234 } 235 } 236 237 if (info->del_set.index != IPSET_INVALID_ID) { 238 index = ip_set_nfnl_get_byindex(par->net, info->del_set.index); 239 if (index == IPSET_INVALID_ID) { 240 pr_warn("Cannot find del_set index %u as target\n", 241 info->del_set.index); 242 if (info->add_set.index != IPSET_INVALID_ID) 243 ip_set_nfnl_put(par->net, info->add_set.index); 244 return -ENOENT; 245 } 246 } 247 if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 || 248 info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) { 249 pr_warn("Protocol error: SET target dimension is over the limit!\n"); 250 if (info->add_set.index != IPSET_INVALID_ID) 251 ip_set_nfnl_put(par->net, info->add_set.index); 252 if (info->del_set.index != IPSET_INVALID_ID) 253 ip_set_nfnl_put(par->net, info->del_set.index); 254 return -ERANGE; 255 } 256 257 /* Fill out compatibility data */ 258 compat_flags(&info->add_set); 259 compat_flags(&info->del_set); 260 261 return 0; 262} 263 264static void 265set_target_v0_destroy(const struct xt_tgdtor_param *par) 266{ 267 const struct xt_set_info_target_v0 *info = par->targinfo; 268 269 if (info->add_set.index != IPSET_INVALID_ID) 270 ip_set_nfnl_put(par->net, info->add_set.index); 271 if (info->del_set.index != IPSET_INVALID_ID) 272 ip_set_nfnl_put(par->net, info->del_set.index); 273} 274 275/* Revision 1 target */ 276 277static unsigned int 278set_target_v1(struct sk_buff *skb, const struct xt_action_param *par) 279{ 280 const struct xt_set_info_target_v1 *info = par->targinfo; 281 ADT_OPT(add_opt, par->family, info->add_set.dim, 282 info->add_set.flags, 0, UINT_MAX); 283 ADT_OPT(del_opt, par->family, info->del_set.dim, 284 info->del_set.flags, 0, UINT_MAX); 285 286 if (info->add_set.index != IPSET_INVALID_ID) 287 ip_set_add(info->add_set.index, skb, par, &add_opt); 288 if (info->del_set.index != IPSET_INVALID_ID) 289 ip_set_del(info->del_set.index, skb, par, &del_opt); 290 291 return XT_CONTINUE; 292} 293 294static int 295set_target_v1_checkentry(const struct xt_tgchk_param *par) 296{ 297 const struct xt_set_info_target_v1 *info = par->targinfo; 298 ip_set_id_t index; 299 300 if (info->add_set.index != IPSET_INVALID_ID) { 301 index = ip_set_nfnl_get_byindex(par->net, info->add_set.index); 302 if (index == IPSET_INVALID_ID) { 303 pr_warn("Cannot find add_set index %u as target\n", 304 info->add_set.index); 305 return -ENOENT; 306 } 307 } 308 309 if (info->del_set.index != IPSET_INVALID_ID) { 310 index = ip_set_nfnl_get_byindex(par->net, info->del_set.index); 311 if (index == IPSET_INVALID_ID) { 312 pr_warn("Cannot find del_set index %u as target\n", 313 info->del_set.index); 314 if (info->add_set.index != IPSET_INVALID_ID) 315 ip_set_nfnl_put(par->net, info->add_set.index); 316 return -ENOENT; 317 } 318 } 319 if (info->add_set.dim > IPSET_DIM_MAX || 320 info->del_set.dim > IPSET_DIM_MAX) { 321 pr_warn("Protocol error: SET target dimension is over the limit!\n"); 322 if (info->add_set.index != IPSET_INVALID_ID) 323 ip_set_nfnl_put(par->net, info->add_set.index); 324 if (info->del_set.index != IPSET_INVALID_ID) 325 ip_set_nfnl_put(par->net, info->del_set.index); 326 return -ERANGE; 327 } 328 329 return 0; 330} 331 332static void 333set_target_v1_destroy(const struct xt_tgdtor_param *par) 334{ 335 const struct xt_set_info_target_v1 *info = par->targinfo; 336 337 if (info->add_set.index != IPSET_INVALID_ID) 338 ip_set_nfnl_put(par->net, info->add_set.index); 339 if (info->del_set.index != IPSET_INVALID_ID) 340 ip_set_nfnl_put(par->net, info->del_set.index); 341} 342 343/* Revision 2 target */ 344 345static unsigned int 346set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) 347{ 348 const struct xt_set_info_target_v2 *info = par->targinfo; 349 ADT_OPT(add_opt, par->family, info->add_set.dim, 350 info->add_set.flags, info->flags, info->timeout); 351 ADT_OPT(del_opt, par->family, info->del_set.dim, 352 info->del_set.flags, 0, UINT_MAX); 353 354 /* Normalize to fit into jiffies */ 355 if (add_opt.ext.timeout != IPSET_NO_TIMEOUT && 356 add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC) 357 add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC; 358 if (info->add_set.index != IPSET_INVALID_ID) 359 ip_set_add(info->add_set.index, skb, par, &add_opt); 360 if (info->del_set.index != IPSET_INVALID_ID) 361 ip_set_del(info->del_set.index, skb, par, &del_opt); 362 363 return XT_CONTINUE; 364} 365 366#define set_target_v2_checkentry set_target_v1_checkentry 367#define set_target_v2_destroy set_target_v1_destroy 368 369/* Revision 3 target */ 370 371static unsigned int 372set_target_v3(struct sk_buff *skb, const struct xt_action_param *par) 373{ 374 const struct xt_set_info_target_v3 *info = par->targinfo; 375 ADT_OPT(add_opt, par->family, info->add_set.dim, 376 info->add_set.flags, info->flags, info->timeout); 377 ADT_OPT(del_opt, par->family, info->del_set.dim, 378 info->del_set.flags, 0, UINT_MAX); 379 ADT_OPT(map_opt, par->family, info->map_set.dim, 380 info->map_set.flags, 0, UINT_MAX); 381 382 int ret; 383 384 /* Normalize to fit into jiffies */ 385 if (add_opt.ext.timeout != IPSET_NO_TIMEOUT && 386 add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC) 387 add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC; 388 if (info->add_set.index != IPSET_INVALID_ID) 389 ip_set_add(info->add_set.index, skb, par, &add_opt); 390 if (info->del_set.index != IPSET_INVALID_ID) 391 ip_set_del(info->del_set.index, skb, par, &del_opt); 392 if (info->map_set.index != IPSET_INVALID_ID) { 393 map_opt.cmdflags |= info->flags & (IPSET_FLAG_MAP_SKBMARK | 394 IPSET_FLAG_MAP_SKBPRIO | 395 IPSET_FLAG_MAP_SKBQUEUE); 396 ret = match_set(info->map_set.index, skb, par, &map_opt, 397 info->map_set.flags & IPSET_INV_MATCH); 398 if (!ret) 399 return XT_CONTINUE; 400 if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBMARK) 401 skb->mark = (skb->mark & ~(map_opt.ext.skbmarkmask)) 402 ^ (map_opt.ext.skbmark); 403 if (map_opt.cmdflags & IPSET_FLAG_MAP_SKBPRIO) 404 skb->priority = map_opt.ext.skbprio; 405 if ((map_opt.cmdflags & IPSET_FLAG_MAP_SKBQUEUE) && 406 skb->dev && 407 skb->dev->real_num_tx_queues > map_opt.ext.skbqueue) 408 skb_set_queue_mapping(skb, map_opt.ext.skbqueue); 409 } 410 return XT_CONTINUE; 411} 412 413 414static int 415set_target_v3_checkentry(const struct xt_tgchk_param *par) 416{ 417 const struct xt_set_info_target_v3 *info = par->targinfo; 418 ip_set_id_t index; 419 420 if (info->add_set.index != IPSET_INVALID_ID) { 421 index = ip_set_nfnl_get_byindex(par->net, 422 info->add_set.index); 423 if (index == IPSET_INVALID_ID) { 424 pr_warn("Cannot find add_set index %u as target\n", 425 info->add_set.index); 426 return -ENOENT; 427 } 428 } 429 430 if (info->del_set.index != IPSET_INVALID_ID) { 431 index = ip_set_nfnl_get_byindex(par->net, 432 info->del_set.index); 433 if (index == IPSET_INVALID_ID) { 434 pr_warn("Cannot find del_set index %u as target\n", 435 info->del_set.index); 436 if (info->add_set.index != IPSET_INVALID_ID) 437 ip_set_nfnl_put(par->net, 438 info->add_set.index); 439 return -ENOENT; 440 } 441 } 442 443 if (info->map_set.index != IPSET_INVALID_ID) { 444 if (strncmp(par->table, "mangle", 7)) { 445 pr_warn("--map-set only usable from mangle table\n"); 446 return -EINVAL; 447 } 448 if (((info->flags & IPSET_FLAG_MAP_SKBPRIO) | 449 (info->flags & IPSET_FLAG_MAP_SKBQUEUE)) && 450 !(par->hook_mask & (1 << NF_INET_FORWARD | 451 1 << NF_INET_LOCAL_OUT | 452 1 << NF_INET_POST_ROUTING))) { 453 pr_warn("mapping of prio or/and queue is allowed only" 454 "from OUTPUT/FORWARD/POSTROUTING chains\n"); 455 return -EINVAL; 456 } 457 index = ip_set_nfnl_get_byindex(par->net, 458 info->map_set.index); 459 if (index == IPSET_INVALID_ID) { 460 pr_warn("Cannot find map_set index %u as target\n", 461 info->map_set.index); 462 if (info->add_set.index != IPSET_INVALID_ID) 463 ip_set_nfnl_put(par->net, 464 info->add_set.index); 465 if (info->del_set.index != IPSET_INVALID_ID) 466 ip_set_nfnl_put(par->net, 467 info->del_set.index); 468 return -ENOENT; 469 } 470 } 471 472 if (info->add_set.dim > IPSET_DIM_MAX || 473 info->del_set.dim > IPSET_DIM_MAX || 474 info->map_set.dim > IPSET_DIM_MAX) { 475 pr_warn("Protocol error: SET target dimension " 476 "is over the limit!\n"); 477 if (info->add_set.index != IPSET_INVALID_ID) 478 ip_set_nfnl_put(par->net, info->add_set.index); 479 if (info->del_set.index != IPSET_INVALID_ID) 480 ip_set_nfnl_put(par->net, info->del_set.index); 481 if (info->map_set.index != IPSET_INVALID_ID) 482 ip_set_nfnl_put(par->net, info->map_set.index); 483 return -ERANGE; 484 } 485 486 return 0; 487} 488 489static void 490set_target_v3_destroy(const struct xt_tgdtor_param *par) 491{ 492 const struct xt_set_info_target_v3 *info = par->targinfo; 493 494 if (info->add_set.index != IPSET_INVALID_ID) 495 ip_set_nfnl_put(par->net, info->add_set.index); 496 if (info->del_set.index != IPSET_INVALID_ID) 497 ip_set_nfnl_put(par->net, info->del_set.index); 498 if (info->map_set.index != IPSET_INVALID_ID) 499 ip_set_nfnl_put(par->net, info->map_set.index); 500} 501 502 503static struct xt_match set_matches[] __read_mostly = { 504 { 505 .name = "set", 506 .family = NFPROTO_IPV4, 507 .revision = 0, 508 .match = set_match_v0, 509 .matchsize = sizeof(struct xt_set_info_match_v0), 510 .checkentry = set_match_v0_checkentry, 511 .destroy = set_match_v0_destroy, 512 .me = THIS_MODULE 513 }, 514 { 515 .name = "set", 516 .family = NFPROTO_IPV4, 517 .revision = 1, 518 .match = set_match_v1, 519 .matchsize = sizeof(struct xt_set_info_match_v1), 520 .checkentry = set_match_v1_checkentry, 521 .destroy = set_match_v1_destroy, 522 .me = THIS_MODULE 523 }, 524 { 525 .name = "set", 526 .family = NFPROTO_IPV6, 527 .revision = 1, 528 .match = set_match_v1, 529 .matchsize = sizeof(struct xt_set_info_match_v1), 530 .checkentry = set_match_v1_checkentry, 531 .destroy = set_match_v1_destroy, 532 .me = THIS_MODULE 533 }, 534 /* --return-nomatch flag support */ 535 { 536 .name = "set", 537 .family = NFPROTO_IPV4, 538 .revision = 2, 539 .match = set_match_v1, 540 .matchsize = sizeof(struct xt_set_info_match_v1), 541 .checkentry = set_match_v1_checkentry, 542 .destroy = set_match_v1_destroy, 543 .me = THIS_MODULE 544 }, 545 { 546 .name = "set", 547 .family = NFPROTO_IPV6, 548 .revision = 2, 549 .match = set_match_v1, 550 .matchsize = sizeof(struct xt_set_info_match_v1), 551 .checkentry = set_match_v1_checkentry, 552 .destroy = set_match_v1_destroy, 553 .me = THIS_MODULE 554 }, 555 /* counters support: update, match */ 556 { 557 .name = "set", 558 .family = NFPROTO_IPV4, 559 .revision = 3, 560 .match = set_match_v3, 561 .matchsize = sizeof(struct xt_set_info_match_v3), 562 .checkentry = set_match_v3_checkentry, 563 .destroy = set_match_v3_destroy, 564 .me = THIS_MODULE 565 }, 566 { 567 .name = "set", 568 .family = NFPROTO_IPV6, 569 .revision = 3, 570 .match = set_match_v3, 571 .matchsize = sizeof(struct xt_set_info_match_v3), 572 .checkentry = set_match_v3_checkentry, 573 .destroy = set_match_v3_destroy, 574 .me = THIS_MODULE 575 }, 576}; 577 578static struct xt_target set_targets[] __read_mostly = { 579 { 580 .name = "SET", 581 .revision = 0, 582 .family = NFPROTO_IPV4, 583 .target = set_target_v0, 584 .targetsize = sizeof(struct xt_set_info_target_v0), 585 .checkentry = set_target_v0_checkentry, 586 .destroy = set_target_v0_destroy, 587 .me = THIS_MODULE 588 }, 589 { 590 .name = "SET", 591 .revision = 1, 592 .family = NFPROTO_IPV4, 593 .target = set_target_v1, 594 .targetsize = sizeof(struct xt_set_info_target_v1), 595 .checkentry = set_target_v1_checkentry, 596 .destroy = set_target_v1_destroy, 597 .me = THIS_MODULE 598 }, 599 { 600 .name = "SET", 601 .revision = 1, 602 .family = NFPROTO_IPV6, 603 .target = set_target_v1, 604 .targetsize = sizeof(struct xt_set_info_target_v1), 605 .checkentry = set_target_v1_checkentry, 606 .destroy = set_target_v1_destroy, 607 .me = THIS_MODULE 608 }, 609 /* --timeout and --exist flags support */ 610 { 611 .name = "SET", 612 .revision = 2, 613 .family = NFPROTO_IPV4, 614 .target = set_target_v2, 615 .targetsize = sizeof(struct xt_set_info_target_v2), 616 .checkentry = set_target_v2_checkentry, 617 .destroy = set_target_v2_destroy, 618 .me = THIS_MODULE 619 }, 620 { 621 .name = "SET", 622 .revision = 2, 623 .family = NFPROTO_IPV6, 624 .target = set_target_v2, 625 .targetsize = sizeof(struct xt_set_info_target_v2), 626 .checkentry = set_target_v2_checkentry, 627 .destroy = set_target_v2_destroy, 628 .me = THIS_MODULE 629 }, 630 /* --map-set support */ 631 { 632 .name = "SET", 633 .revision = 3, 634 .family = NFPROTO_IPV4, 635 .target = set_target_v3, 636 .targetsize = sizeof(struct xt_set_info_target_v3), 637 .checkentry = set_target_v3_checkentry, 638 .destroy = set_target_v3_destroy, 639 .me = THIS_MODULE 640 }, 641 { 642 .name = "SET", 643 .revision = 3, 644 .family = NFPROTO_IPV6, 645 .target = set_target_v3, 646 .targetsize = sizeof(struct xt_set_info_target_v3), 647 .checkentry = set_target_v3_checkentry, 648 .destroy = set_target_v3_destroy, 649 .me = THIS_MODULE 650 }, 651}; 652 653static int __init xt_set_init(void) 654{ 655 int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches)); 656 657 if (!ret) { 658 ret = xt_register_targets(set_targets, 659 ARRAY_SIZE(set_targets)); 660 if (ret) 661 xt_unregister_matches(set_matches, 662 ARRAY_SIZE(set_matches)); 663 } 664 return ret; 665} 666 667static void __exit xt_set_fini(void) 668{ 669 xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches)); 670 xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets)); 671} 672 673module_init(xt_set_init); 674module_exit(xt_set_fini);