linux/net/netfilter/xt_CT.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010 Patrick McHardy <kaber@trash.net>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   9#include <linux/module.h>
  10#include <linux/gfp.h>
  11#include <linux/skbuff.h>
  12#include <linux/netfilter_ipv4/ip_tables.h>
  13#include <linux/netfilter_ipv6/ip6_tables.h>
  14#include <linux/netfilter/x_tables.h>
  15#include <linux/netfilter/xt_CT.h>
  16#include <net/netfilter/nf_conntrack.h>
  17#include <net/netfilter/nf_conntrack_l4proto.h>
  18#include <net/netfilter/nf_conntrack_helper.h>
  19#include <net/netfilter/nf_conntrack_ecache.h>
  20#include <net/netfilter/nf_conntrack_timeout.h>
  21#include <net/netfilter/nf_conntrack_zones.h>
  22
  23static unsigned int xt_ct_target_v0(struct sk_buff *skb,
  24                                    const struct xt_action_param *par)
  25{
  26        const struct xt_ct_target_info *info = par->targinfo;
  27        struct nf_conn *ct = info->ct;
  28
  29        /* Previously seen (loopback)? Ignore. */
  30        if (skb->nfct != NULL)
  31                return XT_CONTINUE;
  32
  33        atomic_inc(&ct->ct_general.use);
  34        skb->nfct = &ct->ct_general;
  35        skb->nfctinfo = IP_CT_NEW;
  36
  37        return XT_CONTINUE;
  38}
  39
  40static unsigned int xt_ct_target_v1(struct sk_buff *skb,
  41                                    const struct xt_action_param *par)
  42{
  43        const struct xt_ct_target_info_v1 *info = par->targinfo;
  44        struct nf_conn *ct = info->ct;
  45
  46        /* Previously seen (loopback)? Ignore. */
  47        if (skb->nfct != NULL)
  48                return XT_CONTINUE;
  49
  50        atomic_inc(&ct->ct_general.use);
  51        skb->nfct = &ct->ct_general;
  52        skb->nfctinfo = IP_CT_NEW;
  53
  54        return XT_CONTINUE;
  55}
  56
  57static u8 xt_ct_find_proto(const struct xt_tgchk_param *par)
  58{
  59        if (par->family == NFPROTO_IPV4) {
  60                const struct ipt_entry *e = par->entryinfo;
  61
  62                if (e->ip.invflags & IPT_INV_PROTO)
  63                        return 0;
  64                return e->ip.proto;
  65        } else if (par->family == NFPROTO_IPV6) {
  66                const struct ip6t_entry *e = par->entryinfo;
  67
  68                if (e->ipv6.invflags & IP6T_INV_PROTO)
  69                        return 0;
  70                return e->ipv6.proto;
  71        } else
  72                return 0;
  73}
  74
  75static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
  76{
  77        struct xt_ct_target_info *info = par->targinfo;
  78        struct nf_conntrack_tuple t;
  79        struct nf_conn_help *help;
  80        struct nf_conn *ct;
  81        int ret = 0;
  82        u8 proto;
  83
  84        if (info->flags & ~XT_CT_NOTRACK)
  85                return -EINVAL;
  86
  87        if (info->flags & XT_CT_NOTRACK) {
  88                ct = nf_ct_untracked_get();
  89                atomic_inc(&ct->ct_general.use);
  90                goto out;
  91        }
  92
  93#ifndef CONFIG_NF_CONNTRACK_ZONES
  94        if (info->zone)
  95                goto err1;
  96#endif
  97
  98        ret = nf_ct_l3proto_try_module_get(par->family);
  99        if (ret < 0)
 100                goto err1;
 101
 102        memset(&t, 0, sizeof(t));
 103        ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
 104        ret = PTR_ERR(ct);
 105        if (IS_ERR(ct))
 106                goto err2;
 107
 108        ret = 0;
 109        if ((info->ct_events || info->exp_events) &&
 110            !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
 111                                  GFP_KERNEL))
 112                goto err3;
 113
 114        if (info->helper[0]) {
 115                struct nf_conntrack_helper *helper;
 116
 117                ret = -ENOENT;
 118                proto = xt_ct_find_proto(par);
 119                if (!proto) {
 120                        pr_info("You must specify a L4 protocol, "
 121                                "and not use inversions on it.\n");
 122                        goto err3;
 123                }
 124
 125                ret = -ENOENT;
 126                helper = nf_conntrack_helper_try_module_get(info->helper,
 127                                                            par->family,
 128                                                            proto);
 129                if (helper == NULL) {
 130                        pr_info("No such helper \"%s\"\n", info->helper);
 131                        goto err3;
 132                }
 133
 134                ret = -ENOMEM;
 135                help = nf_ct_helper_ext_add(ct, helper, GFP_KERNEL);
 136                if (help == NULL)
 137                        goto err3;
 138
 139                help->helper = helper;
 140        }
 141
 142        __set_bit(IPS_TEMPLATE_BIT, &ct->status);
 143        __set_bit(IPS_CONFIRMED_BIT, &ct->status);
 144out:
 145        info->ct = ct;
 146        return 0;
 147
 148err3:
 149        nf_conntrack_free(ct);
 150err2:
 151        nf_ct_l3proto_module_put(par->family);
 152err1:
 153        return ret;
 154}
 155
 156#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 157static void __xt_ct_tg_timeout_put(struct ctnl_timeout *timeout)
 158{
 159        typeof(nf_ct_timeout_put_hook) timeout_put;
 160
 161        timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
 162        if (timeout_put)
 163                timeout_put(timeout);
 164}
 165#endif
 166
 167static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
 168{
 169        struct xt_ct_target_info_v1 *info = par->targinfo;
 170        struct nf_conntrack_tuple t;
 171        struct nf_conn_help *help;
 172        struct nf_conn *ct;
 173        int ret = 0;
 174        u8 proto;
 175#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 176        struct ctnl_timeout *timeout;
 177#endif
 178        if (info->flags & ~XT_CT_NOTRACK)
 179                return -EINVAL;
 180
 181        if (info->flags & XT_CT_NOTRACK) {
 182                ct = nf_ct_untracked_get();
 183                atomic_inc(&ct->ct_general.use);
 184                goto out;
 185        }
 186
 187#ifndef CONFIG_NF_CONNTRACK_ZONES
 188        if (info->zone)
 189                goto err1;
 190#endif
 191
 192        ret = nf_ct_l3proto_try_module_get(par->family);
 193        if (ret < 0)
 194                goto err1;
 195
 196        memset(&t, 0, sizeof(t));
 197        ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
 198        ret = PTR_ERR(ct);
 199        if (IS_ERR(ct))
 200                goto err2;
 201
 202        ret = 0;
 203        if ((info->ct_events || info->exp_events) &&
 204            !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
 205                                  GFP_KERNEL))
 206                goto err3;
 207
 208        if (info->helper[0]) {
 209                struct nf_conntrack_helper *helper;
 210
 211                ret = -ENOENT;
 212                proto = xt_ct_find_proto(par);
 213                if (!proto) {
 214                        pr_info("You must specify a L4 protocol, "
 215                                "and not use inversions on it.\n");
 216                        goto err3;
 217                }
 218
 219                ret = -ENOENT;
 220                helper = nf_conntrack_helper_try_module_get(info->helper,
 221                                                            par->family,
 222                                                            proto);
 223                if (helper == NULL) {
 224                        pr_info("No such helper \"%s\"\n", info->helper);
 225                        goto err3;
 226                }
 227
 228                ret = -ENOMEM;
 229                help = nf_ct_helper_ext_add(ct, helper, GFP_KERNEL);
 230                if (help == NULL)
 231                        goto err3;
 232
 233                help->helper = helper;
 234        }
 235
 236#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 237        if (info->timeout[0]) {
 238                typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
 239                struct nf_conn_timeout *timeout_ext;
 240
 241                rcu_read_lock();
 242                timeout_find_get =
 243                        rcu_dereference(nf_ct_timeout_find_get_hook);
 244
 245                if (timeout_find_get) {
 246                        const struct ipt_entry *e = par->entryinfo;
 247                        struct nf_conntrack_l4proto *l4proto;
 248
 249                        if (e->ip.invflags & IPT_INV_PROTO) {
 250                                ret = -EINVAL;
 251                                pr_info("You cannot use inversion on "
 252                                         "L4 protocol\n");
 253                                goto err4;
 254                        }
 255                        timeout = timeout_find_get(info->timeout);
 256                        if (timeout == NULL) {
 257                                ret = -ENOENT;
 258                                pr_info("No such timeout policy \"%s\"\n",
 259                                        info->timeout);
 260                                goto err4;
 261                        }
 262                        if (timeout->l3num != par->family) {
 263                                ret = -EINVAL;
 264                                pr_info("Timeout policy `%s' can only be "
 265                                        "used by L3 protocol number %d\n",
 266                                        info->timeout, timeout->l3num);
 267                                goto err5;
 268                        }
 269                        /* Make sure the timeout policy matches any existing
 270                         * protocol tracker, otherwise default to generic.
 271                         */
 272                        l4proto = __nf_ct_l4proto_find(par->family,
 273                                                       e->ip.proto);
 274                        if (timeout->l4proto->l4proto != l4proto->l4proto) {
 275                                ret = -EINVAL;
 276                                pr_info("Timeout policy `%s' can only be "
 277                                        "used by L4 protocol number %d\n",
 278                                        info->timeout,
 279                                        timeout->l4proto->l4proto);
 280                                goto err5;
 281                        }
 282                        timeout_ext = nf_ct_timeout_ext_add(ct, timeout,
 283                                                            GFP_ATOMIC);
 284                        if (timeout_ext == NULL) {
 285                                ret = -ENOMEM;
 286                                goto err5;
 287                        }
 288                } else {
 289                        ret = -ENOENT;
 290                        pr_info("Timeout policy base is empty\n");
 291                        goto err4;
 292                }
 293                rcu_read_unlock();
 294        }
 295#endif
 296
 297        __set_bit(IPS_TEMPLATE_BIT, &ct->status);
 298        __set_bit(IPS_CONFIRMED_BIT, &ct->status);
 299out:
 300        info->ct = ct;
 301        return 0;
 302
 303#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 304err5:
 305        __xt_ct_tg_timeout_put(timeout);
 306err4:
 307        rcu_read_unlock();
 308#endif
 309err3:
 310        nf_conntrack_free(ct);
 311err2:
 312        nf_ct_l3proto_module_put(par->family);
 313err1:
 314        return ret;
 315}
 316
 317static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
 318{
 319        struct xt_ct_target_info *info = par->targinfo;
 320        struct nf_conn *ct = info->ct;
 321        struct nf_conn_help *help;
 322
 323        if (!nf_ct_is_untracked(ct)) {
 324                help = nfct_help(ct);
 325                if (help)
 326                        module_put(help->helper->me);
 327
 328                nf_ct_l3proto_module_put(par->family);
 329        }
 330        nf_ct_put(info->ct);
 331}
 332
 333static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
 334{
 335        struct xt_ct_target_info_v1 *info = par->targinfo;
 336        struct nf_conn *ct = info->ct;
 337        struct nf_conn_help *help;
 338#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 339        struct nf_conn_timeout *timeout_ext;
 340        typeof(nf_ct_timeout_put_hook) timeout_put;
 341#endif
 342        if (!nf_ct_is_untracked(ct)) {
 343                help = nfct_help(ct);
 344                if (help)
 345                        module_put(help->helper->me);
 346
 347                nf_ct_l3proto_module_put(par->family);
 348
 349#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 350                rcu_read_lock();
 351                timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
 352
 353                if (timeout_put) {
 354                        timeout_ext = nf_ct_timeout_find(ct);
 355                        if (timeout_ext)
 356                                timeout_put(timeout_ext->timeout);
 357                }
 358                rcu_read_unlock();
 359#endif
 360        }
 361        nf_ct_put(info->ct);
 362}
 363
 364static struct xt_target xt_ct_tg_reg[] __read_mostly = {
 365        {
 366                .name           = "CT",
 367                .family         = NFPROTO_UNSPEC,
 368                .targetsize     = sizeof(struct xt_ct_target_info),
 369                .checkentry     = xt_ct_tg_check_v0,
 370                .destroy        = xt_ct_tg_destroy_v0,
 371                .target         = xt_ct_target_v0,
 372                .table          = "raw",
 373                .me             = THIS_MODULE,
 374        },
 375        {
 376                .name           = "CT",
 377                .family         = NFPROTO_UNSPEC,
 378                .revision       = 1,
 379                .targetsize     = sizeof(struct xt_ct_target_info_v1),
 380                .checkentry     = xt_ct_tg_check_v1,
 381                .destroy        = xt_ct_tg_destroy_v1,
 382                .target         = xt_ct_target_v1,
 383                .table          = "raw",
 384                .me             = THIS_MODULE,
 385        },
 386};
 387
 388static int __init xt_ct_tg_init(void)
 389{
 390        return xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
 391}
 392
 393static void __exit xt_ct_tg_exit(void)
 394{
 395        xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
 396}
 397
 398module_init(xt_ct_tg_init);
 399module_exit(xt_ct_tg_exit);
 400
 401MODULE_LICENSE("GPL");
 402MODULE_DESCRIPTION("Xtables: connection tracking target");
 403MODULE_ALIAS("ipt_CT");
 404MODULE_ALIAS("ip6t_CT");
 405
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.