diff options
Diffstat (limited to 'net/l2tp/l2tp_ip6.c')
| -rw-r--r-- | net/l2tp/l2tp_ip6.c | 85 | 
1 files changed, 46 insertions, 39 deletions
| diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index ad3468c32b53..f092ac441fdd 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -72,8 +72,9 @@ static struct sock *__l2tp_ip6_bind_lookup(struct net *net,  		if ((l2tp->conn_id == tunnel_id) &&  		    net_eq(sock_net(sk), net) && -		    !(addr && ipv6_addr_equal(addr, laddr)) && -		    !(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)) +		    (!addr || ipv6_addr_equal(addr, laddr)) && +		    (!sk->sk_bound_dev_if || !dif || +		     sk->sk_bound_dev_if == dif))  			goto found;  	} @@ -196,16 +197,17 @@ pass_up:  		struct ipv6hdr *iph = ipv6_hdr(skb);  		read_lock_bh(&l2tp_ip6_lock); -		sk = __l2tp_ip6_bind_lookup(net, &iph->daddr, -					    0, tunnel_id); +		sk = __l2tp_ip6_bind_lookup(net, &iph->daddr, inet6_iif(skb), +					    tunnel_id); +		if (!sk) { +			read_unlock_bh(&l2tp_ip6_lock); +			goto discard; +		} + +		sock_hold(sk);  		read_unlock_bh(&l2tp_ip6_lock);  	} -	if (sk == NULL) -		goto discard; - -	sock_hold(sk); -  	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))  		goto discard_put; @@ -266,11 +268,10 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)  	struct sockaddr_l2tpip6 *addr = (struct sockaddr_l2tpip6 *) uaddr;  	struct net *net = sock_net(sk);  	__be32 v4addr = 0; +	int bound_dev_if;  	int addr_type;  	int err; -	if (!sock_flag(sk, SOCK_ZAPPED)) -		return -EINVAL;  	if (addr->l2tp_family != AF_INET6)  		return -EINVAL;  	if (addr_len < sizeof(*addr)) @@ -286,41 +287,34 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)  	if (addr_type & IPV6_ADDR_MULTICAST)  		return -EADDRNOTAVAIL; -	err = -EADDRINUSE; -	read_lock_bh(&l2tp_ip6_lock); -	if (__l2tp_ip6_bind_lookup(net, &addr->l2tp_addr, -				   sk->sk_bound_dev_if, addr->l2tp_conn_id)) -		goto out_in_use; -	read_unlock_bh(&l2tp_ip6_lock); -  	lock_sock(sk);  	err = -EINVAL; +	if (!sock_flag(sk, SOCK_ZAPPED)) +		goto out_unlock; +  	if (sk->sk_state != TCP_CLOSE)  		goto out_unlock; +	bound_dev_if = sk->sk_bound_dev_if; +  	/* Check if the address belongs to the host. */  	rcu_read_lock();  	if (addr_type != IPV6_ADDR_ANY) {  		struct net_device *dev = NULL;  		if (addr_type & IPV6_ADDR_LINKLOCAL) { -			if (addr_len >= sizeof(struct sockaddr_in6) && -			    addr->l2tp_scope_id) { -				/* Override any existing binding, if another -				 * one is supplied by user. -				 */ -				sk->sk_bound_dev_if = addr->l2tp_scope_id; -			} +			if (addr->l2tp_scope_id) +				bound_dev_if = addr->l2tp_scope_id;  			/* Binding to link-local address requires an -			   interface */ -			if (!sk->sk_bound_dev_if) +			 * interface. +			 */ +			if (!bound_dev_if)  				goto out_unlock_rcu;  			err = -ENODEV; -			dev = dev_get_by_index_rcu(sock_net(sk), -						   sk->sk_bound_dev_if); +			dev = dev_get_by_index_rcu(sock_net(sk), bound_dev_if);  			if (!dev)  				goto out_unlock_rcu;  		} @@ -335,13 +329,22 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)  	}  	rcu_read_unlock(); -	inet->inet_rcv_saddr = inet->inet_saddr = v4addr; +	write_lock_bh(&l2tp_ip6_lock); +	if (__l2tp_ip6_bind_lookup(net, &addr->l2tp_addr, bound_dev_if, +				   addr->l2tp_conn_id)) { +		write_unlock_bh(&l2tp_ip6_lock); +		err = -EADDRINUSE; +		goto out_unlock; +	} + +	inet->inet_saddr = v4addr; +	inet->inet_rcv_saddr = v4addr; +	sk->sk_bound_dev_if = bound_dev_if;  	sk->sk_v6_rcv_saddr = addr->l2tp_addr;  	np->saddr = addr->l2tp_addr;  	l2tp_ip6_sk(sk)->conn_id = addr->l2tp_conn_id; -	write_lock_bh(&l2tp_ip6_lock);  	sk_add_bind_node(sk, &l2tp_ip6_bind_table);  	sk_del_node_init(sk);  	write_unlock_bh(&l2tp_ip6_lock); @@ -354,10 +357,7 @@ out_unlock_rcu:  	rcu_read_unlock();  out_unlock:  	release_sock(sk); -	return err; -out_in_use: -	read_unlock_bh(&l2tp_ip6_lock);  	return err;  } @@ -370,9 +370,6 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr,  	int	addr_type;  	int rc; -	if (sock_flag(sk, SOCK_ZAPPED)) /* Must bind first - autobinding does not work */ -		return -EINVAL; -  	if (addr_len < sizeof(*lsa))  		return -EINVAL; @@ -389,10 +386,18 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr,  			return -EINVAL;  	} -	rc = ip6_datagram_connect(sk, uaddr, addr_len); -  	lock_sock(sk); +	 /* Must bind first - autobinding does not work */ +	if (sock_flag(sk, SOCK_ZAPPED)) { +		rc = -EINVAL; +		goto out_sk; +	} + +	rc = __ip6_datagram_connect(sk, uaddr, addr_len); +	if (rc < 0) +		goto out_sk; +  	l2tp_ip6_sk(sk)->peer_conn_id = lsa->l2tp_conn_id;  	write_lock_bh(&l2tp_ip6_lock); @@ -400,6 +405,7 @@ static int l2tp_ip6_connect(struct sock *sk, struct sockaddr *uaddr,  	sk_add_bind_node(sk, &l2tp_ip6_bind_table);  	write_unlock_bh(&l2tp_ip6_lock); +out_sk:  	release_sock(sk);  	return rc; @@ -519,6 +525,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)  	memset(&fl6, 0, sizeof(fl6));  	fl6.flowi6_mark = sk->sk_mark; +	fl6.flowi6_uid = sk->sk_uid;  	ipc6.hlimit = -1;  	ipc6.tclass = -1; |