From 524c80a804edc9003ae4efdd13a8d428dd0021c3 Mon Sep 17 00:00:00 2001 From: Francisco Paisana Date: Wed, 4 Dec 2019 16:23:54 +0000 Subject: [PATCH] added handling of ho command --- .../srslte/interfaces/enb_interfaces.h | 16 ++-- srsenb/hdr/stack/rrc/rrc.h | 4 +- srsenb/hdr/stack/rrc/rrc_mobility.h | 5 +- srsenb/hdr/stack/upper/s1ap.h | 11 ++- srsenb/src/stack/rrc/rrc.cc | 8 +- srsenb/src/stack/rrc/rrc_mobility.cc | 51 ++++++++++-- srsenb/src/stack/upper/s1ap.cc | 81 ++++++++++++++++++- 7 files changed, 149 insertions(+), 27 deletions(-) diff --git a/lib/include/srslte/interfaces/enb_interfaces.h b/lib/include/srslte/interfaces/enb_interfaces.h index ce84954c3..3d4fdae8c 100644 --- a/lib/include/srslte/interfaces/enb_interfaces.h +++ b/lib/include/srslte/interfaces/enb_interfaces.h @@ -264,14 +264,14 @@ public: class rrc_interface_s1ap { public: - virtual void write_dl_info(uint16_t rnti, srslte::unique_byte_buffer_t sdu) = 0; - virtual void release_complete(uint16_t rnti) = 0; - virtual bool setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT* msg) = 0; - virtual bool modify_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT* msg) = 0; - virtual bool setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT* msg) = 0; - virtual bool release_erabs(uint32_t rnti) = 0; - virtual void add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID) = 0; - virtual void ho_preparation_complete(uint16_t rnti, bool is_success) = 0; + virtual void write_dl_info(uint16_t rnti, srslte::unique_byte_buffer_t sdu) = 0; + virtual void release_complete(uint16_t rnti) = 0; + virtual bool setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT* msg) = 0; + virtual bool modify_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_UECONTEXTMODIFICATIONREQUEST_STRUCT* msg) = 0; + virtual bool setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT* msg) = 0; + virtual bool release_erabs(uint32_t rnti) = 0; + virtual void add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID) = 0; + virtual void ho_preparation_complete(uint16_t rnti, bool is_success, srslte::unique_byte_buffer_t container) = 0; }; // GTPU interface for PDCP diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index d0819ba66..79e6e1f0d 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -180,7 +180,7 @@ public: bool setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT* msg) override; bool release_erabs(uint32_t rnti) override; void add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID) override; - void ho_preparation_complete(uint16_t rnti, bool is_success) override; + void ho_preparation_complete(uint16_t rnti, bool is_success, srslte::unique_byte_buffer_t rrc_container) override; // rrc_interface_pdcp void write_pdu(uint16_t rnti, uint32_t lcid, srslte::unique_byte_buffer_t pdu) override; @@ -249,7 +249,7 @@ public: bool release_erabs(); // handover - void handle_ho_preparation_complete(bool is_success); + void handle_ho_preparation_complete(bool is_success, srslte::unique_byte_buffer_t container); void notify_s1ap_ue_ctxt_setup_complete(); void notify_s1ap_ue_erab_setup_response(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT* e); diff --git a/srsenb/hdr/stack/rrc/rrc_mobility.h b/srsenb/hdr/stack/rrc/rrc_mobility.h index a24d90e9b..fe4d7ae7f 100644 --- a/srsenb/hdr/stack/rrc/rrc_mobility.h +++ b/srsenb/hdr/stack/rrc/rrc_mobility.h @@ -85,7 +85,7 @@ public: explicit rrc_mobility(srsenb::rrc::ue* outer_ue); bool fill_conn_recfg_msg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg); void handle_ue_meas_report(const asn1::rrc::meas_report_s& msg); - void handle_ho_preparation_complete(bool is_success); + void handle_ho_preparation_complete(bool is_success, srslte::unique_byte_buffer_t container); private: enum class ho_interface_t { S1, X2, interSector }; @@ -105,7 +105,8 @@ private: { public: struct ho_prep_result { - bool is_success; + bool is_success; + srslte::unique_byte_buffer_t rrc_container; }; explicit sourceenb_ho_proc_t(rrc_mobility* ue_mobility_); diff --git a/srsenb/hdr/stack/upper/s1ap.h b/srsenb/hdr/stack/upper/s1ap.h index 32a358e13..c44b76871 100644 --- a/srsenb/hdr/stack/upper/s1ap.h +++ b/srsenb/hdr/stack/upper/s1ap.h @@ -119,6 +119,7 @@ private: bool handle_paging(LIBLTE_S1AP_MESSAGE_PAGING_STRUCT* msg); bool handle_s1setupresponse(LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT* msg); + bool handle_dlnastransport(LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT* msg); bool handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT* msg); bool handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT* msg); @@ -147,6 +148,7 @@ private: srslte::plmn_id_t target_plmn, srslte::unique_byte_buffer_t rrc_container); bool handle_hopreparationfailure(LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT* msg); + bool handle_s1hocommand(LIBLTE_S1AP_MESSAGE_HANDOVERCOMMAND_STRUCT& msg); bool find_mme_ue_id(uint32_t mme_ue_id, uint16_t* rnti, uint32_t* enb_ue_id); std::string get_cause(const LIBLTE_S1AP_CAUSE_STRUCT* c); @@ -164,6 +166,7 @@ private: srslte::proc_outcome_t step() { return srslte::proc_outcome_t::yield; } srslte::proc_outcome_t react(ts1_reloc_prep_expired e); srslte::proc_outcome_t react(const LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT& msg); + srslte::proc_outcome_t react(LIBLTE_S1AP_MESSAGE_HANDOVERCOMMAND_STRUCT& msg); void then(const srslte::proc_state_t& result); const char* name() { return "HandoverPreparation"; } @@ -171,8 +174,9 @@ private: s1ap::ue* ue_ptr = nullptr; s1ap* s1ap_ptr = nullptr; - uint32_t target_eci = 0; - srslte::plmn_id_t target_plmn; + uint32_t target_eci = 0; + srslte::plmn_id_t target_plmn; + srslte::unique_byte_buffer_t rrc_container; }; explicit ue(uint16_t rnti, s1ap* s1ap_ptr_); @@ -188,7 +192,8 @@ private: srslte::log* s1ap_log; ue_ctxt_t ctxt = {}; - srslte::timer_handler::unique_timer ts1_reloc_prep; ///< TS1_{RELOCprep} - max time for HO preparation + srslte::timer_handler::unique_timer ts1_reloc_prep; ///< TS1_{RELOCprep} - max time for HO preparation + srslte::timer_handler::unique_timer ts1_reloc_overall; ///< TS1_{RELOCOverall} // user procedures srslte::proc_t ho_prep_proc; diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index 1e9549cc6..000d49541 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -624,9 +624,9 @@ void rrc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) Handover functions *******************************************************************************/ -void rrc::ho_preparation_complete(uint16_t rnti, bool is_success) +void rrc::ho_preparation_complete(uint16_t rnti, bool is_success, srslte::unique_byte_buffer_t rrc_container) { - users.at(rnti)->handle_ho_preparation_complete(is_success); + users.at(rnti)->handle_ho_preparation_complete(is_success, std::move(rrc_container)); } /******************************************************************************* @@ -2012,9 +2012,9 @@ void rrc::ue::send_ue_cap_enquiry() /********************** Handover **************************/ -void rrc::ue::handle_ho_preparation_complete(bool is_success) +void rrc::ue::handle_ho_preparation_complete(bool is_success, srslte::unique_byte_buffer_t container) { - mobility_handler->handle_ho_preparation_complete(is_success); + mobility_handler->handle_ho_preparation_complete(is_success, std::move(container)); } /********************** HELPERS ***************************/ diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index fdd8660d9..329e4d403 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -36,6 +36,7 @@ namespace srsenb { #define Debug(fmt, ...) rrc_log->debug("Mobility: " fmt, ##__VA_ARGS__) #define procInfo(fmt, ...) parent->rrc_log->info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procWarning(fmt, ...) parent->rrc_log->warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) #define procError(fmt, ...) parent->rrc_log->error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) using namespace asn1::rrc; @@ -738,7 +739,8 @@ void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg) /** * Description: Send "HO Required" message from source eNB to MME * - 1st Message of the handover preparation phase - * - includes info about the target eNB and the radio resources of the source eNB + * - The RRC stores info regarding the source eNB configuration in a HO Preparation Info struct + * - This struct goes in a transparent container to the S1AP */ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci, uint8_t measobj_id, @@ -876,9 +878,9 @@ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci, return success; } -void rrc::ue::rrc_mobility::handle_ho_preparation_complete(bool is_success) +void rrc::ue::rrc_mobility::handle_ho_preparation_complete(bool is_success, srslte::unique_byte_buffer_t container) { - source_ho_proc.trigger(sourceenb_ho_proc_t::ho_prep_result{is_success}); + source_ho_proc.trigger(sourceenb_ho_proc_t::ho_prep_result{is_success, std::move(container)}); } /************************************************************************************************* @@ -921,8 +923,47 @@ srslte::proc_outcome_t rrc::ue::rrc_mobility::sourceenb_ho_proc_t::react(ho_prep procError("Failure during handover preparation.\n"); return srslte::proc_outcome_t::error; } - procError("Handover preparation successful\n"); - // TODO: send HO command to UE + + /* unpack RRC HOCmd struct and perform sanity checks */ + asn1::rrc::ho_cmd_s rrchocmd; + { + asn1::bit_ref bref(e.rrc_container->msg, e.rrc_container->N_bytes); + if (rrchocmd.unpack(bref) != asn1::SRSASN_SUCCESS) { + procError("Unpacking of RRC HO Command was unsuccessful\n"); + return srslte::proc_outcome_t::error; + } + } + if (rrchocmd.crit_exts.type().value != c1_or_crit_ext_opts::c1 or + rrchocmd.crit_exts.c1().type().value != ho_cmd_s::crit_exts_c_::c1_c_::types_opts::ho_cmd_r8) { + procError("Only handling r8 Handover Commands\n"); + } + + /* unpack DL-DCCH message containing the RRCRonnectionReconf (with MobilityInfo) to be sent to the UE */ + asn1::rrc::dl_dcch_msg_s dl_dcch_msg; + { + asn1::bit_ref bref(&rrchocmd.crit_exts.c1().ho_cmd_r8().ho_cmd_msg[0], + rrchocmd.crit_exts.c1().ho_cmd_r8().ho_cmd_msg.size()); + if (dl_dcch_msg.unpack(bref) != asn1::SRSASN_SUCCESS) { + procError("Unpacking of RRC DL-DCCH message with HO Command was unsuccessful.\n"); + return srslte::proc_outcome_t::error; + } + } + if (dl_dcch_msg.msg.type().value != dl_dcch_msg_type_c::types_opts::c1 or + dl_dcch_msg.msg.c1().type().value != dl_dcch_msg_type_c::c1_c_::types_opts::rrc_conn_recfg) { + procError("HandoverCommand is expected to contain an RRC Connection Reconf message inside\n"); + return srslte::proc_outcome_t::error; + } + asn1::rrc::rrc_conn_recfg_s& reconf = dl_dcch_msg.msg.c1().rrc_conn_recfg(); + if (not reconf.crit_exts.c1().rrc_conn_recfg_r8().mob_ctrl_info_present) { + procWarning("HandoverCommand is expected to have mobility control subfield\n"); + return srslte::proc_outcome_t::error; + } + + // TODO: Do anything with MeasCfg info within the Msg (e.g. update ue_var_meas)? + + /* Send HO Command to UE */ + parent->rrc_ue->send_dl_dcch(&dl_dcch_msg); + procInfo("Handover command of rnti=0x%x handled successfully.\n", parent->rrc_ue->rnti); return srslte::proc_outcome_t::success; } diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/upper/s1ap.cc index a68af50de..e79c6b5ac 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/upper/s1ap.cc @@ -37,6 +37,7 @@ using srslte::s1ap_mccmnc_to_plmn; using srslte::uint32_to_uint8; #define procError(fmt, ...) s1ap_ptr->s1ap_log->error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procWarning(fmt, ...) s1ap_ptr->s1ap_log->warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) #define procInfo(fmt, ...) s1ap_ptr->s1ap_log->info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) namespace srsenb { @@ -48,18 +49,19 @@ s1ap::ue::ho_prep_proc_t::ho_prep_proc_t(s1ap::ue* ue_) : ue_ptr(ue_), s1ap_ptr( srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::init(uint32_t target_eci_, srslte::plmn_id_t target_plmn_, - srslte::unique_byte_buffer_t rrc_container) + srslte::unique_byte_buffer_t rrc_container_) { target_eci = target_eci_; target_plmn = target_plmn_; procInfo("Sending HandoverRequired to MME id=%d\n", ue_ptr->ctxt.MME_UE_S1AP_ID); - if (not ue_ptr->send_ho_required(target_eci, target_plmn, std::move(rrc_container))) { + if (not ue_ptr->send_ho_required(target_eci, target_plmn, std::move(rrc_container_))) { procError("Failed to send HORequired to cell 0x%x\n", target_eci); return srslte::proc_outcome_t::error; } ue_ptr->ts1_reloc_prep.run(); + return srslte::proc_outcome_t::yield; } srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(ts1_reloc_prep_expired e) @@ -70,15 +72,74 @@ srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(ts1_reloc_prep_expired e) } srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(const LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFAILURE_STRUCT& msg) { + ue_ptr->ts1_reloc_prep.stop(); + std::string cause = s1ap_ptr->get_cause(&msg.Cause); procError("HO preparation Failure. Cause: %s\n", cause.c_str()); s1ap_ptr->s1ap_log->console("HO preparation Failure. Cause: %s\n", cause.c_str()); + return srslte::proc_outcome_t::error; } +/** + * TS 36.413 - Section 8.4.1.2 - HandoverPreparation Successful Operation + */ +srslte::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(LIBLTE_S1AP_MESSAGE_HANDOVERCOMMAND_STRUCT& msg) +{ + // update timers + ue_ptr->ts1_reloc_prep.stop(); + ue_ptr->ts1_reloc_overall.run(); + + // Check for unsupported S1AP fields + if (msg.ext or msg.Target_ToSource_TransparentContainer_Secondary_present or msg.HandoverType.ext or + msg.HandoverType.e != LIBLTE_S1AP_HANDOVERTYPE_INTRALTE or msg.CriticalityDiagnostics_present or + msg.NASSecurityParametersfromE_UTRAN_present) { + procWarning("Not handling HandoverCommand extensions and non-intraLTE params\n"); + } + + // Check for E-RABs that could not be admitted in the target + if (msg.E_RABtoReleaseListHOCmd_present) { + procWarning("Not handling E-RABtoReleaseList\n"); + // TODO + } + + // Check for E-RABs subject to being forwarded + if (msg.E_RABSubjecttoDataForwardingList_present) { + procWarning("Not handling E-RABSubjecttoDataForwardingList\n"); + // TODO + } + + // In case of intra-system Handover, Target to Source Transparent Container IE shall be encoded as + // Target eNB to Source eNB Transparent Container IE + LIBLTE_BIT_MSG_STRUCT bit_msg; + uint8_t* bit_ptr = &bit_msg.msg[0]; + LIBLTE_S1AP_TARGETENB_TOSOURCEENB_TRANSPARENTCONTAINER_STRUCT container; + liblte_unpack( + &msg.Target_ToSource_TransparentContainer.buffer[0], msg.Target_ToSource_TransparentContainer.n_octets, bit_ptr); + liblte_s1ap_unpack_targetenb_tosourceenb_transparentcontainer(&bit_ptr, &container); + if (container.iE_Extensions_present or container.ext) { + procWarning("Not handling extensions\n"); + } + + // Create a unique buffer out of transparent container to pass to RRC + rrc_container = srslte::allocate_unique_buffer(*s1ap_ptr->pool, false); + if (rrc_container == nullptr) { + procError("Fatal Error: Couldn't allocate buffer.\n"); + return srslte::proc_outcome_t::error; + } + memcpy(rrc_container->msg, container.rRC_Container.buffer, container.rRC_Container.n_octets); + + return srslte::proc_outcome_t::success; +} + void s1ap::ue::ho_prep_proc_t::then(const srslte::proc_state_t& result) { - s1ap_ptr->rrc->ho_preparation_complete(ue_ptr->ctxt.rnti, result.is_success()); + if (result.is_error()) { + s1ap_ptr->rrc->ho_preparation_complete(ue_ptr->ctxt.rnti, false, {}); + } else { + s1ap_ptr->rrc->ho_preparation_complete(ue_ptr->ctxt.rnti, true, std::move(rrc_container)); + procInfo("Completed with success\n"); + } } /********************************************************* @@ -511,6 +572,8 @@ bool s1ap::handle_successfuloutcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT* msg) switch (msg->choice_type) { case LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_S1SETUPRESPONSE: return handle_s1setupresponse(&msg->choice.S1SetupResponse); + case LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_HANDOVERCOMMAND: + return handle_s1hocommand(msg->choice.HandoverCommand); default: s1ap_log->error("Unhandled successful outcome message: %s\n", liblte_s1ap_successfuloutcome_choice_text[msg->choice_type]); @@ -769,6 +832,16 @@ bool s1ap::handle_hopreparationfailure(LIBLTE_S1AP_MESSAGE_HANDOVERPREPARATIONFA return true; } +bool s1ap::handle_s1hocommand(LIBLTE_S1AP_MESSAGE_HANDOVERCOMMAND_STRUCT& msg) +{ + auto user_it = users.find(enbid_to_rnti_map[msg.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID]); + if (user_it == users.end()) { + s1ap_log->error("user rnti=0x%x no longer exists\n", user_it->first); + } + user_it->second->get_ho_prep_proc().trigger(msg); + return true; +} + /******************************************************************************* /* S1AP message senders ********************************************************************************/ @@ -1282,6 +1355,8 @@ s1ap::ue::ue(uint16_t rnti_, s1ap* s1ap_ptr_) : s1ap_ptr(s1ap_ptr_), s1ap_log(s1 // initialize timers ts1_reloc_prep = s1ap_ptr->timers->get_unique_timer(); ts1_reloc_prep.set(10000, [this](uint32_t tid) { ho_prep_proc.trigger(ho_prep_proc_t::ts1_reloc_prep_expired{}); }); + ts1_reloc_overall = s1ap_ptr->timers->get_unique_timer(); + ts1_reloc_overall.set(10000, [this](uint32_t tid) {}); } bool s1ap::ue::send_ho_required(uint32_t target_eci,