[NETFILTER]: nf_conntrack: more sanity checks in protocol registration/unregistration
[powerpc.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/skbuff.h>
16 #include <linux/vmalloc.h>
17 #include <linux/stddef.h>
18 #include <linux/err.h>
19 #include <linux/percpu.h>
20 #include <linux/moduleparam.h>
21 #include <linux/notifier.h>
22 #include <linux/kernel.h>
23 #include <linux/netdevice.h>
24
25 #include <net/netfilter/nf_conntrack.h>
26 #include <net/netfilter/nf_conntrack_l3proto.h>
27 #include <net/netfilter/nf_conntrack_l4proto.h>
28 #include <net/netfilter/nf_conntrack_core.h>
29
30 struct nf_conntrack_l4proto **nf_ct_protos[PF_MAX] __read_mostly;
31 struct nf_conntrack_l3proto *nf_ct_l3protos[AF_MAX] __read_mostly;
32
33 struct nf_conntrack_l4proto *
34 __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto)
35 {
36         if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL))
37                 return &nf_conntrack_l4proto_generic;
38
39         return nf_ct_protos[l3proto][l4proto];
40 }
41
42 /* this is guaranteed to always return a valid protocol helper, since
43  * it falls back to generic_protocol */
44 struct nf_conntrack_l4proto *
45 nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto)
46 {
47         struct nf_conntrack_l4proto *p;
48
49         preempt_disable();
50         p = __nf_ct_l4proto_find(l3proto, l4proto);
51         if (!try_module_get(p->me))
52                 p = &nf_conntrack_l4proto_generic;
53         preempt_enable();
54
55         return p;
56 }
57
58 void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p)
59 {
60         module_put(p->me);
61 }
62
63 struct nf_conntrack_l3proto *
64 nf_ct_l3proto_find_get(u_int16_t l3proto)
65 {
66         struct nf_conntrack_l3proto *p;
67
68         preempt_disable();
69         p = __nf_ct_l3proto_find(l3proto);
70         if (!try_module_get(p->me))
71                 p = &nf_conntrack_l3proto_generic;
72         preempt_enable();
73
74         return p;
75 }
76
77 void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p)
78 {
79         module_put(p->me);
80 }
81
82 int
83 nf_ct_l3proto_try_module_get(unsigned short l3proto)
84 {
85         int ret;
86         struct nf_conntrack_l3proto *p;
87
88 retry:  p = nf_ct_l3proto_find_get(l3proto);
89         if (p == &nf_conntrack_l3proto_generic) {
90                 ret = request_module("nf_conntrack-%d", l3proto);
91                 if (!ret)
92                         goto retry;
93
94                 return -EPROTOTYPE;
95         }
96
97         return 0;
98 }
99
100 void nf_ct_l3proto_module_put(unsigned short l3proto)
101 {
102         struct nf_conntrack_l3proto *p;
103
104         preempt_disable();
105         p = __nf_ct_l3proto_find(l3proto);
106         preempt_enable();
107
108         module_put(p->me);
109 }
110
111 static int kill_l3proto(struct nf_conn *i, void *data)
112 {
113         return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num ==
114                         ((struct nf_conntrack_l3proto *)data)->l3proto);
115 }
116
117 static int kill_l4proto(struct nf_conn *i, void *data)
118 {
119         struct nf_conntrack_l4proto *l4proto;
120         l4proto = (struct nf_conntrack_l4proto *)data;
121         return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum ==
122                         l4proto->l4proto) &&
123                (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num ==
124                         l4proto->l3proto);
125 }
126
127 int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto)
128 {
129         int ret = 0;
130
131         if (proto->l3proto >= AF_MAX) {
132                 ret = -EBUSY;
133                 goto out;
134         }
135
136         write_lock_bh(&nf_conntrack_lock);
137         if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_l3proto_generic) {
138                 ret = -EBUSY;
139                 goto out_unlock;
140         }
141         nf_ct_l3protos[proto->l3proto] = proto;
142
143 out_unlock:
144         write_unlock_bh(&nf_conntrack_lock);
145 out:
146         return ret;
147 }
148
149 int nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto)
150 {
151         int ret = 0;
152
153         if (proto->l3proto >= AF_MAX) {
154                 ret = -EBUSY;
155                 goto out;
156         }
157
158         write_lock_bh(&nf_conntrack_lock);
159         if (nf_ct_l3protos[proto->l3proto] != proto) {
160                 write_unlock_bh(&nf_conntrack_lock);
161                 ret = -EBUSY;
162                 goto out;
163         }
164
165         nf_ct_l3protos[proto->l3proto] = &nf_conntrack_l3proto_generic;
166         write_unlock_bh(&nf_conntrack_lock);
167
168         /* Somebody could be still looking at the proto in bh. */
169         synchronize_net();
170
171         /* Remove all contrack entries for this protocol */
172         nf_ct_iterate_cleanup(kill_l3proto, proto);
173
174 out:
175         return ret;
176 }
177
178 /* FIXME: Allow NULL functions and sub in pointers to generic for
179    them. --RR */
180 int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto)
181 {
182         int ret = 0;
183
184         if (l4proto->l3proto >= PF_MAX) {
185                 ret = -EBUSY;
186                 goto out;
187         }
188
189 retry:
190         write_lock_bh(&nf_conntrack_lock);
191         if (nf_ct_protos[l4proto->l3proto]) {
192                 if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto]
193                                 != &nf_conntrack_l4proto_generic) {
194                         ret = -EBUSY;
195                         goto out_unlock;
196                 }
197         } else {
198                 /* l3proto may be loaded latter. */
199                 struct nf_conntrack_l4proto **proto_array;
200                 int i;
201
202                 write_unlock_bh(&nf_conntrack_lock);
203
204                 proto_array = (struct nf_conntrack_l4proto **)
205                                 kmalloc(MAX_NF_CT_PROTO *
206                                          sizeof(struct nf_conntrack_l4proto *),
207                                         GFP_KERNEL);
208                 if (proto_array == NULL) {
209                         ret = -ENOMEM;
210                         goto out;
211                 }
212                 for (i = 0; i < MAX_NF_CT_PROTO; i++)
213                         proto_array[i] = &nf_conntrack_l4proto_generic;
214
215                 write_lock_bh(&nf_conntrack_lock);
216                 if (nf_ct_protos[l4proto->l3proto]) {
217                         /* bad timing, but no problem */
218                         write_unlock_bh(&nf_conntrack_lock);
219                         kfree(proto_array);
220                 } else {
221                         nf_ct_protos[l4proto->l3proto] = proto_array;
222                         write_unlock_bh(&nf_conntrack_lock);
223                 }
224
225                 /*
226                  * Just once because array is never freed until unloading
227                  * nf_conntrack.ko
228                  */
229                 goto retry;
230         }
231
232         nf_ct_protos[l4proto->l3proto][l4proto->l4proto] = l4proto;
233
234 out_unlock:
235         write_unlock_bh(&nf_conntrack_lock);
236 out:
237         return ret;
238 }
239
240 int nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto)
241 {
242         int ret = 0;
243
244         if (l4proto->l3proto >= PF_MAX) {
245                 ret = -EBUSY;
246                 goto out;
247         }
248
249         write_lock_bh(&nf_conntrack_lock);
250         if (nf_ct_protos[l4proto->l3proto][l4proto->l4proto]
251             != l4proto) {
252                 write_unlock_bh(&nf_conntrack_lock);
253                 ret = -EBUSY;
254                 goto out;
255         }
256         nf_ct_protos[l4proto->l3proto][l4proto->l4proto]
257                 = &nf_conntrack_l4proto_generic;
258         write_unlock_bh(&nf_conntrack_lock);
259
260         /* Somebody could be still looking at the proto in bh. */
261         synchronize_net();
262
263         /* Remove all contrack entries for this protocol */
264         nf_ct_iterate_cleanup(kill_l4proto, l4proto);
265
266 out:
267         return ret;
268 }