summaryrefslogtreecommitdiffstats
path: root/net/ipv6/icmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/icmp.c')
-rw-r--r--net/ipv6/icmp.c134
1 files changed, 75 insertions, 59 deletions
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 03e62f9..5566595 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -157,20 +157,20 @@ static int is_ineligible(struct sk_buff *skb)
/*
* Check the ICMP output rate limit
*/
-static inline int icmpv6_xrlim_allow(struct sock *sk, u8 type,
- struct flowi *fl)
+static inline bool icmpv6_xrlim_allow(struct sock *sk, u8 type,
+ struct flowi *fl)
{
struct dst_entry *dst;
struct net *net = sock_net(sk);
- int res = 0;
+ bool res = false;
/* Informational messages are not limited. */
if (type & ICMPV6_INFOMSG_MASK)
- return 1;
+ return true;
/* Do not limit pmtu discovery, it would break it. */
if (type == ICMPV6_PKT_TOOBIG)
- return 1;
+ return true;
/*
* Look up the output route.
@@ -182,7 +182,7 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, u8 type,
IP6_INC_STATS(net, ip6_dst_idev(dst),
IPSTATS_MIB_OUTNOROUTES);
} else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) {
- res = 1;
+ res = true;
} else {
struct rt6_info *rt = (struct rt6_info *)dst;
int tmo = net->ipv6.sysctl.icmpv6_time;
@@ -191,7 +191,9 @@ static inline int icmpv6_xrlim_allow(struct sock *sk, u8 type,
if (rt->rt6i_dst.plen < 128)
tmo >>= ((128 - rt->rt6i_dst.plen)>>5);
- res = xrlim_allow(dst, tmo);
+ if (!rt->rt6i_peer)
+ rt6_bind_peer(rt, 1);
+ res = inet_peer_xrlim_allow(rt->rt6i_peer, tmo);
}
dst_release(dst);
return res;
@@ -298,6 +300,68 @@ 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;
+
+ dst = xfrm_lookup(net, dst, fl, sk, 0);
+ if (!IS_ERR(dst)) {
+ if (dst != dst2)
+ return dst;
+ } else {
+ if (PTR_ERR(dst) == -EPERM)
+ dst = NULL;
+ else
+ return dst;
+ }
+
+ 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;
+
+ dst2 = xfrm_lookup(net, dst2, &fl2, sk, XFRM_LOOKUP_ICMP);
+ if (!IS_ERR(dst2)) {
+ dst_release(dst);
+ dst = dst2;
+ } else {
+ err = PTR_ERR(dst2);
+ if (err == -EPERM) {
+ dst_release(dst);
+ return dst2;
+ } else
+ goto relookup_failed;
+ }
+
+relookup_failed:
+ if (dst)
+ return dst;
+ return ERR_PTR(err);
+}
+
/*
* Send an ICMP message in response to a packet in error
*/
@@ -310,10 +374,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;
@@ -406,57 +468,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
@@ -543,7 +558,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
err = ip6_dst_lookup(sk, &dst, &fl);
if (err)
goto out;
- if ((err = xfrm_lookup(net, &dst, &fl, sk, 0)) < 0)
+ dst = xfrm_lookup(net, dst, &fl, sk, 0);
+ if (IS_ERR(dst))
goto out;
if (ipv6_addr_is_multicast(&fl.fl6_dst))
OpenPOWER on IntegriCloud