diff --git a/lib/include/srsran/asn1/s1ap_utils.h b/lib/include/srsran/asn1/s1ap_utils.h index 6627f7852..d9e3376e4 100644 --- a/lib/include/srsran/asn1/s1ap_utils.h +++ b/lib/include/srsran/asn1/s1ap_utils.h @@ -39,6 +39,8 @@ struct erab_level_qos_params_s; struct ho_cmd_s; struct erab_admitted_item_s; struct erab_to_be_modified_item_bearer_mod_req_s; +struct cause_c; +struct erab_item_s; template struct protocol_ie_single_container_s; diff --git a/lib/include/srsran/interfaces/enb_rrc_interfaces.h b/lib/include/srsran/interfaces/enb_rrc_interfaces.h index cc0260f0b..2f80dd75c 100644 --- a/lib/include/srsran/interfaces/enb_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/enb_rrc_interfaces.h @@ -52,7 +52,8 @@ public: srsran::unique_byte_buffer_t container) = 0; virtual uint16_t start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg, - const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container) = 0; + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + asn1::s1ap::cause_c& failure_cause) = 0; virtual void set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs) = 0; }; diff --git a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h index 211d92cb3..08e4caa2f 100644 --- a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h +++ b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h @@ -86,25 +86,23 @@ public: */ virtual bool send_enb_status_transfer_proc(uint16_t rnti, std::vector& bearer_status_list) = 0; - /* Acknowledge Handover Request message back to MME. - * This message signals the completion of the HandoverPreparation from the TeNB point of view. */ - virtual bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, - uint16_t rnti, - uint32_t enb_cc_idx, - srsran::unique_byte_buffer_t ho_cmd, - srsran::span admitted_bearers) = 0; - - /** - * Notify MME that Handover is complete - */ - virtual void send_ho_notify(uint16_t rnti, uint64_t target_eci) = 0; - /** * Cancel on-going S1 Handover. MME should release UE context in target eNB * SeNB --> MME */ virtual void send_ho_cancel(uint16_t rnti) = 0; + /************************* + * Target eNB Handover + ************************/ + virtual bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, + uint16_t rnti, + uint32_t enb_cc_idx, + srsran::unique_byte_buffer_t ho_cmd, + srsran::span admitted_bearers, + srsran::const_span not_admitted_bearers) = 0; + virtual void send_ho_notify(uint16_t rnti, uint64_t target_eci) = 0; + /** * Called during release of a subset of eNB E-RABs. Send E-RAB RELEASE INDICATION to MME. * SeNB --> MME diff --git a/lib/src/asn1/s1ap.cc b/lib/src/asn1/s1ap.cc index fc0275c19..752dfa9b3 100644 --- a/lib/src/asn1/s1ap.cc +++ b/lib/src/asn1/s1ap.cc @@ -14493,6 +14493,7 @@ std::string erab_modify_resp_ies_o::value_c::types_opts::to_string() const } template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::s1ap::protocol_ie_single_container_s; erab_modify_resp_ies_container::erab_modify_resp_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index 56549acc4..16b86e066 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -101,7 +101,8 @@ public: srsran::unique_byte_buffer_t rrc_container) override; uint16_t start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg, - const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container) override; + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + asn1::s1ap::cause_c& failure_cause) override; void set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs) override; // rrc_interface_pdcp diff --git a/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h b/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h index 1e4d833b4..1cffb9a6a 100644 --- a/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h +++ b/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h @@ -91,7 +91,7 @@ public: const asn1::unbounded_octstring* nas_pdu); // Methods to apply bearer updates - void add_gtpu_bearer(uint32_t erab_id); + int add_gtpu_bearer(uint32_t erab_id); srsran::expected add_gtpu_bearer(uint32_t erab_id, uint32_t teid_out, uint32_t addr, diff --git a/srsenb/hdr/stack/rrc/rrc_mobility.h b/srsenb/hdr/stack/rrc/rrc_mobility.h index e18976a03..24bb5d270 100644 --- a/srsenb/hdr/stack/rrc/rrc_mobility.h +++ b/srsenb/hdr/stack/rrc/rrc_mobility.h @@ -45,10 +45,6 @@ public: bool start_s1_tenb_ho(const asn1::s1ap::ho_request_s& msg, const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container); - static uint16_t - start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg, - const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container); - private: // helper methods bool update_ue_var_meas_cfg(uint32_t src_earfcn, @@ -63,7 +59,9 @@ private: const enb_cell_common& target_cell, uint32_t src_dl_earfcn, uint32_t src_pci); - bool apply_ho_prep_cfg(const asn1::rrc::ho_prep_info_r8_ies_s& ho_prep, const asn1::s1ap::ho_request_s& ho_req_msg); + void apply_ho_prep_cfg(const asn1::rrc::ho_prep_info_r8_ies_s& ho_prep, + const asn1::s1ap::ho_request_s& ho_req_msg, + std::vector& erabs_failed_to_setup); rrc::ue* rrc_ue = nullptr; rrc* rrc_enb = nullptr; @@ -82,9 +80,12 @@ private: const asn1::s1ap::ho_request_s* ho_req_msg; const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s* transparent_container; }; - using unsuccessful_outcome_ev = std::false_type; - using recfg_complete_ev = asn1::rrc::rrc_conn_recfg_complete_s; - using status_transfer_ev = asn1::s1ap::bearers_subject_to_status_transfer_list_l; + struct ho_failure_ev { + asn1::s1ap::cause_c cause; + ho_failure_ev(const asn1::s1ap::cause_c& cause_) : cause(cause_) {} + }; + using recfg_complete_ev = asn1::rrc::rrc_conn_recfg_complete_s; + using status_transfer_ev = asn1::s1ap::bearers_subject_to_status_transfer_list_l; // states struct idle_st {}; @@ -96,6 +97,7 @@ private: void enter(rrc_mobility* f, const ho_meas_report_ev& meas_report); }; struct s1_target_ho_st { + asn1::s1ap::cause_c failure_cause; std::vector pending_tunnels; }; struct wait_recfg_comp {}; @@ -145,6 +147,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_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); @@ -172,6 +175,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 >, 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/hdr/stack/upper/s1ap.h b/srsenb/hdr/stack/upper/s1ap.h index 901a588df..3bf750491 100644 --- a/srsenb/hdr/stack/upper/s1ap.h +++ b/srsenb/hdr/stack/upper/s1ap.h @@ -84,13 +84,12 @@ public: srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container) override; bool send_enb_status_transfer_proc(uint16_t rnti, std::vector& bearer_status_list) override; - bool send_ho_failure(uint32_t mme_ue_s1ap_id); bool send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, uint16_t rnti, uint32_t enb_cc_idx, srsran::unique_byte_buffer_t ho_cmd, - srsran::span admitted_bearers) override; - void send_ho_notify(uint16_t rnti, uint64_t target_eci) override; + srsran::span admitted_bearers, + srsran::const_span not_admitted_bearers) override; void send_ho_cancel(uint16_t rnti) override; bool release_erabs(uint16_t rnti, const std::vector& erabs_successfully_released) override; bool send_error_indication(const asn1::s1ap::cause_c& cause, @@ -98,6 +97,12 @@ public: srsran::optional mme_ue_s1ap_id = {}); bool send_ue_cap_info_indication(uint16_t rnti, srsran::unique_byte_buffer_t ue_radio_cap) override; + /// Target eNB Handover + /// Section 8.4.2 - Handover Resource Allocation + void send_ho_failure(uint32_t mme_ue_s1ap_id, const asn1::s1ap::cause_c& cause); + /// Section 8.4.3 - Handover Notification + void send_ho_notify(uint16_t rnti, uint64_t target_eci) override; + // Stack interface bool handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); diff --git a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc index 8bdbdcfad..1809df990 100644 --- a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc +++ b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc @@ -222,8 +222,8 @@ int bearer_cfg_handler::add_erab(uint8_t logger->error("QCI=%d not configured", qos.qci); return SRSRAN_ERROR; } - if (lcid < 3 or lcid > 10) { - logger->error("DRB logical channel ids must be within 3 and 10"); + if (not srsran::is_lte_drb(lcid)) { + logger->error("E-RAB=%d logical channel id=%d is invalid", erab_id, lcid); return SRSRAN_ERROR; } const rrc_cfg_qci_t& qci_cfg = qci_it->second; @@ -306,7 +306,7 @@ bool bearer_cfg_handler::modify_erab(uint8_t return true; } -void bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id) +int bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id) { auto it = erabs.find(erab_id); if (it != erabs.end()) { @@ -314,10 +314,11 @@ void bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id) add_gtpu_bearer(erab_id, it->second.teid_out, it->second.address.to_number(), nullptr); if (teidin.has_value()) { it->second.teid_in = teidin.value(); - return; + return SRSRAN_SUCCESS; } } logger->error("Adding erab_id=%d to GTPU", erab_id); + return SRSRAN_ERROR; } srsran::expected bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id, diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 9ba7cca19..97adf51c0 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -138,7 +138,8 @@ std::string to_string(const cells_to_add_mod_s& obj) * @return rnti of created ue */ uint16_t rrc::start_ho_ue_resource_alloc(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) { // TODO: Decision Making on whether the same QoS of the source eNB can be provided by target eNB @@ -147,6 +148,7 @@ uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& const enb_cell_common* target_cell = cell_common_list->get_cell_id(rrc_details::eci_to_cellid(target_eci)); if (target_cell == nullptr) { logger.error("The S1-handover target cell_id=0x%x does not exist", rrc_details::eci_to_cellid(target_eci)); + cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::ho_target_not_allowed; return SRSRAN_INVALID_RNTI; } @@ -162,6 +164,7 @@ uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& uint16_t rnti = mac->reserve_new_crnti(ue_cfg); if (rnti == SRSRAN_INVALID_RNTI) { logger.error("Failed to allocate C-RNTI resources"); + cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::radio_res_not_available; return SRSRAN_INVALID_RNTI; } @@ -177,7 +180,6 @@ 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", ); // } - // TODO: KeNB derivations if (not ue_ptr->mobility_handler->start_s1_tenb_ho(msg, container)) { rem_user_thread(rnti); return SRSRAN_INVALID_RNTI; @@ -671,29 +673,33 @@ 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; + 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(); /* TS 36.331 10.2.2. - Decode HandoverPreparationInformation */ asn1::cbit_ref bref{rrc_container.data(), rrc_container.size()}; 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"); - trigger(srsran::failure_ev{}); + failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; + trigger(ho_failure_ev{failure_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"); - trigger(srsran::failure_ev{}); + failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::semantic_error; + trigger(ho_failure_ev{failure_cause}); return; } rrc_enb->log_rrc_message("HandoverPreparation", direction_t::fromS1AP, rrc_container, hoprep, "HandoverPreparation"); /* Setup UE current state in TeNB based on HandoverPreparation message */ const ho_prep_info_r8_ies_s& hoprep_r8 = hoprep.crit_exts.c1().ho_prep_info_r8(); - if (not apply_ho_prep_cfg(hoprep_r8, *ho_req.ho_req_msg)) { - return; - } + apply_ho_prep_cfg(hoprep_r8, *ho_req.ho_req_msg, not_admitted_erabs); /* Prepare Handover Request Acknowledgment - Handover Command */ dl_dcch_msg_s dl_dcch_msg; @@ -721,13 +727,16 @@ 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__); - trigger(srsran::failure_ev{}); + 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}); 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"); - trigger(srsran::failure_ev{}); + failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; + trigger(ho_failure_ev{failure_cause}); return; } ho_cmd_pdu->N_bytes = bref2.distance_bytes(); @@ -740,7 +749,8 @@ 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"); - trigger(srsran::failure_ev{}); + failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; + trigger(ho_failure_ev{failure_cause}); return; } ho_cmd_pdu->N_bytes = bref2.distance_bytes(); @@ -756,8 +766,6 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& // Set admitted E-RABs std::vector admitted_erabs; - auto& fwd_tunnels = get_state()->pending_tunnels; - fwd_tunnels.clear(); for (const auto& erab : rrc_ue->bearer_list.get_erabs()) { admitted_erabs.emplace_back(); asn1::s1ap::erab_admitted_item_s& admitted_erab = admitted_erabs.back(); @@ -766,8 +774,8 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& // Establish GTPU Forwarding Paths if (ho_req.transparent_container->erab_info_list_present) { - auto& lst = ho_req.transparent_container->erab_info_list; - auto it = std::find_if( + const auto& lst = ho_req.transparent_container->erab_info_list; + const auto* it = std::find_if( lst.begin(), lst.end(), [&erab](const asn1::s1ap::protocol_ie_single_container_s& fwd_erab) { @@ -776,7 +784,7 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& if (it == lst.end()) { continue; } - auto& fwd_erab = it->value.erab_info_list_item(); + const auto& fwd_erab = it->value.erab_info_list_item(); if (fwd_erab.dl_forwarding_present and fwd_erab.dl_forwarding.value == asn1::s1ap::dl_forwarding_opts::dl_forwarding_proposed) { @@ -787,9 +795,13 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& srsran::expected dl_teid_in = rrc_ue->bearer_list.add_gtpu_bearer( erab.second.id, erab.second.teid_out, erab.second.address.to_number(), &props); if (not dl_teid_in.has_value()) { - logger.error("Failed to allocate GTPU TEID"); - trigger(srsran::failure_ev{}); - return; + logger.error("Failed to allocate GTPU TEID for E-RAB id=%d", fwd_erab.erab_id); + not_admitted_erabs.emplace_back(); + not_admitted_erabs.back().erab_id = erab.second.id; + not_admitted_erabs.back().cause.set_radio_network().value = + asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell; + admitted_erabs.pop_back(); + continue; } fwd_tunnels.push_back(dl_teid_in.value()); srsran::uint32_to_uint8(dl_teid_in.value(), admitted_erabs.back().dl_g_tp_teid.data()); @@ -802,42 +814,60 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& rrc_ue->rnti, rrc_ue->ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, std::move(ho_cmd_pdu), - admitted_erabs)) { - trigger(srsran::failure_ev{}); + admitted_erabs, + not_admitted_erabs)) { + failure_cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::transfer_syntax_error; + trigger(ho_failure_ev{failure_cause}); return; } } -bool rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& ho_prep, - const asn1::s1ap::ho_request_s& ho_req_msg) +void rrc::ue::rrc_mobility::handle_ho_failure(s1_target_ho_st& s, const ho_failure_ev& ev) +{ + // Store Handover failure cause + s.failure_cause = ev.cause; +} + +void rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& ho_prep, + const asn1::s1ap::ho_request_s& ho_req_msg, + std::vector& erabs_failed_to_setup) { const ue_cell_ded* target_cell = rrc_ue->ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX); const cell_cfg_t& target_cell_cfg = target_cell->cell_common->cell_cfg; // Establish ERABs/DRBs for (const auto& erab_item : ho_req_msg.protocol_ies.erab_to_be_setup_list_ho_req.value) { - auto& erab = erab_item.value.erab_to_be_setup_item_ho_req(); + const auto& erab = erab_item.value.erab_to_be_setup_item_ho_req(); if (erab.ext) { get_logger().warning("Not handling E-RABToBeSetupList extensions"); } if (erab.transport_layer_address.length() > 32) { get_logger().error("IPv6 addresses not currently supported"); - trigger(srsran::failure_ev{}); - return false; - } - - if (not erab.ie_exts_present or not erab.ie_exts.data_forwarding_not_possible_present or - erab.ie_exts.data_forwarding_not_possible.ext.value != - asn1::s1ap::data_forwarding_not_possible_opts::data_forwarding_not_possible) { - get_logger().warning("Data Forwarding of E-RABs not supported"); + erabs_failed_to_setup.emplace_back(); + erabs_failed_to_setup.back().erab_id = erab.erab_id; + erabs_failed_to_setup.back().cause.set_transport().value = asn1::s1ap::cause_transport_opts::unspecified; + continue; } // Create E-RAB and associated main GTPU tunnel - uint32_t teid_out; + uint32_t teid_out = 0; srsran::uint8_to_uint32(erab.gtp_teid.data(), &teid_out); - rrc_ue->bearer_list.add_erab( - erab.erab_id, erab.erab_level_qos_params, erab.transport_layer_address, teid_out, nullptr); - rrc_ue->bearer_list.add_gtpu_bearer(erab.erab_id); + if (rrc_ue->bearer_list.add_erab( + erab.erab_id, erab.erab_level_qos_params, erab.transport_layer_address, teid_out, nullptr) != + SRSRAN_SUCCESS) { + erabs_failed_to_setup.emplace_back(); + erabs_failed_to_setup.back().erab_id = erab.erab_id; + erabs_failed_to_setup.back().cause.set_radio_network().value = + asn1::s1ap::cause_radio_network_opts::invalid_qos_combination; + continue; + } + if (rrc_ue->bearer_list.add_gtpu_bearer(erab.erab_id) != SRSRAN_SUCCESS) { + erabs_failed_to_setup.emplace_back(); + erabs_failed_to_setup.back().erab_id = erab.erab_id; + erabs_failed_to_setup.back().cause.set_radio_network().value = + asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell; + continue; + } } // Regenerate AS Keys @@ -876,8 +906,6 @@ bool rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& ho // Save source UE MAC configuration as a base rrc_ue->mac_ctrl.handle_ho_prep(ho_prep); - - return true; } void rrc::ue::rrc_mobility::handle_recfg_complete(wait_recfg_comp& s, const recfg_complete_ev& ev) diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index 920079ab4..58c0bc083 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -978,14 +978,10 @@ bool s1ap::handle_handover_command(const asn1::s1ap::ho_cmd_s& msg) bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg) { - uint16_t rnti = SRSRAN_INVALID_RNTI; - - auto on_scope_exit = srsran::make_scope_exit([this, &rnti, msg]() { - // If rnti is not allocated successfully, remove from s1ap and send handover failure - if (rnti == SRSRAN_INVALID_RNTI) { - send_ho_failure(msg.protocol_ies.mme_ue_s1ap_id.value.value); - } - }); + uint16_t rnti = SRSRAN_INVALID_RNTI; + uint32_t mme_ue_s1ap_id = msg.protocol_ies.mme_ue_s1ap_id.value.value; + asn1::s1ap::cause_c cause; + cause.set_misc().value = cause_misc_opts::unspecified; if (msg.ext or msg.protocol_ies.ho_restrict_list_present) { logger.warning("Not handling S1AP Handover Request extensions or Handover Restriction List"); @@ -993,6 +989,8 @@ bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg) if (msg.protocol_ies.handov_type.value.value != handov_type_opts::intralte) { logger.error("Not handling S1AP non-intra LTE handovers"); + cause.set_radio_network().value = cause_radio_network_opts::interrat_redirection; + send_ho_failure(mme_ue_s1ap_id, cause); return false; } @@ -1000,31 +998,37 @@ bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg) if (users.find_ue_mmeid(msg.protocol_ies.mme_ue_s1ap_id.value.value) != nullptr) { logger.error("The provided MME_UE_S1AP_ID=%" PRIu64 " is already connected to the cell", msg.protocol_ies.mme_ue_s1ap_id.value.value); + cause.set_radio_network().value = cause_radio_network_opts::unknown_mme_ue_s1ap_id; + send_ho_failure(mme_ue_s1ap_id, cause); return false; } // Create user ctxt object and associated MME context std::unique_ptr ue_ptr{new ue{this}}; ue_ptr->ctxt.mme_ue_s1ap_id = msg.protocol_ies.mme_ue_s1ap_id.value.value; - if (users.add_user(std::move(ue_ptr)) == nullptr) { - return false; - } + srsran_assert(users.add_user(std::move(ue_ptr)) != nullptr, "Unexpected failure to create S1AP UE"); // Unpack Transparent Container sourceenb_to_targetenb_transparent_container_s container; asn1::cbit_ref bref{msg.protocol_ies.source_to_target_transparent_container.value.data(), msg.protocol_ies.source_to_target_transparent_container.value.size()}; if (container.unpack(bref) != asn1::SRSASN_SUCCESS) { - logger.error("Failed to unpack SourceToTargetTransparentContainer"); + logger.warning("Failed to unpack SourceToTargetTransparentContainer"); + cause.set_protocol().value = cause_protocol_opts::transfer_syntax_error; + send_ho_failure(mme_ue_s1ap_id, cause); return false; } // Handle Handover Resource Allocation - rnti = rrc->start_ho_ue_resource_alloc(msg, container); - return rnti != SRSRAN_INVALID_RNTI; + rnti = rrc->start_ho_ue_resource_alloc(msg, container, cause); + if (rnti == SRSRAN_INVALID_RNTI) { + send_ho_failure(mme_ue_s1ap_id, cause); + return false; + } + return true; } -bool s1ap::send_ho_failure(uint32_t mme_ue_s1ap_id) +void s1ap::send_ho_failure(uint32_t mme_ue_s1ap_id, const asn1::s1ap::cause_c& cause) { // Remove created s1ap user ue* u = users.find_ue_mmeid(mme_ue_s1ap_id); @@ -1037,17 +1041,17 @@ bool s1ap::send_ho_failure(uint32_t mme_ue_s1ap_id) ho_fail_ies_container& container = tx_pdu.unsuccessful_outcome().value.ho_fail().protocol_ies; container.mme_ue_s1ap_id.value = mme_ue_s1ap_id; - // TODO: Setup cause - container.cause.value.set_radio_network().value = cause_radio_network_opts::ho_target_not_allowed; + container.cause.value = cause; - return sctp_send_s1ap_pdu(tx_pdu, SRSRAN_INVALID_RNTI, "HandoverFailure"); + sctp_send_s1ap_pdu(tx_pdu, SRSRAN_INVALID_RNTI, "HandoverFailure"); } bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, uint16_t rnti, uint32_t enb_cc_idx, srsran::unique_byte_buffer_t ho_cmd, - srsran::span admitted_bearers) + srsran::span admitted_bearers, + srsran::const_span not_admitted_bearers) { s1ap_pdu_c tx_pdu; tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_HO_RES_ALLOC); @@ -1079,6 +1083,19 @@ bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, } } + // Add failed to Setup E-RABs + if (not not_admitted_bearers.empty()) { + container.erab_failed_to_setup_list_ho_req_ack_present = true; + container.erab_failed_to_setup_list_ho_req_ack.value.resize(not_admitted_bearers.size()); + for (size_t i = 0; i < not_admitted_bearers.size(); ++i) { + container.erab_failed_to_setup_list_ho_req_ack.value[i].load_info_obj( + ASN1_S1AP_ID_ERAB_FAILEDTO_SETUP_ITEM_HO_REQ_ACK); + auto& erab = container.erab_failed_to_setup_list_ho_req_ack.value[i].value.erab_failedto_setup_item_ho_req_ack(); + erab.erab_id = not_admitted_bearers[i].erab_id; + erab.cause = not_admitted_bearers[i].cause; + } + } + // Pack transparent container asn1::s1ap::targetenb_to_sourceenb_transparent_container_s transparent_container; transparent_container.rrc_container.resize(ho_cmd->N_bytes); diff --git a/srsenb/test/common/dummy_classes.h b/srsenb/test/common/dummy_classes.h index 563ef8948..440b03934 100644 --- a/srsenb/test/common/dummy_classes.h +++ b/srsenb/test/common/dummy_classes.h @@ -123,7 +123,8 @@ public: uint16_t rnti, uint32_t enb_cc_idx, srsran::unique_byte_buffer_t ho_cmd, - srsran::span admitted_bearers) override + srsran::span admitted_bearers, + srsran::const_span not_admitted_bearers) override { return true; } @@ -184,9 +185,9 @@ public: const asn1::s1ap::ho_cmd_s& msg, srsran::unique_byte_buffer_t container) override {} - uint16_t - start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg, - const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container) override + uint16_t start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& msg, + const asn1::s1ap::sourceenb_to_targetenb_transparent_container_s& container, + asn1::s1ap::cause_c& failure_cause) override { return SRSRAN_INVALID_RNTI; } diff --git a/srsenb/test/upper/rrc_mobility_test.cc b/srsenb/test/upper/rrc_mobility_test.cc index 0c5aeaca2..44389fe7b 100644 --- a/srsenb/test/upper/rrc_mobility_test.cc +++ b/srsenb/test/upper/rrc_mobility_test.cc @@ -27,7 +27,8 @@ struct mobility_test_params { concurrent_ho, ho_prep_failure, duplicate_crnti_ce, - recover + recover, + wrong_target_cell, } fail_at; const char* to_string() { @@ -44,6 +45,8 @@ struct mobility_test_params { return "fail and success"; case test_event::duplicate_crnti_ce: return "duplicate CRNTI CE"; + case test_event::wrong_target_cell: + return "wrong target cell"; default: return "none"; } @@ -271,7 +274,11 @@ int test_s1ap_tenb_mobility(mobility_test_params test_params) erab.erab_id = 5; erab.erab_level_qos_params.qci = 9; 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, 0x06, 0xa4, 0x02, 0xf0, 0x04, 0x04, 0xf0, 0x00, 0x14, 0x80, 0x4a, 0x00, 0x00, 0x00, 0x02, 0x12, 0x31, 0xb6, @@ -289,7 +296,13 @@ int test_s1ap_tenb_mobility(mobility_test_params test_params) container.erab_info_list[0].value.erab_info_list_item().dl_forwarding.value = asn1::s1ap::dl_forwarding_opts::dl_forwarding_proposed; memcpy(container.rrc_container.data(), ho_prep_container, sizeof(ho_prep_container)); - tester.rrc.start_ho_ue_resource_alloc(ho_req, container); + asn1::s1ap::cause_c cause; + 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(tester.rrc.get_nof_users() == 0); + return SRSRAN_SUCCESS; + } tester.tic(); TESTASSERT(tester.rrc.get_nof_users() == 1); TESTASSERT(tester.mac.ue_db.count(0x46)); @@ -525,12 +538,13 @@ int main(int argc, char** argv) } argparse::parse_args(argc, argv); - // S1AP Handover + // Source ENB - S1 Handover TESTASSERT(test_s1ap_mobility(*spy, mobility_test_params{event::wrong_measreport}) == 0); TESTASSERT(test_s1ap_mobility(*spy, mobility_test_params{event::concurrent_ho}) == 0); TESTASSERT(test_s1ap_mobility(*spy, mobility_test_params{event::ho_prep_failure}) == 0); 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::success}) == 0); // intraeNB Handover diff --git a/srsenb/test/upper/test_helpers.h b/srsenb/test/upper/test_helpers.h index afa0ca4b0..30cb34b82 100644 --- a/srsenb/test/upper/test_helpers.h +++ b/srsenb/test/upper/test_helpers.h @@ -97,7 +97,8 @@ public: uint16_t rnti, uint32_t enb_cc_idx, srsran::unique_byte_buffer_t ho_cmd, - srsran::span admitted_bearers) override + srsran::span admitted_bearers, + srsran::const_span not_admitted_bearers) override { last_ho_req_ack.rnti = rnti; last_ho_req_ack.ho_cmd_pdu = std::move(ho_cmd);