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