ipv6: Pull main logic of rt6_redirect() into rt6_do_redirect().
authorDavid S. Miller <davem@davemloft.net>
Thu, 12 Jul 2012 07:05:02 +0000 (00:05 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 12 Jul 2012 07:05:02 +0000 (00:05 -0700)
Hook it into dst_ops->redirect as well.

Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/route.c

index 73cf3f7..545b152 100644 (file)
@@ -79,6 +79,7 @@ static int            ip6_pkt_discard(struct sk_buff *skb);
 static int             ip6_pkt_discard_out(struct sk_buff *skb);
 static void            ip6_link_failure(struct sk_buff *skb);
 static void            ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu);
+static void            rt6_do_redirect(struct dst_entry *dst, struct sk_buff *skb);
 
 #ifdef CONFIG_IPV6_ROUTE_INFO
 static struct rt6_info *rt6_add_route_info(struct net *net,
@@ -174,6 +175,7 @@ static struct dst_ops ip6_dst_ops_template = {
        .negative_advice        =       ip6_negative_advice,
        .link_failure           =       ip6_link_failure,
        .update_pmtu            =       ip6_rt_update_pmtu,
+       .redirect               =       rt6_do_redirect,
        .local_out              =       __ip6_local_out,
        .neigh_lookup           =       ip6_neigh_lookup,
 };
@@ -1690,28 +1692,26 @@ static struct rt6_info *ip6_route_redirect(const struct in6_addr *dest,
                                                   flags, __ip6_route_redirect);
 }
 
-void rt6_redirect(struct sk_buff *skb)
+static void rt6_do_redirect(struct dst_entry *dst, struct sk_buff *skb)
 {
        struct net *net = dev_net(skb->dev);
        struct netevent_redirect netevent;
        struct rt6_info *rt, *nrt = NULL;
        const struct in6_addr *target;
-       struct neighbour *old_neigh;
-       const struct in6_addr *dest;
-       const struct in6_addr *src;
-       const struct in6_addr *saddr;
        struct ndisc_options ndopts;
+       const struct in6_addr *dest;
+       struct neighbour *old_neigh;
        struct inet6_dev *in6_dev;
        struct neighbour *neigh;
        struct icmp6hdr *icmph;
-       int on_link, optlen;
-       u8 *lladdr = NULL;
+       int optlen, on_link;
+       u8 *lladdr;
 
        optlen = skb->tail - skb->transport_header;
        optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);
 
        if (optlen < 0) {
-               net_dbg_ratelimited("rt6_redirect: packet too short\n");
+               net_dbg_ratelimited("rt6_do_redirect: packet too short\n");
                return;
        }
 
@@ -1720,15 +1720,16 @@ void rt6_redirect(struct sk_buff *skb)
        dest = target + 1;
 
        if (ipv6_addr_is_multicast(dest)) {
-               net_dbg_ratelimited("rt6_redirect: destination address is multicast\n");
+               net_dbg_ratelimited("rt6_do_redirect: destination address is multicast\n");
                return;
        }
 
+       on_link = 0;
        if (ipv6_addr_equal(dest, target)) {
                on_link = 1;
        } else if (ipv6_addr_type(target) !=
                   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {
-               net_dbg_ratelimited("rt6_redirect: target address is not link-local unicast\n");
+               net_dbg_ratelimited("rt6_do_redirect: target address is not link-local unicast\n");
                return;
        }
 
@@ -1747,6 +1748,8 @@ void rt6_redirect(struct sk_buff *skb)
                net_dbg_ratelimited("rt6_redirect: invalid ND options\n");
                return;
        }
+
+       lladdr = NULL;
        if (ndopts.nd_opts_tgt_lladdr) {
                lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,
                                             skb->dev);
@@ -1756,19 +1759,26 @@ void rt6_redirect(struct sk_buff *skb)
                }
        }
 
-       neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
-       if (!neigh)
+       rt = (struct rt6_info *) dst;
+       if (rt == net->ipv6.ip6_null_entry) {
+               net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
                return;
+       }
 
-       src = &ipv6_hdr(skb)->daddr;
-       saddr = &ipv6_hdr(skb)->saddr;
+       /* Redirect received -> path was valid.
+        * Look, redirects are sent only in response to data packets,
+        * so that this nexthop apparently is reachable. --ANK
+        */
+       dst_confirm(&rt->dst);
 
-       rt = ip6_route_redirect(dest, src, saddr, neigh->dev);
+       neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);
+       if (!neigh)
+               return;
 
-       if (rt == net->ipv6.ip6_null_entry) {
-               net_dbg_ratelimited("rt6_redirect: source isn't a valid nexthop for redirect target\n");
+       /* Duplicate redirect: silently ignore. */
+       old_neigh = rt->n;
+       if (neigh == old_neigh)
                goto out;
-       }
 
        /*
         *      We have finally decided to accept it.
@@ -1781,18 +1791,6 @@ void rt6_redirect(struct sk_buff *skb)
                                     NEIGH_UPDATE_F_ISROUTER))
                     );
 
-       /*
-        * Redirect received -> path was valid.
-        * Look, redirects are sent only in response to data packets,
-        * so that this nexthop apparently is reachable. --ANK
-        */
-       dst_confirm(&rt->dst);
-
-       /* Duplicate redirect: silently ignore. */
-       old_neigh = rt->n;
-       if (neigh == old_neigh)
-               goto out;
-
        nrt = ip6_rt_copy(rt, dest);
        if (!nrt)
                goto out;
@@ -1815,12 +1813,32 @@ void rt6_redirect(struct sk_buff *skb)
        call_netevent_notifiers(NETEVENT_REDIRECT, &netevent);
 
        if (rt->rt6i_flags & RTF_CACHE) {
+               rt = (struct rt6_info *) dst_clone(&rt->dst);
                ip6_del_rt(rt);
-               return;
        }
 
 out:
        neigh_release(neigh);
+}
+
+void rt6_redirect(struct sk_buff *skb)
+{
+       const struct in6_addr *target;
+       const struct in6_addr *dest;
+       const struct in6_addr *src;
+       const struct in6_addr *saddr;
+       struct icmp6hdr *icmph;
+       struct rt6_info *rt;
+
+       icmph = icmp6_hdr(skb);
+       target = (const struct in6_addr *) (icmph + 1);
+       dest = target + 1;
+
+       src = &ipv6_hdr(skb)->daddr;
+       saddr = &ipv6_hdr(skb)->saddr;
+
+       rt = ip6_route_redirect(dest, src, saddr, skb->dev);
+       rt6_do_redirect(&rt->dst, skb);
        dst_release(&rt->dst);
 }