From 92abefb815a4f7067b9f0e93a8e0445fb750724e Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 7 Mar 2025 12:15:20 +0100 Subject: [PATCH] Add NGAP Handover Failure message This message is sent from the Target NG-RAN to the AMF. The commit introduces: * NGAP encoding function * N2 callback for the target gNB * handle the failure in RRC, inform NGAP * send NGAP message to AMF via SCTP Note: UE context in NGAP is fetched from the amf_ue_ngap_id Co-authored-by: batuhan duyuler --- openair2/COMMON/ngap_messages_def.h | 1 + openair2/COMMON/ngap_messages_types.h | 10 +++++ openair2/RRC/NR/rrc_gNB_NGAP.c | 10 ++++- openair2/RRC/NR/rrc_gNB_NGAP.h | 2 + openair2/RRC/NR/rrc_gNB_mobility.c | 13 ++++++ openair2/RRC/NR/rrc_gNB_mobility.h | 2 + openair3/NGAP/ngap_gNB.c | 44 ++++++++++++++++++++ openair3/NGAP/ngap_gNB_mobility_management.c | 33 +++++++++++++++ openair3/NGAP/ngap_gNB_mobility_management.h | 1 + openair3/NGAP/ngap_gNB_ue_context.c | 6 +++ openair3/NGAP/ngap_gNB_ue_context.h | 1 + 11 files changed, 122 insertions(+), 1 deletion(-) diff --git a/openair2/COMMON/ngap_messages_def.h b/openair2/COMMON/ngap_messages_def.h index eea62d6c00..9cf975d34a 100644 --- a/openair2/COMMON/ngap_messages_def.h +++ b/openair2/COMMON/ngap_messages_def.h @@ -72,6 +72,7 @@ MESSAGE_DEF(NGAP_PDUSESSION_SETUP_RESP , MESSAGE_PRIORITY_MED, ngap_pdu MESSAGE_DEF(NGAP_PDUSESSION_MODIFY_RESP , MESSAGE_PRIORITY_MED, ngap_pdusession_modify_resp_t , ngap_pdusession_modify_resp) MESSAGE_DEF(NGAP_PDUSESSION_RELEASE_RESPONSE , MESSAGE_PRIORITY_MED, ngap_pdusession_release_resp_t , ngap_pdusession_release_resp) MESSAGE_DEF(NGAP_HANDOVER_REQUIRED, MESSAGE_PRIORITY_MED, ngap_handover_required_t, ngap_handover_required) +MESSAGE_DEF(NGAP_HANDOVER_FAILURE, MESSAGE_PRIORITY_MED, ngap_handover_failure_t, ngap_handover_failure) /* NGAP -> RRC messages */ MESSAGE_DEF(NGAP_DOWNLINK_NAS , MESSAGE_PRIORITY_MED, ngap_downlink_nas_t , ngap_downlink_nas ) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index 57ecc51e79..9a3e7a9a45 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -66,6 +66,8 @@ #define NGAP_PDUSESSION_MODIFY_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_modify_req #define NGAP_PAGING_IND(mSGpTR) (mSGpTR)->ittiMsg.ngap_paging_ind #define NGAP_HANDOVER_REQUIRED(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_required +#define NGAP_HANDOVER_FAILURE(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_failure + #define NGAP_UE_CONTEXT_RELEASE_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_ue_release_req #define NGAP_PDUSESSION_RELEASE_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_command #define NGAP_PDUSESSION_RELEASE_RESPONSE(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_resp @@ -548,6 +550,14 @@ typedef struct { source_to_target_transparent_container_t *source2target; } ngap_handover_required_t; +/* 3GPP TS 38.413 9.2.3.6 */ +typedef struct { + // AMF UE NGAP ID (M) + uint64_t amf_ue_ngap_id; + // Cause (M) + ngap_cause_t cause; +} ngap_handover_failure_t; + typedef struct ngap_ue_cap_info_ind_s { uint32_t gNB_ue_ngap_id; byte_array_t ue_radio_cap; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 5632e5932b..9b4096da4b 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1063,7 +1063,15 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_REQ(MessageDef *msg_p, instance_t in } } -//----------------------------------------------------------------------------- +/** @brief Sends the NG Handover Failure from the Target NG-RAN to the AMF */ +void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure_t *msg) +{ + LOG_I(NR_RRC, "Send NG Handover Failure message (amf_ue_ngap_id %ld) with cause %d \n ", msg->amf_ue_ngap_id, msg->cause.value); + MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_FAILURE); + NGAP_HANDOVER_FAILURE(msg_p) = *msg; + itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p); +} + /* * Process the NG command NGAP_UE_CONTEXT_RELEASE_COMMAND, sent by AMF. * The gNB should remove all pdu session, NG context, and other context of the UE. diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h index fd8cb31ff1..7c52843c0f 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -94,4 +94,6 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc, const nr_neighbour_cell_t *neighbourCellConfiguration, const byte_array_t hoPrepInfo); +void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure_t *msg); + #endif diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index fa2eead5ca..7b1324d6e5 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -406,6 +406,19 @@ static byte_array_t rrc_gNB_generate_HandoverPreparationInformation(gNB_RRC_INST return hoPrepInfo; } +/** @brief Callback function to trigger NG Handover Failure on the target gNB, to inform the AMF + * that the preparation of resources has failed (e.g. unsatisfied criteria, gNB is already loaded). + * This message represents an Unsuccessful Outcome of the Handover Resource Allocation */ +void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg) +{ + LOG_I(NR_RRC, "Triggering N2 Handover Failure\n"); + rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, msg); + LOG_I(NR_RRC, "Send UE Context Release for gnb_ue_id %d\n", gnb_ue_id); + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, gnb_ue_id); + rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(rrc->module_id, ue_context_p, msg->cause); + return; +} + /** @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 3169ea23c0..0bb5faf7fa 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -53,6 +53,7 @@ typedef struct nr_ho_source_cu { /* acknowledgement of handover request. buf+len is the RRC Reconfiguration */ 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 struct nr_ho_target_cu { /// pointer to the (target) DU structure const nr_rrc_du_container_t *du; @@ -78,6 +79,7 @@ nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type); void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du); void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue); +void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg); void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, diff --git a/openair3/NGAP/ngap_gNB.c b/openair3/NGAP/ngap_gNB.c index 4b4fc2f850..58511f307c 100644 --- a/openair3/NGAP/ngap_gNB.c +++ b/openair3/NGAP/ngap_gNB.c @@ -334,6 +334,46 @@ void ngap_gNB_init(void) { itti_mark_task_ready(TASK_NGAP); } +static int ngap_gNB_handover_failure(instance_t instance, const ngap_handover_failure_t *msg) +{ + ngap_gNB_ue_context_t *ue_context_p = NULL; + uint8_t *buffer = NULL; + uint32_t length; + + ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance); + DevAssert(msg != NULL); + DevAssert(ngap_gNB_instance_p != NULL); + + NGAP_NGAP_PDU_t *pdu = encode_ng_handover_failure(msg); + if (!pdu) { + NGAP_ERROR("Failed to encode NG Handover Failure\n"); + ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu); + return -1; + } + + if (ngap_gNB_encode_pdu(pdu, &buffer, &length) < 0) { + NGAP_ERROR("Failed to encode HANDOVER FAILURE MESSAGE\n"); + ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu); + return -1; + } + + ngap_gNB_amf_data_t *amf = ngap_gNB_get_AMF_from_instance(ngap_gNB_instance_p); + if ((ue_context_p = ngap_get_ue_context_from_amf_ue_ngap_id(msg->amf_ue_ngap_id)) == NULL) { + //The context for this gNB ue ngap id doesn't exist in the map of gNB UEs + NGAP_WARN("Failed to find UE context associated with amf_ue_ngap_id=%ld\n", msg->amf_ue_ngap_id); + /*In this case there is no ue context since HO REQ might be failed at preprocessing stage itself. + * so sctp message send to AMF based on its assoc_id */ + ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, amf->assoc_id, buffer, length, 0); + ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu); + return -1; + } + + ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, amf->assoc_id, buffer, length, amf->nextstream); + ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, &pdu); + + return 0; +} + void *ngap_gNB_process_itti_msg(void *notUsed) { MessageDef *received_msg = NULL; int result; @@ -415,6 +455,10 @@ void *ngap_gNB_process_itti_msg(void *notUsed) { } break; + case NGAP_HANDOVER_FAILURE: + ngap_gNB_handover_failure(instance, &NGAP_HANDOVER_FAILURE(received_msg)); + break; + default: NGAP_ERROR("Received unhandled message: %d:%s\n", ITTI_MSG_ID(received_msg), ITTI_MSG_NAME(received_msg)); break; diff --git a/openair3/NGAP/ngap_gNB_mobility_management.c b/openair3/NGAP/ngap_gNB_mobility_management.c index 4f116c8f34..9d84fccd09 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.c +++ b/openair3/NGAP/ngap_gNB_mobility_management.c @@ -191,3 +191,36 @@ NGAP_NGAP_PDU_t *encode_ng_handover_required(const ngap_handover_required_t *msg return pdu; } + +NGAP_NGAP_PDU_t *encode_ng_handover_failure(const ngap_handover_failure_t *msg) +{ + NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu)); + + /* Prepare the NGAP message to encode */ + pdu->present = NGAP_NGAP_PDU_PR_unsuccessfulOutcome; + asn1cCalloc(pdu->choice.unsuccessfulOutcome, head); + head->procedureCode = NGAP_ProcedureCode_id_HandoverResourceAllocation; + head->criticality = NGAP_Criticality_reject; + head->value.present = NGAP_UnsuccessfulOutcome__value_PR_HandoverFailure; + NGAP_HandoverFailure_t *out = &head->value.choice.HandoverFailure; + + // AMF_UE_NGAP_ID (M) + { + asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverFailureIEs_t, ie); + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_HandoverFailureIEs__value_PR_AMF_UE_NGAP_ID; + asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id); + } + + // Cause (M) + { + asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverFailureIEs_t, ie); + ie->id = NGAP_ProtocolIE_ID_id_Cause; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_HandoverFailureIEs__value_PR_Cause; + encode_ngap_cause(&ie->value.choice.Cause, &msg->cause); + } + + return pdu; +} diff --git a/openair3/NGAP/ngap_gNB_mobility_management.h b/openair3/NGAP/ngap_gNB_mobility_management.h index b3926877eb..6f5cdde742 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.h +++ b/openair3/NGAP/ngap_gNB_mobility_management.h @@ -27,5 +27,6 @@ #include "ngap_messages_types.h" NGAP_NGAP_PDU_t *encode_ng_handover_required(const ngap_handover_required_t *msg); +NGAP_NGAP_PDU_t *encode_ng_handover_failure(const ngap_handover_failure_t *msg); #endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */ diff --git a/openair3/NGAP/ngap_gNB_ue_context.c b/openair3/NGAP/ngap_gNB_ue_context.c index d0949b88c2..994271d822 100644 --- a/openair3/NGAP/ngap_gNB_ue_context.c +++ b/openair3/NGAP/ngap_gNB_ue_context.c @@ -76,6 +76,12 @@ struct ngap_gNB_ue_context_s *ngap_get_ue_context(uint32_t gNB_ue_ngap_id) return RB_FIND(ngap_ue_map, &ngap_ue_head, &temp); } +ngap_gNB_ue_context_t *ngap_get_ue_context_from_amf_ue_ngap_id(uint32_t amf_ue_ngap_id) +{ + ngap_gNB_ue_context_t temp = {.amf_ue_ngap_id = amf_ue_ngap_id}; + return RB_FIND(ngap_ue_map, &ngap_ue_head, &temp); +} + struct ngap_gNB_ue_context_s *ngap_detach_ue_context(uint32_t gNB_ue_ngap_id) { struct ngap_gNB_ue_context_s *tmp = ngap_get_ue_context(gNB_ue_ngap_id); diff --git a/openair3/NGAP/ngap_gNB_ue_context.h b/openair3/NGAP/ngap_gNB_ue_context.h index aaf9e83bbc..1022f2f4c3 100644 --- a/openair3/NGAP/ngap_gNB_ue_context.h +++ b/openair3/NGAP/ngap_gNB_ue_context.h @@ -81,6 +81,7 @@ typedef struct ngap_gNB_ue_context_s { void ngap_store_ue_context(const ngap_gNB_ue_context_t *ue_desc_p); ngap_gNB_ue_context_t *ngap_get_ue_context(uint32_t gNB_ue_ngap_id); +ngap_gNB_ue_context_t *ngap_get_ue_context_from_amf_ue_ngap_id(uint32_t amf_ue_ngap_id); ngap_gNB_ue_context_t *ngap_detach_ue_context(uint32_t gNB_ue_ngap_id); #endif /* NGAP_GNB_UE_CONTEXT_H_ */