]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blame - net/netfilter/nf_conntrack_proto.c
[NETFILTER]: ip_conntrack: properly use RCU API for ip_ct_protos array
[mirror_ubuntu-zesty-kernel.git] / net / netfilter / nf_conntrack_proto.c
CommitLineData
8f03dea5
MJ
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 *
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/module.h>
d62f9ed4 15#include <linux/mutex.h>
8f03dea5
MJ
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>
21#include <linux/moduleparam.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>
605dcad6 28#include <net/netfilter/nf_conntrack_l4proto.h>
8f03dea5
MJ
29#include <net/netfilter/nf_conntrack_core.h>
30
605dcad6 31struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly;
ae5718fb 32struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX] __read_mostly;
13b18339 33EXPORT_SYMBOL_GPL(nf_ct_l3protos);
8f03dea5 34
d62f9ed4
PM
35#ifdef CONFIG_SYSCTL
36static DEFINE_MUTEX(nf_ct_proto_sysctl_mutex);
37
38static int
39nf_ct_register_sysctl(struct ctl_table_header **header, struct ctl_table *path,
40 struct ctl_table *table, unsigned int *users)
41{
42 if (*header == NULL) {
43 *header = nf_register_sysctl_table(path, table);
44 if (*header == NULL)
45 return -ENOMEM;
46 }
47 if (users != NULL)
48 (*users)++;
49 return 0;
50}
51
52static void
53nf_ct_unregister_sysctl(struct ctl_table_header **header,
54 struct ctl_table *table, unsigned int *users)
55{
56 if (users != NULL && --*users > 0)
57 return;
58 nf_unregister_sysctl_table(*header, table);
59 *header = NULL;
60}
61#endif
62
605dcad6
MJ
63struct nf_conntrack_l4proto *
64__nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto)
8f03dea5
MJ
65{
66 if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL))
605dcad6 67 return &nf_conntrack_l4proto_generic;
8f03dea5 68
605dcad6 69 return nf_ct_protos[l3proto][l4proto];
8f03dea5 70}
13b18339 71EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find);
8f03dea5
MJ
72
73/* this is guaranteed to always return a valid protocol helper, since
74 * it falls back to generic_protocol */
605dcad6
MJ
75struct nf_conntrack_l4proto *
76nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto)
8f03dea5 77{
605dcad6 78 struct nf_conntrack_l4proto *p;
8f03dea5
MJ
79
80 preempt_disable();
605dcad6 81 p = __nf_ct_l4proto_find(l3proto, l4proto);
8f03dea5 82 if (!try_module_get(p->me))
605dcad6 83 p = &nf_conntrack_l4proto_generic;
8f03dea5
MJ
84 preempt_enable();
85
86 return p;
87}
13b18339 88EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get);
8f03dea5 89
605dcad6 90void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p)
8f03dea5
MJ
91{
92 module_put(p->me);
93}
13b18339 94EXPORT_SYMBOL_GPL(nf_ct_l4proto_put);
8f03dea5
MJ
95
96struct nf_conntrack_l3proto *
97nf_ct_l3proto_find_get(u_int16_t l3proto)
98{
99 struct nf_conntrack_l3proto *p;
100
101 preempt_disable();
102 p = __nf_ct_l3proto_find(l3proto);
103 if (!try_module_get(p->me))
605dcad6 104 p = &nf_conntrack_l3proto_generic;
8f03dea5
MJ
105 preempt_enable();
106
107 return p;
108}
13b18339 109EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get);
8f03dea5
MJ
110
111void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p)
112{
113 module_put(p->me);
114}
13b18339 115EXPORT_SYMBOL_GPL(nf_ct_l3proto_put);
8f03dea5
MJ
116
117int
118nf_ct_l3proto_try_module_get(unsigned short l3proto)
119{
120 int ret;
121 struct nf_conntrack_l3proto *p;
122
123retry: p = nf_ct_l3proto_find_get(l3proto);
605dcad6 124 if (p == &nf_conntrack_l3proto_generic) {
8f03dea5
MJ
125 ret = request_module("nf_conntrack-%d", l3proto);
126 if (!ret)
127 goto retry;
128
129 return -EPROTOTYPE;
130 }
131
132 return 0;
133}
13b18339 134EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get);
8f03dea5
MJ
135
136void nf_ct_l3proto_module_put(unsigned short l3proto)
137{
138 struct nf_conntrack_l3proto *p;
139
140 preempt_disable();
141 p = __nf_ct_l3proto_find(l3proto);
142 preempt_enable();
143
144 module_put(p->me);
145}
13b18339 146EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put);
8f03dea5
MJ
147
148static int kill_l3proto(struct nf_conn *i, void *data)
149{
150 return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num ==
151 ((struct nf_conntrack_l3proto *)data)->l3proto);
152}
153
605dcad6 154static int kill_l4proto(struct nf_conn *i, void *data)
8f03dea5 155{
605dcad6
MJ
156 struct nf_conntrack_l4proto *l4proto;
157 l4proto = (struct nf_conntrack_l4proto *)data;
8f03dea5 158 return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum ==
605dcad6 159 l4proto->l4proto) &&
8f03dea5 160 (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num ==
605dcad6 161 l4proto->l3proto);
8f03dea5
MJ
162}
163
d62f9ed4
PM
164static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto)
165{
166 int err = 0;
167
168#ifdef CONFIG_SYSCTL
169 mutex_lock(&nf_ct_proto_sysctl_mutex);
170 if (l3proto->ctl_table != NULL) {
171 err = nf_ct_register_sysctl(&l3proto->ctl_table_header,
172 l3proto->ctl_table_path,
173 l3proto->ctl_table, NULL);
174 }
175 mutex_unlock(&nf_ct_proto_sysctl_mutex);
176#endif
177 return err;
178}
179
180static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto)
181{
182#ifdef CONFIG_SYSCTL
183 mutex_lock(&nf_ct_proto_sysctl_mutex);
184 if (l3proto->ctl_table_header != NULL)
185 nf_ct_unregister_sysctl(&l3proto->ctl_table_header,
186 l3proto->ctl_table, NULL);
187 mutex_unlock(&nf_ct_proto_sysctl_mutex);
188#endif
189}
190
8f03dea5
MJ
191int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
192{
193 int ret = 0;
194
ae5718fb
MJ
195 if (proto->l3proto >= AF_MAX) {
196 ret = -EBUSY;
197 goto out;
198 }
199
8f03dea5 200 write_lock_bh(&nf_conntrack_lock);
605dcad6 201 if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) {
8f03dea5 202 ret = -EBUSY;
ae5718fb 203 goto out_unlock;
8f03dea5
MJ
204 }
205 nf_ct_l3protos[proto->l3proto] = proto;
d62f9ed4
PM
206 write_unlock_bh(&nf_conntrack_lock);
207
208 ret = nf_ct_l3proto_register_sysctl(proto);
209 if (ret < 0)
210 nf_conntrack_l3proto_unregister(proto);
211 return ret;
8f03dea5 212
ae5718fb
MJ
213out_unlock:
214 write_unlock_bh(&nf_conntrack_lock);
215out:
8f03dea5
MJ
216 return ret;
217}
13b18339 218EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register);
8f03dea5 219
ae5718fb 220int nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
8f03dea5 221{
ae5718fb
MJ
222 int ret = 0;
223
224 if (proto->l3proto >= AF_MAX) {
225 ret = -EBUSY;
226 goto out;
227 }
228
8f03dea5 229 write_lock_bh(&nf_conntrack_lock);
ae5718fb
MJ
230 if (nf_ct_l3protos[proto->l3proto] != proto) {
231 write_unlock_bh(&nf_conntrack_lock);
232 ret = -EBUSY;
233 goto out;
234 }
235
605dcad6 236 nf_ct_l3protos[proto->l3proto] = &nf_conntrack_l3proto_generic;
8f03dea5
MJ
237 write_unlock_bh(&nf_conntrack_lock);
238
d62f9ed4
PM
239 nf_ct_l3proto_unregister_sysctl(proto);
240
8f03dea5
MJ
241 /* Somebody could be still looking at the proto in bh. */
242 synchronize_net();
243
244 /* Remove all contrack entries for this protocol */
245 nf_ct_iterate_cleanup(kill_l3proto, proto);
ae5718fb
MJ
246
247out:
248 return ret;
8f03dea5 249}
13b18339 250EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister);
8f03dea5 251
d62f9ed4
PM
252static int nf_ct_l4proto_register_sysctl(struct nf_conntrack_l4proto *l4proto)
253{
254 int err = 0;
255
256#ifdef CONFIG_SYSCTL
257 mutex_lock(&nf_ct_proto_sysctl_mutex);
258 if (l4proto->ctl_table != NULL) {
259 err = nf_ct_register_sysctl(l4proto->ctl_table_header,
260 nf_net_netfilter_sysctl_path,
261 l4proto->ctl_table,
262 l4proto->ctl_table_users);
a999e683
PM
263 if (err < 0)
264 goto out;
d62f9ed4 265 }
a999e683
PM
266#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
267 if (l4proto->ctl_compat_table != NULL) {
268 err = nf_ct_register_sysctl(&l4proto->ctl_compat_table_header,
269 nf_net_ipv4_netfilter_sysctl_path,
270 l4proto->ctl_compat_table, NULL);
271 if (err == 0)
272 goto out;
273 nf_ct_unregister_sysctl(l4proto->ctl_table_header,
274 l4proto->ctl_table,
275 l4proto->ctl_table_users);
276 }
277#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
278out:
d62f9ed4 279 mutex_unlock(&nf_ct_proto_sysctl_mutex);
933a41e7 280#endif /* CONFIG_SYSCTL */
d62f9ed4
PM
281 return err;
282}
283
284static void nf_ct_l4proto_unregister_sysctl(struct nf_conntrack_l4proto *l4proto)
285{
286#ifdef CONFIG_SYSCTL
287 mutex_lock(&nf_ct_proto_sysctl_mutex);
288 if (l4proto->ctl_table_header != NULL &&
289 *l4proto->ctl_table_header != NULL)
290 nf_ct_unregister_sysctl(l4proto->ctl_table_header,
291 l4proto->ctl_table,
292 l4proto->ctl_table_users);
a999e683
PM
293#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
294 if (l4proto->ctl_compat_table_header != NULL)
295 nf_ct_unregister_sysctl(&l4proto->ctl_compat_table_header,
296 l4proto->ctl_compat_table, NULL);
297#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
d62f9ed4 298 mutex_unlock(&nf_ct_proto_sysctl_mutex);
933a41e7 299#endif /* CONFIG_SYSCTL */
d62f9ed4
PM
300}
301
8f03dea5
MJ
302/* FIXME: Allow NULL functions and sub in pointers to generic for
303 them. --RR */
605dcad6 304int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto)
8f03dea5
MJ
305{
306 int ret = 0;
307
ae5718fb
MJ
308 if (l4proto->l3proto >= PF_MAX) {
309 ret = -EBUSY;
310 goto out;
311 }
312
933a41e7
PM
313 if (l4proto == &nf_conntrack_l4proto_generic)
314 return nf_ct_l4proto_register_sysctl(l4proto);
315
8f03dea5
MJ
316retry:
317 write_lock_bh(&nf_conntrack_lock);
605dcad6
MJ
318 if (nf_ct_protos[l4proto->l3proto]) {
319 if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto]
320 != &nf_conntrack_l4proto_generic) {
8f03dea5
MJ
321 ret = -EBUSY;
322 goto out_unlock;
323 }
324 } else {
325 /* l3proto may be loaded latter. */
605dcad6 326 struct nf_conntrack_l4proto **proto_array;
8f03dea5
MJ
327 int i;
328
329 write_unlock_bh(&nf_conntrack_lock);
330
605dcad6 331 proto_array = (struct nf_conntrack_l4proto **)
8f03dea5 332 kmalloc(MAX_NF_CT_PROTO *
605dcad6 333 sizeof(struct nf_conntrack_l4proto *),
8f03dea5
MJ
334 GFP_KERNEL);
335 if (proto_array == NULL) {
336 ret = -ENOMEM;
337 goto out;
338 }
339 for (i = 0; i < MAX_NF_CT_PROTO; i++)
605dcad6 340 proto_array[i] = &nf_conntrack_l4proto_generic;
8f03dea5
MJ
341
342 write_lock_bh(&nf_conntrack_lock);
605dcad6 343 if (nf_ct_protos[l4proto->l3proto]) {
8f03dea5
MJ
344 /* bad timing, but no problem */
345 write_unlock_bh(&nf_conntrack_lock);
346 kfree(proto_array);
347 } else {
605dcad6 348 nf_ct_protos[l4proto->l3proto] = proto_array;
8f03dea5
MJ
349 write_unlock_bh(&nf_conntrack_lock);
350 }
351
352 /*
353 * Just once because array is never freed until unloading
354 * nf_conntrack.ko
355 */
356 goto retry;
357 }
358
605dcad6 359 nf_ct_protos[l4proto->l3proto][l4proto->l4proto] = l4proto;
d62f9ed4
PM
360 write_unlock_bh(&nf_conntrack_lock);
361
362 ret = nf_ct_l4proto_register_sysctl(l4proto);
363 if (ret < 0)
364 nf_conntrack_l4proto_unregister(l4proto);
365 return ret;
8f03dea5
MJ
366
367out_unlock:
368 write_unlock_bh(&nf_conntrack_lock);
369out:
370 return ret;
371}
13b18339 372EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register);
8f03dea5 373
ae5718fb 374int nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto)
8f03dea5 375{
ae5718fb
MJ
376 int ret = 0;
377
378 if (l4proto->l3proto >= PF_MAX) {
379 ret = -EBUSY;
380 goto out;
381 }
382
933a41e7
PM
383 if (l4proto == &nf_conntrack_l4proto_generic) {
384 nf_ct_l4proto_unregister_sysctl(l4proto);
385 goto out;
386 }
387
8f03dea5 388 write_lock_bh(&nf_conntrack_lock);
ae5718fb
MJ
389 if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto]
390 != l4proto) {
391 write_unlock_bh(&nf_conntrack_lock);
392 ret = -EBUSY;
393 goto out;
394 }
605dcad6
MJ
395 nf_ct_protos[l4proto->l3proto][l4proto->l4proto]
396 = &nf_conntrack_l4proto_generic;
8f03dea5
MJ
397 write_unlock_bh(&nf_conntrack_lock);
398
d62f9ed4
PM
399 nf_ct_l4proto_unregister_sysctl(l4proto);
400
8f03dea5
MJ
401 /* Somebody could be still looking at the proto in bh. */
402 synchronize_net();
403
404 /* Remove all contrack entries for this protocol */
605dcad6 405 nf_ct_iterate_cleanup(kill_l4proto, l4proto);
ae5718fb
MJ
406
407out:
408 return ret;
8f03dea5 409}
13b18339 410EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister);