|
@@ -480,18 +480,16 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
|
|
if (connected)
|
|
if (connected)
|
|
rt = (struct rtable *) __sk_dst_check(sk, 0);
|
|
rt = (struct rtable *) __sk_dst_check(sk, 0);
|
|
|
|
|
|
|
|
+ rcu_read_lock();
|
|
if (rt == NULL) {
|
|
if (rt == NULL) {
|
|
- struct ip_options_rcu *inet_opt;
|
|
|
|
|
|
+ const struct ip_options_rcu *inet_opt;
|
|
|
|
|
|
- rcu_read_lock();
|
|
|
|
inet_opt = rcu_dereference(inet->inet_opt);
|
|
inet_opt = rcu_dereference(inet->inet_opt);
|
|
|
|
|
|
/* Use correct destination address if we have options. */
|
|
/* Use correct destination address if we have options. */
|
|
if (inet_opt && inet_opt->opt.srr)
|
|
if (inet_opt && inet_opt->opt.srr)
|
|
daddr = inet_opt->opt.faddr;
|
|
daddr = inet_opt->opt.faddr;
|
|
|
|
|
|
- rcu_read_unlock();
|
|
|
|
-
|
|
|
|
/* If this fails, retransmit mechanism of transport layer will
|
|
/* If this fails, retransmit mechanism of transport layer will
|
|
* keep trying until route appears or the connection times
|
|
* keep trying until route appears or the connection times
|
|
* itself out.
|
|
* itself out.
|
|
@@ -503,12 +501,20 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
|
|
sk->sk_bound_dev_if);
|
|
sk->sk_bound_dev_if);
|
|
if (IS_ERR(rt))
|
|
if (IS_ERR(rt))
|
|
goto no_route;
|
|
goto no_route;
|
|
- sk_setup_caps(sk, &rt->dst);
|
|
|
|
|
|
+ if (connected)
|
|
|
|
+ sk_setup_caps(sk, &rt->dst);
|
|
|
|
+ else
|
|
|
|
+ dst_release(&rt->dst); /* safe since we hold rcu_read_lock */
|
|
}
|
|
}
|
|
- skb_dst_set(skb, dst_clone(&rt->dst));
|
|
|
|
|
|
+
|
|
|
|
+ /* We dont need to clone dst here, it is guaranteed to not disappear.
|
|
|
|
+ * __dev_xmit_skb() might force a refcount if needed.
|
|
|
|
+ */
|
|
|
|
+ skb_dst_set_noref(skb, &rt->dst);
|
|
|
|
|
|
/* Queue the packet to IP for output */
|
|
/* Queue the packet to IP for output */
|
|
rc = ip_queue_xmit(skb, &inet->cork.fl);
|
|
rc = ip_queue_xmit(skb, &inet->cork.fl);
|
|
|
|
+ rcu_read_unlock();
|
|
|
|
|
|
error:
|
|
error:
|
|
/* Update stats */
|
|
/* Update stats */
|
|
@@ -525,6 +531,7 @@ out:
|
|
return rc;
|
|
return rc;
|
|
|
|
|
|
no_route:
|
|
no_route:
|
|
|
|
+ rcu_read_unlock();
|
|
IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
|
|
IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
|
|
kfree_skb(skb);
|
|
kfree_skb(skb);
|
|
rc = -EHOSTUNREACH;
|
|
rc = -EHOSTUNREACH;
|