linux/net/netfilter/nf_conntrack_ecache.c
<<
>>
Prefs
   1/* Event cache for netfilter. */
   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/skbuff.h>
  15#include <linux/vmalloc.h>
  16#include <linux/stddef.h>
  17#include <linux/err.h>
  18#include <linux/percpu.h>
  19#include <linux/kernel.h>
  20#include <linux/netdevice.h>
  21#include <linux/slab.h>
  22
  23#include <net/netfilter/nf_conntrack.h>
  24#include <net/netfilter/nf_conntrack_core.h>
  25#include <net/netfilter/nf_conntrack_extend.h>
  26
  27static DEFINE_MUTEX(nf_ct_ecache_mutex);
  28
  29struct nf_ct_event_notifier *nf_conntrack_event_cb __read_mostly;
  30EXPORT_SYMBOL_GPL(nf_conntrack_event_cb);
  31
  32struct nf_exp_event_notifier *nf_expect_event_cb __read_mostly;
  33EXPORT_SYMBOL_GPL(nf_expect_event_cb);
  34
  35/* deliver cached events and clear cache entry - must be called with locally
  36 * disabled softirqs */
  37void nf_ct_deliver_cached_events(struct nf_conn *ct)
  38{
  39        unsigned long events;
  40        struct nf_ct_event_notifier *notify;
  41        struct nf_conntrack_ecache *e;
  42
  43        rcu_read_lock();
  44        notify = rcu_dereference(nf_conntrack_event_cb);
  45        if (notify == NULL)
  46                goto out_unlock;
  47
  48        e = nf_ct_ecache_find(ct);
  49        if (e == NULL)
  50                goto out_unlock;
  51
  52        events = xchg(&e->cache, 0);
  53
  54        if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct) && events) {
  55                struct nf_ct_event item = {
  56                        .ct     = ct,
  57                        .pid    = 0,
  58                        .report = 0
  59                };
  60                int ret;
  61                /* We make a copy of the missed event cache without taking
  62                 * the lock, thus we may send missed events twice. However,
  63                 * this does not harm and it happens very rarely. */
  64                unsigned long missed = e->missed;
  65
  66                ret = notify->fcn(events | missed, &item);
  67                if (unlikely(ret < 0 || missed)) {
  68                        spin_lock_bh(&ct->lock);
  69                        if (ret < 0)
  70                                e->missed |= events;
  71                        else
  72                                e->missed &= ~missed;
  73                        spin_unlock_bh(&ct->lock);
  74                } 
  75        }
  76
  77out_unlock:
  78        rcu_read_unlock();
  79}
  80EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events);
  81
  82int nf_conntrack_register_notifier(struct nf_ct_event_notifier *new)
  83{
  84        int ret = 0;
  85        struct nf_ct_event_notifier *notify;
  86
  87        mutex_lock(&nf_ct_ecache_mutex);
  88        notify = rcu_dereference_protected(nf_conntrack_event_cb,
  89                                           lockdep_is_held(&nf_ct_ecache_mutex));
  90        if (notify != NULL) {
  91                ret = -EBUSY;
  92                goto out_unlock;
  93        }
  94        rcu_assign_pointer(nf_conntrack_event_cb, new);
  95        mutex_unlock(&nf_ct_ecache_mutex);
  96        return ret;
  97
  98out_unlock:
  99        mutex_unlock(&nf_ct_ecache_mutex);
 100        return ret;
 101}
 102EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier);
 103
 104void nf_conntrack_unregister_notifier(struct nf_ct_event_notifier *new)
 105{
 106        struct nf_ct_event_notifier *notify;
 107
 108        mutex_lock(&nf_ct_ecache_mutex);
 109        notify = rcu_dereference_protected(nf_conntrack_event_cb,
 110                                           lockdep_is_held(&nf_ct_ecache_mutex));
 111        BUG_ON(notify != new);
 112        rcu_assign_pointer(nf_conntrack_event_cb, NULL);
 113        mutex_unlock(&nf_ct_ecache_mutex);
 114}
 115EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
 116
 117int nf_ct_expect_register_notifier(struct nf_exp_event_notifier *new)
 118{
 119        int ret = 0;
 120        struct nf_exp_event_notifier *notify;
 121
 122        mutex_lock(&nf_ct_ecache_mutex);
 123        notify = rcu_dereference_protected(nf_expect_event_cb,
 124                                           lockdep_is_held(&nf_ct_ecache_mutex));
 125        if (notify != NULL) {
 126                ret = -EBUSY;
 127                goto out_unlock;
 128        }
 129        rcu_assign_pointer(nf_expect_event_cb, new);
 130        mutex_unlock(&nf_ct_ecache_mutex);
 131        return ret;
 132
 133out_unlock:
 134        mutex_unlock(&nf_ct_ecache_mutex);
 135        return ret;
 136}
 137EXPORT_SYMBOL_GPL(nf_ct_expect_register_notifier);
 138
 139void nf_ct_expect_unregister_notifier(struct nf_exp_event_notifier *new)
 140{
 141        struct nf_exp_event_notifier *notify;
 142
 143        mutex_lock(&nf_ct_ecache_mutex);
 144        notify = rcu_dereference_protected(nf_expect_event_cb,
 145                                           lockdep_is_held(&nf_ct_ecache_mutex));
 146        BUG_ON(notify != new);
 147        rcu_assign_pointer(nf_expect_event_cb, NULL);
 148        mutex_unlock(&nf_ct_ecache_mutex);
 149}
 150EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
 151
 152#define NF_CT_EVENTS_DEFAULT 1
 153static int nf_ct_events __read_mostly = NF_CT_EVENTS_DEFAULT;
 154static int nf_ct_events_retry_timeout __read_mostly = 15*HZ;
 155
 156#ifdef CONFIG_SYSCTL
 157static struct ctl_table event_sysctl_table[] = {
 158        {
 159                .procname       = "nf_conntrack_events",
 160                .data           = &init_net.ct.sysctl_events,
 161                .maxlen         = sizeof(unsigned int),
 162                .mode           = 0644,
 163                .proc_handler   = proc_dointvec,
 164        },
 165        {
 166                .procname       = "nf_conntrack_events_retry_timeout",
 167                .data           = &init_net.ct.sysctl_events_retry_timeout,
 168                .maxlen         = sizeof(unsigned int),
 169                .mode           = 0644,
 170                .proc_handler   = proc_dointvec_jiffies,
 171        },
 172        {}
 173};
 174#endif /* CONFIG_SYSCTL */
 175
 176static struct nf_ct_ext_type event_extend __read_mostly = {
 177        .len    = sizeof(struct nf_conntrack_ecache),
 178        .align  = __alignof__(struct nf_conntrack_ecache),
 179        .id     = NF_CT_EXT_ECACHE,
 180};
 181
 182#ifdef CONFIG_SYSCTL
 183static int nf_conntrack_event_init_sysctl(struct net *net)
 184{
 185        struct ctl_table *table;
 186
 187        table = kmemdup(event_sysctl_table, sizeof(event_sysctl_table),
 188                        GFP_KERNEL);
 189        if (!table)
 190                goto out;
 191
 192        table[0].data = &net->ct.sysctl_events;
 193        table[1].data = &net->ct.sysctl_events_retry_timeout;
 194
 195        net->ct.event_sysctl_header =
 196                register_net_sysctl_table(net,
 197                                          nf_net_netfilter_sysctl_path, table);
 198        if (!net->ct.event_sysctl_header) {
 199                printk(KERN_ERR "nf_ct_event: can't register to sysctl.\n");
 200                goto out_register;
 201        }
 202        return 0;
 203
 204out_register:
 205        kfree(table);
 206out:
 207        return -ENOMEM;
 208}
 209
 210static void nf_conntrack_event_fini_sysctl(struct net *net)
 211{
 212        struct ctl_table *table;
 213
 214        table = net->ct.event_sysctl_header->ctl_table_arg;
 215        unregister_net_sysctl_table(net->ct.event_sysctl_header);
 216        kfree(table);
 217}
 218#else
 219static int nf_conntrack_event_init_sysctl(struct net *net)
 220{
 221        return 0;
 222}
 223
 224static void nf_conntrack_event_fini_sysctl(struct net *net)
 225{
 226}
 227#endif /* CONFIG_SYSCTL */
 228
 229int nf_conntrack_ecache_init(struct net *net)
 230{
 231        int ret;
 232
 233        net->ct.sysctl_events = nf_ct_events;
 234        net->ct.sysctl_events_retry_timeout = nf_ct_events_retry_timeout;
 235
 236        if (net_eq(net, &init_net)) {
 237                ret = nf_ct_extend_register(&event_extend);
 238                if (ret < 0) {
 239                        printk(KERN_ERR "nf_ct_event: Unable to register "
 240                                        "event extension.\n");
 241                        goto out_extend_register;
 242                }
 243        }
 244
 245        ret = nf_conntrack_event_init_sysctl(net);
 246        if (ret < 0)
 247                goto out_sysctl;
 248
 249        return 0;
 250
 251out_sysctl:
 252        if (net_eq(net, &init_net))
 253                nf_ct_extend_unregister(&event_extend);
 254out_extend_register:
 255        return ret;
 256}
 257
 258void nf_conntrack_ecache_fini(struct net *net)
 259{
 260        nf_conntrack_event_fini_sysctl(net);
 261        if (net_eq(net, &init_net))
 262                nf_ct_extend_unregister(&event_extend);
 263}
 264