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 ctl_table_header **header, struct ctl_path *path,
  40                      struct ctl_table *table, unsigned int *users)
  41{
  42        if (*header == NULL) {
  43                *header = register_sysctl_paths(path, table);
  44                if (*header == NULL)
  45                        return -ENOMEM;
  46        }
  47        if (users != NULL)
  48                (*users)++;
  49        return 0;
  50}
  51
  52static void
  53nf_ct_unregister_sysctl(struct ctl_table_header **header,
  54                        struct ctl_table *table, unsigned int *users)
  55{
  56        if (users != NULL && --*users > 0)
  57                return;
  58
  59        unregister_sysctl_table(*header);
  60        *header = NULL;
  61}
  62#endif
  63
  64struct nf_conntrack_l4proto *
  65__nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto)
  66{
  67        if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL))
  68                return &nf_conntrack_l4proto_generic;
  69
  70        return rcu_dereference(nf_ct_protos[l3proto][l4proto]);
  71}
  72EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find);
  73
  74/* this is guaranteed to always return a valid protocol helper, since
  75 * it falls back to generic_protocol */
  76struct nf_conntrack_l3proto *
  77nf_ct_l3proto_find_get(u_int16_t l3proto)
  78{
  79        struct nf_conntrack_l3proto *p;
  80
  81        rcu_read_lock();
  82        p = __nf_ct_l3proto_find(l3proto);
  83        if (!try_module_get(p->me))
  84                p = &nf_conntrack_l3proto_generic;
  85        rcu_read_unlock();
  86
  87        return p;
  88}
  89EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get);
  90
  91void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p)
  92{
  93        module_put(p->me);
  94}
  95EXPORT_SYMBOL_GPL(nf_ct_l3proto_put);
  96
  97int
  98nf_ct_l3proto_try_module_get(unsigned short l3proto)
  99{
 100        int ret;
 101        struct nf_conntrack_l3proto *p;
 102
 103retry:  p = nf_ct_l3proto_find_get(l3proto);
 104        if (p == &nf_conntrack_l3proto_generic) {
 105                ret = request_module("nf_conntrack-%d", l3proto);
 106                if (!ret)
 107                        goto retry;
 108
 109                return -EPROTOTYPE;
 110        }
 111
 112        return 0;
 113}
 114EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get);
 115
 116void nf_ct_l3proto_module_put(unsigned short l3proto)
 117{
 118        struct nf_conntrack_l3proto *p;
 119
 120        /* rcu_read_lock not necessary since the caller holds a reference, but
 121         * taken anyways to avoid lockdep warnings in __nf_ct_l3proto_find()
 122         */
 123        rcu_read_lock();
 124        p = __nf_ct_l3proto_find(l3proto);
 125        module_put(p->me);
 126        rcu_read_unlock();
 127}
 128EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put);
 129
 130static int kill_l3proto(struct nf_conn *i, void *data)
 131{
 132        return nf_ct_l3num(i) == ((struct nf_conntrack_l3proto *)data)->l3proto;
 133}
 134
 135static int kill_l4proto(struct nf_conn *i, void *data)
 136{
 137        struct nf_conntrack_l4proto *l4proto;
 138        l4proto = (struct nf_conntrack_l4proto *)data;
 139        return nf_ct_protonum(i) == l4proto->l4proto &&
 140               nf_ct_l3num(i) == l4proto->l3proto;
 141}
 142
 143static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto)
 144{
 145        int err = 0;
 146
 147#ifdef CONFIG_SYSCTL
 148        if (l3proto->ctl_table != NULL) {
 149                err = nf_ct_register_sysctl(&l3proto->ctl_table_header,
 150                                            l3proto->ctl_table_path,
 151                                            l3proto->ctl_table, NULL);
 152        }
 153#endif
 154        return err;
 155}
 156
 157static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto)
 158{
 159#ifdef CONFIG_SYSCTL
 160        if (l3proto->ctl_table_header != NULL)
 161                nf_ct_unregister_sysctl(&l3proto->ctl_table_header,
 162                                        l3proto->ctl_table, NULL);
 163#endif
 164}
 165
 166int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
 167{
 168        int ret = 0;
 169        struct nf_conntrack_l3proto *old;
 170
 171        if (proto->l3proto >= AF_MAX)
 172                return -EBUSY;
 173
 174        if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size)
 175                return -EINVAL;
 176
 177        mutex_lock(&nf_ct_proto_mutex);
 178        old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],
 179                                        lockdep_is_held(&nf_ct_proto_mutex));
 180        if (old != &nf_conntrack_l3proto_generic) {
 181                ret = -EBUSY;
 182                goto out_unlock;
 183        }
 184
 185        ret = nf_ct_l3proto_register_sysctl(proto);
 186        if (ret < 0)
 187                goto out_unlock;
 188
 189        if (proto->nlattr_tuple_size)
 190                proto->nla_size = 3 * proto->nlattr_tuple_size();
 191
 192        rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
 193
 194out_unlock:
 195        mutex_unlock(&nf_ct_proto_mutex);
 196        return ret;
 197}
 198EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register);
 199
 200void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
 201{
 202        struct net *net;
 203
 204        BUG_ON(proto->l3proto >= AF_MAX);
 205
 206        mutex_lock(&nf_ct_proto_mutex);
 207        BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],
 208                                         lockdep_is_held(&nf_ct_proto_mutex)
 209                                         ) != proto);
 210        rcu_assign_pointer(nf_ct_l3protos[proto->l3proto],
 211                           &nf_conntrack_l3proto_generic);
 212        nf_ct_l3proto_unregister_sysctl(proto);
 213        mutex_unlock(&nf_ct_proto_mutex);
 214
 215        synchronize_rcu();
 216
 217        /* Remove all contrack entries for this protocol */
 218        rtnl_lock();
 219        for_each_net(net)
 220                nf_ct_iterate_cleanup(net, kill_l3proto, proto);
 221        rtnl_unlock();
 222}
 223EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister);
 224
 225static int nf_ct_l4proto_register_sysctl(struct nf_conntrack_l4proto *l4proto)
 226{
 227        int err = 0;
 228
 229#ifdef CONFIG_SYSCTL
 230        if (l4proto->ctl_table != NULL) {
 231                err = nf_ct_register_sysctl(l4proto->ctl_table_header,
 232                                            nf_net_netfilter_sysctl_path,
 233                                            l4proto->ctl_table,
 234                                            l4proto->ctl_table_users);
 235                if (err < 0)
 236                        goto out;
 237        }
 238#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
 239        if (l4proto->ctl_compat_table != NULL) {
 240                err = nf_ct_register_sysctl(&l4proto->ctl_compat_table_header,
 241                                            nf_net_ipv4_netfilter_sysctl_path,
 242                                            l4proto->ctl_compat_table, NULL);
 243                if (err == 0)
 244                        goto out;
 245                nf_ct_unregister_sysctl(l4proto->ctl_table_header,
 246                                        l4proto->ctl_table,
 247                                        l4proto->ctl_table_users);
 248        }
 249#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
 250out:
 251#endif /* CONFIG_SYSCTL */
 252        return err;
 253}
 254
 255static void nf_ct_l4proto_unregister_sysctl(struct nf_conntrack_l4proto *l4proto)
 256{
 257#ifdef CONFIG_SYSCTL
 258        if (l4proto->ctl_table_header != NULL &&
 259            *l4proto->ctl_table_header != NULL)
 260                nf_ct_unregister_sysctl(l4proto->ctl_table_header,
 261                                        l4proto->ctl_table,
 262                                        l4proto->ctl_table_users);
 263#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
 264        if (l4proto->ctl_compat_table_header != NULL)
 265                nf_ct_unregister_sysctl(&l4proto->ctl_compat_table_header,
 266                                        l4proto->ctl_compat_table, NULL);
 267#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
 268#endif /* CONFIG_SYSCTL */
 269}
 270
 271/* FIXME: Allow NULL functions and sub in pointers to generic for
 272   them. --RR */
 273int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto)
 274{
 275        int ret = 0;
 276
 277        if (l4proto->l3proto >= PF_MAX)
 278                return -EBUSY;
 279
 280        if ((l4proto->to_nlattr && !l4proto->nlattr_size)
 281                || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size))
 282                return -EINVAL;
 283
 284        mutex_lock(&nf_ct_proto_mutex);
 285        if (!nf_ct_protos[l4proto->l3proto]) {
 286                /* l3proto may be loaded latter. */
 287                struct nf_conntrack_l4proto __rcu **proto_array;
 288                int i;
 289
 290                proto_array = kmalloc(MAX_NF_CT_PROTO *
 291                                      sizeof(struct nf_conntrack_l4proto *),
 292                                      GFP_KERNEL);
 293                if (proto_array == NULL) {
 294                        ret = -ENOMEM;
 295                        goto out_unlock;
 296                }
 297
 298                for (i = 0; i < MAX_NF_CT_PROTO; i++)
 299                        RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic);
 300
 301                /* Before making proto_array visible to lockless readers,
 302                 * we must make sure its content is committed to memory.
 303                 */
 304                smp_wmb();
 305
 306                nf_ct_protos[l4proto->l3proto] = proto_array;
 307        } else if (rcu_dereference_protected(
 308                        nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 309                        lockdep_is_held(&nf_ct_proto_mutex)
 310                        ) != &nf_conntrack_l4proto_generic) {
 311                ret = -EBUSY;
 312                goto out_unlock;
 313        }
 314
 315        ret = nf_ct_l4proto_register_sysctl(l4proto);
 316        if (ret < 0)
 317                goto out_unlock;
 318
 319        l4proto->nla_size = 0;
 320        if (l4proto->nlattr_size)
 321                l4proto->nla_size += l4proto->nlattr_size();
 322        if (l4proto->nlattr_tuple_size)
 323                l4proto->nla_size += 3 * l4proto->nlattr_tuple_size();
 324
 325        rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 326                           l4proto);
 327
 328out_unlock:
 329        mutex_unlock(&nf_ct_proto_mutex);
 330        return ret;
 331}
 332EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register);
 333
 334void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto)
 335{
 336        struct net *net;
 337
 338        BUG_ON(l4proto->l3proto >= PF_MAX);
 339
 340        mutex_lock(&nf_ct_proto_mutex);
 341        BUG_ON(rcu_dereference_protected(
 342                        nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 343                        lockdep_is_held(&nf_ct_proto_mutex)
 344                        ) != l4proto);
 345        rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 346                           &nf_conntrack_l4proto_generic);
 347        nf_ct_l4proto_unregister_sysctl(l4proto);
 348        mutex_unlock(&nf_ct_proto_mutex);
 349
 350        synchronize_rcu();
 351
 352        /* Remove all contrack entries for this protocol */
 353        rtnl_lock();
 354        for_each_net(net)
 355                nf_ct_iterate_cleanup(net, kill_l4proto, l4proto);
 356        rtnl_unlock();
 357}
 358EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister);
 359
 360int nf_conntrack_proto_init(void)
 361{
 362        unsigned int i;
 363        int err;
 364
 365        err = nf_ct_l4proto_register_sysctl(&nf_conntrack_l4proto_generic);
 366        if (err < 0)
 367                return err;
 368
 369        for (i = 0; i < AF_MAX; i++)
 370                rcu_assign_pointer(nf_ct_l3protos[i],
 371                                   &nf_conntrack_l3proto_generic);
 372        return 0;
 373}
 374
 375void nf_conntrack_proto_fini(void)
 376{
 377        unsigned int i;
 378
 379        nf_ct_l4proto_unregister_sysctl(&nf_conntrack_l4proto_generic);
 380
 381        /* free l3proto protocol tables */
 382        for (i = 0; i < PF_MAX; i++)
 383                kfree(nf_ct_protos[i]);
 384}
 385