diff options
author | Martin KaFai Lau <kafai@fb.com> | 2015-05-22 20:56:03 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-05-25 13:25:34 -0400 |
commit | 3da59bd94583d1239e4fbdee452265a160b9cd71 (patch) | |
tree | b78147954d2bc53e4f45aec22240d2ef55e2d722 /net | |
parent | 48e8aa6e3137692d38f20e8bfff100e408c6bc53 (diff) | |
download | lwn-3da59bd94583d1239e4fbdee452265a160b9cd71.tar.gz lwn-3da59bd94583d1239e4fbdee452265a160b9cd71.zip |
ipv6: Create RTF_CACHE clone when FLOWI_FLAG_KNOWN_NH is set
This patch always creates RTF_CACHE clone with DST_NOCACHE
when FLOWI_FLAG_KNOWN_NH is set so that the rt6i_dst is set to
the fl6->daddr.
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Julian Anastasov <ja@ssi.bg>
Tested-by: Julian Anastasov <ja@ssi.bg>
Cc: Hannes Frederic Sowa <hannes@stressinduktion.org>
Cc: Steffen Klassert <steffen.klassert@secunet.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/route.c | 59 |
1 files changed, 49 insertions, 10 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index e7ae2430dfed..9e4c3c5d1591 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -901,13 +901,34 @@ redo_rt6_select: } } - dst_hold(&rt->dst); + dst_use(&rt->dst, jiffies); read_unlock_bh(&table->tb6_lock); - rt6_dst_from_metrics_check(rt); - rt->dst.lastuse = jiffies; - rt->dst.__use++; + if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) { + goto done; + } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && + !(rt->rt6i_flags & RTF_GATEWAY))) { + /* Create a RTF_CACHE clone which will not be + * owned by the fib6 tree. It is for the special case where + * the daddr in the skb during the neighbor look-up is different + * from the fl6->daddr used to look-up route here. + */ + + struct rt6_info *uncached_rt; + + uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL); + dst_release(&rt->dst); + + if (uncached_rt) + uncached_rt->dst.flags |= DST_NOCACHE; + else + uncached_rt = net->ipv6.ip6_null_entry; + dst_hold(&uncached_rt->dst); + return uncached_rt; + } +done: + rt6_dst_from_metrics_check(rt); return rt; } @@ -1019,6 +1040,26 @@ static void rt6_dst_from_metrics_check(struct rt6_info *rt) dst_init_metrics(&rt->dst, dst_metrics_ptr(rt->dst.from), true); } +static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie) +{ + if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) + return NULL; + + if (rt6_check_expired(rt)) + return NULL; + + return &rt->dst; +} + +static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie) +{ + if (rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK && + rt6_check((struct rt6_info *)(rt->dst.from), cookie)) + return &rt->dst; + else + return NULL; +} + static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) { struct rt6_info *rt; @@ -1029,15 +1070,13 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie) * DST_OBSOLETE_FORCE_CHK which forces validation calls down * into this function always. */ - if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie)) - return NULL; - - if (rt6_check_expired(rt)) - return NULL; rt6_dst_from_metrics_check(rt); - return dst; + if (unlikely(dst->flags & DST_NOCACHE)) + return rt6_dst_from_check(rt, cookie); + else + return rt6_check(rt, cookie); } static struct dst_entry *ip6_negative_advice(struct dst_entry *dst) |