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> | |
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_protocol.h> | |
28 | #include <net/netfilter/nf_conntrack_core.h> | |
29 | ||
30 | struct nf_conntrack_protocol **nf_ct_protos[PF_MAX] __read_mostly; | |
31 | struct nf_conntrack_l3proto *nf_ct_l3protos[PF_MAX] __read_mostly; | |
32 | ||
33 | struct nf_conntrack_protocol * | |
34 | __nf_ct_proto_find(u_int16_t l3proto, u_int8_t protocol) | |
35 | { | |
36 | if (unlikely(l3proto >= AF_MAX || nf_ct_protos[l3proto] == NULL)) | |
37 | return &nf_conntrack_generic_protocol; | |
38 | ||
39 | return nf_ct_protos[l3proto][protocol]; | |
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_protocol * | |
45 | nf_ct_proto_find_get(u_int16_t l3proto, u_int8_t protocol) | |
46 | { | |
47 | struct nf_conntrack_protocol *p; | |
48 | ||
49 | preempt_disable(); | |
50 | p = __nf_ct_proto_find(l3proto, protocol); | |
51 | if (!try_module_get(p->me)) | |
52 | p = &nf_conntrack_generic_protocol; | |
53 | preempt_enable(); | |
54 | ||
55 | return p; | |
56 | } | |
57 | ||
58 | void nf_ct_proto_put(struct nf_conntrack_protocol *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_generic_l3proto; | |
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_generic_l3proto) { | |
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_proto(struct nf_conn *i, void *data) | |
118 | { | |
119 | struct nf_conntrack_protocol *proto; | |
120 | proto = (struct nf_conntrack_protocol *)data; | |
121 | return (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.protonum == | |
122 | proto->proto) && | |
123 | (i->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num == | |
124 | proto->l3proto); | |
125 | } | |
126 | ||
127 | int nf_conntrack_l3proto_register(struct nf_conntrack_l3proto *proto) | |
128 | { | |
129 | int ret = 0; | |
130 | ||
131 | write_lock_bh(&nf_conntrack_lock); | |
132 | if (nf_ct_l3protos[proto->l3proto] != &nf_conntrack_generic_l3proto) { | |
133 | ret = -EBUSY; | |
134 | goto out; | |
135 | } | |
136 | nf_ct_l3protos[proto->l3proto] = proto; | |
137 | out: | |
138 | write_unlock_bh(&nf_conntrack_lock); | |
139 | ||
140 | return ret; | |
141 | } | |
142 | ||
143 | void nf_conntrack_l3proto_unregister(struct nf_conntrack_l3proto *proto) | |
144 | { | |
145 | write_lock_bh(&nf_conntrack_lock); | |
146 | nf_ct_l3protos[proto->l3proto] = &nf_conntrack_generic_l3proto; | |
147 | write_unlock_bh(&nf_conntrack_lock); | |
148 | ||
149 | /* Somebody could be still looking at the proto in bh. */ | |
150 | synchronize_net(); | |
151 | ||
152 | /* Remove all contrack entries for this protocol */ | |
153 | nf_ct_iterate_cleanup(kill_l3proto, proto); | |
154 | } | |
155 | ||
156 | /* FIXME: Allow NULL functions and sub in pointers to generic for | |
157 | them. --RR */ | |
158 | int nf_conntrack_protocol_register(struct nf_conntrack_protocol *proto) | |
159 | { | |
160 | int ret = 0; | |
161 | ||
162 | retry: | |
163 | write_lock_bh(&nf_conntrack_lock); | |
164 | if (nf_ct_protos[proto->l3proto]) { | |
165 | if (nf_ct_protos[proto->l3proto][proto->proto] | |
166 | != &nf_conntrack_generic_protocol) { | |
167 | ret = -EBUSY; | |
168 | goto out_unlock; | |
169 | } | |
170 | } else { | |
171 | /* l3proto may be loaded latter. */ | |
172 | struct nf_conntrack_protocol **proto_array; | |
173 | int i; | |
174 | ||
175 | write_unlock_bh(&nf_conntrack_lock); | |
176 | ||
177 | proto_array = (struct nf_conntrack_protocol **) | |
178 | kmalloc(MAX_NF_CT_PROTO * | |
179 | sizeof(struct nf_conntrack_protocol *), | |
180 | GFP_KERNEL); | |
181 | if (proto_array == NULL) { | |
182 | ret = -ENOMEM; | |
183 | goto out; | |
184 | } | |
185 | for (i = 0; i < MAX_NF_CT_PROTO; i++) | |
186 | proto_array[i] = &nf_conntrack_generic_protocol; | |
187 | ||
188 | write_lock_bh(&nf_conntrack_lock); | |
189 | if (nf_ct_protos[proto->l3proto]) { | |
190 | /* bad timing, but no problem */ | |
191 | write_unlock_bh(&nf_conntrack_lock); | |
192 | kfree(proto_array); | |
193 | } else { | |
194 | nf_ct_protos[proto->l3proto] = proto_array; | |
195 | write_unlock_bh(&nf_conntrack_lock); | |
196 | } | |
197 | ||
198 | /* | |
199 | * Just once because array is never freed until unloading | |
200 | * nf_conntrack.ko | |
201 | */ | |
202 | goto retry; | |
203 | } | |
204 | ||
205 | nf_ct_protos[proto->l3proto][proto->proto] = proto; | |
206 | ||
207 | out_unlock: | |
208 | write_unlock_bh(&nf_conntrack_lock); | |
209 | out: | |
210 | return ret; | |
211 | } | |
212 | ||
213 | void nf_conntrack_protocol_unregister(struct nf_conntrack_protocol *proto) | |
214 | { | |
215 | write_lock_bh(&nf_conntrack_lock); | |
216 | nf_ct_protos[proto->l3proto][proto->proto] | |
217 | = &nf_conntrack_generic_protocol; | |
218 | write_unlock_bh(&nf_conntrack_lock); | |
219 | ||
220 | /* Somebody could be still looking at the proto in bh. */ | |
221 | synchronize_net(); | |
222 | ||
223 | /* Remove all contrack entries for this protocol */ | |
224 | nf_ct_iterate_cleanup(kill_proto, proto); | |
225 | } |