summaryrefslogtreecommitdiffstats
path: root/net/l2tp
diff options
context:
space:
mode:
Diffstat (limited to 'net/l2tp')
-rw-r--r--net/l2tp/l2tp_core.c28
-rw-r--r--net/l2tp/l2tp_ip.c52
-rw-r--r--net/l2tp/l2tp_netlink.c3
3 files changed, 56 insertions, 27 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index c64ce0a0bb03..ed8a2335442f 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -954,7 +954,7 @@ static int l2tp_build_l2tpv3_header(struct l2tp_session *session, void *buf)
}
static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
- size_t data_len)
+ struct flowi *fl, size_t data_len)
{
struct l2tp_tunnel *tunnel = session->tunnel;
unsigned int len = skb->len;
@@ -987,7 +987,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
/* Queue the packet to IP for output */
skb->local_df = 1;
- error = ip_queue_xmit(skb);
+ error = ip_queue_xmit(skb, fl);
/* Update stats */
if (error >= 0) {
@@ -1028,6 +1028,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
int data_len = skb->len;
struct l2tp_tunnel *tunnel = session->tunnel;
struct sock *sk = tunnel->sock;
+ struct flowi *fl;
struct udphdr *uh;
struct inet_sock *inet;
__wsum csum;
@@ -1060,14 +1061,21 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
IPSKB_REROUTED);
nf_reset(skb);
+ bh_lock_sock(sk);
+ if (sock_owned_by_user(sk)) {
+ dev_kfree_skb(skb);
+ goto out_unlock;
+ }
+
/* Get routing info from the tunnel socket */
skb_dst_drop(skb);
skb_dst_set(skb, dst_clone(__sk_dst_get(sk)));
+ inet = inet_sk(sk);
+ fl = &inet->cork.fl;
switch (tunnel->encap) {
case L2TP_ENCAPTYPE_UDP:
/* Setup UDP header */
- inet = inet_sk(sk);
__skb_push(skb, sizeof(*uh));
skb_reset_transport_header(skb);
uh = udp_hdr(skb);
@@ -1105,7 +1113,9 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
l2tp_skb_set_owner_w(skb, sk);
- l2tp_xmit_core(session, skb, data_len);
+ l2tp_xmit_core(session, skb, fl, data_len);
+out_unlock:
+ bh_unlock_sock(sk);
abort:
return 0;
@@ -1425,16 +1435,15 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
/* Add tunnel to our list */
INIT_LIST_HEAD(&tunnel->list);
- spin_lock_bh(&pn->l2tp_tunnel_list_lock);
- list_add_rcu(&tunnel->list, &pn->l2tp_tunnel_list);
- spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
- synchronize_rcu();
atomic_inc(&l2tp_tunnel_count);
/* Bump the reference count. The tunnel context is deleted
- * only when this drops to zero.
+ * only when this drops to zero. Must be done before list insertion
*/
l2tp_tunnel_inc_refcount(tunnel);
+ spin_lock_bh(&pn->l2tp_tunnel_list_lock);
+ list_add_rcu(&tunnel->list, &pn->l2tp_tunnel_list);
+ spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
err = 0;
err:
@@ -1626,7 +1635,6 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn
hlist_add_head_rcu(&session->global_hlist,
l2tp_session_id_hash_2(pn, session_id));
spin_unlock_bh(&pn->l2tp_session_hlist_lock);
- synchronize_rcu();
}
/* Ignore management session in session count value */
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index 5c04f3e42704..b6466e71f5e1 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -296,12 +296,12 @@ out_in_use:
static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
- int rc;
- struct inet_sock *inet = inet_sk(sk);
struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *) uaddr;
+ struct inet_sock *inet = inet_sk(sk);
+ struct flowi4 *fl4;
struct rtable *rt;
__be32 saddr;
- int oif;
+ int oif, rc;
rc = -EINVAL;
if (addr_len < sizeof(*lsa))
@@ -311,6 +311,8 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
if (lsa->l2tp_family != AF_INET)
goto out;
+ lock_sock(sk);
+
sk_dst_reset(sk);
oif = sk->sk_bound_dev_if;
@@ -320,7 +322,8 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
if (ipv4_is_multicast(lsa->l2tp_addr.s_addr))
goto out;
- rt = ip_route_connect(lsa->l2tp_addr.s_addr, saddr,
+ fl4 = &inet->cork.fl.u.ip4;
+ rt = ip_route_connect(fl4, lsa->l2tp_addr.s_addr, saddr,
RT_CONN_FLAGS(sk), oif,
IPPROTO_L2TP,
0, 0, sk, true);
@@ -340,10 +343,10 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
l2tp_ip_sk(sk)->peer_conn_id = lsa->l2tp_conn_id;
if (!inet->inet_saddr)
- inet->inet_saddr = rt->rt_src;
+ inet->inet_saddr = fl4->saddr;
if (!inet->inet_rcv_saddr)
- inet->inet_rcv_saddr = rt->rt_src;
- inet->inet_daddr = rt->rt_dst;
+ inet->inet_rcv_saddr = fl4->saddr;
+ inet->inet_daddr = fl4->daddr;
sk->sk_state = TCP_ESTABLISHED;
inet->inet_id = jiffies;
@@ -356,6 +359,7 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len
rc = 0;
out:
+ release_sock(sk);
return rc;
}
@@ -416,23 +420,28 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
int rc;
struct l2tp_ip_sock *lsa = l2tp_ip_sk(sk);
struct inet_sock *inet = inet_sk(sk);
- struct ip_options *opt = inet->opt;
struct rtable *rt = NULL;
+ struct flowi4 *fl4;
int connected = 0;
__be32 daddr;
+ lock_sock(sk);
+
+ rc = -ENOTCONN;
if (sock_flag(sk, SOCK_DEAD))
- return -ENOTCONN;
+ goto out;
/* Get and verify the address. */
if (msg->msg_name) {
struct sockaddr_l2tpip *lip = (struct sockaddr_l2tpip *) msg->msg_name;
+ rc = -EINVAL;
if (msg->msg_namelen < sizeof(*lip))
- return -EINVAL;
+ goto out;
if (lip->l2tp_family != AF_INET) {
+ rc = -EAFNOSUPPORT;
if (lip->l2tp_family != AF_UNSPEC)
- return -EAFNOSUPPORT;
+ goto out;
}
daddr = lip->l2tp_addr.s_addr;
@@ -467,19 +476,27 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
goto error;
}
+ fl4 = &inet->cork.fl.u.ip4;
if (connected)
rt = (struct rtable *) __sk_dst_check(sk, 0);
if (rt == NULL) {
+ 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 (opt && opt->srr)
- daddr = opt->faddr;
+ 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.
*/
- rt = ip_route_output_ports(sock_net(sk), sk,
+ rt = ip_route_output_ports(sock_net(sk), fl4, sk,
daddr, inet->inet_saddr,
inet->inet_dport, inet->inet_sport,
sk->sk_protocol, RT_CONN_FLAGS(sk),
@@ -491,7 +508,7 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
skb_dst_set(skb, dst_clone(&rt->dst));
/* Queue the packet to IP for output */
- rc = ip_queue_xmit(skb);
+ rc = ip_queue_xmit(skb, &inet->cork.fl);
error:
/* Update stats */
@@ -503,12 +520,15 @@ error:
lsa->tx_errors++;
}
+out:
+ release_sock(sk);
return rc;
no_route:
IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
kfree_skb(skb);
- return -EHOSTUNREACH;
+ rc = -EHOSTUNREACH;
+ goto out;
}
static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c
index 4c1e540732d7..93a41a09458b 100644
--- a/net/l2tp/l2tp_netlink.c
+++ b/net/l2tp/l2tp_netlink.c
@@ -795,11 +795,12 @@ int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops
goto out;
l2tp_nl_cmd_ops[pw_type] = ops;
+ ret = 0;
out:
genl_unlock();
err:
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(l2tp_nl_register_ops);