linux/net/netfilter/nf_conntrack_proto.c
<<
>>
Prefs
   1/* L3/L4 protocol support for nf_conntrack. */
   2
   3/* (C) 1999-2001 Paul `Rusty' Russell
   4 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
   5 * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#include <linux/types.h>
  13#include <linux/netfilter.h>
  14#include <linux/module.h>
  15#include <linux/slab.h>
  16#include <linux/mutex.h>
  17#include <linux/vmalloc.h>
  18#include <linux/stddef.h>
  19#include <linux/err.h>
  20#include <linux/percpu.h>
  21#include <linux/notifier.h>
  22#include <linux/kernel.h>
  23#include <linux/netdevice.h>
  24
  25#include <net/netfilter/nf_conntrack.h>
  26#include <net/netfilter/nf_conntrack_l3proto.h>
  27#include <net/netfilter/nf_conntrack_l4proto.h>
  28#include <net/netfilter/nf_conntrack_core.h>
  29
  30static struct nf_conntrack_l4proto __rcu **nf_ct_protos[PF_MAX] __read_mostly;
  31struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX] __read_mostly;
  32EXPORT_SYMBOL_GPL(nf_ct_l3protos);
  33
  34static DEFINE_MUTEX(nf_ct_proto_mutex);
  35
  36#ifdef CONFIG_SYSCTL
  37static int
  38nf_ct_register_sysctl(struct net *net,
  39                      struct ctl_table_header **header,
  40                      const char *path,
  41                      struct ctl_table *table)
  42{
  43        if (*header == NULL) {
  44                *header = register_net_sysctl(net, path, table);
  45                if (*header == NULL)
  46                        return -ENOMEM;
  47        }
  48
  49        return 0;
  50}
  51
  52static void
  53nf_ct_unregister_sysctl(struct ctl_table_header **header,
  54                        struct ctl_table **table,
  55                        unsigned int users)
  56{
  57        if (users > 0)
  58                return;
  59
  60        unregister_net_sysctl_table(*header);
  61        kfree(*table);
  62        *header = NULL;
  63        *table = NULL;
  64}
  65#endif
  66
  67struct nf_conntrack_l4proto *
  68__nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto)
  69{
  70        if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL))
  71                return &nf_conntrack_l4proto_generic;
  72
  73        return rcu_dereference(nf_ct_protos[l3proto][l4proto]);
  74}
  75EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find);
  76
  77/* this is guaranteed to always return a valid protocol helper, since
  78 * it falls back to generic_protocol */
  79struct nf_conntrack_l3proto *
  80nf_ct_l3proto_find_get(u_int16_t l3proto)
  81{
  82        struct nf_conntrack_l3proto *p;
  83
  84        rcu_read_lock();
  85        p = __nf_ct_l3proto_find(l3proto);
  86        if (!try_module_get(p->me))
  87                p = &nf_conntrack_l3proto_generic;
  88        rcu_read_unlock();
  89
  90        return p;
  91}
  92EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get);
  93
  94void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p)
  95{
  96        module_put(p->me);
  97}
  98EXPORT_SYMBOL_GPL(nf_ct_l3proto_put);
  99
 100int
 101nf_ct_l3proto_try_module_get(unsigned short l3proto)
 102{
 103        int ret;
 104        struct nf_conntrack_l3proto *p;
 105
 106retry:  p = nf_ct_l3proto_find_get(l3proto);
 107        if (p == &nf_conntrack_l3proto_generic) {
 108                ret = request_module("nf_conntrack-%d", l3proto);
 109                if (!ret)
 110                        goto retry;
 111
 112                return -EPROTOTYPE;
 113        }
 114
 115        return 0;
 116}
 117EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get);
 118
 119void nf_ct_l3proto_module_put(unsigned short l3proto)
 120{
 121        struct nf_conntrack_l3proto *p;
 122
 123        /* rcu_read_lock not necessary since the caller holds a reference, but
 124         * taken anyways to avoid lockdep warnings in __nf_ct_l3proto_find()
 125         */
 126        rcu_read_lock();
 127        p = __nf_ct_l3proto_find(l3proto);
 128        module_put(p->me);
 129        rcu_read_unlock();
 130}
 131EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put);
 132
 133struct nf_conntrack_l4proto *
 134nf_ct_l4proto_find_get(u_int16_t l3num, u_int8_t l4num)
 135{
 136        struct nf_conntrack_l4proto *p;
 137
 138        rcu_read_lock();
 139        p = __nf_ct_l4proto_find(l3num, l4num);
 140        if (!try_module_get(p->me))
 141                p = &nf_conntrack_l4proto_generic;
 142        rcu_read_unlock();
 143
 144        return p;
 145}
 146EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get);
 147
 148void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p)
 149{
 150        module_put(p->me);
 151}
 152EXPORT_SYMBOL_GPL(nf_ct_l4proto_put);
 153
 154static int kill_l3proto(struct nf_conn *i, void *data)
 155{
 156        return nf_ct_l3num(i) == ((struct nf_conntrack_l3proto *)data)->l3proto;
 157}
 158
 159static int kill_l4proto(struct nf_conn *i, void *data)
 160{
 161        struct nf_conntrack_l4proto *l4proto;
 162        l4proto = (struct nf_conntrack_l4proto *)data;
 163        return nf_ct_protonum(i) == l4proto->l4proto &&
 164               nf_ct_l3num(i) == l4proto->l3proto;
 165}
 166
 167static struct nf_ip_net *nf_ct_l3proto_net(struct net *net,
 168                                           struct nf_conntrack_l3proto *l3proto)
 169{
 170        if (l3proto->l3proto == PF_INET)
 171                return &net->ct.nf_ct_proto;
 172        else
 173                return NULL;
 174}
 175
 176static int nf_ct_l3proto_register_sysctl(struct net *net,
 177                                         struct nf_conntrack_l3proto *l3proto)
 178{
 179        int err = 0;
 180        struct nf_ip_net *in = nf_ct_l3proto_net(net, l3proto);
 181        /* nf_conntrack_l3proto_ipv6 doesn't support sysctl */
 182        if (in == NULL)
 183                return 0;
 184
 185#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
 186        if (in->ctl_table != NULL) {
 187                err = nf_ct_register_sysctl(net,
 188                                            &in->ctl_table_header,
 189                                            l3proto->ctl_table_path,
 190                                            in->ctl_table);
 191                if (err < 0) {
 192                        kfree(in->ctl_table);
 193                        in->ctl_table = NULL;
 194                }
 195        }
 196#endif
 197        return err;
 198}
 199
 200static void nf_ct_l3proto_unregister_sysctl(struct net *net,
 201                                            struct nf_conntrack_l3proto *l3proto)
 202{
 203        struct nf_ip_net *in = nf_ct_l3proto_net(net, l3proto);
 204
 205        if (in == NULL)
 206                return;
 207#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
 208        if (in->ctl_table_header != NULL)
 209                nf_ct_unregister_sysctl(&in->ctl_table_header,
 210                                        &in->ctl_table,
 211                                        0);
 212#endif
 213}
 214
 215static int
 216nf_conntrack_l3proto_register_net(struct nf_conntrack_l3proto *proto)
 217{
 218        int ret = 0;
 219        struct nf_conntrack_l3proto *old;
 220
 221        if (proto->l3proto >= AF_MAX)
 222                return -EBUSY;
 223
 224        if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size)
 225                return -EINVAL;
 226
 227        mutex_lock(&nf_ct_proto_mutex);
 228        old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],
 229                                        lockdep_is_held(&nf_ct_proto_mutex));
 230        if (old != &nf_conntrack_l3proto_generic) {
 231                ret = -EBUSY;
 232                goto out_unlock;
 233        }
 234
 235        if (proto->nlattr_tuple_size)
 236                proto->nla_size = 3 * proto->nlattr_tuple_size();
 237
 238        rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
 239
 240out_unlock:
 241        mutex_unlock(&nf_ct_proto_mutex);
 242        return ret;
 243
 244}
 245
 246int nf_conntrack_l3proto_register(struct net *net,
 247                                  struct nf_conntrack_l3proto *proto)
 248{
 249        int ret = 0;
 250
 251        if (proto->init_net) {
 252                ret = proto->init_net(net);
 253                if (ret < 0)
 254                        return ret;
 255        }
 256
 257        ret = nf_ct_l3proto_register_sysctl(net, proto);
 258        if (ret < 0)
 259                return ret;
 260
 261        if (net == &init_net) {
 262                ret = nf_conntrack_l3proto_register_net(proto);
 263                if (ret < 0)
 264                        nf_ct_l3proto_unregister_sysctl(net, proto);
 265        }
 266
 267        return ret;
 268}
 269EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register);
 270
 271static void
 272nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto)
 273{
 274        BUG_ON(proto->l3proto >= AF_MAX);
 275
 276        mutex_lock(&nf_ct_proto_mutex);
 277        BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],
 278                                         lockdep_is_held(&nf_ct_proto_mutex)
 279                                         ) != proto);
 280        rcu_assign_pointer(nf_ct_l3protos[proto->l3proto],
 281                           &nf_conntrack_l3proto_generic);
 282        mutex_unlock(&nf_ct_proto_mutex);
 283
 284        synchronize_rcu();
 285}
 286
 287void nf_conntrack_l3proto_unregister(struct net *net,
 288                                     struct nf_conntrack_l3proto *proto)
 289{
 290        if (net == &init_net)
 291                nf_conntrack_l3proto_unregister_net(proto);
 292
 293        nf_ct_l3proto_unregister_sysctl(net, proto);
 294
 295        /* Remove all contrack entries for this protocol */
 296        nf_ct_iterate_cleanup(net, kill_l3proto, proto);
 297}
 298EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister);
 299
 300static struct nf_proto_net *nf_ct_l4proto_net(struct net *net,
 301                                              struct nf_conntrack_l4proto *l4proto)
 302{
 303        if (l4proto->get_net_proto) {
 304                /* statically built-in protocols use static per-net */
 305                return l4proto->get_net_proto(net);
 306        } else if (l4proto->net_id) {
 307                /* ... and loadable protocols use dynamic per-net */
 308                return net_generic(net, *l4proto->net_id);
 309        }
 310        return NULL;
 311}
 312
 313static
 314int nf_ct_l4proto_register_sysctl(struct net *net,
 315                                  struct nf_proto_net *pn,
 316                                  struct nf_conntrack_l4proto *l4proto)
 317{
 318        int err = 0;
 319
 320#ifdef CONFIG_SYSCTL
 321        if (pn->ctl_table != NULL) {
 322                err = nf_ct_register_sysctl(net,
 323                                            &pn->ctl_table_header,
 324                                            "net/netfilter",
 325                                            pn->ctl_table);
 326                if (err < 0) {
 327                        if (!pn->users) {
 328                                kfree(pn->ctl_table);
 329                                pn->ctl_table = NULL;
 330                        }
 331                }
 332        }
 333#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
 334        if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_table != NULL) {
 335                if (err < 0) {
 336                        nf_ct_kfree_compat_sysctl_table(pn);
 337                        goto out;
 338                }
 339                err = nf_ct_register_sysctl(net,
 340                                            &pn->ctl_compat_header,
 341                                            "net/ipv4/netfilter",
 342                                            pn->ctl_compat_table);
 343                if (err == 0)
 344                        goto out;
 345
 346                nf_ct_kfree_compat_sysctl_table(pn);
 347                nf_ct_unregister_sysctl(&pn->ctl_table_header,
 348                                        &pn->ctl_table,
 349                                        pn->users);
 350        }
 351out:
 352#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
 353#endif /* CONFIG_SYSCTL */
 354        return err;
 355}
 356
 357static
 358void nf_ct_l4proto_unregister_sysctl(struct net *net,
 359                                     struct nf_proto_net *pn,
 360                                     struct nf_conntrack_l4proto *l4proto)
 361{
 362#ifdef CONFIG_SYSCTL
 363        if (pn->ctl_table_header != NULL)
 364                nf_ct_unregister_sysctl(&pn->ctl_table_header,
 365                                        &pn->ctl_table,
 366                                        pn->users);
 367
 368#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
 369        if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_header != NULL)
 370                nf_ct_unregister_sysctl(&pn->ctl_compat_header,
 371                                        &pn->ctl_compat_table,
 372                                        0);
 373#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
 374#endif /* CONFIG_SYSCTL */
 375}
 376
 377/* FIXME: Allow NULL functions and sub in pointers to generic for
 378   them. --RR */
 379static int
 380nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto)
 381{
 382        int ret = 0;
 383
 384        if (l4proto->l3proto >= PF_MAX)
 385                return -EBUSY;
 386
 387        if ((l4proto->to_nlattr && !l4proto->nlattr_size)
 388                || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size))
 389                return -EINVAL;
 390
 391        mutex_lock(&nf_ct_proto_mutex);
 392        if (!nf_ct_protos[l4proto->l3proto]) {
 393                /* l3proto may be loaded latter. */
 394                struct nf_conntrack_l4proto __rcu **proto_array;
 395                int i;
 396
 397                proto_array = kmalloc(MAX_NF_CT_PROTO *
 398                                      sizeof(struct nf_conntrack_l4proto *),
 399                                      GFP_KERNEL);
 400                if (proto_array == NULL) {
 401                        ret = -ENOMEM;
 402                        goto out_unlock;
 403                }
 404
 405                for (i = 0; i < MAX_NF_CT_PROTO; i++)
 406                        RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic);
 407
 408                /* Before making proto_array visible to lockless readers,
 409                 * we must make sure its content is committed to memory.
 410                 */
 411                smp_wmb();
 412
 413                nf_ct_protos[l4proto->l3proto] = proto_array;
 414        } else if (rcu_dereference_protected(
 415                        nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 416                        lockdep_is_held(&nf_ct_proto_mutex)
 417                        ) != &nf_conntrack_l4proto_generic) {
 418                ret = -EBUSY;
 419                goto out_unlock;
 420        }
 421
 422        l4proto->nla_size = 0;
 423        if (l4proto->nlattr_size)
 424                l4proto->nla_size += l4proto->nlattr_size();
 425        if (l4proto->nlattr_tuple_size)
 426                l4proto->nla_size += 3 * l4proto->nlattr_tuple_size();
 427
 428        rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 429                           l4proto);
 430out_unlock:
 431        mutex_unlock(&nf_ct_proto_mutex);
 432        return ret;
 433}
 434
 435int nf_conntrack_l4proto_register(struct net *net,
 436                                  struct nf_conntrack_l4proto *l4proto)
 437{
 438        int ret = 0;
 439        struct nf_proto_net *pn = NULL;
 440
 441        if (l4proto->init_net) {
 442                ret = l4proto->init_net(net, l4proto->l3proto);
 443                if (ret < 0)
 444                        goto out;
 445        }
 446
 447        pn = nf_ct_l4proto_net(net, l4proto);
 448        if (pn == NULL)
 449                goto out;
 450
 451        ret = nf_ct_l4proto_register_sysctl(net, pn, l4proto);
 452        if (ret < 0)
 453                goto out;
 454
 455        if (net == &init_net) {
 456                ret = nf_conntrack_l4proto_register_net(l4proto);
 457                if (ret < 0) {
 458                        nf_ct_l4proto_unregister_sysctl(net, pn, l4proto);
 459                        goto out;
 460                }
 461        }
 462
 463        pn->users++;
 464out:
 465        return ret;
 466}
 467EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register);
 468
 469static void
 470nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto)
 471{
 472        BUG_ON(l4proto->l3proto >= PF_MAX);
 473
 474        mutex_lock(&nf_ct_proto_mutex);
 475        BUG_ON(rcu_dereference_protected(
 476                        nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 477                        lockdep_is_held(&nf_ct_proto_mutex)
 478                        ) != l4proto);
 479        rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 480                           &nf_conntrack_l4proto_generic);
 481        mutex_unlock(&nf_ct_proto_mutex);
 482
 483        synchronize_rcu();
 484}
 485
 486void nf_conntrack_l4proto_unregister(struct net *net,
 487                                     struct nf_conntrack_l4proto *l4proto)
 488{
 489        struct nf_proto_net *pn = NULL;
 490
 491        if (net == &init_net)
 492                nf_conntrack_l4proto_unregister_net(l4proto);
 493
 494        pn = nf_ct_l4proto_net(net, l4proto);
 495        if (pn == NULL)
 496                return;
 497
 498        pn->users--;
 499        nf_ct_l4proto_unregister_sysctl(net, pn, l4proto);
 500
 501        /* Remove all contrack entries for this protocol */
 502        nf_ct_iterate_cleanup(net, kill_l4proto, l4proto);
 503}
 504EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister);
 505
 506int nf_conntrack_proto_init(struct net *net)
 507{
 508        unsigned int i;
 509        int err;
 510        struct nf_proto_net *pn = nf_ct_l4proto_net(net,
 511                                        &nf_conntrack_l4proto_generic);
 512
 513        err = nf_conntrack_l4proto_generic.init_net(net,
 514                                        nf_conntrack_l4proto_generic.l3proto);
 515        if (err < 0)
 516                return err;
 517        err = nf_ct_l4proto_register_sysctl(net,
 518                                            pn,
 519                                            &nf_conntrack_l4proto_generic);
 520        if (err < 0)
 521                return err;
 522
 523        if (net == &init_net) {
 524                for (i = 0; i < AF_MAX; i++)
 525                        rcu_assign_pointer(nf_ct_l3protos[i],
 526                                           &nf_conntrack_l3proto_generic);
 527        }
 528
 529        pn->users++;
 530        return 0;
 531}
 532
 533void nf_conntrack_proto_fini(struct net *net)
 534{
 535        unsigned int i;
 536        struct nf_proto_net *pn = nf_ct_l4proto_net(net,
 537                                        &nf_conntrack_l4proto_generic);
 538
 539        pn->users--;
 540        nf_ct_l4proto_unregister_sysctl(net,
 541                                        pn,
 542                                        &nf_conntrack_l4proto_generic);
 543        if (net == &init_net) {
 544                /* free l3proto protocol tables */
 545                for (i = 0; i < PF_MAX; i++)
 546                        kfree(nf_ct_protos[i]);
 547        }
 548}
 549
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.