blob: 582ec3efc8a55f32603feeca879a8267a2639d6b [file] [log] [blame]
Harald Weltef6ebe772005-08-09 20:21:49 -07001#include <linux/kernel.h>
2#include <linux/init.h>
3#include <linux/module.h>
4#include <linux/proc_fs.h>
5#include <linux/skbuff.h>
6#include <linux/netfilter.h>
Harald Weltebbd86b9f2005-08-09 20:23:11 -07007#include <linux/seq_file.h>
Patrick McHardy7a11b982006-02-27 13:03:24 -08008#include <linux/rcupdate.h>
Harald Weltef6ebe772005-08-09 20:21:49 -07009#include <net/protocol.h>
Patrick McHardyc01cd422007-12-05 01:24:48 -080010#include <net/netfilter/nf_queue.h>
Harald Weltef6ebe772005-08-09 20:21:49 -070011
12#include "nf_internals.h"
13
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -080014/*
Harald Weltef6ebe772005-08-09 20:21:49 -070015 * 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 */
Patrick McHardye3ac5292007-12-05 01:23:57 -080019static const struct nf_queue_handler *queue_handler[NPROTO];
Harald Weltef6ebe772005-08-09 20:21:49 -070020
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -070021static DEFINE_MUTEX(queue_handler_mutex);
Harald Weltef6ebe772005-08-09 20:21:49 -070022
Harald Welted72367b2005-08-09 20:23:36 -070023/* return EBUSY when somebody else is registered, return EEXIST if the
24 * same handler is registered, return 0 in case of success. */
Patrick McHardye3ac5292007-12-05 01:23:57 -080025int nf_register_queue_handler(int pf, const struct nf_queue_handler *qh)
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -080026{
Harald Weltef6ebe772005-08-09 20:21:49 -070027 int ret;
28
29 if (pf >= NPROTO)
30 return -EINVAL;
31
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -070032 mutex_lock(&queue_handler_mutex);
Harald Welted72367b2005-08-09 20:23:36 -070033 if (queue_handler[pf] == qh)
34 ret = -EEXIST;
35 else if (queue_handler[pf])
Harald Weltef6ebe772005-08-09 20:21:49 -070036 ret = -EBUSY;
37 else {
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -070038 rcu_assign_pointer(queue_handler[pf], qh);
Harald Weltef6ebe772005-08-09 20:21:49 -070039 ret = 0;
40 }
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -070041 mutex_unlock(&queue_handler_mutex);
Harald Weltef6ebe772005-08-09 20:21:49 -070042
43 return ret;
44}
45EXPORT_SYMBOL(nf_register_queue_handler);
46
47/* The caller must flush their queue before this */
Patrick McHardye3ac5292007-12-05 01:23:57 -080048int nf_unregister_queue_handler(int pf, const struct nf_queue_handler *qh)
Harald Weltef6ebe772005-08-09 20:21:49 -070049{
50 if (pf >= NPROTO)
51 return -EINVAL;
52
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -070053 mutex_lock(&queue_handler_mutex);
Patrick McHardy94be1a32008-03-10 16:45:05 -070054 if (queue_handler[pf] && queue_handler[pf] != qh) {
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -070055 mutex_unlock(&queue_handler_mutex);
Yasuyuki Kozakaice7663d2007-07-07 22:40:08 -070056 return -EINVAL;
57 }
58
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -070059 rcu_assign_pointer(queue_handler[pf], NULL);
60 mutex_unlock(&queue_handler_mutex);
61
62 synchronize_rcu();
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -080063
Harald Weltef6ebe772005-08-09 20:21:49 -070064 return 0;
65}
66EXPORT_SYMBOL(nf_unregister_queue_handler);
67
Patrick McHardye3ac5292007-12-05 01:23:57 -080068void nf_unregister_queue_handlers(const struct nf_queue_handler *qh)
Harald Weltef6ebe772005-08-09 20:21:49 -070069{
70 int pf;
71
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -070072 mutex_lock(&queue_handler_mutex);
Harald Weltef6ebe772005-08-09 20:21:49 -070073 for (pf = 0; pf < NPROTO; pf++) {
Harald Weltebbd86b9f2005-08-09 20:23:11 -070074 if (queue_handler[pf] == qh)
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -070075 rcu_assign_pointer(queue_handler[pf], NULL);
Harald Weltef6ebe772005-08-09 20:21:49 -070076 }
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -070077 mutex_unlock(&queue_handler_mutex);
78
79 synchronize_rcu();
Harald Weltef6ebe772005-08-09 20:21:49 -070080}
81EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers);
82
Patrick McHardydaaa8be2007-12-05 01:27:19 -080083static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
84{
85 /* Release those devices we held, or Alexey will kill me. */
86 if (entry->indev)
87 dev_put(entry->indev);
88 if (entry->outdev)
89 dev_put(entry->outdev);
90#ifdef CONFIG_BRIDGE_NETFILTER
91 if (entry->skb->nf_bridge) {
92 struct nf_bridge_info *nf_bridge = entry->skb->nf_bridge;
93
94 if (nf_bridge->physindev)
95 dev_put(nf_bridge->physindev);
96 if (nf_bridge->physoutdev)
97 dev_put(nf_bridge->physoutdev);
98 }
99#endif
100 /* Drop reference to owner of hook which queued us. */
101 module_put(entry->elem->owner);
102}
103
YOSHIFUJI Hideaki601e68e2007-02-12 11:15:49 -0800104/*
105 * Any packet that leaves via this function must come back
Harald Weltef6ebe772005-08-09 20:21:49 -0700106 * through nf_reinject().
107 */
Patrick McHardy394f5452006-08-05 00:58:52 -0700108static int __nf_queue(struct sk_buff *skb,
109 struct list_head *elem,
110 int pf, unsigned int hook,
111 struct net_device *indev,
112 struct net_device *outdev,
113 int (*okfn)(struct sk_buff *),
114 unsigned int queuenum)
Harald Weltef6ebe772005-08-09 20:21:49 -0700115{
116 int status;
Patrick McHardydaaa8be2007-12-05 01:27:19 -0800117 struct nf_queue_entry *entry = NULL;
Harald Weltef6ebe772005-08-09 20:21:49 -0700118#ifdef CONFIG_BRIDGE_NETFILTER
Patrick McHardydaaa8be2007-12-05 01:27:19 -0800119 struct net_device *physindev;
120 struct net_device *physoutdev;
Harald Weltef6ebe772005-08-09 20:21:49 -0700121#endif
Patrick McHardy1e796fd2007-12-17 22:42:27 -0800122 const struct nf_afinfo *afinfo;
Patrick McHardye3ac5292007-12-05 01:23:57 -0800123 const struct nf_queue_handler *qh;
Harald Weltef6ebe772005-08-09 20:21:49 -0700124
125 /* QUEUE == DROP if noone is waiting, to be safe. */
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -0700126 rcu_read_lock();
127
128 qh = rcu_dereference(queue_handler[pf]);
Patrick McHardydaaa8be2007-12-05 01:27:19 -0800129 if (!qh)
130 goto err_unlock;
Harald Weltef6ebe772005-08-09 20:21:49 -0700131
Patrick McHardybce80322006-04-06 14:18:09 -0700132 afinfo = nf_get_afinfo(pf);
Patrick McHardydaaa8be2007-12-05 01:27:19 -0800133 if (!afinfo)
134 goto err_unlock;
Patrick McHardybce80322006-04-06 14:18:09 -0700135
Patrick McHardy02f014d2007-12-05 01:26:33 -0800136 entry = kmalloc(sizeof(*entry) + afinfo->route_key_size, GFP_ATOMIC);
Patrick McHardydaaa8be2007-12-05 01:27:19 -0800137 if (!entry)
138 goto err_unlock;
Harald Weltef6ebe772005-08-09 20:21:49 -0700139
Patrick McHardy02f014d2007-12-05 01:26:33 -0800140 *entry = (struct nf_queue_entry) {
141 .skb = skb,
142 .elem = list_entry(elem, struct nf_hook_ops, list),
143 .pf = pf,
144 .hook = hook,
145 .indev = indev,
146 .outdev = outdev,
147 .okfn = okfn,
148 };
Harald Weltef6ebe772005-08-09 20:21:49 -0700149
150 /* If it's going away, ignore hook. */
Patrick McHardy02f014d2007-12-05 01:26:33 -0800151 if (!try_module_get(entry->elem->owner)) {
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -0700152 rcu_read_unlock();
Patrick McHardy02f014d2007-12-05 01:26:33 -0800153 kfree(entry);
Harald Weltef6ebe772005-08-09 20:21:49 -0700154 return 0;
155 }
156
157 /* Bump dev refs so they don't vanish while packet is out */
Patrick McHardy8b1cf0d2007-12-05 01:23:17 -0800158 if (indev)
159 dev_hold(indev);
160 if (outdev)
161 dev_hold(outdev);
Harald Weltef6ebe772005-08-09 20:21:49 -0700162#ifdef CONFIG_BRIDGE_NETFILTER
Patrick McHardy394f5452006-08-05 00:58:52 -0700163 if (skb->nf_bridge) {
164 physindev = skb->nf_bridge->physindev;
Patrick McHardy8b1cf0d2007-12-05 01:23:17 -0800165 if (physindev)
166 dev_hold(physindev);
Patrick McHardy394f5452006-08-05 00:58:52 -0700167 physoutdev = skb->nf_bridge->physoutdev;
Patrick McHardy8b1cf0d2007-12-05 01:23:17 -0800168 if (physoutdev)
169 dev_hold(physoutdev);
Harald Weltef6ebe772005-08-09 20:21:49 -0700170 }
171#endif
Patrick McHardy02f014d2007-12-05 01:26:33 -0800172 afinfo->saveroute(skb, entry);
173 status = qh->outfn(entry, queuenum);
Harald Weltef6ebe772005-08-09 20:21:49 -0700174
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -0700175 rcu_read_unlock();
Harald Weltef6ebe772005-08-09 20:21:49 -0700176
177 if (status < 0) {
Patrick McHardydaaa8be2007-12-05 01:27:19 -0800178 nf_queue_entry_release_refs(entry);
179 goto err;
Harald Weltef6ebe772005-08-09 20:21:49 -0700180 }
181
182 return 1;
Patrick McHardydaaa8be2007-12-05 01:27:19 -0800183
184err_unlock:
185 rcu_read_unlock();
186err:
187 kfree_skb(skb);
188 kfree(entry);
189 return 1;
Harald Weltef6ebe772005-08-09 20:21:49 -0700190}
191
Patrick McHardy394f5452006-08-05 00:58:52 -0700192int nf_queue(struct sk_buff *skb,
193 struct list_head *elem,
194 int pf, unsigned int hook,
195 struct net_device *indev,
196 struct net_device *outdev,
197 int (*okfn)(struct sk_buff *),
198 unsigned int queuenum)
199{
200 struct sk_buff *segs;
201
202 if (!skb_is_gso(skb))
203 return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
204 queuenum);
205
206 switch (pf) {
207 case AF_INET:
208 skb->protocol = htons(ETH_P_IP);
209 break;
210 case AF_INET6:
211 skb->protocol = htons(ETH_P_IPV6);
212 break;
213 }
214
215 segs = skb_gso_segment(skb, 0);
216 kfree_skb(skb);
Hirofumi Nakagawa801678c2008-04-29 01:03:09 -0700217 if (IS_ERR(segs))
Patrick McHardy394f5452006-08-05 00:58:52 -0700218 return 1;
219
220 do {
221 struct sk_buff *nskb = segs->next;
222
223 segs->next = NULL;
224 if (!__nf_queue(segs, elem, pf, hook, indev, outdev, okfn,
225 queuenum))
226 kfree_skb(segs);
227 segs = nskb;
228 } while (segs);
229 return 1;
230}
231
Patrick McHardy02f014d2007-12-05 01:26:33 -0800232void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict)
Harald Weltef6ebe772005-08-09 20:21:49 -0700233{
Patrick McHardy02f014d2007-12-05 01:26:33 -0800234 struct sk_buff *skb = entry->skb;
235 struct list_head *elem = &entry->elem->list;
Patrick McHardy1e796fd2007-12-17 22:42:27 -0800236 const struct nf_afinfo *afinfo;
Harald Weltef6ebe772005-08-09 20:21:49 -0700237
238 rcu_read_lock();
239
Patrick McHardydaaa8be2007-12-05 01:27:19 -0800240 nf_queue_entry_release_refs(entry);
Harald Weltef6ebe772005-08-09 20:21:49 -0700241
Harald Weltef6ebe772005-08-09 20:21:49 -0700242 /* Continue traversal iff userspace said ok... */
243 if (verdict == NF_REPEAT) {
244 elem = elem->prev;
245 verdict = NF_ACCEPT;
246 }
247
248 if (verdict == NF_ACCEPT) {
Patrick McHardy02f014d2007-12-05 01:26:33 -0800249 afinfo = nf_get_afinfo(entry->pf);
250 if (!afinfo || afinfo->reroute(skb, entry) < 0)
Patrick McHardy7a11b982006-02-27 13:03:24 -0800251 verdict = NF_DROP;
252 }
253
254 if (verdict == NF_ACCEPT) {
Harald Weltef6ebe772005-08-09 20:21:49 -0700255 next_hook:
Patrick McHardy02f014d2007-12-05 01:26:33 -0800256 verdict = nf_iterate(&nf_hooks[entry->pf][entry->hook],
257 skb, entry->hook,
258 entry->indev, entry->outdev, &elem,
259 entry->okfn, INT_MIN);
Harald Weltef6ebe772005-08-09 20:21:49 -0700260 }
261
262 switch (verdict & NF_VERDICT_MASK) {
263 case NF_ACCEPT:
Patrick McHardy3bc38712006-07-24 22:52:47 -0700264 case NF_STOP:
Patrick McHardy4b3d15e2007-12-05 01:27:02 -0800265 local_bh_disable();
Patrick McHardy02f014d2007-12-05 01:26:33 -0800266 entry->okfn(skb);
Patrick McHardy4b3d15e2007-12-05 01:27:02 -0800267 local_bh_enable();
Patrick McHardy3bc38712006-07-24 22:52:47 -0700268 case NF_STOLEN:
Harald Weltef6ebe772005-08-09 20:21:49 -0700269 break;
Harald Weltef6ebe772005-08-09 20:21:49 -0700270 case NF_QUEUE:
Patrick McHardy02f014d2007-12-05 01:26:33 -0800271 if (!__nf_queue(skb, elem, entry->pf, entry->hook,
272 entry->indev, entry->outdev, entry->okfn,
Patrick McHardy394f5452006-08-05 00:58:52 -0700273 verdict >> NF_VERDICT_BITS))
Harald Weltef6ebe772005-08-09 20:21:49 -0700274 goto next_hook;
275 break;
Patrick McHardy3bc38712006-07-24 22:52:47 -0700276 default:
277 kfree_skb(skb);
Harald Weltef6ebe772005-08-09 20:21:49 -0700278 }
279 rcu_read_unlock();
Patrick McHardy02f014d2007-12-05 01:26:33 -0800280 kfree(entry);
Harald Weltef6ebe772005-08-09 20:21:49 -0700281 return;
282}
283EXPORT_SYMBOL(nf_reinject);
284
Harald Weltebbd86b9f2005-08-09 20:23:11 -0700285#ifdef CONFIG_PROC_FS
286static void *seq_start(struct seq_file *seq, loff_t *pos)
287{
288 if (*pos >= NPROTO)
289 return NULL;
290
291 return pos;
292}
293
294static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
295{
296 (*pos)++;
297
298 if (*pos >= NPROTO)
299 return NULL;
300
301 return pos;
302}
303
304static void seq_stop(struct seq_file *s, void *v)
305{
306
307}
308
309static int seq_show(struct seq_file *s, void *v)
310{
311 int ret;
312 loff_t *pos = v;
Patrick McHardye3ac5292007-12-05 01:23:57 -0800313 const struct nf_queue_handler *qh;
Harald Weltebbd86b9f2005-08-09 20:23:11 -0700314
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -0700315 rcu_read_lock();
316 qh = rcu_dereference(queue_handler[*pos]);
Harald Weltebbd86b9f2005-08-09 20:23:11 -0700317 if (!qh)
318 ret = seq_printf(s, "%2lld NONE\n", *pos);
319 else
320 ret = seq_printf(s, "%2lld %s\n", *pos, qh->name);
Yasuyuki Kozakai585426f2007-07-07 22:40:26 -0700321 rcu_read_unlock();
Harald Weltebbd86b9f2005-08-09 20:23:11 -0700322
323 return ret;
324}
325
Philippe De Muyter56b3d972007-07-10 23:07:31 -0700326static const struct seq_operations nfqueue_seq_ops = {
Harald Weltebbd86b9f2005-08-09 20:23:11 -0700327 .start = seq_start,
328 .next = seq_next,
329 .stop = seq_stop,
330 .show = seq_show,
331};
332
333static int nfqueue_open(struct inode *inode, struct file *file)
334{
335 return seq_open(file, &nfqueue_seq_ops);
336}
337
Arjan van de Venda7071d2007-02-12 00:55:36 -0800338static const struct file_operations nfqueue_file_ops = {
Harald Weltebbd86b9f2005-08-09 20:23:11 -0700339 .owner = THIS_MODULE,
340 .open = nfqueue_open,
341 .read = seq_read,
342 .llseek = seq_lseek,
343 .release = seq_release,
344};
345#endif /* PROC_FS */
346
347
Harald Weltef6ebe772005-08-09 20:21:49 -0700348int __init netfilter_queue_init(void)
349{
Harald Weltebbd86b9f2005-08-09 20:23:11 -0700350#ifdef CONFIG_PROC_FS
Denis V. Lunev8eeee8b2008-03-27 16:55:53 -0700351 if (!proc_create("nf_queue", S_IRUGO,
352 proc_net_netfilter, &nfqueue_file_ops))
Harald Weltebbd86b9f2005-08-09 20:23:11 -0700353 return -1;
Harald Weltebbd86b9f2005-08-09 20:23:11 -0700354#endif
Harald Weltef6ebe772005-08-09 20:21:49 -0700355 return 0;
356}
357