netfilter: nf_conntrack: prepare namespace support for l3 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
524a53e5
G
171static struct nf_ip_net *nf_ct_l3proto_net(struct net *net,
172 struct nf_conntrack_l3proto *l3proto)
d62f9ed4 173{
524a53e5
G
174 if (l3proto->l3proto == PF_INET)
175 return &net->ct.nf_ct_proto;
176 else
177 return NULL;
178}
d62f9ed4 179
524a53e5
G
180static int nf_ct_l3proto_register_sysctl(struct net *net,
181 struct nf_conntrack_l3proto *l3proto)
182{
183 int err = 0;
184 struct nf_ip_net *in = nf_ct_l3proto_net(net, l3proto);
185 /* nf_conntrack_l3proto_ipv6 doesn't support sysctl */
186 if (in == NULL)
187 return 0;
188
189#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
190 if (in->ctl_table != NULL) {
191 err = nf_ct_register_sysctl(net,
192 &in->ctl_table_header,
d62f9ed4 193 l3proto->ctl_table_path,
524a53e5
G
194 in->ctl_table,
195 NULL);
196 if (err < 0) {
197 kfree(in->ctl_table);
198 in->ctl_table = NULL;
199 }
d62f9ed4 200 }
d62f9ed4
PM
201#endif
202 return err;
203}
204
524a53e5
G
205static void nf_ct_l3proto_unregister_sysctl(struct net *net,
206 struct nf_conntrack_l3proto *l3proto)
d62f9ed4 207{
524a53e5
G
208 struct nf_ip_net *in = nf_ct_l3proto_net(net, l3proto);
209
210 if (in == NULL)
211 return;
212#if defined(CONFIG_SYSCTL) && defined(CONFIG_NF_CONNTRACK_PROC_COMPAT)
213 if (in->ctl_table_header != NULL)
214 nf_ct_unregister_sysctl(&in->ctl_table_header,
215 &in->ctl_table,
216 NULL);
d62f9ed4
PM
217#endif
218}
219
524a53e5
G
220static int
221nf_conntrack_l3proto_register_net(struct nf_conntrack_l3proto *proto)
8f03dea5
MJ
222{
223 int ret = 0;
0e60ebe0 224 struct nf_conntrack_l3proto *old;
8f03dea5 225
0661cca9
PM
226 if (proto->l3proto >= AF_MAX)
227 return -EBUSY;
ae5718fb 228
d0dba725
HE
229 if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size)
230 return -EINVAL;
231
b19caa0c 232 mutex_lock(&nf_ct_proto_mutex);
0e60ebe0
ED
233 old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],
234 lockdep_is_held(&nf_ct_proto_mutex));
235 if (old != &nf_conntrack_l3proto_generic) {
8f03dea5 236 ret = -EBUSY;
ae5718fb 237 goto out_unlock;
8f03dea5 238 }
d62f9ed4 239
d0dba725
HE
240 if (proto->nlattr_tuple_size)
241 proto->nla_size = 3 * proto->nlattr_tuple_size();
242
0661cca9 243 rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
8f03dea5 244
ae5718fb 245out_unlock:
b19caa0c 246 mutex_unlock(&nf_ct_proto_mutex);
8f03dea5 247 return ret;
524a53e5 248
8f03dea5
MJ
249}
250
524a53e5
G
251int nf_conntrack_l3proto_register(struct net *net,
252 struct nf_conntrack_l3proto *proto)
8f03dea5 253{
524a53e5
G
254 int ret = 0;
255
256 if (net == &init_net)
257 ret = nf_conntrack_l3proto_register_net(proto);
258
259 if (ret < 0)
260 return ret;
261
262 if (proto->init_net) {
263 ret = proto->init_net(net);
264 if (ret < 0)
265 return ret;
266 }
267 return nf_ct_l3proto_register_sysctl(net, proto);
268}
269EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register);
678d6675 270
524a53e5
G
271static void
272nf_conntrack_l3proto_unregister_net(struct nf_conntrack_l3proto *proto)
273{
fe3eb20c 274 BUG_ON(proto->l3proto >= AF_MAX);
ae5718fb 275
b19caa0c 276 mutex_lock(&nf_ct_proto_mutex);
0e60ebe0
ED
277 BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],
278 lockdep_is_held(&nf_ct_proto_mutex)
279 ) != proto);
923f4902
PM
280 rcu_assign_pointer(nf_ct_l3protos[proto->l3proto],
281 &nf_conntrack_l3proto_generic);
b19caa0c 282 mutex_unlock(&nf_ct_proto_mutex);
8f03dea5 283
0661cca9 284 synchronize_rcu();
524a53e5
G
285}
286
287void nf_conntrack_l3proto_unregister(struct net *net,
288 struct nf_conntrack_l3proto *proto)
289{
290 if (net == &init_net)
291 nf_conntrack_l3proto_unregister_net(proto);
292
293 nf_ct_l3proto_unregister_sysctl(net, proto);
d62f9ed4 294
8f03dea5 295 /* Remove all contrack entries for this protocol */
efb9a8c2 296 rtnl_lock();
524a53e5 297 nf_ct_iterate_cleanup(net, kill_l3proto, proto);
efb9a8c2 298 rtnl_unlock();
8f03dea5 299}
13b18339 300EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister);
8f03dea5 301
2c352f44
G
302static struct nf_proto_net *nf_ct_l4proto_net(struct net *net,
303 struct nf_conntrack_l4proto *l4proto)
304{
305 if (l4proto->net_id)
306 return net_generic(net, *l4proto->net_id);
307 else
308 return NULL;
309}
310
311static
312int nf_ct_l4proto_register_sysctl(struct net *net,
313 struct nf_conntrack_l4proto *l4proto)
d62f9ed4
PM
314{
315 int err = 0;
2c352f44
G
316 struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto);
317 if (pn == NULL)
318 return 0;
d62f9ed4
PM
319
320#ifdef CONFIG_SYSCTL
2c352f44
G
321 if (pn->ctl_table != NULL) {
322 err = nf_ct_register_sysctl(net,
323 &pn->ctl_table_header,
f99e8f71 324 "net/netfilter",
2c352f44
G
325 pn->ctl_table,
326 &pn->users);
327 if (err < 0) {
328 if (!pn->users) {
329 kfree(pn->ctl_table);
330 pn->ctl_table = NULL;
331 }
a999e683 332 goto out;
2c352f44 333 }
d62f9ed4 334 }
a999e683 335#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
2c352f44
G
336 if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_table != NULL) {
337 err = nf_ct_register_sysctl(net,
338 &pn->ctl_compat_header,
f99e8f71 339 "net/ipv4/netfilter",
2c352f44
G
340 pn->ctl_compat_table,
341 NULL);
a999e683
PM
342 if (err == 0)
343 goto out;
2c352f44
G
344
345 kfree(pn->ctl_compat_table);
346 pn->ctl_compat_table = NULL;
347 nf_ct_unregister_sysctl(&pn->ctl_table_header,
348 &pn->ctl_table,
349 &pn->users);
a999e683
PM
350 }
351#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
352out:
933a41e7 353#endif /* CONFIG_SYSCTL */
d62f9ed4
PM
354 return err;
355}
356
2c352f44
G
357static
358void nf_ct_l4proto_unregister_sysctl(struct net *net,
359 struct nf_conntrack_l4proto *l4proto)
d62f9ed4 360{
2c352f44
G
361 struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto);
362 if (pn == NULL)
363 return;
d62f9ed4 364#ifdef CONFIG_SYSCTL
2c352f44
G
365 if (pn->ctl_table_header != NULL)
366 nf_ct_unregister_sysctl(&pn->ctl_table_header,
367 &pn->ctl_table,
368 &pn->users);
369
a999e683 370#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
2c352f44
G
371 if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_header != NULL)
372 nf_ct_unregister_sysctl(&pn->ctl_compat_header,
373 &pn->ctl_compat_table,
374 NULL);
a999e683 375#endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
2c352f44
G
376#else
377 pn->users--;
933a41e7 378#endif /* CONFIG_SYSCTL */
d62f9ed4
PM
379}
380
8f03dea5
MJ
381/* FIXME: Allow NULL functions and sub in pointers to generic for
382 them. --RR */
2c352f44
G
383static int
384nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto)
8f03dea5
MJ
385{
386 int ret = 0;
387
0661cca9
PM
388 if (l4proto->l3proto >= PF_MAX)
389 return -EBUSY;
ae5718fb 390
d0dba725
HE
391 if ((l4proto->to_nlattr && !l4proto->nlattr_size)
392 || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size))
393 return -EINVAL;
394
b19caa0c 395 mutex_lock(&nf_ct_proto_mutex);
c6a1e615 396 if (!nf_ct_protos[l4proto->l3proto]) {
8f03dea5 397 /* l3proto may be loaded latter. */
c5d277d2 398 struct nf_conntrack_l4proto __rcu **proto_array;
8f03dea5
MJ
399 int i;
400
c6a1e615
PM
401 proto_array = kmalloc(MAX_NF_CT_PROTO *
402 sizeof(struct nf_conntrack_l4proto *),
403 GFP_KERNEL);
8f03dea5
MJ
404 if (proto_array == NULL) {
405 ret = -ENOMEM;
b19caa0c 406 goto out_unlock;
8f03dea5 407 }
c6a1e615 408
8f03dea5 409 for (i = 0; i < MAX_NF_CT_PROTO; i++)
c5d277d2 410 RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic);
d817d29d
ED
411
412 /* Before making proto_array visible to lockless readers,
413 * we must make sure its content is committed to memory.
414 */
415 smp_wmb();
416
c6a1e615 417 nf_ct_protos[l4proto->l3proto] = proto_array;
0e60ebe0
ED
418 } else if (rcu_dereference_protected(
419 nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
420 lockdep_is_held(&nf_ct_proto_mutex)
421 ) != &nf_conntrack_l4proto_generic) {
c6a1e615
PM
422 ret = -EBUSY;
423 goto out_unlock;
8f03dea5
MJ
424 }
425
d0dba725
HE
426 l4proto->nla_size = 0;
427 if (l4proto->nlattr_size)
428 l4proto->nla_size += l4proto->nlattr_size();
429 if (l4proto->nlattr_tuple_size)
430 l4proto->nla_size += 3 * l4proto->nlattr_tuple_size();
431
c6a1e615
PM
432 rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
433 l4proto);
8f03dea5 434out_unlock:
b19caa0c 435 mutex_unlock(&nf_ct_proto_mutex);
8f03dea5
MJ
436 return ret;
437}
438
2c352f44
G
439int nf_conntrack_l4proto_register(struct net *net,
440 struct nf_conntrack_l4proto *l4proto)
8f03dea5 441{
2c352f44
G
442 int ret = 0;
443 if (net == &init_net)
444 ret = nf_conntrack_l4proto_register_net(l4proto);
445
446 if (ret < 0)
447 return ret;
448
449 if (l4proto->init_net)
450 ret = l4proto->init_net(net);
678d6675 451
2c352f44
G
452 if (ret < 0)
453 return ret;
454
455 return nf_ct_l4proto_register_sysctl(net, l4proto);
456}
457EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register);
458
459static void
460nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto)
461{
fe3eb20c 462 BUG_ON(l4proto->l3proto >= PF_MAX);
ae5718fb 463
b19caa0c 464 mutex_lock(&nf_ct_proto_mutex);
0e60ebe0
ED
465 BUG_ON(rcu_dereference_protected(
466 nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
467 lockdep_is_held(&nf_ct_proto_mutex)
468 ) != l4proto);
923f4902
PM
469 rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
470 &nf_conntrack_l4proto_generic);
b19caa0c 471 mutex_unlock(&nf_ct_proto_mutex);
8f03dea5 472
0661cca9 473 synchronize_rcu();
2c352f44 474}
d62f9ed4 475
2c352f44
G
476void nf_conntrack_l4proto_unregister(struct net *net,
477 struct nf_conntrack_l4proto *l4proto)
478{
479 if (net == &init_net)
480 nf_conntrack_l4proto_unregister_net(l4proto);
481
482 nf_ct_l4proto_unregister_sysctl(net, l4proto);
8f03dea5 483 /* Remove all contrack entries for this protocol */
efb9a8c2 484 rtnl_lock();
2c352f44 485 nf_ct_iterate_cleanup(net, kill_l4proto, l4proto);
efb9a8c2 486 rtnl_unlock();
8f03dea5 487}
13b18339 488EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister);
ac5357eb
PM
489
490int nf_conntrack_proto_init(void)
491{
492 unsigned int i;
493 int err;
494
2c352f44 495 err = nf_ct_l4proto_register_sysctl(&init_net, &nf_conntrack_l4proto_generic);
ac5357eb
PM
496 if (err < 0)
497 return err;
498
499 for (i = 0; i < AF_MAX; i++)
500 rcu_assign_pointer(nf_ct_l3protos[i],
501 &nf_conntrack_l3proto_generic);
502 return 0;
503}
504
505void nf_conntrack_proto_fini(void)
506{
507 unsigned int i;
508
2c352f44 509 nf_ct_l4proto_unregister_sysctl(&init_net, &nf_conntrack_l4proto_generic);
ac5357eb
PM
510
511 /* free l3proto protocol tables */
512 for (i = 0; i < PF_MAX; i++)
513 kfree(nf_ct_protos[i]);
514}
This page took 0.48637 seconds and 5 git commands to generate.