if (type == ICMPV6_PKT_TOOBIG)
                ip6_update_pmtu(skb, net, info, 0, 0);
        else if (type == NDISC_REDIRECT)
-               ip6_redirect(skb, net, 0, 0);
+               ip6_redirect(skb, net, skb->dev->ifindex, 0);
 
        if (!(type & ICMPV6_INFOMSG_MASK))
                if (icmp6->icmp6_type == ICMPV6_ECHO_REQUEST)
 
 }
 EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
 
+/* Handle redirects */
+struct ip6rd_flowi {
+       struct flowi6 fl6;
+       struct in6_addr gateway;
+};
+
+static struct rt6_info *__ip6_route_redirect(struct net *net,
+                                            struct fib6_table *table,
+                                            struct flowi6 *fl6,
+                                            int flags)
+{
+       struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6;
+       struct rt6_info *rt;
+       struct fib6_node *fn;
+
+       /* Get the "current" route for this destination and
+        * check if the redirect has come from approriate router.
+        *
+        * RFC 4861 specifies that redirects should only be
+        * accepted if they come from the nexthop to the target.
+        * Due to the way the routes are chosen, this notion
+        * is a bit fuzzy and one might need to check all possible
+        * routes.
+        */
+
+       read_lock_bh(&table->tb6_lock);
+       fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
+restart:
+       for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) {
+               if (rt6_check_expired(rt))
+                       continue;
+               if (rt->dst.error)
+                       break;
+               if (!(rt->rt6i_flags & RTF_GATEWAY))
+                       continue;
+               if (fl6->flowi6_oif != rt->dst.dev->ifindex)
+                       continue;
+               if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway))
+                       continue;
+               break;
+       }
+
+       if (!rt)
+               rt = net->ipv6.ip6_null_entry;
+       else if (rt->dst.error) {
+               rt = net->ipv6.ip6_null_entry;
+               goto out;
+       }
+       BACKTRACK(net, &fl6->saddr);
+out:
+       dst_hold(&rt->dst);
+
+       read_unlock_bh(&table->tb6_lock);
+
+       return rt;
+};
+
+static struct dst_entry *ip6_route_redirect(struct net *net,
+                                       const struct flowi6 *fl6,
+                                       const struct in6_addr *gateway)
+{
+       int flags = RT6_LOOKUP_F_HAS_SADDR;
+       struct ip6rd_flowi rdfl;
+
+       rdfl.fl6 = *fl6;
+       rdfl.gateway = *gateway;
+
+       return fib6_rule_lookup(net, &rdfl.fl6,
+                               flags, __ip6_route_redirect);
+}
+
 void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
 {
        const struct ipv6hdr *iph = (struct ipv6hdr *) skb->data;
        fl6.saddr = iph->saddr;
        fl6.flowlabel = ip6_flowinfo(iph);
 
-       dst = ip6_route_output(net, NULL, &fl6);
-       if (!dst->error)
-               rt6_do_redirect(dst, NULL, skb);
+       dst = ip6_route_redirect(net, &fl6, &ipv6_hdr(skb)->saddr);
+       rt6_do_redirect(dst, NULL, skb);
        dst_release(dst);
 }
 EXPORT_SYMBOL_GPL(ip6_redirect);
        fl6.daddr = msg->dest;
        fl6.saddr = iph->daddr;
 
-       dst = ip6_route_output(net, NULL, &fl6);
-       if (!dst->error)
-               rt6_do_redirect(dst, NULL, skb);
+       dst = ip6_route_redirect(net, &fl6, &iph->saddr);
+       rt6_do_redirect(dst, NULL, skb);
        dst_release(dst);
 }