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