aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Dumazet <eric.dumazet@gmail.com>2011-06-11 18:27:09 -0400
committerDavid S. Miller <davem@conan.davemloft.net>2011-06-13 17:31:30 -0400
commit081b1b1bb27f1f4a3b682f4cf75103108f2066d7 (patch)
tree6b46eac808fe4292bff144ff0b5e6461ef957810
parentec1d7c9a5e9c136e08a9b1df3109ad5d38d8179c (diff)
l2tp: fix l2tp_ip_sendmsg() route handling
l2tp_ip_sendmsg() in non connected mode incorrectly calls sk_setup_caps(). Subsequent send() calls send data to wrong destination. We can also avoid changing dst refcount in connected mode, using appropriate rcu locking. Once output route lookups can also be done under rcu, sendto() calls wont change dst refcounts too. Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com> CC: James Chapman <jchapman@katalix.com> Signed-off-by: David S. Miller <davem@conan.davemloft.net>
-rw-r--r--net/l2tp/l2tp_ip.c19
1 files changed, 13 insertions, 6 deletions
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index b6466e71f5e1..d21e7ebd91ca 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -480,18 +480,16 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
480 if (connected) 480 if (connected)
481 rt = (struct rtable *) __sk_dst_check(sk, 0); 481 rt = (struct rtable *) __sk_dst_check(sk, 0);
482 482
483 rcu_read_lock();
483 if (rt == NULL) { 484 if (rt == NULL) {
484 struct ip_options_rcu *inet_opt; 485 const struct ip_options_rcu *inet_opt;
485 486
486 rcu_read_lock();
487 inet_opt = rcu_dereference(inet->inet_opt); 487 inet_opt = rcu_dereference(inet->inet_opt);
488 488
489 /* Use correct destination address if we have options. */ 489 /* Use correct destination address if we have options. */
490 if (inet_opt && inet_opt->opt.srr) 490 if (inet_opt && inet_opt->opt.srr)
491 daddr = inet_opt->opt.faddr; 491 daddr = inet_opt->opt.faddr;
492 492
493 rcu_read_unlock();
494
495 /* If this fails, retransmit mechanism of transport layer will 493 /* If this fails, retransmit mechanism of transport layer will
496 * keep trying until route appears or the connection times 494 * keep trying until route appears or the connection times
497 * itself out. 495 * itself out.
@@ -503,12 +501,20 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
503 sk->sk_bound_dev_if); 501 sk->sk_bound_dev_if);
504 if (IS_ERR(rt)) 502 if (IS_ERR(rt))
505 goto no_route; 503 goto no_route;
506 sk_setup_caps(sk, &rt->dst); 504 if (connected)
505 sk_setup_caps(sk, &rt->dst);
506 else
507 dst_release(&rt->dst); /* safe since we hold rcu_read_lock */
507 } 508 }
508 skb_dst_set(skb, dst_clone(&rt->dst)); 509
510 /* We dont need to clone dst here, it is guaranteed to not disappear.
511 * __dev_xmit_skb() might force a refcount if needed.
512 */
513 skb_dst_set_noref(skb, &rt->dst);
509 514
510 /* Queue the packet to IP for output */ 515 /* Queue the packet to IP for output */
511 rc = ip_queue_xmit(skb, &inet->cork.fl); 516 rc = ip_queue_xmit(skb, &inet->cork.fl);
517 rcu_read_unlock();
512 518
513error: 519error:
514 /* Update stats */ 520 /* Update stats */
@@ -525,6 +531,7 @@ out:
525 return rc; 531 return rc;
526 532
527no_route: 533no_route:
534 rcu_read_unlock();
528 IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); 535 IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
529 kfree_skb(skb); 536 kfree_skb(skb);
530 rc = -EHOSTUNREACH; 537 rc = -EHOSTUNREACH;