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