diff --git a/srsenb/hdr/stack/rrc/rrc_mobility.h b/srsenb/hdr/stack/rrc/rrc_mobility.h index 24bb5d270..3efd607a4 100644 --- a/srsenb/hdr/stack/rrc/rrc_mobility.h +++ b/srsenb/hdr/stack/rrc/rrc_mobility.h @@ -43,7 +43,8 @@ public: // S1-Handover bool start_s1_tenb_ho(const asn1::s1ap::ho_request_s& msg, - const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container); + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + asn1::s1ap::cause_c& cause); private: // helper methods @@ -70,6 +71,7 @@ private: // vars asn1::rrc::meas_cfg_s current_meas_cfg; asn1::rrc::rrc_conn_recfg_complete_s pending_recfg_complete; + asn1::s1ap::cause_c failure_cause; // events struct ho_meas_report_ev { @@ -147,7 +149,7 @@ private: void handle_crnti_ce(intraenb_ho_st& s, const user_crnti_upd_ev& ev); void handle_recfg_complete(intraenb_ho_st& s, const recfg_complete_ev& ev); void handle_ho_requested(idle_st& s, const ho_req_rx_ev& ho_req); - void handle_ho_failure(s1_target_ho_st& s, const ho_failure_ev& ev); + void handle_ho_failure(const ho_failure_ev& ev); void handle_status_transfer(s1_target_ho_st& s, const status_transfer_ev& ev); void defer_recfg_complete(s1_target_ho_st& s, const recfg_complete_ev& ev); void handle_recfg_complete(wait_recfg_comp& s, const recfg_complete_ev& ev); @@ -175,7 +177,7 @@ protected: row< intraenb_ho_st, idle_st, recfg_complete_ev, &fsm::handle_recfg_complete >, // +----------------+-------------------+---------------------+----------------------------+-------------------------+ row< s1_target_ho_st, wait_recfg_comp, status_transfer_ev, &fsm::handle_status_transfer >, - row< s1_target_ho_st, idle_st, ho_failure_ev, &fsm::handle_ho_failure >, + to_state< idle_st, ho_failure_ev, &fsm::handle_ho_failure >, upd< s1_target_ho_st, recfg_complete_ev, &fsm::defer_recfg_complete >, row< wait_recfg_comp, idle_st, recfg_complete_ev, &fsm::handle_recfg_complete > // +----------------+-------------------+---------------------+----------------------------+-------------------------+ diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 97adf51c0..aff8ab4d7 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -180,8 +180,8 @@ uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& // rrc_ptr->logger.error("Failed to setup e-RABs for rnti=0x%x", ); // } - if (not ue_ptr->mobility_handler->start_s1_tenb_ho(msg, container)) { - rem_user_thread(rnti); + if (not ue_ptr->mobility_handler->start_s1_tenb_ho(msg, container, cause)) { + rem_user(rnti); return SRSRAN_INVALID_RNTI; } return rnti; @@ -435,10 +435,15 @@ void rrc::ue::rrc_mobility::handle_ho_preparation_complete(bool bool rrc::ue::rrc_mobility::start_s1_tenb_ho( const asn1::s1ap::ho_request_s& msg, - const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container) + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + asn1::s1ap::cause_c& cause) { trigger(ho_req_rx_ev{&msg, &container}); - return is_in_state(); + if (not is_in_state()) { + cause = failure_cause; + return false; + } + return true; } /** @@ -674,7 +679,6 @@ void rrc::ue::rrc_mobility::s1_source_ho_st::handle_ho_cancel(const ho_cancel_ev void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& ho_req) { const auto& rrc_container = ho_req.transparent_container->rrc_container; - asn1::s1ap::cause_c failure_cause; std::vector not_admitted_erabs; auto& fwd_tunnels = get_state()->pending_tunnels; fwd_tunnels.clear(); @@ -684,15 +688,17 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& asn1::rrc::ho_prep_info_s hoprep; if (hoprep.unpack(bref) != asn1::SRSASN_SUCCESS) { rrc_enb->logger.error("Failed to decode HandoverPreparationinformation in S1AP SourceENBToTargetENBContainer"); - failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; - trigger(ho_failure_ev{failure_cause}); + asn1::s1ap::cause_c cause; + cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; + trigger(ho_failure_ev{cause}); return; } if (hoprep.crit_exts.type().value != c1_or_crit_ext_opts::c1 or hoprep.crit_exts.c1().type().value != ho_prep_info_s::crit_exts_c_::c1_c_::types_opts::ho_prep_info_r8) { rrc_enb->logger.error("Only release 8 supported"); - failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::semantic_error; - trigger(ho_failure_ev{failure_cause}); + asn1::s1ap::cause_c cause; + cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::semantic_error; + trigger(ho_failure_ev{cause}); return; } rrc_enb->log_rrc_message("HandoverPreparation", direction_t::fromS1AP, rrc_container, hoprep, "HandoverPreparation"); @@ -727,16 +733,17 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& srsran::unique_byte_buffer_t ho_cmd_pdu = srsran::make_byte_buffer(); if (ho_cmd_pdu == nullptr) { logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); - failure_cause.set_radio_network().value = - asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell; - trigger(ho_failure_ev{failure_cause}); + asn1::s1ap::cause_c cause; + cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell; + trigger(ho_failure_ev{cause}); return; } asn1::bit_ref bref2{ho_cmd_pdu->msg, ho_cmd_pdu->get_tailroom()}; if (dl_dcch_msg.pack(bref2) != asn1::SRSASN_SUCCESS) { logger.error("Failed to pack HandoverCommand"); - failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; - trigger(ho_failure_ev{failure_cause}); + asn1::s1ap::cause_c cause; + cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; + trigger(ho_failure_ev{cause}); return; } ho_cmd_pdu->N_bytes = bref2.distance_bytes(); @@ -749,8 +756,9 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& bref2 = {ho_cmd_pdu->msg, ho_cmd_pdu->get_tailroom()}; if (ho_cmd.pack(bref2) != asn1::SRSASN_SUCCESS) { logger.error("Failed to pack HandoverCommand"); - failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; - trigger(ho_failure_ev{failure_cause}); + asn1::s1ap::cause_c cause; + cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; + trigger(ho_failure_ev{cause}); return; } ho_cmd_pdu->N_bytes = bref2.distance_bytes(); @@ -809,6 +817,17 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& } } + /// If the target eNB does not admit at least one non-GBR E-RAB, ..., it shall send the HANDOVER FAILURE message ... + if (admitted_erabs.empty()) { + asn1::s1ap::cause_c cause; + cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::unspecified; + if (not not_admitted_erabs.empty()) { + cause = not_admitted_erabs[0].cause; + } + trigger(ho_failure_ev{cause}); + return; + } + // send S1AP HandoverRequestAcknowledge if (not rrc_enb->s1ap->send_ho_req_ack(*ho_req.ho_req_msg, rrc_ue->rnti, @@ -816,16 +835,17 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& std::move(ho_cmd_pdu), admitted_erabs, not_admitted_erabs)) { - failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; - trigger(ho_failure_ev{failure_cause}); + asn1::s1ap::cause_c cause; + cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; + trigger(ho_failure_ev{cause}); return; } } -void rrc::ue::rrc_mobility::handle_ho_failure(s1_target_ho_st& s, const ho_failure_ev& ev) +void rrc::ue::rrc_mobility::handle_ho_failure(const ho_failure_ev& ev) { // Store Handover failure cause - s.failure_cause = ev.cause; + failure_cause = ev.cause; } void rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& ho_prep, diff --git a/srsenb/test/upper/rrc_mobility_test.cc b/srsenb/test/upper/rrc_mobility_test.cc index 44389fe7b..e1df237c0 100644 --- a/srsenb/test/upper/rrc_mobility_test.cc +++ b/srsenb/test/upper/rrc_mobility_test.cc @@ -29,6 +29,7 @@ struct mobility_test_params { duplicate_crnti_ce, recover, wrong_target_cell, + wrong_qos, } fail_at; const char* to_string() { @@ -47,6 +48,8 @@ struct mobility_test_params { return "duplicate CRNTI CE"; case test_event::wrong_target_cell: return "wrong target cell"; + case test_event::wrong_qos: + return "invalid QoS"; default: return "none"; } @@ -273,11 +276,13 @@ int test_s1ap_tenb_mobility(mobility_test_params test_params) auto& erab = ho_req.protocol_ies.erab_to_be_setup_list_ho_req.value[0].value.erab_to_be_setup_item_ho_req(); erab.erab_id = 5; erab.erab_level_qos_params.qci = 9; + if (test_params.fail_at == mobility_test_params::test_event::wrong_qos) { + erab.erab_level_qos_params.qci = 10; + } asn1::s1ap::sourceenb_to_targetenb_transparent_container_s container; + container.target_cell_id.cell_id.from_number(0x19C02); if (test_params.fail_at == mobility_test_params::test_event::wrong_target_cell) { container.target_cell_id.cell_id.from_number(0x19C03); - } else { - container.target_cell_id.cell_id.from_number(0x19C02); } uint8_t ho_prep_container[] = { 0x0a, 0x10, 0x0b, 0x81, 0x80, 0x00, 0x01, 0x80, 0x00, 0xf3, 0x02, 0x08, 0x00, 0x00, 0x15, 0x80, 0x00, 0x14, @@ -300,6 +305,15 @@ int test_s1ap_tenb_mobility(mobility_test_params test_params) int rnti = tester.rrc.start_ho_ue_resource_alloc(ho_req, container, cause); if (test_params.fail_at == mobility_test_params::test_event::wrong_target_cell) { TESTASSERT(rnti == SRSRAN_INVALID_RNTI); + TESTASSERT(cause.type().value == asn1::s1ap::cause_c::types_opts::radio_network); + TESTASSERT(cause.radio_network().value == asn1::s1ap::cause_radio_network_opts::ho_target_not_allowed); + TESTASSERT(tester.rrc.get_nof_users() == 0); + return SRSRAN_SUCCESS; + } + if (test_params.fail_at == mobility_test_params::test_event::wrong_qos) { + TESTASSERT(rnti == SRSRAN_INVALID_RNTI); + TESTASSERT(cause.type().value == asn1::s1ap::cause_c::types_opts::radio_network); + TESTASSERT(cause.radio_network().value == asn1::s1ap::cause_radio_network_opts::invalid_qos_combination); TESTASSERT(tester.rrc.get_nof_users() == 0); return SRSRAN_SUCCESS; } @@ -545,6 +559,7 @@ int main(int argc, char** argv) TESTASSERT(test_s1ap_mobility(*spy, mobility_test_params{event::success}) == 0); TESTASSERT(test_s1ap_tenb_mobility(mobility_test_params{event::wrong_target_cell}) == 0); + TESTASSERT(test_s1ap_tenb_mobility(mobility_test_params{event::wrong_qos}) == 0); TESTASSERT(test_s1ap_tenb_mobility(mobility_test_params{event::success}) == 0); // intraeNB Handover diff --git a/srsenb/test/upper/test_helpers.h b/srsenb/test/upper/test_helpers.h index 30cb34b82..23f7b683c 100644 --- a/srsenb/test/upper/test_helpers.h +++ b/srsenb/test/upper/test_helpers.h @@ -77,6 +77,7 @@ public: uint16_t rnti; srsran::unique_byte_buffer_t ho_cmd_pdu; std::vector admitted_bearers; + std::vector not_admitted_bearers; } last_ho_req_ack; bool send_ho_required(uint16_t rnti, @@ -103,6 +104,7 @@ public: last_ho_req_ack.rnti = rnti; last_ho_req_ack.ho_cmd_pdu = std::move(ho_cmd); last_ho_req_ack.admitted_bearers.assign(admitted_bearers.begin(), admitted_bearers.end()); + last_ho_req_ack.not_admitted_bearers.assign(not_admitted_bearers.begin(), not_admitted_bearers.end()); return true; } void ue_erab_setup_complete(uint16_t rnti, const asn1::s1ap::erab_setup_resp_s& res) override