From 28f926607e8ae1ccc77e48319340325f77943c05 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 14 Mar 2025 14:51:50 +0100 Subject: [PATCH] Add N2 HO trigger on Bearer Context Setup Response (target NG-RAN) * add trigger function to (1) add the callbacks and (2) initiate handover on the target NG-RAN * add handover failure callback on target NG-RAN: the Handover Failure is a procedure that belongs to the target NG-RAN and it is different from the Handover Cancel procedure Co-authored-by: batuhan duyuler --- openair2/RRC/NR/MESSAGES/asn1_msg.c | 43 +++++++++++++++++++++++++++++ openair2/RRC/NR/MESSAGES/asn1_msg.h | 4 +++ openair2/RRC/NR/rrc_gNB.c | 26 ++++++++--------- openair2/RRC/NR/rrc_gNB_NGAP.c | 1 + openair2/RRC/NR/rrc_gNB_mobility.c | 26 +++++++++++++++-- openair2/RRC/NR/rrc_gNB_mobility.h | 7 +++++ 6 files changed, 92 insertions(+), 15 deletions(-) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 20bf56390b..ac58b3a278 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -87,6 +87,7 @@ #include "NR_HandoverPreparationInformation.h" #include "NR_HandoverPreparationInformation-IEs.h" #include "NR_HandoverCommand.h" +#include "NR_UE-CapabilityRAT-ContainerList.h" #include "common/utils/nr/nr_common.h" #if defined(NR_Rel16) #include "NR_SCS-SpecificCarrier.h" @@ -202,6 +203,48 @@ int xer_nr_sprint(char *string, size_t string_size, asn_TYPE_descriptor_t *td, v return er.encoded; } +struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len) +{ + if (!buf || len == 0) return NULL; + + NR_UE_CapabilityRAT_ContainerList_t *clist = NULL; + asn_dec_rval_t rval = uper_decode(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList, + (void **)&clist, buf, len, 0, 0); + + if (rval.code != RC_OK) { + LOG_W(NR_MAC, "UE RNTI %04x: Failed to decode container list\n", rnti); + return NULL; + } + + NR_UE_NR_Capability_t *cap = decode_nr_ue_capability(rnti, clist); + ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, clist); + return cap; +} + +NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist) +{ + if (!clist) return NULL; + + for (int i = 0; i < clist->list.count; i++) { + const NR_UE_CapabilityRAT_Container_t *c = clist->list.array[i]; + if (c->rat_Type != NR_RAT_Type_nr) + continue; + + NR_UE_NR_Capability_t *cap = NULL; + asn_dec_rval_t rval = uper_decode(NULL, &asn_DEF_NR_UE_NR_Capability, + (void **)&cap, + c->ue_CapabilityRAT_Container.buf, + c->ue_CapabilityRAT_Container.size, + 0, 0); + if (rval.code == RC_OK) + return cap; + + LOG_W(NR_MAC, "UE RNTI %04x: Failed to decode NR capability (%zu bytes)\n", rnti, rval.consumed); + ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, cap); + } + return NULL; +} + //------------------------------------------------------------------------------ int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 665edeef4b..fb43237d2a 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -51,6 +51,7 @@ #include "NR_SRB-ToAddModList.h" #include "NR_SecurityConfig.h" #include "NR_MeasurementTimingConfiguration.h" +#include "NR_UE-CapabilityRAT-ContainerList.h" #include "ds/seq_arr.h" #include "ds/byte_array.h" #include "rrc_messages_types.h" @@ -167,4 +168,7 @@ byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params); void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *measConfig, byte_array_t prep_info); int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, byte_array_t handoverCommand); +struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len); +NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist); + #endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */ diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 32021d7f22..75a3296b8f 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1607,21 +1607,14 @@ static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, UE->UE_Capability_nr = 0; } - asn_dec_rval_t dec_rval = uper_decode(NULL, - &asn_DEF_NR_UE_NR_Capability, - (void **)&UE->UE_Capability_nr, - ue_cap_container->ue_CapabilityRAT_Container.buf, - ue_cap_container->ue_CapabilityRAT_Container.size, - 0, - 0); - if (LOG_DEBUGFLAG(DEBUG_ASN1)) { - xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr); + UE->UE_Capability_nr = decode_nr_ue_capability(UE->rnti, ue_CapabilityRAT_ContainerList); + if (!UE->UE_Capability_nr) { + LOG_E(NR_RRC, "UE %d: NR capability decoding failed\n", UE->rrc_ue_id); + return; } - if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) { - LOG_E(NR_RRC, "UE %d: Failed to decode nr UE capabilities (%zu bytes)\n", UE->rrc_ue_id, dec_rval.consumed); - ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr); - UE->UE_Capability_nr = 0; + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { + xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr); } const NR_RedCapParameters_r17_t *redcap_p = get_redcapparam_r17(UE->UE_Capability_nr); @@ -2557,6 +2550,13 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp } } + // If HO Preparation Info is stored, N2 handover is ongoing + if (UE->ho_context) { + LOG_I(NR_RRC, "Received Bearer Context Setup Response for UE %d with valid HO Context\n", UE->rrc_ue_id); + UE->ho_context->target->ho_trigger(rrc, UE); + return; + } + if (!UE->f1_ue_context_active) rrc_gNB_generate_UeContextSetupRequest(rrc, ue_context_p, resp); else diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index e7459b1d49..961191d171 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1111,6 +1111,7 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga return -1; } UE->ho_context = alloc_ho_ctx(HO_CTX_TARGET); + UE->ho_context->target->ho_trigger = nr_rrc_trigger_n2_ho_target; // Store IDs in UE context UE->amf_ue_ngap_id = msg->amf_ue_ngap_id; diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 9e76217dc9..a2978e2d56 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -122,7 +122,8 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, byte_array_t *ho_prep_info, ho_req_ack_t ack, ho_success_t success, - ho_cancel_t cancel) + ho_cancel_t cancel, + ho_failure_t failure) { DevAssert(rrc != NULL); DevAssert(ue != NULL); @@ -145,6 +146,7 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, // response ho_ctx->target->ho_req_ack = ack; ho_ctx->target->ho_success = success; + ho_ctx->target->ho_failure = failure; const f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id); if (source_du != NULL) { @@ -353,7 +355,7 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain ho_success_t success = nr_rrc_f1_ho_complete; ho_cancel_t cancel = nr_rrc_cancel_f1_ho; byte_array_t hpi = {.buf = buf, .len = size}; - nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel); + nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel, NULL); } void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue) @@ -496,6 +498,26 @@ void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_f return; } +/** @brief Trigger N2 Handover on the target gNB: + * 1) set up callbacks + * 2) initiate N2 handover on target NG-RAN, which triggers the set up + * of HO context and UE Context Setup procedures */ +void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue) +{ + ho_req_ack_t ack = nr_rrc_n2_ho_acknowledge; + ho_success_t success = nr_rrc_n2_ho_complete; + ho_failure_t failure = nr_rrc_n2_ho_failure; + + const nr_rrc_du_container_t *target_du = get_du_for_ue(rrc, ue->rrc_ue_id); + nr_initiate_handover(rrc, ue, NULL, target_du, &ue->ho_context->target->ue_ho_prep_info, ack, success, NULL, failure); + FREE_AND_ZERO_BYTE_ARRAY(ue->ho_context->target->ue_ho_prep_info); + + NR_UE_NR_Capability_t *ue_cap = get_ue_nr_capability(ue->rnti, ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len); + ue->UE_Capability_nr = ue_cap; + ue->UE_Capability_size = ue->ue_cap_buffer.len; + +} + /** @brief Trigger N2 handover on source gNB: * 1) Prepare RRC Container with HandoverPreparationInformation message * 2) send NGAP Handover Required message */ diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index 5a6542132c..db0645c5e3 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -54,6 +54,7 @@ typedef struct nr_ho_source_cu { typedef void (*ho_req_ack_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue); typedef void (*ho_success_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue); typedef void (*ho_failure_t)(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg); +typedef void (*ho_trigger_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue); typedef struct nr_ho_target_cu { /// pointer to the (target) DU structure @@ -66,10 +67,14 @@ typedef struct nr_ho_target_cu { rnti_t new_rnti; /// Handover Preparation Buffer byte_array_t ue_ho_prep_info; + /// function pointer to trigger handover on target gNB + ho_trigger_t ho_trigger; /// function pointer to announce handover request acknowledgment ho_req_ack_t ho_req_ack; /// function pointer to announce handover success ho_success_t ho_success; + /// function pointer to announce the handover failure + ho_failure_t ho_failure; } nr_ho_target_cu_t; typedef struct nr_handover_context_s { @@ -91,4 +96,6 @@ void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len); +void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue); + #endif /* RRC_GNB_MOBILITY_H_ */