Bluetooth: ATT: Retry in case of recoverable error

If the remote response with encryption/authentication error attempt to
elevate the security and retry sending the request:

bt: bt_att_recv (0x00112e0c): Received ATT code 0x01 len 5
bt: att_error_rsp (0x00112e0c): request 0x0a handle 0x0018 error 0x0f
bt: security_changed (0x00112e0c): conn 0x0010f5ac level 1
bt: security_changed (0x00112e0c): Retrying
bt: bt_att_recv (0x00112e0c): Received ATT code 0x0b len 1
bt: att_handle_read_rsp (0x00112e0c):
bt: att_read_rsp (0x00112e0c): err 0x00

Change-Id: I9d74fb50385c39b2092357f4fd6912a3eb041442
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
This commit is contained in:
Luiz Augusto von Dentz 2015-09-08 17:41:57 +03:00 committed by Anas Nashif
commit c74c6fd453

View file

@ -77,6 +77,7 @@ struct bt_att_req {
void *user_data;
bt_att_destroy_t destroy;
struct bt_buf *buf;
bool retrying;
};
/* ATT channel specific context */
@ -1193,6 +1194,28 @@ static uint8_t att_signed_write_cmd(struct bt_conn *conn, struct bt_buf *buf)
buf->len - sizeof(struct bt_att_signature));
}
static int att_change_security(struct bt_conn *conn, uint8_t err)
{
bt_security_t sec;
switch (err) {
case BT_ATT_ERR_INSUFFICIENT_ENCRYPTION:
if (conn->sec_level >= BT_SECURITY_MEDIUM)
return -EALREADY;
sec = BT_SECURITY_MEDIUM;
break;
case BT_ATT_ERR_AUTHENTICATION:
if (conn->sec_level >= BT_SECURITY_HIGH)
return -EALREADY;
sec = BT_SECURITY_HIGH;
break;
default:
return -EINVAL;
}
return bt_conn_security(conn, sec);
}
static uint8_t att_error_rsp(struct bt_conn *conn, struct bt_buf *buf)
{
struct bt_att *att = conn->att;
@ -1215,6 +1238,16 @@ static uint8_t att_error_rsp(struct bt_conn *conn, struct bt_buf *buf)
err = rsp->request == hdr->code ? rsp->error : BT_ATT_ERR_UNLIKELY;
if (req->retrying)
goto done;
/* Check if security needs to be changed */
if (!att_change_security(conn, err)) {
req->retrying = true;
/* Wait security_changed: TODO: Handle fail case */
return 0;
}
done:
return att_handle_rsp(conn, NULL, 0, err);
}
@ -1464,6 +1497,32 @@ static void bt_att_disconnected(struct bt_conn *conn)
bt_gatt_disconnected(conn);
}
static void security_changed(struct bt_conn *conn, bt_security_t level)
{
struct bt_att *att = conn->att;
struct bt_att_req *req;
if (!att) {
return;
}
BT_DBG("conn %p level %u\n", conn, level);
req = &att->req;
if (!req->retrying)
return;
BT_DBG("Retrying\n");
/* Resend buffer */
bt_l2cap_send(conn, BT_L2CAP_CID_ATT, req->buf);
req->buf = NULL;
}
static struct bt_conn_cb conn_callbacks = {
.security_changed = security_changed,
};
void bt_att_init(void)
{
static struct bt_l2cap_chan chan = {
@ -1474,6 +1533,8 @@ void bt_att_init(void)
};
bt_l2cap_chan_register(&chan);
bt_conn_cb_register(&conn_callbacks);
}
uint16_t bt_att_get_mtu(struct bt_conn *conn)
@ -1506,6 +1567,7 @@ int bt_att_send(struct bt_conn *conn, struct bt_buf *buf, bt_att_func_t func,
}
att->req.buf = bt_buf_clone(buf);
att->req.retrying = false;
att->req.func = func;
att->req.user_data = user_data;
att->req.destroy = destroy;