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 * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/types.h>
  14#include <linux/netfilter.h>
  15#include <linux/module.h>
  16#include <linux/slab.h>
  17#include <linux/mutex.h>
  18#include <linux/vmalloc.h>
  19#include <linux/stddef.h>
  20#include <linux/err.h>
  21#include <linux/percpu.h>
  22#include <linux/notifier.h>
  23#include <linux/kernel.h>
  24#include <linux/netdevice.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
 216int nf_ct_l3proto_register(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}
 245EXPORT_SYMBOL_GPL(nf_ct_l3proto_register);
 246
 247int nf_ct_l3proto_pernet_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        return nf_ct_l3proto_register_sysctl(net, proto);
 259}
 260EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_register);
 261
 262void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto)
 263{
 264        BUG_ON(proto->l3proto >= AF_MAX);
 265
 266        mutex_lock(&nf_ct_proto_mutex);
 267        BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],
 268                                         lockdep_is_held(&nf_ct_proto_mutex)
 269                                         ) != proto);
 270        rcu_assign_pointer(nf_ct_l3protos[proto->l3proto],
 271                           &nf_conntrack_l3proto_generic);
 272        mutex_unlock(&nf_ct_proto_mutex);
 273
 274        synchronize_rcu();
 275}
 276EXPORT_SYMBOL_GPL(nf_ct_l3proto_unregister);
 277
 278void nf_ct_l3proto_pernet_unregister(struct net *net,
 279                                     struct nf_conntrack_l3proto *proto)
 280{
 281        nf_ct_l3proto_unregister_sysctl(net, proto);
 282
 283        /* Remove all contrack entries for this protocol */
 284        nf_ct_iterate_cleanup(net, kill_l3proto, proto);
 285}
 286EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_unregister);
 287
 288static struct nf_proto_net *nf_ct_l4proto_net(struct net *net,
 289                                              struct nf_conntrack_l4proto *l4proto)
 290{
 291        if (l4proto->get_net_proto) {
 292                /* statically built-in protocols use static per-net */
 293                return l4proto->get_net_proto(net);
 294        } else if (l4proto->net_id) {
 295                /* ... and loadable protocols use dynamic per-net */
 296                return net_generic(net, *l4proto->net_id);
 297        }
 298        return NULL;
 299}
 300
 301static
 302int nf_ct_l4proto_register_sysctl(struct net *net,
 303                                  struct nf_proto_net *pn,
 304                                  struct nf_conntrack_l4proto *l4proto)
 305{
 306        int err = 0;
 307
 308#ifdef CONFIG_SYSCTL
 309        if (pn->ctl_table != NULL) {
 310                err = nf_ct_register_sysctl(net,
 311                                            &pn->ctl_table_header,
 312                                            "net/netfilter",
 313                                            pn->ctl_table);
 314                if (err < 0) {
 315                        if (!pn->users) {
 316                                kfree(pn->ctl_table);
 317                                pn->ctl_table = NULL;
 318                        }
 319                }
 320        }
 321#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
 322        if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_table != NULL) {
 323                if (err < 0) {
 324                        nf_ct_kfree_compat_sysctl_table(pn);
 325                        goto out;
 326                }
 327                err = nf_ct_register_sysctl(net,
 328                                            &pn->ctl_compat_header,
 329                                            "net/ipv4/netfilter",
 330                                            pn->ctl_compat_table);
 331                if (err == 0)
 332                        goto out;
 333
 334                nf_ct_kfree_compat_sysctl_table(pn);
 335                nf_ct_unregister_sysctl(&pn->ctl_table_header,
 336                                        &pn->ctl_table,
 337                                        pn->users);
 338        }
 339out:
 340#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
 341#endif /* CONFIG_SYSCTL */
 342        return err;
 343}
 344
 345static
 346void nf_ct_l4proto_unregister_sysctl(struct net *net,
 347                                     struct nf_proto_net *pn,
 348                                     struct nf_conntrack_l4proto *l4proto)
 349{
 350#ifdef CONFIG_SYSCTL
 351        if (pn->ctl_table_header != NULL)
 352                nf_ct_unregister_sysctl(&pn->ctl_table_header,
 353                                        &pn->ctl_table,
 354                                        pn->users);
 355
 356#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
 357        if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_header != NULL)
 358                nf_ct_unregister_sysctl(&pn->ctl_compat_header,
 359                                        &pn->ctl_compat_table,
 360                                        0);
 361#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
 362#endif /* CONFIG_SYSCTL */
 363}
 364
 365/* FIXME: Allow NULL functions and sub in pointers to generic for
 366   them. --RR */
 367int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto)
 368{
 369        int ret = 0;
 370
 371        if (l4proto->l3proto >= PF_MAX)
 372                return -EBUSY;
 373
 374        if ((l4proto->to_nlattr && !l4proto->nlattr_size)
 375                || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size))
 376                return -EINVAL;
 377
 378        mutex_lock(&nf_ct_proto_mutex);
 379        if (!nf_ct_protos[l4proto->l3proto]) {
 380                /* l3proto may be loaded latter. */
 381                struct nf_conntrack_l4proto __rcu **proto_array;
 382                int i;
 383
 384                proto_array = kmalloc(MAX_NF_CT_PROTO *
 385                                      sizeof(struct nf_conntrack_l4proto *),
 386                                      GFP_KERNEL);
 387                if (proto_array == NULL) {
 388                        ret = -ENOMEM;
 389                        goto out_unlock;
 390                }
 391
 392                for (i = 0; i < MAX_NF_CT_PROTO; i++)
 393                        RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic);
 394
 395                /* Before making proto_array visible to lockless readers,
 396                 * we must make sure its content is committed to memory.
 397                 */
 398                smp_wmb();
 399
 400                nf_ct_protos[l4proto->l3proto] = proto_array;
 401        } else if (rcu_dereference_protected(
 402                        nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 403                        lockdep_is_held(&nf_ct_proto_mutex)
 404                        ) != &nf_conntrack_l4proto_generic) {
 405                ret = -EBUSY;
 406                goto out_unlock;
 407        }
 408
 409        l4proto->nla_size = 0;
 410        if (l4proto->nlattr_size)
 411                l4proto->nla_size += l4proto->nlattr_size();
 412        if (l4proto->nlattr_tuple_size)
 413                l4proto->nla_size += 3 * l4proto->nlattr_tuple_size();
 414
 415        rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 416                           l4proto);
 417out_unlock:
 418        mutex_unlock(&nf_ct_proto_mutex);
 419        return ret;
 420}
 421EXPORT_SYMBOL_GPL(nf_ct_l4proto_register);
 422
 423int nf_ct_l4proto_pernet_register(struct net *net,
 424                                  struct nf_conntrack_l4proto *l4proto)
 425{
 426        int ret = 0;
 427        struct nf_proto_net *pn = NULL;
 428
 429        if (l4proto->init_net) {
 430                ret = l4proto->init_net(net, l4proto->l3proto);
 431                if (ret < 0)
 432                        goto out;
 433        }
 434
 435        pn = nf_ct_l4proto_net(net, l4proto);
 436        if (pn == NULL)
 437                goto out;
 438
 439        ret = nf_ct_l4proto_register_sysctl(net, pn, l4proto);
 440        if (ret < 0)
 441                goto out;
 442
 443        pn->users++;
 444out:
 445        return ret;
 446}
 447EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register);
 448
 449void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *l4proto)
 450{
 451        BUG_ON(l4proto->l3proto >= PF_MAX);
 452
 453        mutex_lock(&nf_ct_proto_mutex);
 454        BUG_ON(rcu_dereference_protected(
 455                        nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 456                        lockdep_is_held(&nf_ct_proto_mutex)
 457                        ) != l4proto);
 458        rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
 459                           &nf_conntrack_l4proto_generic);
 460        mutex_unlock(&nf_ct_proto_mutex);
 461
 462        synchronize_rcu();
 463}
 464EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister);
 465
 466void nf_ct_l4proto_pernet_unregister(struct net *net,
 467                                     struct nf_conntrack_l4proto *l4proto)
 468{
 469        struct nf_proto_net *pn = NULL;
 470
 471        pn = nf_ct_l4proto_net(net, l4proto);
 472        if (pn == NULL)
 473                return;
 474
 475        pn->users--;
 476        nf_ct_l4proto_unregister_sysctl(net, pn, l4proto);
 477
 478        /* Remove all contrack entries for this protocol */
 479        nf_ct_iterate_cleanup(net, kill_l4proto, l4proto);
 480}
 481EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_unregister);
 482
 483int nf_conntrack_proto_pernet_init(struct net *net)
 484{
 485        int err;
 486        struct nf_proto_net *pn = nf_ct_l4proto_net(net,
 487                                        &nf_conntrack_l4proto_generic);
 488
 489        err = nf_conntrack_l4proto_generic.init_net(net,
 490                                        nf_conntrack_l4proto_generic.l3proto);
 491        if (err < 0)
 492                return err;
 493        err = nf_ct_l4proto_register_sysctl(net,
 494                                            pn,
 495                                            &nf_conntrack_l4proto_generic);
 496        if (err < 0)
 497                return err;
 498
 499        pn->users++;
 500        return 0;
 501}
 502
 503void nf_conntrack_proto_pernet_fini(struct net *net)
 504{
 505        struct nf_proto_net *pn = nf_ct_l4proto_net(net,
 506                                        &nf_conntrack_l4proto_generic);
 507
 508        pn->users--;
 509        nf_ct_l4proto_unregister_sysctl(net,
 510                                        pn,
 511                                        &nf_conntrack_l4proto_generic);
 512}
 513
 514int nf_conntrack_proto_init(void)
 515{
 516        unsigned int i;
 517        for (i = 0; i < AF_MAX; i++)
 518                rcu_assign_pointer(nf_ct_l3protos[i],
 519                                   &nf_conntrack_l3proto_generic);
 520        return 0;
 521}
 522
 523void nf_conntrack_proto_fini(void)
 524{
 525        unsigned int i;
 526        /* free l3proto protocol tables */
 527        for (i = 0; i < PF_MAX; i++)
 528                kfree(nf_ct_protos[i]);
 529}
 530
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.