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