aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/icmp.c')
-rw-r--r--net/ipv6/icmp.c60
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
87static struct inet6_protocol icmpv6_protocol = { 88static 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
92static __inline__ int icmpv6_xmit_lock(void) 93static __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
459route_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
718discard_it: 769discard_it:
719 ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS); 770 ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS);
771drop_no_count:
720 kfree_skb(skb); 772 kfree_skb(skb);
721 return 0; 773 return 0;
722} 774}