Bluetooth: ISO: Pass BIG encryption info through QoS
authorIulia Tanasescu <iulia.tanasescu@nxp.com>
Wed, 6 Sep 2023 14:01:03 +0000 (17:01 +0300)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 23 Oct 2023 17:58:18 +0000 (10:58 -0700)
This enables a broadcast sink to be informed if the PA
it has synced with is associated with an encrypted BIG,
by retrieving the socket QoS and checking the encryption
field.

After PA sync has been successfully established and the
first BIGInfo advertising report is received, a new hcon
is added and notified to the ISO layer. The ISO layer
sets the encryption field of the socket and hcon QoS
according to the encryption parameter of the BIGInfo
advertising report event.

After that, the userspace is woken up, and the QoS of the
new PA sync socket can be read, to inspect the encryption
field and follow up accordingly.

Signed-off-by: Iulia Tanasescu <iulia.tanasescu@nxp.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_event.c
net/bluetooth/iso.c

index 87d92accc26ea91e4a6c19aec906b40a3914d988..bdee5d649cc61de069d829b7f456de9c0e598cf6 100644 (file)
@@ -1,6 +1,7 @@
 /*
    BlueZ - Bluetooth protocol stack for Linux
    Copyright (C) 2000-2001 Qualcomm Incorporated
+   Copyright 2023 NXP
 
    Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
 
@@ -673,6 +674,8 @@ enum {
 #define HCI_TX_POWER_INVALID   127
 #define HCI_RSSI_INVALID       127
 
+#define HCI_SYNC_HANDLE_INVALID        0xffff
+
 #define HCI_ROLE_MASTER                0x00
 #define HCI_ROLE_SLAVE         0x01
 
index c33348ba1657e34e888119cc78ec1d245f4539f2..f36c1fd5d64ed00a6fa904e5c6281252bd17c9d9 100644 (file)
@@ -1314,7 +1314,7 @@ static inline struct hci_conn *hci_conn_hash_lookup_big_any_dst(struct hci_dev *
 }
 
 static inline struct hci_conn *
-hci_conn_hash_lookup_pa_sync(struct hci_dev *hdev, __u8 big)
+hci_conn_hash_lookup_pa_sync_big_handle(struct hci_dev *hdev, __u8 big)
 {
        struct hci_conn_hash *h = &hdev->conn_hash;
        struct hci_conn  *c;
@@ -1336,6 +1336,29 @@ hci_conn_hash_lookup_pa_sync(struct hci_dev *hdev, __u8 big)
        return NULL;
 }
 
+static inline struct hci_conn *
+hci_conn_hash_lookup_pa_sync_handle(struct hci_dev *hdev, __u16 sync_handle)
+{
+       struct hci_conn_hash *h = &hdev->conn_hash;
+       struct hci_conn  *c;
+
+       rcu_read_lock();
+
+       list_for_each_entry_rcu(c, &h->list, list) {
+               if (c->type != ISO_LINK ||
+                       !test_bit(HCI_CONN_PA_SYNC, &c->flags))
+                       continue;
+
+               if (c->sync_handle == sync_handle) {
+                       rcu_read_unlock();
+                       return c;
+               }
+       }
+       rcu_read_unlock();
+
+       return NULL;
+}
+
 static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev,
                                                        __u8 type, __u16 state)
 {
index 973c9de01a0428444af635cb1a6fcf629afc45a7..271da46e7428d863a182d0561fdec9f8508c5a2f 100644 (file)
@@ -974,6 +974,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
        conn->rssi = HCI_RSSI_INVALID;
        conn->tx_power = HCI_TX_POWER_INVALID;
        conn->max_tx_power = HCI_TX_POWER_INVALID;
+       conn->sync_handle = HCI_SYNC_HANDLE_INVALID;
 
        set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
        conn->disc_timeout = HCI_DISCONN_TIMEOUT;
index 1e1c9147356c3cf931a7f605fcb1f35e3c7694ae..9b34c9f8ee02cf12787ac783dea269111af3303a 100644 (file)
@@ -6603,7 +6603,7 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
        struct hci_ev_le_pa_sync_established *ev = data;
        int mask = hdev->link_mode;
        __u8 flags = 0;
-       struct hci_conn *bis;
+       struct hci_conn *pa_sync;
 
        bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
 
@@ -6620,20 +6620,19 @@ static void hci_le_pa_sync_estabilished_evt(struct hci_dev *hdev, void *data,
        if (!(flags & HCI_PROTO_DEFER))
                goto unlock;
 
-       /* Add connection to indicate the PA sync event */
-       bis = hci_conn_add(hdev, ISO_LINK, BDADDR_ANY,
-                          HCI_ROLE_SLAVE);
+       if (ev->status) {
+               /* Add connection to indicate the failed PA sync event */
+               pa_sync = hci_conn_add(hdev, ISO_LINK, BDADDR_ANY,
+                                      HCI_ROLE_SLAVE);
 
-       if (!bis)
-               goto unlock;
+               if (!pa_sync)
+                       goto unlock;
 
-       if (ev->status)
-               set_bit(HCI_CONN_PA_SYNC_FAILED, &bis->flags);
-       else
-               set_bit(HCI_CONN_PA_SYNC, &bis->flags);
+               set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
 
-       /* Notify connection to iso layer */
-       hci_connect_cfm(bis, ev->status);
+               /* Notify iso layer */
+               hci_connect_cfm(pa_sync, ev->status);
+       }
 
 unlock:
        hci_dev_unlock(hdev);
@@ -7125,7 +7124,7 @@ static void hci_le_big_sync_established_evt(struct hci_dev *hdev, void *data,
        hci_dev_lock(hdev);
 
        if (!ev->status) {
-               pa_sync = hci_conn_hash_lookup_pa_sync(hdev, ev->handle);
+               pa_sync = hci_conn_hash_lookup_pa_sync_big_handle(hdev, ev->handle);
                if (pa_sync)
                        /* Also mark the BIG sync established event on the
                         * associated PA sync hcon
@@ -7186,15 +7185,42 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,
        struct hci_evt_le_big_info_adv_report *ev = data;
        int mask = hdev->link_mode;
        __u8 flags = 0;
+       struct hci_conn *pa_sync;
 
        bt_dev_dbg(hdev, "sync_handle 0x%4.4x", le16_to_cpu(ev->sync_handle));
 
        hci_dev_lock(hdev);
 
        mask |= hci_proto_connect_ind(hdev, BDADDR_ANY, ISO_LINK, &flags);
-       if (!(mask & HCI_LM_ACCEPT))
+       if (!(mask & HCI_LM_ACCEPT)) {
                hci_le_pa_term_sync(hdev, ev->sync_handle);
+               goto unlock;
+       }
 
+       if (!(flags & HCI_PROTO_DEFER))
+               goto unlock;
+
+       pa_sync = hci_conn_hash_lookup_pa_sync_handle
+                       (hdev,
+                       le16_to_cpu(ev->sync_handle));
+
+       if (pa_sync)
+               goto unlock;
+
+       /* Add connection to indicate the PA sync event */
+       pa_sync = hci_conn_add(hdev, ISO_LINK, BDADDR_ANY,
+                              HCI_ROLE_SLAVE);
+
+       if (!pa_sync)
+               goto unlock;
+
+       pa_sync->sync_handle = le16_to_cpu(ev->sync_handle);
+       set_bit(HCI_CONN_PA_SYNC, &pa_sync->flags);
+
+       /* Notify iso layer */
+       hci_connect_cfm(pa_sync, 0x00);
+
+unlock:
        hci_dev_unlock(hdev);
 }
 
index 71248163ce9a5c76582037e7ad3ac1a3f2f7e008..2132a16be93cd0a32292d2def0095d89170c33b1 100644 (file)
@@ -77,6 +77,7 @@ static struct bt_iso_qos default_qos;
 static bool check_ucast_qos(struct bt_iso_qos *qos);
 static bool check_bcast_qos(struct bt_iso_qos *qos);
 static bool iso_match_sid(struct sock *sk, void *data);
+static bool iso_match_sync_handle(struct sock *sk, void *data);
 static void iso_sock_disconn(struct sock *sk);
 
 /* ---- ISO timers ---- */
@@ -1202,7 +1203,6 @@ static int iso_sock_recvmsg(struct socket *sock, struct msghdr *msg,
                            test_bit(HCI_CONN_PA_SYNC, &pi->conn->hcon->flags)) {
                                iso_conn_big_sync(sk);
                                sk->sk_state = BT_LISTEN;
-                               set_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags);
                        } else {
                                iso_conn_defer_accept(pi->conn->hcon);
                                sk->sk_state = BT_CONFIG;
@@ -1579,6 +1579,7 @@ static void iso_conn_ready(struct iso_conn *conn)
        struct sock *sk = conn->sk;
        struct hci_ev_le_big_sync_estabilished *ev = NULL;
        struct hci_ev_le_pa_sync_established *ev2 = NULL;
+       struct hci_evt_le_big_info_adv_report *ev3 = NULL;
        struct hci_conn *hcon;
 
        BT_DBG("conn %p", conn);
@@ -1603,14 +1604,20 @@ static void iso_conn_ready(struct iso_conn *conn)
                                parent = iso_get_sock_listen(&hcon->src,
                                                             &hcon->dst,
                                                             iso_match_big, ev);
-               } else if (test_bit(HCI_CONN_PA_SYNC, &hcon->flags) ||
-                               test_bit(HCI_CONN_PA_SYNC_FAILED, &hcon->flags)) {
+               } else if (test_bit(HCI_CONN_PA_SYNC_FAILED, &hcon->flags)) {
                        ev2 = hci_recv_event_data(hcon->hdev,
                                                  HCI_EV_LE_PA_SYNC_ESTABLISHED);
                        if (ev2)
                                parent = iso_get_sock_listen(&hcon->src,
                                                             &hcon->dst,
                                                             iso_match_sid, ev2);
+               } else if (test_bit(HCI_CONN_PA_SYNC, &hcon->flags)) {
+                       ev3 = hci_recv_event_data(hcon->hdev,
+                                                 HCI_EVT_LE_BIG_INFO_ADV_REPORT);
+                       if (ev3)
+                               parent = iso_get_sock_listen(&hcon->src,
+                                                            &hcon->dst,
+                                                            iso_match_sync_handle, ev3);
                }
 
                if (!parent)
@@ -1650,11 +1657,13 @@ static void iso_conn_ready(struct iso_conn *conn)
                        hcon->sync_handle = iso_pi(parent)->sync_handle;
                }
 
-               if (ev2 && !ev2->status) {
-                       iso_pi(sk)->sync_handle = iso_pi(parent)->sync_handle;
+               if (ev3) {
                        iso_pi(sk)->qos = iso_pi(parent)->qos;
+                       iso_pi(sk)->qos.bcast.encryption = ev3->encryption;
+                       hcon->iso_qos = iso_pi(sk)->qos;
                        iso_pi(sk)->bc_num_bis = iso_pi(parent)->bc_num_bis;
                        memcpy(iso_pi(sk)->bc_bis, iso_pi(parent)->bc_bis, ISO_MAX_NUM_BIS);
+                       set_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags);
                }
 
                bacpy(&iso_pi(sk)->dst, &hcon->dst);