|
@@ -359,7 +359,7 @@ EXPORT_SYMBOL(ip6_dst_alloc);
|
|
|
static void ip6_dst_destroy(struct dst_entry *dst)
|
|
|
{
|
|
|
struct rt6_info *rt = (struct rt6_info *)dst;
|
|
|
- struct fib6_info *from = rt->from;
|
|
|
+ struct fib6_info *from;
|
|
|
struct inet6_dev *idev;
|
|
|
|
|
|
dst_destroy_metrics_generic(dst);
|
|
@@ -371,8 +371,11 @@ static void ip6_dst_destroy(struct dst_entry *dst)
|
|
|
in6_dev_put(idev);
|
|
|
}
|
|
|
|
|
|
- rt->from = NULL;
|
|
|
+ rcu_read_lock();
|
|
|
+ from = rcu_dereference(rt->from);
|
|
|
+ rcu_assign_pointer(rt->from, NULL);
|
|
|
fib6_info_release(from);
|
|
|
+ rcu_read_unlock();
|
|
|
}
|
|
|
|
|
|
static void ip6_dst_ifdown(struct dst_entry *dst, struct net_device *dev,
|
|
@@ -402,12 +405,16 @@ static bool __rt6_check_expired(const struct rt6_info *rt)
|
|
|
|
|
|
static bool rt6_check_expired(const struct rt6_info *rt)
|
|
|
{
|
|
|
+ struct fib6_info *from;
|
|
|
+
|
|
|
+ from = rcu_dereference(rt->from);
|
|
|
+
|
|
|
if (rt->rt6i_flags & RTF_EXPIRES) {
|
|
|
if (time_after(jiffies, rt->dst.expires))
|
|
|
return true;
|
|
|
- } else if (rt->from) {
|
|
|
+ } else if (from) {
|
|
|
return rt->dst.obsolete != DST_OBSOLETE_FORCE_CHK ||
|
|
|
- fib6_check_expired(rt->from);
|
|
|
+ fib6_check_expired(from);
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
@@ -963,7 +970,7 @@ static void rt6_set_from(struct rt6_info *rt, struct fib6_info *from)
|
|
|
{
|
|
|
rt->rt6i_flags &= ~RTF_EXPIRES;
|
|
|
fib6_info_hold(from);
|
|
|
- rt->from = from;
|
|
|
+ rcu_assign_pointer(rt->from, from);
|
|
|
dst_init_metrics(&rt->dst, from->fib6_metrics->metrics, true);
|
|
|
if (from->fib6_metrics != &dst_default_metrics) {
|
|
|
rt->dst._metrics |= DST_METRICS_REFCOUNTED;
|
|
@@ -1164,10 +1171,8 @@ static struct rt6_info *ip6_rt_cache_alloc(struct fib6_info *ort,
|
|
|
* Clone the route.
|
|
|
*/
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
dev = ip6_rt_get_dev_rcu(ort);
|
|
|
rt = ip6_dst_alloc(dev_net(dev), dev, 0);
|
|
|
- rcu_read_unlock();
|
|
|
if (!rt)
|
|
|
return NULL;
|
|
|
|
|
@@ -1855,14 +1860,11 @@ redo_rt6_select:
|
|
|
* 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;
|
|
|
|
|
|
- fib6_info_hold(f6i);
|
|
|
- rcu_read_unlock();
|
|
|
-
|
|
|
uncached_rt = ip6_rt_cache_alloc(f6i, &fl6->daddr, NULL);
|
|
|
- fib6_info_release(f6i);
|
|
|
+
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
|
if (uncached_rt) {
|
|
|
/* Uncached_rt's refcnt is taken during ip6_rt_cache_alloc()
|
|
@@ -2128,8 +2130,7 @@ static bool fib6_check(struct fib6_info *f6i, u32 cookie)
|
|
|
{
|
|
|
u32 rt_cookie = 0;
|
|
|
|
|
|
- if ((f6i && !rt6_get_cookie_safe(f6i, &rt_cookie)) ||
|
|
|
- rt_cookie != cookie)
|
|
|
+ if (!fib6_get_cookie_safe(f6i, &rt_cookie) || rt_cookie != cookie)
|
|
|
return false;
|
|
|
|
|
|
if (fib6_check_expired(f6i))
|
|
@@ -2138,11 +2139,13 @@ static bool fib6_check(struct fib6_info *f6i, u32 cookie)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
|
|
|
+static struct dst_entry *rt6_check(struct rt6_info *rt,
|
|
|
+ struct fib6_info *from,
|
|
|
+ u32 cookie)
|
|
|
{
|
|
|
u32 rt_cookie = 0;
|
|
|
|
|
|
- if ((rt->from && !rt6_get_cookie_safe(rt->from, &rt_cookie)) ||
|
|
|
+ if ((from && !fib6_get_cookie_safe(from, &rt_cookie)) ||
|
|
|
rt_cookie != cookie)
|
|
|
return NULL;
|
|
|
|
|
@@ -2152,11 +2155,13 @@ static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
|
|
|
return &rt->dst;
|
|
|
}
|
|
|
|
|
|
-static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
|
|
|
+static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt,
|
|
|
+ struct fib6_info *from,
|
|
|
+ u32 cookie)
|
|
|
{
|
|
|
if (!__rt6_check_expired(rt) &&
|
|
|
rt->dst.obsolete == DST_OBSOLETE_FORCE_CHK &&
|
|
|
- fib6_check(rt->from, cookie))
|
|
|
+ fib6_check(from, cookie))
|
|
|
return &rt->dst;
|
|
|
else
|
|
|
return NULL;
|
|
@@ -2164,20 +2169,30 @@ static struct dst_entry *rt6_dst_from_check(struct rt6_info *rt, u32 cookie)
|
|
|
|
|
|
static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
|
|
|
{
|
|
|
+ struct dst_entry *dst_ret;
|
|
|
+ struct fib6_info *from;
|
|
|
struct rt6_info *rt;
|
|
|
|
|
|
- rt = (struct rt6_info *) dst;
|
|
|
+ rt = container_of(dst, struct rt6_info, dst);
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
|
|
|
/* All IPV6 dsts are created with ->obsolete set to the value
|
|
|
* DST_OBSOLETE_FORCE_CHK which forces validation calls down
|
|
|
* into this function always.
|
|
|
*/
|
|
|
|
|
|
- if (rt->rt6i_flags & RTF_PCPU ||
|
|
|
- (unlikely(!list_empty(&rt->rt6i_uncached)) && rt->from))
|
|
|
- return rt6_dst_from_check(rt, cookie);
|
|
|
+ from = rcu_dereference(rt->from);
|
|
|
+
|
|
|
+ if (from && (rt->rt6i_flags & RTF_PCPU ||
|
|
|
+ unlikely(!list_empty(&rt->rt6i_uncached))))
|
|
|
+ dst_ret = rt6_dst_from_check(rt, from, cookie);
|
|
|
else
|
|
|
- return rt6_check(rt, cookie);
|
|
|
+ dst_ret = rt6_check(rt, from, cookie);
|
|
|
+
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ return dst_ret;
|
|
|
}
|
|
|
|
|
|
static struct dst_entry *ip6_negative_advice(struct dst_entry *dst)
|
|
@@ -2209,18 +2224,38 @@ static void ip6_link_failure(struct sk_buff *skb)
|
|
|
if (rt->rt6i_flags & RTF_CACHE) {
|
|
|
if (dst_hold_safe(&rt->dst))
|
|
|
rt6_remove_exception_rt(rt);
|
|
|
- } else if (rt->from) {
|
|
|
+ } else {
|
|
|
+ struct fib6_info *from;
|
|
|
struct fib6_node *fn;
|
|
|
|
|
|
rcu_read_lock();
|
|
|
- fn = rcu_dereference(rt->from->fib6_node);
|
|
|
- if (fn && (rt->rt6i_flags & RTF_DEFAULT))
|
|
|
- fn->fn_sernum = -1;
|
|
|
+ from = rcu_dereference(rt->from);
|
|
|
+ if (from) {
|
|
|
+ fn = rcu_dereference(from->fib6_node);
|
|
|
+ if (fn && (rt->rt6i_flags & RTF_DEFAULT))
|
|
|
+ fn->fn_sernum = -1;
|
|
|
+ }
|
|
|
rcu_read_unlock();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void rt6_update_expires(struct rt6_info *rt0, int timeout)
|
|
|
+{
|
|
|
+ if (!(rt0->rt6i_flags & RTF_EXPIRES)) {
|
|
|
+ struct fib6_info *from;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ from = rcu_dereference(rt0->from);
|
|
|
+ if (from)
|
|
|
+ rt0->dst.expires = from->expires;
|
|
|
+ rcu_read_unlock();
|
|
|
+ }
|
|
|
+
|
|
|
+ dst_set_expires(&rt0->dst, timeout);
|
|
|
+ rt0->rt6i_flags |= RTF_EXPIRES;
|
|
|
+}
|
|
|
+
|
|
|
static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
|
|
|
{
|
|
|
struct net *net = dev_net(rt->dst.dev);
|
|
@@ -2232,8 +2267,14 @@ static void rt6_do_update_pmtu(struct rt6_info *rt, u32 mtu)
|
|
|
|
|
|
static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
|
|
|
{
|
|
|
+ bool from_set;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ from_set = !!rcu_dereference(rt->from);
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
return !(rt->rt6i_flags & RTF_CACHE) &&
|
|
|
- (rt->rt6i_flags & RTF_PCPU || rt->from);
|
|
|
+ (rt->rt6i_flags & RTF_PCPU || from_set);
|
|
|
}
|
|
|
|
|
|
static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
|
|
@@ -2269,14 +2310,18 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
|
|
|
if (rt6->rt6i_flags & RTF_CACHE)
|
|
|
rt6_update_exception_stamp_rt(rt6);
|
|
|
} else if (daddr) {
|
|
|
+ struct fib6_info *from;
|
|
|
struct rt6_info *nrt6;
|
|
|
|
|
|
- nrt6 = ip6_rt_cache_alloc(rt6->from, daddr, saddr);
|
|
|
+ rcu_read_lock();
|
|
|
+ from = rcu_dereference(rt6->from);
|
|
|
+ nrt6 = ip6_rt_cache_alloc(from, daddr, saddr);
|
|
|
if (nrt6) {
|
|
|
rt6_do_update_pmtu(nrt6, mtu);
|
|
|
- if (rt6_insert_exception(nrt6, rt6->from))
|
|
|
+ if (rt6_insert_exception(nrt6, from))
|
|
|
dst_release_immediate(&nrt6->dst);
|
|
|
}
|
|
|
+ rcu_read_unlock();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -3209,6 +3254,7 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
|
|
|
struct ndisc_options ndopts;
|
|
|
struct inet6_dev *in6_dev;
|
|
|
struct neighbour *neigh;
|
|
|
+ struct fib6_info *from;
|
|
|
struct rd_msg *msg;
|
|
|
int optlen, on_link;
|
|
|
u8 *lladdr;
|
|
@@ -3290,7 +3336,10 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu
|
|
|
NEIGH_UPDATE_F_ISROUTER)),
|
|
|
NDISC_REDIRECT, &ndopts);
|
|
|
|
|
|
- nrt = ip6_rt_cache_alloc(rt->from, &msg->dest, NULL);
|
|
|
+ rcu_read_lock();
|
|
|
+ from = rcu_dereference(rt->from);
|
|
|
+ nrt = ip6_rt_cache_alloc(from, &msg->dest, NULL);
|
|
|
+ rcu_read_unlock();
|
|
|
if (!nrt)
|
|
|
goto out;
|
|
|
|
|
@@ -4672,6 +4721,7 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
|
|
|
struct net *net = sock_net(in_skb->sk);
|
|
|
struct nlattr *tb[RTA_MAX+1];
|
|
|
int err, iif = 0, oif = 0;
|
|
|
+ struct fib6_info *from;
|
|
|
struct dst_entry *dst;
|
|
|
struct rt6_info *rt;
|
|
|
struct sk_buff *skb;
|
|
@@ -4768,15 +4818,21 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh,
|
|
|
}
|
|
|
|
|
|
skb_dst_set(skb, &rt->dst);
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ from = rcu_dereference(rt->from);
|
|
|
+
|
|
|
if (fibmatch)
|
|
|
- err = rt6_fill_node(net, skb, rt->from, NULL, NULL, NULL, iif,
|
|
|
+ err = rt6_fill_node(net, skb, from, NULL, NULL, NULL, iif,
|
|
|
RTM_NEWROUTE, NETLINK_CB(in_skb).portid,
|
|
|
nlh->nlmsg_seq, 0);
|
|
|
else
|
|
|
- err = rt6_fill_node(net, skb, rt->from, dst,
|
|
|
- &fl6.daddr, &fl6.saddr, iif, RTM_NEWROUTE,
|
|
|
+ err = rt6_fill_node(net, skb, from, dst, &fl6.daddr,
|
|
|
+ &fl6.saddr, iif, RTM_NEWROUTE,
|
|
|
NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
|
|
|
0);
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
if (err < 0) {
|
|
|
kfree_skb(skb);
|
|
|
goto errout;
|