aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/route.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv6/route.c')
-rw-r--r--net/ipv6/route.c58
1 files changed, 34 insertions, 24 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 23a20d62daac..7a014ca877ed 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -111,8 +111,8 @@ static int rt6_fill_node(struct net *net, struct sk_buff *skb,
111 int iif, int type, u32 portid, u32 seq, 111 int iif, int type, u32 portid, u32 seq,
112 unsigned int flags); 112 unsigned int flags);
113static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res, 113static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
114 struct in6_addr *daddr, 114 const struct in6_addr *daddr,
115 struct in6_addr *saddr); 115 const struct in6_addr *saddr);
116 116
117#ifdef CONFIG_IPV6_ROUTE_INFO 117#ifdef CONFIG_IPV6_ROUTE_INFO
118static struct fib6_info *rt6_add_route_info(struct net *net, 118static struct fib6_info *rt6_add_route_info(struct net *net,
@@ -1295,6 +1295,13 @@ static struct rt6_info *rt6_make_pcpu_route(struct net *net,
1295 prev = cmpxchg(p, NULL, pcpu_rt); 1295 prev = cmpxchg(p, NULL, pcpu_rt);
1296 BUG_ON(prev); 1296 BUG_ON(prev);
1297 1297
1298 if (res->f6i->fib6_destroying) {
1299 struct fib6_info *from;
1300
1301 from = xchg((__force struct fib6_info **)&pcpu_rt->from, NULL);
1302 fib6_info_release(from);
1303 }
1304
1298 return pcpu_rt; 1305 return pcpu_rt;
1299} 1306}
1300 1307
@@ -1566,31 +1573,44 @@ out:
1566 * Caller has to hold rcu_read_lock() 1573 * Caller has to hold rcu_read_lock()
1567 */ 1574 */
1568static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res, 1575static struct rt6_info *rt6_find_cached_rt(const struct fib6_result *res,
1569 struct in6_addr *daddr, 1576 const struct in6_addr *daddr,
1570 struct in6_addr *saddr) 1577 const struct in6_addr *saddr)
1571{ 1578{
1579 const struct in6_addr *src_key = NULL;
1572 struct rt6_exception_bucket *bucket; 1580 struct rt6_exception_bucket *bucket;
1573 struct in6_addr *src_key = NULL;
1574 struct rt6_exception *rt6_ex; 1581 struct rt6_exception *rt6_ex;
1575 struct rt6_info *ret = NULL; 1582 struct rt6_info *ret = NULL;
1576 1583
1577 bucket = rcu_dereference(res->f6i->rt6i_exception_bucket);
1578
1579#ifdef CONFIG_IPV6_SUBTREES 1584#ifdef CONFIG_IPV6_SUBTREES
1580 /* fib6i_src.plen != 0 indicates f6i is in subtree 1585 /* fib6i_src.plen != 0 indicates f6i is in subtree
1581 * and exception table is indexed by a hash of 1586 * and exception table is indexed by a hash of
1582 * both fib6_dst and fib6_src. 1587 * both fib6_dst and fib6_src.
1583 * Otherwise, the exception table is indexed by 1588 * However, the src addr used to create the hash
1584 * a hash of only fib6_dst. 1589 * might not be exactly the passed in saddr which
1590 * is a /128 addr from the flow.
1591 * So we need to use f6i->fib6_src to redo lookup
1592 * if the passed in saddr does not find anything.
1593 * (See the logic in ip6_rt_cache_alloc() on how
1594 * rt->rt6i_src is updated.)
1585 */ 1595 */
1586 if (res->f6i->fib6_src.plen) 1596 if (res->f6i->fib6_src.plen)
1587 src_key = saddr; 1597 src_key = saddr;
1598find_ex:
1588#endif 1599#endif
1600 bucket = rcu_dereference(res->f6i->rt6i_exception_bucket);
1589 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key); 1601 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
1590 1602
1591 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i)) 1603 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
1592 ret = rt6_ex->rt6i; 1604 ret = rt6_ex->rt6i;
1593 1605
1606#ifdef CONFIG_IPV6_SUBTREES
1607 /* Use fib6_src as src_key and redo lookup */
1608 if (!ret && src_key && src_key != &res->f6i->fib6_src.addr) {
1609 src_key = &res->f6i->fib6_src.addr;
1610 goto find_ex;
1611 }
1612#endif
1613
1594 return ret; 1614 return ret;
1595} 1615}
1596 1616
@@ -2665,12 +2685,10 @@ u32 ip6_mtu_from_fib6(const struct fib6_result *res,
2665 const struct in6_addr *daddr, 2685 const struct in6_addr *daddr,
2666 const struct in6_addr *saddr) 2686 const struct in6_addr *saddr)
2667{ 2687{
2668 struct rt6_exception_bucket *bucket;
2669 const struct fib6_nh *nh = res->nh; 2688 const struct fib6_nh *nh = res->nh;
2670 struct fib6_info *f6i = res->f6i; 2689 struct fib6_info *f6i = res->f6i;
2671 const struct in6_addr *src_key;
2672 struct rt6_exception *rt6_ex;
2673 struct inet6_dev *idev; 2690 struct inet6_dev *idev;
2691 struct rt6_info *rt;
2674 u32 mtu = 0; 2692 u32 mtu = 0;
2675 2693
2676 if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) { 2694 if (unlikely(fib6_metric_locked(f6i, RTAX_MTU))) {
@@ -2679,18 +2697,10 @@ u32 ip6_mtu_from_fib6(const struct fib6_result *res,
2679 goto out; 2697 goto out;
2680 } 2698 }
2681 2699
2682 src_key = NULL; 2700 rt = rt6_find_cached_rt(res, daddr, saddr);
2683#ifdef CONFIG_IPV6_SUBTREES 2701 if (unlikely(rt)) {
2684 if (f6i->fib6_src.plen) 2702 mtu = dst_metric_raw(&rt->dst, RTAX_MTU);
2685 src_key = saddr; 2703 } else {
2686#endif
2687
2688 bucket = rcu_dereference(f6i->rt6i_exception_bucket);
2689 rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key);
2690 if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i))
2691 mtu = dst_metric_raw(&rt6_ex->rt6i->dst, RTAX_MTU);
2692
2693 if (likely(!mtu)) {
2694 struct net_device *dev = nh->fib_nh_dev; 2704 struct net_device *dev = nh->fib_nh_dev;
2695 2705
2696 mtu = IPV6_MIN_MTU; 2706 mtu = IPV6_MIN_MTU;