diff options
author | David S. Miller <davem@davemloft.net> | 2011-03-01 22:06:22 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-01 22:07:37 -0800 |
commit | b42835dbe83d725198c7ab0bbe726d6dfd92a634 (patch) | |
tree | 8728dccb3fa9bb54dba53a0a00c6e2818b843a91 /net/ipv6 | |
parent | f6d460cf0ed16d35aec48f823685e7a0e0283d84 (diff) | |
download | linux-b42835dbe83d725198c7ab0bbe726d6dfd92a634.tar.bz2 |
ipv6: Make icmp route lookup code a bit clearer.
The route lookup code in icmpv6_send() is slightly tricky as a result of
having to handle all of the requirements of RFC 4301 host relookups.
Pull the route resolution into a seperate function, so that the error
handling and route reference counting is hopefully easier to see and
contained wholly within this new routine.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/icmp.c | 117 |
1 files changed, 66 insertions, 51 deletions
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index a31d91b04c87..e332bae104ee 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -300,6 +300,70 @@ static void mip6_addr_swap(struct sk_buff *skb) static inline void mip6_addr_swap(struct sk_buff *skb) {} #endif +static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, + struct sock *sk, struct flowi *fl) +{ + struct dst_entry *dst, *dst2; + struct flowi fl2; + int err; + + err = ip6_dst_lookup(sk, &dst, fl); + if (err) + return ERR_PTR(err); + + /* + * We won't send icmp if the destination is known + * anycast. + */ + if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { + LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n"); + dst_release(dst); + return ERR_PTR(-EINVAL); + } + + /* No need to clone since we're just using its address. */ + dst2 = dst; + + err = xfrm_lookup(net, &dst, fl, sk, 0); + switch (err) { + case 0: + if (dst != dst2) + return dst; + break; + case -EPERM: + dst = NULL; + break; + default: + return ERR_PTR(err); + } + + err = xfrm_decode_session_reverse(skb, &fl2, AF_INET6); + if (err) + goto relookup_failed; + + err = ip6_dst_lookup(sk, &dst2, &fl2); + if (err) + goto relookup_failed; + + err = xfrm_lookup(net, &dst2, &fl2, sk, XFRM_LOOKUP_ICMP); + switch (err) { + case 0: + dst_release(dst); + dst = dst2; + break; + case -EPERM: + dst_release(dst); + return ERR_PTR(err); + default: + goto relookup_failed; + } + +relookup_failed: + if (dst) + return dst; + return ERR_PTR(err); +} + /* * Send an ICMP message in response to a packet in error */ @@ -312,10 +376,8 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) struct ipv6_pinfo *np; struct in6_addr *saddr = NULL; struct dst_entry *dst; - struct dst_entry *dst2; struct icmp6hdr tmp_hdr; struct flowi fl; - struct flowi fl2; struct icmpv6_msg msg; int iif = 0; int addr_type = 0; @@ -408,57 +470,10 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) fl.oif = np->mcast_oif; - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) - goto out; - - /* - * We won't send icmp if the destination is known - * anycast. - */ - if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { - LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n"); - goto out_dst_release; - } - - /* No need to clone since we're just using its address. */ - dst2 = dst; - - err = xfrm_lookup(net, &dst, &fl, sk, 0); - switch (err) { - case 0: - if (dst != dst2) - goto route_done; - break; - case -EPERM: - dst = NULL; - break; - default: + dst = icmpv6_route_lookup(net, skb, sk, &fl); + if (IS_ERR(dst)) goto out; - } - - if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6)) - goto relookup_failed; - - if (ip6_dst_lookup(sk, &dst2, &fl2)) - goto relookup_failed; - - err = xfrm_lookup(net, &dst2, &fl2, sk, XFRM_LOOKUP_ICMP); - switch (err) { - case 0: - dst_release(dst); - dst = dst2; - break; - case -EPERM: - goto out_dst_release; - default: -relookup_failed: - if (!dst) - goto out; - break; - } -route_done: if (ipv6_addr_is_multicast(&fl.fl6_dst)) hlimit = np->mcast_hops; else |