Add N2 Handover Cancel (8.4.5 of 3GPP TS 38.413)

This commit completes the NGAP Mobility Management procedure for N2 handover.
Upon failure on source gNB the Handover Cancel message is sent to the AMF which
in return responds with an ack.
This commit is contained in:
Guido Casati
2025-09-18 11:34:31 +02:00
parent 4facb4d635
commit 85f586c76d
9 changed files with 215 additions and 1 deletions
+2
View File
@@ -75,6 +75,7 @@ MESSAGE_DEF(NGAP_HANDOVER_REQUIRED, MESSAGE_PRIORITY_MED, ngap_handover_required
MESSAGE_DEF(NGAP_HANDOVER_FAILURE, MESSAGE_PRIORITY_MED, ngap_handover_failure_t, ngap_handover_failure)
MESSAGE_DEF(NGAP_HANDOVER_REQUEST_ACKNOWLEDGE, MESSAGE_PRIORITY_MED, ngap_handover_request_ack_t, ngap_handover_request_ack)
MESSAGE_DEF(NGAP_HANDOVER_NOTIFY, MESSAGE_PRIORITY_MED, ngap_handover_notify_t, ngap_handover_notify)
MESSAGE_DEF(NGAP_HANDOVER_CANCEL, MESSAGE_PRIORITY_MED, ngap_handover_cancel_t, ngap_handover_cancel)
MESSAGE_DEF(NGAP_UL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_ul_ran_status_transfer)
MESSAGE_DEF(NGAP_DL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_dl_ran_status_transfer)
@@ -89,6 +90,7 @@ MESSAGE_DEF(NGAP_PDUSESSION_RELEASE_COMMAND , MESSAGE_PRIORITY_MED, ngap_pdu
MESSAGE_DEF(NGAP_UE_CONTEXT_RELEASE_COMMAND, MESSAGE_PRIORITY_MED, ngap_ue_release_command_t , ngap_ue_release_command)
MESSAGE_DEF(NGAP_HANDOVER_REQUEST, MESSAGE_PRIORITY_MED, ngap_handover_request_t, ngap_handover_request)
MESSAGE_DEF(NGAP_HANDOVER_COMMAND, MESSAGE_PRIORITY_MED, ngap_handover_command_t, ngap_handover_command)
MESSAGE_DEF(NGAP_HANDOVER_CANCEL_ACK, MESSAGE_PRIORITY_MED, ngap_handover_cancel_ack_t, ngap_handover_cancel_ack)
/* NGAP <-> RRC messages (can be initiated either by MME or gNB) */
MESSAGE_DEF(NGAP_UE_CONTEXT_RELEASE_REQ , MESSAGE_PRIORITY_MED, ngap_ue_release_req_t , ngap_ue_release_req)
+20
View File
@@ -71,6 +71,8 @@
#define NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_request_ack
#define NGAP_HANDOVER_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_command
#define NGAP_HANDOVER_NOTIFY(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_notify
#define NGAP_HANDOVER_CANCEL(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel
#define NGAP_HANDOVER_CANCEL_ACK(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel_ack
#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
@@ -701,6 +703,24 @@ typedef struct {
user_location_information_t user_info;
} ngap_handover_notify_t;
/* 9.2.3.11 3GPP TS 38.413 */
typedef struct {
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
// Cause
ngap_cause_t cause;
} ngap_handover_cancel_t;
/* 9.2.3.12 3GPP TS 38.413 */
typedef struct {
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
} ngap_handover_cancel_ack_t;
typedef struct ngap_ue_cap_info_ind_s {
uint32_t gNB_ue_ngap_id;
byte_array_t ue_radio_cap;
+24
View File
@@ -1403,6 +1403,30 @@ void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
}
/** @brief Prepare NG Handover Cancel (source NG-RAN) and inform NGAP (N2-based HO only)
* 3GPP TS 38.413 §8.4.5 (Handover Cancellation) - source NG-RAN -> AMF
* 3GPP TS 38.413 §9.2.3.11 (HANDOVER CANCEL)
* @param rrc CU/RRC instance
* @param UE UE context
* @param cause cancellation cause */
void rrc_gNB_send_NGAP_HANDOVER_CANCEL(int module_id, gNB_RRC_UE_t *UE, ngap_cause_t cause)
{
DevAssert(UE != NULL);
LOG_I(NR_RRC, "Triggering NGAP Handover Cancel (gNB_ue_ngap_id=%d, amf_ue_ngap_id=%ld)\n", UE->rrc_ue_id, UE->amf_ue_ngap_id);
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_CANCEL);
ngap_handover_cancel_t *ho_cancel = &NGAP_HANDOVER_CANCEL(msg_p);
memset(ho_cancel, 0, sizeof(*ho_cancel));
/* Mandatory IEs (38.413 §9.2.3.11) */
ho_cancel->gNB_ue_ngap_id = UE->rrc_ue_id;
ho_cancel->amf_ue_ngap_id = UE->amf_ue_ngap_id;
ho_cancel->cause = cause;
itti_send_msg_to_task(TASK_NGAP, module_id, msg_p);
}
void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid)
{
int pdu_sessions_released = 0;
+1
View File
@@ -105,6 +105,7 @@ void rrc_gNB_process_HandoverCommand(gNB_RRC_INST *rrc, const ngap_handover_comm
void rrc_gNB_free_Handover_Command(ngap_handover_command_t *msg);
void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE);
void rrc_gNB_send_NGAP_HANDOVER_CANCEL(int module_id, gNB_RRC_UE_t *UE, ngap_cause_t cause);
int rrc_gNB_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const int n_to_mod, const e1_pdcp_status_info_t *pdcp_status);
+11
View File
@@ -489,6 +489,16 @@ static void nr_rrc_n2_ho_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
rrc_gNB_send_NGAP_HANDOVER_NOTIFY(rrc, UE);
}
/** @brief This callback is used by the source gNB to inform the AMF
* about the cancellation of an ongoing handover */
static void nr_rrc_n2_ho_cancel(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
DevAssert(UE->ho_context);
DevAssert(UE->ho_context->source);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_CANCELLED};
rrc_gNB_send_NGAP_HANDOVER_CANCEL(rrc->module_id, UE, cause);
}
/** @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 */
@@ -546,6 +556,7 @@ void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc,
ue->ho_context = alloc_ho_ctx(HO_CTX_SOURCE);
ue->ho_context->source->du = get_du_for_ue(rrc, ue->rrc_ue_id);
ue->ho_context->source->ho_status_transfer = rrc_gNB_send_NGAP_ul_ran_status_transfer;
ue->ho_context->source->ho_cancel = nr_rrc_n2_ho_cancel;
rrc_gNB_send_NGAP_HANDOVER_REQUIRED(rrc, ue, neighbour_config, hoPrepInfo);
free_byte_array(hoPrepInfo);