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(info->match_set.index);
  85
  86        if (index == IPSET_INVALID_ID) {
  87                pr_warning("Cannot find set indentified 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_warning("Protocol error: set match dimension "
  93                           "is over the limit!\n");
  94                ip_set_nfnl_put(info->match_set.index);
  95                return -ERANGE;
  96        }
  97
  98        /* Fill out compatibility data */
  99        compat_flags(&info->match_set);
 100
 101        return 0;
 102}
 103
 104static void
 105set_match_v0_destroy(const struct xt_mtdtor_param *par)
 106{
 107        struct xt_set_info_match_v0 *info = par->matchinfo;
 108
 109        ip_set_nfnl_put(info->match_set.index);
 110}
 111
 112static unsigned int
 113set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
 114{
 115        const struct xt_set_info_target_v0 *info = par->targinfo;
 116        ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
 117                info->add_set.u.compat.flags, 0, UINT_MAX);
 118        ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
 119                info->del_set.u.compat.flags, 0, UINT_MAX);
 120
 121        if (info->add_set.index != IPSET_INVALID_ID)
 122                ip_set_add(info->add_set.index, skb, par, &add_opt);
 123        if (info->del_set.index != IPSET_INVALID_ID)
 124                ip_set_del(info->del_set.index, skb, par, &del_opt);
 125
 126        return XT_CONTINUE;
 127}
 128
 129static int
 130set_target_v0_checkentry(const struct xt_tgchk_param *par)
 131{
 132        struct xt_set_info_target_v0 *info = par->targinfo;
 133        ip_set_id_t index;
 134
 135        if (info->add_set.index != IPSET_INVALID_ID) {
 136                index = ip_set_nfnl_get_byindex(info->add_set.index);
 137                if (index == IPSET_INVALID_ID) {
 138                        pr_warning("Cannot find add_set index %u as target\n",
 139                                   info->add_set.index);
 140                        return -ENOENT;
 141                }
 142        }
 143
 144        if (info->del_set.index != IPSET_INVALID_ID) {
 145                index = ip_set_nfnl_get_byindex(info->del_set.index);
 146                if (index == IPSET_INVALID_ID) {
 147                        pr_warning("Cannot find del_set index %u as target\n",
 148                                   info->del_set.index);
 149                        if (info->add_set.index != IPSET_INVALID_ID)
 150                                ip_set_nfnl_put(info->add_set.index);
 151                        return -ENOENT;
 152                }
 153        }
 154        if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
 155            info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
 156                pr_warning("Protocol error: SET target dimension "
 157                           "is over the limit!\n");
 158                if (info->add_set.index != IPSET_INVALID_ID)
 159                        ip_set_nfnl_put(info->add_set.index);
 160                if (info->del_set.index != IPSET_INVALID_ID)
 161                        ip_set_nfnl_put(info->del_set.index);
 162                return -ERANGE;
 163        }
 164
 165        /* Fill out compatibility data */
 166        compat_flags(&info->add_set);
 167        compat_flags(&info->del_set);
 168
 169        return 0;
 170}
 171
 172static void
 173set_target_v0_destroy(const struct xt_tgdtor_param *par)
 174{
 175        const struct xt_set_info_target_v0 *info = par->targinfo;
 176
 177        if (info->add_set.index != IPSET_INVALID_ID)
 178                ip_set_nfnl_put(info->add_set.index);
 179        if (info->del_set.index != IPSET_INVALID_ID)
 180                ip_set_nfnl_put(info->del_set.index);
 181}
 182
 183/* Revision 1 match and target */
 184
 185static bool
 186set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
 187{
 188        const struct xt_set_info_match_v1 *info = par->matchinfo;
 189        ADT_OPT(opt, par->family, info->match_set.dim,
 190                info->match_set.flags, 0, UINT_MAX);
 191
 192        if (opt.flags & IPSET_RETURN_NOMATCH)
 193                opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH;
 194
 195        return match_set(info->match_set.index, skb, par, &opt,
 196                         info->match_set.flags & IPSET_INV_MATCH);
 197}
 198
 199static int
 200set_match_v1_checkentry(const struct xt_mtchk_param *par)
 201{
 202        struct xt_set_info_match_v1 *info = par->matchinfo;
 203        ip_set_id_t index;
 204
 205        index = ip_set_nfnl_get_byindex(info->match_set.index);
 206
 207        if (index == IPSET_INVALID_ID) {
 208                pr_warning("Cannot find set indentified by id %u to match\n",
 209                           info->match_set.index);
 210                return -ENOENT;
 211        }
 212        if (info->match_set.dim > IPSET_DIM_MAX) {
 213                pr_warning("Protocol error: set match dimension "
 214                           "is over the limit!\n");
 215                ip_set_nfnl_put(info->match_set.index);
 216                return -ERANGE;
 217        }
 218
 219        return 0;
 220}
 221
 222static void
 223set_match_v1_destroy(const struct xt_mtdtor_param *par)
 224{
 225        struct xt_set_info_match_v1 *info = par->matchinfo;
 226
 227        ip_set_nfnl_put(info->match_set.index);
 228}
 229
 230static unsigned int
 231set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
 232{
 233        const struct xt_set_info_target_v1 *info = par->targinfo;
 234        ADT_OPT(add_opt, par->family, info->add_set.dim,
 235                info->add_set.flags, 0, UINT_MAX);
 236        ADT_OPT(del_opt, par->family, info->del_set.dim,
 237                info->del_set.flags, 0, UINT_MAX);
 238
 239        if (info->add_set.index != IPSET_INVALID_ID)
 240                ip_set_add(info->add_set.index, skb, par, &add_opt);
 241        if (info->del_set.index != IPSET_INVALID_ID)
 242                ip_set_del(info->del_set.index, skb, par, &del_opt);
 243
 244        return XT_CONTINUE;
 245}
 246
 247static int
 248set_target_v1_checkentry(const struct xt_tgchk_param *par)
 249{
 250        const struct xt_set_info_target_v1 *info = par->targinfo;
 251        ip_set_id_t index;
 252
 253        if (info->add_set.index != IPSET_INVALID_ID) {
 254                index = ip_set_nfnl_get_byindex(info->add_set.index);
 255                if (index == IPSET_INVALID_ID) {
 256                        pr_warning("Cannot find add_set index %u as target\n",
 257                                   info->add_set.index);
 258                        return -ENOENT;
 259                }
 260        }
 261
 262        if (info->del_set.index != IPSET_INVALID_ID) {
 263                index = ip_set_nfnl_get_byindex(info->del_set.index);
 264                if (index == IPSET_INVALID_ID) {
 265                        pr_warning("Cannot find del_set index %u as target\n",
 266                                   info->del_set.index);
 267                        if (info->add_set.index != IPSET_INVALID_ID)
 268                                ip_set_nfnl_put(info->add_set.index);
 269                        return -ENOENT;
 270                }
 271        }
 272        if (info->add_set.dim > IPSET_DIM_MAX ||
 273            info->del_set.dim > IPSET_DIM_MAX) {
 274                pr_warning("Protocol error: SET target dimension "
 275                           "is over the limit!\n");
 276                if (info->add_set.index != IPSET_INVALID_ID)
 277                        ip_set_nfnl_put(info->add_set.index);
 278                if (info->del_set.index != IPSET_INVALID_ID)
 279                        ip_set_nfnl_put(info->del_set.index);
 280                return -ERANGE;
 281        }
 282
 283        return 0;
 284}
 285
 286static void
 287set_target_v1_destroy(const struct xt_tgdtor_param *par)
 288{
 289        const struct xt_set_info_target_v1 *info = par->targinfo;
 290
 291        if (info->add_set.index != IPSET_INVALID_ID)
 292                ip_set_nfnl_put(info->add_set.index);
 293        if (info->del_set.index != IPSET_INVALID_ID)
 294                ip_set_nfnl_put(info->del_set.index);
 295}
 296
 297/* Revision 2 target */
 298
 299static unsigned int
 300set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
 301{
 302        const struct xt_set_info_target_v2 *info = par->targinfo;
 303        ADT_OPT(add_opt, par->family, info->add_set.dim,
 304                info->add_set.flags, info->flags, info->timeout);
 305        ADT_OPT(del_opt, par->family, info->del_set.dim,
 306                info->del_set.flags, 0, UINT_MAX);
 307
 308        /* Normalize to fit into jiffies */
 309        if (add_opt.ext.timeout != IPSET_NO_TIMEOUT &&
 310            add_opt.ext.timeout > UINT_MAX/MSEC_PER_SEC)
 311                add_opt.ext.timeout = UINT_MAX/MSEC_PER_SEC;
 312        if (info->add_set.index != IPSET_INVALID_ID)
 313                ip_set_add(info->add_set.index, skb, par, &add_opt);
 314        if (info->del_set.index != IPSET_INVALID_ID)
 315                ip_set_del(info->del_set.index, skb, par, &del_opt);
 316
 317        return XT_CONTINUE;
 318}
 319
 320#define set_target_v2_checkentry        set_target_v1_checkentry
 321#define set_target_v2_destroy           set_target_v1_destroy
 322
 323/* Revision 3 match */
 324
 325static bool
 326match_counter(u64 counter, const struct ip_set_counter_match *info)
 327{
 328        switch (info->op) {
 329        case IPSET_COUNTER_NONE:
 330                return true;
 331        case IPSET_COUNTER_EQ:
 332                return counter == info->value;
 333        case IPSET_COUNTER_NE:
 334                return counter != info->value;
 335        case IPSET_COUNTER_LT:
 336                return counter < info->value;
 337        case IPSET_COUNTER_GT:
 338                return counter > info->value;
 339        }
 340        return false;
 341}
 342
 343static bool
 344set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
 345{
 346        const struct xt_set_info_match_v3 *info = par->matchinfo;
 347        ADT_OPT(opt, par->family, info->match_set.dim,
 348                info->match_set.flags, info->flags, UINT_MAX);
 349        int ret;
 350
 351        if (info->packets.op != IPSET_COUNTER_NONE ||
 352            info->bytes.op != IPSET_COUNTER_NONE)
 353                opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
 354
 355        ret = match_set(info->match_set.index, skb, par, &opt,
 356                        info->match_set.flags & IPSET_INV_MATCH);
 357
 358        if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
 359                return ret;
 360
 361        if (!match_counter(opt.ext.packets, &info->packets))
 362                return 0;
 363        return match_counter(opt.ext.bytes, &info->bytes);
 364}
 365
 366#define set_match_v3_checkentry set_match_v1_checkentry
 367#define set_match_v3_destroy    set_match_v1_destroy
 368
 369static struct xt_match set_matches[] __read_mostly = {
 370        {
 371                .name           = "set",
 372                .family         = NFPROTO_IPV4,
 373                .revision       = 0,
 374                .match          = set_match_v0,
 375                .matchsize      = sizeof(struct xt_set_info_match_v0),
 376                .checkentry     = set_match_v0_checkentry,
 377                .destroy        = set_match_v0_destroy,
 378                .me             = THIS_MODULE
 379        },
 380        {
 381                .name           = "set",
 382                .family         = NFPROTO_IPV4,
 383                .revision       = 1,
 384                .match          = set_match_v1,
 385                .matchsize      = sizeof(struct xt_set_info_match_v1),
 386                .checkentry     = set_match_v1_checkentry,
 387                .destroy        = set_match_v1_destroy,
 388                .me             = THIS_MODULE
 389        },
 390        {
 391                .name           = "set",
 392                .family         = NFPROTO_IPV6,
 393                .revision       = 1,
 394                .match          = set_match_v1,
 395                .matchsize      = sizeof(struct xt_set_info_match_v1),
 396                .checkentry     = set_match_v1_checkentry,
 397                .destroy        = set_match_v1_destroy,
 398                .me             = THIS_MODULE
 399        },
 400        /* --return-nomatch flag support */
 401        {
 402                .name           = "set",
 403                .family         = NFPROTO_IPV4,
 404                .revision       = 2,
 405                .match          = set_match_v1,
 406                .matchsize      = sizeof(struct xt_set_info_match_v1),
 407                .checkentry     = set_match_v1_checkentry,
 408                .destroy        = set_match_v1_destroy,
 409                .me             = THIS_MODULE
 410        },
 411        {
 412                .name           = "set",
 413                .family         = NFPROTO_IPV6,
 414                .revision       = 2,
 415                .match          = set_match_v1,
 416                .matchsize      = sizeof(struct xt_set_info_match_v1),
 417                .checkentry     = set_match_v1_checkentry,
 418                .destroy        = set_match_v1_destroy,
 419                .me             = THIS_MODULE
 420        },
 421        /* counters support: update, match */
 422        {
 423                .name           = "set",
 424                .family         = NFPROTO_IPV4,
 425                .revision       = 3,
 426                .match          = set_match_v3,
 427                .matchsize      = sizeof(struct xt_set_info_match_v3),
 428                .checkentry     = set_match_v3_checkentry,
 429                .destroy        = set_match_v3_destroy,
 430                .me             = THIS_MODULE
 431        },
 432        {
 433                .name           = "set",
 434                .family         = NFPROTO_IPV6,
 435                .revision       = 3,
 436                .match          = set_match_v3,
 437                .matchsize      = sizeof(struct xt_set_info_match_v3),
 438                .checkentry     = set_match_v3_checkentry,
 439                .destroy        = set_match_v3_destroy,
 440                .me             = THIS_MODULE
 441        },
 442};
 443
 444static struct xt_target set_targets[] __read_mostly = {
 445        {
 446                .name           = "SET",
 447                .revision       = 0,
 448                .family         = NFPROTO_IPV4,
 449                .target         = set_target_v0,
 450                .targetsize     = sizeof(struct xt_set_info_target_v0),
 451                .checkentry     = set_target_v0_checkentry,
 452                .destroy        = set_target_v0_destroy,
 453                .me             = THIS_MODULE
 454        },
 455        {
 456                .name           = "SET",
 457                .revision       = 1,
 458                .family         = NFPROTO_IPV4,
 459                .target         = set_target_v1,
 460                .targetsize     = sizeof(struct xt_set_info_target_v1),
 461                .checkentry     = set_target_v1_checkentry,
 462                .destroy        = set_target_v1_destroy,
 463                .me             = THIS_MODULE
 464        },
 465        {
 466                .name           = "SET",
 467                .revision       = 1,
 468                .family         = NFPROTO_IPV6,
 469                .target         = set_target_v1,
 470                .targetsize     = sizeof(struct xt_set_info_target_v1),
 471                .checkentry     = set_target_v1_checkentry,
 472                .destroy        = set_target_v1_destroy,
 473                .me             = THIS_MODULE
 474        },
 475        /* --timeout and --exist flags support */
 476        {
 477                .name           = "SET",
 478                .revision       = 2,
 479                .family         = NFPROTO_IPV4,
 480                .target         = set_target_v2,
 481                .targetsize     = sizeof(struct xt_set_info_target_v2),
 482                .checkentry     = set_target_v2_checkentry,
 483                .destroy        = set_target_v2_destroy,
 484                .me             = THIS_MODULE
 485        },
 486        {
 487                .name           = "SET",
 488                .revision       = 2,
 489                .family         = NFPROTO_IPV6,
 490                .target         = set_target_v2,
 491                .targetsize     = sizeof(struct xt_set_info_target_v2),
 492                .checkentry     = set_target_v2_checkentry,
 493                .destroy        = set_target_v2_destroy,
 494                .me             = THIS_MODULE
 495        },
 496};
 497
 498static int __init xt_set_init(void)
 499{
 500        int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
 501
 502        if (!ret) {
 503                ret = xt_register_targets(set_targets,
 504                                          ARRAY_SIZE(set_targets));
 505                if (ret)
 506                        xt_unregister_matches(set_matches,
 507                                              ARRAY_SIZE(set_matches));
 508        }
 509        return ret;
 510}
 511
 512static void __exit xt_set_fini(void)
 513{
 514        xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
 515        xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
 516}
 517
 518module_init(xt_set_init);
 519module_exit(xt_set_fini);
 520