Commit f66dc81f authored by Marcel Holtmann's avatar Marcel Holtmann

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: default avatarMarcel Holtmann <marcel@holtmann.org>
parent bb23c0ab
...@@ -372,9 +372,17 @@ static void l2cap_conn_start(struct l2cap_conn *conn) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 = { ...@@ -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,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment