diff options
Diffstat (limited to 'net/netfilter/nfnetlink_queue.c')
-rw-r--r-- | net/netfilter/nfnetlink_queue.c | 1028 |
1 files changed, 1028 insertions, 0 deletions
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c new file mode 100644 index 00000000000..a80b0cb03f1 --- /dev/null +++ b/net/netfilter/nfnetlink_queue.c | |||
@@ -0,0 +1,1028 @@ | |||
1 | /* | ||
2 | * This is a module which is used for queueing packets and communicating with | ||
3 | * userspace via nfnetlink. | ||
4 | * | ||
5 | * (C) 2005 by Harald Welte <laforge@netfilter.org> | ||
6 | * (C) 2007 by Patrick McHardy <kaber@trash.net> | ||
7 | * | ||
8 | * Based on the old ipv4-only ip_queue.c: | ||
9 | * (C) 2000-2002 James Morris <jmorris@intercode.com.au> | ||
10 | * (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 as | ||
14 | * published by the Free Software Foundation. | ||
15 | * | ||
16 | */ | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/skbuff.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/notifier.h> | ||
23 | #include <linux/netdevice.h> | ||
24 | #include <linux/netfilter.h> | ||
25 | #include <linux/proc_fs.h> | ||
26 | #include <linux/netfilter_ipv4.h> | ||
27 | #include <linux/netfilter_ipv6.h> | ||
28 | #include <linux/netfilter/nfnetlink.h> | ||
29 | #include <linux/netfilter/nfnetlink_queue.h> | ||
30 | #include <linux/list.h> | ||
31 | #include <net/sock.h> | ||
32 | #include <net/netfilter/nf_queue.h> | ||
33 | |||
34 | #include <linux/atomic.h> | ||
35 | |||
36 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
37 | #include "../bridge/br_private.h" | ||
38 | #endif | ||
39 | |||
40 | #define NFQNL_QMAX_DEFAULT 1024 | ||
41 | |||
42 | struct nfqnl_instance { | ||
43 | struct hlist_node hlist; /* global list of queues */ | ||
44 | struct rcu_head rcu; | ||
45 | |||
46 | int peer_pid; | ||
47 | unsigned int queue_maxlen; | ||
48 | unsigned int copy_range; | ||
49 | unsigned int queue_dropped; | ||
50 | unsigned int queue_user_dropped; | ||
51 | |||
52 | |||
53 | u_int16_t queue_num; /* number of this queue */ | ||
54 | u_int8_t copy_mode; | ||
55 | /* | ||
56 | * Following fields are dirtied for each queued packet, | ||
57 | * keep them in same cache line if possible. | ||
58 | */ | ||
59 | spinlock_t lock; | ||
60 | unsigned int queue_total; | ||
61 | unsigned int id_sequence; /* 'sequence' of pkt ids */ | ||
62 | struct list_head queue_list; /* packets in queue */ | ||
63 | }; | ||
64 | |||
65 | typedef int (*nfqnl_cmpfn)(struct nf_queue_entry *, unsigned long); | ||
66 | |||
67 | static DEFINE_SPINLOCK(instances_lock); | ||
68 | |||
69 | #define INSTANCE_BUCKETS 16 | ||
70 | static struct hlist_head instance_table[INSTANCE_BUCKETS] __read_mostly; | ||
71 | |||
72 | static inline u_int8_t instance_hashfn(u_int16_t queue_num) | ||
73 | { | ||
74 | return ((queue_num >> 8) | queue_num) % INSTANCE_BUCKETS; | ||
75 | } | ||
76 | |||
77 | static struct nfqnl_instance * | ||
78 | instance_lookup(u_int16_t queue_num) | ||
79 | { | ||
80 | struct hlist_head *head; | ||
81 | struct hlist_node *pos; | ||
82 | struct nfqnl_instance *inst; | ||
83 | |||
84 | head = &instance_table[instance_hashfn(queue_num)]; | ||
85 | hlist_for_each_entry_rcu(inst, pos, head, hlist) { | ||
86 | if (inst->queue_num == queue_num) | ||
87 | return inst; | ||
88 | } | ||
89 | return NULL; | ||
90 | } | ||
91 | |||
92 | static struct nfqnl_instance * | ||
93 | instance_create(u_int16_t queue_num, int pid) | ||
94 | { | ||
95 | struct nfqnl_instance *inst; | ||
96 | unsigned int h; | ||
97 | int err; | ||
98 | |||
99 | spin_lock(&instances_lock); | ||
100 | if (instance_lookup(queue_num)) { | ||
101 | err = -EEXIST; | ||
102 | goto out_unlock; | ||
103 | } | ||
104 | |||
105 | inst = kzalloc(sizeof(*inst), GFP_ATOMIC); | ||
106 | if (!inst) { | ||
107 | err = -ENOMEM; | ||
108 | goto out_unlock; | ||
109 | } | ||
110 | |||
111 | inst->queue_num = queue_num; | ||
112 | inst->peer_pid = pid; | ||
113 | inst->queue_maxlen = NFQNL_QMAX_DEFAULT; | ||
114 | inst->copy_range = 0xfffff; | ||
115 | inst->copy_mode = NFQNL_COPY_NONE; | ||
116 | spin_lock_init(&inst->lock); | ||
117 | INIT_LIST_HEAD(&inst->queue_list); | ||
118 | |||
119 | if (!try_module_get(THIS_MODULE)) { | ||
120 | err = -EAGAIN; | ||
121 | goto out_free; | ||
122 | } | ||
123 | |||
124 | h = instance_hashfn(queue_num); | ||
125 | hlist_add_head_rcu(&inst->hlist, &instance_table[h]); | ||
126 | |||
127 | spin_unlock(&instances_lock); | ||
128 | |||
129 | return inst; | ||
130 | |||
131 | out_free: | ||
132 | kfree(inst); | ||
133 | out_unlock: | ||
134 | spin_unlock(&instances_lock); | ||
135 | return ERR_PTR(err); | ||
136 | } | ||
137 | |||
138 | static void nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, | ||
139 | unsigned long data); | ||
140 | |||
141 | static void | ||
142 | instance_destroy_rcu(struct rcu_head *head) | ||
143 | { | ||
144 | struct nfqnl_instance *inst = container_of(head, struct nfqnl_instance, | ||
145 | rcu); | ||
146 | |||
147 | nfqnl_flush(inst, NULL, 0); | ||
148 | kfree(inst); | ||
149 | module_put(THIS_MODULE); | ||
150 | } | ||
151 | |||
152 | static void | ||
153 | __instance_destroy(struct nfqnl_instance *inst) | ||
154 | { | ||
155 | hlist_del_rcu(&inst->hlist); | ||
156 | call_rcu(&inst->rcu, instance_destroy_rcu); | ||
157 | } | ||
158 | |||
159 | static void | ||
160 | instance_destroy(struct nfqnl_instance *inst) | ||
161 | { | ||
162 | spin_lock(&instances_lock); | ||
163 | __instance_destroy(inst); | ||
164 | spin_unlock(&instances_lock); | ||
165 | } | ||
166 | |||
167 | static inline void | ||
168 | __enqueue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) | ||
169 | { | ||
170 | list_add_tail(&entry->list, &queue->queue_list); | ||
171 | queue->queue_total++; | ||
172 | } | ||
173 | |||
174 | static void | ||
175 | __dequeue_entry(struct nfqnl_instance *queue, struct nf_queue_entry *entry) | ||
176 | { | ||
177 | list_del(&entry->list); | ||
178 | queue->queue_total--; | ||
179 | } | ||
180 | |||
181 | static struct nf_queue_entry * | ||
182 | find_dequeue_entry(struct nfqnl_instance *queue, unsigned int id) | ||
183 | { | ||
184 | struct nf_queue_entry *entry = NULL, *i; | ||
185 | |||
186 | spin_lock_bh(&queue->lock); | ||
187 | |||
188 | list_for_each_entry(i, &queue->queue_list, list) { | ||
189 | if (i->id == id) { | ||
190 | entry = i; | ||
191 | break; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | if (entry) | ||
196 | __dequeue_entry(queue, entry); | ||
197 | |||
198 | spin_unlock_bh(&queue->lock); | ||
199 | |||
200 | return entry; | ||
201 | } | ||
202 | |||
203 | static void | ||
204 | nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data) | ||
205 | { | ||
206 | struct nf_queue_entry *entry, *next; | ||
207 | |||
208 | spin_lock_bh(&queue->lock); | ||
209 | list_for_each_entry_safe(entry, next, &queue->queue_list, list) { | ||
210 | if (!cmpfn || cmpfn(entry, data)) { | ||
211 | list_del(&entry->list); | ||
212 | queue->queue_total--; | ||
213 | nf_reinject(entry, NF_DROP); | ||
214 | } | ||
215 | } | ||
216 | spin_unlock_bh(&queue->lock); | ||
217 | } | ||
218 | |||
219 | static struct sk_buff * | ||
220 | nfqnl_build_packet_message(struct nfqnl_instance *queue, | ||
221 | struct nf_queue_entry *entry, | ||
222 | __be32 **packet_id_ptr) | ||
223 | { | ||
224 | sk_buff_data_t old_tail; | ||
225 | size_t size; | ||
226 | size_t data_len = 0; | ||
227 | struct sk_buff *skb; | ||
228 | struct nlattr *nla; | ||
229 | struct nfqnl_msg_packet_hdr *pmsg; | ||
230 | struct nlmsghdr *nlh; | ||
231 | struct nfgenmsg *nfmsg; | ||
232 | struct sk_buff *entskb = entry->skb; | ||
233 | struct net_device *indev; | ||
234 | struct net_device *outdev; | ||
235 | |||
236 | size = NLMSG_SPACE(sizeof(struct nfgenmsg)) | ||
237 | + nla_total_size(sizeof(struct nfqnl_msg_packet_hdr)) | ||
238 | + nla_total_size(sizeof(u_int32_t)) /* ifindex */ | ||
239 | + nla_total_size(sizeof(u_int32_t)) /* ifindex */ | ||
240 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
241 | + nla_total_size(sizeof(u_int32_t)) /* ifindex */ | ||
242 | + nla_total_size(sizeof(u_int32_t)) /* ifindex */ | ||
243 | #endif | ||
244 | + nla_total_size(sizeof(u_int32_t)) /* mark */ | ||
245 | + nla_total_size(sizeof(struct nfqnl_msg_packet_hw)) | ||
246 | + nla_total_size(sizeof(struct nfqnl_msg_packet_timestamp)); | ||
247 | |||
248 | outdev = entry->outdev; | ||
249 | |||
250 | switch ((enum nfqnl_config_mode)ACCESS_ONCE(queue->copy_mode)) { | ||
251 | case NFQNL_COPY_META: | ||
252 | case NFQNL_COPY_NONE: | ||
253 | break; | ||
254 | |||
255 | case NFQNL_COPY_PACKET: | ||
256 | if (entskb->ip_summed == CHECKSUM_PARTIAL && | ||
257 | skb_checksum_help(entskb)) | ||
258 | return NULL; | ||
259 | |||
260 | data_len = ACCESS_ONCE(queue->copy_range); | ||
261 | if (data_len == 0 || data_len > entskb->len) | ||
262 | data_len = entskb->len; | ||
263 | |||
264 | size += nla_total_size(data_len); | ||
265 | break; | ||
266 | } | ||
267 | |||
268 | |||
269 | skb = alloc_skb(size, GFP_ATOMIC); | ||
270 | if (!skb) | ||
271 | goto nlmsg_failure; | ||
272 | |||
273 | old_tail = skb->tail; | ||
274 | nlh = NLMSG_PUT(skb, 0, 0, | ||
275 | NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, | ||
276 | sizeof(struct nfgenmsg)); | ||
277 | nfmsg = NLMSG_DATA(nlh); | ||
278 | nfmsg->nfgen_family = entry->pf; | ||
279 | nfmsg->version = NFNETLINK_V0; | ||
280 | nfmsg->res_id = htons(queue->queue_num); | ||
281 | |||
282 | nla = __nla_reserve(skb, NFQA_PACKET_HDR, sizeof(*pmsg)); | ||
283 | pmsg = nla_data(nla); | ||
284 | pmsg->hw_protocol = entskb->protocol; | ||
285 | pmsg->hook = entry->hook; | ||
286 | *packet_id_ptr = &pmsg->packet_id; | ||
287 | |||
288 | indev = entry->indev; | ||
289 | if (indev) { | ||
290 | #ifndef CONFIG_BRIDGE_NETFILTER | ||
291 | NLA_PUT_BE32(skb, NFQA_IFINDEX_INDEV, htonl(indev->ifindex)); | ||
292 | #else | ||
293 | if (entry->pf == PF_BRIDGE) { | ||
294 | /* Case 1: indev is physical input device, we need to | ||
295 | * look for bridge group (when called from | ||
296 | * netfilter_bridge) */ | ||
297 | NLA_PUT_BE32(skb, NFQA_IFINDEX_PHYSINDEV, | ||
298 | htonl(indev->ifindex)); | ||
299 | /* this is the bridge group "brX" */ | ||
300 | /* rcu_read_lock()ed by __nf_queue */ | ||
301 | NLA_PUT_BE32(skb, NFQA_IFINDEX_INDEV, | ||
302 | htonl(br_port_get_rcu(indev)->br->dev->ifindex)); | ||
303 | } else { | ||
304 | /* Case 2: indev is bridge group, we need to look for | ||
305 | * physical device (when called from ipv4) */ | ||
306 | NLA_PUT_BE32(skb, NFQA_IFINDEX_INDEV, | ||
307 | htonl(indev->ifindex)); | ||
308 | if (entskb->nf_bridge && entskb->nf_bridge->physindev) | ||
309 | NLA_PUT_BE32(skb, NFQA_IFINDEX_PHYSINDEV, | ||
310 | htonl(entskb->nf_bridge->physindev->ifindex)); | ||
311 | } | ||
312 | #endif | ||
313 | } | ||
314 | |||
315 | if (outdev) { | ||
316 | #ifndef CONFIG_BRIDGE_NETFILTER | ||
317 | NLA_PUT_BE32(skb, NFQA_IFINDEX_OUTDEV, htonl(outdev->ifindex)); | ||
318 | #else | ||
319 | if (entry->pf == PF_BRIDGE) { | ||
320 | /* Case 1: outdev is physical output device, we need to | ||
321 | * look for bridge group (when called from | ||
322 | * netfilter_bridge) */ | ||
323 | NLA_PUT_BE32(skb, NFQA_IFINDEX_PHYSOUTDEV, | ||
324 | htonl(outdev->ifindex)); | ||
325 | /* this is the bridge group "brX" */ | ||
326 | /* rcu_read_lock()ed by __nf_queue */ | ||
327 | NLA_PUT_BE32(skb, NFQA_IFINDEX_OUTDEV, | ||
328 | htonl(br_port_get_rcu(outdev)->br->dev->ifindex)); | ||
329 | } else { | ||
330 | /* Case 2: outdev is bridge group, we need to look for | ||
331 | * physical output device (when called from ipv4) */ | ||
332 | NLA_PUT_BE32(skb, NFQA_IFINDEX_OUTDEV, | ||
333 | htonl(outdev->ifindex)); | ||
334 | if (entskb->nf_bridge && entskb->nf_bridge->physoutdev) | ||
335 | NLA_PUT_BE32(skb, NFQA_IFINDEX_PHYSOUTDEV, | ||
336 | htonl(entskb->nf_bridge->physoutdev->ifindex)); | ||
337 | } | ||
338 | #endif | ||
339 | } | ||
340 | |||
341 | if (entskb->mark) | ||
342 | NLA_PUT_BE32(skb, NFQA_MARK, htonl(entskb->mark)); | ||
343 | |||
344 | if (indev && entskb->dev && | ||
345 | entskb->mac_header != entskb->network_header) { | ||
346 | struct nfqnl_msg_packet_hw phw; | ||
347 | int len = dev_parse_header(entskb, phw.hw_addr); | ||
348 | if (len) { | ||
349 | phw.hw_addrlen = htons(len); | ||
350 | NLA_PUT(skb, NFQA_HWADDR, sizeof(phw), &phw); | ||
351 | } | ||
352 | } | ||
353 | |||
354 | if (entskb->tstamp.tv64) { | ||
355 | struct nfqnl_msg_packet_timestamp ts; | ||
356 | struct timeval tv = ktime_to_timeval(entskb->tstamp); | ||
357 | ts.sec = cpu_to_be64(tv.tv_sec); | ||
358 | ts.usec = cpu_to_be64(tv.tv_usec); | ||
359 | |||
360 | NLA_PUT(skb, NFQA_TIMESTAMP, sizeof(ts), &ts); | ||
361 | } | ||
362 | |||
363 | if (data_len) { | ||
364 | struct nlattr *nla; | ||
365 | int sz = nla_attr_size(data_len); | ||
366 | |||
367 | if (skb_tailroom(skb) < nla_total_size(data_len)) { | ||
368 | printk(KERN_WARNING "nf_queue: no tailroom!\n"); | ||
369 | goto nlmsg_failure; | ||
370 | } | ||
371 | |||
372 | nla = (struct nlattr *)skb_put(skb, nla_total_size(data_len)); | ||
373 | nla->nla_type = NFQA_PAYLOAD; | ||
374 | nla->nla_len = sz; | ||
375 | |||
376 | if (skb_copy_bits(entskb, 0, nla_data(nla), data_len)) | ||
377 | BUG(); | ||
378 | } | ||
379 | |||
380 | nlh->nlmsg_len = skb->tail - old_tail; | ||
381 | return skb; | ||
382 | |||
383 | nlmsg_failure: | ||
384 | nla_put_failure: | ||
385 | if (skb) | ||
386 | kfree_skb(skb); | ||
387 | if (net_ratelimit()) | ||
388 | printk(KERN_ERR "nf_queue: error creating packet message\n"); | ||
389 | return NULL; | ||
390 | } | ||
391 | |||
392 | static int | ||
393 | nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) | ||
394 | { | ||
395 | struct sk_buff *nskb; | ||
396 | struct nfqnl_instance *queue; | ||
397 | int err = -ENOBUFS; | ||
398 | __be32 *packet_id_ptr; | ||
399 | |||
400 | /* rcu_read_lock()ed by nf_hook_slow() */ | ||
401 | queue = instance_lookup(queuenum); | ||
402 | if (!queue) { | ||
403 | err = -ESRCH; | ||
404 | goto err_out; | ||
405 | } | ||
406 | |||
407 | if (queue->copy_mode == NFQNL_COPY_NONE) { | ||
408 | err = -EINVAL; | ||
409 | goto err_out; | ||
410 | } | ||
411 | |||
412 | nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr); | ||
413 | if (nskb == NULL) { | ||
414 | err = -ENOMEM; | ||
415 | goto err_out; | ||
416 | } | ||
417 | spin_lock_bh(&queue->lock); | ||
418 | |||
419 | if (!queue->peer_pid) { | ||
420 | err = -EINVAL; | ||
421 | goto err_out_free_nskb; | ||
422 | } | ||
423 | if (queue->queue_total >= queue->queue_maxlen) { | ||
424 | queue->queue_dropped++; | ||
425 | if (net_ratelimit()) | ||
426 | printk(KERN_WARNING "nf_queue: full at %d entries, " | ||
427 | "dropping packets(s).\n", | ||
428 | queue->queue_total); | ||
429 | goto err_out_free_nskb; | ||
430 | } | ||
431 | entry->id = ++queue->id_sequence; | ||
432 | *packet_id_ptr = htonl(entry->id); | ||
433 | |||
434 | /* nfnetlink_unicast will either free the nskb or add it to a socket */ | ||
435 | err = nfnetlink_unicast(nskb, &init_net, queue->peer_pid, MSG_DONTWAIT); | ||
436 | if (err < 0) { | ||
437 | queue->queue_user_dropped++; | ||
438 | goto err_out_unlock; | ||
439 | } | ||
440 | |||
441 | __enqueue_entry(queue, entry); | ||
442 | |||
443 | spin_unlock_bh(&queue->lock); | ||
444 | return 0; | ||
445 | |||
446 | err_out_free_nskb: | ||
447 | kfree_skb(nskb); | ||
448 | err_out_unlock: | ||
449 | spin_unlock_bh(&queue->lock); | ||
450 | err_out: | ||
451 | return err; | ||
452 | } | ||
453 | |||
454 | static int | ||
455 | nfqnl_mangle(void *data, int data_len, struct nf_queue_entry *e) | ||
456 | { | ||
457 | struct sk_buff *nskb; | ||
458 | int diff; | ||
459 | |||
460 | diff = data_len - e->skb->len; | ||
461 | if (diff < 0) { | ||
462 | if (pskb_trim(e->skb, data_len)) | ||
463 | return -ENOMEM; | ||
464 | } else if (diff > 0) { | ||
465 | if (data_len > 0xFFFF) | ||
466 | return -EINVAL; | ||
467 | if (diff > skb_tailroom(e->skb)) { | ||
468 | nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), | ||
469 | diff, GFP_ATOMIC); | ||
470 | if (!nskb) { | ||
471 | printk(KERN_WARNING "nf_queue: OOM " | ||
472 | "in mangle, dropping packet\n"); | ||
473 | return -ENOMEM; | ||
474 | } | ||
475 | kfree_skb(e->skb); | ||
476 | e->skb = nskb; | ||
477 | } | ||
478 | skb_put(e->skb, diff); | ||
479 | } | ||
480 | if (!skb_make_writable(e->skb, data_len)) | ||
481 | return -ENOMEM; | ||
482 | skb_copy_to_linear_data(e->skb, data, data_len); | ||
483 | e->skb->ip_summed = CHECKSUM_NONE; | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static int | ||
488 | nfqnl_set_mode(struct nfqnl_instance *queue, | ||
489 | unsigned char mode, unsigned int range) | ||
490 | { | ||
491 | int status = 0; | ||
492 | |||
493 | spin_lock_bh(&queue->lock); | ||
494 | switch (mode) { | ||
495 | case NFQNL_COPY_NONE: | ||
496 | case NFQNL_COPY_META: | ||
497 | queue->copy_mode = mode; | ||
498 | queue->copy_range = 0; | ||
499 | break; | ||
500 | |||
501 | case NFQNL_COPY_PACKET: | ||
502 | queue->copy_mode = mode; | ||
503 | /* we're using struct nlattr which has 16bit nla_len */ | ||
504 | if (range > 0xffff) | ||
505 | queue->copy_range = 0xffff; | ||
506 | else | ||
507 | queue->copy_range = range; | ||
508 | break; | ||
509 | |||
510 | default: | ||
511 | status = -EINVAL; | ||
512 | |||
513 | } | ||
514 | spin_unlock_bh(&queue->lock); | ||
515 | |||
516 | return status; | ||
517 | } | ||
518 | |||
519 | static int | ||
520 | dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) | ||
521 | { | ||
522 | if (entry->indev) | ||
523 | if (entry->indev->ifindex == ifindex) | ||
524 | return 1; | ||
525 | if (entry->outdev) | ||
526 | if (entry->outdev->ifindex == ifindex) | ||
527 | return 1; | ||
528 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
529 | if (entry->skb->nf_bridge) { | ||
530 | if (entry->skb->nf_bridge->physindev && | ||
531 | entry->skb->nf_bridge->physindev->ifindex == ifindex) | ||
532 | return 1; | ||
533 | if (entry->skb->nf_bridge->physoutdev && | ||
534 | entry->skb->nf_bridge->physoutdev->ifindex == ifindex) | ||
535 | return 1; | ||
536 | } | ||
537 | #endif | ||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | /* drop all packets with either indev or outdev == ifindex from all queue | ||
542 | * instances */ | ||
543 | static void | ||
544 | nfqnl_dev_drop(int ifindex) | ||
545 | { | ||
546 | int i; | ||
547 | |||
548 | rcu_read_lock(); | ||
549 | |||
550 | for (i = 0; i < INSTANCE_BUCKETS; i++) { | ||
551 | struct hlist_node *tmp; | ||
552 | struct nfqnl_instance *inst; | ||
553 | struct hlist_head *head = &instance_table[i]; | ||
554 | |||
555 | hlist_for_each_entry_rcu(inst, tmp, head, hlist) | ||
556 | nfqnl_flush(inst, dev_cmp, ifindex); | ||
557 | } | ||
558 | |||
559 | rcu_read_unlock(); | ||
560 | } | ||
561 | |||
562 | #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) | ||
563 | |||
564 | static int | ||
565 | nfqnl_rcv_dev_event(struct notifier_block *this, | ||
566 | unsigned long event, void *ptr) | ||
567 | { | ||
568 | struct net_device *dev = ptr; | ||
569 | |||
570 | if (!net_eq(dev_net(dev), &init_net)) | ||
571 | return NOTIFY_DONE; | ||
572 | |||
573 | /* Drop any packets associated with the downed device */ | ||
574 | if (event == NETDEV_DOWN) | ||
575 | nfqnl_dev_drop(dev->ifindex); | ||
576 | return NOTIFY_DONE; | ||
577 | } | ||
578 | |||
579 | static struct notifier_block nfqnl_dev_notifier = { | ||
580 | .notifier_call = nfqnl_rcv_dev_event, | ||
581 | }; | ||
582 | |||
583 | static int | ||
584 | nfqnl_rcv_nl_event(struct notifier_block *this, | ||
585 | unsigned long event, void *ptr) | ||
586 | { | ||
587 | struct netlink_notify *n = ptr; | ||
588 | |||
589 | if (event == NETLINK_URELEASE && n->protocol == NETLINK_NETFILTER) { | ||
590 | int i; | ||
591 | |||
592 | /* destroy all instances for this pid */ | ||
593 | spin_lock(&instances_lock); | ||
594 | for (i = 0; i < INSTANCE_BUCKETS; i++) { | ||
595 | struct hlist_node *tmp, *t2; | ||
596 | struct nfqnl_instance *inst; | ||
597 | struct hlist_head *head = &instance_table[i]; | ||
598 | |||
599 | hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) { | ||
600 | if ((n->net == &init_net) && | ||
601 | (n->pid == inst->peer_pid)) | ||
602 | __instance_destroy(inst); | ||
603 | } | ||
604 | } | ||
605 | spin_unlock(&instances_lock); | ||
606 | } | ||
607 | return NOTIFY_DONE; | ||
608 | } | ||
609 | |||
610 | static struct notifier_block nfqnl_rtnl_notifier = { | ||
611 | .notifier_call = nfqnl_rcv_nl_event, | ||
612 | }; | ||
613 | |||
614 | static const struct nla_policy nfqa_verdict_policy[NFQA_MAX+1] = { | ||
615 | [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, | ||
616 | [NFQA_MARK] = { .type = NLA_U32 }, | ||
617 | [NFQA_PAYLOAD] = { .type = NLA_UNSPEC }, | ||
618 | }; | ||
619 | |||
620 | static const struct nla_policy nfqa_verdict_batch_policy[NFQA_MAX+1] = { | ||
621 | [NFQA_VERDICT_HDR] = { .len = sizeof(struct nfqnl_msg_verdict_hdr) }, | ||
622 | [NFQA_MARK] = { .type = NLA_U32 }, | ||
623 | }; | ||
624 | |||
625 | static struct nfqnl_instance *verdict_instance_lookup(u16 queue_num, int nlpid) | ||
626 | { | ||
627 | struct nfqnl_instance *queue; | ||
628 | |||
629 | queue = instance_lookup(queue_num); | ||
630 | if (!queue) | ||
631 | return ERR_PTR(-ENODEV); | ||
632 | |||
633 | if (queue->peer_pid != nlpid) | ||
634 | return ERR_PTR(-EPERM); | ||
635 | |||
636 | return queue; | ||
637 | } | ||
638 | |||
639 | static struct nfqnl_msg_verdict_hdr* | ||
640 | verdicthdr_get(const struct nlattr * const nfqa[]) | ||
641 | { | ||
642 | struct nfqnl_msg_verdict_hdr *vhdr; | ||
643 | unsigned int verdict; | ||
644 | |||
645 | if (!nfqa[NFQA_VERDICT_HDR]) | ||
646 | return NULL; | ||
647 | |||
648 | vhdr = nla_data(nfqa[NFQA_VERDICT_HDR]); | ||
649 | verdict = ntohl(vhdr->verdict) & NF_VERDICT_MASK; | ||
650 | if (verdict > NF_MAX_VERDICT || verdict == NF_STOLEN) | ||
651 | return NULL; | ||
652 | return vhdr; | ||
653 | } | ||
654 | |||
655 | static int nfq_id_after(unsigned int id, unsigned int max) | ||
656 | { | ||
657 | return (int)(id - max) > 0; | ||
658 | } | ||
659 | |||
660 | static int | ||
661 | nfqnl_recv_verdict_batch(struct sock *ctnl, struct sk_buff *skb, | ||
662 | const struct nlmsghdr *nlh, | ||
663 | const struct nlattr * const nfqa[]) | ||
664 | { | ||
665 | struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); | ||
666 | struct nf_queue_entry *entry, *tmp; | ||
667 | unsigned int verdict, maxid; | ||
668 | struct nfqnl_msg_verdict_hdr *vhdr; | ||
669 | struct nfqnl_instance *queue; | ||
670 | LIST_HEAD(batch_list); | ||
671 | u16 queue_num = ntohs(nfmsg->res_id); | ||
672 | |||
673 | queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid); | ||
674 | if (IS_ERR(queue)) | ||
675 | return PTR_ERR(queue); | ||
676 | |||
677 | vhdr = verdicthdr_get(nfqa); | ||
678 | if (!vhdr) | ||
679 | return -EINVAL; | ||
680 | |||
681 | verdict = ntohl(vhdr->verdict); | ||
682 | maxid = ntohl(vhdr->id); | ||
683 | |||
684 | spin_lock_bh(&queue->lock); | ||
685 | |||
686 | list_for_each_entry_safe(entry, tmp, &queue->queue_list, list) { | ||
687 | if (nfq_id_after(entry->id, maxid)) | ||
688 | break; | ||
689 | __dequeue_entry(queue, entry); | ||
690 | list_add_tail(&entry->list, &batch_list); | ||
691 | } | ||
692 | |||
693 | spin_unlock_bh(&queue->lock); | ||
694 | |||
695 | if (list_empty(&batch_list)) | ||
696 | return -ENOENT; | ||
697 | |||
698 | list_for_each_entry_safe(entry, tmp, &batch_list, list) { | ||
699 | if (nfqa[NFQA_MARK]) | ||
700 | entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); | ||
701 | nf_reinject(entry, verdict); | ||
702 | } | ||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | static int | ||
707 | nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, | ||
708 | const struct nlmsghdr *nlh, | ||
709 | const struct nlattr * const nfqa[]) | ||
710 | { | ||
711 | struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); | ||
712 | u_int16_t queue_num = ntohs(nfmsg->res_id); | ||
713 | |||
714 | struct nfqnl_msg_verdict_hdr *vhdr; | ||
715 | struct nfqnl_instance *queue; | ||
716 | unsigned int verdict; | ||
717 | struct nf_queue_entry *entry; | ||
718 | |||
719 | queue = instance_lookup(queue_num); | ||
720 | if (!queue) | ||
721 | |||
722 | queue = verdict_instance_lookup(queue_num, NETLINK_CB(skb).pid); | ||
723 | if (IS_ERR(queue)) | ||
724 | return PTR_ERR(queue); | ||
725 | |||
726 | vhdr = verdicthdr_get(nfqa); | ||
727 | if (!vhdr) | ||
728 | return -EINVAL; | ||
729 | |||
730 | verdict = ntohl(vhdr->verdict); | ||
731 | |||
732 | entry = find_dequeue_entry(queue, ntohl(vhdr->id)); | ||
733 | if (entry == NULL) | ||
734 | return -ENOENT; | ||
735 | |||
736 | if (nfqa[NFQA_PAYLOAD]) { | ||
737 | if (nfqnl_mangle(nla_data(nfqa[NFQA_PAYLOAD]), | ||
738 | nla_len(nfqa[NFQA_PAYLOAD]), entry) < 0) | ||
739 | verdict = NF_DROP; | ||
740 | } | ||
741 | |||
742 | if (nfqa[NFQA_MARK]) | ||
743 | entry->skb->mark = ntohl(nla_get_be32(nfqa[NFQA_MARK])); | ||
744 | |||
745 | nf_reinject(entry, verdict); | ||
746 | return 0; | ||
747 | } | ||
748 | |||
749 | static int | ||
750 | nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, | ||
751 | const struct nlmsghdr *nlh, | ||
752 | const struct nlattr * const nfqa[]) | ||
753 | { | ||
754 | return -ENOTSUPP; | ||
755 | } | ||
756 | |||
757 | static const struct nla_policy nfqa_cfg_policy[NFQA_CFG_MAX+1] = { | ||
758 | [NFQA_CFG_CMD] = { .len = sizeof(struct nfqnl_msg_config_cmd) }, | ||
759 | [NFQA_CFG_PARAMS] = { .len = sizeof(struct nfqnl_msg_config_params) }, | ||
760 | }; | ||
761 | |||
762 | static const struct nf_queue_handler nfqh = { | ||
763 | .name = "nf_queue", | ||
764 | .outfn = &nfqnl_enqueue_packet, | ||
765 | }; | ||
766 | |||
767 | static int | ||
768 | nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, | ||
769 | const struct nlmsghdr *nlh, | ||
770 | const struct nlattr * const nfqa[]) | ||
771 | { | ||
772 | struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); | ||
773 | u_int16_t queue_num = ntohs(nfmsg->res_id); | ||
774 | struct nfqnl_instance *queue; | ||
775 | struct nfqnl_msg_config_cmd *cmd = NULL; | ||
776 | int ret = 0; | ||
777 | |||
778 | if (nfqa[NFQA_CFG_CMD]) { | ||
779 | cmd = nla_data(nfqa[NFQA_CFG_CMD]); | ||
780 | |||
781 | /* Commands without queue context - might sleep */ | ||
782 | switch (cmd->command) { | ||
783 | case NFQNL_CFG_CMD_PF_BIND: | ||
784 | return nf_register_queue_handler(ntohs(cmd->pf), | ||
785 | &nfqh); | ||
786 | case NFQNL_CFG_CMD_PF_UNBIND: | ||
787 | return nf_unregister_queue_handler(ntohs(cmd->pf), | ||
788 | &nfqh); | ||
789 | } | ||
790 | } | ||
791 | |||
792 | rcu_read_lock(); | ||
793 | queue = instance_lookup(queue_num); | ||
794 | if (queue && queue->peer_pid != NETLINK_CB(skb).pid) { | ||
795 | ret = -EPERM; | ||
796 | goto err_out_unlock; | ||
797 | } | ||
798 | |||
799 | if (cmd != NULL) { | ||
800 | switch (cmd->command) { | ||
801 | case NFQNL_CFG_CMD_BIND: | ||
802 | if (queue) { | ||
803 | ret = -EBUSY; | ||
804 | goto err_out_unlock; | ||
805 | } | ||
806 | queue = instance_create(queue_num, NETLINK_CB(skb).pid); | ||
807 | if (IS_ERR(queue)) { | ||
808 | ret = PTR_ERR(queue); | ||
809 | goto err_out_unlock; | ||
810 | } | ||
811 | break; | ||
812 | case NFQNL_CFG_CMD_UNBIND: | ||
813 | if (!queue) { | ||
814 | ret = -ENODEV; | ||
815 | goto err_out_unlock; | ||
816 | } | ||
817 | instance_destroy(queue); | ||
818 | break; | ||
819 | case NFQNL_CFG_CMD_PF_BIND: | ||
820 | case NFQNL_CFG_CMD_PF_UNBIND: | ||
821 | break; | ||
822 | default: | ||
823 | ret = -ENOTSUPP; | ||
824 | break; | ||
825 | } | ||
826 | } | ||
827 | |||
828 | if (nfqa[NFQA_CFG_PARAMS]) { | ||
829 | struct nfqnl_msg_config_params *params; | ||
830 | |||
831 | if (!queue) { | ||
832 | ret = -ENODEV; | ||
833 | goto err_out_unlock; | ||
834 | } | ||
835 | params = nla_data(nfqa[NFQA_CFG_PARAMS]); | ||
836 | nfqnl_set_mode(queue, params->copy_mode, | ||
837 | ntohl(params->copy_range)); | ||
838 | } | ||
839 | |||
840 | if (nfqa[NFQA_CFG_QUEUE_MAXLEN]) { | ||
841 | __be32 *queue_maxlen; | ||
842 | |||
843 | if (!queue) { | ||
844 | ret = -ENODEV; | ||
845 | goto err_out_unlock; | ||
846 | } | ||
847 | queue_maxlen = nla_data(nfqa[NFQA_CFG_QUEUE_MAXLEN]); | ||
848 | spin_lock_bh(&queue->lock); | ||
849 | queue->queue_maxlen = ntohl(*queue_maxlen); | ||
850 | spin_unlock_bh(&queue->lock); | ||
851 | } | ||
852 | |||
853 | err_out_unlock: | ||
854 | rcu_read_unlock(); | ||
855 | return ret; | ||
856 | } | ||
857 | |||
858 | static const struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { | ||
859 | [NFQNL_MSG_PACKET] = { .call_rcu = nfqnl_recv_unsupp, | ||
860 | .attr_count = NFQA_MAX, }, | ||
861 | [NFQNL_MSG_VERDICT] = { .call_rcu = nfqnl_recv_verdict, | ||
862 | .attr_count = NFQA_MAX, | ||
863 | .policy = nfqa_verdict_policy }, | ||
864 | [NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config, | ||
865 | .attr_count = NFQA_CFG_MAX, | ||
866 | .policy = nfqa_cfg_policy }, | ||
867 | [NFQNL_MSG_VERDICT_BATCH]={ .call_rcu = nfqnl_recv_verdict_batch, | ||
868 | .attr_count = NFQA_MAX, | ||
869 | .policy = nfqa_verdict_batch_policy }, | ||
870 | }; | ||
871 | |||
872 | static const struct nfnetlink_subsystem nfqnl_subsys = { | ||
873 | .name = "nf_queue", | ||
874 | .subsys_id = NFNL_SUBSYS_QUEUE, | ||
875 | .cb_count = NFQNL_MSG_MAX, | ||
876 | .cb = nfqnl_cb, | ||
877 | }; | ||
878 | |||
879 | #ifdef CONFIG_PROC_FS | ||
880 | struct iter_state { | ||
881 | unsigned int bucket; | ||
882 | }; | ||
883 | |||
884 | static struct hlist_node *get_first(struct seq_file *seq) | ||
885 | { | ||
886 | struct iter_state *st = seq->private; | ||
887 | |||
888 | if (!st) | ||
889 | return NULL; | ||
890 | |||
891 | for (st->bucket = 0; st->bucket < INSTANCE_BUCKETS; st->bucket++) { | ||
892 | if (!hlist_empty(&instance_table[st->bucket])) | ||
893 | return instance_table[st->bucket].first; | ||
894 | } | ||
895 | return NULL; | ||
896 | } | ||
897 | |||
898 | static struct hlist_node *get_next(struct seq_file *seq, struct hlist_node *h) | ||
899 | { | ||
900 | struct iter_state *st = seq->private; | ||
901 | |||
902 | h = h->next; | ||
903 | while (!h) { | ||
904 | if (++st->bucket >= INSTANCE_BUCKETS) | ||
905 | return NULL; | ||
906 | |||
907 | h = instance_table[st->bucket].first; | ||
908 | } | ||
909 | return h; | ||
910 | } | ||
911 | |||
912 | static struct hlist_node *get_idx(struct seq_file *seq, loff_t pos) | ||
913 | { | ||
914 | struct hlist_node *head; | ||
915 | head = get_first(seq); | ||
916 | |||
917 | if (head) | ||
918 | while (pos && (head = get_next(seq, head))) | ||
919 | pos--; | ||
920 | return pos ? NULL : head; | ||
921 | } | ||
922 | |||
923 | static void *seq_start(struct seq_file *seq, loff_t *pos) | ||
924 | __acquires(instances_lock) | ||
925 | { | ||
926 | spin_lock(&instances_lock); | ||
927 | return get_idx(seq, *pos); | ||
928 | } | ||
929 | |||
930 | static void *seq_next(struct seq_file *s, void *v, loff_t *pos) | ||
931 | { | ||
932 | (*pos)++; | ||
933 | return get_next(s, v); | ||
934 | } | ||
935 | |||
936 | static void seq_stop(struct seq_file *s, void *v) | ||
937 | __releases(instances_lock) | ||
938 | { | ||
939 | spin_unlock(&instances_lock); | ||
940 | } | ||
941 | |||
942 | static int seq_show(struct seq_file *s, void *v) | ||
943 | { | ||
944 | const struct nfqnl_instance *inst = v; | ||
945 | |||
946 | return seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n", | ||
947 | inst->queue_num, | ||
948 | inst->peer_pid, inst->queue_total, | ||
949 | inst->copy_mode, inst->copy_range, | ||
950 | inst->queue_dropped, inst->queue_user_dropped, | ||
951 | inst->id_sequence, 1); | ||
952 | } | ||
953 | |||
954 | static const struct seq_operations nfqnl_seq_ops = { | ||
955 | .start = seq_start, | ||
956 | .next = seq_next, | ||
957 | .stop = seq_stop, | ||
958 | .show = seq_show, | ||
959 | }; | ||
960 | |||
961 | static int nfqnl_open(struct inode *inode, struct file *file) | ||
962 | { | ||
963 | return seq_open_private(file, &nfqnl_seq_ops, | ||
964 | sizeof(struct iter_state)); | ||
965 | } | ||
966 | |||
967 | static const struct file_operations nfqnl_file_ops = { | ||
968 | .owner = THIS_MODULE, | ||
969 | .open = nfqnl_open, | ||
970 | .read = seq_read, | ||
971 | .llseek = seq_lseek, | ||
972 | .release = seq_release_private, | ||
973 | }; | ||
974 | |||
975 | #endif /* PROC_FS */ | ||
976 | |||
977 | static int __init nfnetlink_queue_init(void) | ||
978 | { | ||
979 | int i, status = -ENOMEM; | ||
980 | |||
981 | for (i = 0; i < INSTANCE_BUCKETS; i++) | ||
982 | INIT_HLIST_HEAD(&instance_table[i]); | ||
983 | |||
984 | netlink_register_notifier(&nfqnl_rtnl_notifier); | ||
985 | status = nfnetlink_subsys_register(&nfqnl_subsys); | ||
986 | if (status < 0) { | ||
987 | printk(KERN_ERR "nf_queue: failed to create netlink socket\n"); | ||
988 | goto cleanup_netlink_notifier; | ||
989 | } | ||
990 | |||
991 | #ifdef CONFIG_PROC_FS | ||
992 | if (!proc_create("nfnetlink_queue", 0440, | ||
993 | proc_net_netfilter, &nfqnl_file_ops)) | ||
994 | goto cleanup_subsys; | ||
995 | #endif | ||
996 | |||
997 | register_netdevice_notifier(&nfqnl_dev_notifier); | ||
998 | return status; | ||
999 | |||
1000 | #ifdef CONFIG_PROC_FS | ||
1001 | cleanup_subsys: | ||
1002 | nfnetlink_subsys_unregister(&nfqnl_subsys); | ||
1003 | #endif | ||
1004 | cleanup_netlink_notifier: | ||
1005 | netlink_unregister_notifier(&nfqnl_rtnl_notifier); | ||
1006 | return status; | ||
1007 | } | ||
1008 | |||
1009 | static void __exit nfnetlink_queue_fini(void) | ||
1010 | { | ||
1011 | nf_unregister_queue_handlers(&nfqh); | ||
1012 | unregister_netdevice_notifier(&nfqnl_dev_notifier); | ||
1013 | #ifdef CONFIG_PROC_FS | ||
1014 | remove_proc_entry("nfnetlink_queue", proc_net_netfilter); | ||
1015 | #endif | ||
1016 | nfnetlink_subsys_unregister(&nfqnl_subsys); | ||
1017 | netlink_unregister_notifier(&nfqnl_rtnl_notifier); | ||
1018 | |||
1019 | rcu_barrier(); /* Wait for completion of call_rcu()'s */ | ||
1020 | } | ||
1021 | |||
1022 | MODULE_DESCRIPTION("netfilter packet queue handler"); | ||
1023 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | ||
1024 | MODULE_LICENSE("GPL"); | ||
1025 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_QUEUE); | ||
1026 | |||
1027 | module_init(nfnetlink_queue_init); | ||
1028 | module_exit(nfnetlink_queue_fini); | ||