netfilter: nf_conntrack: prepare namespace support for l4 protocol trackers
[deliverable/linux.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>
5a0e3ad6 15#include <linux/slab.h>
d62f9ed4 16#include <linux/mutex.h>
8f03dea5
MJ
17#include <linux/vmalloc.h>
18#include <linux/stddef.h>
19#include <linux/err.h>
20#include <linux/percpu.h>
8f03dea5
MJ
21#include <linux/notifier.h>
22#include <linux/kernel.h>
23#include <linux/netdevice.h>
efb9a8c2 24#include <linux/rtnetlink.h>
8f03dea5
MJ
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
0906a372
AB
31static struct nf_conntrack_l4proto __rcu **nf_ct_protos[PF_MAX] __read_mostly;
32struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX] __read_mostly;
13b18339 33EXPORT_SYMBOL_GPL(nf_ct_l3protos);
8f03dea5 34
b19caa0c 35static DEFINE_MUTEX(nf_ct_proto_mutex);
d62f9ed4 36
b19caa0c 37#ifdef CONFIG_SYSCTL
d62f9ed4 38static int
2c352f44
G
39nf_ct_register_sysctl(struct net *net,
40 struct ctl_table_header **header,
41 const char *path,
42 struct ctl_table *table,
43 unsigned int *users)
d62f9ed4
PM
44{
45 if (*header == NULL) {
2c352f44 46 *header = register_net_sysctl(net, path, table);
d62f9ed4
PM
47 if (*header == NULL)
48 return -ENOMEM;
49 }
50 if (users != NULL)
51 (*users)++;
2c352f44 52
d62f9ed4
PM
53 return 0;
54}
55
56static void
57nf_ct_unregister_sysctl(struct ctl_table_header **header,
2c352f44
G
58 struct ctl_table **table,
59 unsigned int *users)
d62f9ed4
PM
60{
61 if (users != NULL && --*users > 0)
62 return;
b3fd3ffe 63
5dd3df10 64 unregister_net_sysctl_table(*header);
2c352f44 65 kfree(*table);
d62f9ed4 66 *header = NULL;
2c352f44 67 *table = NULL;
d62f9ed4
PM
68}
69#endif
70
605dcad6
MJ
71struct nf_conntrack_l4proto *
72__nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto)
8f03dea5
MJ
73{
74 if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL))
605dcad6 75 return &nf_conntrack_l4proto_generic;
8f03dea5 76
923f4902 77 return rcu_dereference(nf_ct_protos[l3proto][l4proto]);
8f03dea5 78}
13b18339 79EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find);
8f03dea5
MJ
80
81/* this is guaranteed to always return a valid protocol helper, since
82 * it falls back to generic_protocol */
8f03dea5
MJ
83struct nf_conntrack_l3proto *
84nf_ct_l3proto_find_get(u_int16_t l3proto)
85{
86 struct nf_conntrack_l3proto *p;
87
923f4902 88 rcu_read_lock();
8f03dea5
MJ
89 p = __nf_ct_l3proto_find(l3proto);
90 if (!try_module_get(p->me))
605dcad6 91 p = &nf_conntrack_l3proto_generic;
923f4902 92 rcu_read_unlock();
8f03dea5
MJ
93
94 return p;
95}
13b18339 96EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get);
8f03dea5
MJ
97
98void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p)
99{
100 module_put(p->me);
101}
13b18339 102EXPORT_SYMBOL_GPL(nf_ct_l3proto_put);
8f03dea5
MJ
103
104int
105nf_ct_l3proto_try_module_get(unsigned short l3proto)
106{
107 int ret;
108 struct nf_conntrack_l3proto *p;
109
110retry: p = nf_ct_l3proto_find_get(l3proto);
605dcad6 111 if (p == &nf_conntrack_l3proto_generic) {
8f03dea5
MJ
112 ret = request_module("nf_conntrack-%d", l3proto);
113 if (!ret)
114 goto retry;
115
116 return -EPROTOTYPE;
117 }
118
119 return 0;
120}
13b18339 121EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get);
8f03dea5
MJ
122
123void nf_ct_l3proto_module_put(unsigned short l3proto)
124{
125 struct nf_conntrack_l3proto *p;
126
3b254c54
PM
127 /* rcu_read_lock not necessary since the caller holds a reference, but
128 * taken anyways to avoid lockdep warnings in __nf_ct_l3proto_find()
129 */
130 rcu_read_lock();
8f03dea5 131 p = __nf_ct_l3proto_find(l3proto);
8f03dea5 132 module_put(p->me);
3b254c54 133 rcu_read_unlock();
8f03dea5 134}
13b18339 135EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put);
8f03dea5 136
c1ebd7df
PNA
137struct nf_conntrack_l4proto *
138nf_ct_l4proto_find_get(u_int16_t l3num, u_int8_t l4num)
139{
140 struct nf_conntrack_l4proto *p;
141
142 rcu_read_lock();
143 p = __nf_ct_l4proto_find(l3num, l4num);
144 if (!try_module_get(p->me))
145 p = &nf_conntrack_l4proto_generic;
146 rcu_read_unlock();
147
148 return p;
149}
150EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get);
151
152void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p)
153{
154 module_put(p->me);
155}
156EXPORT_SYMBOL_GPL(nf_ct_l4proto_put);
157
8f03dea5
MJ
158static int kill_l3proto(struct nf_conn *i, void *data)
159{
5e8fbe2a 160 return nf_ct_l3num(i) == ((struct nf_conntrack_l3proto *)data)->l3proto;
8f03dea5
MJ
161}
162
605dcad6 163static int kill_l4proto(struct nf_conn *i, void *data)
8f03dea5 164{
605dcad6
MJ
165 struct nf_conntrack_l4proto *l4proto;
166 l4proto = (struct nf_conntrack_l4proto *)data;
5e8fbe2a
PM
167 return nf_ct_protonum(i) == l4proto->l4proto &&
168 nf_ct_l3num(i) == l4proto->l3proto;
8f03dea5
MJ
169}
170
d62f9ed4
PM
171static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto)
172{
173 int err = 0;
174
175#ifdef CONFIG_SYSCTL
d62f9ed4 176 if (l3proto->ctl_table != NULL) {
2c352f44
G
177 err = nf_ct_register_sysctl(&init_net,
178 &l3proto->ctl_table_header,
d62f9ed4
PM
179 l3proto->ctl_table_path,
180 l3proto->ctl_table, NULL);
181 }
d62f9ed4
PM
182#endif
183 return err;
184}
185
186static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto)
187{
188#ifdef CONFIG_SYSCTL
d62f9ed4
PM
189 if (l3proto->ctl_table_header != NULL)
190 nf_ct_unregister_sysctl(&l3proto->ctl_table_header,
2c352f44 191 &l3proto->ctl_table, NULL);
d62f9ed4
PM
192#endif
193}
194
8f03dea5
MJ
195int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
196{
197 int ret = 0;
0e60ebe0 198 struct nf_conntrack_l3proto *old;
8f03dea5 199
0661cca9
PM
200 if (proto->l3proto >= AF_MAX)
201 return -EBUSY;
ae5718fb 202
d0dba725
HE
203 if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size)
204 return -EINVAL;
205
b19caa0c 206 mutex_lock(&nf_ct_proto_mutex);
0e60ebe0
ED
207 old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],
208 lockdep_is_held(&nf_ct_proto_mutex));
209 if (old != &nf_conntrack_l3proto_generic) {
8f03dea5 210 ret = -EBUSY;
ae5718fb 211 goto out_unlock;
8f03dea5 212 }
d62f9ed4
PM
213
214 ret = nf_ct_l3proto_register_sysctl(proto);
215 if (ret < 0)
0661cca9
PM
216 goto out_unlock;
217
d0dba725
HE
218 if (proto->nlattr_tuple_size)
219 proto->nla_size = 3 * proto->nlattr_tuple_size();
220
0661cca9 221 rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
8f03dea5 222
ae5718fb 223out_unlock:
b19caa0c 224 mutex_unlock(&nf_ct_proto_mutex);
8f03dea5
MJ
225 return ret;
226}
13b18339 227EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register);
8f03dea5 228
fe3eb20c 229void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
8f03dea5 230{
678d6675
AD
231 struct net *net;
232
fe3eb20c 233 BUG_ON(proto->l3proto >= AF_MAX);
ae5718fb 234
b19caa0c 235 mutex_lock(&nf_ct_proto_mutex);
0e60ebe0
ED
236 BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],
237 lockdep_is_held(&nf_ct_proto_mutex)
238 ) != proto);
923f4902
PM
239 rcu_assign_pointer(nf_ct_l3protos[proto->l3proto],
240 &nf_conntrack_l3proto_generic);
0661cca9 241 nf_ct_l3proto_unregister_sysctl(proto);
b19caa0c 242 mutex_unlock(&nf_ct_proto_mutex);
8f03dea5 243
0661cca9 244 synchronize_rcu();
d62f9ed4 245
8f03dea5 246 /* Remove all contrack entries for this protocol */
efb9a8c2 247 rtnl_lock();
678d6675
AD
248 for_each_net(net)
249 nf_ct_iterate_cleanup(net, kill_l3proto, proto);
efb9a8c2 250 rtnl_unlock();
8f03dea5 251}
13b18339 252EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister);
8f03dea5 253
2c352f44
G
254static struct nf_proto_net *nf_ct_l4proto_net(struct net *net,
255 struct nf_conntrack_l4proto *l4proto)
256{
257 if (l4proto->net_id)
258 return net_generic(net, *l4proto->net_id);
259 else
260 return NULL;
261}
262
263static
264int nf_ct_l4proto_register_sysctl(struct net *net,
265 struct nf_conntrack_l4proto *l4proto)
d62f9ed4
PM
266{
267 int err = 0;
2c352f44
G
268 struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto);
269 if (pn == NULL)
270 return 0;
d62f9ed4
PM
271
272#ifdef CONFIG_SYSCTL
2c352f44
G
273 if (pn->ctl_table != NULL) {
274 err = nf_ct_register_sysctl(net,
275 &pn->ctl_table_header,
f99e8f71 276 "net/netfilter",
2c352f44
G
277 pn->ctl_table,
278 &pn->users);
279 if (err < 0) {
280 if (!pn->users) {
281 kfree(pn->ctl_table);
282 pn->ctl_table = NULL;
283 }
a999e683 284 goto out;
2c352f44 285 }
d62f9ed4 286 }
a999e683 287#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
2c352f44
G
288 if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_table != NULL) {
289 err = nf_ct_register_sysctl(net,
290 &pn->ctl_compat_header,
f99e8f71 291 "net/ipv4/netfilter",
2c352f44
G
292 pn->ctl_compat_table,
293 NULL);
a999e683
PM
294 if (err == 0)
295 goto out;
2c352f44
G
296
297 kfree(pn->ctl_compat_table);
298 pn->ctl_compat_table = NULL;
299 nf_ct_unregister_sysctl(&pn->ctl_table_header,
300 &pn->ctl_table,
301 &pn->users);
a999e683
PM
302 }
303#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
304out:
933a41e7 305#endif /* CONFIG_SYSCTL */
d62f9ed4
PM
306 return err;
307}
308
2c352f44
G
309static
310void nf_ct_l4proto_unregister_sysctl(struct net *net,
311 struct nf_conntrack_l4proto *l4proto)
d62f9ed4 312{
2c352f44
G
313 struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto);
314 if (pn == NULL)
315 return;
d62f9ed4 316#ifdef CONFIG_SYSCTL
2c352f44
G
317 if (pn->ctl_table_header != NULL)
318 nf_ct_unregister_sysctl(&pn->ctl_table_header,
319 &pn->ctl_table,
320 &pn->users);
321
a999e683 322#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
2c352f44
G
323 if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_header != NULL)
324 nf_ct_unregister_sysctl(&pn->ctl_compat_header,
325 &pn->ctl_compat_table,
326 NULL);
a999e683 327#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
2c352f44
G
328#else
329 pn->users--;
933a41e7 330#endif /* CONFIG_SYSCTL */
d62f9ed4
PM
331}
332
8f03dea5
MJ
333/* FIXME: Allow NULL functions and sub in pointers to generic for
334 them. --RR */
2c352f44
G
335static int
336nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto)
8f03dea5
MJ
337{
338 int ret = 0;
339
0661cca9
PM
340 if (l4proto->l3proto >= PF_MAX)
341 return -EBUSY;
ae5718fb 342
d0dba725
HE
343 if ((l4proto->to_nlattr && !l4proto->nlattr_size)
344 || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size))
345 return -EINVAL;
346
b19caa0c 347 mutex_lock(&nf_ct_proto_mutex);
c6a1e615 348 if (!nf_ct_protos[l4proto->l3proto]) {
8f03dea5 349 /* l3proto may be loaded latter. */
c5d277d2 350 struct nf_conntrack_l4proto __rcu **proto_array;
8f03dea5
MJ
351 int i;
352
c6a1e615
PM
353 proto_array = kmalloc(MAX_NF_CT_PROTO *
354 sizeof(struct nf_conntrack_l4proto *),
355 GFP_KERNEL);
8f03dea5
MJ
356 if (proto_array == NULL) {
357 ret = -ENOMEM;
b19caa0c 358 goto out_unlock;
8f03dea5 359 }
c6a1e615 360
8f03dea5 361 for (i = 0; i < MAX_NF_CT_PROTO; i++)
c5d277d2 362 RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic);
d817d29d
ED
363
364 /* Before making proto_array visible to lockless readers,
365 * we must make sure its content is committed to memory.
366 */
367 smp_wmb();
368
c6a1e615 369 nf_ct_protos[l4proto->l3proto] = proto_array;
0e60ebe0
ED
370 } else if (rcu_dereference_protected(
371 nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
372 lockdep_is_held(&nf_ct_proto_mutex)
373 ) != &nf_conntrack_l4proto_generic) {
c6a1e615
PM
374 ret = -EBUSY;
375 goto out_unlock;
8f03dea5
MJ
376 }
377
d0dba725
HE
378 l4proto->nla_size = 0;
379 if (l4proto->nlattr_size)
380 l4proto->nla_size += l4proto->nlattr_size();
381 if (l4proto->nlattr_tuple_size)
382 l4proto->nla_size += 3 * l4proto->nlattr_tuple_size();
383
c6a1e615
PM
384 rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
385 l4proto);
8f03dea5 386out_unlock:
b19caa0c 387 mutex_unlock(&nf_ct_proto_mutex);
8f03dea5
MJ
388 return ret;
389}
390
2c352f44
G
391int nf_conntrack_l4proto_register(struct net *net,
392 struct nf_conntrack_l4proto *l4proto)
8f03dea5 393{
2c352f44
G
394 int ret = 0;
395 if (net == &init_net)
396 ret = nf_conntrack_l4proto_register_net(l4proto);
397
398 if (ret < 0)
399 return ret;
400
401 if (l4proto->init_net)
402 ret = l4proto->init_net(net);
678d6675 403
2c352f44
G
404 if (ret < 0)
405 return ret;
406
407 return nf_ct_l4proto_register_sysctl(net, l4proto);
408}
409EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register);
410
411static void
412nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto)
413{
fe3eb20c 414 BUG_ON(l4proto->l3proto >= PF_MAX);
ae5718fb 415
b19caa0c 416 mutex_lock(&nf_ct_proto_mutex);
0e60ebe0
ED
417 BUG_ON(rcu_dereference_protected(
418 nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
419 lockdep_is_held(&nf_ct_proto_mutex)
420 ) != l4proto);
923f4902
PM
421 rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
422 &nf_conntrack_l4proto_generic);
b19caa0c 423 mutex_unlock(&nf_ct_proto_mutex);
8f03dea5 424
0661cca9 425 synchronize_rcu();
2c352f44 426}
d62f9ed4 427
2c352f44
G
428void nf_conntrack_l4proto_unregister(struct net *net,
429 struct nf_conntrack_l4proto *l4proto)
430{
431 if (net == &init_net)
432 nf_conntrack_l4proto_unregister_net(l4proto);
433
434 nf_ct_l4proto_unregister_sysctl(net, l4proto);
8f03dea5 435 /* Remove all contrack entries for this protocol */
efb9a8c2 436 rtnl_lock();
2c352f44 437 nf_ct_iterate_cleanup(net, kill_l4proto, l4proto);
efb9a8c2 438 rtnl_unlock();
8f03dea5 439}
13b18339 440EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister);
ac5357eb
PM
441
442int nf_conntrack_proto_init(void)
443{
444 unsigned int i;
445 int err;
446
2c352f44 447 err = nf_ct_l4proto_register_sysctl(&init_net, &nf_conntrack_l4proto_generic);
ac5357eb
PM
448 if (err < 0)
449 return err;
450
451 for (i = 0; i < AF_MAX; i++)
452 rcu_assign_pointer(nf_ct_l3protos[i],
453 &nf_conntrack_l3proto_generic);
454 return 0;
455}
456
457void nf_conntrack_proto_fini(void)
458{
459 unsigned int i;
460
2c352f44 461 nf_ct_l4proto_unregister_sysctl(&init_net, &nf_conntrack_l4proto_generic);
ac5357eb
PM
462
463 /* free l3proto protocol tables */
464 for (i = 0; i < PF_MAX; i++)
465 kfree(nf_ct_protos[i]);
466}
This page took 0.469444 seconds and 5 git commands to generate.