Bluetooth: controller: implement connection termination on invalid pdus
When a running procedure receives a REJECT or UNKNOWN_RSP PDU that is not an expected part of the procedure flow this leads to termination of the connection This affects procedures: CU/CPR, CTE, PHY, PING, DLE, FEX, VEX, CHMU Unit tests are updated to cover the updated behaviour. Signed-off-by: Erik Brockhoff <erbr@oticon.com>
This commit is contained in:
parent
0b87ebbdd3
commit
d8f0c1adf6
15 changed files with 1343 additions and 39 deletions
|
@ -186,6 +186,21 @@ static void lp_chmu_execute_fsm(struct ll_conn *conn, struct proc_ctx *ctx, uint
|
|||
}
|
||||
}
|
||||
|
||||
void llcp_lp_chmu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx)
|
||||
{
|
||||
struct pdu_data *pdu = (struct pdu_data *)rx->pdu;
|
||||
|
||||
switch (pdu->llctrl.opcode) {
|
||||
default:
|
||||
/* Invalid behaviour */
|
||||
/* Invalid PDU received so terminate connection */
|
||||
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
|
||||
llcp_lr_complete(conn);
|
||||
ctx->state = LP_CHMU_STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void llcp_lp_chmu_init_proc(struct proc_ctx *ctx)
|
||||
{
|
||||
ctx->state = LP_CHMU_STATE_IDLE;
|
||||
|
@ -195,6 +210,7 @@ void llcp_lp_chmu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param)
|
|||
{
|
||||
lp_chmu_execute_fsm(conn, ctx, LP_CHMU_EVT_RUN, param);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BT_CENTRAL */
|
||||
|
||||
#if defined(CONFIG_BT_PERIPHERAL)
|
||||
|
@ -285,8 +301,12 @@ void llcp_rp_chmu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_
|
|||
rp_chmu_execute_fsm(conn, ctx, RP_CHMU_EVT_RX_CHAN_MAP_IND, pdu);
|
||||
break;
|
||||
default:
|
||||
/* Unknown opcode */
|
||||
LL_ASSERT(0);
|
||||
/* Invalid behaviour */
|
||||
/* Invalid PDU received so terminate connection */
|
||||
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
|
||||
llcp_rr_complete(conn);
|
||||
ctx->state = RP_CHMU_STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -169,18 +169,11 @@ static void lp_comm_ntf_feature_exchange(struct ll_conn *conn, struct proc_ctx *
|
|||
case PDU_DATA_LLCTRL_TYPE_FEATURE_RSP:
|
||||
llcp_ntf_encode_feature_rsp(conn, pdu);
|
||||
break;
|
||||
case PDU_DATA_LLCTRL_TYPE_PER_INIT_FEAT_XCHG:
|
||||
case PDU_DATA_LLCTRL_TYPE_FEATURE_REQ:
|
||||
/*
|
||||
* No notification on feature-request or periph-feature request
|
||||
* TODO: probably handle as an unexpected call
|
||||
*/
|
||||
break;
|
||||
case PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP:
|
||||
llcp_ntf_encode_unknown_rsp(ctx, pdu);
|
||||
break;
|
||||
default:
|
||||
/* TODO: define behaviour for unexpected PDU */
|
||||
/* Unexpected PDU, should not get through, so ASSERT */
|
||||
LL_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +186,7 @@ static void lp_comm_ntf_version_ind(struct ll_conn *conn, struct proc_ctx *ctx,
|
|||
llcp_ntf_encode_version_ind(conn, pdu);
|
||||
break;
|
||||
default:
|
||||
/* TODO: define behaviour for unexpected PDU */
|
||||
/* Unexpected PDU, should not get through, so ASSERT */
|
||||
LL_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
@ -244,7 +237,7 @@ static void lp_comm_ntf_cte_req(struct ll_conn *conn, struct proc_ctx *ctx, stru
|
|||
llcp_ntf_encode_reject_ext_ind(ctx, pdu);
|
||||
break;
|
||||
default:
|
||||
/* TODO (ppryga): Update when behavior for unexpected PDU is defined */
|
||||
/* Unexpected PDU, should not get through, so ASSERT */
|
||||
LL_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
@ -290,6 +283,15 @@ static void lp_comm_ntf(struct ll_conn *conn, struct proc_ctx *ctx)
|
|||
ll_rx_sched();
|
||||
}
|
||||
|
||||
static void lp_comm_terminate_invalid_pdu(struct ll_conn *conn, struct proc_ctx *ctx)
|
||||
{
|
||||
/* Invalid behaviour */
|
||||
/* Invalid PDU received so terminate connection */
|
||||
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
|
||||
llcp_lr_complete(conn);
|
||||
ctx->state = LP_COMMON_STATE_IDLE;
|
||||
}
|
||||
|
||||
static void lp_comm_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t evt, void *param)
|
||||
{
|
||||
switch (ctx->proc) {
|
||||
|
@ -301,17 +303,23 @@ static void lp_comm_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
|
|||
ctx->state = LP_COMMON_STATE_IDLE;
|
||||
} else {
|
||||
/* Illegal response opcode */
|
||||
LL_ASSERT(0);
|
||||
lp_comm_terminate_invalid_pdu(conn, ctx);
|
||||
}
|
||||
break;
|
||||
#endif /* CONFIG_BT_CTLR_LE_PING */
|
||||
case PROC_FEATURE_EXCHANGE:
|
||||
if (!llcp_ntf_alloc_is_available()) {
|
||||
ctx->state = LP_COMMON_STATE_WAIT_NTF;
|
||||
if (ctx->response_opcode == PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP ||
|
||||
ctx->response_opcode == PDU_DATA_LLCTRL_TYPE_FEATURE_RSP) {
|
||||
if (!llcp_ntf_alloc_is_available()) {
|
||||
ctx->state = LP_COMMON_STATE_WAIT_NTF;
|
||||
} else {
|
||||
lp_comm_ntf(conn, ctx);
|
||||
llcp_lr_complete(conn);
|
||||
ctx->state = LP_COMMON_STATE_IDLE;
|
||||
}
|
||||
} else {
|
||||
lp_comm_ntf(conn, ctx);
|
||||
llcp_lr_complete(conn);
|
||||
ctx->state = LP_COMMON_STATE_IDLE;
|
||||
/* Illegal response opcode */
|
||||
lp_comm_terminate_invalid_pdu(conn, ctx);
|
||||
}
|
||||
break;
|
||||
#if defined(CONFIG_BT_CTLR_MIN_USED_CHAN) && defined(CONFIG_BT_PERIPHERAL)
|
||||
|
@ -321,12 +329,17 @@ static void lp_comm_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
|
|||
break;
|
||||
#endif /* CONFIG_BT_CTLR_MIN_USED_CHAN && CONFIG_BT_PERIPHERAL */
|
||||
case PROC_VERSION_EXCHANGE:
|
||||
if (!llcp_ntf_alloc_is_available()) {
|
||||
ctx->state = LP_COMMON_STATE_WAIT_NTF;
|
||||
if (ctx->response_opcode == PDU_DATA_LLCTRL_TYPE_VERSION_IND) {
|
||||
if (!llcp_ntf_alloc_is_available()) {
|
||||
ctx->state = LP_COMMON_STATE_WAIT_NTF;
|
||||
} else {
|
||||
lp_comm_ntf(conn, ctx);
|
||||
llcp_lr_complete(conn);
|
||||
ctx->state = LP_COMMON_STATE_IDLE;
|
||||
}
|
||||
} else {
|
||||
lp_comm_ntf(conn, ctx);
|
||||
llcp_lr_complete(conn);
|
||||
ctx->state = LP_COMMON_STATE_IDLE;
|
||||
/* Illegal response opcode */
|
||||
lp_comm_terminate_invalid_pdu(conn, ctx);
|
||||
}
|
||||
break;
|
||||
case PROC_TERMINATE:
|
||||
|
@ -339,7 +352,7 @@ static void lp_comm_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
|
|||
break;
|
||||
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
|
||||
case PROC_DATA_LENGTH_UPDATE:
|
||||
if (ctx->response_opcode != PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP) {
|
||||
if (ctx->response_opcode == PDU_DATA_LLCTRL_TYPE_LENGTH_RSP) {
|
||||
/* Apply changes in data lengths/times */
|
||||
uint8_t dle_changed = ull_dle_update_eff(conn);
|
||||
|
||||
|
@ -353,12 +366,16 @@ static void lp_comm_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
|
|||
llcp_lr_complete(conn);
|
||||
ctx->state = LP_COMMON_STATE_IDLE;
|
||||
}
|
||||
} else {
|
||||
} else if (ctx->response_opcode == PDU_DATA_LLCTRL_TYPE_UNKNOWN_RSP) {
|
||||
/* Peer does not accept DLU, so disable on current connection */
|
||||
feature_unmask_features(conn, LL_FEAT_BIT_DLE);
|
||||
|
||||
llcp_lr_complete(conn);
|
||||
ctx->state = LP_COMMON_STATE_IDLE;
|
||||
} else {
|
||||
/* Illegal response opcode */
|
||||
lp_comm_terminate_invalid_pdu(conn, ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!ull_cp_remote_dle_pending(conn)) {
|
||||
|
@ -402,6 +419,9 @@ static void lp_comm_complete(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t
|
|||
*/
|
||||
ull_cp_cte_req_set_disable(conn);
|
||||
ctx->state = LP_COMMON_STATE_IDLE;
|
||||
} else {
|
||||
/* Illegal response opcode */
|
||||
lp_comm_terminate_invalid_pdu(conn, ctx);
|
||||
}
|
||||
|
||||
if (ctx->state == LP_COMMON_STATE_IDLE) {
|
||||
|
@ -627,6 +647,9 @@ static void lp_comm_rx_decode(struct ll_conn *conn, struct proc_ctx *ctx, struct
|
|||
case PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND:
|
||||
llcp_pdu_decode_reject_ext_ind(ctx, pdu);
|
||||
break;
|
||||
case PDU_DATA_LLCTRL_TYPE_REJECT_IND:
|
||||
/* Empty on purpose, as we don't care about the PDU content, we'll disconnect */
|
||||
break;
|
||||
default:
|
||||
/* Unknown opcode */
|
||||
LL_ASSERT(0);
|
||||
|
|
|
@ -212,6 +212,7 @@ static void lp_cu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
|
|||
break;
|
||||
#endif /* CONFIG_BT_CENTRAL */
|
||||
default:
|
||||
/* Unknown opcode */
|
||||
LL_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
@ -601,8 +602,11 @@ void llcp_lp_cu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pd
|
|||
lp_cu_execute_fsm(conn, ctx, LP_CU_EVT_REJECT, pdu);
|
||||
break;
|
||||
default:
|
||||
/* Unknown opcode */
|
||||
LL_ASSERT(0);
|
||||
/* Invalid behaviour */
|
||||
/* Invalid PDU received so terminate connection */
|
||||
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
|
||||
llcp_lr_complete(conn);
|
||||
ctx->state = LP_CU_STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -652,6 +656,7 @@ static void rp_cu_tx(struct ll_conn *conn, struct proc_ctx *ctx, uint8_t opcode)
|
|||
llcp_pdu_encode_unknown_rsp(ctx, pdu);
|
||||
break;
|
||||
default:
|
||||
/* Unknown opcode */
|
||||
LL_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
|
@ -1079,8 +1084,11 @@ void llcp_rp_cu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pd
|
|||
rp_cu_execute_fsm(conn, ctx, RP_CU_EVT_CONN_UPDATE_IND, pdu);
|
||||
break;
|
||||
default:
|
||||
/* Unknown opcode */
|
||||
LL_ASSERT(0);
|
||||
/* Invalid behaviour */
|
||||
/* Invalid PDU received so terminate connection */
|
||||
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
|
||||
llcp_rr_complete(conn);
|
||||
ctx->state = RP_CU_STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -413,6 +413,7 @@ void llcp_lp_cu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
|
|||
/*
|
||||
* LLCP Local Channel Map Update
|
||||
*/
|
||||
void llcp_lp_chmu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *rx);
|
||||
void llcp_lp_chmu_init_proc(struct proc_ctx *ctx);
|
||||
void llcp_lp_chmu_run(struct ll_conn *conn, struct proc_ctx *ctx, void *param);
|
||||
|
||||
|
|
|
@ -170,6 +170,11 @@ void llcp_lr_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pdu *
|
|||
case PROC_TERMINATE:
|
||||
llcp_lp_comm_rx(conn, ctx, rx);
|
||||
break;
|
||||
#if defined(CONFIG_BT_CENTRAL)
|
||||
case PROC_CHAN_MAP_UPDATE:
|
||||
llcp_lp_chmu_rx(conn, ctx, rx);
|
||||
break;
|
||||
#endif /* CONFIG_BT_CENTRAL */
|
||||
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
|
||||
case PROC_DATA_LENGTH_UPDATE:
|
||||
llcp_lp_comm_rx(conn, ctx, rx);
|
||||
|
|
|
@ -767,8 +767,12 @@ void llcp_lp_pu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pd
|
|||
lp_pu_execute_fsm(conn, ctx, LP_PU_EVT_REJECT, pdu);
|
||||
break;
|
||||
default:
|
||||
/* Unknown opcode */
|
||||
LL_ASSERT(0);
|
||||
/* Invalid behaviour */
|
||||
/* Invalid PDU received so terminate connection */
|
||||
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
|
||||
llcp_lr_complete(conn);
|
||||
ctx->state = LP_PU_STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1145,8 +1149,12 @@ void llcp_rp_pu_rx(struct ll_conn *conn, struct proc_ctx *ctx, struct node_rx_pd
|
|||
break;
|
||||
#endif /* CONFIG_BT_PERIPHERAL */
|
||||
default:
|
||||
/* Unknown opcode */
|
||||
LL_ASSERT(0);
|
||||
/* Invalid behaviour */
|
||||
/* Invalid PDU received so terminate connection */
|
||||
conn->llcp_terminate.reason_final = BT_HCI_ERR_LMP_PDU_NOT_ALLOWED;
|
||||
llcp_rr_complete(conn);
|
||||
ctx->state = RP_PU_STATE_IDLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -569,10 +569,10 @@ static void rr_st_idle(struct ll_conn *conn, uint8_t evt, void *param)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rr_st_reject(struct ll_conn *conn, uint8_t evt, void *param)
|
||||
{
|
||||
/* TODO */
|
||||
LL_ASSERT(0);
|
||||
rr_act_reject(conn);
|
||||
}
|
||||
|
||||
static void rr_st_unsupported(struct ll_conn *conn, uint8_t evt, void *param)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue