From d8f0c1adf6a2e8caff17ea5646415f1b0cddbcc0 Mon Sep 17 00:00:00 2001 From: Erik Brockhoff Date: Tue, 5 Apr 2022 14:56:19 +0200 Subject: [PATCH] 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 --- .../controller/ll_sw/ull_llcp_chmu.c | 24 +- .../controller/ll_sw/ull_llcp_common.c | 69 ++-- .../controller/ll_sw/ull_llcp_conn_upd.c | 16 +- .../controller/ll_sw/ull_llcp_internal.h | 1 + .../controller/ll_sw/ull_llcp_local.c | 5 + .../bluetooth/controller/ll_sw/ull_llcp_phy.c | 16 +- .../controller/ll_sw/ull_llcp_remote.c | 4 +- .../bluetooth/controller/ctrl_chmu/src/main.c | 134 ++++++++ .../controller/ctrl_conn_update/src/main.c | 304 +++++++++++++++++- .../controller/ctrl_cte_req/src/main.c | 108 +++++++ .../ctrl_data_length_update/src/main.c | 190 ++++++++++- .../ctrl_feature_exchange/src/main.c | 104 +++++- .../controller/ctrl_le_ping/src/main.c | 144 +++++++++ .../controller/ctrl_phy_update/src/main.c | 108 +++++++ .../controller/ctrl_version/src/main.c | 155 ++++++++- 15 files changed, 1343 insertions(+), 39 deletions(-) diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_chmu.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_chmu.c index f0be644385a..10f22dad780 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_chmu.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_chmu.c @@ -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; } } diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c index f3e9b451567..0beda78a1fb 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_common.c @@ -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); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c index 5e871b518ae..851e02119ca 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_conn_upd.c @@ -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; } } diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h index f2c6351b20c..6f702fd4fd4 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_internal.h @@ -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); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c index 5de980ed96e..6998f975836 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_local.c @@ -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); diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c index c4b1d912507..15b89da4044 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_phy.c @@ -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; } } diff --git a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c index f5201e4ff40..243087d0970 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c +++ b/subsys/bluetooth/controller/ll_sw/ull_llcp_remote.c @@ -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) diff --git a/tests/bluetooth/controller/ctrl_chmu/src/main.c b/tests/bluetooth/controller/ctrl_chmu/src/main.c index 439bdc4c51c..66b8c7c8eef 100644 --- a/tests/bluetooth/controller/ctrl_chmu/src/main.c +++ b/tests/bluetooth/controller/ctrl_chmu/src/main.c @@ -132,6 +132,77 @@ void test_channel_map_update_central_loc(void) "Free CTX buffers %d", ctx_buffers_free()); } +void test_channel_map_update_central_invalid(void) +{ + uint8_t chm[5] = { 0x00, 0x04, 0x05, 0x06, 0x00 }; + uint8_t err; + struct node_tx *tx; + struct pdu_data_llctrl_unknown_rsp unknown_rsp = { + .type = PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND + }; + struct pdu_data_llctrl_chan_map_ind chmu_ind = { + .instant = 6, + .chm = { 0x00, 0x04, 0x05, 0x06, 0x00 }, + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + err = ull_cp_chan_map_update(&conn, chm); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CHAN_MAP_UPDATE_IND, &conn, &tx, &chmu_ind); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* There should NOT be a host notification */ + ut_rx_q_is_empty(); + + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Inject invalid 'RSP' */ + lt_tx(LL_UNKNOWN_RSP, &conn, &unknown_rsp); + + /* Done */ + event_done(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + void test_channel_map_update_periph_rem(void) { uint8_t chm[5] = { 0x00, 0x04, 0x05, 0x06, 0x00 }; @@ -203,6 +274,65 @@ void test_channel_map_update_periph_rem(void) "Free CTX buffers %d", ctx_buffers_free()); } +void test_channel_map_update_periph_invalid(void) +{ + struct pdu_data_llctrl_chan_map_ind chmu_ind = { + .instant = 6, + .chm = { 0x00, 0x04, 0x05, 0x06, 0x00 }, + }; + struct pdu_data_llctrl_unknown_rsp unknown_rsp = { + .type = PDU_DATA_LLCTRL_TYPE_UNUSED + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* RX */ + lt_tx(LL_CHAN_MAP_UPDATE_IND, &conn, &chmu_ind); + + /* Done */ + event_done(&conn); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + /* Prepare */ + event_prepare(&conn); + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Inject invalid 'RSP' */ + lt_tx(LL_UNKNOWN_RSP, &conn, &unknown_rsp); + + /* Done */ + event_done(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + void test_channel_map_update_periph_loc(void) { uint8_t err; @@ -226,8 +356,12 @@ void test_main(void) ztest_test_suite(chmu, ztest_unit_test_setup_teardown(test_channel_map_update_central_loc, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_channel_map_update_central_invalid, + setup, unit_test_noop), ztest_unit_test_setup_teardown(test_channel_map_update_periph_rem, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_channel_map_update_periph_invalid, + setup, unit_test_noop), ztest_unit_test_setup_teardown(test_channel_map_update_periph_loc, setup, unit_test_noop)); diff --git a/tests/bluetooth/controller/ctrl_conn_update/src/main.c b/tests/bluetooth/controller/ctrl_conn_update/src/main.c index 6990e6bf255..c21879a6684 100644 --- a/tests/bluetooth/controller/ctrl_conn_update/src/main.c +++ b/tests/bluetooth/controller/ctrl_conn_update/src/main.c @@ -336,7 +336,6 @@ void test_conn_update_central_loc_invalid_param_rsp(void) .error_code = BT_HCI_ERR_INVALID_LL_PARAM }; - /* Role */ test_set_role(&conn, BT_HCI_ROLE_CENTRAL); @@ -377,6 +376,72 @@ void test_conn_update_central_loc_invalid_param_rsp(void) "Free CTX buffers %d", ctx_buffers_free()); } +/* + * Central-initiated Connection Parameters Request procedure. + * Central requests change in LE connection parameters, peripheral’s Host accepts. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_C | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | LE Connection Update | | + * |-------------------------->| | + * | | LL_CONNECTION_PARAM_REQ | + * | |-------------------------->| + * | | | + * | | LL_REJECT_IND | + * | |<--------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~~ TERMINATE CONNECTION ~~~~~~~~~~~~~~~~~ + * | | | + * | | | + */ +void test_conn_update_central_loc_invalid_rsp(void) +{ + uint8_t err; + struct node_tx *tx; + struct pdu_data_llctrl_reject_ind reject_ind = { + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate a Connection Parameter Request Procedure */ + err = ull_cp_conn_update(&conn, INTVL_MIN, INTVL_MAX, LATENCY, TIMEOUT); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CONNECTION_PARAM_REQ, &conn, &tx, &conn_param_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + +} + /* * Central-initiated Connection Parameters Request procedure. * Central requests change in LE connection parameters, peripheral’s Host rejects. @@ -2147,6 +2212,239 @@ void test_conn_update_periph_rem_invalid_req(void) } +/* + * Central-initiated Connection Parameters Request procedure. + * Central requests change in LE connection parameters, peripheral’s Host accepts. + * + * +-----+ +-------+ +-----+ + * | UT | | LL_S | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | | LL_CONNECTION_PARAM_REQ | + * | |<--------------------------| + * | | | + * | LE Remote Connection | | + * | Parameter Request | | + * |<--------------------------| | + * | LE Remote Connection | | + * | Parameter Request | | + * | Reply | | + * |-------------------------->| | + * | | | + * | | LL_CONNECTION_PARAM_RSP | + * | |-------------------------->| + * | | | + * | | LL_ | + * | |<--------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~ TERMINATE CONNECTION ~~~~~~~~~~~~~~~~~~ + * | | | + * | | | + */ +void test_conn_update_periph_rem_invalid_ind(void) +{ + struct node_tx *tx; + struct node_rx_pdu *ntf; + struct pdu_data_llctrl_reject_ind reject_ind = { + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP, + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_unknown_rsp unknown_rsp = { + .type = PDU_DATA_LLCTRL_TYPE_CONN_PARAM_RSP + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_CONNECTION_PARAM_REQ, &conn, &conn_param_req); + + /* Done */ + event_done(&conn); + + /*******************/ + + /* There should be one host notification */ + ut_rx_pdu(LL_CONNECTION_PARAM_REQ, &ntf, &conn_param_req); + ut_rx_q_is_empty(); + + /* Release Ntf */ + ull_cp_release_ntf(ntf); + + /*******************/ + + ull_cp_conn_param_req_reply(&conn); + + /*******************/ + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CONNECTION_PARAM_RSP, &conn, &tx, &conn_param_rsp); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_CONNECTION_PARAM_REQ, &conn, &conn_param_req); + + /* Done */ + event_done(&conn); + + /*******************/ + + /* There should be one host notification */ + ut_rx_pdu(LL_CONNECTION_PARAM_REQ, &ntf, &conn_param_req); + ut_rx_q_is_empty(); + + /* Release Ntf */ + ull_cp_release_ntf(ntf); + + /*******************/ + + ull_cp_conn_param_req_reply(&conn); + + /*******************/ + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CONNECTION_PARAM_RSP, &conn, &tx, &conn_param_rsp); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_REJECT_EXT_IND, &conn, &reject_ext_ind); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_CONNECTION_PARAM_REQ, &conn, &conn_param_req); + + /* Done */ + event_done(&conn); + + /*******************/ + + /* There should be one host notification */ + ut_rx_pdu(LL_CONNECTION_PARAM_REQ, &ntf, &conn_param_req); + ut_rx_q_is_empty(); + + /* Release Ntf */ + ull_cp_release_ntf(ntf); + + /*******************/ + + ull_cp_conn_param_req_reply(&conn); + + /*******************/ + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CONNECTION_PARAM_RSP, &conn, &tx, &conn_param_rsp); + lt_rx_q_is_empty(&conn); + + /* Done */ + event_done(&conn); + + /* Prepare */ + event_prepare(&conn); + + /* Rx */ + lt_tx(LL_UNKNOWN_RSP, &conn, &unknown_rsp); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + /* * Central-initiated Connection Parameters Request procedure. * Central requests change in LE connection parameters, peripheral’s Host rejects. @@ -2792,6 +3090,8 @@ void test_main(void) setup, unit_test_noop), ztest_unit_test_setup_teardown(test_conn_update_central_loc_invalid_param_rsp, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_conn_update_central_loc_invalid_rsp, + setup, unit_test_noop), ztest_unit_test_setup_teardown(test_conn_update_central_loc_reject, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_conn_update_central_loc_remote_legacy, @@ -2833,6 +3133,8 @@ void test_main(void) setup, unit_test_noop), ztest_unit_test_setup_teardown(test_conn_update_periph_rem_invalid_req, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_conn_update_periph_rem_invalid_ind, + setup, unit_test_noop), ztest_unit_test_setup_teardown(test_conn_update_periph_rem_reject, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_conn_update_periph_rem_unsupp_feat, diff --git a/tests/bluetooth/controller/ctrl_cte_req/src/main.c b/tests/bluetooth/controller/ctrl_cte_req/src/main.c index ca852e39539..d05547e9cbc 100644 --- a/tests/bluetooth/controller/ctrl_cte_req/src/main.c +++ b/tests/bluetooth/controller/ctrl_cte_req/src/main.c @@ -114,6 +114,112 @@ void test_cte_req_central_local(void) "Free CTX buffers %d", ctx_buffers_free()); } +/* Tests of invalid rsp execution of CTE Request Procedure */ + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start initiation | | + * | CTE Request Proc. | | + * |--------------------------->| | + * | | | + * | | LL_LE_CTE_REQ | + * | |------------------>| + * | | | + * | | LL__RSP | + * | |<------------------| + * | | | + * ~~~~~~~~~~~~~~~~~ TERMINATE CONNECTION ~~~~~~~~~~~~~~ + * | | | + * | | | + * | | | + */ +void test_cte_req_central_local_invalid_rsp(void) +{ + uint8_t err; + struct node_tx *tx; + struct pdu_data_llctrl_unknown_rsp unknown_rsp = { + .type = PDU_DATA_LLCTRL_TYPE_CTE_REQ + }; + struct pdu_data_llctrl_reject_ind reject_ind = { + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_cte_req local_cte_req = { + .cte_type_req = BT_HCI_LE_AOA_CTE, + .min_cte_len_req = BT_HCI_LE_CTE_LEN_MIN, + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate an CTE Request Procedure */ + err = ull_cp_cte_req(&conn, local_cte_req.min_cte_len_req, local_cte_req.cte_type_req); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CTE_REQ, &conn, &tx, &local_cte_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_UNKNOWN_RSP, &conn, &unknown_rsp); + + /* Done */ + event_done(&conn); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + /* Initiate another CTE Request Procedure */ + err = ull_cp_cte_req(&conn, local_cte_req.min_cte_len_req, local_cte_req.cte_type_req); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_CTE_REQ, &conn, &tx, &local_cte_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* Done */ + event_done(&conn); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + /* +-----+ +-------+ +-----+ * | UT | | LL_A | | LT | * +-----+ +-------+ +-----+ @@ -1364,6 +1470,8 @@ void test_main(void) ztest_test_suite( cte_req, ztest_unit_test_setup_teardown(test_cte_req_central_local, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_cte_req_central_local_invalid_rsp, setup, + unit_test_noop), ztest_unit_test_setup_teardown(test_cte_req_peripheral_local, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_cte_req_central_remote, setup, unit_test_noop), diff --git a/tests/bluetooth/controller/ctrl_data_length_update/src/main.c b/tests/bluetooth/controller/ctrl_data_length_update/src/main.c index 51c7d64fbcb..fb595e6a68b 100644 --- a/tests/bluetooth/controller/ctrl_data_length_update/src/main.c +++ b/tests/bluetooth/controller/ctrl_data_length_update/src/main.c @@ -36,6 +36,7 @@ #include "ull_llcp.h" #include "ull_conn_internal.h" #include "ull_llcp_internal.h" +#include "ull_llcp_features.h" #include "helper_pdu.h" #include "helper_util.h" @@ -48,7 +49,7 @@ static void setup(void) test_setup(&conn); } -/*C +/* * Locally triggered Data Length Update procedure * * +-----+ +-------+ +-----+ @@ -128,6 +129,189 @@ void test_data_length_update_central_loc(void) conn.lll.event_counter); } +/* + * Locally triggered Data Length Update procedure + * + * +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | Data Length Update Proc. | | + * |--------------------------->| | + * | | (251,2120,211,1800) | + * | | LL_DATA_LENGTH_UPDATE_REQ | + * | |----------------------------->| + * | | | + * | | LL_UNKNOWN_RSP | + * | |<-----------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~~~~~~~ Unmask DLE support ~~~~~~~~~~~~~~~~~~~~ + * | | | + * | | | + */ +void test_data_length_update_central_loc_unknown_rsp(void) +{ + uint8_t err; + struct node_tx *tx; + struct pdu_data_llctrl_unknown_rsp unknown_rsp = { + .type = PDU_DATA_LLCTRL_TYPE_LENGTH_REQ + }; + struct pdu_data_llctrl_length_req local_length_req = { 251, 2120, 211, 1800 }; + + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + /* Init DLE data */ + ull_conn_default_tx_octets_set(251); + ull_conn_default_tx_time_set(2120); + ull_dle_init(&conn, PHY_1M); + + /* Confirm DLE is indicated as supported */ + zassert_equal(feature_dle(&conn), true, "DLE Feature masked out"); + + /* Initiate a Data Length Update Procedure */ + err = ull_cp_data_length_update(&conn, 211, 1800); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + event_prepare(&conn); + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_LENGTH_REQ, &conn, &tx, &local_length_req); + lt_rx_q_is_empty(&conn); + + /* TX Ack */ + event_tx_ack(&conn, tx); + + /* Rx */ + lt_tx(LL_UNKNOWN_RSP, &conn, &unknown_rsp); + + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Confirm DLE is no longer indicated as supported */ + zassert_equal(feature_dle(&conn), false, "DLE Feature not masked out"); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + +/* + * Locally triggered Data Length Update procedure + * + * +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | Data Length Update Proc. | | + * |--------------------------->| | + * | | (251,2120,211,1800) | + * | | LL_DATA_LENGTH_UPDATE_REQ | + * | |----------------------------->| + * | | | + * | | LL__RSP | + * | |<-----------------------------| + * | | | + * ~~~~~~~~~~~~~~~~~~~~ TERMINATE CONNECTION ~~~~~~~~~~~~~~~~~~~ + * | | | + * | | | + */ +void test_data_length_update_central_loc_invalid_rsp(void) +{ + uint8_t err; + struct node_tx *tx; + struct pdu_data_llctrl_reject_ind reject_ind = { + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_LENGTH_REQ, + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + + struct pdu_data_llctrl_length_req local_length_req = { 251, 2120, 211, 1800 }; + + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + /* Init DLE data */ + ull_conn_default_tx_octets_set(251); + ull_conn_default_tx_time_set(2120); + ull_dle_init(&conn, PHY_1M); + + /* Initiate a Data Length Update Procedure */ + err = ull_cp_data_length_update(&conn, 211, 1800); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + event_prepare(&conn); + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_LENGTH_REQ, &conn, &tx, &local_length_req); + lt_rx_q_is_empty(&conn); + + /* TX Ack */ + event_tx_ack(&conn, tx); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + /* Init DLE data */ + ull_conn_default_tx_octets_set(251); + ull_conn_default_tx_time_set(2120); + ull_dle_init(&conn, PHY_1M); + + /* Initiate another Data Length Update Procedure */ + err = ull_cp_data_length_update(&conn, 211, 1800); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + event_prepare(&conn); + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_LENGTH_REQ, &conn, &tx, &local_length_req); + lt_rx_q_is_empty(&conn); + + /* TX Ack */ + event_tx_ack(&conn, tx); + + /* Rx */ + lt_tx(LL_REJECT_EXT_IND, &conn, &reject_ext_ind); + + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + /* * Locally triggered Data Length Update procedure - with no update to eff and thus no ntf * @@ -648,6 +832,10 @@ void test_main(void) data_length_update_central, ztest_unit_test_setup_teardown(test_data_length_update_central_loc, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_data_length_update_central_loc_unknown_rsp, + setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_data_length_update_central_loc_invalid_rsp, + setup, unit_test_noop), ztest_unit_test_setup_teardown(test_data_length_update_central_loc_no_eff_change, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_data_length_update_central_loc_no_eff_change2, diff --git a/tests/bluetooth/controller/ctrl_feature_exchange/src/main.c b/tests/bluetooth/controller/ctrl_feature_exchange/src/main.c index 73a85ccc38f..0615bee027d 100644 --- a/tests/bluetooth/controller/ctrl_feature_exchange/src/main.c +++ b/tests/bluetooth/controller/ctrl_feature_exchange/src/main.c @@ -132,6 +132,106 @@ void test_feat_exchange_central_loc(void) "Free CTX buffers %d", ctx_buffers_free()); } +/* + * +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | Feature Exchange Proc. | | + * |--------------------------->| | + * | | | + * | | LL_FEATURE_REQ | + * | |------------------>| + * | | | + * | | LL__RSP | + * | |<------------------| + * | | | + * ~~~~~~~~~~~~~~~~ TERMINATE CONNECTION ~~~~~~~~~~~~~~ + * | | | + */ +void test_feat_exchange_central_loc_invalid_rsp(void) +{ + uint64_t err; + struct pdu_data_llctrl_feature_req local_feature_req; + struct pdu_data_llctrl_reject_ind reject_ind = { + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_FEATURE_REQ, + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct node_tx *tx; + + + sys_put_le64(DEFAULT_FEATURE, local_feature_req.features); + + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate a Feature Exchange Procedure */ + err = ull_cp_feature_exchange(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + event_prepare(&conn); + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_FEATURE_REQ, &conn, &tx, &local_feature_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate another Feature Exchange Procedure */ + err = ull_cp_feature_exchange(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + event_prepare(&conn); + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_FEATURE_REQ, &conn, &tx, &local_feature_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_EXT_IND, &conn, &reject_ext_ind); + + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + void test_feat_exchange_central_loc_2(void) { uint8_t err; @@ -156,7 +256,7 @@ void test_feat_exchange_central_loc_2(void) * | UT | | LL_A | | LT | * +-----+ +-------+ +-----+ * | | | - * | | LL_PERIPH_FEAT_XCHG | + * | | LL_PERIPH_FEAT_XCHG | * | |<------------------------| * | | | * | | LL_FEATURE_RSP | @@ -430,6 +530,8 @@ void test_main(void) ztest_test_suite(feat_exchange_central, ztest_unit_test_setup_teardown(test_feat_exchange_central_loc, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_feat_exchange_central_loc_invalid_rsp, + setup, unit_test_noop), ztest_unit_test_setup_teardown(test_feat_exchange_central_loc_2, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_feat_exchange_central_rem, setup, diff --git a/tests/bluetooth/controller/ctrl_le_ping/src/main.c b/tests/bluetooth/controller/ctrl_le_ping/src/main.c index 7f153815edf..779fd3e99c4 100644 --- a/tests/bluetooth/controller/ctrl_le_ping/src/main.c +++ b/tests/bluetooth/controller/ctrl_le_ping/src/main.c @@ -57,6 +57,15 @@ static void setup(void) * | | LL_LE_PING_RSP | * | |<------------------| * | | | + * | Start | | + * | LE Ping Proc. | | + * |--------------------------->| | + * | | | + * | | LL_LE_PING_REQ | + * | |------------------>| + * | | | + * | | LL_UNKNOWN_RSP | + * | |<------------------| * | | | */ void test_ping_central_loc(void) @@ -68,6 +77,10 @@ void test_ping_central_loc(void) struct pdu_data_llctrl_ping_rsp remote_ping_rsp = {}; + struct pdu_data_llctrl_unknown_rsp unknown_rsp = { + .type = PDU_DATA_LLCTRL_TYPE_PING_REQ + }; + /* Role */ test_set_role(&conn, BT_HCI_ROLE_CENTRAL); @@ -99,6 +112,135 @@ void test_ping_central_loc(void) zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), "Free CTX buffers %d", ctx_buffers_free()); + + /* Initiate another LE Ping Procedure */ + err = ull_cp_le_ping(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_LE_PING_REQ, &conn, &tx, &local_ping_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_UNKNOWN_RSP, &conn, &unknown_rsp); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + +} + +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | LE Ping Proc. | | + * |--------------------------->| | + * | | | + * | | LL_LE_PING_REQ | + * | |------------------>| + * | | | + * | | LL__RSP | + * | |<------------------| + * | | | + * ~~~~~~~~~~~~~~~~~ TERMINATE CONNECTION ~~~~~~~~~~~~~~ + * | | | + */ +void test_ping_central_loc_invalid_rsp(void) +{ + uint8_t err; + struct node_tx *tx; + + struct pdu_data_llctrl_reject_ind reject_ind = { + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_PING_REQ, + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + struct pdu_data_llctrl_ping_req local_ping_req = {}; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate an LE Ping Procedure */ + err = ull_cp_le_ping(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_LE_PING_REQ, &conn, &tx, &local_ping_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_EXT_IND, &conn, &reject_ext_ind); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + /* Initiate another LE Ping Procedure */ + err = ull_cp_le_ping(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_LE_PING_REQ, &conn, &tx, &local_ping_req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* Done */ + event_done(&conn); + + /* Release tx node */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should not be a host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + } /* +-----+ +-------+ +-----+ @@ -272,6 +414,8 @@ void test_main(void) ztest_test_suite(ping, ztest_unit_test_setup_teardown(test_ping_central_loc, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_ping_central_loc_invalid_rsp, setup, + unit_test_noop), ztest_unit_test_setup_teardown(test_ping_periph_loc, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_ping_central_rem, setup, diff --git a/tests/bluetooth/controller/ctrl_phy_update/src/main.c b/tests/bluetooth/controller/ctrl_phy_update/src/main.c index 0d16a1cfc02..6594cdd6aa5 100644 --- a/tests/bluetooth/controller/ctrl_phy_update/src/main.c +++ b/tests/bluetooth/controller/ctrl_phy_update/src/main.c @@ -219,6 +219,54 @@ void test_phy_update_central_loc(void) "Free CTX buffers %d", ctx_buffers_free()); } +void test_phy_update_central_loc_invalid(void) +{ + uint8_t err; + struct node_tx *tx; + struct pdu_data_llctrl_phy_req req = { .rx_phys = PHY_2M, .tx_phys = PHY_2M }; + + struct pdu_data_llctrl_reject_ind reject_ind = { }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate an PHY Update Procedure */ + err = ull_cp_phy_update(&conn, PHY_2M, PREFER_S8_CODING, PHY_2M, 1); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_PHY_REQ, &conn, &tx, &req); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* TX Ack */ + event_tx_ack(&conn, tx); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should be nohost notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + void test_phy_update_central_loc_unsupp_feat(void) { uint8_t err; @@ -567,6 +615,62 @@ void test_phy_update_periph_rem(void) "Free CTX buffers %d", ctx_buffers_free()); } +void test_phy_update_periph_rem_invalid(void) +{ + struct node_tx *tx; + struct pdu_data_llctrl_phy_req req = { .rx_phys = PHY_1M, .tx_phys = PHY_2M }; + struct pdu_data_llctrl_phy_req rsp = { .rx_phys = PHY_1M | PHY_2M | PHY_CODED, + .tx_phys = PHY_1M | PHY_2M | PHY_CODED }; + struct pdu_data_llctrl_reject_ind reject_ind = { }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_PERIPHERAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should NOT have a LL Control PDU */ + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_PHY_REQ, &conn, &req); + + /* Done */ + event_done(&conn); + + /* We received a REQ, so data tx should be paused */ + zassert_equal(conn.tx_q.pause_data, 1U, "Data tx is not paused"); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_PHY_RSP, &conn, &tx, &rsp); + lt_rx_q_is_empty(&conn); + + /* Inject invalid PDU */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* TX Ack */ + event_tx_ack(&conn, tx); + + /* Done */ + event_done(&conn); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + void test_phy_update_central_loc_collision(void) { uint8_t err; @@ -1044,12 +1148,16 @@ void test_main(void) { ztest_test_suite( phy, + ztest_unit_test_setup_teardown(test_phy_update_central_loc_invalid, setup, + unit_test_noop), ztest_unit_test_setup_teardown(test_phy_update_central_loc, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_phy_update_central_loc_unsupp_feat, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_phy_update_central_rem, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_phy_update_periph_loc, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_phy_update_periph_rem, setup, unit_test_noop), + ztest_unit_test_setup_teardown(test_phy_update_periph_rem_invalid, setup, + unit_test_noop), ztest_unit_test_setup_teardown(test_phy_update_central_loc_collision, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_phy_update_central_rem_collision, setup, diff --git a/tests/bluetooth/controller/ctrl_version/src/main.c b/tests/bluetooth/controller/ctrl_version/src/main.c index f767e9fc4f2..3ebec73ff08 100644 --- a/tests/bluetooth/controller/ctrl_version/src/main.c +++ b/tests/bluetooth/controller/ctrl_version/src/main.c @@ -111,6 +111,156 @@ void test_version_exchange_central_loc(void) "Free CTX buffers %d", ctx_buffers_free()); } +/* +-----+ +-------+ +-----+ + * | UT | | LL_A | | LT | + * +-----+ +-------+ +-----+ + * | | | + * | Start | | + * | Version Exchange Proc. | | + * |--------------------------->| | + * | | | + * | | LL_VERSION_IND | + * | |------------------>| + * | | | + * | | LL__RSP | + * | |<------------------| + * | | | + * ~~~~~~~~~~~~~~~~~~~ TERMINATE CONN ~~~~~~~~~~~~~~~~~~ + * | | | + */ +void test_version_exchange_central_loc_invalid_rsp(void) +{ + uint8_t err; + struct node_tx *tx; + + struct pdu_data_llctrl_version_ind local_version_ind = { + .version_number = LL_VERSION_NUMBER, + .company_id = CONFIG_BT_CTLR_COMPANY_ID, + .sub_version_number = CONFIG_BT_CTLR_SUBVERSION_NUMBER, + }; + + struct pdu_data_llctrl_unknown_rsp unknown_rsp = { + .type = PDU_DATA_LLCTRL_TYPE_VERSION_IND + }; + + struct pdu_data_llctrl_reject_ext_ind reject_ext_ind = { + .reject_opcode = PDU_DATA_LLCTRL_TYPE_VERSION_IND, + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + + struct pdu_data_llctrl_reject_ind reject_ind = { + .error_code = BT_HCI_ERR_LL_PROC_COLLISION + }; + + /* Role */ + test_set_role(&conn, BT_HCI_ROLE_CENTRAL); + + /* Connect */ + ull_cp_state_set(&conn, ULL_CP_CONNECTED); + + /* Initiate a Version Exchange Procedure */ + err = ull_cp_version_exchange(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_VERSION_IND, &conn, &tx, &local_version_ind); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_UNKNOWN_RSP, &conn, &unknown_rsp); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + /* Cheat, to allow second VEX */ + conn.llcp.vex.sent = 0; + + /* Initiate another Version Exchange Procedure */ + err = ull_cp_version_exchange(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_VERSION_IND, &conn, &tx, &local_version_ind); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_EXT_IND, &conn, &reject_ext_ind); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* Clear termination flag for subsequent test cycle */ + conn.llcp_terminate.reason_final = 0; + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); + + /* Cheat, to allow second VEX */ + conn.llcp.vex.sent = 0; + + /* Initiate yet another Version Exchange Procedure */ + err = ull_cp_version_exchange(&conn); + zassert_equal(err, BT_HCI_ERR_SUCCESS, NULL); + + /* Prepare */ + event_prepare(&conn); + + /* Tx Queue should have one LL Control PDU */ + lt_rx(LL_VERSION_IND, &conn, &tx, &local_version_ind); + lt_rx_q_is_empty(&conn); + + /* Rx */ + lt_tx(LL_REJECT_IND, &conn, &reject_ind); + + /* Done */ + event_done(&conn); + + /* Release Tx */ + ull_cp_release_tx(&conn, tx); + + /* Termination 'triggered' */ + zassert_equal(conn.llcp_terminate.reason_final, BT_HCI_ERR_LMP_PDU_NOT_ALLOWED, + "Terminate reason %d", conn.llcp_terminate.reason_final); + + /* There should be no host notifications */ + ut_rx_q_is_empty(); + + zassert_equal(ctx_buffers_free(), test_ctx_buffers_cnt(), + "Free CTX buffers %d", ctx_buffers_free()); +} + void test_version_exchange_central_loc_2(void) { uint8_t err; @@ -367,7 +517,10 @@ void test_main(void) ztest_unit_test_setup_teardown(test_version_exchange_central_rem_2, setup, unit_test_noop), ztest_unit_test_setup_teardown(test_version_exchange_central_loc_twice, - setup, unit_test_noop)); + setup, unit_test_noop), + ztest_unit_test_setup_teardown( + test_version_exchange_central_loc_invalid_rsp, setup, + unit_test_noop)); ztest_run_test_suite(version_exchange); }