|
@@ -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,6 +268,7 @@ 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;
|
|
|
|
|
@@ -284,13 +287,6 @@ 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;
|
|
@@ -300,28 +296,25 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
|
|
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;
|
|
|
}
|
|
@@ -336,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);
|
|
@@ -355,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;
|
|
|
}
|
|
|
|
|
@@ -371,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;
|
|
|
|
|
@@ -390,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);
|
|
@@ -401,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;
|