Blame view

net/netfilter/nf_conntrack_ecache.c 6.57 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>
bc3b2d7fb   Paul Gortmaker   net: Add export.h...
22
  #include <linux/export.h>
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
23
24
  
  #include <net/netfilter/nf_conntrack.h>
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
25
  #include <net/netfilter/nf_conntrack_core.h>
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
26
  #include <net/netfilter/nf_conntrack_extend.h>
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
27

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

f61801218   Martin Josefsson   [NETFILTER]: nf_c...
30
31
  /* deliver cached events and clear cache entry - must be called with locally
   * disabled softirqs */
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
32
  void nf_ct_deliver_cached_events(struct nf_conn *ct)
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
33
  {
70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
34
  	struct net *net = nf_ct_net(ct);
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
35
  	unsigned long events;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
36
  	struct nf_ct_event_notifier *notify;
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
37
  	struct nf_conntrack_ecache *e;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
38
39
  
  	rcu_read_lock();
70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
40
  	notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
41
42
  	if (notify == NULL)
  		goto out_unlock;
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
43
44
45
46
47
48
49
  	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...
50
  		struct nf_ct_event item = {
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
51
  			.ct	= ct,
19abb7b09   Pablo Neira Ayuso   netfilter: ctnetl...
52
53
54
  			.pid	= 0,
  			.report	= 0
  		};
dd7669a92   Pablo Neira Ayuso   netfilter: conntr...
55
56
57
58
59
  		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;
3db7e93d3   Pablo Neira Ayuso   netfilter: ecache...
60
61
  		if (!((events | missed) & e->ctmask))
  			goto out_unlock;
dd7669a92   Pablo Neira Ayuso   netfilter: conntr...
62
63
64
65
66
67
68
69
70
  		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...
71
  	}
f61801218   Martin Josefsson   [NETFILTER]: nf_c...
72

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

70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
78
79
  int nf_conntrack_register_notifier(struct net *net,
  				   struct nf_ct_event_notifier *new)
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
80
  {
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
81
  	int ret = 0;
b56f2d55c   Patrick McHardy   netfilter: use rc...
82
  	struct nf_ct_event_notifier *notify;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
83
84
  
  	mutex_lock(&nf_ct_ecache_mutex);
70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
85
  	notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb,
b56f2d55c   Patrick McHardy   netfilter: use rc...
86
87
  					   lockdep_is_held(&nf_ct_ecache_mutex));
  	if (notify != NULL) {
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
88
89
90
  		ret = -EBUSY;
  		goto out_unlock;
  	}
cf778b00e   Eric Dumazet   net: reintroduce ...
91
  	rcu_assign_pointer(net->ct.nf_conntrack_event_cb, new);
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
92
93
94
95
96
97
  	mutex_unlock(&nf_ct_ecache_mutex);
  	return ret;
  
  out_unlock:
  	mutex_unlock(&nf_ct_ecache_mutex);
  	return ret;
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
98
99
  }
  EXPORT_SYMBOL_GPL(nf_conntrack_register_notifier);
70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
100
101
  void nf_conntrack_unregister_notifier(struct net *net,
  				      struct nf_ct_event_notifier *new)
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
102
  {
b56f2d55c   Patrick McHardy   netfilter: use rc...
103
  	struct nf_ct_event_notifier *notify;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
104
  	mutex_lock(&nf_ct_ecache_mutex);
70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
105
  	notify = rcu_dereference_protected(net->ct.nf_conntrack_event_cb,
b56f2d55c   Patrick McHardy   netfilter: use rc...
106
107
  					   lockdep_is_held(&nf_ct_ecache_mutex));
  	BUG_ON(notify != new);
70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
108
  	RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, NULL);
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
109
  	mutex_unlock(&nf_ct_ecache_mutex);
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
110
111
  }
  EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
112
113
  int nf_ct_expect_register_notifier(struct net *net,
  				   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);
70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
119
  	notify = rcu_dereference_protected(net->ct.nf_expect_event_cb,
b56f2d55c   Patrick McHardy   netfilter: use rc...
120
121
  					   lockdep_is_held(&nf_ct_ecache_mutex));
  	if (notify != NULL) {
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
122
123
124
  		ret = -EBUSY;
  		goto out_unlock;
  	}
cf778b00e   Eric Dumazet   net: reintroduce ...
125
  	rcu_assign_pointer(net->ct.nf_expect_event_cb, new);
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
126
127
128
129
130
131
  	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

70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
135
136
  void nf_ct_expect_unregister_notifier(struct net *net,
  				      struct nf_exp_event_notifier *new)
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
137
  {
b56f2d55c   Patrick McHardy   netfilter: use rc...
138
  	struct nf_exp_event_notifier *notify;
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
139
  	mutex_lock(&nf_ct_ecache_mutex);
70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
140
  	notify = rcu_dereference_protected(net->ct.nf_expect_event_cb,
b56f2d55c   Patrick McHardy   netfilter: use rc...
141
142
  					   lockdep_is_held(&nf_ct_ecache_mutex));
  	BUG_ON(notify != new);
70e9942f1   Pablo Neira Ayuso   netfilter: nf_con...
143
  	RCU_INIT_POINTER(net->ct.nf_expect_event_cb, NULL);
e34d5c1a4   Pablo Neira Ayuso   netfilter: conntr...
144
  	mutex_unlock(&nf_ct_ecache_mutex);
010c7d6f8   Patrick McHardy   [NETFILTER]: nf_c...
145
  }
6823645d6   Patrick McHardy   [NETFILTER]: nf_c...
146
  EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
147
148
149
  
  #define NF_CT_EVENTS_DEFAULT 1
  static int nf_ct_events __read_mostly = NF_CT_EVENTS_DEFAULT;
dd7669a92   Pablo Neira Ayuso   netfilter: conntr...
150
  static int nf_ct_events_retry_timeout __read_mostly = 15*HZ;
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
151
152
153
154
  
  #ifdef CONFIG_SYSCTL
  static struct ctl_table event_sysctl_table[] = {
  	{
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
155
156
157
158
159
160
  		.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...
161
  	{
dd7669a92   Pablo Neira Ayuso   netfilter: conntr...
162
163
164
165
166
167
  		.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...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
  	{}
  };
  #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...
189
  	table[1].data = &net->ct.sysctl_events_retry_timeout;
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
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
230
  
  	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...
231
  	net->ct.sysctl_events_retry_timeout = nf_ct_events_retry_timeout;
a0891aa6a   Pablo Neira Ayuso   netfilter: conntr...
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
261
  
  	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);
  }