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>
This commit is contained in:
David S. Miller 2011-03-01 22:06:22 -08:00
parent f6d460cf0e
commit b42835dbe8
1 changed files with 66 additions and 51 deletions

View File

@ -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)
dst = icmpv6_route_lookup(net, skb, sk, &fl);
if (IS_ERR(dst))
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:
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