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