diff options
Diffstat (limited to 'net/l2tp/l2tp_ip.c')
| -rw-r--r-- | net/l2tp/l2tp_ip.c | 54 | 
1 files changed, 37 insertions, 17 deletions
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index fce9bd3bd3f..b6466e71f5e 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, @@ -667,7 +687,7 @@ MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");  MODULE_DESCRIPTION("L2TP over IP");  MODULE_VERSION("1.0"); -/* Use the value of SOCK_DGRAM (2) directory, because __stringify does't like +/* Use the value of SOCK_DGRAM (2) directory, because __stringify doesn't like   * enums   */  MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, 2, IPPROTO_L2TP);  |