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-2011 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          const 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)    \
  41const struct ip_set_adt_opt n = {       \
  42        .family = f,                    \
  43        .dim = d,                       \
  44        .flags = fs,                    \
  45        .cmdflags = cfs,                \
  46        .timeout = t,                   \
  47}
  48#define ADT_MOPT(n, f, d, fs, cfs, t)   \
  49struct ip_set_adt_opt n = {             \
  50        .family = f,                    \
  51        .dim = d,                       \
  52        .flags = fs,                    \
  53        .cmdflags = cfs,                \
  54        .timeout = t,                   \
  55}
  56
  57/* Revision 0 interface: backward compatible with netfilter/iptables */
  58
  59static bool
  60set_match_v0(const struct sk_buff *skb, struct xt_action_param *par)
  61{
  62        const struct xt_set_info_match_v0 *info = par->matchinfo;
  63        ADT_OPT(opt, par->family, info->match_set.u.compat.dim,
  64                info->match_set.u.compat.flags, 0, UINT_MAX);
  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(info->match_set.index);
  93
  94        if (index == IPSET_INVALID_ID) {
  95                pr_warning("Cannot find set indentified 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_warning("Protocol error: set match dimension "
 101                           "is over the limit!\n");
 102                ip_set_nfnl_put(info->match_set.index);
 103                return -ERANGE;
 104        }
 105
 106        /* Fill out compatibility data */
 107        compat_flags(&info->match_set);
 108
 109        return 0;
 110}
 111
 112static void
 113set_match_v0_destroy(const struct xt_mtdtor_param *par)
 114{
 115        struct xt_set_info_match_v0 *info = par->matchinfo;
 116
 117        ip_set_nfnl_put(info->match_set.index);
 118}
 119
 120static unsigned int
 121set_target_v0(struct sk_buff *skb, const struct xt_action_param *par)
 122{
 123        const struct xt_set_info_target_v0 *info = par->targinfo;
 124        ADT_OPT(add_opt, par->family, info->add_set.u.compat.dim,
 125                info->add_set.u.compat.flags, 0, UINT_MAX);
 126        ADT_OPT(del_opt, par->family, info->del_set.u.compat.dim,
 127                info->del_set.u.compat.flags, 0, UINT_MAX);
 128
 129        if (info->add_set.index != IPSET_INVALID_ID)
 130                ip_set_add(info->add_set.index, skb, par, &add_opt);
 131        if (info->del_set.index != IPSET_INVALID_ID)
 132                ip_set_del(info->del_set.index, skb, par, &del_opt);
 133
 134        return XT_CONTINUE;
 135}
 136
 137static int
 138set_target_v0_checkentry(const struct xt_tgchk_param *par)
 139{
 140        struct xt_set_info_target_v0 *info = par->targinfo;
 141        ip_set_id_t index;
 142
 143        if (info->add_set.index != IPSET_INVALID_ID) {
 144                index = ip_set_nfnl_get_byindex(info->add_set.index);
 145                if (index == IPSET_INVALID_ID) {
 146                        pr_warning("Cannot find add_set index %u as target\n",
 147                                   info->add_set.index);
 148                        return -ENOENT;
 149                }
 150        }
 151
 152        if (info->del_set.index != IPSET_INVALID_ID) {
 153                index = ip_set_nfnl_get_byindex(info->del_set.index);
 154                if (index == IPSET_INVALID_ID) {
 155                        pr_warning("Cannot find del_set index %u as target\n",
 156                                   info->del_set.index);
 157                        if (info->add_set.index != IPSET_INVALID_ID)
 158                                ip_set_nfnl_put(info->add_set.index);
 159                        return -ENOENT;
 160                }
 161        }
 162        if (info->add_set.u.flags[IPSET_DIM_MAX-1] != 0 ||
 163            info->del_set.u.flags[IPSET_DIM_MAX-1] != 0) {
 164                pr_warning("Protocol error: SET target dimension "
 165                           "is over the limit!\n");
 166                if (info->add_set.index != IPSET_INVALID_ID)
 167                        ip_set_nfnl_put(info->add_set.index);
 168                if (info->del_set.index != IPSET_INVALID_ID)
 169                        ip_set_nfnl_put(info->del_set.index);
 170                return -ERANGE;
 171        }
 172
 173        /* Fill out compatibility data */
 174        compat_flags(&info->add_set);
 175        compat_flags(&info->del_set);
 176
 177        return 0;
 178}
 179
 180static void
 181set_target_v0_destroy(const struct xt_tgdtor_param *par)
 182{
 183        const struct xt_set_info_target_v0 *info = par->targinfo;
 184
 185        if (info->add_set.index != IPSET_INVALID_ID)
 186                ip_set_nfnl_put(info->add_set.index);
 187        if (info->del_set.index != IPSET_INVALID_ID)
 188                ip_set_nfnl_put(info->del_set.index);
 189}
 190
 191/* Revision 1 match and target */
 192
 193static bool
 194set_match_v1(const struct sk_buff *skb, struct xt_action_param *par)
 195{
 196        const struct xt_set_info_match_v1 *info = par->matchinfo;
 197        ADT_OPT(opt, par->family, info->match_set.dim,
 198                info->match_set.flags, 0, UINT_MAX);
 199
 200        return match_set(info->match_set.index, skb, par, &opt,
 201                         info->match_set.flags & IPSET_INV_MATCH);
 202}
 203
 204static int
 205set_match_v1_checkentry(const struct xt_mtchk_param *par)
 206{
 207        struct xt_set_info_match_v1 *info = par->matchinfo;
 208        ip_set_id_t index;
 209
 210        index = ip_set_nfnl_get_byindex(info->match_set.index);
 211
 212        if (index == IPSET_INVALID_ID) {
 213                pr_warning("Cannot find set indentified by id %u to match\n",
 214                           info->match_set.index);
 215                return -ENOENT;
 216        }
 217        if (info->match_set.dim > IPSET_DIM_MAX) {
 218                pr_warning("Protocol error: set match dimension "
 219                           "is over the limit!\n");
 220                ip_set_nfnl_put(info->match_set.index);
 221                return -ERANGE;
 222        }
 223
 224        return 0;
 225}
 226
 227static void
 228set_match_v1_destroy(const struct xt_mtdtor_param *par)
 229{
 230        struct xt_set_info_match_v1 *info = par->matchinfo;
 231
 232        ip_set_nfnl_put(info->match_set.index);
 233}
 234
 235static unsigned int
 236set_target_v1(struct sk_buff *skb, const struct xt_action_param *par)
 237{
 238        const struct xt_set_info_target_v1 *info = par->targinfo;
 239        ADT_OPT(add_opt, par->family, info->add_set.dim,
 240                info->add_set.flags, 0, UINT_MAX);
 241        ADT_OPT(del_opt, par->family, info->del_set.dim,
 242                info->del_set.flags, 0, UINT_MAX);
 243
 244        if (info->add_set.index != IPSET_INVALID_ID)
 245                ip_set_add(info->add_set.index, skb, par, &add_opt);
 246        if (info->del_set.index != IPSET_INVALID_ID)
 247                ip_set_del(info->del_set.index, skb, par, &del_opt);
 248
 249        return XT_CONTINUE;
 250}
 251
 252static int
 253set_target_v1_checkentry(const struct xt_tgchk_param *par)
 254{
 255        const struct xt_set_info_target_v1 *info = par->targinfo;
 256        ip_set_id_t index;
 257
 258        if (info->add_set.index != IPSET_INVALID_ID) {
 259                index = ip_set_nfnl_get_byindex(info->add_set.index);
 260                if (index == IPSET_INVALID_ID) {
 261                        pr_warning("Cannot find add_set index %u as target\n",
 262                                   info->add_set.index);
 263                        return -ENOENT;
 264                }
 265        }
 266
 267        if (info->del_set.index != IPSET_INVALID_ID) {
 268                index = ip_set_nfnl_get_byindex(info->del_set.index);
 269                if (index == IPSET_INVALID_ID) {
 270                        pr_warning("Cannot find del_set index %u as target\n",
 271                                   info->del_set.index);
 272                        if (info->add_set.index != IPSET_INVALID_ID)
 273                                ip_set_nfnl_put(info->add_set.index);
 274                        return -ENOENT;
 275                }
 276        }
 277        if (info->add_set.dim > IPSET_DIM_MAX ||
 278            info->del_set.dim > IPSET_DIM_MAX) {
 279                pr_warning("Protocol error: SET target dimension "
 280                           "is over the limit!\n");
 281                if (info->add_set.index != IPSET_INVALID_ID)
 282                        ip_set_nfnl_put(info->add_set.index);
 283                if (info->del_set.index != IPSET_INVALID_ID)
 284                        ip_set_nfnl_put(info->del_set.index);
 285                return -ERANGE;
 286        }
 287
 288        return 0;
 289}
 290
 291static void
 292set_target_v1_destroy(const struct xt_tgdtor_param *par)
 293{
 294        const struct xt_set_info_target_v1 *info = par->targinfo;
 295
 296        if (info->add_set.index != IPSET_INVALID_ID)
 297                ip_set_nfnl_put(info->add_set.index);
 298        if (info->del_set.index != IPSET_INVALID_ID)
 299                ip_set_nfnl_put(info->del_set.index);
 300}
 301
 302/* Revision 2 target */
 303
 304static unsigned int
 305set_target_v2(struct sk_buff *skb, const struct xt_action_param *par)
 306{
 307        const struct xt_set_info_target_v2 *info = par->targinfo;
 308        ADT_MOPT(add_opt, par->family, info->add_set.dim,
 309                 info->add_set.flags, info->flags, info->timeout);
 310        ADT_OPT(del_opt, par->family, info->del_set.dim,
 311                info->del_set.flags, 0, UINT_MAX);
 312
 313        /* Normalize to fit into jiffies */
 314        if (add_opt.timeout != IPSET_NO_TIMEOUT &&
 315            add_opt.timeout > UINT_MAX/MSEC_PER_SEC)
 316                add_opt.timeout = UINT_MAX/MSEC_PER_SEC;
 317        if (info->add_set.index != IPSET_INVALID_ID)
 318                ip_set_add(info->add_set.index, skb, par, &add_opt);
 319        if (info->del_set.index != IPSET_INVALID_ID)
 320                ip_set_del(info->del_set.index, skb, par, &del_opt);
 321
 322        return XT_CONTINUE;
 323}
 324
 325#define set_target_v2_checkentry        set_target_v1_checkentry
 326#define set_target_v2_destroy           set_target_v1_destroy
 327
 328static struct xt_match set_matches[] __read_mostly = {
 329        {
 330                .name           = "set",
 331                .family         = NFPROTO_IPV4,
 332                .revision       = 0,
 333                .match          = set_match_v0,
 334                .matchsize      = sizeof(struct xt_set_info_match_v0),
 335                .checkentry     = set_match_v0_checkentry,
 336                .destroy        = set_match_v0_destroy,
 337                .me             = THIS_MODULE
 338        },
 339        {
 340                .name           = "set",
 341                .family         = NFPROTO_IPV4,
 342                .revision       = 1,
 343                .match          = set_match_v1,
 344                .matchsize      = sizeof(struct xt_set_info_match_v1),
 345                .checkentry     = set_match_v1_checkentry,
 346                .destroy        = set_match_v1_destroy,
 347                .me             = THIS_MODULE
 348        },
 349        {
 350                .name           = "set",
 351                .family         = NFPROTO_IPV6,
 352                .revision       = 1,
 353                .match          = set_match_v1,
 354                .matchsize      = sizeof(struct xt_set_info_match_v1),
 355                .checkentry     = set_match_v1_checkentry,
 356                .destroy        = set_match_v1_destroy,
 357                .me             = THIS_MODULE
 358        },
 359        /* --return-nomatch flag support */
 360        {
 361                .name           = "set",
 362                .family         = NFPROTO_IPV4,
 363                .revision       = 2,
 364                .match          = set_match_v1,
 365                .matchsize      = sizeof(struct xt_set_info_match_v1),
 366                .checkentry     = set_match_v1_checkentry,
 367                .destroy        = set_match_v1_destroy,
 368                .me             = THIS_MODULE
 369        },
 370        {
 371                .name           = "set",
 372                .family         = NFPROTO_IPV6,
 373                .revision       = 2,
 374                .match          = set_match_v1,
 375                .matchsize      = sizeof(struct xt_set_info_match_v1),
 376                .checkentry     = set_match_v1_checkentry,
 377                .destroy        = set_match_v1_destroy,
 378                .me             = THIS_MODULE
 379        },
 380};
 381
 382static struct xt_target set_targets[] __read_mostly = {
 383        {
 384                .name           = "SET",
 385                .revision       = 0,
 386                .family         = NFPROTO_IPV4,
 387                .target         = set_target_v0,
 388                .targetsize     = sizeof(struct xt_set_info_target_v0),
 389                .checkentry     = set_target_v0_checkentry,
 390                .destroy        = set_target_v0_destroy,
 391                .me             = THIS_MODULE
 392        },
 393        {
 394                .name           = "SET",
 395                .revision       = 1,
 396                .family         = NFPROTO_IPV4,
 397                .target         = set_target_v1,
 398                .targetsize     = sizeof(struct xt_set_info_target_v1),
 399                .checkentry     = set_target_v1_checkentry,
 400                .destroy        = set_target_v1_destroy,
 401                .me             = THIS_MODULE
 402        },
 403        {
 404                .name           = "SET",
 405                .revision       = 1,
 406                .family         = NFPROTO_IPV6,
 407                .target         = set_target_v1,
 408                .targetsize     = sizeof(struct xt_set_info_target_v1),
 409                .checkentry     = set_target_v1_checkentry,
 410                .destroy        = set_target_v1_destroy,
 411                .me             = THIS_MODULE
 412        },
 413        /* --timeout and --exist flags support */
 414        {
 415                .name           = "SET",
 416                .revision       = 2,
 417                .family         = NFPROTO_IPV4,
 418                .target         = set_target_v2,
 419                .targetsize     = sizeof(struct xt_set_info_target_v2),
 420                .checkentry     = set_target_v2_checkentry,
 421                .destroy        = set_target_v2_destroy,
 422                .me             = THIS_MODULE
 423        },
 424        {
 425                .name           = "SET",
 426                .revision       = 2,
 427                .family         = NFPROTO_IPV6,
 428                .target         = set_target_v2,
 429                .targetsize     = sizeof(struct xt_set_info_target_v2),
 430                .checkentry     = set_target_v2_checkentry,
 431                .destroy        = set_target_v2_destroy,
 432                .me             = THIS_MODULE
 433        },
 434};
 435
 436static int __init xt_set_init(void)
 437{
 438        int ret = xt_register_matches(set_matches, ARRAY_SIZE(set_matches));
 439
 440        if (!ret) {
 441                ret = xt_register_targets(set_targets,
 442                                          ARRAY_SIZE(set_targets));
 443                if (ret)
 444                        xt_unregister_matches(set_matches,
 445                                              ARRAY_SIZE(set_matches));
 446        }
 447        return ret;
 448}
 449
 450static void __exit xt_set_fini(void)
 451{
 452        xt_unregister_matches(set_matches, ARRAY_SIZE(set_matches));
 453        xt_unregister_targets(set_targets, ARRAY_SIZE(set_targets));
 454}
 455
 456module_init(xt_set_init);
 457module_exit(xt_set_fini);
 458
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.