]>
Commit | Line | Data |
---|---|---|
f6ebe77f HW |
1 | #include <linux/config.h> |
2 | #include <linux/kernel.h> | |
3 | #include <linux/init.h> | |
4 | #include <linux/module.h> | |
5 | #include <linux/proc_fs.h> | |
6 | #include <linux/skbuff.h> | |
7 | #include <linux/netfilter.h> | |
bbd86b9f | 8 | #include <linux/seq_file.h> |
7a11b984 | 9 | #include <linux/rcupdate.h> |
f6ebe77f HW |
10 | #include <net/protocol.h> |
11 | ||
12 | #include "nf_internals.h" | |
13 | ||
14 | /* | |
15 | * A queue handler may be registered for each protocol. Each is protected by | |
16 | * long term mutex. The handler must provide an an outfn() to accept packets | |
17 | * for queueing and must reinject all packets it receives, no matter what. | |
18 | */ | |
bbd86b9f | 19 | static struct nf_queue_handler *queue_handler[NPROTO]; |
e02f7d16 | 20 | static struct nf_queue_rerouter *queue_rerouter[NPROTO]; |
f6ebe77f HW |
21 | |
22 | static DEFINE_RWLOCK(queue_handler_lock); | |
23 | ||
d72367b6 HW |
24 | /* return EBUSY when somebody else is registered, return EEXIST if the |
25 | * same handler is registered, return 0 in case of success. */ | |
bbd86b9f | 26 | int nf_register_queue_handler(int pf, struct nf_queue_handler *qh) |
f6ebe77f HW |
27 | { |
28 | int ret; | |
29 | ||
30 | if (pf >= NPROTO) | |
31 | return -EINVAL; | |
32 | ||
33 | write_lock_bh(&queue_handler_lock); | |
d72367b6 HW |
34 | if (queue_handler[pf] == qh) |
35 | ret = -EEXIST; | |
36 | else if (queue_handler[pf]) | |
f6ebe77f HW |
37 | ret = -EBUSY; |
38 | else { | |
bbd86b9f | 39 | queue_handler[pf] = qh; |
f6ebe77f HW |
40 | ret = 0; |
41 | } | |
42 | write_unlock_bh(&queue_handler_lock); | |
43 | ||
44 | return ret; | |
45 | } | |
46 | EXPORT_SYMBOL(nf_register_queue_handler); | |
47 | ||
48 | /* The caller must flush their queue before this */ | |
49 | int nf_unregister_queue_handler(int pf) | |
50 | { | |
51 | if (pf >= NPROTO) | |
52 | return -EINVAL; | |
53 | ||
54 | write_lock_bh(&queue_handler_lock); | |
bbd86b9f | 55 | queue_handler[pf] = NULL; |
f6ebe77f HW |
56 | write_unlock_bh(&queue_handler_lock); |
57 | ||
58 | return 0; | |
59 | } | |
60 | EXPORT_SYMBOL(nf_unregister_queue_handler); | |
61 | ||
62 | int nf_register_queue_rerouter(int pf, struct nf_queue_rerouter *rer) | |
63 | { | |
64 | if (pf >= NPROTO) | |
65 | return -EINVAL; | |
66 | ||
67 | write_lock_bh(&queue_handler_lock); | |
7a11b984 | 68 | rcu_assign_pointer(queue_rerouter[pf], rer); |
f6ebe77f HW |
69 | write_unlock_bh(&queue_handler_lock); |
70 | ||
71 | return 0; | |
72 | } | |
73 | EXPORT_SYMBOL_GPL(nf_register_queue_rerouter); | |
74 | ||
75 | int nf_unregister_queue_rerouter(int pf) | |
76 | { | |
77 | if (pf >= NPROTO) | |
78 | return -EINVAL; | |
79 | ||
80 | write_lock_bh(&queue_handler_lock); | |
7a11b984 | 81 | rcu_assign_pointer(queue_rerouter[pf], NULL); |
f6ebe77f | 82 | write_unlock_bh(&queue_handler_lock); |
7a11b984 | 83 | synchronize_rcu(); |
f6ebe77f HW |
84 | return 0; |
85 | } | |
86 | EXPORT_SYMBOL_GPL(nf_unregister_queue_rerouter); | |
87 | ||
bbd86b9f | 88 | void nf_unregister_queue_handlers(struct nf_queue_handler *qh) |
f6ebe77f HW |
89 | { |
90 | int pf; | |
91 | ||
92 | write_lock_bh(&queue_handler_lock); | |
93 | for (pf = 0; pf < NPROTO; pf++) { | |
bbd86b9f HW |
94 | if (queue_handler[pf] == qh) |
95 | queue_handler[pf] = NULL; | |
f6ebe77f HW |
96 | } |
97 | write_unlock_bh(&queue_handler_lock); | |
98 | } | |
99 | EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers); | |
100 | ||
101 | /* | |
102 | * Any packet that leaves via this function must come back | |
103 | * through nf_reinject(). | |
104 | */ | |
105 | int nf_queue(struct sk_buff **skb, | |
106 | struct list_head *elem, | |
107 | int pf, unsigned int hook, | |
108 | struct net_device *indev, | |
109 | struct net_device *outdev, | |
110 | int (*okfn)(struct sk_buff *), | |
111 | unsigned int queuenum) | |
112 | { | |
113 | int status; | |
114 | struct nf_info *info; | |
115 | #ifdef CONFIG_BRIDGE_NETFILTER | |
116 | struct net_device *physindev = NULL; | |
117 | struct net_device *physoutdev = NULL; | |
118 | #endif | |
7a11b984 | 119 | struct nf_queue_rerouter *rerouter; |
f6ebe77f HW |
120 | |
121 | /* QUEUE == DROP if noone is waiting, to be safe. */ | |
122 | read_lock(&queue_handler_lock); | |
e121e9ec | 123 | if (!queue_handler[pf]) { |
f6ebe77f HW |
124 | read_unlock(&queue_handler_lock); |
125 | kfree_skb(*skb); | |
126 | return 1; | |
127 | } | |
128 | ||
e02f7d16 | 129 | info = kmalloc(sizeof(*info)+queue_rerouter[pf]->rer_size, GFP_ATOMIC); |
f6ebe77f HW |
130 | if (!info) { |
131 | if (net_ratelimit()) | |
132 | printk(KERN_ERR "OOM queueing packet %p\n", | |
133 | *skb); | |
134 | read_unlock(&queue_handler_lock); | |
135 | kfree_skb(*skb); | |
136 | return 1; | |
137 | } | |
138 | ||
139 | *info = (struct nf_info) { | |
140 | (struct nf_hook_ops *)elem, pf, hook, indev, outdev, okfn }; | |
141 | ||
142 | /* If it's going away, ignore hook. */ | |
143 | if (!try_module_get(info->elem->owner)) { | |
144 | read_unlock(&queue_handler_lock); | |
145 | kfree(info); | |
146 | return 0; | |
147 | } | |
148 | ||
149 | /* Bump dev refs so they don't vanish while packet is out */ | |
150 | if (indev) dev_hold(indev); | |
151 | if (outdev) dev_hold(outdev); | |
152 | ||
153 | #ifdef CONFIG_BRIDGE_NETFILTER | |
154 | if ((*skb)->nf_bridge) { | |
155 | physindev = (*skb)->nf_bridge->physindev; | |
156 | if (physindev) dev_hold(physindev); | |
157 | physoutdev = (*skb)->nf_bridge->physoutdev; | |
158 | if (physoutdev) dev_hold(physoutdev); | |
159 | } | |
160 | #endif | |
7a11b984 PM |
161 | rerouter = rcu_dereference(queue_rerouter[pf]); |
162 | if (rerouter) | |
163 | rerouter->save(*skb, info); | |
f6ebe77f | 164 | |
bbd86b9f HW |
165 | status = queue_handler[pf]->outfn(*skb, info, queuenum, |
166 | queue_handler[pf]->data); | |
f6ebe77f | 167 | |
f6ebe77f HW |
168 | read_unlock(&queue_handler_lock); |
169 | ||
170 | if (status < 0) { | |
171 | /* James M doesn't say fuck enough. */ | |
172 | if (indev) dev_put(indev); | |
173 | if (outdev) dev_put(outdev); | |
174 | #ifdef CONFIG_BRIDGE_NETFILTER | |
175 | if (physindev) dev_put(physindev); | |
176 | if (physoutdev) dev_put(physoutdev); | |
177 | #endif | |
178 | module_put(info->elem->owner); | |
179 | kfree(info); | |
180 | kfree_skb(*skb); | |
181 | ||
182 | return 1; | |
183 | } | |
184 | ||
185 | return 1; | |
186 | } | |
187 | ||
188 | void nf_reinject(struct sk_buff *skb, struct nf_info *info, | |
189 | unsigned int verdict) | |
190 | { | |
191 | struct list_head *elem = &info->elem->list; | |
192 | struct list_head *i; | |
7a11b984 | 193 | struct nf_queue_rerouter *rerouter; |
f6ebe77f HW |
194 | |
195 | rcu_read_lock(); | |
196 | ||
197 | /* Release those devices we held, or Alexey will kill me. */ | |
198 | if (info->indev) dev_put(info->indev); | |
199 | if (info->outdev) dev_put(info->outdev); | |
200 | #ifdef CONFIG_BRIDGE_NETFILTER | |
201 | if (skb->nf_bridge) { | |
202 | if (skb->nf_bridge->physindev) | |
203 | dev_put(skb->nf_bridge->physindev); | |
204 | if (skb->nf_bridge->physoutdev) | |
205 | dev_put(skb->nf_bridge->physoutdev); | |
206 | } | |
207 | #endif | |
208 | ||
209 | /* Drop reference to owner of hook which queued us. */ | |
210 | module_put(info->elem->owner); | |
211 | ||
212 | list_for_each_rcu(i, &nf_hooks[info->pf][info->hook]) { | |
213 | if (i == elem) | |
214 | break; | |
215 | } | |
216 | ||
45fe4dc0 | 217 | if (i == &nf_hooks[info->pf][info->hook]) { |
f6ebe77f HW |
218 | /* The module which sent it to userspace is gone. */ |
219 | NFDEBUG("%s: module disappeared, dropping packet.\n", | |
220 | __FUNCTION__); | |
221 | verdict = NF_DROP; | |
222 | } | |
223 | ||
224 | /* Continue traversal iff userspace said ok... */ | |
225 | if (verdict == NF_REPEAT) { | |
226 | elem = elem->prev; | |
227 | verdict = NF_ACCEPT; | |
228 | } | |
229 | ||
7a11b984 PM |
230 | if (verdict == NF_ACCEPT) { |
231 | rerouter = rcu_dereference(queue_rerouter[info->pf]); | |
232 | if (rerouter && rerouter->reroute(&skb, info) < 0) | |
233 | verdict = NF_DROP; | |
234 | } | |
235 | ||
f6ebe77f HW |
236 | if (verdict == NF_ACCEPT) { |
237 | next_hook: | |
238 | verdict = nf_iterate(&nf_hooks[info->pf][info->hook], | |
239 | &skb, info->hook, | |
240 | info->indev, info->outdev, &elem, | |
241 | info->okfn, INT_MIN); | |
242 | } | |
243 | ||
244 | switch (verdict & NF_VERDICT_MASK) { | |
245 | case NF_ACCEPT: | |
246 | info->okfn(skb); | |
247 | break; | |
248 | ||
249 | case NF_QUEUE: | |
250 | if (!nf_queue(&skb, elem, info->pf, info->hook, | |
251 | info->indev, info->outdev, info->okfn, | |
252 | verdict >> NF_VERDICT_BITS)) | |
253 | goto next_hook; | |
254 | break; | |
255 | } | |
256 | rcu_read_unlock(); | |
257 | ||
258 | if (verdict == NF_DROP) | |
259 | kfree_skb(skb); | |
260 | ||
261 | kfree(info); | |
262 | return; | |
263 | } | |
264 | EXPORT_SYMBOL(nf_reinject); | |
265 | ||
bbd86b9f HW |
266 | #ifdef CONFIG_PROC_FS |
267 | static void *seq_start(struct seq_file *seq, loff_t *pos) | |
268 | { | |
269 | if (*pos >= NPROTO) | |
270 | return NULL; | |
271 | ||
272 | return pos; | |
273 | } | |
274 | ||
275 | static void *seq_next(struct seq_file *s, void *v, loff_t *pos) | |
276 | { | |
277 | (*pos)++; | |
278 | ||
279 | if (*pos >= NPROTO) | |
280 | return NULL; | |
281 | ||
282 | return pos; | |
283 | } | |
284 | ||
285 | static void seq_stop(struct seq_file *s, void *v) | |
286 | { | |
287 | ||
288 | } | |
289 | ||
290 | static int seq_show(struct seq_file *s, void *v) | |
291 | { | |
292 | int ret; | |
293 | loff_t *pos = v; | |
294 | struct nf_queue_handler *qh; | |
295 | ||
296 | read_lock_bh(&queue_handler_lock); | |
297 | qh = queue_handler[*pos]; | |
298 | if (!qh) | |
299 | ret = seq_printf(s, "%2lld NONE\n", *pos); | |
300 | else | |
301 | ret = seq_printf(s, "%2lld %s\n", *pos, qh->name); | |
302 | read_unlock_bh(&queue_handler_lock); | |
303 | ||
304 | return ret; | |
305 | } | |
306 | ||
307 | static struct seq_operations nfqueue_seq_ops = { | |
308 | .start = seq_start, | |
309 | .next = seq_next, | |
310 | .stop = seq_stop, | |
311 | .show = seq_show, | |
312 | }; | |
313 | ||
314 | static int nfqueue_open(struct inode *inode, struct file *file) | |
315 | { | |
316 | return seq_open(file, &nfqueue_seq_ops); | |
317 | } | |
318 | ||
319 | static struct file_operations nfqueue_file_ops = { | |
320 | .owner = THIS_MODULE, | |
321 | .open = nfqueue_open, | |
322 | .read = seq_read, | |
323 | .llseek = seq_lseek, | |
324 | .release = seq_release, | |
325 | }; | |
326 | #endif /* PROC_FS */ | |
327 | ||
328 | ||
f6ebe77f HW |
329 | int __init netfilter_queue_init(void) |
330 | { | |
bbd86b9f HW |
331 | #ifdef CONFIG_PROC_FS |
332 | struct proc_dir_entry *pde; | |
f6ebe77f | 333 | |
bbd86b9f | 334 | pde = create_proc_entry("nf_queue", S_IRUGO, proc_net_netfilter); |
e02f7d16 | 335 | if (!pde) |
bbd86b9f | 336 | return -1; |
bbd86b9f HW |
337 | pde->proc_fops = &nfqueue_file_ops; |
338 | #endif | |
f6ebe77f HW |
339 | return 0; |
340 | } | |
341 |