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