blob: 1df176146567aba5fbf922794022e04c0f93a83f [file] [log] [blame]
Martin Josefssonf6180122006-11-29 02:35:01 +01001/* Event cache for netfilter. */
2
Patrick McHardyf229f6c2013-04-06 15:24:29 +02003/*
4 * (C) 2005 Harald Welte <laforge@gnumonks.org>
5 * (C) 2005 Patrick McHardy <kaber@trash.net>
6 * (C) 2005-2006 Netfilter Core Team <coreteam@netfilter.org>
7 * (C) 2005 USAGI/WIDE Project <http://www.linux-ipv6.org>
Martin Josefssonf6180122006-11-29 02:35:01 +01008 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/types.h>
15#include <linux/netfilter.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>
Martin Josefssonf6180122006-11-29 02:35:01 +010021#include <linux/kernel.h>
22#include <linux/netdevice.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090023#include <linux/slab.h>
Paul Gortmakerbc3b2d72011-07-15 11:47:34 -040024#include <linux/export.h>
Martin Josefssonf6180122006-11-29 02:35:01 +010025
26#include <net/netfilter/nf_conntrack.h>
Martin Josefssonf6180122006-11-29 02:35:01 +010027#include <net/netfilter/nf_conntrack_core.h>
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +020028#include <net/netfilter/nf_conntrack_extend.h>
Martin Josefssonf6180122006-11-29 02:35:01 +010029
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +020030static DEFINE_MUTEX(nf_ct_ecache_mutex);
Patrick McHardy13b18332006-12-02 22:11:25 -080031
Martin Josefssonf6180122006-11-29 02:35:01 +010032/* deliver cached events and clear cache entry - must be called with locally
33 * disabled softirqs */
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +020034void nf_ct_deliver_cached_events(struct nf_conn *ct)
Martin Josefssonf6180122006-11-29 02:35:01 +010035{
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +010036 struct net *net = nf_ct_net(ct);
Tony Zelenoff58020f72012-02-22 10:48:01 +040037 unsigned long events, missed;
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +020038 struct nf_ct_event_notifier *notify;
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +020039 struct nf_conntrack_ecache *e;
Tony Zelenoff58020f72012-02-22 10:48:01 +040040 struct nf_ct_event item;
41 int ret;
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +020042
43 rcu_read_lock();
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +010044 notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +020045 if (notify == NULL)
46 goto out_unlock;
47
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +020048 e = nf_ct_ecache_find(ct);
49 if (e == NULL)
50 goto out_unlock;
51
52 events = xchg(&e->cache, 0);
53
Tony Zelenoff58020f72012-02-22 10:48:01 +040054 if (!nf_ct_is_confirmed(ct) || nf_ct_is_dying(ct) || !events)
55 goto out_unlock;
Pablo Neira Ayuso19abb7b2008-11-18 11:56:20 +010056
Tony Zelenoff58020f72012-02-22 10:48:01 +040057 /* We make a copy of the missed event cache without taking
58 * the lock, thus we may send missed events twice. However,
59 * this does not harm and it happens very rarely. */
60 missed = e->missed;
Pablo Neira Ayuso3db7e932011-02-01 16:06:30 +010061
Tony Zelenoff58020f72012-02-22 10:48:01 +040062 if (!((events | missed) & e->ctmask))
63 goto out_unlock;
64
65 item.ct = ct;
Eric W. Biederman15e47302012-09-07 20:12:54 +000066 item.portid = 0;
Tony Zelenoff58020f72012-02-22 10:48:01 +040067 item.report = 0;
68
69 ret = notify->fcn(events | missed, &item);
70
71 if (likely(ret >= 0 && !missed))
72 goto out_unlock;
73
74 spin_lock_bh(&ct->lock);
75 if (ret < 0)
76 e->missed |= events;
77 else
78 e->missed &= ~missed;
79 spin_unlock_bh(&ct->lock);
Martin Josefssonf6180122006-11-29 02:35:01 +010080
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +020081out_unlock:
82 rcu_read_unlock();
Martin Josefssonf6180122006-11-29 02:35:01 +010083}
Patrick McHardy13b18332006-12-02 22:11:25 -080084EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events);
Martin Josefssonf6180122006-11-29 02:35:01 +010085
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +010086int nf_conntrack_register_notifier(struct net *net,
87 struct nf_ct_event_notifier *new)
Patrick McHardy010c7d62007-03-14 16:40:10 -070088{
Tony Zelenoff031d7702012-03-08 23:35:39 +000089 int ret;
Patrick McHardyb56f2d52010-05-10 18:47:57 +020090 struct nf_ct_event_notifier *notify;
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +020091
92 mutex_lock(&nf_ct_ecache_mutex);
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +010093 notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb,
Patrick McHardyb56f2d52010-05-10 18:47:57 +020094 lockdep_is_held(&nf_ct_ecache_mutex));
95 if (notify != NULL) {
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +020096 ret = -EBUSY;
97 goto out_unlock;
98 }
Eric Dumazetcf778b02012-01-12 04:41:32 +000099 rcu_assign_pointer(net->ct.nf_conntrack_event_cb, new);
Tony Zelenoff031d7702012-03-08 23:35:39 +0000100 ret = 0;
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +0200101
102out_unlock:
103 mutex_unlock(&nf_ct_ecache_mutex);
104 return ret;
Patrick McHardy010c7d62007-03-14 16:40:10 -0700105}
106EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier);
107
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +0100108void nf_conntrack_unregister_notifier(struct net *net,
109 struct nf_ct_event_notifier *new)
Patrick McHardy010c7d62007-03-14 16:40:10 -0700110{
Patrick McHardyb56f2d52010-05-10 18:47:57 +0200111 struct nf_ct_event_notifier *notify;
112
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +0200113 mutex_lock(&nf_ct_ecache_mutex);
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +0100114 notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb,
Patrick McHardyb56f2d52010-05-10 18:47:57 +0200115 lockdep_is_held(&nf_ct_ecache_mutex));
116 BUG_ON(notify != new);
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +0100117 RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, NULL);
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +0200118 mutex_unlock(&nf_ct_ecache_mutex);
Patrick McHardy010c7d62007-03-14 16:40:10 -0700119}
120EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
121
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +0100122int nf_ct_expect_register_notifier(struct net *net,
123 struct nf_exp_event_notifier *new)
Patrick McHardy010c7d62007-03-14 16:40:10 -0700124{
Tony Zelenoff031d7702012-03-08 23:35:39 +0000125 int ret;
Patrick McHardyb56f2d52010-05-10 18:47:57 +0200126 struct nf_exp_event_notifier *notify;
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +0200127
128 mutex_lock(&nf_ct_ecache_mutex);
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +0100129 notify = rcu_dereference_protected(net->ct.nf_expect_event_cb,
Patrick McHardyb56f2d52010-05-10 18:47:57 +0200130 lockdep_is_held(&nf_ct_ecache_mutex));
131 if (notify != NULL) {
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +0200132 ret = -EBUSY;
133 goto out_unlock;
134 }
Eric Dumazetcf778b02012-01-12 04:41:32 +0000135 rcu_assign_pointer(net->ct.nf_expect_event_cb, new);
Tony Zelenoff031d7702012-03-08 23:35:39 +0000136 ret = 0;
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +0200137
138out_unlock:
139 mutex_unlock(&nf_ct_ecache_mutex);
140 return ret;
Patrick McHardy010c7d62007-03-14 16:40:10 -0700141}
Patrick McHardy68236452007-07-07 22:30:49 -0700142EXPORT_SYMBOL_GPL(nf_ct_expect_register_notifier);
Patrick McHardy010c7d62007-03-14 16:40:10 -0700143
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +0100144void nf_ct_expect_unregister_notifier(struct net *net,
145 struct nf_exp_event_notifier *new)
Patrick McHardy010c7d62007-03-14 16:40:10 -0700146{
Patrick McHardyb56f2d52010-05-10 18:47:57 +0200147 struct nf_exp_event_notifier *notify;
148
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +0200149 mutex_lock(&nf_ct_ecache_mutex);
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +0100150 notify = rcu_dereference_protected(net->ct.nf_expect_event_cb,
Patrick McHardyb56f2d52010-05-10 18:47:57 +0200151 lockdep_is_held(&nf_ct_ecache_mutex));
152 BUG_ON(notify != new);
Pablo Neira Ayuso70e99422011-11-22 00:16:51 +0100153 RCU_INIT_POINTER(net->ct.nf_expect_event_cb, NULL);
Pablo Neira Ayusoe34d5c12009-06-03 10:32:06 +0200154 mutex_unlock(&nf_ct_ecache_mutex);
Patrick McHardy010c7d62007-03-14 16:40:10 -0700155}
Patrick McHardy68236452007-07-07 22:30:49 -0700156EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200157
158#define NF_CT_EVENTS_DEFAULT 1
159static int nf_ct_events __read_mostly = NF_CT_EVENTS_DEFAULT;
Pablo Neira Ayusodd7669a2009-06-13 12:30:52 +0200160static int nf_ct_events_retry_timeout __read_mostly = 15*HZ;
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200161
162#ifdef CONFIG_SYSCTL
163static struct ctl_table event_sysctl_table[] = {
164 {
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200165 .procname = "nf_conntrack_events",
166 .data = &init_net.ct.sysctl_events,
167 .maxlen = sizeof(unsigned int),
168 .mode = 0644,
169 .proc_handler = proc_dointvec,
170 },
Pablo Neira Ayusodd7669a2009-06-13 12:30:52 +0200171 {
Pablo Neira Ayusodd7669a2009-06-13 12:30:52 +0200172 .procname = "nf_conntrack_events_retry_timeout",
173 .data = &init_net.ct.sysctl_events_retry_timeout,
174 .maxlen = sizeof(unsigned int),
175 .mode = 0644,
176 .proc_handler = proc_dointvec_jiffies,
177 },
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200178 {}
179};
180#endif /* CONFIG_SYSCTL */
181
182static struct nf_ct_ext_type event_extend __read_mostly = {
183 .len = sizeof(struct nf_conntrack_ecache),
184 .align = __alignof__(struct nf_conntrack_ecache),
185 .id = NF_CT_EXT_ECACHE,
186};
187
188#ifdef CONFIG_SYSCTL
189static int nf_conntrack_event_init_sysctl(struct net *net)
190{
191 struct ctl_table *table;
192
193 table = kmemdup(event_sysctl_table, sizeof(event_sysctl_table),
194 GFP_KERNEL);
195 if (!table)
196 goto out;
197
198 table[0].data = &net->ct.sysctl_events;
Pablo Neira Ayusodd7669a2009-06-13 12:30:52 +0200199 table[1].data = &net->ct.sysctl_events_retry_timeout;
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200200
Eric W. Biederman464dc802012-11-16 03:02:59 +0000201 /* Don't export sysctls to unprivileged users */
202 if (net->user_ns != &init_user_ns)
203 table[0].procname = NULL;
204
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200205 net->ct.event_sysctl_header =
Eric W. Biedermanec8f23c2012-04-19 13:44:49 +0000206 register_net_sysctl(net, "net/netfilter", table);
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200207 if (!net->ct.event_sysctl_header) {
208 printk(KERN_ERR "nf_ct_event: can't register to sysctl.\n");
209 goto out_register;
210 }
211 return 0;
212
213out_register:
214 kfree(table);
215out:
216 return -ENOMEM;
217}
218
219static void nf_conntrack_event_fini_sysctl(struct net *net)
220{
221 struct ctl_table *table;
222
223 table = net->ct.event_sysctl_header->ctl_table_arg;
224 unregister_net_sysctl_table(net->ct.event_sysctl_header);
225 kfree(table);
226}
227#else
228static int nf_conntrack_event_init_sysctl(struct net *net)
229{
230 return 0;
231}
232
233static void nf_conntrack_event_fini_sysctl(struct net *net)
234{
235}
236#endif /* CONFIG_SYSCTL */
237
Gao feng3fe0f942013-01-21 22:10:28 +0000238int nf_conntrack_ecache_pernet_init(struct net *net)
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200239{
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200240 net->ct.sysctl_events = nf_ct_events;
Pablo Neira Ayusodd7669a2009-06-13 12:30:52 +0200241 net->ct.sysctl_events_retry_timeout = nf_ct_events_retry_timeout;
Gao feng3fe0f942013-01-21 22:10:28 +0000242 return nf_conntrack_event_init_sysctl(net);
243}
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200244
Gao feng3fe0f942013-01-21 22:10:28 +0000245void nf_conntrack_ecache_pernet_fini(struct net *net)
246{
247 nf_conntrack_event_fini_sysctl(net);
248}
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200249
Gao feng3fe0f942013-01-21 22:10:28 +0000250int nf_conntrack_ecache_init(void)
251{
252 int ret = nf_ct_extend_register(&event_extend);
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200253 if (ret < 0)
Gao feng3fe0f942013-01-21 22:10:28 +0000254 pr_err("nf_ct_event: Unable to register event extension.\n");
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200255 return ret;
256}
257
Gao feng3fe0f942013-01-21 22:10:28 +0000258void nf_conntrack_ecache_fini(void)
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200259{
Gao feng3fe0f942013-01-21 22:10:28 +0000260 nf_ct_extend_unregister(&event_extend);
Pablo Neira Ayusoa0891aa2009-06-13 12:26:29 +0200261}