Bluetooth: Add support for deferring L2CAP connection setup
In order to decide if listening L2CAP sockets should be accept()ed the BD_ADDR of the remote device needs to be known. This patch adds a socket option which defines a timeout for deferring the actual connection setup. Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
@@ -372,9 +372,17 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
|
|||||||
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||||
|
|
||||||
if (l2cap_check_link_mode(sk)) {
|
if (l2cap_check_link_mode(sk)) {
|
||||||
|
if (bt_sk(sk)->defer_setup) {
|
||||||
|
struct sock *parent = bt_sk(sk)->parent;
|
||||||
|
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
|
||||||
|
rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
|
||||||
|
parent->sk_data_ready(parent, 0);
|
||||||
|
|
||||||
|
} else {
|
||||||
sk->sk_state = BT_CONFIG;
|
sk->sk_state = BT_CONFIG;
|
||||||
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
||||||
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
|
rsp.result = cpu_to_le16(L2CAP_CR_PEND);
|
||||||
rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
|
rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
|
||||||
@@ -608,7 +616,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
|
|||||||
|
|
||||||
case BT_CONNECTED:
|
case BT_CONNECTED:
|
||||||
case BT_CONFIG:
|
case BT_CONFIG:
|
||||||
case BT_CONNECT2:
|
|
||||||
if (sk->sk_type == SOCK_SEQPACKET) {
|
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||||
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||||
struct l2cap_disconn_req req;
|
struct l2cap_disconn_req req;
|
||||||
@@ -624,6 +631,27 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
|
|||||||
l2cap_chan_del(sk, reason);
|
l2cap_chan_del(sk, reason);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BT_CONNECT2:
|
||||||
|
if (sk->sk_type == SOCK_SEQPACKET) {
|
||||||
|
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
|
||||||
|
struct l2cap_conn_rsp rsp;
|
||||||
|
__u16 result;
|
||||||
|
|
||||||
|
if (bt_sk(sk)->defer_setup)
|
||||||
|
result = L2CAP_CR_SEC_BLOCK;
|
||||||
|
else
|
||||||
|
result = L2CAP_CR_BAD_PSM;
|
||||||
|
|
||||||
|
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
|
||||||
|
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||||
|
rsp.result = cpu_to_le16(result);
|
||||||
|
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||||
|
l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
|
||||||
|
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
|
||||||
|
} else
|
||||||
|
l2cap_chan_del(sk, reason);
|
||||||
|
break;
|
||||||
|
|
||||||
case BT_CONNECT:
|
case BT_CONNECT:
|
||||||
case BT_DISCONN:
|
case BT_DISCONN:
|
||||||
l2cap_chan_del(sk, reason);
|
l2cap_chan_del(sk, reason);
|
||||||
@@ -653,6 +681,8 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
|
|||||||
|
|
||||||
if (parent) {
|
if (parent) {
|
||||||
sk->sk_type = parent->sk_type;
|
sk->sk_type = parent->sk_type;
|
||||||
|
bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup;
|
||||||
|
|
||||||
pi->imtu = l2cap_pi(parent)->imtu;
|
pi->imtu = l2cap_pi(parent)->imtu;
|
||||||
pi->omtu = l2cap_pi(parent)->omtu;
|
pi->omtu = l2cap_pi(parent)->omtu;
|
||||||
pi->link_mode = l2cap_pi(parent)->link_mode;
|
pi->link_mode = l2cap_pi(parent)->link_mode;
|
||||||
@@ -1106,6 +1136,33 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags)
|
||||||
|
{
|
||||||
|
struct sock *sk = sock->sk;
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
|
||||||
|
if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) {
|
||||||
|
struct l2cap_conn_rsp rsp;
|
||||||
|
|
||||||
|
sk->sk_state = BT_CONFIG;
|
||||||
|
|
||||||
|
rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
|
||||||
|
rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
|
||||||
|
rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
|
||||||
|
rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
|
||||||
|
l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident,
|
||||||
|
L2CAP_CONN_RSP, sizeof(rsp), &rsp);
|
||||||
|
|
||||||
|
release_sock(sk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
release_sock(sk);
|
||||||
|
|
||||||
|
return bt_sock_recvmsg(iocb, sock, msg, len, flags);
|
||||||
|
}
|
||||||
|
|
||||||
static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen)
|
static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen)
|
||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
@@ -1156,6 +1213,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
{
|
{
|
||||||
struct sock *sk = sock->sk;
|
struct sock *sk = sock->sk;
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
u32 opt;
|
||||||
|
|
||||||
BT_DBG("sk %p", sk);
|
BT_DBG("sk %p", sk);
|
||||||
|
|
||||||
@@ -1165,6 +1223,20 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
|
case BT_DEFER_SETUP:
|
||||||
|
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
|
||||||
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_user(opt, (u32 __user *) optval)) {
|
||||||
|
err = -EFAULT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_sk(sk)->defer_setup = opt;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = -ENOPROTOOPT;
|
err = -ENOPROTOOPT;
|
||||||
break;
|
break;
|
||||||
@@ -1207,7 +1279,9 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_CONNINFO:
|
case L2CAP_CONNINFO:
|
||||||
if (sk->sk_state != BT_CONNECTED) {
|
if (sk->sk_state != BT_CONNECTED &&
|
||||||
|
!(sk->sk_state == BT_CONNECT2 &&
|
||||||
|
bt_sk(sk)->defer_setup)) {
|
||||||
err = -ENOTCONN;
|
err = -ENOTCONN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1246,6 +1320,17 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
|
|||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
|
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
|
case BT_DEFER_SETUP:
|
||||||
|
if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
|
||||||
|
err = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval))
|
||||||
|
err = -EFAULT;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
err = -ENOPROTOOPT;
|
err = -ENOPROTOOPT;
|
||||||
break;
|
break;
|
||||||
@@ -1670,9 +1755,16 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
|
|||||||
|
|
||||||
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
|
if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
|
||||||
if (l2cap_check_link_mode(sk)) {
|
if (l2cap_check_link_mode(sk)) {
|
||||||
|
if (bt_sk(sk)->defer_setup) {
|
||||||
|
sk->sk_state = BT_CONNECT2;
|
||||||
|
result = L2CAP_CR_PEND;
|
||||||
|
status = L2CAP_CS_AUTHOR_PEND;
|
||||||
|
parent->sk_data_ready(parent, 0);
|
||||||
|
} else {
|
||||||
sk->sk_state = BT_CONFIG;
|
sk->sk_state = BT_CONFIG;
|
||||||
result = L2CAP_CR_SUCCESS;
|
result = L2CAP_CR_SUCCESS;
|
||||||
status = L2CAP_CS_NO_INFO;
|
status = L2CAP_CS_NO_INFO;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sk->sk_state = BT_CONNECT2;
|
sk->sk_state = BT_CONNECT2;
|
||||||
result = L2CAP_CR_PEND;
|
result = L2CAP_CR_PEND;
|
||||||
@@ -2494,7 +2586,7 @@ static const struct proto_ops l2cap_sock_ops = {
|
|||||||
.accept = l2cap_sock_accept,
|
.accept = l2cap_sock_accept,
|
||||||
.getname = l2cap_sock_getname,
|
.getname = l2cap_sock_getname,
|
||||||
.sendmsg = l2cap_sock_sendmsg,
|
.sendmsg = l2cap_sock_sendmsg,
|
||||||
.recvmsg = bt_sock_recvmsg,
|
.recvmsg = l2cap_sock_recvmsg,
|
||||||
.poll = bt_sock_poll,
|
.poll = bt_sock_poll,
|
||||||
.ioctl = bt_sock_ioctl,
|
.ioctl = bt_sock_ioctl,
|
||||||
.mmap = sock_no_mmap,
|
.mmap = sock_no_mmap,
|
||||||
|
Reference in New Issue
Block a user