netfilter: nf_conntrack: prepare namespace support for l4 protocol trackers
[linux-2.6-block.git] / net / netfilter / nf_conntrack_proto.c
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>
15 #include <linux/slab.h>
16 #include <linux/mutex.h>
17 #include <linux/vmalloc.h>
18 #include <linux/stddef.h>
19 #include <linux/err.h>
20 #include <linux/percpu.h>
21 #include <linux/notifier.h>
22 #include <linux/kernel.h>
23 #include <linux/netdevice.h>
24 #include <linux/rtnetlink.h>
25
26 #include <net/netfilter/nf_conntrack.h>
27 #include <net/netfilter/nf_conntrack_l3proto.h>
28 #include <net/netfilter/nf_conntrack_l4proto.h>
29 #include <net/netfilter/nf_conntrack_core.h>
30
31 static struct nf_conntrack_l4proto __rcu **nf_ct_protos[PF_MAX] __read_mostly;
32 struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX] __read_mostly;
33 EXPORT_SYMBOL_GPL(nf_ct_l3protos);
34
35 static DEFINE_MUTEX(nf_ct_proto_mutex);
36
37 #ifdef CONFIG_SYSCTL
38 static int
39 nf_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)
44 {
45         if (*header == NULL) {
46                 *header = register_net_sysctl(net, path, table);
47                 if (*header == NULL)
48                         return -ENOMEM;
49         }
50         if (users != NULL)
51                 (*users)++;
52
53         return 0;
54 }
55
56 static void
57 nf_ct_unregister_sysctl(struct ctl_table_header **header,
58                         struct ctl_table **table,
59                         unsigned int *users)
60 {
61         if (users != NULL && --*users > 0)
62                 return;
63
64         unregister_net_sysctl_table(*header);
65         kfree(*table);
66         *header = NULL;
67         *table = NULL;
68 }
69 #endif
70
71 struct nf_conntrack_l4proto *
72 __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto)
73 {
74         if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL))
75                 return &nf_conntrack_l4proto_generic;
76
77         return rcu_dereference(nf_ct_protos[l3proto][l4proto]);
78 }
79 EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find);
80
81 /* this is guaranteed to always return a valid protocol helper, since
82  * it falls back to generic_protocol */
83 struct nf_conntrack_l3proto *
84 nf_ct_l3proto_find_get(u_int16_t l3proto)
85 {
86         struct nf_conntrack_l3proto *p;
87
88         rcu_read_lock();
89         p = __nf_ct_l3proto_find(l3proto);
90         if (!try_module_get(p->me))
91                 p = &nf_conntrack_l3proto_generic;
92         rcu_read_unlock();
93
94         return p;
95 }
96 EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get);
97
98 void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p)
99 {
100         module_put(p->me);
101 }
102 EXPORT_SYMBOL_GPL(nf_ct_l3proto_put);
103
104 int
105 nf_ct_l3proto_try_module_get(unsigned short l3proto)
106 {
107         int ret;
108         struct nf_conntrack_l3proto *p;
109
110 retry:  p = nf_ct_l3proto_find_get(l3proto);
111         if (p == &nf_conntrack_l3proto_generic) {
112                 ret = request_module("nf_conntrack-%d", l3proto);
113                 if (!ret)
114                         goto retry;
115
116                 return -EPROTOTYPE;
117         }
118
119         return 0;
120 }
121 EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get);
122
123 void nf_ct_l3proto_module_put(unsigned short l3proto)
124 {
125         struct nf_conntrack_l3proto *p;
126
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();
131         p = __nf_ct_l3proto_find(l3proto);
132         module_put(p->me);
133         rcu_read_unlock();
134 }
135 EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put);
136
137 struct nf_conntrack_l4proto *
138 nf_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 }
150 EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get);
151
152 void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p)
153 {
154         module_put(p->me);
155 }
156 EXPORT_SYMBOL_GPL(nf_ct_l4proto_put);
157
158 static int kill_l3proto(struct nf_conn *i, void *data)
159 {
160         return nf_ct_l3num(i) == ((struct nf_conntrack_l3proto *)data)->l3proto;
161 }
162
163 static int kill_l4proto(struct nf_conn *i, void *data)
164 {
165         struct nf_conntrack_l4proto *l4proto;
166         l4proto = (struct nf_conntrack_l4proto *)data;
167         return nf_ct_protonum(i) == l4proto->l4proto &&
168                nf_ct_l3num(i) == l4proto->l3proto;
169 }
170
171 static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto)
172 {
173         int err = 0;
174
175 #ifdef CONFIG_SYSCTL
176         if (l3proto->ctl_table != NULL) {
177                 err = nf_ct_register_sysctl(&init_net,
178                                             &l3proto->ctl_table_header,
179                                             l3proto->ctl_table_path,
180                                             l3proto->ctl_table, NULL);
181         }
182 #endif
183         return err;
184 }
185
186 static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto)
187 {
188 #ifdef CONFIG_SYSCTL
189         if (l3proto->ctl_table_header != NULL)
190                 nf_ct_unregister_sysctl(&l3proto->ctl_table_header,
191                                         &l3proto->ctl_table, NULL);
192 #endif
193 }
194
195 int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
196 {
197         int ret = 0;
198         struct nf_conntrack_l3proto *old;
199
200         if (proto->l3proto >= AF_MAX)
201                 return -EBUSY;
202
203         if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size)
204                 return -EINVAL;
205
206         mutex_lock(&nf_ct_proto_mutex);
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) {
210                 ret = -EBUSY;
211                 goto out_unlock;
212         }
213
214         ret = nf_ct_l3proto_register_sysctl(proto);
215         if (ret < 0)
216                 goto out_unlock;
217
218         if (proto->nlattr_tuple_size)
219                 proto->nla_size = 3 * proto->nlattr_tuple_size();
220
221         rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto);
222
223 out_unlock:
224         mutex_unlock(&nf_ct_proto_mutex);
225         return ret;
226 }
227 EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register);
228
229 void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
230 {
231         struct net *net;
232
233         BUG_ON(proto->l3proto >= AF_MAX);
234
235         mutex_lock(&nf_ct_proto_mutex);
236         BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto],
237                                          lockdep_is_held(&nf_ct_proto_mutex)
238                                          ) != proto);
239         rcu_assign_pointer(nf_ct_l3protos[proto->l3proto],
240                            &nf_conntrack_l3proto_generic);
241         nf_ct_l3proto_unregister_sysctl(proto);
242         mutex_unlock(&nf_ct_proto_mutex);
243
244         synchronize_rcu();
245
246         /* Remove all contrack entries for this protocol */
247         rtnl_lock();
248         for_each_net(net)
249                 nf_ct_iterate_cleanup(net, kill_l3proto, proto);
250         rtnl_unlock();
251 }
252 EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister);
253
254 static 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
263 static
264 int nf_ct_l4proto_register_sysctl(struct net *net,
265                                   struct nf_conntrack_l4proto *l4proto)
266 {
267         int err = 0;
268         struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto);
269         if (pn == NULL)
270                 return 0;
271
272 #ifdef CONFIG_SYSCTL
273         if (pn->ctl_table != NULL) {
274                 err = nf_ct_register_sysctl(net,
275                                             &pn->ctl_table_header,
276                                             "net/netfilter",
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                         }
284                         goto out;
285                 }
286         }
287 #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
288         if (l4proto->l3proto != AF_INET6 && pn->ctl_compat_table != NULL) {
289                 err = nf_ct_register_sysctl(net,
290                                             &pn->ctl_compat_header,
291                                             "net/ipv4/netfilter",
292                                             pn->ctl_compat_table,
293                                             NULL);
294                 if (err == 0)
295                         goto out;
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);
302         }
303 #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
304 out:
305 #endif /* CONFIG_SYSCTL */
306         return err;
307 }
308
309 static
310 void nf_ct_l4proto_unregister_sysctl(struct net *net,
311                                      struct nf_conntrack_l4proto *l4proto)
312 {
313         struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto);
314         if (pn == NULL)
315                 return;
316 #ifdef CONFIG_SYSCTL
317         if (pn->ctl_table_header != NULL)
318                 nf_ct_unregister_sysctl(&pn->ctl_table_header,
319                                         &pn->ctl_table,
320                                         &pn->users);
321
322 #ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT
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);
327 #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */
328 #else
329         pn->users--;
330 #endif /* CONFIG_SYSCTL */
331 }
332
333 /* FIXME: Allow NULL functions and sub in pointers to generic for
334    them. --RR */
335 static int
336 nf_conntrack_l4proto_register_net(struct nf_conntrack_l4proto *l4proto)
337 {
338         int ret = 0;
339
340         if (l4proto->l3proto >= PF_MAX)
341                 return -EBUSY;
342
343         if ((l4proto->to_nlattr && !l4proto->nlattr_size)
344                 || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size))
345                 return -EINVAL;
346
347         mutex_lock(&nf_ct_proto_mutex);
348         if (!nf_ct_protos[l4proto->l3proto]) {
349                 /* l3proto may be loaded latter. */
350                 struct nf_conntrack_l4proto __rcu **proto_array;
351                 int i;
352
353                 proto_array = kmalloc(MAX_NF_CT_PROTO *
354                                       sizeof(struct nf_conntrack_l4proto *),
355                                       GFP_KERNEL);
356                 if (proto_array == NULL) {
357                         ret = -ENOMEM;
358                         goto out_unlock;
359                 }
360
361                 for (i = 0; i < MAX_NF_CT_PROTO; i++)
362                         RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic);
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
369                 nf_ct_protos[l4proto->l3proto] = proto_array;
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) {
374                 ret = -EBUSY;
375                 goto out_unlock;
376         }
377
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
384         rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
385                            l4proto);
386 out_unlock:
387         mutex_unlock(&nf_ct_proto_mutex);
388         return ret;
389 }
390
391 int nf_conntrack_l4proto_register(struct net *net,
392                                   struct nf_conntrack_l4proto *l4proto)
393 {
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);
403
404         if (ret < 0)
405                 return ret;
406
407         return nf_ct_l4proto_register_sysctl(net, l4proto);
408 }
409 EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register);
410
411 static void
412 nf_conntrack_l4proto_unregister_net(struct nf_conntrack_l4proto *l4proto)
413 {
414         BUG_ON(l4proto->l3proto >= PF_MAX);
415
416         mutex_lock(&nf_ct_proto_mutex);
417         BUG_ON(rcu_dereference_protected(
418                         nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
419                         lockdep_is_held(&nf_ct_proto_mutex)
420                         ) != l4proto);
421         rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto],
422                            &nf_conntrack_l4proto_generic);
423         mutex_unlock(&nf_ct_proto_mutex);
424
425         synchronize_rcu();
426 }
427
428 void 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);
435         /* Remove all contrack entries for this protocol */
436         rtnl_lock();
437         nf_ct_iterate_cleanup(net, kill_l4proto, l4proto);
438         rtnl_unlock();
439 }
440 EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister);
441
442 int nf_conntrack_proto_init(void)
443 {
444         unsigned int i;
445         int err;
446
447         err = nf_ct_l4proto_register_sysctl(&init_net, &nf_conntrack_l4proto_generic);
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
457 void nf_conntrack_proto_fini(void)
458 {
459         unsigned int i;
460
461         nf_ct_l4proto_unregister_sysctl(&init_net, &nf_conntrack_l4proto_generic);
462
463         /* free l3proto protocol tables */
464         for (i = 0; i < PF_MAX; i++)
465                 kfree(nf_ct_protos[i]);
466 }