diff options
Diffstat (limited to 'net/bluetooth/hci_conn.c')
-rw-r--r-- | net/bluetooth/hci_conn.c | 106 |
1 files changed, 80 insertions, 26 deletions
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ba5366c320da..bd66c52eff95 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -225,8 +225,8 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, cp.conn_interval_max = cpu_to_le16(max); cp.conn_latency = cpu_to_le16(latency); cp.supervision_timeout = cpu_to_le16(to_multiplier); - cp.min_ce_len = __constant_cpu_to_le16(0x0001); - cp.max_ce_len = __constant_cpu_to_le16(0x0001); + cp.min_ce_len = __constant_cpu_to_le16(0x0000); + cp.max_ce_len = __constant_cpu_to_le16(0x0000); hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); } @@ -514,6 +514,21 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) } EXPORT_SYMBOL(hci_get_route); +/* This function requires the caller holds hdev->lock */ +static void le_conn_failed(struct hci_conn *conn, u8 status) +{ + struct hci_dev *hdev = conn->hdev; + + conn->state = BT_CLOSED; + + mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type, + status); + + hci_proto_connect_cfm(conn, status); + + hci_conn_del(conn); +} + static void create_le_conn_complete(struct hci_dev *hdev, u8 status) { struct hci_conn *conn; @@ -530,14 +545,7 @@ static void create_le_conn_complete(struct hci_dev *hdev, u8 status) if (!conn) goto done; - conn->state = BT_CLOSED; - - mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type, - status); - - hci_proto_connect_cfm(conn, status); - - hci_conn_del(conn); + le_conn_failed(conn, status); done: hci_dev_unlock(hdev); @@ -558,8 +566,8 @@ static int hci_create_le_conn(struct hci_conn *conn) bacpy(&cp.peer_addr, &conn->dst); cp.peer_addr_type = conn->dst_type; cp.own_address_type = conn->src_type; - cp.conn_interval_min = cpu_to_le16(hdev->le_conn_min_interval); - cp.conn_interval_max = cpu_to_le16(hdev->le_conn_max_interval); + cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval); + cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval); cp.supervision_timeout = __constant_cpu_to_le16(0x002a); cp.min_ce_len = __constant_cpu_to_le16(0x0000); cp.max_ce_len = __constant_cpu_to_le16(0x0000); @@ -578,7 +586,9 @@ static int hci_create_le_conn(struct hci_conn *conn) static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, u8 dst_type, u8 sec_level, u8 auth_type) { + struct hci_conn_params *params; struct hci_conn *conn; + struct smp_irk *irk; int err; if (test_bit(HCI_ADVERTISING, &hdev->flags)) @@ -607,15 +617,36 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, if (conn) return ERR_PTR(-EBUSY); + /* Convert from L2CAP channel address type to HCI address type */ + if (dst_type == BDADDR_LE_PUBLIC) + dst_type = ADDR_LE_DEV_PUBLIC; + else + dst_type = ADDR_LE_DEV_RANDOM; + + /* When given an identity address with existing identity + * resolving key, the connection needs to be established + * to a resolvable random address. + * + * This uses the cached random resolvable address from + * a previous scan. When no cached address is available, + * try connecting to the identity address instead. + * + * 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_add(hdev, LE_LINK, dst); if (!conn) return ERR_PTR(-ENOMEM); - if (dst_type == BDADDR_LE_PUBLIC) - conn->dst_type = ADDR_LE_DEV_PUBLIC; - else - conn->dst_type = ADDR_LE_DEV_RANDOM; - + conn->dst_type = dst_type; conn->src_type = hdev->own_addr_type; conn->state = BT_CONNECT; @@ -625,6 +656,15 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, conn->pending_sec_level = sec_level; conn->auth_type = auth_type; + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (params) { + conn->le_conn_min_interval = params->conn_min_interval; + conn->le_conn_max_interval = params->conn_max_interval; + } else { + conn->le_conn_min_interval = hdev->le_conn_min_interval; + conn->le_conn_max_interval = hdev->le_conn_max_interval; + } + err = hci_create_le_conn(conn); if (err) return ERR_PTR(err); @@ -800,14 +840,23 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) if (!(conn->link_mode & HCI_LM_AUTH)) goto auth; - /* An authenticated combination key has sufficient security for any - security level. */ - if (conn->key_type == HCI_LK_AUTH_COMBINATION) + /* An authenticated FIPS approved combination key has sufficient + * security for security level 4. */ + if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256 && + sec_level == BT_SECURITY_FIPS) + goto encrypt; + + /* An authenticated combination key has sufficient security for + security level 3. */ + if ((conn->key_type == HCI_LK_AUTH_COMBINATION_P192 || + conn->key_type == HCI_LK_AUTH_COMBINATION_P256) && + sec_level == BT_SECURITY_HIGH) goto encrypt; /* An unauthenticated combination key has sufficient security for security level 1 and 2. */ - if (conn->key_type == HCI_LK_UNAUTH_COMBINATION && + if ((conn->key_type == HCI_LK_UNAUTH_COMBINATION_P192 || + conn->key_type == HCI_LK_UNAUTH_COMBINATION_P256) && (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW)) goto encrypt; @@ -816,7 +865,8 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) is generated using maximum PIN code length (16). For pre 2.1 units. */ if (conn->key_type == HCI_LK_COMBINATION && - (sec_level != BT_SECURITY_HIGH || conn->pin_length == 16)) + (sec_level == BT_SECURITY_MEDIUM || sec_level == BT_SECURITY_LOW || + conn->pin_length == 16)) goto encrypt; auth: @@ -840,13 +890,17 @@ int hci_conn_check_secure(struct hci_conn *conn, __u8 sec_level) { BT_DBG("hcon %p", conn); - if (sec_level != BT_SECURITY_HIGH) - return 1; /* Accept if non-secure is required */ + /* Accept if non-secure or higher security level is required */ + if (sec_level != BT_SECURITY_HIGH && sec_level != BT_SECURITY_FIPS) + return 1; - if (conn->sec_level == BT_SECURITY_HIGH) + /* Accept if secure or higher security level is already present */ + if (conn->sec_level == BT_SECURITY_HIGH || + conn->sec_level == BT_SECURITY_FIPS) return 1; - return 0; /* Reject not secure link */ + /* Reject not secure link */ + return 0; } EXPORT_SYMBOL(hci_conn_check_secure); |