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
  76xt_ct_set_helper(struct nf_conn *ct, const char *helper_name,
  77                 const struct xt_tgchk_param *par)
  78{
  79        struct nf_conntrack_helper *helper;
  80        struct nf_conn_help *help;
  81        u8 proto;
  82
  83        proto = xt_ct_find_proto(par);
  84        if (!proto) {
  85                pr_info("You must specify a L4 protocol, and not use "
  86                        "inversions on it.\n");
  87                return -ENOENT;
  88        }
  89
  90        helper = nf_conntrack_helper_try_module_get(helper_name, par->family,
  91                                                    proto);
  92        if (helper == NULL) {
  93                pr_info("No such helper \"%s\"\n", helper_name);
  94                return -ENOENT;
  95        }
  96
  97        help = nf_ct_helper_ext_add(ct, helper, GFP_KERNEL);
  98        if (help == NULL) {
  99                module_put(helper->me);
 100                return -ENOMEM;
 101        }
 102
 103        help->helper = helper;
 104        return 0;
 105}
 106
 107static int xt_ct_tg_check_v0(const struct xt_tgchk_param *par)
 108{
 109        struct xt_ct_target_info *info = par->targinfo;
 110        struct nf_conntrack_tuple t;
 111        struct nf_conn *ct;
 112        int ret;
 113
 114        if (info->flags & ~XT_CT_NOTRACK)
 115                return -EINVAL;
 116
 117        if (info->flags & XT_CT_NOTRACK) {
 118                ct = nf_ct_untracked_get();
 119                atomic_inc(&ct->ct_general.use);
 120                goto out;
 121        }
 122
 123#ifndef CONFIG_NF_CONNTRACK_ZONES
 124        if (info->zone)
 125                goto err1;
 126#endif
 127
 128        ret = nf_ct_l3proto_try_module_get(par->family);
 129        if (ret < 0)
 130                goto err1;
 131
 132        memset(&t, 0, sizeof(t));
 133        ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
 134        ret = PTR_ERR(ct);
 135        if (IS_ERR(ct))
 136                goto err2;
 137
 138        ret = 0;
 139        if ((info->ct_events || info->exp_events) &&
 140            !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
 141                                  GFP_KERNEL))
 142                goto err3;
 143
 144        if (info->helper[0]) {
 145                ret = xt_ct_set_helper(ct, info->helper, par);
 146                if (ret < 0)
 147                        goto err3;
 148        }
 149
 150        __set_bit(IPS_TEMPLATE_BIT, &ct->status);
 151        __set_bit(IPS_CONFIRMED_BIT, &ct->status);
 152out:
 153        info->ct = ct;
 154        return 0;
 155
 156err3:
 157        nf_conntrack_free(ct);
 158err2:
 159        nf_ct_l3proto_module_put(par->family);
 160err1:
 161        return ret;
 162}
 163
 164#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 165static void __xt_ct_tg_timeout_put(struct ctnl_timeout *timeout)
 166{
 167        typeof(nf_ct_timeout_put_hook) timeout_put;
 168
 169        timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
 170        if (timeout_put)
 171                timeout_put(timeout);
 172}
 173#endif
 174
 175static int
 176xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par,
 177                  const char *timeout_name)
 178{
 179#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 180        typeof(nf_ct_timeout_find_get_hook) timeout_find_get;
 181        struct ctnl_timeout *timeout;
 182        struct nf_conn_timeout *timeout_ext;
 183        struct nf_conntrack_l4proto *l4proto;
 184        int ret = 0;
 185        u8 proto;
 186
 187        rcu_read_lock();
 188        timeout_find_get = rcu_dereference(nf_ct_timeout_find_get_hook);
 189        if (timeout_find_get == NULL) {
 190                ret = -ENOENT;
 191                pr_info("Timeout policy base is empty\n");
 192                goto out;
 193        }
 194
 195        proto = xt_ct_find_proto(par);
 196        if (!proto) {
 197                ret = -EINVAL;
 198                pr_info("You must specify a L4 protocol, and not use "
 199                        "inversions on it.\n");
 200                goto out;
 201        }
 202
 203        timeout = timeout_find_get(timeout_name);
 204        if (timeout == NULL) {
 205                ret = -ENOENT;
 206                pr_info("No such timeout policy \"%s\"\n", timeout_name);
 207                goto out;
 208        }
 209
 210        if (timeout->l3num != par->family) {
 211                ret = -EINVAL;
 212                pr_info("Timeout policy `%s' can only be used by L3 protocol "
 213                        "number %d\n", timeout_name, timeout->l3num);
 214                goto err_put_timeout;
 215        }
 216        /* Make sure the timeout policy matches any existing protocol tracker,
 217         * otherwise default to generic.
 218         */
 219        l4proto = __nf_ct_l4proto_find(par->family, proto);
 220        if (timeout->l4proto->l4proto != l4proto->l4proto) {
 221                ret = -EINVAL;
 222                pr_info("Timeout policy `%s' can only be used by L4 protocol "
 223                        "number %d\n",
 224                        timeout_name, timeout->l4proto->l4proto);
 225                goto err_put_timeout;
 226        }
 227        timeout_ext = nf_ct_timeout_ext_add(ct, timeout, GFP_ATOMIC);
 228        if (timeout_ext == NULL)
 229                ret = -ENOMEM;
 230
 231err_put_timeout:
 232        __xt_ct_tg_timeout_put(timeout);
 233out:
 234        rcu_read_unlock();
 235        return ret;
 236#else
 237        return -EOPNOTSUPP;
 238#endif
 239}
 240
 241static int xt_ct_tg_check_v1(const struct xt_tgchk_param *par)
 242{
 243        struct xt_ct_target_info_v1 *info = par->targinfo;
 244        struct nf_conntrack_tuple t;
 245        struct nf_conn *ct;
 246        int ret;
 247
 248        if (info->flags & ~XT_CT_NOTRACK)
 249                return -EINVAL;
 250
 251        if (info->flags & XT_CT_NOTRACK) {
 252                ct = nf_ct_untracked_get();
 253                atomic_inc(&ct->ct_general.use);
 254                goto out;
 255        }
 256
 257#ifndef CONFIG_NF_CONNTRACK_ZONES
 258        if (info->zone)
 259                goto err1;
 260#endif
 261
 262        ret = nf_ct_l3proto_try_module_get(par->family);
 263        if (ret < 0)
 264                goto err1;
 265
 266        memset(&t, 0, sizeof(t));
 267        ct = nf_conntrack_alloc(par->net, info->zone, &t, &t, GFP_KERNEL);
 268        ret = PTR_ERR(ct);
 269        if (IS_ERR(ct))
 270                goto err2;
 271
 272        ret = 0;
 273        if ((info->ct_events || info->exp_events) &&
 274            !nf_ct_ecache_ext_add(ct, info->ct_events, info->exp_events,
 275                                  GFP_KERNEL))
 276                goto err3;
 277
 278        if (info->helper[0]) {
 279                ret = xt_ct_set_helper(ct, info->helper, par);
 280                if (ret < 0)
 281                        goto err3;
 282        }
 283
 284        if (info->timeout[0]) {
 285                ret = xt_ct_set_timeout(ct, par, info->timeout);
 286                if (ret < 0)
 287                        goto err3;
 288        }
 289
 290        __set_bit(IPS_TEMPLATE_BIT, &ct->status);
 291        __set_bit(IPS_CONFIRMED_BIT, &ct->status);
 292out:
 293        info->ct = ct;
 294        return 0;
 295
 296err3:
 297        nf_conntrack_free(ct);
 298err2:
 299        nf_ct_l3proto_module_put(par->family);
 300err1:
 301        return ret;
 302}
 303
 304static void xt_ct_tg_destroy_v0(const struct xt_tgdtor_param *par)
 305{
 306        struct xt_ct_target_info *info = par->targinfo;
 307        struct nf_conn *ct = info->ct;
 308        struct nf_conn_help *help;
 309
 310        if (!nf_ct_is_untracked(ct)) {
 311                help = nfct_help(ct);
 312                if (help)
 313                        module_put(help->helper->me);
 314
 315                nf_ct_l3proto_module_put(par->family);
 316        }
 317        nf_ct_put(info->ct);
 318}
 319
 320static void xt_ct_destroy_timeout(struct nf_conn *ct)
 321{
 322#ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 323        struct nf_conn_timeout *timeout_ext;
 324        typeof(nf_ct_timeout_put_hook) timeout_put;
 325
 326        rcu_read_lock();
 327        timeout_put = rcu_dereference(nf_ct_timeout_put_hook);
 328
 329        if (timeout_put) {
 330                timeout_ext = nf_ct_timeout_find(ct);
 331                if (timeout_ext)
 332                        timeout_put(timeout_ext->timeout);
 333        }
 334        rcu_read_unlock();
 335#endif
 336}
 337
 338static void xt_ct_tg_destroy_v1(const struct xt_tgdtor_param *par)
 339{
 340        struct xt_ct_target_info_v1 *info = par->targinfo;
 341        struct nf_conn *ct = info->ct;
 342        struct nf_conn_help *help;
 343
 344        if (!nf_ct_is_untracked(ct)) {
 345                help = nfct_help(ct);
 346                if (help)
 347                        module_put(help->helper->me);
 348
 349                nf_ct_l3proto_module_put(par->family);
 350
 351                xt_ct_destroy_timeout(ct);
 352        }
 353        nf_ct_put(info->ct);
 354}
 355
 356static struct xt_target xt_ct_tg_reg[] __read_mostly = {
 357        {
 358                .name           = "CT",
 359                .family         = NFPROTO_UNSPEC,
 360                .targetsize     = sizeof(struct xt_ct_target_info),
 361                .checkentry     = xt_ct_tg_check_v0,
 362                .destroy        = xt_ct_tg_destroy_v0,
 363                .target         = xt_ct_target_v0,
 364                .table          = "raw",
 365                .me             = THIS_MODULE,
 366        },
 367        {
 368                .name           = "CT",
 369                .family         = NFPROTO_UNSPEC,
 370                .revision       = 1,
 371                .targetsize     = sizeof(struct xt_ct_target_info_v1),
 372                .checkentry     = xt_ct_tg_check_v1,
 373                .destroy        = xt_ct_tg_destroy_v1,
 374                .target         = xt_ct_target_v1,
 375                .table          = "raw",
 376                .me             = THIS_MODULE,
 377        },
 378};
 379
 380static int __init xt_ct_tg_init(void)
 381{
 382        return xt_register_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
 383}
 384
 385static void __exit xt_ct_tg_exit(void)
 386{
 387        xt_unregister_targets(xt_ct_tg_reg, ARRAY_SIZE(xt_ct_tg_reg));
 388}
 389
 390module_init(xt_ct_tg_init);
 391module_exit(xt_ct_tg_exit);
 392
 393MODULE_LICENSE("GPL");
 394MODULE_DESCRIPTION("Xtables: connection tracking target");
 395MODULE_ALIAS("ipt_CT");
 396MODULE_ALIAS("ip6t_CT");
 397
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.