|
@@ -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
|