diff options
Diffstat (limited to 'net/ipv6/netfilter')
-rw-r--r-- | net/ipv6/netfilter/ip6_queue.c | 638 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_LOG.c | 527 |
2 files changed, 1165 insertions, 0 deletions
diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c new file mode 100644 index 00000000000..e63c3972a73 --- /dev/null +++ b/net/ipv6/netfilter/ip6_queue.c | |||
@@ -0,0 +1,638 @@ | |||
1 | /* | ||
2 | * This is a module which is used for queueing IPv6 packets and | ||
3 | * communicating with userspace via netlink. | ||
4 | * | ||
5 | * (C) 2001 Fernando Anton, this code is GPL. | ||
6 | * IPv64 Project - Work based in IPv64 draft by Arturo Azcorra. | ||
7 | * Universidad Carlos III de Madrid - Leganes (Madrid) - Spain | ||
8 | * Universidad Politecnica de Alcala de Henares - Alcala de H. (Madrid) - Spain | ||
9 | * email: fanton@it.uc3m.es | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/skbuff.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/ipv6.h> | ||
19 | #include <linux/notifier.h> | ||
20 | #include <linux/netdevice.h> | ||
21 | #include <linux/netfilter.h> | ||
22 | #include <linux/netlink.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/sysctl.h> | ||
25 | #include <linux/proc_fs.h> | ||
26 | #include <linux/seq_file.h> | ||
27 | #include <linux/mutex.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <net/net_namespace.h> | ||
30 | #include <net/sock.h> | ||
31 | #include <net/ipv6.h> | ||
32 | #include <net/ip6_route.h> | ||
33 | #include <net/netfilter/nf_queue.h> | ||
34 | #include <linux/netfilter_ipv4/ip_queue.h> | ||
35 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
36 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
37 | |||
38 | #define IPQ_QMAX_DEFAULT 1024 | ||
39 | #define IPQ_PROC_FS_NAME "ip6_queue" | ||
40 | #define NET_IPQ_QMAX_NAME "ip6_queue_maxlen" | ||
41 | |||
42 | typedef int (*ipq_cmpfn)(struct nf_queue_entry *, unsigned long); | ||
43 | |||
44 | static unsigned char copy_mode __read_mostly = IPQ_COPY_NONE; | ||
45 | static unsigned int queue_maxlen __read_mostly = IPQ_QMAX_DEFAULT; | ||
46 | static DEFINE_SPINLOCK(queue_lock); | ||
47 | static int peer_pid __read_mostly; | ||
48 | static unsigned int copy_range __read_mostly; | ||
49 | static unsigned int queue_total; | ||
50 | static unsigned int queue_dropped = 0; | ||
51 | static unsigned int queue_user_dropped = 0; | ||
52 | static struct sock *ipqnl __read_mostly; | ||
53 | static LIST_HEAD(queue_list); | ||
54 | static DEFINE_MUTEX(ipqnl_mutex); | ||
55 | |||
56 | static inline void | ||
57 | __ipq_enqueue_entry(struct nf_queue_entry *entry) | ||
58 | { | ||
59 | list_add_tail(&entry->list, &queue_list); | ||
60 | queue_total++; | ||
61 | } | ||
62 | |||
63 | static inline int | ||
64 | __ipq_set_mode(unsigned char mode, unsigned int range) | ||
65 | { | ||
66 | int status = 0; | ||
67 | |||
68 | switch(mode) { | ||
69 | case IPQ_COPY_NONE: | ||
70 | case IPQ_COPY_META: | ||
71 | copy_mode = mode; | ||
72 | copy_range = 0; | ||
73 | break; | ||
74 | |||
75 | case IPQ_COPY_PACKET: | ||
76 | if (range > 0xFFFF) | ||
77 | range = 0xFFFF; | ||
78 | copy_range = range; | ||
79 | copy_mode = mode; | ||
80 | break; | ||
81 | |||
82 | default: | ||
83 | status = -EINVAL; | ||
84 | |||
85 | } | ||
86 | return status; | ||
87 | } | ||
88 | |||
89 | static void __ipq_flush(ipq_cmpfn cmpfn, unsigned long data); | ||
90 | |||
91 | static inline void | ||
92 | __ipq_reset(void) | ||
93 | { | ||
94 | peer_pid = 0; | ||
95 | net_disable_timestamp(); | ||
96 | __ipq_set_mode(IPQ_COPY_NONE, 0); | ||
97 | __ipq_flush(NULL, 0); | ||
98 | } | ||
99 | |||
100 | static struct nf_queue_entry * | ||
101 | ipq_find_dequeue_entry(unsigned long id) | ||
102 | { | ||
103 | struct nf_queue_entry *entry = NULL, *i; | ||
104 | |||
105 | spin_lock_bh(&queue_lock); | ||
106 | |||
107 | list_for_each_entry(i, &queue_list, list) { | ||
108 | if ((unsigned long)i == id) { | ||
109 | entry = i; | ||
110 | break; | ||
111 | } | ||
112 | } | ||
113 | |||
114 | if (entry) { | ||
115 | list_del(&entry->list); | ||
116 | queue_total--; | ||
117 | } | ||
118 | |||
119 | spin_unlock_bh(&queue_lock); | ||
120 | return entry; | ||
121 | } | ||
122 | |||
123 | static void | ||
124 | __ipq_flush(ipq_cmpfn cmpfn, unsigned long data) | ||
125 | { | ||
126 | struct nf_queue_entry *entry, *next; | ||
127 | |||
128 | list_for_each_entry_safe(entry, next, &queue_list, list) { | ||
129 | if (!cmpfn || cmpfn(entry, data)) { | ||
130 | list_del(&entry->list); | ||
131 | queue_total--; | ||
132 | nf_reinject(entry, NF_DROP); | ||
133 | } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | static void | ||
138 | ipq_flush(ipq_cmpfn cmpfn, unsigned long data) | ||
139 | { | ||
140 | spin_lock_bh(&queue_lock); | ||
141 | __ipq_flush(cmpfn, data); | ||
142 | spin_unlock_bh(&queue_lock); | ||
143 | } | ||
144 | |||
145 | static struct sk_buff * | ||
146 | ipq_build_packet_message(struct nf_queue_entry *entry, int *errp) | ||
147 | { | ||
148 | sk_buff_data_t old_tail; | ||
149 | size_t size = 0; | ||
150 | size_t data_len = 0; | ||
151 | struct sk_buff *skb; | ||
152 | struct ipq_packet_msg *pmsg; | ||
153 | struct nlmsghdr *nlh; | ||
154 | struct timeval tv; | ||
155 | |||
156 | switch (ACCESS_ONCE(copy_mode)) { | ||
157 | case IPQ_COPY_META: | ||
158 | case IPQ_COPY_NONE: | ||
159 | size = NLMSG_SPACE(sizeof(*pmsg)); | ||
160 | break; | ||
161 | |||
162 | case IPQ_COPY_PACKET: | ||
163 | if (entry->skb->ip_summed == CHECKSUM_PARTIAL && | ||
164 | (*errp = skb_checksum_help(entry->skb))) | ||
165 | return NULL; | ||
166 | |||
167 | data_len = ACCESS_ONCE(copy_range); | ||
168 | if (data_len == 0 || data_len > entry->skb->len) | ||
169 | data_len = entry->skb->len; | ||
170 | |||
171 | size = NLMSG_SPACE(sizeof(*pmsg) + data_len); | ||
172 | break; | ||
173 | |||
174 | default: | ||
175 | *errp = -EINVAL; | ||
176 | return NULL; | ||
177 | } | ||
178 | |||
179 | skb = alloc_skb(size, GFP_ATOMIC); | ||
180 | if (!skb) | ||
181 | goto nlmsg_failure; | ||
182 | |||
183 | old_tail = skb->tail; | ||
184 | nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh)); | ||
185 | pmsg = NLMSG_DATA(nlh); | ||
186 | memset(pmsg, 0, sizeof(*pmsg)); | ||
187 | |||
188 | pmsg->packet_id = (unsigned long )entry; | ||
189 | pmsg->data_len = data_len; | ||
190 | tv = ktime_to_timeval(entry->skb->tstamp); | ||
191 | pmsg->timestamp_sec = tv.tv_sec; | ||
192 | pmsg->timestamp_usec = tv.tv_usec; | ||
193 | pmsg->mark = entry->skb->mark; | ||
194 | pmsg->hook = entry->hook; | ||
195 | pmsg->hw_protocol = entry->skb->protocol; | ||
196 | |||
197 | if (entry->indev) | ||
198 | strcpy(pmsg->indev_name, entry->indev->name); | ||
199 | else | ||
200 | pmsg->indev_name[0] = '\0'; | ||
201 | |||
202 | if (entry->outdev) | ||
203 | strcpy(pmsg->outdev_name, entry->outdev->name); | ||
204 | else | ||
205 | pmsg->outdev_name[0] = '\0'; | ||
206 | |||
207 | if (entry->indev && entry->skb->dev && | ||
208 | entry->skb->mac_header != entry->skb->network_header) { | ||
209 | pmsg->hw_type = entry->skb->dev->type; | ||
210 | pmsg->hw_addrlen = dev_parse_header(entry->skb, pmsg->hw_addr); | ||
211 | } | ||
212 | |||
213 | if (data_len) | ||
214 | if (skb_copy_bits(entry->skb, 0, pmsg->payload, data_len)) | ||
215 | BUG(); | ||
216 | |||
217 | nlh->nlmsg_len = skb->tail - old_tail; | ||
218 | return skb; | ||
219 | |||
220 | nlmsg_failure: | ||
221 | kfree_skb(skb); | ||
222 | *errp = -EINVAL; | ||
223 | printk(KERN_ERR "ip6_queue: error creating packet message\n"); | ||
224 | return NULL; | ||
225 | } | ||
226 | |||
227 | static int | ||
228 | ipq_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) | ||
229 | { | ||
230 | int status = -EINVAL; | ||
231 | struct sk_buff *nskb; | ||
232 | |||
233 | if (copy_mode == IPQ_COPY_NONE) | ||
234 | return -EAGAIN; | ||
235 | |||
236 | nskb = ipq_build_packet_message(entry, &status); | ||
237 | if (nskb == NULL) | ||
238 | return status; | ||
239 | |||
240 | spin_lock_bh(&queue_lock); | ||
241 | |||
242 | if (!peer_pid) | ||
243 | goto err_out_free_nskb; | ||
244 | |||
245 | if (queue_total >= queue_maxlen) { | ||
246 | queue_dropped++; | ||
247 | status = -ENOSPC; | ||
248 | if (net_ratelimit()) | ||
249 | printk (KERN_WARNING "ip6_queue: fill at %d entries, " | ||
250 | "dropping packet(s). Dropped: %d\n", queue_total, | ||
251 | queue_dropped); | ||
252 | goto err_out_free_nskb; | ||
253 | } | ||
254 | |||
255 | /* netlink_unicast will either free the nskb or attach it to a socket */ | ||
256 | status = netlink_unicast(ipqnl, nskb, peer_pid, MSG_DONTWAIT); | ||
257 | if (status < 0) { | ||
258 | queue_user_dropped++; | ||
259 | goto err_out_unlock; | ||
260 | } | ||
261 | |||
262 | __ipq_enqueue_entry(entry); | ||
263 | |||
264 | spin_unlock_bh(&queue_lock); | ||
265 | return status; | ||
266 | |||
267 | err_out_free_nskb: | ||
268 | kfree_skb(nskb); | ||
269 | |||
270 | err_out_unlock: | ||
271 | spin_unlock_bh(&queue_lock); | ||
272 | return status; | ||
273 | } | ||
274 | |||
275 | static int | ||
276 | ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct nf_queue_entry *e) | ||
277 | { | ||
278 | int diff; | ||
279 | struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload; | ||
280 | struct sk_buff *nskb; | ||
281 | |||
282 | if (v->data_len < sizeof(*user_iph)) | ||
283 | return 0; | ||
284 | diff = v->data_len - e->skb->len; | ||
285 | if (diff < 0) { | ||
286 | if (pskb_trim(e->skb, v->data_len)) | ||
287 | return -ENOMEM; | ||
288 | } else if (diff > 0) { | ||
289 | if (v->data_len > 0xFFFF) | ||
290 | return -EINVAL; | ||
291 | if (diff > skb_tailroom(e->skb)) { | ||
292 | nskb = skb_copy_expand(e->skb, skb_headroom(e->skb), | ||
293 | diff, GFP_ATOMIC); | ||
294 | if (!nskb) { | ||
295 | printk(KERN_WARNING "ip6_queue: OOM " | ||
296 | "in mangle, dropping packet\n"); | ||
297 | return -ENOMEM; | ||
298 | } | ||
299 | kfree_skb(e->skb); | ||
300 | e->skb = nskb; | ||
301 | } | ||
302 | skb_put(e->skb, diff); | ||
303 | } | ||
304 | if (!skb_make_writable(e->skb, v->data_len)) | ||
305 | return -ENOMEM; | ||
306 | skb_copy_to_linear_data(e->skb, v->payload, v->data_len); | ||
307 | e->skb->ip_summed = CHECKSUM_NONE; | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int | ||
313 | ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len) | ||
314 | { | ||
315 | struct nf_queue_entry *entry; | ||
316 | |||
317 | if (vmsg->value > NF_MAX_VERDICT || vmsg->value == NF_STOLEN) | ||
318 | return -EINVAL; | ||
319 | |||
320 | entry = ipq_find_dequeue_entry(vmsg->id); | ||
321 | if (entry == NULL) | ||
322 | return -ENOENT; | ||
323 | else { | ||
324 | int verdict = vmsg->value; | ||
325 | |||
326 | if (vmsg->data_len && vmsg->data_len == len) | ||
327 | if (ipq_mangle_ipv6(vmsg, entry) < 0) | ||
328 | verdict = NF_DROP; | ||
329 | |||
330 | nf_reinject(entry, verdict); | ||
331 | return 0; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | static int | ||
336 | ipq_set_mode(unsigned char mode, unsigned int range) | ||
337 | { | ||
338 | int status; | ||
339 | |||
340 | spin_lock_bh(&queue_lock); | ||
341 | status = __ipq_set_mode(mode, range); | ||
342 | spin_unlock_bh(&queue_lock); | ||
343 | return status; | ||
344 | } | ||
345 | |||
346 | static int | ||
347 | ipq_receive_peer(struct ipq_peer_msg *pmsg, | ||
348 | unsigned char type, unsigned int len) | ||
349 | { | ||
350 | int status = 0; | ||
351 | |||
352 | if (len < sizeof(*pmsg)) | ||
353 | return -EINVAL; | ||
354 | |||
355 | switch (type) { | ||
356 | case IPQM_MODE: | ||
357 | status = ipq_set_mode(pmsg->msg.mode.value, | ||
358 | pmsg->msg.mode.range); | ||
359 | break; | ||
360 | |||
361 | case IPQM_VERDICT: | ||
362 | status = ipq_set_verdict(&pmsg->msg.verdict, | ||
363 | len - sizeof(*pmsg)); | ||
364 | break; | ||
365 | default: | ||
366 | status = -EINVAL; | ||
367 | } | ||
368 | return status; | ||
369 | } | ||
370 | |||
371 | static int | ||
372 | dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex) | ||
373 | { | ||
374 | if (entry->indev) | ||
375 | if (entry->indev->ifindex == ifindex) | ||
376 | return 1; | ||
377 | |||
378 | if (entry->outdev) | ||
379 | if (entry->outdev->ifindex == ifindex) | ||
380 | return 1; | ||
381 | #ifdef CONFIG_BRIDGE_NETFILTER | ||
382 | if (entry->skb->nf_bridge) { | ||
383 | if (entry->skb->nf_bridge->physindev && | ||
384 | entry->skb->nf_bridge->physindev->ifindex == ifindex) | ||
385 | return 1; | ||
386 | if (entry->skb->nf_bridge->physoutdev && | ||
387 | entry->skb->nf_bridge->physoutdev->ifindex == ifindex) | ||
388 | return 1; | ||
389 | } | ||
390 | #endif | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | static void | ||
395 | ipq_dev_drop(int ifindex) | ||
396 | { | ||
397 | ipq_flush(dev_cmp, ifindex); | ||
398 | } | ||
399 | |||
400 | #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) | ||
401 | |||
402 | static inline void | ||
403 | __ipq_rcv_skb(struct sk_buff *skb) | ||
404 | { | ||
405 | int status, type, pid, flags; | ||
406 | unsigned int nlmsglen, skblen; | ||
407 | struct nlmsghdr *nlh; | ||
408 | |||
409 | skblen = skb->len; | ||
410 | if (skblen < sizeof(*nlh)) | ||
411 | return; | ||
412 | |||
413 | nlh = nlmsg_hdr(skb); | ||
414 | nlmsglen = nlh->nlmsg_len; | ||
415 | if (nlmsglen < sizeof(*nlh) || skblen < nlmsglen) | ||
416 | return; | ||
417 | |||
418 | pid = nlh->nlmsg_pid; | ||
419 | flags = nlh->nlmsg_flags; | ||
420 | |||
421 | if(pid <= 0 || !(flags & NLM_F_REQUEST) || flags & NLM_F_MULTI) | ||
422 | RCV_SKB_FAIL(-EINVAL); | ||
423 | |||
424 | if (flags & MSG_TRUNC) | ||
425 | RCV_SKB_FAIL(-ECOMM); | ||
426 | |||
427 | type = nlh->nlmsg_type; | ||
428 | if (type < NLMSG_NOOP || type >= IPQM_MAX) | ||
429 | RCV_SKB_FAIL(-EINVAL); | ||
430 | |||
431 | if (type <= IPQM_BASE) | ||
432 | return; | ||
433 | |||
434 | if (security_netlink_recv(skb, CAP_NET_ADMIN)) | ||
435 | RCV_SKB_FAIL(-EPERM); | ||
436 | |||
437 | spin_lock_bh(&queue_lock); | ||
438 | |||
439 | if (peer_pid) { | ||
440 | if (peer_pid != pid) { | ||
441 | spin_unlock_bh(&queue_lock); | ||
442 | RCV_SKB_FAIL(-EBUSY); | ||
443 | } | ||
444 | } else { | ||
445 | net_enable_timestamp(); | ||
446 | peer_pid = pid; | ||
447 | } | ||
448 | |||
449 | spin_unlock_bh(&queue_lock); | ||
450 | |||
451 | status = ipq_receive_peer(NLMSG_DATA(nlh), type, | ||
452 | nlmsglen - NLMSG_LENGTH(0)); | ||
453 | if (status < 0) | ||
454 | RCV_SKB_FAIL(status); | ||
455 | |||
456 | if (flags & NLM_F_ACK) | ||
457 | netlink_ack(skb, nlh, 0); | ||
458 | } | ||
459 | |||
460 | static void | ||
461 | ipq_rcv_skb(struct sk_buff *skb) | ||
462 | { | ||
463 | mutex_lock(&ipqnl_mutex); | ||
464 | __ipq_rcv_skb(skb); | ||
465 | mutex_unlock(&ipqnl_mutex); | ||
466 | } | ||
467 | |||
468 | static int | ||
469 | ipq_rcv_dev_event(struct notifier_block *this, | ||
470 | unsigned long event, void *ptr) | ||
471 | { | ||
472 | struct net_device *dev = ptr; | ||
473 | |||
474 | if (!net_eq(dev_net(dev), &init_net)) | ||
475 | return NOTIFY_DONE; | ||
476 | |||
477 | /* Drop any packets associated with the downed device */ | ||
478 | if (event == NETDEV_DOWN) | ||
479 | ipq_dev_drop(dev->ifindex); | ||
480 | return NOTIFY_DONE; | ||
481 | } | ||
482 | |||
483 | static struct notifier_block ipq_dev_notifier = { | ||
484 | .notifier_call = ipq_rcv_dev_event, | ||
485 | }; | ||
486 | |||
487 | static int | ||
488 | ipq_rcv_nl_event(struct notifier_block *this, | ||
489 | unsigned long event, void *ptr) | ||
490 | { | ||
491 | struct netlink_notify *n = ptr; | ||
492 | |||
493 | if (event == NETLINK_URELEASE && n->protocol == NETLINK_IP6_FW) { | ||
494 | spin_lock_bh(&queue_lock); | ||
495 | if ((net_eq(n->net, &init_net)) && (n->pid == peer_pid)) | ||
496 | __ipq_reset(); | ||
497 | spin_unlock_bh(&queue_lock); | ||
498 | } | ||
499 | return NOTIFY_DONE; | ||
500 | } | ||
501 | |||
502 | static struct notifier_block ipq_nl_notifier = { | ||
503 | .notifier_call = ipq_rcv_nl_event, | ||
504 | }; | ||
505 | |||
506 | #ifdef CONFIG_SYSCTL | ||
507 | static struct ctl_table_header *ipq_sysctl_header; | ||
508 | |||
509 | static ctl_table ipq_table[] = { | ||
510 | { | ||
511 | .procname = NET_IPQ_QMAX_NAME, | ||
512 | .data = &queue_maxlen, | ||
513 | .maxlen = sizeof(queue_maxlen), | ||
514 | .mode = 0644, | ||
515 | .proc_handler = proc_dointvec | ||
516 | }, | ||
517 | { } | ||
518 | }; | ||
519 | #endif | ||
520 | |||
521 | #ifdef CONFIG_PROC_FS | ||
522 | static int ip6_queue_show(struct seq_file *m, void *v) | ||
523 | { | ||
524 | spin_lock_bh(&queue_lock); | ||
525 | |||
526 | seq_printf(m, | ||
527 | "Peer PID : %d\n" | ||
528 | "Copy mode : %hu\n" | ||
529 | "Copy range : %u\n" | ||
530 | "Queue length : %u\n" | ||
531 | "Queue max. length : %u\n" | ||
532 | "Queue dropped : %u\n" | ||
533 | "Netfilter dropped : %u\n", | ||
534 | peer_pid, | ||
535 | copy_mode, | ||
536 | copy_range, | ||
537 | queue_total, | ||
538 | queue_maxlen, | ||
539 | queue_dropped, | ||
540 | queue_user_dropped); | ||
541 | |||
542 | spin_unlock_bh(&queue_lock); | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | static int ip6_queue_open(struct inode *inode, struct file *file) | ||
547 | { | ||
548 | return single_open(file, ip6_queue_show, NULL); | ||
549 | } | ||
550 | |||
551 | static const struct file_operations ip6_queue_proc_fops = { | ||
552 | .open = ip6_queue_open, | ||
553 | .read = seq_read, | ||
554 | .llseek = seq_lseek, | ||
555 | .release = single_release, | ||
556 | .owner = THIS_MODULE, | ||
557 | }; | ||
558 | #endif | ||
559 | |||
560 | static const struct nf_queue_handler nfqh = { | ||
561 | .name = "ip6_queue", | ||
562 | .outfn = &ipq_enqueue_packet, | ||
563 | }; | ||
564 | |||
565 | static int __init ip6_queue_init(void) | ||
566 | { | ||
567 | int status = -ENOMEM; | ||
568 | struct proc_dir_entry *proc __maybe_unused; | ||
569 | |||
570 | netlink_register_notifier(&ipq_nl_notifier); | ||
571 | ipqnl = netlink_kernel_create(&init_net, NETLINK_IP6_FW, 0, | ||
572 | ipq_rcv_skb, NULL, THIS_MODULE); | ||
573 | if (ipqnl == NULL) { | ||
574 | printk(KERN_ERR "ip6_queue: failed to create netlink socket\n"); | ||
575 | goto cleanup_netlink_notifier; | ||
576 | } | ||
577 | |||
578 | #ifdef CONFIG_PROC_FS | ||
579 | proc = proc_create(IPQ_PROC_FS_NAME, 0, init_net.proc_net, | ||
580 | &ip6_queue_proc_fops); | ||
581 | if (!proc) { | ||
582 | printk(KERN_ERR "ip6_queue: failed to create proc entry\n"); | ||
583 | goto cleanup_ipqnl; | ||
584 | } | ||
585 | #endif | ||
586 | register_netdevice_notifier(&ipq_dev_notifier); | ||
587 | #ifdef CONFIG_SYSCTL | ||
588 | ipq_sysctl_header = register_sysctl_paths(net_ipv6_ctl_path, ipq_table); | ||
589 | #endif | ||
590 | status = nf_register_queue_handler(NFPROTO_IPV6, &nfqh); | ||
591 | if (status < 0) { | ||
592 | printk(KERN_ERR "ip6_queue: failed to register queue handler\n"); | ||
593 | goto cleanup_sysctl; | ||
594 | } | ||
595 | return status; | ||
596 | |||
597 | cleanup_sysctl: | ||
598 | #ifdef CONFIG_SYSCTL | ||
599 | unregister_sysctl_table(ipq_sysctl_header); | ||
600 | #endif | ||
601 | unregister_netdevice_notifier(&ipq_dev_notifier); | ||
602 | proc_net_remove(&init_net, IPQ_PROC_FS_NAME); | ||
603 | |||
604 | cleanup_ipqnl: __maybe_unused | ||
605 | netlink_kernel_release(ipqnl); | ||
606 | mutex_lock(&ipqnl_mutex); | ||
607 | mutex_unlock(&ipqnl_mutex); | ||
608 | |||
609 | cleanup_netlink_notifier: | ||
610 | netlink_unregister_notifier(&ipq_nl_notifier); | ||
611 | return status; | ||
612 | } | ||
613 | |||
614 | static void __exit ip6_queue_fini(void) | ||
615 | { | ||
616 | nf_unregister_queue_handlers(&nfqh); | ||
617 | |||
618 | ipq_flush(NULL, 0); | ||
619 | |||
620 | #ifdef CONFIG_SYSCTL | ||
621 | unregister_sysctl_table(ipq_sysctl_header); | ||
622 | #endif | ||
623 | unregister_netdevice_notifier(&ipq_dev_notifier); | ||
624 | proc_net_remove(&init_net, IPQ_PROC_FS_NAME); | ||
625 | |||
626 | netlink_kernel_release(ipqnl); | ||
627 | mutex_lock(&ipqnl_mutex); | ||
628 | mutex_unlock(&ipqnl_mutex); | ||
629 | |||
630 | netlink_unregister_notifier(&ipq_nl_notifier); | ||
631 | } | ||
632 | |||
633 | MODULE_DESCRIPTION("IPv6 packet queue handler"); | ||
634 | MODULE_LICENSE("GPL"); | ||
635 | MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_IP6_FW); | ||
636 | |||
637 | module_init(ip6_queue_init); | ||
638 | module_exit(ip6_queue_fini); | ||
diff --git a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c new file mode 100644 index 00000000000..e6af8d72f26 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_LOG.c | |||
@@ -0,0 +1,527 @@ | |||
1 | /* | ||
2 | * This is a module which is used for logging packets. | ||
3 | */ | ||
4 | |||
5 | /* (C) 2001 Jan Rekorajski <baggins@pld.org.pl> | ||
6 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/skbuff.h> | ||
15 | #include <linux/if_arp.h> | ||
16 | #include <linux/ip.h> | ||
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/icmpv6.h> | ||
19 | #include <net/udp.h> | ||
20 | #include <net/tcp.h> | ||
21 | #include <net/ipv6.h> | ||
22 | #include <linux/netfilter.h> | ||
23 | #include <linux/netfilter/x_tables.h> | ||
24 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
25 | #include <net/netfilter/nf_log.h> | ||
26 | #include <net/netfilter/xt_log.h> | ||
27 | |||
28 | MODULE_AUTHOR("Jan Rekorajski <baggins@pld.org.pl>"); | ||
29 | MODULE_DESCRIPTION("Xtables: IPv6 packet logging to syslog"); | ||
30 | MODULE_LICENSE("GPL"); | ||
31 | |||
32 | struct in_device; | ||
33 | #include <net/route.h> | ||
34 | #include <linux/netfilter_ipv6/ip6t_LOG.h> | ||
35 | |||
36 | /* One level of recursion won't kill us */ | ||
37 | static void dump_packet(struct sbuff *m, | ||
38 | const struct nf_loginfo *info, | ||
39 | const struct sk_buff *skb, unsigned int ip6hoff, | ||
40 | int recurse) | ||
41 | { | ||
42 | u_int8_t currenthdr; | ||
43 | int fragment; | ||
44 | struct ipv6hdr _ip6h; | ||
45 | const struct ipv6hdr *ih; | ||
46 | unsigned int ptr; | ||
47 | unsigned int hdrlen = 0; | ||
48 | unsigned int logflags; | ||
49 | |||
50 | if (info->type == NF_LOG_TYPE_LOG) | ||
51 | logflags = info->u.log.logflags; | ||
52 | else | ||
53 | logflags = NF_LOG_MASK; | ||
54 | |||
55 | ih = skb_header_pointer(skb, ip6hoff, sizeof(_ip6h), &_ip6h); | ||
56 | if (ih == NULL) { | ||
57 | sb_add(m, "TRUNCATED"); | ||
58 | return; | ||
59 | } | ||
60 | |||
61 | /* Max length: 88 "SRC=0000.0000.0000.0000.0000.0000.0000.0000 DST=0000.0000.0000.0000.0000.0000.0000.0000 " */ | ||
62 | sb_add(m, "SRC=%pI6 DST=%pI6 ", &ih->saddr, &ih->daddr); | ||
63 | |||
64 | /* Max length: 44 "LEN=65535 TC=255 HOPLIMIT=255 FLOWLBL=FFFFF " */ | ||
65 | sb_add(m, "LEN=%Zu TC=%u HOPLIMIT=%u FLOWLBL=%u ", | ||
66 | ntohs(ih->payload_len) + sizeof(struct ipv6hdr), | ||
67 | (ntohl(*(__be32 *)ih) & 0x0ff00000) >> 20, | ||
68 | ih->hop_limit, | ||
69 | (ntohl(*(__be32 *)ih) & 0x000fffff)); | ||
70 | |||
71 | fragment = 0; | ||
72 | ptr = ip6hoff + sizeof(struct ipv6hdr); | ||
73 | currenthdr = ih->nexthdr; | ||
74 | while (currenthdr != NEXTHDR_NONE && ip6t_ext_hdr(currenthdr)) { | ||
75 | struct ipv6_opt_hdr _hdr; | ||
76 | const struct ipv6_opt_hdr *hp; | ||
77 | |||
78 | hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr); | ||
79 | if (hp == NULL) { | ||
80 | sb_add(m, "TRUNCATED"); | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | /* Max length: 48 "OPT (...) " */ | ||
85 | if (logflags & IP6T_LOG_IPOPT) | ||
86 | sb_add(m, "OPT ( "); | ||
87 | |||
88 | switch (currenthdr) { | ||
89 | case IPPROTO_FRAGMENT: { | ||
90 | struct frag_hdr _fhdr; | ||
91 | const struct frag_hdr *fh; | ||
92 | |||
93 | sb_add(m, "FRAG:"); | ||
94 | fh = skb_header_pointer(skb, ptr, sizeof(_fhdr), | ||
95 | &_fhdr); | ||
96 | if (fh == NULL) { | ||
97 | sb_add(m, "TRUNCATED "); | ||
98 | return; | ||
99 | } | ||
100 | |||
101 | /* Max length: 6 "65535 " */ | ||
102 | sb_add(m, "%u ", ntohs(fh->frag_off) & 0xFFF8); | ||
103 | |||
104 | /* Max length: 11 "INCOMPLETE " */ | ||
105 | if (fh->frag_off & htons(0x0001)) | ||
106 | sb_add(m, "INCOMPLETE "); | ||
107 | |||
108 | sb_add(m, "ID:%08x ", ntohl(fh->identification)); | ||
109 | |||
110 | if (ntohs(fh->frag_off) & 0xFFF8) | ||
111 | fragment = 1; | ||
112 | |||
113 | hdrlen = 8; | ||
114 | |||
115 | break; | ||
116 | } | ||
117 | case IPPROTO_DSTOPTS: | ||
118 | case IPPROTO_ROUTING: | ||
119 | case IPPROTO_HOPOPTS: | ||
120 | if (fragment) { | ||
121 | if (logflags & IP6T_LOG_IPOPT) | ||
122 | sb_add(m, ")"); | ||
123 | return; | ||
124 | } | ||
125 | hdrlen = ipv6_optlen(hp); | ||
126 | break; | ||
127 | /* Max Length */ | ||
128 | case IPPROTO_AH: | ||
129 | if (logflags & IP6T_LOG_IPOPT) { | ||
130 | struct ip_auth_hdr _ahdr; | ||
131 | const struct ip_auth_hdr *ah; | ||
132 | |||
133 | /* Max length: 3 "AH " */ | ||
134 | sb_add(m, "AH "); | ||
135 | |||
136 | if (fragment) { | ||
137 | sb_add(m, ")"); | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | ah = skb_header_pointer(skb, ptr, sizeof(_ahdr), | ||
142 | &_ahdr); | ||
143 | if (ah == NULL) { | ||
144 | /* | ||
145 | * Max length: 26 "INCOMPLETE [65535 | ||
146 | * bytes] )" | ||
147 | */ | ||
148 | sb_add(m, "INCOMPLETE [%u bytes] )", | ||
149 | skb->len - ptr); | ||
150 | return; | ||
151 | } | ||
152 | |||
153 | /* Length: 15 "SPI=0xF1234567 */ | ||
154 | sb_add(m, "SPI=0x%x ", ntohl(ah->spi)); | ||
155 | |||
156 | } | ||
157 | |||
158 | hdrlen = (hp->hdrlen+2)<<2; | ||
159 | break; | ||
160 | case IPPROTO_ESP: | ||
161 | if (logflags & IP6T_LOG_IPOPT) { | ||
162 | struct ip_esp_hdr _esph; | ||
163 | const struct ip_esp_hdr *eh; | ||
164 | |||
165 | /* Max length: 4 "ESP " */ | ||
166 | sb_add(m, "ESP "); | ||
167 | |||
168 | if (fragment) { | ||
169 | sb_add(m, ")"); | ||
170 | return; | ||
171 | } | ||
172 | |||
173 | /* | ||
174 | * Max length: 26 "INCOMPLETE [65535 bytes] )" | ||
175 | */ | ||
176 | eh = skb_header_pointer(skb, ptr, sizeof(_esph), | ||
177 | &_esph); | ||
178 | if (eh == NULL) { | ||
179 | sb_add(m, "INCOMPLETE [%u bytes] )", | ||
180 | skb->len - ptr); | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | /* Length: 16 "SPI=0xF1234567 )" */ | ||
185 | sb_add(m, "SPI=0x%x )", ntohl(eh->spi) ); | ||
186 | |||
187 | } | ||
188 | return; | ||
189 | default: | ||
190 | /* Max length: 20 "Unknown Ext Hdr 255" */ | ||
191 | sb_add(m, "Unknown Ext Hdr %u", currenthdr); | ||
192 | return; | ||
193 | } | ||
194 | if (logflags & IP6T_LOG_IPOPT) | ||
195 | sb_add(m, ") "); | ||
196 | |||
197 | currenthdr = hp->nexthdr; | ||
198 | ptr += hdrlen; | ||
199 | } | ||
200 | |||
201 | switch (currenthdr) { | ||
202 | case IPPROTO_TCP: { | ||
203 | struct tcphdr _tcph; | ||
204 | const struct tcphdr *th; | ||
205 | |||
206 | /* Max length: 10 "PROTO=TCP " */ | ||
207 | sb_add(m, "PROTO=TCP "); | ||
208 | |||
209 | if (fragment) | ||
210 | break; | ||
211 | |||
212 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
213 | th = skb_header_pointer(skb, ptr, sizeof(_tcph), &_tcph); | ||
214 | if (th == NULL) { | ||
215 | sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); | ||
216 | return; | ||
217 | } | ||
218 | |||
219 | /* Max length: 20 "SPT=65535 DPT=65535 " */ | ||
220 | sb_add(m, "SPT=%u DPT=%u ", | ||
221 | ntohs(th->source), ntohs(th->dest)); | ||
222 | /* Max length: 30 "SEQ=4294967295 ACK=4294967295 " */ | ||
223 | if (logflags & IP6T_LOG_TCPSEQ) | ||
224 | sb_add(m, "SEQ=%u ACK=%u ", | ||
225 | ntohl(th->seq), ntohl(th->ack_seq)); | ||
226 | /* Max length: 13 "WINDOW=65535 " */ | ||
227 | sb_add(m, "WINDOW=%u ", ntohs(th->window)); | ||
228 | /* Max length: 9 "RES=0x3C " */ | ||
229 | sb_add(m, "RES=0x%02x ", (u_int8_t)(ntohl(tcp_flag_word(th) & TCP_RESERVED_BITS) >> 22)); | ||
230 | /* Max length: 32 "CWR ECE URG ACK PSH RST SYN FIN " */ | ||
231 | if (th->cwr) | ||
232 | sb_add(m, "CWR "); | ||
233 | if (th->ece) | ||
234 | sb_add(m, "ECE "); | ||
235 | if (th->urg) | ||
236 | sb_add(m, "URG "); | ||
237 | if (th->ack) | ||
238 | sb_add(m, "ACK "); | ||
239 | if (th->psh) | ||
240 | sb_add(m, "PSH "); | ||
241 | if (th->rst) | ||
242 | sb_add(m, "RST "); | ||
243 | if (th->syn) | ||
244 | sb_add(m, "SYN "); | ||
245 | if (th->fin) | ||
246 | sb_add(m, "FIN "); | ||
247 | /* Max length: 11 "URGP=65535 " */ | ||
248 | sb_add(m, "URGP=%u ", ntohs(th->urg_ptr)); | ||
249 | |||
250 | if ((logflags & IP6T_LOG_TCPOPT) && | ||
251 | th->doff * 4 > sizeof(struct tcphdr)) { | ||
252 | u_int8_t _opt[60 - sizeof(struct tcphdr)]; | ||
253 | const u_int8_t *op; | ||
254 | unsigned int i; | ||
255 | unsigned int optsize = th->doff * 4 | ||
256 | - sizeof(struct tcphdr); | ||
257 | |||
258 | op = skb_header_pointer(skb, | ||
259 | ptr + sizeof(struct tcphdr), | ||
260 | optsize, _opt); | ||
261 | if (op == NULL) { | ||
262 | sb_add(m, "OPT (TRUNCATED)"); | ||
263 | return; | ||
264 | } | ||
265 | |||
266 | /* Max length: 127 "OPT (" 15*4*2chars ") " */ | ||
267 | sb_add(m, "OPT ("); | ||
268 | for (i =0; i < optsize; i++) | ||
269 | sb_add(m, "%02X", op[i]); | ||
270 | sb_add(m, ") "); | ||
271 | } | ||
272 | break; | ||
273 | } | ||
274 | case IPPROTO_UDP: | ||
275 | case IPPROTO_UDPLITE: { | ||
276 | struct udphdr _udph; | ||
277 | const struct udphdr *uh; | ||
278 | |||
279 | if (currenthdr == IPPROTO_UDP) | ||
280 | /* Max length: 10 "PROTO=UDP " */ | ||
281 | sb_add(m, "PROTO=UDP " ); | ||
282 | else /* Max length: 14 "PROTO=UDPLITE " */ | ||
283 | sb_add(m, "PROTO=UDPLITE "); | ||
284 | |||
285 | if (fragment) | ||
286 | break; | ||
287 | |||
288 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
289 | uh = skb_header_pointer(skb, ptr, sizeof(_udph), &_udph); | ||
290 | if (uh == NULL) { | ||
291 | sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); | ||
292 | return; | ||
293 | } | ||
294 | |||
295 | /* Max length: 20 "SPT=65535 DPT=65535 " */ | ||
296 | sb_add(m, "SPT=%u DPT=%u LEN=%u ", | ||
297 | ntohs(uh->source), ntohs(uh->dest), | ||
298 | ntohs(uh->len)); | ||
299 | break; | ||
300 | } | ||
301 | case IPPROTO_ICMPV6: { | ||
302 | struct icmp6hdr _icmp6h; | ||
303 | const struct icmp6hdr *ic; | ||
304 | |||
305 | /* Max length: 13 "PROTO=ICMPv6 " */ | ||
306 | sb_add(m, "PROTO=ICMPv6 "); | ||
307 | |||
308 | if (fragment) | ||
309 | break; | ||
310 | |||
311 | /* Max length: 25 "INCOMPLETE [65535 bytes] " */ | ||
312 | ic = skb_header_pointer(skb, ptr, sizeof(_icmp6h), &_icmp6h); | ||
313 | if (ic == NULL) { | ||
314 | sb_add(m, "INCOMPLETE [%u bytes] ", skb->len - ptr); | ||
315 | return; | ||
316 | } | ||
317 | |||
318 | /* Max length: 18 "TYPE=255 CODE=255 " */ | ||
319 | sb_add(m, "TYPE=%u CODE=%u ", ic->icmp6_type, ic->icmp6_code); | ||
320 | |||
321 | switch (ic->icmp6_type) { | ||
322 | case ICMPV6_ECHO_REQUEST: | ||
323 | case ICMPV6_ECHO_REPLY: | ||
324 | /* Max length: 19 "ID=65535 SEQ=65535 " */ | ||
325 | sb_add(m, "ID=%u SEQ=%u ", | ||
326 | ntohs(ic->icmp6_identifier), | ||
327 | ntohs(ic->icmp6_sequence)); | ||
328 | break; | ||
329 | case ICMPV6_MGM_QUERY: | ||
330 | case ICMPV6_MGM_REPORT: | ||
331 | case ICMPV6_MGM_REDUCTION: | ||
332 | break; | ||
333 | |||
334 | case ICMPV6_PARAMPROB: | ||
335 | /* Max length: 17 "POINTER=ffffffff " */ | ||
336 | sb_add(m, "POINTER=%08x ", ntohl(ic->icmp6_pointer)); | ||
337 | /* Fall through */ | ||
338 | case ICMPV6_DEST_UNREACH: | ||
339 | case ICMPV6_PKT_TOOBIG: | ||
340 | case ICMPV6_TIME_EXCEED: | ||
341 | /* Max length: 3+maxlen */ | ||
342 | if (recurse) { | ||
343 | sb_add(m, "["); | ||
344 | dump_packet(m, info, skb, | ||
345 | ptr + sizeof(_icmp6h), 0); | ||
346 | sb_add(m, "] "); | ||
347 | } | ||
348 | |||
349 | /* Max length: 10 "MTU=65535 " */ | ||
350 | if (ic->icmp6_type == ICMPV6_PKT_TOOBIG) | ||
351 | sb_add(m, "MTU=%u ", ntohl(ic->icmp6_mtu)); | ||
352 | } | ||
353 | break; | ||
354 | } | ||
355 | /* Max length: 10 "PROTO=255 " */ | ||
356 | default: | ||
357 | sb_add(m, "PROTO=%u ", currenthdr); | ||
358 | } | ||
359 | |||
360 | /* Max length: 15 "UID=4294967295 " */ | ||
361 | if ((logflags & IP6T_LOG_UID) && recurse && skb->sk) { | ||
362 | read_lock_bh(&skb->sk->sk_callback_lock); | ||
363 | if (skb->sk->sk_socket && skb->sk->sk_socket->file) | ||
364 | sb_add(m, "UID=%u GID=%u ", | ||
365 | skb->sk->sk_socket->file->f_cred->fsuid, | ||
366 | skb->sk->sk_socket->file->f_cred->fsgid); | ||
367 | read_unlock_bh(&skb->sk->sk_callback_lock); | ||
368 | } | ||
369 | |||
370 | /* Max length: 16 "MARK=0xFFFFFFFF " */ | ||
371 | if (!recurse && skb->mark) | ||
372 | sb_add(m, "MARK=0x%x ", skb->mark); | ||
373 | } | ||
374 | |||
375 | static void dump_mac_header(struct sbuff *m, | ||
376 | const struct nf_loginfo *info, | ||
377 | const struct sk_buff *skb) | ||
378 | { | ||
379 | struct net_device *dev = skb->dev; | ||
380 | unsigned int logflags = 0; | ||
381 | |||
382 | if (info->type == NF_LOG_TYPE_LOG) | ||
383 | logflags = info->u.log.logflags; | ||
384 | |||
385 | if (!(logflags & IP6T_LOG_MACDECODE)) | ||
386 | goto fallback; | ||
387 | |||
388 | switch (dev->type) { | ||
389 | case ARPHRD_ETHER: | ||
390 | sb_add(m, "MACSRC=%pM MACDST=%pM MACPROTO=%04x ", | ||
391 | eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest, | ||
392 | ntohs(eth_hdr(skb)->h_proto)); | ||
393 | return; | ||
394 | default: | ||
395 | break; | ||
396 | } | ||
397 | |||
398 | fallback: | ||
399 | sb_add(m, "MAC="); | ||
400 | if (dev->hard_header_len && | ||
401 | skb->mac_header != skb->network_header) { | ||
402 | const unsigned char *p = skb_mac_header(skb); | ||
403 | unsigned int len = dev->hard_header_len; | ||
404 | unsigned int i; | ||
405 | |||
406 | if (dev->type == ARPHRD_SIT && | ||
407 | (p -= ETH_HLEN) < skb->head) | ||
408 | p = NULL; | ||
409 | |||
410 | if (p != NULL) { | ||
411 | sb_add(m, "%02x", *p++); | ||
412 | for (i = 1; i < len; i++) | ||
413 | sb_add(m, ":%02x", *p++); | ||
414 | } | ||
415 | sb_add(m, " "); | ||
416 | |||
417 | if (dev->type == ARPHRD_SIT) { | ||
418 | const struct iphdr *iph = | ||
419 | (struct iphdr *)skb_mac_header(skb); | ||
420 | sb_add(m, "TUNNEL=%pI4->%pI4 ", &iph->saddr, &iph->daddr); | ||
421 | } | ||
422 | } else | ||
423 | sb_add(m, " "); | ||
424 | } | ||
425 | |||
426 | static struct nf_loginfo default_loginfo = { | ||
427 | .type = NF_LOG_TYPE_LOG, | ||
428 | .u = { | ||
429 | .log = { | ||
430 | .level = 5, | ||
431 | .logflags = NF_LOG_MASK, | ||
432 | }, | ||
433 | }, | ||
434 | }; | ||
435 | |||
436 | static void | ||
437 | ip6t_log_packet(u_int8_t pf, | ||
438 | unsigned int hooknum, | ||
439 | const struct sk_buff *skb, | ||
440 | const struct net_device *in, | ||
441 | const struct net_device *out, | ||
442 | const struct nf_loginfo *loginfo, | ||
443 | const char *prefix) | ||
444 | { | ||
445 | struct sbuff *m = sb_open(); | ||
446 | |||
447 | if (!loginfo) | ||
448 | loginfo = &default_loginfo; | ||
449 | |||
450 | sb_add(m, "<%d>%sIN=%s OUT=%s ", loginfo->u.log.level, | ||
451 | prefix, | ||
452 | in ? in->name : "", | ||
453 | out ? out->name : ""); | ||
454 | |||
455 | if (in != NULL) | ||
456 | dump_mac_header(m, loginfo, skb); | ||
457 | |||
458 | dump_packet(m, loginfo, skb, skb_network_offset(skb), 1); | ||
459 | |||
460 | sb_close(m); | ||
461 | } | ||
462 | |||
463 | static unsigned int | ||
464 | log_tg6(struct sk_buff *skb, const struct xt_action_param *par) | ||
465 | { | ||
466 | const struct ip6t_log_info *loginfo = par->targinfo; | ||
467 | struct nf_loginfo li; | ||
468 | |||
469 | li.type = NF_LOG_TYPE_LOG; | ||
470 | li.u.log.level = loginfo->level; | ||
471 | li.u.log.logflags = loginfo->logflags; | ||
472 | |||
473 | ip6t_log_packet(NFPROTO_IPV6, par->hooknum, skb, par->in, par->out, | ||
474 | &li, loginfo->prefix); | ||
475 | return XT_CONTINUE; | ||
476 | } | ||
477 | |||
478 | |||
479 | static int log_tg6_check(const struct xt_tgchk_param *par) | ||
480 | { | ||
481 | const struct ip6t_log_info *loginfo = par->targinfo; | ||
482 | |||
483 | if (loginfo->level >= 8) { | ||
484 | pr_debug("level %u >= 8\n", loginfo->level); | ||
485 | return -EINVAL; | ||
486 | } | ||
487 | if (loginfo->prefix[sizeof(loginfo->prefix)-1] != '\0') { | ||
488 | pr_debug("prefix not null-terminated\n"); | ||
489 | return -EINVAL; | ||
490 | } | ||
491 | return 0; | ||
492 | } | ||
493 | |||
494 | static struct xt_target log_tg6_reg __read_mostly = { | ||
495 | .name = "LOG", | ||
496 | .family = NFPROTO_IPV6, | ||
497 | .target = log_tg6, | ||
498 | .targetsize = sizeof(struct ip6t_log_info), | ||
499 | .checkentry = log_tg6_check, | ||
500 | .me = THIS_MODULE, | ||
501 | }; | ||
502 | |||
503 | static struct nf_logger ip6t_logger __read_mostly = { | ||
504 | .name = "ip6t_LOG", | ||
505 | .logfn = &ip6t_log_packet, | ||
506 | .me = THIS_MODULE, | ||
507 | }; | ||
508 | |||
509 | static int __init log_tg6_init(void) | ||
510 | { | ||
511 | int ret; | ||
512 | |||
513 | ret = xt_register_target(&log_tg6_reg); | ||
514 | if (ret < 0) | ||
515 | return ret; | ||
516 | nf_log_register(NFPROTO_IPV6, &ip6t_logger); | ||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | static void __exit log_tg6_exit(void) | ||
521 | { | ||
522 | nf_log_unregister(&ip6t_logger); | ||
523 | xt_unregister_target(&log_tg6_reg); | ||
524 | } | ||
525 | |||
526 | module_init(log_tg6_init); | ||
527 | module_exit(log_tg6_exit); | ||