Commit 85f586c7 authored by Guido Casati's avatar Guido Casati

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.
parent 4facb4d6
......@@ -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)
......@@ -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;
......
......@@ -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;
......
......@@ -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);
......
......@@ -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);
......
......@@ -463,6 +463,57 @@ static int ngap_gNB_handover_notify(instance_t instance, const ngap_handover_not
return 0;
}
/** @brief NGAP: Handover Cancel callback */
static int ngap_gNB_handover_cancel(instance_t instance, const ngap_handover_cancel_t *msg)
{
DevAssert(msg != NULL);
byte_array_t ba = {.buf = NULL, .len = 0};
// Retrieve NGAP gNB instance
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
DevAssert(ngap_gNB_instance_p != NULL);
// we must have the UE context by RAN UE NGAP ID
ngap_gNB_ue_context_t *ue_context_p = ngap_get_ue_context(msg->gNB_ue_ngap_id);
if (ue_context_p == NULL) {
NGAP_ERROR("Failed to encode Handover Cancel: no UE context for gNB_ue_ngap_id=%d\n", msg->gNB_ue_ngap_id);
return -1;
}
if (ue_context_p->gNB_ue_ngap_id != msg->gNB_ue_ngap_id) {
NGAP_ERROR("Failed to encode Handover Cancel: mismatched gNB_ue_ngap_id=%d\n", msg->gNB_ue_ngap_id);
return -1;
}
if (ue_context_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
NGAP_ERROR("Failed to encode Handover Cancel: mismatched amf_ue_ngap_id=%ld\n", msg->amf_ue_ngap_id);
return -1;
}
/* Encode NGAP PDU */
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_cancel(msg);
if (pdu == NULL) {
NGAP_ERROR("Failed to encode NG Handover Cancel\n");
return -1;
}
if (ngap_gNB_encode_pdu(pdu, &ba.buf, (uint32_t *)&ba.len) < 0) {
NGAP_ERROR("Failed to encode NG Handover Cancel message PDU\n");
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
return -1;
}
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
/* UE-associated signalling -> use the UE's allocated SCTP stream */
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
ue_context_p->amf_ref->assoc_id,
ba.buf,
ba.len,
ue_context_p->tx_stream);
return 0;
}
int ngap_gNB_handle_ul_ran_status_transfer(instance_t instance, const ngap_ran_status_transfer_t *msg)
{
NGAP_DEBUG("Triggered UL RAN Status Transfer\n");
......@@ -587,6 +638,10 @@ void *ngap_gNB_process_itti_msg(void *notUsed) {
ngap_gNB_handover_notify(instance, &NGAP_HANDOVER_NOTIFY(received_msg));
break;
case NGAP_HANDOVER_CANCEL:
ngap_gNB_handover_cancel(instance, &NGAP_HANDOVER_CANCEL(received_msg));
break;
case NGAP_UL_RAN_STATUS_TRANSFER:
ngap_gNB_handle_ul_ran_status_transfer(instance, &NGAP_UL_RAN_STATUS_TRANSFER(received_msg));
break;
......
......@@ -1051,6 +1051,46 @@ static int ngap_gNB_handle_handover_command(sctp_assoc_t assoc_id, uint32_t stre
return 0;
}
/** @brief Handler for NG Handover Cancel Acknowledge */
static int ngap_gNB_handle_handover_cancel_ack(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
{
NGAP_INFO("Received NG Handover Cancel Acknowledge\n");
ngap_gNB_amf_data_t *amf_desc_p = NULL;
DevAssert(pdu != NULL);
amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0);
if (amf_desc_p == NULL) {
NGAP_ERROR("[SCTP %u] Received Handover Cancel Ack for non-existing AMF context\n", assoc_id);
return -1;
}
MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_CANCEL_ACK);
ngap_handover_cancel_ack_t *msg = &NGAP_HANDOVER_CANCEL_ACK(message_p);
if (decode_ng_handover_cancel_ack(msg, pdu) < 0) {
NGAP_ERROR("Failed to decode NG Handover Cancel Acknowledge\n");
itti_free(TASK_NGAP, message_p);
return -1;
}
ngap_gNB_ue_context_t *ue_desc_p = ngap_get_ue_context(msg->gNB_ue_ngap_id);
if (!ue_desc_p) {
NGAP_ERROR("[SCTP %u] Handover Cancel Ack for unknown UE context (gNB_ue_ngap_id %d)\n", assoc_id, msg->gNB_ue_ngap_id);
itti_free(TASK_NGAP, message_p);
return -1;
}
if (ue_desc_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) {
NGAP_WARN("AMF UE NGAP ID mismatch in HO Cancel Ack (ctx %ld != msg %ld)", ue_desc_p->amf_ue_ngap_id, msg->amf_ue_ngap_id);
}
ue_desc_p->rx_stream = stream;
// cancel was already handled at RRC level, do not forward
return 0;
}
static int ngap_gNB_handle_paging(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu)
{
ngap_gNB_amf_data_t *amf_desc_p = NULL;
......@@ -1484,7 +1524,7 @@ const ngap_message_decoded_callback ngap_messages_callback[][3] = {
{ngap_gNB_handle_dl_ran_status_transfer, 0, 0}, /* DownlinkRANStatusTransfer */
{0, 0, 0}, /* DownlinkUEAssociatedNRPPaTransport */
{ngap_gNB_handle_error_indication, 0, 0}, /* ErrorIndication */
{0, 0, 0}, /* HandoverCancel */
{0, ngap_gNB_handle_handover_cancel_ack, 0}, /* HandoverCancel */
{0, 0, 0}, /* HandoverNotification */
{0, ngap_gNB_handle_handover_command, 0}, /* HandoverPreparation */
{ngap_gNB_handle_handover_request, 0, 0}, /* HandoverResourceAllocation */
......
......@@ -618,6 +618,65 @@ NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg)
return pdu;
}
/** @brief NGAP Handover Cancel encoding (9.2.3.11 3GPP TS 38.413 Handover Cancel)
* 8.4.5 Handover Cancellation (source NG-RAN -> AMF) */
NGAP_NGAP_PDU_t *encode_ng_handover_cancel(const ngap_handover_cancel_t *msg)
{
DevAssert(msg != NULL);
NGAP_NGAP_PDU_t *pdu = calloc_or_fail(1, sizeof(*pdu));
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
asn1cCalloc(pdu->choice.initiatingMessage, head);
head->procedureCode = NGAP_ProcedureCode_id_HandoverCancel;
head->criticality = NGAP_Criticality_reject;
head->value.present = NGAP_InitiatingMessage__value_PR_HandoverCancel;
NGAP_HandoverCancel_t *out = &head->value.choice.HandoverCancel;
// AMF UE NGAP ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverCancelIEs_t, ie1);
ie1->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
ie1->criticality = NGAP_Criticality_reject;
ie1->value.present = NGAP_HandoverCancelIEs__value_PR_AMF_UE_NGAP_ID;
asn_uint642INTEGER(&ie1->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
// RAN UE NGAP ID (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverCancelIEs_t, ie2);
ie2->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
ie2->criticality = NGAP_Criticality_reject;
ie2->value.present = NGAP_HandoverCancelIEs__value_PR_RAN_UE_NGAP_ID;
ie2->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
// Cause (M)
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverCancelIEs_t, ie3);
ie3->id = NGAP_ProtocolIE_ID_id_Cause;
ie3->criticality = NGAP_Criticality_reject;
ie3->value.present = NGAP_HandoverCancelIEs__value_PR_Cause;
encode_ngap_cause(&ie3->value.choice.Cause, &msg->cause);
return pdu;
}
/** @brief Decode NGAP Handover Cancel Acknowledge (9.2.3.12 3GPP TS 38.413) */
int decode_ng_handover_cancel_ack(ngap_handover_cancel_ack_t *out, const NGAP_NGAP_PDU_t *pdu)
{
DevAssert(out != NULL);
DevAssert(pdu != NULL);
NGAP_HandoverCancelAcknowledge_t *container = &pdu->choice.successfulOutcome->value.choice.HandoverCancelAcknowledge;
NGAP_HandoverCancelAcknowledgeIEs_t *ie;
/* AMF UE NGAP ID (M) */
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCancelAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
asn_INTEGER2ulong(&ie->value.choice.AMF_UE_NGAP_ID, &out->amf_ue_ngap_id);
/* RAN UE NGAP ID (M) */
NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCancelAcknowledgeIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true);
out->gNB_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID;
return 0;
}
/** @brief Encode NGAP UL RAN Status Transfer (9.2.3.14 of 3GPP TS 38.413) */
NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer_t *msg)
{
......
......@@ -35,5 +35,7 @@ int decode_ng_handover_command(ngap_handover_command_t *msg, NGAP_NGAP_PDU_t *pd
void free_ng_handover_command(ngap_handover_command_t *msg);
NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg);
NGAP_NGAP_PDU_t *encode_ng_ul_ran_status_transfer(const ngap_ran_status_transfer_t *msg);
NGAP_NGAP_PDU_t *encode_ng_handover_cancel(const ngap_handover_cancel_t *msg);
int decode_ng_handover_cancel_ack(ngap_handover_cancel_ack_t *out, const NGAP_NGAP_PDU_t *pdu);
#endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment