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