From bb5dd92dcaaf1476a1a0959165ada3c1361dad7d Mon Sep 17 00:00:00 2001 From: Francisco Paisana Date: Thu, 20 Aug 2020 19:27:01 +0100 Subject: [PATCH] implemented s1 handover cancellation procedure to force the target enb to release the ue ctxt --- .../srslte/interfaces/enb_interfaces.h | 6 +++ srsenb/hdr/stack/rrc/rrc_mobility.h | 20 ++++++---- srsenb/hdr/stack/upper/s1ap.h | 1 + srsenb/src/stack/rrc/rrc_mobility.cc | 18 ++++++++- srsenb/src/stack/rrc/rrc_ue.cc | 3 ++ srsenb/src/stack/upper/s1ap.cc | 38 ++++++++++++++++--- srsenb/test/common/dummy_classes.h | 2 + 7 files changed, 75 insertions(+), 13 deletions(-) diff --git a/lib/include/srslte/interfaces/enb_interfaces.h b/lib/include/srslte/interfaces/enb_interfaces.h index 38a4129e3..a343af736 100644 --- a/lib/include/srslte/interfaces/enb_interfaces.h +++ b/lib/include/srslte/interfaces/enb_interfaces.h @@ -519,6 +519,12 @@ public: * 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; }; // Combined interface for PHY to access stack (MAC and RRC) diff --git a/srsenb/hdr/stack/rrc/rrc_mobility.h b/srsenb/hdr/stack/rrc/rrc_mobility.h index 27b104702..e4507553b 100644 --- a/srsenb/hdr/stack/rrc/rrc_mobility.h +++ b/srsenb/hdr/stack/rrc/rrc_mobility.h @@ -103,6 +103,7 @@ public: uint16_t crnti; uint16_t temp_crnti; }; + struct ho_cancel_ev {}; explicit rrc_mobility(srsenb::rrc::ue* outer_ue); bool fill_conn_recfg_msg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg); @@ -164,6 +165,7 @@ private: struct s1_target_ho_st {}; struct s1_source_ho_st : public subfsm_t { ho_meas_report_ev report; + using ho_cmd_msg = srslte::unique_byte_buffer_t; struct wait_ho_req_ack_st { void enter(s1_source_ho_st* f, const ho_meas_report_ev& ev); @@ -175,19 +177,23 @@ private: explicit s1_source_ho_st(rrc_mobility* parent_) : base_t(parent_) {} private: - bool send_ho_cmd(wait_ho_req_ack_st& s, const srslte::unique_byte_buffer_t& container); + bool send_ho_cmd(wait_ho_req_ack_st& s, const ho_cmd_msg& container); + void handle_ho_cancel(wait_ho_req_ack_st& s, const ho_cancel_ev& ev); + void handle_ho_cancel(status_transfer_st& s, const ho_cancel_ev& ev); protected: using fsm = s1_source_ho_st; state_list states{this}; // clang-format off using transitions = transition_table< - // Start Target Event Action Guard - // +-------------------+------------------+------------------------------+---------+---------------------+ - to_state< idle_st, srslte::failure_ev >, - row< wait_ho_req_ack_st, status_transfer_st, srslte::unique_byte_buffer_t, nullptr, &fsm::send_ho_cmd >, - row< wait_ho_req_ack_st, idle_st , srslte::unique_byte_buffer_t > - // +-------------------+------------------+------------------------------+---------+---------------------+ + // Start Target Event Action Guard + // +-------------------+------------------+---------------------+-----------------------+---------------------+ + to_state< idle_st, srslte::failure_ev >, + row< wait_ho_req_ack_st, status_transfer_st, ho_cmd_msg, nullptr, &fsm::send_ho_cmd >, + row< wait_ho_req_ack_st, idle_st, ho_cmd_msg >, + row< wait_ho_req_ack_st, idle_st, ho_cancel_ev, &fsm::handle_ho_cancel >, + row< status_transfer_st, idle_st, ho_cancel_ev, &fsm::handle_ho_cancel > + // +-------------------+------------------+---------------------+-----------------------+---------------------+ >; // clang-format on }; diff --git a/srsenb/hdr/stack/upper/s1ap.h b/srsenb/hdr/stack/upper/s1ap.h index c2924d518..f4104d3dc 100644 --- a/srsenb/hdr/stack/upper/s1ap.h +++ b/srsenb/hdr/stack/upper/s1ap.h @@ -87,6 +87,7 @@ public: srslte::unique_byte_buffer_t ho_cmd, srslte::span > admitted_bearers) override; void send_ho_notify(uint16_t rnti, uint64_t target_eci) override; + void send_ho_cancel(uint16_t rnti) override; // void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps); // Stack interface diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 1d7cf3c35..79e5f5f71 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -522,7 +522,11 @@ uint16_t rrc::enb_mobility_handler::start_ho_ue_resource_alloc( // TODO: KeNB derivations - return ue_ptr->mobility_handler->start_s1_tenb_ho(msg, container) ? rnti : SRSLTE_INVALID_RNTI; + if (not ue_ptr->mobility_handler->start_s1_tenb_ho(msg, container)) { + rrc_ptr->rem_user_thread(rnti); + return SRSLTE_INVALID_RNTI; + } + return rnti; } /************************************************************************************************* @@ -1013,6 +1017,18 @@ bool rrc::ue::rrc_mobility::s1_source_ho_st::send_ho_cmd(wait_ho_req_ack_st& return true; } +//! Called in Source ENB during S1-Handover when there was a Reestablishment Request +void rrc::ue::rrc_mobility::s1_source_ho_st::handle_ho_cancel(wait_ho_req_ack_st& s, const ho_cancel_ev& ev) +{ + parent_fsm()->rrc_enb->s1ap->send_ho_cancel(parent_fsm()->rrc_ue->rnti); +} + +//! Called in Source ENB during S1-Handover when there was a Reestablishment Request +void rrc::ue::rrc_mobility::s1_source_ho_st::handle_ho_cancel(status_transfer_st& s, const ho_cancel_ev& ev) +{ + parent_fsm()->rrc_enb->s1ap->send_ho_cancel(parent_fsm()->rrc_ue->rnti); +} + void rrc::ue::rrc_mobility::s1_source_ho_st::status_transfer_st::enter(s1_source_ho_st* f) { f->get_log()->info("HandoverCommand of rnti=0x%x handled successfully.\n", f->parent_fsm()->rrc_ue->rnti); diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index 0ef585958..20d91df21 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -341,6 +341,9 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) old_rnti); send_connection_reest(); + // Cancel Handover in Target eNB if on-going + parent->users[old_rnti]->mobility_handler->trigger(rrc_mobility::ho_cancel_ev{}); + // Setup security const cell_info_common* pcell_cfg = get_ue_cc_cfg(UE_PCELL_CC_IDX); ue_security_cfg = parent->users[old_rnti]->ue_security_cfg; diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index fbed3f1e8..49766d166 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -571,9 +571,8 @@ bool s1ap::handle_initiatingmessage(const init_msg_s& msg) return handle_erabsetuprequest(msg.value.erab_setup_request()); case s1ap_elem_procs_o::init_msg_c::types_opts::ue_context_mod_request: return handle_uecontextmodifyrequest(msg.value.ue_context_mod_request()); - case s1ap_elem_procs_o::init_msg_c::types_opts::ho_request: { + case s1ap_elem_procs_o::init_msg_c::types_opts::ho_request: return handle_ho_request(msg.value.ho_request()); - } case s1ap_elem_procs_o::init_msg_c::types_opts::mme_status_transfer: return handle_mme_status_transfer(msg.value.mme_status_transfer()); default: @@ -589,6 +588,9 @@ bool s1ap::handle_successfuloutcome(const successful_outcome_s& msg) return handle_s1setupresponse(msg.value.s1_setup_resp()); case s1ap_elem_procs_o::successful_outcome_c::types_opts::ho_cmd: return handle_s1hocommand(msg.value.ho_cmd()); + case s1ap_elem_procs_o::successful_outcome_c::types_opts::ho_cancel_ack: + s1ap_log->info("Received %s\n", msg.value.type().to_string().c_str()); + return true; default: s1ap_log->error("Unhandled successful outcome message: %s\n", msg.value.type().to_string().c_str()); } @@ -813,12 +815,13 @@ bool s1ap::handle_s1hocommand(const asn1::s1ap::ho_cmd_s& msg) bool s1ap::handle_ho_request(const asn1::s1ap::ho_request_s& msg) { + uint16_t rnti = SRSLTE_INVALID_RNTI; + s1ap_log->info("Received S1 HO Request\n"); s1ap_log->console("Received S1 HO Request\n"); - // If user is not allocated, send handover failure - uint16_t rnti = SRSLTE_INVALID_RNTI; - auto on_scope_exit = srslte::make_scope_exit([this, &rnti, msg]() { + auto on_scope_exit = srslte::make_scope_exit([this, &rnti, msg]() { + // If rnti is not allocated successfully, remove from s1ap and send handover failure if (rnti == SRSLTE_INVALID_RNTI) { send_ho_failure(msg.protocol_ies.mme_ue_s1ap_id.value.value); } @@ -861,6 +864,12 @@ bool s1ap::handle_ho_request(const asn1::s1ap::ho_request_s& msg) bool s1ap::send_ho_failure(uint32_t mme_ue_s1ap_id) { + // Remove created s1ap user + ue* u = users.find_ue_mmeid(mme_ue_s1ap_id); + if (u != nullptr) { + users.erase(u); + } + s1ap_pdu_c tx_pdu; tx_pdu.set_unsuccessful_outcome().load_info_obj(ASN1_S1AP_ID_HO_RES_ALLOC); ho_fail_ies_container& container = tx_pdu.unsuccessful_outcome().value.ho_fail().protocol_ies; @@ -956,6 +965,25 @@ void s1ap::send_ho_notify(uint16_t rnti, uint64_t target_eci) sctp_send_s1ap_pdu(tx_pdu, rnti, "HandoverNotify"); } +void s1ap::send_ho_cancel(uint16_t rnti) +{ + ue* user_ptr = users.find_ue_rnti(rnti); + if (user_ptr == nullptr) { + return; + } + + s1ap_pdu_c tx_pdu; + + tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_CANCEL); + ho_cancel_ies_container& container = tx_pdu.init_msg().value.ho_cancel().protocol_ies; + + container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id; + container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id; + container.cause.value.set_radio_network().value = cause_radio_network_opts::ho_cancelled; + + sctp_send_s1ap_pdu(tx_pdu, rnti, "HandoverCancel"); +} + /******************************************************************************* /* S1AP message senders ********************************************************************************/ diff --git a/srsenb/test/common/dummy_classes.h b/srsenb/test/common/dummy_classes.h index d48d422f4..eafe90f55 100644 --- a/srsenb/test/common/dummy_classes.h +++ b/srsenb/test/common/dummy_classes.h @@ -113,6 +113,8 @@ public: return true; } void send_ho_notify(uint16_t rnti, uint64_t target_eci) override {} + + void send_ho_cancel(uint16_t rnti) override {} }; class phy_dummy : public phy_interface_rrc_lte