Blame view
net/netfilter/nf_conntrack_proto.c
9.77 KB
8f03dea52 [NETFILTER]: nf_c... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* L3/L4 protocol support for nf_conntrack. */ /* (C) 1999-2001 Paul `Rusty' Russell * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/types.h> #include <linux/netfilter.h> #include <linux/module.h> |
5a0e3ad6a include cleanup: ... |
15 |
#include <linux/slab.h> |
d62f9ed4a [NETFILTER]: nf_c... |
16 |
#include <linux/mutex.h> |
8f03dea52 [NETFILTER]: nf_c... |
17 18 19 20 |
#include <linux/vmalloc.h> #include <linux/stddef.h> #include <linux/err.h> #include <linux/percpu.h> |
8f03dea52 [NETFILTER]: nf_c... |
21 22 23 |
#include <linux/notifier.h> #include <linux/kernel.h> #include <linux/netdevice.h> |
efb9a8c28 netfilter: netns ... |
24 |
#include <linux/rtnetlink.h> |
8f03dea52 [NETFILTER]: nf_c... |
25 26 27 |
#include <net/netfilter/nf_conntrack.h> #include <net/netfilter/nf_conntrack_l3proto.h> |
605dcad6c [NETFILTER]: nf_c... |
28 |
#include <net/netfilter/nf_conntrack_l4proto.h> |
8f03dea52 [NETFILTER]: nf_c... |
29 |
#include <net/netfilter/nf_conntrack_core.h> |
0906a372f net/netfilter: __... |
30 31 |
static struct nf_conntrack_l4proto __rcu **nf_ct_protos[PF_MAX] __read_mostly; struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX] __read_mostly; |
13b183391 [NETFILTER]: nf_c... |
32 |
EXPORT_SYMBOL_GPL(nf_ct_l3protos); |
8f03dea52 [NETFILTER]: nf_c... |
33 |
|
b19caa0ca [NETFILTER]: nf_c... |
34 |
static DEFINE_MUTEX(nf_ct_proto_mutex); |
d62f9ed4a [NETFILTER]: nf_c... |
35 |
|
b19caa0ca [NETFILTER]: nf_c... |
36 |
#ifdef CONFIG_SYSCTL |
d62f9ed4a [NETFILTER]: nf_c... |
37 |
static int |
b3fd3ffe3 [NETFILTER]: Use ... |
38 |
nf_ct_register_sysctl(struct ctl_table_header **header, struct ctl_path *path, |
d62f9ed4a [NETFILTER]: nf_c... |
39 40 41 |
struct ctl_table *table, unsigned int *users) { if (*header == NULL) { |
b3fd3ffe3 [NETFILTER]: Use ... |
42 |
*header = register_sysctl_paths(path, table); |
d62f9ed4a [NETFILTER]: nf_c... |
43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
if (*header == NULL) return -ENOMEM; } if (users != NULL) (*users)++; return 0; } static void nf_ct_unregister_sysctl(struct ctl_table_header **header, struct ctl_table *table, unsigned int *users) { if (users != NULL && --*users > 0) return; |
b3fd3ffe3 [NETFILTER]: Use ... |
57 58 |
unregister_sysctl_table(*header); |
d62f9ed4a [NETFILTER]: nf_c... |
59 60 61 |
*header = NULL; } #endif |
605dcad6c [NETFILTER]: nf_c... |
62 63 |
struct nf_conntrack_l4proto * __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto) |
8f03dea52 [NETFILTER]: nf_c... |
64 65 |
{ if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL)) |
605dcad6c [NETFILTER]: nf_c... |
66 |
return &nf_conntrack_l4proto_generic; |
8f03dea52 [NETFILTER]: nf_c... |
67 |
|
923f4902f [NETFILTER]: nf_c... |
68 |
return rcu_dereference(nf_ct_protos[l3proto][l4proto]); |
8f03dea52 [NETFILTER]: nf_c... |
69 |
} |
13b183391 [NETFILTER]: nf_c... |
70 |
EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find); |
8f03dea52 [NETFILTER]: nf_c... |
71 72 73 |
/* this is guaranteed to always return a valid protocol helper, since * it falls back to generic_protocol */ |
8f03dea52 [NETFILTER]: nf_c... |
74 75 76 77 |
struct nf_conntrack_l3proto * nf_ct_l3proto_find_get(u_int16_t l3proto) { struct nf_conntrack_l3proto *p; |
923f4902f [NETFILTER]: nf_c... |
78 |
rcu_read_lock(); |
8f03dea52 [NETFILTER]: nf_c... |
79 80 |
p = __nf_ct_l3proto_find(l3proto); if (!try_module_get(p->me)) |
605dcad6c [NETFILTER]: nf_c... |
81 |
p = &nf_conntrack_l3proto_generic; |
923f4902f [NETFILTER]: nf_c... |
82 |
rcu_read_unlock(); |
8f03dea52 [NETFILTER]: nf_c... |
83 84 85 |
return p; } |
13b183391 [NETFILTER]: nf_c... |
86 |
EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get); |
8f03dea52 [NETFILTER]: nf_c... |
87 88 89 90 91 |
void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p) { module_put(p->me); } |
13b183391 [NETFILTER]: nf_c... |
92 |
EXPORT_SYMBOL_GPL(nf_ct_l3proto_put); |
8f03dea52 [NETFILTER]: nf_c... |
93 94 95 96 97 98 99 100 |
int nf_ct_l3proto_try_module_get(unsigned short l3proto) { int ret; struct nf_conntrack_l3proto *p; retry: p = nf_ct_l3proto_find_get(l3proto); |
605dcad6c [NETFILTER]: nf_c... |
101 |
if (p == &nf_conntrack_l3proto_generic) { |
8f03dea52 [NETFILTER]: nf_c... |
102 103 104 105 106 107 108 109 110 |
ret = request_module("nf_conntrack-%d", l3proto); if (!ret) goto retry; return -EPROTOTYPE; } return 0; } |
13b183391 [NETFILTER]: nf_c... |
111 |
EXPORT_SYMBOL_GPL(nf_ct_l3proto_try_module_get); |
8f03dea52 [NETFILTER]: nf_c... |
112 113 114 115 |
void nf_ct_l3proto_module_put(unsigned short l3proto) { struct nf_conntrack_l3proto *p; |
3b254c54e netfilter: nf_con... |
116 117 118 119 |
/* rcu_read_lock not necessary since the caller holds a reference, but * taken anyways to avoid lockdep warnings in __nf_ct_l3proto_find() */ rcu_read_lock(); |
8f03dea52 [NETFILTER]: nf_c... |
120 |
p = __nf_ct_l3proto_find(l3proto); |
8f03dea52 [NETFILTER]: nf_c... |
121 |
module_put(p->me); |
3b254c54e netfilter: nf_con... |
122 |
rcu_read_unlock(); |
8f03dea52 [NETFILTER]: nf_c... |
123 |
} |
13b183391 [NETFILTER]: nf_c... |
124 |
EXPORT_SYMBOL_GPL(nf_ct_l3proto_module_put); |
8f03dea52 [NETFILTER]: nf_c... |
125 126 127 |
static int kill_l3proto(struct nf_conn *i, void *data) { |
5e8fbe2ac [NETFILTER]: nf_c... |
128 |
return nf_ct_l3num(i) == ((struct nf_conntrack_l3proto *)data)->l3proto; |
8f03dea52 [NETFILTER]: nf_c... |
129 |
} |
605dcad6c [NETFILTER]: nf_c... |
130 |
static int kill_l4proto(struct nf_conn *i, void *data) |
8f03dea52 [NETFILTER]: nf_c... |
131 |
{ |
605dcad6c [NETFILTER]: nf_c... |
132 133 |
struct nf_conntrack_l4proto *l4proto; l4proto = (struct nf_conntrack_l4proto *)data; |
5e8fbe2ac [NETFILTER]: nf_c... |
134 135 |
return nf_ct_protonum(i) == l4proto->l4proto && nf_ct_l3num(i) == l4proto->l3proto; |
8f03dea52 [NETFILTER]: nf_c... |
136 |
} |
d62f9ed4a [NETFILTER]: nf_c... |
137 138 139 140 141 |
static int nf_ct_l3proto_register_sysctl(struct nf_conntrack_l3proto *l3proto) { int err = 0; #ifdef CONFIG_SYSCTL |
d62f9ed4a [NETFILTER]: nf_c... |
142 143 144 145 146 |
if (l3proto->ctl_table != NULL) { err = nf_ct_register_sysctl(&l3proto->ctl_table_header, l3proto->ctl_table_path, l3proto->ctl_table, NULL); } |
d62f9ed4a [NETFILTER]: nf_c... |
147 148 149 150 151 152 153 |
#endif return err; } static void nf_ct_l3proto_unregister_sysctl(struct nf_conntrack_l3proto *l3proto) { #ifdef CONFIG_SYSCTL |
d62f9ed4a [NETFILTER]: nf_c... |
154 155 156 |
if (l3proto->ctl_table_header != NULL) nf_ct_unregister_sysctl(&l3proto->ctl_table_header, l3proto->ctl_table, NULL); |
d62f9ed4a [NETFILTER]: nf_c... |
157 158 |
#endif } |
8f03dea52 [NETFILTER]: nf_c... |
159 160 161 |
int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) { int ret = 0; |
0e60ebe04 netfilter: add __... |
162 |
struct nf_conntrack_l3proto *old; |
8f03dea52 [NETFILTER]: nf_c... |
163 |
|
0661cca9c [NETFILTER]: nf_c... |
164 165 |
if (proto->l3proto >= AF_MAX) return -EBUSY; |
ae5718fb3 [NETFILTER]: nf_c... |
166 |
|
d0dba7255 netfilter: ctnetl... |
167 168 |
if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size) return -EINVAL; |
b19caa0ca [NETFILTER]: nf_c... |
169 |
mutex_lock(&nf_ct_proto_mutex); |
0e60ebe04 netfilter: add __... |
170 171 172 |
old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], lockdep_is_held(&nf_ct_proto_mutex)); if (old != &nf_conntrack_l3proto_generic) { |
8f03dea52 [NETFILTER]: nf_c... |
173 |
ret = -EBUSY; |
ae5718fb3 [NETFILTER]: nf_c... |
174 |
goto out_unlock; |
8f03dea52 [NETFILTER]: nf_c... |
175 |
} |
d62f9ed4a [NETFILTER]: nf_c... |
176 177 178 |
ret = nf_ct_l3proto_register_sysctl(proto); if (ret < 0) |
0661cca9c [NETFILTER]: nf_c... |
179 |
goto out_unlock; |
d0dba7255 netfilter: ctnetl... |
180 181 |
if (proto->nlattr_tuple_size) proto->nla_size = 3 * proto->nlattr_tuple_size(); |
0661cca9c [NETFILTER]: nf_c... |
182 |
rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); |
8f03dea52 [NETFILTER]: nf_c... |
183 |
|
ae5718fb3 [NETFILTER]: nf_c... |
184 |
out_unlock: |
b19caa0ca [NETFILTER]: nf_c... |
185 |
mutex_unlock(&nf_ct_proto_mutex); |
8f03dea52 [NETFILTER]: nf_c... |
186 187 |
return ret; } |
13b183391 [NETFILTER]: nf_c... |
188 |
EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_register); |
8f03dea52 [NETFILTER]: nf_c... |
189 |
|
fe3eb20c1 [NETFILTER]: nf_c... |
190 |
void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto) |
8f03dea52 [NETFILTER]: nf_c... |
191 |
{ |
678d66753 netfilter: netns ... |
192 |
struct net *net; |
fe3eb20c1 [NETFILTER]: nf_c... |
193 |
BUG_ON(proto->l3proto >= AF_MAX); |
ae5718fb3 [NETFILTER]: nf_c... |
194 |
|
b19caa0ca [NETFILTER]: nf_c... |
195 |
mutex_lock(&nf_ct_proto_mutex); |
0e60ebe04 netfilter: add __... |
196 197 198 |
BUG_ON(rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], lockdep_is_held(&nf_ct_proto_mutex) ) != proto); |
923f4902f [NETFILTER]: nf_c... |
199 200 |
rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], &nf_conntrack_l3proto_generic); |
0661cca9c [NETFILTER]: nf_c... |
201 |
nf_ct_l3proto_unregister_sysctl(proto); |
b19caa0ca [NETFILTER]: nf_c... |
202 |
mutex_unlock(&nf_ct_proto_mutex); |
8f03dea52 [NETFILTER]: nf_c... |
203 |
|
0661cca9c [NETFILTER]: nf_c... |
204 |
synchronize_rcu(); |
d62f9ed4a [NETFILTER]: nf_c... |
205 |
|
8f03dea52 [NETFILTER]: nf_c... |
206 |
/* Remove all contrack entries for this protocol */ |
efb9a8c28 netfilter: netns ... |
207 |
rtnl_lock(); |
678d66753 netfilter: netns ... |
208 209 |
for_each_net(net) nf_ct_iterate_cleanup(net, kill_l3proto, proto); |
efb9a8c28 netfilter: netns ... |
210 |
rtnl_unlock(); |
8f03dea52 [NETFILTER]: nf_c... |
211 |
} |
13b183391 [NETFILTER]: nf_c... |
212 |
EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_unregister); |
8f03dea52 [NETFILTER]: nf_c... |
213 |
|
d62f9ed4a [NETFILTER]: nf_c... |
214 215 216 217 218 |
static int nf_ct_l4proto_register_sysctl(struct nf_conntrack_l4proto *l4proto) { int err = 0; #ifdef CONFIG_SYSCTL |
d62f9ed4a [NETFILTER]: nf_c... |
219 220 221 222 223 |
if (l4proto->ctl_table != NULL) { err = nf_ct_register_sysctl(l4proto->ctl_table_header, nf_net_netfilter_sysctl_path, l4proto->ctl_table, l4proto->ctl_table_users); |
a999e6837 [NETFILTER]: nf_c... |
224 225 |
if (err < 0) goto out; |
d62f9ed4a [NETFILTER]: nf_c... |
226 |
} |
a999e6837 [NETFILTER]: nf_c... |
227 228 229 230 231 232 233 234 235 236 237 238 239 |
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT if (l4proto->ctl_compat_table != NULL) { err = nf_ct_register_sysctl(&l4proto->ctl_compat_table_header, nf_net_ipv4_netfilter_sysctl_path, l4proto->ctl_compat_table, NULL); if (err == 0) goto out; nf_ct_unregister_sysctl(l4proto->ctl_table_header, l4proto->ctl_table, l4proto->ctl_table_users); } #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ out: |
933a41e7e [NETFILTER]: nf_c... |
240 |
#endif /* CONFIG_SYSCTL */ |
d62f9ed4a [NETFILTER]: nf_c... |
241 242 243 244 245 246 |
return err; } static void nf_ct_l4proto_unregister_sysctl(struct nf_conntrack_l4proto *l4proto) { #ifdef CONFIG_SYSCTL |
d62f9ed4a [NETFILTER]: nf_c... |
247 248 249 250 251 |
if (l4proto->ctl_table_header != NULL && *l4proto->ctl_table_header != NULL) nf_ct_unregister_sysctl(l4proto->ctl_table_header, l4proto->ctl_table, l4proto->ctl_table_users); |
a999e6837 [NETFILTER]: nf_c... |
252 253 254 255 256 |
#ifdef CONFIG_NF_CONNTRACK_PROC_COMPAT if (l4proto->ctl_compat_table_header != NULL) nf_ct_unregister_sysctl(&l4proto->ctl_compat_table_header, l4proto->ctl_compat_table, NULL); #endif /* CONFIG_NF_CONNTRACK_PROC_COMPAT */ |
933a41e7e [NETFILTER]: nf_c... |
257 |
#endif /* CONFIG_SYSCTL */ |
d62f9ed4a [NETFILTER]: nf_c... |
258 |
} |
8f03dea52 [NETFILTER]: nf_c... |
259 260 |
/* FIXME: Allow NULL functions and sub in pointers to generic for them. --RR */ |
605dcad6c [NETFILTER]: nf_c... |
261 |
int nf_conntrack_l4proto_register(struct nf_conntrack_l4proto *l4proto) |
8f03dea52 [NETFILTER]: nf_c... |
262 263 |
{ int ret = 0; |
0661cca9c [NETFILTER]: nf_c... |
264 265 |
if (l4proto->l3proto >= PF_MAX) return -EBUSY; |
ae5718fb3 [NETFILTER]: nf_c... |
266 |
|
d0dba7255 netfilter: ctnetl... |
267 268 269 |
if ((l4proto->to_nlattr && !l4proto->nlattr_size) || (l4proto->tuple_to_nlattr && !l4proto->nlattr_tuple_size)) return -EINVAL; |
b19caa0ca [NETFILTER]: nf_c... |
270 |
mutex_lock(&nf_ct_proto_mutex); |
c6a1e615d [NETFILTER]: nf_c... |
271 |
if (!nf_ct_protos[l4proto->l3proto]) { |
8f03dea52 [NETFILTER]: nf_c... |
272 |
/* l3proto may be loaded latter. */ |
c5d277d29 netfilter: rcu sp... |
273 |
struct nf_conntrack_l4proto __rcu **proto_array; |
8f03dea52 [NETFILTER]: nf_c... |
274 |
int i; |
c6a1e615d [NETFILTER]: nf_c... |
275 276 277 |
proto_array = kmalloc(MAX_NF_CT_PROTO * sizeof(struct nf_conntrack_l4proto *), GFP_KERNEL); |
8f03dea52 [NETFILTER]: nf_c... |
278 279 |
if (proto_array == NULL) { ret = -ENOMEM; |
b19caa0ca [NETFILTER]: nf_c... |
280 |
goto out_unlock; |
8f03dea52 [NETFILTER]: nf_c... |
281 |
} |
c6a1e615d [NETFILTER]: nf_c... |
282 |
|
8f03dea52 [NETFILTER]: nf_c... |
283 |
for (i = 0; i < MAX_NF_CT_PROTO; i++) |
c5d277d29 netfilter: rcu sp... |
284 |
RCU_INIT_POINTER(proto_array[i], &nf_conntrack_l4proto_generic); |
d817d29d0 netfilter: fix nf... |
285 286 287 288 289 |
/* Before making proto_array visible to lockless readers, * we must make sure its content is committed to memory. */ smp_wmb(); |
c6a1e615d [NETFILTER]: nf_c... |
290 |
nf_ct_protos[l4proto->l3proto] = proto_array; |
0e60ebe04 netfilter: add __... |
291 292 293 294 |
} else if (rcu_dereference_protected( nf_ct_protos[l4proto->l3proto][l4proto->l4proto], lockdep_is_held(&nf_ct_proto_mutex) ) != &nf_conntrack_l4proto_generic) { |
c6a1e615d [NETFILTER]: nf_c... |
295 296 |
ret = -EBUSY; goto out_unlock; |
8f03dea52 [NETFILTER]: nf_c... |
297 |
} |
d62f9ed4a [NETFILTER]: nf_c... |
298 299 |
ret = nf_ct_l4proto_register_sysctl(l4proto); if (ret < 0) |
0661cca9c [NETFILTER]: nf_c... |
300 |
goto out_unlock; |
d0dba7255 netfilter: ctnetl... |
301 302 303 304 305 |
l4proto->nla_size = 0; if (l4proto->nlattr_size) l4proto->nla_size += l4proto->nlattr_size(); if (l4proto->nlattr_tuple_size) l4proto->nla_size += 3 * l4proto->nlattr_tuple_size(); |
c6a1e615d [NETFILTER]: nf_c... |
306 307 |
rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], l4proto); |
8f03dea52 [NETFILTER]: nf_c... |
308 309 |
out_unlock: |
b19caa0ca [NETFILTER]: nf_c... |
310 |
mutex_unlock(&nf_ct_proto_mutex); |
8f03dea52 [NETFILTER]: nf_c... |
311 312 |
return ret; } |
13b183391 [NETFILTER]: nf_c... |
313 |
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_register); |
8f03dea52 [NETFILTER]: nf_c... |
314 |
|
fe3eb20c1 [NETFILTER]: nf_c... |
315 |
void nf_conntrack_l4proto_unregister(struct nf_conntrack_l4proto *l4proto) |
8f03dea52 [NETFILTER]: nf_c... |
316 |
{ |
678d66753 netfilter: netns ... |
317 |
struct net *net; |
fe3eb20c1 [NETFILTER]: nf_c... |
318 |
BUG_ON(l4proto->l3proto >= PF_MAX); |
ae5718fb3 [NETFILTER]: nf_c... |
319 |
|
b19caa0ca [NETFILTER]: nf_c... |
320 |
mutex_lock(&nf_ct_proto_mutex); |
0e60ebe04 netfilter: add __... |
321 322 323 324 |
BUG_ON(rcu_dereference_protected( nf_ct_protos[l4proto->l3proto][l4proto->l4proto], lockdep_is_held(&nf_ct_proto_mutex) ) != l4proto); |
923f4902f [NETFILTER]: nf_c... |
325 326 |
rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], &nf_conntrack_l4proto_generic); |
0661cca9c [NETFILTER]: nf_c... |
327 |
nf_ct_l4proto_unregister_sysctl(l4proto); |
b19caa0ca [NETFILTER]: nf_c... |
328 |
mutex_unlock(&nf_ct_proto_mutex); |
8f03dea52 [NETFILTER]: nf_c... |
329 |
|
0661cca9c [NETFILTER]: nf_c... |
330 |
synchronize_rcu(); |
d62f9ed4a [NETFILTER]: nf_c... |
331 |
|
8f03dea52 [NETFILTER]: nf_c... |
332 |
/* Remove all contrack entries for this protocol */ |
efb9a8c28 netfilter: netns ... |
333 |
rtnl_lock(); |
678d66753 netfilter: netns ... |
334 335 |
for_each_net(net) nf_ct_iterate_cleanup(net, kill_l4proto, l4proto); |
efb9a8c28 netfilter: netns ... |
336 |
rtnl_unlock(); |
8f03dea52 [NETFILTER]: nf_c... |
337 |
} |
13b183391 [NETFILTER]: nf_c... |
338 |
EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_unregister); |
ac5357eba [NETFILTER]: nf_c... |
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 |
int nf_conntrack_proto_init(void) { unsigned int i; int err; err = nf_ct_l4proto_register_sysctl(&nf_conntrack_l4proto_generic); if (err < 0) return err; for (i = 0; i < AF_MAX; i++) rcu_assign_pointer(nf_ct_l3protos[i], &nf_conntrack_l3proto_generic); return 0; } void nf_conntrack_proto_fini(void) { unsigned int i; nf_ct_l4proto_unregister_sysctl(&nf_conntrack_l4proto_generic); /* free l3proto protocol tables */ for (i = 0; i < PF_MAX; i++) kfree(nf_ct_protos[i]); } |