|
@@ -1156,6 +1156,77 @@ void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
|
|
|
}
|
|
|
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;
|
|
@@ -1170,9 +1241,8 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
|
|
|
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);
|
|
@@ -1192,9 +1262,8 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
|
|
|
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);
|
|
|
}
|
|
|
|