From 11b4a572a0e4188dd88b95add4f2be44b40d5710 Mon Sep 17 00:00:00 2001 From: Francisco Date: Wed, 1 Dec 2021 16:40:43 +0000 Subject: [PATCH] nr,gnb,rrc&ngap: implementation of NGAP UE Context Release Request and RRC Release for the case RRC_CONNECTED->RRC_IDLE --- .../srsran/interfaces/gnb_ngap_interfaces.h | 12 +++--- .../srsran/interfaces/gnb_rrc_nr_interfaces.h | 9 +++-- .../hdr/stack/common/test/dummy_nr_classes.h | 2 +- srsgnb/hdr/stack/ngap/ngap.h | 21 ++++++----- srsgnb/hdr/stack/ngap/ngap_ue.h | 2 + .../hdr/stack/ngap/ngap_ue_bearer_manager.h | 2 + srsgnb/hdr/stack/rrc/rrc_nr.h | 1 + srsgnb/hdr/stack/rrc/rrc_nr_ue.h | 2 +- srsgnb/src/stack/ngap/ngap.cc | 17 +++++++++ srsgnb/src/stack/ngap/ngap_ue.cc | 37 +++++++++++++++++++ srsgnb/src/stack/ngap/ngap_ue_proc.cc | 3 +- srsgnb/src/stack/ngap/test/ngap_test.cc | 1 + srsgnb/src/stack/rrc/rrc_nr.cc | 10 +++++ srsgnb/src/stack/rrc/rrc_nr_ue.cc | 28 ++++++++++---- 14 files changed, 117 insertions(+), 30 deletions(-) diff --git a/lib/include/srsran/interfaces/gnb_ngap_interfaces.h b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h index 70472a69b..eaec4236d 100644 --- a/lib/include/srsran/interfaces/gnb_ngap_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h @@ -44,12 +44,12 @@ public: srsran::const_byte_span pdu, uint32_t m_tmsi) = 0; - virtual void write_pdu(uint16_t rnti, srsran::const_byte_span pdu) = 0; - virtual bool user_exists(uint16_t rnti) = 0; - virtual void user_mod(uint16_t old_rnti, uint16_t new_rnti) = 0; - virtual bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) = 0; - virtual bool is_amf_connected() = 0; - virtual void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) = 0; + virtual void write_pdu(uint16_t rnti, srsran::const_byte_span pdu) = 0; + virtual bool user_exists(uint16_t rnti) = 0; + virtual void user_mod(uint16_t old_rnti, uint16_t new_rnti) = 0; + virtual void user_release_request(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) = 0; + virtual bool is_amf_connected() = 0; + virtual void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) = 0; }; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h index f2b8867f6..54b25b538 100644 --- a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h @@ -26,10 +26,11 @@ public: virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps) = 0; virtual int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) = 0; virtual int - establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0; - virtual int allocate_lcid(uint16_t rnti) = 0; - virtual int release_bearers(uint16_t rnti) = 0; - virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; + establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0; + virtual int allocate_lcid(uint16_t rnti) = 0; + virtual int release_bearers(uint16_t rnti) = 0; + virtual void release_user(uint16_t rnti) = 0; + virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; }; } // namespace srsenb diff --git a/srsgnb/hdr/stack/common/test/dummy_nr_classes.h b/srsgnb/hdr/stack/common/test/dummy_nr_classes.h index eb0339225..1fac72327 100644 --- a/srsgnb/hdr/stack/common/test/dummy_nr_classes.h +++ b/srsgnb/hdr/stack/common/test/dummy_nr_classes.h @@ -36,7 +36,7 @@ class ngap_dummy : public ngap_interface_rrc_nr void write_pdu(uint16_t rnti, srsran::const_byte_span pdu) {} bool user_exists(uint16_t rnti) { return true; } void user_mod(uint16_t old_rnti, uint16_t new_rnti) {} - bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) { return true; } + void user_release_request(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) {} bool is_amf_connected() { return true; } void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) {} }; diff --git a/srsgnb/hdr/stack/ngap/ngap.h b/srsgnb/hdr/stack/ngap/ngap.h index 27c900781..86b3da74a 100644 --- a/srsgnb/hdr/stack/ngap/ngap.h +++ b/srsgnb/hdr/stack/ngap/ngap.h @@ -51,27 +51,30 @@ public: void initial_ue(uint16_t rnti, uint32_t gnb_cc_idx, asn1::ngap_nr::rrcestablishment_cause_e cause, - srsran::const_byte_span pdu); + srsran::const_byte_span pdu) override; void initial_ue(uint16_t rnti, uint32_t gnb_cc_idx, asn1::ngap_nr::rrcestablishment_cause_e cause, srsran::const_byte_span pdu, - uint32_t s_tmsi); + uint32_t s_tmsi) override; - void write_pdu(uint16_t rnti, srsran::const_byte_span pdu); - bool user_exists(uint16_t rnti) { return true; }; - void user_mod(uint16_t old_rnti, uint16_t new_rnti){}; - bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) { return true; }; - bool is_amf_connected(); + void write_pdu(uint16_t rnti, srsran::const_byte_span pdu) override; + bool user_exists(uint16_t rnti) override { return true; }; + void user_mod(uint16_t old_rnti, uint16_t new_rnti) override {} + + // TS 38.413 - Section 8.3.2 - UE Context Release Request + void user_release_request(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) override; + + bool is_amf_connected() override; bool send_error_indication(const asn1::ngap_nr::cause_c& cause, srsran::optional ran_ue_ngap_id = {}, srsran::optional amf_ue_ngap_id = {}); - void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome); + void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) override; bool send_pdu_session_resource_setup_response(); // Stack interface bool - handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); + handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); void get_metrics(ngap_metrics_t& m); void get_args(ngap_args_t& args_); diff --git a/srsgnb/hdr/stack/ngap/ngap_ue.h b/srsgnb/hdr/stack/ngap/ngap_ue.h index e2eb7ba7f..f51e2722d 100644 --- a/srsgnb/hdr/stack/ngap/ngap_ue.h +++ b/srsgnb/hdr/stack/ngap/ngap_ue.h @@ -48,6 +48,8 @@ public: bool send_ue_ctxt_release_complete(); // TS 38.413 - Section 9.2.2.1 - Initial Context Setup Request bool handle_initial_ctxt_setup_request(const asn1::ngap_nr::init_context_setup_request_s& msg); + // TS 38.413 - Section 9.2.2.4 - UE Context Release Request + bool send_ue_context_release_request(asn1::ngap_nr::cause_c cause); // TS 38.413 - Section 9.2.2.5 - UE Context Release Command bool handle_ue_ctxt_release_cmd(const asn1::ngap_nr::ue_context_release_cmd_s& msg); // TS 38.413 - Section 9.2.1.1 - PDU Session Resource Setup Request diff --git a/srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h b/srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h index 361e4f46f..bb249b364 100644 --- a/srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h +++ b/srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h @@ -56,6 +56,8 @@ public: int reset_pdu_sessions(uint16_t rnti); + const std::map& pdu_sessions() const { return pdu_session_list; } + private: gtpu_interface_rrc* gtpu = nullptr; std::map pdu_session_list; diff --git a/srsgnb/hdr/stack/rrc/rrc_nr.h b/srsgnb/hdr/stack/rrc/rrc_nr.h index afe3e00c0..76ed30972 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr.h @@ -103,6 +103,7 @@ public: srsran::const_byte_span nas_pdu, uint32_t lcid) final; int release_bearers(uint16_t rnti) final; + void release_user(uint16_t rnti) final; void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) final; int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates) final; int allocate_lcid(uint16_t rnti) final; diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h index d48093894..8fc4947ce 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h @@ -90,7 +90,7 @@ private: int send_dl_ccch(const asn1::rrc_nr::dl_ccch_msg_s& dl_ccch_msg); int send_dl_dcch(srsran::nr_srb srb, const asn1::rrc_nr::dl_dcch_msg_s& dl_dcch_msg); - /* TS 38.331 - 5.3.3 RRC connection establishment */ + /** TS 38.331 - 5.3.3 RRC connection establishment */ void send_rrc_setup(); void send_rrc_reject(uint8_t reject_wait_time_secs); diff --git a/srsgnb/src/stack/ngap/ngap.cc b/srsgnb/src/stack/ngap/ngap.cc index bdc3a8a50..6dc157fcf 100644 --- a/srsgnb/src/stack/ngap/ngap.cc +++ b/srsgnb/src/stack/ngap/ngap.cc @@ -265,6 +265,23 @@ void ngap::write_pdu(uint16_t rnti, srsran::const_byte_span pdu) u->send_ul_nas_transport(pdu); } +void ngap::user_release_request(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) +{ + ue* u = users.find_ue_rnti(rnti); + if (u == nullptr) { + logger.warning("Released UE rnti=0x%x not found.", rnti); + return; + } + + cause_c cause; + cause.set_radio_network().value = cause_radio; + if (not u->send_ue_context_release_request(cause)) { + logger.error("Failed to initiate RRC Release for rnti=0x%x. Removing user", rnti); + rrc->release_user(rnti); + users.erase(u); + } +} + /********************************************************* * ngap::user_list class *********************************************************/ diff --git a/srsgnb/src/stack/ngap/ngap_ue.cc b/srsgnb/src/stack/ngap/ngap_ue.cc index f83b446e9..2a2f0dc1c 100644 --- a/srsgnb/src/stack/ngap/ngap_ue.cc +++ b/srsgnb/src/stack/ngap/ngap_ue.cc @@ -244,6 +244,43 @@ bool ngap::ue::send_ue_ctxt_release_complete() return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseComplete"); } +bool ngap::ue::send_ue_context_release_request(asn1::ngap_nr::cause_c cause) +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + if (was_uectxtrelease_requested()) { + // let timeout auto-remove user. + return false; + } + release_requested = true; + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_NR_ID_UE_CONTEXT_RELEASE_REQUEST); + ue_context_release_request_s& container = tx_pdu.init_msg().value.ue_context_release_request(); + + container.protocol_ies.cause.value = cause; + + // PDU Session Resource List + auto& session_lst = container.protocol_ies.pdu_session_res_list_cxt_rel_req.value; + for (const auto& pdu_pair : bearer_manager.pdu_sessions()) { + const ngap_ue_bearer_manager::pdu_session_t& session = pdu_pair.second; + + pdu_session_res_item_cxt_rel_req_s obj; + obj.pdu_session_id = session.id; + session_lst.push_back(obj); + } + container.protocol_ies.pdu_session_res_list_cxt_rel_req_present = session_lst.size() > 0; + + container.protocol_ies.ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + container.protocol_ies.amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + + // TODO: Implement timeout + return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseRequest"); +} + /******************************************************************************* /* NGAP message handler ********************************************************************************/ diff --git a/srsgnb/src/stack/ngap/ngap_ue_proc.cc b/srsgnb/src/stack/ngap/ngap_ue_proc.cc index e1e820f91..9ad9fb3d4 100644 --- a/srsgnb/src/stack/ngap/ngap_ue_proc.cc +++ b/srsgnb/src/stack/ngap/ngap_ue_proc.cc @@ -81,9 +81,8 @@ ngap_ue_ue_context_release_proc::ngap_ue_ue_context_release_proc(ngap_interface_ proc_outcome_t ngap_ue_ue_context_release_proc::init(const asn1::ngap_nr::ue_context_release_cmd_s& msg) { logger.info("Started %s", name()); - // TODO: How to approach erasing users ? bearer_manager->reset_pdu_sessions(ue_ctxt->rnti); - rrc->release_bearers(ue_ctxt->rnti); + rrc->release_user(ue_ctxt->rnti); parent->send_ue_ctxt_release_complete(); return proc_outcome_t::success; } diff --git a/srsgnb/src/stack/ngap/test/ngap_test.cc b/srsgnb/src/stack/ngap/test/ngap_test.cc index c5039b1c9..6e637d7b8 100644 --- a/srsgnb/src/stack/ngap/test/ngap_test.cc +++ b/srsgnb/src/stack/ngap/test/ngap_test.cc @@ -115,6 +115,7 @@ public: } int release_bearers(uint16_t rnti) { return SRSRAN_SUCCESS; } + void release_user(uint16_t rnti) {} int allocate_lcid(uint16_t rnti) { return SRSRAN_SUCCESS; } void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) {} diff --git a/srsgnb/src/stack/rrc/rrc_nr.cc b/srsgnb/src/stack/rrc/rrc_nr.cc index 67e7ac949..d365fcbda 100644 --- a/srsgnb/src/stack/rrc/rrc_nr.cc +++ b/srsgnb/src/stack/rrc/rrc_nr.cc @@ -629,6 +629,16 @@ int rrc_nr::release_bearers(uint16_t rnti) return SRSRAN_SUCCESS; } +void rrc_nr::release_user(uint16_t rnti) +{ + if (not users.contains(rnti)) { + logger.warning("User rnti=0x%x has already been released", rnti); + return; + } + + users[rnti]->send_rrc_release(); +} + int rrc_nr::allocate_lcid(uint16_t rnti) { return SRSRAN_SUCCESS; diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc index 00d2441a3..3790cf8a3 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_ue.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -96,8 +96,8 @@ void rrc_nr::ue::activity_timer_expired(const activity_timeout_type_t type) case UE_INACTIVITY_TIMEOUT: { state = rrc_nr_state_t::RRC_INACTIVE; if (parent->cfg.is_standalone) { - // TODO: This procedure needs to be defined - parent->rrc_release(rnti); + // Start NGAP Release UE context + parent->ngap->user_release_request(rnti, asn1::ngap_nr::cause_radio_network_opts::user_inactivity); } else { parent->rrc_eutra->sgnb_inactivity_timeout(eutra_rnti); } @@ -1017,7 +1017,7 @@ void rrc_nr::ue::send_security_mode_command(srsran::unique_byte_buffer_t nas_pdu ies.security_cfg_smc.security_algorithm_cfg = sec_ctx.get_security_algorithm_cfg(); if (send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg) != SRSRAN_SUCCESS) { - send_rrc_release(); + parent->ngap->user_release_request(rnti, asn1::ngap_nr::cause_radio_network_opts::radio_res_not_available); } } @@ -1061,7 +1061,7 @@ void rrc_nr::ue::send_rrc_reconfiguration() // Pack masterCellGroup into container srsran::unique_byte_buffer_t pdu = parent->pack_into_pdu(master_cell_group, __FUNCTION__); if (pdu == nullptr) { - send_rrc_release(); + parent->ngap->user_release_request(rnti, asn1::ngap_nr::cause_radio_network_opts::radio_res_not_available); return; } ies.non_crit_ext.master_cell_group.resize(pdu->N_bytes); @@ -1097,7 +1097,7 @@ void rrc_nr::ue::send_rrc_reconfiguration() ies.non_crit_ext_present = ies.non_crit_ext.master_cell_group_present or ies.non_crit_ext.ded_nas_msg_list_present; if (send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg) != SRSRAN_SUCCESS) { - send_rrc_release(); + parent->ngap->user_release_request(rnti, asn1::ngap_nr::cause_radio_network_opts::radio_res_not_available); } } @@ -1112,7 +1112,21 @@ void rrc_nr::ue::handle_rrc_reconfiguration_complete(const asn1::rrc_nr::rrc_rec void rrc_nr::ue::send_rrc_release() { - // TODO + static const uint32_t release_delay = 60; // Taken from TS 38.331, 5.3.8.3 + + dl_dcch_msg_s dl_dcch_msg; + rrc_release_s& release = dl_dcch_msg.msg.set_c1().set_rrc_release(); + + release.rrc_transaction_id = (uint8_t)((transaction_id++) % 4); + rrc_release_ies_s& ies = release.crit_exts.set_rrc_release(); + + ies.suspend_cfg_present = false; // goes to RRC_IDLE + + send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg); + state = rrc_nr_state_t::RRC_IDLE; + + // TODO: Obtain acknowledgment from lower layers that RRC Release was received + parent->task_sched.defer_callback(release_delay, [this]() { parent->rem_user(rnti); }); } void rrc_nr::ue::send_dl_information_transfer(srsran::unique_byte_buffer_t sdu) @@ -1126,7 +1140,7 @@ void rrc_nr::ue::send_dl_information_transfer(srsran::unique_byte_buffer_t sdu) memcpy(ies.ded_nas_msg.data(), sdu->data(), ies.ded_nas_msg.size()); if (send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg) != SRSRAN_SUCCESS) { - send_rrc_release(); + parent->ngap->user_release_request(rnti, asn1::ngap_nr::cause_radio_network_opts::radio_res_not_available); } }