net: ipv6: Use passed in table for nexthop lookups
[cascardo/linux.git] / net / ipv6 / route.c
index ed44663..af46e19 100644 (file)
@@ -338,9 +338,9 @@ static struct rt6_info *__ip6_dst_alloc(struct net *net,
        return rt;
 }
 
-static struct rt6_info *ip6_dst_alloc(struct net *net,
-                                     struct net_device *dev,
-                                     int flags)
+struct rt6_info *ip6_dst_alloc(struct net *net,
+                              struct net_device *dev,
+                              int flags)
 {
        struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags);
 
@@ -364,6 +364,7 @@ static struct rt6_info *ip6_dst_alloc(struct net *net,
 
        return rt;
 }
+EXPORT_SYMBOL(ip6_dst_alloc);
 
 static void ip6_dst_destroy(struct dst_entry *dst)
 {
@@ -1417,8 +1418,20 @@ EXPORT_SYMBOL_GPL(ip6_update_pmtu);
 
 void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu)
 {
+       struct dst_entry *dst;
+
        ip6_update_pmtu(skb, sock_net(sk), mtu,
                        sk->sk_bound_dev_if, sk->sk_mark);
+
+       dst = __sk_dst_get(sk);
+       if (!dst || !dst->obsolete ||
+           dst->ops->check(dst, inet6_sk(sk)->dst_cookie))
+               return;
+
+       bh_lock_sock(sk);
+       if (!sock_owned_by_user(sk) && !ipv6_addr_v4mapped(&sk->sk_v6_daddr))
+               ip6_datagram_dst_update(sk, false);
+       bh_unlock_sock(sk);
 }
 EXPORT_SYMBOL_GPL(ip6_sk_update_pmtu);
 
@@ -1756,6 +1769,37 @@ static int ip6_convert_metrics(struct mx6_config *mxc,
        return -EINVAL;
 }
 
+static struct rt6_info *ip6_nh_lookup_table(struct net *net,
+                                           struct fib6_config *cfg,
+                                           const struct in6_addr *gw_addr)
+{
+       struct flowi6 fl6 = {
+               .flowi6_oif = cfg->fc_ifindex,
+               .daddr = *gw_addr,
+               .saddr = cfg->fc_prefsrc,
+       };
+       struct fib6_table *table;
+       struct rt6_info *rt;
+       int flags = 0;
+
+       table = fib6_get_table(net, cfg->fc_table);
+       if (!table)
+               return NULL;
+
+       if (!ipv6_addr_any(&cfg->fc_prefsrc))
+               flags |= RT6_LOOKUP_F_HAS_SADDR;
+
+       rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, flags);
+
+       /* if table lookup failed, fall back to full lookup */
+       if (rt == net->ipv6.ip6_null_entry) {
+               ip6_rt_put(rt);
+               rt = NULL;
+       }
+
+       return rt;
+}
+
 static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
 {
        struct net *net = cfg->fc_nlinfo.nl_net;
@@ -1927,7 +1971,7 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
                rt->rt6i_gateway = *gw_addr;
 
                if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) {
-                       struct rt6_info *grt;
+                       struct rt6_info *grt = NULL;
 
                        /* IPv6 strictly inhibits using not link-local
                           addresses as nexthop address.
@@ -1939,7 +1983,12 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg)
                        if (!(gwa_type & IPV6_ADDR_UNICAST))
                                goto out;
 
-                       grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1);
+                       if (cfg->fc_table)
+                               grt = ip6_nh_lookup_table(net, cfg, gw_addr);
+
+                       if (!grt)
+                               grt = rt6_lookup(net, gw_addr, NULL,
+                                                cfg->fc_ifindex, 1);
 
                        err = -EHOSTUNREACH;
                        if (!grt)