btdev: check error conditions for HCI_Create_Connection_Cancel
authorPauli Virtanen <pav@iki.fi>
Tue, 1 Aug 2023 16:38:38 +0000 (19:38 +0300)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 5 Jan 2024 13:34:03 +0000 (19:04 +0530)
Create Connection Cancel shall return Command Complete with error status
when there is no Create Connection that can be canceled.  In these
cases, we should not send a (spurious) Connection Complete event.

Fix by keeping a list of pending Create Connection commands, and
returning command errors if there is none pending at the moment.

emulator/btdev.c
monitor/bt.h

index 637f0bb..8658b41 100755 (executable)
@@ -62,6 +62,7 @@ struct hook {
 
 #define MAX_HOOK_ENTRIES 16
 #define MAX_EXT_ADV_SETS 3
+#define MAX_PENDING_CONN 16
 
 struct btdev_conn {
        uint16_t handle;
@@ -223,6 +224,8 @@ struct btdev {
        uint8_t  le_rl_enable;
        uint16_t le_rl_timeout;
 
+       struct btdev *pending_conn[MAX_PENDING_CONN];
+
        uint8_t le_local_sk256[32];
 
        uint16_t sync_train_interval;
@@ -1211,10 +1214,36 @@ static struct btdev_conn *conn_link_bis(struct btdev *dev, struct btdev *remote,
        return conn;
 }
 
+static void pending_conn_add(struct btdev *btdev, struct btdev *remote)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(btdev->pending_conn); ++i) {
+               if (!btdev->pending_conn[i]) {
+                       btdev->pending_conn[i] = remote;
+                       return;
+               }
+       }
+}
+
+static bool pending_conn_del(struct btdev *btdev, struct btdev *remote)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(btdev->pending_conn); ++i) {
+               if (btdev->pending_conn[i] == remote) {
+                       btdev->pending_conn[i] = NULL;
+                       return true;
+               }
+       }
+       return false;
+}
+
 static void conn_complete(struct btdev *btdev,
                                        const uint8_t *bdaddr, uint8_t status)
 {
        struct bt_hci_evt_conn_complete cc;
+       struct btdev *remote = find_btdev_by_bdaddr(bdaddr);
 
        if (!status) {
                struct btdev_conn *conn;
@@ -1223,6 +1252,8 @@ static void conn_complete(struct btdev *btdev,
                if (!conn)
                        return;
 
+               pending_conn_del(conn->link->dev, btdev);
+
                cc.status = status;
                memcpy(cc.bdaddr, btdev->bdaddr, 6);
                cc.encr_mode = 0x00;
@@ -1240,6 +1271,8 @@ static void conn_complete(struct btdev *btdev,
                cc.link_type = 0x01;
        }
 
+       pending_conn_del(btdev, remote);
+
        cc.status = status;
        memcpy(cc.bdaddr, bdaddr, 6);
        cc.encr_mode = 0x00;
@@ -1260,6 +1293,8 @@ static int cmd_create_conn_complete(struct btdev *dev, const void *data,
                memcpy(cr.dev_class, dev->dev_class, 3);
                cr.link_type = 0x01;
 
+               pending_conn_add(dev, remote);
+
                send_event(remote, BT_HCI_EVT_CONN_REQUEST, &cr, sizeof(cr));
        } else {
                conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_PAGE_TIMEOUT);
@@ -1296,16 +1331,24 @@ static int cmd_add_sco_conn(struct btdev *dev, const void *data, uint8_t len)
        cc.encr_mode = 0x00;
 
 done:
+       pending_conn_del(dev, conn->link->dev);
+
        send_event(dev, BT_HCI_EVT_CONN_COMPLETE, &cc, sizeof(cc));
 
        return 0;
 }
 
+static bool match_bdaddr(const void *data, const void *match_data)
+{
+       const struct btdev_conn *conn = data;
+       const uint8_t *bdaddr = match_data;
+
+       return !memcmp(conn->link->dev->bdaddr, bdaddr, 6);
+}
+
 static int cmd_create_conn_cancel(struct btdev *dev, const void *data,
                                                        uint8_t len)
 {
-       cmd_status(dev, BT_HCI_ERR_SUCCESS, BT_HCI_CMD_CREATE_CONN_CANCEL);
-
        return 0;
 }
 
@@ -1313,8 +1356,37 @@ static int cmd_create_conn_cancel_complete(struct btdev *dev, const void *data,
                                                        uint8_t len)
 {
        const struct bt_hci_cmd_create_conn_cancel *cmd = data;
+       struct bt_hci_rsp_create_conn_cancel rp;
+       struct btdev *remote = find_btdev_by_bdaddr(cmd->bdaddr);
+       struct btdev_conn *conn;
 
-       conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
+       /* BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 4, Part E page 1848
+        *
+        * If the connection is already established, and the
+        * HCI_Connection_Complete event has been sent, then the Controller
+        * shall return an HCI_Command_Complete event with the error code
+        * Connection Already Exists (0x0B). If the HCI_Create_Connection_Cancel
+        * command is sent to the Controller without a preceding
+        * HCI_Create_Connection command to the same device, the BR/EDR
+        * Controller shall return an HCI_Command_Complete event with the error
+        * code Unknown Connection Identifier (0x02).
+        */
+       if (pending_conn_del(dev, remote)) {
+               rp.status = BT_HCI_ERR_SUCCESS;
+       } else {
+               conn = queue_find(dev->conns, match_bdaddr, cmd->bdaddr);
+               if (conn)
+                       rp.status = BT_HCI_ERR_CONN_ALREADY_EXISTS;
+               else
+                       rp.status = BT_HCI_ERR_UNKNOWN_CONN_ID;
+       }
+
+       memcpy(rp.bdaddr, cmd->bdaddr, sizeof(rp.bdaddr));
+
+       cmd_complete(dev, BT_HCI_CMD_CREATE_CONN_CANCEL, &rp, sizeof(rp));
+
+       if (!rp.status)
+               conn_complete(dev, cmd->bdaddr, BT_HCI_ERR_UNKNOWN_CONN_ID);
 
        return 0;
 }
@@ -1372,14 +1444,6 @@ static int cmd_link_key_reply(struct btdev *dev, const void *data, uint8_t len)
        return 0;
 }
 
-static bool match_bdaddr(const void *data, const void *match_data)
-{
-       const struct btdev_conn *conn = data;
-       const uint8_t *bdaddr = match_data;
-
-       return !memcmp(conn->link->dev->bdaddr, bdaddr, 6);
-}
-
 static void auth_complete(struct btdev_conn *conn, uint8_t status)
 {
        struct bt_hci_evt_auth_complete ev;
index 214cdbb..3c4ec0c 100755 (executable)
@@ -590,6 +590,10 @@ struct bt_hci_cmd_add_sco_conn {
 struct bt_hci_cmd_create_conn_cancel {
        uint8_t  bdaddr[6];
 } __attribute__ ((packed));
+struct bt_hci_rsp_create_conn_cancel {
+       uint8_t  status;
+       uint8_t  bdaddr[6];
+} __attribute__ ((packed));
 
 #define BT_HCI_CMD_ACCEPT_CONN_REQUEST         0x0409
 struct bt_hci_cmd_accept_conn_request {