From mboxrd@z Thu Jan 1 00:00:00 1970 From: Gao feng Subject: [PATCH] ipv6: Fix problem with expired dst cache Date: Wed, 29 Feb 2012 17:45:43 +0800 Message-ID: <1330508743-20005-1-git-send-email-gaofeng@cn.fujitsu.com> References: <4F4DEF2F.7010109@cn.fujitsu.com> Cc: netdev@vger.kernel.org, eric.dumazet@gmail.com, gaofeng@cn.fujitsu.com To: davem@davemloft.net Return-path: Received: from cn.fujitsu.com ([222.73.24.84]:61771 "EHLO song.cn.fujitsu.com" rhost-flags-OK-FAIL-OK-OK) by vger.kernel.org with ESMTP id S964991Ab2B2Joy (ORCPT ); Wed, 29 Feb 2012 04:44:54 -0500 In-Reply-To: <4F4DEF2F.7010109@cn.fujitsu.com> Sender: netdev-owner@vger.kernel.org List-ID: If the ipv6 dst cache which copy from the dst generated by ICMPV6 RA packet. this dst cache will not check expire because it has no RTF_EXPIRES flag. So this dst cache will always be used until the dst gc run. Change the struct dst_entry,add a union contains new pointer from and expires. When rt6_info.rt6i_flags has no RTF_EXPIRES flag,the dst.expires has no use. we can use this field to point to where the dst cache copy from. The dst.from is only used in IPV6. In func rt6_check_expired check if rt6_info.dst.from is expired. In func ip6_rt_copy only set dst.from when the ort has flag RTF_ADDRCONF and RTF_DEFAULT.then hold the ort. In func ip6_dst_destroy release the ort. Add some functions to operate the RTF_EXPIRES flag and expires(from) and change the code to use these new adding functions. Signed-off-by: Gao feng --- include/net/dst.h | 11 ++++++++++- include/net/ip6_fib.h | 41 +++++++++++++++++++++++++++++++++++++++++ net/ipv6/addrconf.c | 9 +++------ net/ipv6/ip6_fib.c | 3 +-- net/ipv6/route.c | 47 +++++++++++++++++++++++++++++------------------ 5 files changed, 84 insertions(+), 27 deletions(-) diff --git a/include/net/dst.h b/include/net/dst.h index 344c8dd..5147839 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -35,7 +35,16 @@ struct dst_entry { struct net_device *dev; struct dst_ops *ops; unsigned long _metrics; - unsigned long expires; + + union { + unsigned long expires; + /* + * from is used only for dst cache witch copy form + * the dst generated by ipv6 RA. + * from is set only when rt6_info has no RTF_EXPIRES flag. + */ + void *from; + }; struct dst_entry *path; struct neighbour __rcu *_neighbour; #ifdef CONFIG_XFRM diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index b26bb81..86cf1ac 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -123,6 +123,47 @@ static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst) return ((struct rt6_info *)dst)->rt6i_idev; } +static inline void rt6_clean_expires(struct rt6_info *rt) +{ + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) + dst_release(&rt->dst); + + rt->rt6i_flags &= ~RTF_EXPIRES; + rt->dst.expires = 0; +} + +static inline void rt6_set_expires(struct rt6_info *rt, unsigned long expires) +{ + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) + dst_release(&rt->dst); + + rt->rt6i_flags |= RTF_EXPIRES; + rt->dst.expires = expires; +} + +static inline void rt6_update_expires(struct rt6_info *rt, int timeout) +{ + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) + dst_release(&rt->dst); + + dst_set_expires(&rt->dst, timeout); + rt->rt6i_flags |= RTF_EXPIRES; +} + +static inline void rt6_set_from(struct rt6_info *rt, struct rt6_info *from) +{ + if (!(rt->rt6i_flags & RTF_EXPIRES) && rt->dst.from) { + if (from == rt->dst.from) + return; + else + dst_release((struct dst_entry *) &rt->dst.from); + } + + rt->rt6i_flags &= ~RTF_EXPIRES; + rt->dst.from = (void *) from; + dst_hold(&from->dst); +} + struct fib6_walker_t { struct list_head lh; struct fib6_node *root, *node; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c02280a..b5cb3b1 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -799,8 +799,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) ip6_del_rt(rt); rt = NULL; } else if (!(rt->rt6i_flags & RTF_EXPIRES)) { - rt->dst.expires = expires; - rt->rt6i_flags |= RTF_EXPIRES; + rt6_set_expires(rt, expires); } } dst_release(&rt->dst); @@ -1883,11 +1882,9 @@ void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao) rt = NULL; } else if (addrconf_finite_timeout(rt_expires)) { /* not infinity */ - rt->dst.expires = jiffies + rt_expires; - rt->rt6i_flags |= RTF_EXPIRES; + rt6_set_expires(rt, jiffies + rt_expires); } else { - rt->rt6i_flags &= ~RTF_EXPIRES; - rt->dst.expires = 0; + rt6_clean_expires(rt); } } else if (valid_lft) { clock_t expires = 0; diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index b82bcde..9a4f51e 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -675,8 +675,7 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, return -EEXIST; iter->dst.expires = rt->dst.expires; if (!(rt->rt6i_flags & RTF_EXPIRES)) { - iter->rt6i_flags &= ~RTF_EXPIRES; - iter->dst.expires = 0; + rt6_clean_expires(iter); } return -EEXIST; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 8c2e3ab..1f6d40a 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -62,7 +62,7 @@ #include #endif -static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort, +static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, const struct in6_addr *dest); static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie); static unsigned int ip6_default_advmss(const struct dst_entry *dst); @@ -272,6 +272,10 @@ static void ip6_dst_destroy(struct dst_entry *dst) rt->rt6i_idev = NULL; in6_dev_put(idev); } + + if (!(rt->rt6i_flags & RTF_EXPIRES) && dst->from) + dst_release((struct dst_entry *)dst->from); + if (peer) { rt->rt6i_peer = NULL; inet_putpeer(peer); @@ -316,8 +320,18 @@ static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, static __inline__ int rt6_check_expired(const struct rt6_info *rt) { - return (rt->rt6i_flags & RTF_EXPIRES) && - time_after(jiffies, rt->dst.expires); + struct rt6_info *ort = NULL; + + if (rt->rt6i_flags & RTF_EXPIRES) { + if (time_after(jiffies, rt->dst.expires)) + return 1; + } else if (rt->dst.from) { + ort = (struct rt6_info *) rt->dst.from; + return (ort->rt6i_flags & RTF_EXPIRES) && + time_after(jiffies, ort->dst.expires); + } + + return 0; } static inline int rt6_need_strict(const struct in6_addr *daddr) @@ -608,10 +622,9 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, if (rt) { if (!addrconf_finite_timeout(lifetime)) { - rt->rt6i_flags &= ~RTF_EXPIRES; + rt6_clean_expires(rt); } else { - rt->dst.expires = jiffies + HZ * lifetime; - rt->rt6i_flags |= RTF_EXPIRES; + rt6_set_expires(rt, jiffies + HZ * lifetime); } dst_release(&rt->dst); } @@ -717,7 +730,7 @@ int ip6_ins_rt(struct rt6_info *rt) return __ip6_ins_rt(rt, &info); } -static struct rt6_info *rt6_alloc_cow(const struct rt6_info *ort, +static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, const struct in6_addr *daddr, const struct in6_addr *saddr) { @@ -934,10 +947,9 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori rt->rt6i_idev = ort->rt6i_idev; if (rt->rt6i_idev) in6_dev_hold(rt->rt6i_idev); - rt->dst.expires = 0; rt->rt6i_gateway = ort->rt6i_gateway; - rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES; + rt6_clean_expires(rt); rt->rt6i_metric = 0; memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key)); @@ -1000,8 +1012,7 @@ static void ip6_link_failure(struct sk_buff *skb) rt = (struct rt6_info *) skb_dst(skb); if (rt) { if (rt->rt6i_flags & RTF_CACHE) { - dst_set_expires(&rt->dst, 0); - rt->rt6i_flags |= RTF_EXPIRES; + rt6_update_expires(rt, 0); } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) rt->rt6i_node->fn_sernum = -1; } @@ -1716,8 +1727,8 @@ again: features |= RTAX_FEATURE_ALLFRAG; dst_metric_set(&rt->dst, RTAX_FEATURES, features); } - dst_set_expires(&rt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires); - rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES; + rt6_update_expires(rt, net->ipv6.sysctl.ip6_rt_mtu_expires); + rt->rt6i_flags |= RTF_MODIFIED; goto out; } @@ -1745,8 +1756,8 @@ again: * which is 10 mins. After 10 mins the decreased pmtu is expired * and detecting PMTU increase will be automatically happened. */ - dst_set_expires(&nrt->dst, net->ipv6.sysctl.ip6_rt_mtu_expires); - nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES; + rt6_update_expires(nrt, net->ipv6.sysctl.ip6_rt_mtu_expires); + nrt->rt6i_flags |= RTF_DYNAMIC; ip6_ins_rt(nrt); } @@ -1779,7 +1790,7 @@ void rt6_pmtu_discovery(const struct in6_addr *daddr, const struct in6_addr *sad * Misc support functions */ -static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort, +static struct rt6_info *ip6_rt_copy(struct rt6_info *ort, const struct in6_addr *dest) { struct net *net = dev_net(ort->dst.dev); @@ -1799,10 +1810,10 @@ static struct rt6_info *ip6_rt_copy(const struct rt6_info *ort, if (rt->rt6i_idev) in6_dev_hold(rt->rt6i_idev); rt->dst.lastuse = jiffies; - rt->dst.expires = 0; rt->rt6i_gateway = ort->rt6i_gateway; - rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES; + rt6_set_from(rt, ort); + rt->rt6i_metric = 0; #ifdef CONFIG_IPV6_SUBTREES -- 1.7.5.4