Blame view

net/netfilter/nf_conntrack_ecache.c 6.49 KB
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  /* Event cache for netfilter. */
  
  /* (C) 1999-2001 Paul `Rusty' Russell
   * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
   * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
  
  #include <linux/types.h>
  #include <linux/netfilter.h>
  #include <linux/skbuff.h>
  #include <linux/vmalloc.h>
  #include <linux/stddef.h>
  #include <linux/err.h>
  #include <linux/percpu.h>
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
19
20
  #include <linux/kernel.h>
  #include <linux/netdevice.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
21
  #include <linux/slab.h>
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
22
23
  
  #include <net/netfilter/nf_conntrack.h>
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
24
  #include <net/netfilter/nf_conntrack_core.h>
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
25
  #include <net/netfilter/nf_conntrack_extend.h>
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
26

e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
27
  static DEFINE_MUTEX(nf_ct_ecache_mutex);
13b183391   Patrick McHardy   [NETFILTER]: nf_c...
28

e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
29
30
31
32
33
  struct nf_ct_event_notifier *nf_conntrack_event_cb __read_mostly;
  EXPORT_SYMBOL_GPL(nf_conntrack_event_cb);
  
  struct nf_exp_event_notifier *nf_expect_event_cb __read_mostly;
  EXPORT_SYMBOL_GPL(nf_expect_event_cb);
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
34

f61801218   Martin Josefsson   [NETFILTER]: nf_c...
35
36
  /* deliver cached events and clear cache entry - must be called with locally
   * disabled softirqs */
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
37
  void nf_ct_deliver_cached_events(struct nf_conn *ct)
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
38
  {
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
39
  	unsigned long events;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
40
  	struct nf_ct_event_notifier *notify;
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
41
  	struct nf_conntrack_ecache *e;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
42
43
44
45
46
  
  	rcu_read_lock();
  	notify = rcu_dereference(nf_conntrack_event_cb);
  	if (notify == NULL)
  		goto out_unlock;
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
47
48
49
50
51
52
53
  	e = nf_ct_ecache_find(ct);
  	if (e == NULL)
  		goto out_unlock;
  
  	events = xchg(&e->cache, 0);
  
  	if (nf_ct_is_confirmed(ct) && !nf_ct_is_dying(ct) && events) {
19abb7b09   Pablo Neira Ayuso   netfilter: ctnetl...
54
  		struct nf_ct_event item = {
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
55
  			.ct	= ct,
19abb7b09   Pablo Neira Ayuso   netfilter: ctnetl...
56
57
58
  			.pid	= 0,
  			.report	= 0
  		};
dd7669a92   Pablo Neira Ayuso   netfilter: conntr...
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  		int ret;
  		/* We make a copy of the missed event cache without taking
  		 * the lock, thus we may send missed events twice. However,
  		 * this does not harm and it happens very rarely. */
  		unsigned long missed = e->missed;
  
  		ret = notify->fcn(events | missed, &item);
  		if (unlikely(ret < 0 || missed)) {
  			spin_lock_bh(&ct->lock);
  			if (ret < 0)
  				e->missed |= events;
  			else
  				e->missed &= ~missed;
  			spin_unlock_bh(&ct->lock);
  		} 
19abb7b09   Pablo Neira Ayuso   netfilter: ctnetl...
74
  	}
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
75

e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
76
77
  out_unlock:
  	rcu_read_unlock();
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
78
  }
13b183391   Patrick McHardy   [NETFILTER]: nf_c...
79
  EXPORT_SYMBOL_GPL(nf_ct_deliver_cached_events);
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
80

e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
81
  int nf_conntrack_register_notifier(struct nf_ct_event_notifier *new)
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
82
  {
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
83
  	int ret = 0;
b56f2d55c   Patrick McHardy   netfilter: use rc...
84
  	struct nf_ct_event_notifier *notify;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
85
86
  
  	mutex_lock(&nf_ct_ecache_mutex);
b56f2d55c   Patrick McHardy   netfilter: use rc...
87
88
89
  	notify = rcu_dereference_protected(nf_conntrack_event_cb,
  					   lockdep_is_held(&nf_ct_ecache_mutex));
  	if (notify != NULL) {
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
90
91
92
93
94
95
96
97
98
99
  		ret = -EBUSY;
  		goto out_unlock;
  	}
  	rcu_assign_pointer(nf_conntrack_event_cb, new);
  	mutex_unlock(&nf_ct_ecache_mutex);
  	return ret;
  
  out_unlock:
  	mutex_unlock(&nf_ct_ecache_mutex);
  	return ret;
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
100
101
  }
  EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier);
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
102
  void nf_conntrack_unregister_notifier(struct nf_ct_event_notifier *new)
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
103
  {
b56f2d55c   Patrick McHardy   netfilter: use rc...
104
  	struct nf_ct_event_notifier *notify;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
105
  	mutex_lock(&nf_ct_ecache_mutex);
b56f2d55c   Patrick McHardy   netfilter: use rc...
106
107
108
  	notify = rcu_dereference_protected(nf_conntrack_event_cb,
  					   lockdep_is_held(&nf_ct_ecache_mutex));
  	BUG_ON(notify != new);
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
109
110
  	rcu_assign_pointer(nf_conntrack_event_cb, NULL);
  	mutex_unlock(&nf_ct_ecache_mutex);
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
111
112
  }
  EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
113
  int nf_ct_expect_register_notifier(struct nf_exp_event_notifier *new)
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
114
  {
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
115
  	int ret = 0;
b56f2d55c   Patrick McHardy   netfilter: use rc...
116
  	struct nf_exp_event_notifier *notify;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
117
118
  
  	mutex_lock(&nf_ct_ecache_mutex);
b56f2d55c   Patrick McHardy   netfilter: use rc...
119
120
121
  	notify = rcu_dereference_protected(nf_expect_event_cb,
  					   lockdep_is_held(&nf_ct_ecache_mutex));
  	if (notify != NULL) {
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
122
123
124
125
126
127
128
129
130
131
  		ret = -EBUSY;
  		goto out_unlock;
  	}
  	rcu_assign_pointer(nf_expect_event_cb, new);
  	mutex_unlock(&nf_ct_ecache_mutex);
  	return ret;
  
  out_unlock:
  	mutex_unlock(&nf_ct_ecache_mutex);
  	return ret;
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
132
  }
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
133
  EXPORT_SYMBOL_GPL(nf_ct_expect_register_notifier);
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
134

e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
135
  void nf_ct_expect_unregister_notifier(struct nf_exp_event_notifier *new)
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
136
  {
b56f2d55c   Patrick McHardy   netfilter: use rc...
137
  	struct nf_exp_event_notifier *notify;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
138
  	mutex_lock(&nf_ct_ecache_mutex);
b56f2d55c   Patrick McHardy   netfilter: use rc...
139
140
141
  	notify = rcu_dereference_protected(nf_expect_event_cb,
  					   lockdep_is_held(&nf_ct_ecache_mutex));
  	BUG_ON(notify != new);
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
142
143
  	rcu_assign_pointer(nf_expect_event_cb, NULL);
  	mutex_unlock(&nf_ct_ecache_mutex);
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
144
  }
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
145
  EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
146
147
148
  
  #define NF_CT_EVENTS_DEFAULT 1
  static int nf_ct_events __read_mostly = NF_CT_EVENTS_DEFAULT;
dd7669a92   Pablo Neira Ayuso   netfilter: conntr...
149
  static int nf_ct_events_retry_timeout __read_mostly = 15*HZ;
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
150
151
152
153
  
  #ifdef CONFIG_SYSCTL
  static struct ctl_table event_sysctl_table[] = {
  	{
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
154
155
156
157
158
159
  		.procname	= "nf_conntrack_events",
  		.data		= &init_net.ct.sysctl_events,
  		.maxlen		= sizeof(unsigned int),
  		.mode		= 0644,
  		.proc_handler	= proc_dointvec,
  	},
dd7669a92   Pablo Neira Ayuso   netfilter: conntr...
160
  	{
dd7669a92   Pablo Neira Ayuso   netfilter: conntr...
161
162
163
164
165
166
  		.procname	= "nf_conntrack_events_retry_timeout",
  		.data		= &init_net.ct.sysctl_events_retry_timeout,
  		.maxlen		= sizeof(unsigned int),
  		.mode		= 0644,
  		.proc_handler	= proc_dointvec_jiffies,
  	},
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  	{}
  };
  #endif /* CONFIG_SYSCTL */
  
  static struct nf_ct_ext_type event_extend __read_mostly = {
  	.len	= sizeof(struct nf_conntrack_ecache),
  	.align	= __alignof__(struct nf_conntrack_ecache),
  	.id	= NF_CT_EXT_ECACHE,
  };
  
  #ifdef CONFIG_SYSCTL
  static int nf_conntrack_event_init_sysctl(struct net *net)
  {
  	struct ctl_table *table;
  
  	table = kmemdup(event_sysctl_table, sizeof(event_sysctl_table),
  			GFP_KERNEL);
  	if (!table)
  		goto out;
  
  	table[0].data = &net->ct.sysctl_events;
dd7669a92   Pablo Neira Ayuso   netfilter: conntr...
188
  	table[1].data = &net->ct.sysctl_events_retry_timeout;
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
  
  	net->ct.event_sysctl_header =
  		register_net_sysctl_table(net,
  					  nf_net_netfilter_sysctl_path, table);
  	if (!net->ct.event_sysctl_header) {
  		printk(KERN_ERR "nf_ct_event: can't register to sysctl.
  ");
  		goto out_register;
  	}
  	return 0;
  
  out_register:
  	kfree(table);
  out:
  	return -ENOMEM;
  }
  
  static void nf_conntrack_event_fini_sysctl(struct net *net)
  {
  	struct ctl_table *table;
  
  	table = net->ct.event_sysctl_header->ctl_table_arg;
  	unregister_net_sysctl_table(net->ct.event_sysctl_header);
  	kfree(table);
  }
  #else
  static int nf_conntrack_event_init_sysctl(struct net *net)
  {
  	return 0;
  }
  
  static void nf_conntrack_event_fini_sysctl(struct net *net)
  {
  }
  #endif /* CONFIG_SYSCTL */
  
  int nf_conntrack_ecache_init(struct net *net)
  {
  	int ret;
  
  	net->ct.sysctl_events = nf_ct_events;
dd7669a92   Pablo Neira Ayuso   netfilter: conntr...
230
  	net->ct.sysctl_events_retry_timeout = nf_ct_events_retry_timeout;
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  
  	if (net_eq(net, &init_net)) {
  		ret = nf_ct_extend_register(&event_extend);
  		if (ret < 0) {
  			printk(KERN_ERR "nf_ct_event: Unable to register "
  					"event extension.
  ");
  			goto out_extend_register;
  		}
  	}
  
  	ret = nf_conntrack_event_init_sysctl(net);
  	if (ret < 0)
  		goto out_sysctl;
  
  	return 0;
  
  out_sysctl:
  	if (net_eq(net, &init_net))
  		nf_ct_extend_unregister(&event_extend);
  out_extend_register:
  	return ret;
  }
  
  void nf_conntrack_ecache_fini(struct net *net)
  {
  	nf_conntrack_event_fini_sysctl(net);
  	if (net_eq(net, &init_net))
  		nf_ct_extend_unregister(&event_extend);
  }