aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/icmp.c
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2007-12-12 13:44:43 -0500
committerDavid S. Miller <davem@davemloft.net>2008-01-28 17:57:23 -0500
commit8b7817f3a959ed99d7443afc12f78a7e1fcc2063 (patch)
tree7e315dfbf5c77e67f6e7ad56f14eaddca621212b /net/ipv6/icmp.c
parentd5422efe680fc55010c6ddca2370ca9548a96355 (diff)
[IPSEC]: Add ICMP host relookup support
RFC 4301 requires us to relookup ICMP traffic that does not match any policies using the reverse of its payload. This patch implements this for ICMP traffic that originates from or terminates on localhost. This is activated on outbound with the new policy flag XFRM_POLICY_ICMP, and on inbound by the new state flag XFRM_STATE_ICMP. On inbound the policy check is now performed by the ICMP protocol so that it can repeat the policy check where necessary. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
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}