aboutsummaryrefslogtreecommitdiffstats
path: root/net/l2tp/l2tp_ip.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/l2tp/l2tp_ip.c')
-rw-r--r--net/l2tp/l2tp_ip.c26
1 files changed, 17 insertions, 9 deletions
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index 78bc442..334a93d 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -494,18 +494,16 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
if (connected)
rt = (struct rtable *) __sk_dst_check(sk, 0);
+ rcu_read_lock();
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);
/* Use correct destination address if we have options. */
if (inet_opt && inet_opt->opt.srr)
daddr = inet_opt->opt.faddr;
- rcu_read_unlock();
-
/* If this fails, retransmit mechanism of transport layer will
* keep trying until route appears or the connection times
* itself out.
@@ -517,12 +515,23 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
sk->sk_bound_dev_if);
if (IS_ERR(rt))
goto no_route;
- sk_setup_caps(sk, &rt->dst);
+ if (connected) {
+ sk_setup_caps(sk, &rt->dst);
+ } else {
+ skb_dst_set(skb, &rt->dst);
+ goto xmit;
+ }
}
- 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);
+
+xmit:
/* Queue the packet to IP for output */
rc = ip_queue_xmit(skb, &inet->cork.fl);
+ rcu_read_unlock();
error:
/* Update stats */
@@ -539,6 +548,7 @@ out:
return rc;
no_route:
+ rcu_read_unlock();
IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
kfree_skb(skb);
rc = -EHOSTUNREACH;
@@ -558,9 +568,6 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
if (flags & MSG_OOB)
goto out;
- if (addr_len)
- *addr_len = sizeof(*sin);
-
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
goto out;
@@ -583,6 +590,7 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
sin->sin_port = 0;
memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
+ *addr_len = sizeof(*sin);
}
if (inet->cmsg_flags)
ip_cmsg_recv(msg, skb);