diff options
Diffstat (limited to 'net/ipv6/icmp.c')
-rw-r--r-- | net/ipv6/icmp.c | 60 |
1 files changed, 56 insertions, 4 deletions
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 93c96cfd5ee1..c0bea7bfaa8a 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c | |||
@@ -63,6 +63,7 @@ | |||
63 | #include <net/ip6_route.h> | 63 | #include <net/ip6_route.h> |
64 | #include <net/addrconf.h> | 64 | #include <net/addrconf.h> |
65 | #include <net/icmp.h> | 65 | #include <net/icmp.h> |
66 | #include <net/xfrm.h> | ||
66 | 67 | ||
67 | #include <asm/uaccess.h> | 68 | #include <asm/uaccess.h> |
68 | #include <asm/system.h> | 69 | #include <asm/system.h> |
@@ -86,7 +87,7 @@ static int icmpv6_rcv(struct sk_buff *skb); | |||
86 | 87 | ||
87 | static struct inet6_protocol icmpv6_protocol = { | 88 | static struct inet6_protocol icmpv6_protocol = { |
88 | .handler = icmpv6_rcv, | 89 | .handler = icmpv6_rcv, |
89 | .flags = INET6_PROTO_FINAL, | 90 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, |
90 | }; | 91 | }; |
91 | 92 | ||
92 | static __inline__ int icmpv6_xmit_lock(void) | 93 | static __inline__ int icmpv6_xmit_lock(void) |
@@ -310,8 +311,10 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, | |||
310 | struct ipv6_pinfo *np; | 311 | struct ipv6_pinfo *np; |
311 | struct in6_addr *saddr = NULL; | 312 | struct in6_addr *saddr = NULL; |
312 | struct dst_entry *dst; | 313 | struct dst_entry *dst; |
314 | struct dst_entry *dst2; | ||
313 | struct icmp6hdr tmp_hdr; | 315 | struct icmp6hdr tmp_hdr; |
314 | struct flowi fl; | 316 | struct flowi fl; |
317 | struct flowi fl2; | ||
315 | struct icmpv6_msg msg; | 318 | struct icmpv6_msg msg; |
316 | int iif = 0; | 319 | int iif = 0; |
317 | int addr_type = 0; | 320 | int addr_type = 0; |
@@ -418,9 +421,42 @@ void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, | |||
418 | goto out_dst_release; | 421 | goto out_dst_release; |
419 | } | 422 | } |
420 | 423 | ||
421 | if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0) | 424 | /* No need to clone since we're just using its address. */ |
425 | dst2 = dst; | ||
426 | |||
427 | err = xfrm_lookup(&dst, &fl, sk, 0); | ||
428 | switch (err) { | ||
429 | case 0: | ||
430 | if (dst != dst2) | ||
431 | goto route_done; | ||
432 | break; | ||
433 | case -EPERM: | ||
434 | dst = NULL; | ||
435 | break; | ||
436 | default: | ||
437 | goto out; | ||
438 | } | ||
439 | |||
440 | if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6)) | ||
441 | goto out; | ||
442 | |||
443 | if (ip6_dst_lookup(sk, &dst2, &fl)) | ||
422 | goto out; | 444 | goto out; |
423 | 445 | ||
446 | err = xfrm_lookup(&dst2, &fl, sk, XFRM_LOOKUP_ICMP); | ||
447 | if (err == -ENOENT) { | ||
448 | if (!dst) | ||
449 | goto out; | ||
450 | goto route_done; | ||
451 | } | ||
452 | |||
453 | dst_release(dst); | ||
454 | dst = dst2; | ||
455 | |||
456 | if (err) | ||
457 | goto out; | ||
458 | |||
459 | route_done: | ||
424 | if (ipv6_addr_is_multicast(&fl.fl6_dst)) | 460 | if (ipv6_addr_is_multicast(&fl.fl6_dst)) |
425 | hlimit = np->mcast_hops; | 461 | hlimit = np->mcast_hops; |
426 | else | 462 | else |
@@ -608,6 +644,22 @@ static int icmpv6_rcv(struct sk_buff *skb) | |||
608 | struct icmp6hdr *hdr; | 644 | struct icmp6hdr *hdr; |
609 | int type; | 645 | int type; |
610 | 646 | ||
647 | if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb) && | ||
648 | skb->sp->xvec[skb->sp->len - 1]->props.flags & XFRM_STATE_ICMP) { | ||
649 | int nh; | ||
650 | |||
651 | if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(*orig_hdr))) | ||
652 | goto drop_no_count; | ||
653 | |||
654 | nh = skb_network_offset(skb); | ||
655 | skb_set_network_header(skb, sizeof(*hdr)); | ||
656 | |||
657 | if (!xfrm6_policy_check_reverse(NULL, XFRM_POLICY_IN, skb)) | ||
658 | goto drop_no_count; | ||
659 | |||
660 | skb_set_network_header(skb, nh); | ||
661 | } | ||
662 | |||
611 | ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS); | 663 | ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS); |
612 | 664 | ||
613 | saddr = &ipv6_hdr(skb)->saddr; | 665 | saddr = &ipv6_hdr(skb)->saddr; |
@@ -630,8 +682,7 @@ static int icmpv6_rcv(struct sk_buff *skb) | |||
630 | } | 682 | } |
631 | } | 683 | } |
632 | 684 | ||
633 | if (!pskb_pull(skb, sizeof(struct icmp6hdr))) | 685 | __skb_pull(skb, sizeof(*hdr)); |
634 | goto discard_it; | ||
635 | 686 | ||
636 | hdr = icmp6_hdr(skb); | 687 | hdr = icmp6_hdr(skb); |
637 | 688 | ||
@@ -717,6 +768,7 @@ static int icmpv6_rcv(struct sk_buff *skb) | |||
717 | 768 | ||
718 | discard_it: | 769 | discard_it: |
719 | ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); | 770 | ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); |
771 | drop_no_count: | ||
720 | kfree_skb(skb); | 772 | kfree_skb(skb); |
721 | return 0; | 773 | return 0; |
722 | } | 774 | } |