/* BlueZ - Bluetooth protocol stack for Linux Copyright (c) 2000-2001, 2010, Code Aurora Forum. All rights reserved. Copyright 2023-2024 NXP
Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation;
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.
*/
/* Check if we need to convert to identity address */
irk = hci_get_irk(hdev, bdaddr, bdaddr_type); if (irk) {
bdaddr = &irk->bdaddr;
bdaddr_type = irk->addr_type;
}
params = hci_pend_le_action_lookup(&hdev->pend_le_conns, bdaddr,
bdaddr_type); if (!params) return;
if (params->conn) {
hci_conn_drop(params->conn);
hci_conn_put(params->conn);
params->conn = NULL;
}
if (!params->explicit_connect) return;
/* If the status indicates successful cancellation of * the attempt (i.e. Unknown Connection Id) there's no point of * notifying failure since we'll go back to keep trying to * connect. The only exception is explicit connect requests * where a timeout + cancel does indicate an actual failure.
*/ if (status && status != HCI_ERROR_UNKNOWN_CONN_ID)
mgmt_connect_failed(hdev, conn, status);
/* The connection attempt was doing scan for new RPA, and is * in scan phase. If params are not associated with any other * autoconnect action, remove them completely. If they are, just unmark * them as waiting for connection, by clearing explicit_connect field.
*/
params->explicit_connect = false;
hci_pend_le_list_del_init(params);
switch (params->auto_connect) { case HCI_AUTO_CONN_EXPLICIT:
hci_conn_params_del(hdev, bdaddr, bdaddr_type); /* return instead of break to avoid duplicate scan update */ return; case HCI_AUTO_CONN_DIRECT: case HCI_AUTO_CONN_ALWAYS:
hci_pend_le_list_add(params, &hdev->pend_le_conns); break; case HCI_AUTO_CONN_REPORT:
hci_pend_le_list_add(params, &hdev->pend_le_reports); break; default: break;
}
if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
if (test_and_clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
hci_remove_link_key(hdev, &conn->dst);
hci_chan_list_flush(conn);
if (HCI_CONN_HANDLE_UNSET(conn->handle))
ida_free(&hdev->unset_handle_ida, conn->handle);
if (conn->cleanup)
conn->cleanup(conn);
if (conn->type == SCO_LINK || conn->type == ESCO_LINK) { switch (conn->setting & SCO_AIRMODE_MASK) { case SCO_AIRMODE_CVSD: case SCO_AIRMODE_TRANSP: if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_DISABLE_SCO); break;
}
} else { if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
}
debugfs_remove_recursive(conn->debugfs);
hci_conn_del_sysfs(conn);
hci_dev_put(hdev);
}
int hci_disconnect(struct hci_conn *conn, __u8 reason)
{
BT_DBG("hcon %p", conn);
/* When we are central of an established connection and it enters * the disconnect timeout, then go ahead and try to read the * current clock offset. Processing of the result is done * within the event handling and hci_clock_offset_evt function.
*/ if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER &&
(conn->state == BT_CONNECTED || conn->state == BT_CONFIG)) { struct hci_dev *hdev = conn->hdev; struct hci_cp_read_clock_offset clkoff_cp;
/* Do not take below 2 checks as error since the 1st means user do not * want to use HFP offload mode and the 2nd means the vendor controller * do not need to send below HCI command for offload mode.
*/ if (!codec->data_path || !hdev->get_codec_config_data) return 0;
BT_DBG("hcon %p state %s", conn, state_to_string(conn->state));
WARN_ON(refcnt < 0);
/* FIXME: It was observed that in pairing failed scenario, refcnt * drops below 0. Probably this is because l2cap_conn_del calls * l2cap_chan_del for each channel, and inside l2cap_chan_del conn is * dropped. After that loop hci_chan_del is called which also drops * conn. For now make sure that ACL is alive if refcnt is higher then 0, * otherwise drop it.
*/ if (refcnt > 0) return;
/* We could end up here due to having done directed advertising, * so clean up the state if necessary. This should however only * happen with broken hardware or if low duty cycle was used * (which doesn't have a timeout of its own).
*/ if (conn->role == HCI_ROLE_SLAVE) { /* Disable LE Advertising */
le_disable_advertising(hdev);
hci_dev_lock(hdev);
hci_conn_failed(conn, HCI_ERROR_ADVERTISING_TIMEOUT);
hci_dev_unlock(hdev); return;
}
if (test_and_clear_bit(HCI_CONN_BIG_SYNC, &conn->flags)) {
hci_conn_hash_list_flag(hdev, find_bis, BIS_LINK,
HCI_CONN_BIG_SYNC, d);
if (!d->count)
d->big_sync_term = true;
}
if (!d->pa_sync_term && !d->big_sync_term) return 0;
ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
terminate_big_destroy); if (ret)
kfree(d);
return ret;
}
/* Cleanup BIS connection * * Detects if there any BIS left connected in a BIG * broadcaster: Remove advertising instance and terminate BIG. * broadcaster receiver: Terminate BIG sync and terminate PA sync.
*/ staticvoid bis_cleanup(struct hci_conn *conn)
{ struct hci_dev *hdev = conn->hdev; struct hci_conn *bis;
bt_dev_dbg(hdev, "conn %p", conn);
if (conn->role == HCI_ROLE_MASTER) { if (!test_and_clear_bit(HCI_CONN_PER_ADV, &conn->flags)) return;
/* Check if ISO connection is a BIS and terminate advertising * set and BIG if there are no other connections using it.
*/
bis = hci_conn_hash_lookup_big_state(hdev,
conn->iso_qos.bcast.big,
BT_CONNECTED,
HCI_ROLE_MASTER); if (bis) return;
bis = hci_conn_hash_lookup_big_state(hdev,
conn->iso_qos.bcast.big,
BT_CONNECT,
HCI_ROLE_MASTER); if (bis) return;
bis = hci_conn_hash_lookup_big_state(hdev,
conn->iso_qos.bcast.big,
BT_OPEN,
HCI_ROLE_MASTER); if (bis) return;
/* Ignore broadcast or if CIG don't match */ if (!bacmp(&conn->dst, BDADDR_ANY) || d->cig != conn->iso_qos.ucast.cig) return;
d->count++;
}
/* Cleanup CIS connection: * * Detects if there any CIS left connected in a CIG and remove it.
*/ staticvoid cis_cleanup(struct hci_conn *conn)
{ struct hci_dev *hdev = conn->hdev; struct iso_list_data d;
if (conn->iso_qos.ucast.cig == BT_ISO_QOS_CIG_UNSET) return;
/* Check if ISO connection is a CIS and remove CIG if there are * no other connections using it.
*/
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_BOUND, &d);
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_CONNECT,
&d);
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK, BT_CONNECTED,
&d); if (d.count) return;
switch (type) { case ACL_LINK: if (!hdev->acl_mtu) return ERR_PTR(-ECONNREFUSED); break; case CIS_LINK: case BIS_LINK: case PA_LINK: if (hdev->iso_mtu) /* Dedicated ISO Buffer exists */ break;
fallthrough; case LE_LINK: if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU) return ERR_PTR(-ECONNREFUSED); if (!hdev->le_mtu && hdev->acl_mtu < HCI_MIN_LE_MTU) return ERR_PTR(-ECONNREFUSED); break; case SCO_LINK: case ESCO_LINK: if (!hdev->sco_pkts) /* Controller does not support SCO or eSCO over HCI */ return ERR_PTR(-ECONNREFUSED); break; default: return ERR_PTR(-ECONNREFUSED);
}
/* The SCO and eSCO connections will only be notified when their * setup has been completed. This is different to ACL links which * can be notified right away.
*/ if (conn->type != SCO_LINK && conn->type != ESCO_LINK) { if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
}
hci_conn_init_sysfs(conn); return conn;
}
struct hci_conn *hci_conn_add_unset(struct hci_dev *hdev, int type,
bdaddr_t *dst, u8 role)
{ int handle;
bt_dev_dbg(hdev, "dst %pMR", dst);
handle = hci_conn_hash_alloc_unset(hdev); if (unlikely(handle < 0)) return ERR_PTR(-ECONNREFUSED);
/* Due to race, SCO/ISO conn might be not established yet at this point, * and nothing else will clean it up. In other cases it is done via HCI * events.
*/ switch (conn->type) { case SCO_LINK: case ESCO_LINK: if (HCI_CONN_HANDLE_UNSET(conn->handle))
hci_conn_failed(conn, reason); break; case CIS_LINK: case BIS_LINK: case PA_LINK: if ((conn->state != BT_CONNECTED &&
!test_bit(HCI_CONN_CREATE_CIS, &conn->flags)) ||
test_bit(HCI_CONN_BIG_CREATED, &conn->flags))
hci_conn_failed(conn, reason); break;
}
}
/* If hdev is down it means * hci_dev_close_sync/hci_conn_hash_flush is in progress * and links don't need to be cleanup as all connections * would be cleanup.
*/ if (!test_bit(HCI_UP, &hdev->flags)) continue;
/* Remove the connection from the list so unacked logic can detect when * a certain pool is not being utilized.
*/
hci_conn_hash_del(hdev, conn);
/* Handle unacked frames: * * - In case there are no connection, or if restoring the buffers * considered in transist would overflow, restore all buffers to the * pool. * - Otherwise restore just the buffers considered in transit for the * hci_conn
*/ switch (conn->type) { case ACL_LINK: if (!hci_conn_num(hdev, ACL_LINK) ||
hdev->acl_cnt + conn->sent > hdev->acl_pkts)
hdev->acl_cnt = hdev->acl_pkts; else
hdev->acl_cnt += conn->sent; break; case LE_LINK:
cancel_delayed_work(&conn->le_conn_timeout);
if (hdev->le_pkts) { if (!hci_conn_num(hdev, LE_LINK) ||
hdev->le_cnt + conn->sent > hdev->le_pkts)
hdev->le_cnt = hdev->le_pkts; else
hdev->le_cnt += conn->sent;
} else { if ((!hci_conn_num(hdev, LE_LINK) &&
!hci_conn_num(hdev, ACL_LINK)) ||
hdev->acl_cnt + conn->sent > hdev->acl_pkts)
hdev->acl_cnt = hdev->acl_pkts; else
hdev->acl_cnt += conn->sent;
} break; case CIS_LINK: case BIS_LINK: case PA_LINK: if (!hci_iso_count(hdev) ||
hdev->iso_cnt + conn->sent > hdev->iso_pkts)
hdev->iso_cnt = hdev->iso_pkts; else
hdev->iso_cnt += conn->sent; break;
}
/* Remove the connection from the list and cleanup its remaining * state. This is a separate function since for some cases like * BT_CONNECT_SCAN we *only* want the cleanup part without the * rest of hci_conn_del.
*/
hci_conn_cleanup(conn);
/* Dequeue callbacks using connection pointer as data */
hci_cmd_sync_dequeue(hdev, NULL, conn, NULL);
}
/* Convert from HCI to three-value type */ if (id_addr_type == ADDR_LE_DEV_PUBLIC)
id_addr_type = BDADDR_LE_PUBLIC; else
id_addr_type = BDADDR_LE_RANDOM;
}
/* This function requires the caller holds hdev->lock */ staticvoid hci_le_conn_failed(struct hci_conn *conn, u8 status)
{ struct hci_dev *hdev = conn->hdev;
hci_connect_le_scan_cleanup(conn, status);
/* Enable advertising in case this was a failed connection * attempt as a peripheral.
*/
hci_enable_advertising(hdev);
}
/* This function requires the caller holds hdev->lock */ void hci_conn_failed(struct hci_conn *conn, u8 status)
{ struct hci_dev *hdev = conn->hdev;
bt_dev_dbg(hdev, "status 0x%2.2x", status);
switch (conn->type) { case LE_LINK:
hci_le_conn_failed(conn, status); break; case ACL_LINK:
mgmt_connect_failed(hdev, conn, status); break;
}
/* In case of BIG/PA sync failed, clear conn flags so that * the conns will be correctly cleaned up by ISO layer
*/
test_and_clear_bit(HCI_CONN_BIG_SYNC_FAILED, &conn->flags);
test_and_clear_bit(HCI_CONN_PA_SYNC_FAILED, &conn->flags);
/* If abort_reason has been sent it means the connection is being * aborted and the handle shall not be changed.
*/ if (conn->abort_reason) return conn->abort_reason;
if (HCI_CONN_HANDLE_UNSET(conn->handle))
ida_free(&hdev->unset_handle_ida, conn->handle);
/* Let's make sure that le is enabled.*/ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { if (lmp_le_capable(hdev)) return ERR_PTR(-ECONNREFUSED);
return ERR_PTR(-EOPNOTSUPP);
}
/* Since the controller supports only one LE connection attempt at a * time, we return -EBUSY if there is any connection attempt running.
*/ if (hci_lookup_le_connect(hdev)) return ERR_PTR(-EBUSY);
/* If there's already a connection object but it's not in * scanning state it means it must already be established, in * which case we can't do anything else except report a failure * to connect.
*/
conn = hci_conn_hash_lookup_le(hdev, dst, dst_type); if (conn && !test_bit(HCI_CONN_SCANNING, &conn->flags)) { return ERR_PTR(-EBUSY);
}
/* Check if the destination address has been resolved by the controller * since if it did then the identity address shall be used.
*/ if (!dst_resolved) { /* When given an identity address with existing identity * resolving key, the connection needs to be established * to a resolvable random address. * * Storing the resolvable random address is required here * to handle connection failures. The address will later * be resolved back into the original identity address * from the connect request.
*/
irk = hci_find_irk_by_addr(hdev, dst, dst_type); if (irk && bacmp(&irk->rpa, BDADDR_ANY)) {
dst = &irk->rpa;
dst_type = ADDR_LE_DEV_RANDOM;
}
}
conn = hci_conn_hash_lookup_le(hdev, addr, type); if (!conn) returnfalse;
if (conn->state != BT_CONNECTED) returnfalse;
returntrue;
}
/* This function requires the caller holds hdev->lock */ staticint hci_explicit_conn_params_set(struct hci_dev *hdev,
bdaddr_t *addr, u8 addr_type)
{ struct hci_conn_params *params;
if (is_connected(hdev, addr, addr_type)) return -EISCONN;
params = hci_conn_params_lookup(hdev, addr, addr_type); if (!params) {
params = hci_conn_params_add(hdev, addr, addr_type); if (!params) return -ENOMEM;
/* If we created new params, mark them to be deleted in * hci_connect_le_scan_cleanup. It's different case than * existing disabled params, those will stay after cleanup.
*/
params->auto_connect = HCI_AUTO_CONN_EXPLICIT;
}
/* We're trying to connect, so make sure params are at pend_le_conns */ if (params->auto_connect == HCI_AUTO_CONN_DISABLED ||
params->auto_connect == HCI_AUTO_CONN_REPORT ||
params->auto_connect == HCI_AUTO_CONN_EXPLICIT) {
hci_pend_le_list_del_init(params);
hci_pend_le_list_add(params, &hdev->pend_le_conns);
}
/* Allocate BIS if not set */ if (qos->bcast.bis == BT_ISO_QOS_BIS_UNSET) { if (qos->bcast.big != BT_ISO_QOS_BIG_UNSET) {
conn = hci_conn_hash_lookup_big(hdev, qos->bcast.big);
if (conn) { /* If the BIG handle is already matched to an advertising * handle, do not allocate a new one.
*/
qos->bcast.bis = conn->iso_qos.bcast.bis; return 0;
}
}
/* Find an unused adv set to advertise BIS, skip instance 0x00 * since it is reserved as general purpose set.
*/ for (bis = 0x01; bis < hdev->le_num_of_adv_sets;
bis++) {
conn = hci_conn_hash_lookup_bis(hdev, BDADDR_ANY, bis); if (!conn) break;
}
if (bis == hdev->le_num_of_adv_sets) return -EADDRNOTAVAIL;
/* Update BIS */
qos->bcast.bis = bis;
}
return 0;
}
/* This function requires the caller holds hdev->lock */ staticstruct hci_conn *hci_add_bis(struct hci_dev *hdev, bdaddr_t *dst,
__u8 sid, struct bt_iso_qos *qos,
__u8 base_len, __u8 *base, u16 timeout)
{ struct hci_conn *conn; int err;
/* Let's make sure that le is enabled.*/ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { if (lmp_le_capable(hdev)) return ERR_PTR(-ECONNREFUSED); return ERR_PTR(-EOPNOTSUPP);
}
err = qos_set_big(hdev, qos); if (err) return ERR_PTR(err);
err = qos_set_bis(hdev, qos); if (err) return ERR_PTR(err);
/* Check if the LE Create BIG command has already been sent */
conn = hci_conn_hash_lookup_per_adv_bis(hdev, dst, qos->bcast.big,
qos->bcast.big); if (conn) return ERR_PTR(-EADDRINUSE);
/* Check BIS settings against other bound BISes, since all * BISes in a BIG must have the same value for all parameters
*/
conn = hci_conn_hash_lookup_big(hdev, qos->bcast.big);
/* This function requires the caller holds hdev->lock */ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
u8 dst_type, u8 sec_level,
u16 conn_timeout, enum conn_reasons conn_reason)
{ struct hci_conn *conn;
/* Let's make sure that le is enabled.*/ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { if (lmp_le_capable(hdev)) return ERR_PTR(-ECONNREFUSED);
return ERR_PTR(-EOPNOTSUPP);
}
/* Some devices send ATT messages as soon as the physical link is * established. To be able to handle these ATT messages, the user- * space first establishes the connection and then starts the pairing * process. * * So if a hci_conn object already exists for the following connection * attempt, we simply update pending_sec_level and auth_type fields * and return the object found.
*/
conn = hci_conn_hash_lookup_le(hdev, dst, dst_type); if (conn) { if (conn->pending_sec_level < sec_level)
conn->pending_sec_level = sec_level; goto done;
}
BT_DBG("requesting refresh of dst_addr");
conn = hci_conn_add_unset(hdev, LE_LINK, dst, HCI_ROLE_MASTER); if (IS_ERR(conn)) return conn;
if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { if (lmp_bredr_capable(hdev)) return ERR_PTR(-ECONNREFUSED);
return ERR_PTR(-EOPNOTSUPP);
}
/* Reject outgoing connection to device with same BD ADDR against * CVE-2020-26555
*/ if (!bacmp(&hdev->bdaddr, dst)) {
bt_dev_dbg(hdev, "Reject connection with same BD_ADDR %pMR\n",
dst); return ERR_PTR(-ECONNREFUSED);
}
acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); if (!acl) {
acl = hci_conn_add_unset(hdev, ACL_LINK, dst, HCI_ROLE_MASTER); if (IS_ERR(acl)) return acl;
}
hci_conn_hold(acl);
acl->conn_reason = conn_reason; if (acl->state == BT_OPEN || acl->state == BT_CLOSED) { int err;
/* Reprogram all CIS(s) with the same CIG, valid range are: * num_cis: 0x00 to 0x1F * cis_id: 0x00 to 0xEF
*/ for (cis_id = 0x00; cis_id < 0xf0 &&
aux_num_cis < pdu->num_cis; cis_id++) { struct hci_cis_params *cis;
conn = hci_conn_hash_lookup_cis(hdev, NULL, 0, cig_id, cis_id); if (!conn) continue;
/* Allocate first still reconfigurable CIG if not set */ if (qos->ucast.cig == BT_ISO_QOS_CIG_UNSET) { for (data.cig = 0x00; data.cig < 0xf0; data.cig++) {
data.count = 0;
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK,
BT_CONNECT, &data); if (data.count) continue;
hci_conn_hash_list_state(hdev, find_cis, CIS_LINK,
BT_CONNECTED, &data); if (!data.count) break;
}
if (data.cig == 0xf0) returnfalse;
/* Update CIG */
qos->ucast.cig = data.cig;
}
if (qos->ucast.cis != BT_ISO_QOS_CIS_UNSET) { if (hci_conn_hash_lookup_cis(hdev, NULL, 0, qos->ucast.cig,
qos->ucast.cis)) returnfalse; goto done;
}
/* Allocate first available CIS if not set */ for (data.cig = qos->ucast.cig, data.cis = 0x00; data.cis < 0xf0;
data.cis++) { if (!hci_conn_hash_lookup_cis(hdev, NULL, 0, data.cig,
data.cis)) { /* Update CIS */
qos->ucast.cis = data.cis; break;
}
}
if (qos->ucast.cis == BT_ISO_QOS_CIS_UNSET) returnfalse;
done: if (hci_cmd_sync_queue(hdev, set_cig_params_sync,
UINT_PTR(qos->ucast.cig), NULL) < 0) returnfalse;
/* Check if CIS has been set and the settings matches */ if (cis->state == BT_BOUND &&
!memcmp(&cis->iso_qos, qos, sizeof(*qos))) return cis;
/* Update LINK PHYs according to QoS preference */
cis->le_tx_phy = qos->ucast.out.phy;
cis->le_rx_phy = qos->ucast.in.phy;
/* If output interval is not set use the input interval as it cannot be * 0x000000.
*/ if (!qos->ucast.out.interval)
qos->ucast.out.interval = qos->ucast.in.interval;
/* If input interval is not set use the output interval as it cannot be * 0x000000.
*/ if (!qos->ucast.in.interval)
qos->ucast.in.interval = qos->ucast.out.interval;
/* If output latency is not set use the input latency as it cannot be * 0x0000.
*/ if (!qos->ucast.out.latency)
qos->ucast.out.latency = qos->ucast.in.latency;
/* If input latency is not set use the output latency as it cannot be * 0x0000.
*/ if (!qos->ucast.in.latency)
qos->ucast.in.latency = qos->ucast.out.latency;
if (!hci_le_set_cig_params(cis, qos)) {
hci_conn_drop(cis); return ERR_PTR(-EINVAL);
}
staticvoid hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn, struct bt_iso_io_qos *qos, __u8 phy)
{ /* Only set MTU if PHY is enabled */ if (!qos->sdu && qos->phy)
qos->sdu = conn->mtu;
/* Use the same PHY as ACL if set to any */ if (qos->phy == BT_ISO_PHY_ANY)
qos->phy = phy;
/* Use LE ACL connection interval if not set */ if (!qos->interval) /* ACL interval unit in 1.25 ms to us */
qos->interval = conn->le_conn_interval * 1250;
/* Use LE ACL connection latency if not set */ if (!qos->latency)
qos->latency = conn->le_conn_latency;
}
/* Look for any BIS that is open for rebinding */
conn = hci_conn_hash_lookup_big_state(hdev, qos->bcast.big, BT_OPEN,
HCI_ROLE_MASTER); if (conn) {
memcpy(qos, &conn->iso_qos, sizeof(*qos));
conn->state = BT_CONNECTED; return conn;
}
/* We need hci_conn object using the BDADDR_ANY as dst */
conn = hci_add_bis(hdev, dst, sid, qos, base_len, eir, timeout); if (IS_ERR(conn)) return conn;
/* Update LINK PHYs according to QoS preference */
conn->le_tx_phy = qos->bcast.out.phy;
conn->le_tx_phy = qos->bcast.out.phy;
/* Add Basic Announcement into Peridic Adv Data if BASE is set */ if (base_len && base) {
memcpy(conn->le_per_adv_data, eir, sizeof(eir));
conn->le_per_adv_data_len = base_len;
}
/* Set HCI_CONN_PER_ADV for all bound connections, to mark that * the start periodic advertising and create BIG commands have * been queued
*/
hci_conn_hash_list_state(hdev, bis_mark_per_adv, BIS_LINK,
BT_BOUND, &data);
/* Queue start periodic advertising and create BIG */
err = hci_cmd_sync_queue(hdev, create_big_sync, conn,
create_big_complete); if (err < 0) {
hci_conn_drop(conn); return ERR_PTR(err);
}
link = hci_conn_link(le, cis);
hci_conn_drop(cis); if (!link) {
hci_conn_drop(le); return ERR_PTR(-ENOLINK);
}
cis->state = BT_CONNECT;
hci_le_create_cis_pending(hdev);
return cis;
}
/* Check link security requirement */ int hci_conn_check_link_mode(struct hci_conn *conn)
{
BT_DBG("hcon %p", conn);
/* In Secure Connections Only mode, it is required that Secure * Connections is used and the link is encrypted with AES-CCM * using a P-256 authenticated combination key.
*/ if (hci_dev_test_flag(conn->hdev, HCI_SC_ONLY)) { if (!hci_conn_sc_enabled(conn) ||
!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
conn->key_type != HCI_LK_AUTH_COMBINATION_P256) return 0;
}
/* AES encryption is required for Level 4: * * BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 3, Part C * page 1319: * * 128-bit equivalent strength for link and encryption keys * required using FIPS approved algorithms (E0 not allowed, * SAFER+ not allowed, and P-192 not allowed; encryption key * not shortened)
*/ if (conn->sec_level == BT_SECURITY_FIPS &&
!test_bit(HCI_CONN_AES_CCM, &conn->flags)) {
bt_dev_err(conn->hdev, "Invalid security: Missing AES-CCM usage"); return 0;
}
if (hci_conn_ssp_enabled(conn) &&
!test_bit(HCI_CONN_ENCRYPT, &conn->flags)) return 0;
/* Set the ENCRYPT_PEND to trigger encryption after * authentication.
*/ if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags);
}
return 0;
}
/* Encrypt the link */ staticvoid hci_conn_encrypt(struct hci_conn *conn)
{
BT_DBG("hcon %p", conn);
if (conn->type == LE_LINK) return smp_conn_security(conn, sec_level);
/* For sdp we don't need the link key. */ if (sec_level == BT_SECURITY_SDP) return 1;
/* For non 2.1 devices and low security level we don't need the link
key. */ if (sec_level == BT_SECURITY_LOW && !hci_conn_ssp_enabled(conn)) return 1;
/* For other security levels we need the link key. */ if (!test_bit(HCI_CONN_AUTH, &conn->flags)) goto auth;
switch (conn->key_type) { case HCI_LK_AUTH_COMBINATION_P256: /* An authenticated FIPS approved combination key has * sufficient security for security level 4 or lower.
*/ if (sec_level <= BT_SECURITY_FIPS) goto encrypt; break; case HCI_LK_AUTH_COMBINATION_P192: /* An authenticated combination key has sufficient security for * security level 3 or lower.
*/ if (sec_level <= BT_SECURITY_HIGH) goto encrypt; break; case HCI_LK_UNAUTH_COMBINATION_P192: case HCI_LK_UNAUTH_COMBINATION_P256: /* An unauthenticated combination key has sufficient security * for security level 2 or lower.
*/ if (sec_level <= BT_SECURITY_MEDIUM) goto encrypt; break; case HCI_LK_COMBINATION: /* A combination key has always sufficient security for the * security levels 2 or lower. High security level requires the * combination key is generated using maximum PIN code length * (16). For pre 2.1 units.
*/ if (sec_level <= BT_SECURITY_MEDIUM || conn->pin_length == 16) goto encrypt; break; default: break;
}
auth: if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return 0;
if (initiator)
set_bit(HCI_CONN_AUTH_INITIATOR, &conn->flags);
if (!hci_conn_auth(conn, sec_level, auth_type)) return 0;
encrypt: if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) { /* Ensure that the encryption key size has been read, * otherwise stall the upper layer responses.
*/ if (!conn->enc_key_size) return 0;
/* Nothing else needed, all requirements are met */ return 1;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.