When the DUT receives a remote L2CAP Connection Request during the Read Encryption Key Size procedure, if it fails to complete reading the Encryption Key Size while processing the request, it will respond with a Connection Response – Refused (security block), resulting in the disconnection of the remote device. Use HCI_CONN_ENC_KEY_READY to determine whether l2cap_connect_request is pending. When l2cap_connect occurs before the read_enc_key_size event, it will be pending because HCI_CONN_ENC_KEY_READY has not yet been set. The connection request will be processed once the read_enc_key_size event completes. Signed-off-by: Shuai Zhang <quic_shuaz@xxxxxxxxxxx> --- include/net/bluetooth/hci_core.h | 3 +++ include/net/bluetooth/l2cap.h | 10 +++++++++- net/bluetooth/hci_event.c | 16 ++++++++++++++++ net/bluetooth/l2cap_core.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b47c74080..db329abbf 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -767,6 +767,8 @@ struct hci_conn { struct bt_codec codec; + struct l2cap_pending_connect *pending_connect; + void (*connect_cfm_cb) (struct hci_conn *conn, u8 status); void (*security_cfm_cb) (struct hci_conn *conn, u8 status); void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason); @@ -970,6 +972,7 @@ enum { HCI_CONN_CREATE_PA_SYNC, HCI_CONN_PA_SYNC, HCI_CONN_PA_SYNC_FAILED, + HCI_CONN_ENC_KEY_READY, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 4bb0eaedd..b1ccd56bd 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -679,6 +679,13 @@ struct l2cap_user { void (*remove) (struct l2cap_conn *conn, struct l2cap_user *user); }; +struct l2cap_pending_connect { + struct l2cap_conn *conn; + struct l2cap_cmd_hdr cmd; + u8 data[sizeof(struct l2cap_conn_req)]; + u8 rsp_code; +}; + #define L2CAP_INFO_CL_MTU_REQ_SENT 0x01 #define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04 #define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08 @@ -976,5 +983,6 @@ void l2cap_conn_put(struct l2cap_conn *conn); int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user); void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user); - +void l2cap_process_pending_connect(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u8 *data, u8 rsp_code); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c4b87bfb4..6c992f83e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -32,6 +32,7 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> #include <net/bluetooth/mgmt.h> +#include <net/bluetooth/l2cap.h> #include "hci_debugfs.h" #include "hci_codec.h" @@ -766,10 +767,23 @@ static u8 hci_cc_read_enc_key_size(struct hci_dev *hdev, void *data, /* Update the key encryption size with the connection one */ if (key_enc_size && *key_enc_size != conn->enc_key_size) *key_enc_size = conn->enc_key_size; + set_bit(HCI_CONN_ENC_KEY_READY, &conn->flags); } hci_encrypt_cfm(conn, status); + /*Defer l2cap_connect here if it's triggered before key size is read.*/ + if (conn->pending_connect) { + struct l2cap_pending_connect *pc = conn->pending_connect; + + conn->pending_connect = NULL; + + bt_dev_dbg(hdev, "Defer l2cap_connect"); + l2cap_process_pending_connect(pc->conn, &pc->cmd, pc->data, pc->rsp_code); + + kfree(pc); + } + done: hci_dev_unlock(hdev); @@ -3396,6 +3410,8 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, void *data, if (!conn) goto unlock; + clear_bit(HCI_CONN_ENC_KEY_READY, &conn->flags); + if (ev->status) { mgmt_disconnect_failed(hdev, &conn->dst, conn->type, conn->dst_type, ev->status); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 40daa3827..c4cb60e65 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3982,6 +3982,30 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, struct l2cap_chan *chan = NULL, *pchan = NULL; int result, status = L2CAP_CS_NO_INFO; + /* If encryption is requested, but the key size is not ready yet, + * we need to wait for the key size to be ready before we can + * proceed with the connection. We do this by deferring the + * connection request until the key size is ready. This is done + * by storing the connection request in the hcon->pending_connect + * field. The connection request will be retried when the key size + * is ready. + */ + if (test_bit(HCI_CONN_ENCRYPT, &conn->hcon->flags) && + !test_bit(HCI_CONN_ENC_KEY_READY, &conn->hcon->flags)) { + struct l2cap_pending_connect *pc; + + pc = kzalloc(sizeof(*pc), GFP_KERNEL); + if (!pc) + return; + pc->conn = conn; + memcpy(&pc->cmd, cmd, sizeof(*cmd)); + memcpy(pc->data, data, sizeof(struct l2cap_conn_req)); + pc->rsp_code = rsp_code; + BT_DBG("store request and retried when keysize is ready"); + conn->hcon->pending_connect = pc; + return; + } + u16 dcid = 0, scid = __le16_to_cpu(req->scid); __le16 psm = req->psm; @@ -4105,6 +4129,12 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, l2cap_chan_put(pchan); } +void l2cap_process_pending_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, + u8 *data, u8 rsp_code) +{ + l2cap_connect(conn, cmd, data, rsp_code); +} + static int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) { -- 2.34.1