diff --git a/openair2/COMMON/ngap_messages_def.h b/openair2/COMMON/ngap_messages_def.h index 826c892533..fc07cffc6d 100644 --- a/openair2/COMMON/ngap_messages_def.h +++ b/openair2/COMMON/ngap_messages_def.h @@ -74,6 +74,7 @@ MESSAGE_DEF(NGAP_PDUSESSION_RELEASE_RESPONSE , MESSAGE_PRIORITY_MED, ngap_pdu 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) 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) /* 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 5d53c5c822..eb21935e03 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -70,6 +70,7 @@ #define NGAP_HANDOVER_REQUEST(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_request #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_UE_CONTEXT_RELEASE_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_ue_release_req #define NGAP_PDUSESSION_RELEASE_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_command @@ -680,6 +681,23 @@ typedef struct { byte_array_t handoverCommand; } ngap_handover_command_t; +/* 9.3.1.16 3GPP TS 38.413 */ +typedef struct { + // NR user location information + target_ran_node_id_t target_ng_ran; + uint32_t nrCellIdentity; +} user_location_information_t; + +/* 9.2.3.7 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; + // User Location Information + user_location_information_t user_info; +} ngap_handover_notify_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 889cd810b2..e7459b1d49 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1334,6 +1334,28 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_U itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p); } +/** @brief Prepare NG Handover Notify message and inform NGAP */ +void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) +{ + LOG_I(NR_RRC, "Triggering NGAP Handover Notify\n"); + + MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_NOTIFY); + ngap_handover_notify_t *ho_notify = &NGAP_HANDOVER_NOTIFY(msg_p); + memset(ho_notify, 0, sizeof(*ho_notify)); + nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, rrc->nr_cellid); + + ho_notify->gNB_ue_ngap_id = UE->rrc_ue_id; + ho_notify->amf_ue_ngap_id = UE->amf_ue_ngap_id; + ho_notify->user_info.nrCellIdentity = rrc->nr_cellid; + ho_notify->user_info.target_ng_ran.tac = *du->setup_req->cell->info.tac; + ho_notify->user_info.target_ng_ran.targetgNBId = rrc->node_id; + ho_notify->user_info.target_ng_ran.plmn_identity.mcc = du->setup_req->cell->info.plmn.mcc; + ho_notify->user_info.target_ng_ran.plmn_identity.mnc = du->setup_req->cell->info.plmn.mnc; + ho_notify->user_info.target_ng_ran.plmn_identity.mnc_digit_length = du->setup_req->cell->info.plmn.mnc_digit_length; + + itti_send_msg_to_task(TASK_NGAP, rrc->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; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h index c249711629..4d44b7197c 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -104,4 +104,6 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_U void rrc_gNB_process_HandoverCommand(gNB_RRC_INST *rrc, const ngap_handover_command_t *msg); 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); + #endif diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 9e35b976e0..9e76217dc9 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -476,6 +476,13 @@ static void nr_rrc_n2_ho_acknowledge(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) free_byte_array(hoCommand); } +/** @brief This callback is used by the target gNB + * to trigger the Handover Notify towards the AMF */ +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 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 */ diff --git a/openair3/NGAP/ngap_gNB.c b/openair3/NGAP/ngap_gNB.c index e80b914871..272759de4c 100644 --- a/openair3/NGAP/ngap_gNB.c +++ b/openair3/NGAP/ngap_gNB.c @@ -419,6 +419,50 @@ static int ngap_gNB_handover_request_acknowledge(instance_t instance, ngap_hando return 0; } +static int ngap_gNB_handover_notify(instance_t instance, const ngap_handover_notify_t *msg) +{ + LOG_D(NGAP, "Encode NGAP Handover Notify\n"); + ngap_gNB_ue_context_t *ue_context_p = NULL; + byte_array_t ba = { .buf = NULL, .len = 0}; + + /* Retrieve the NGAP gNB instance associated with Mod_id */ + ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance); + DevAssert(msg != NULL); + DevAssert(ngap_gNB_instance_p != NULL); + + if ((ue_context_p = ngap_get_ue_context(msg->gNB_ue_ngap_id)) == NULL) { + NGAP_ERROR("Failed to encode Handover Notify: no ue context associated with 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 Notify: unknown 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 Notify: unknown amf_ue_ngap_id=%ld\n", msg->amf_ue_ngap_id); + return -1; + } + + NGAP_NGAP_PDU_t *pdu = encode_ng_handover_notify(msg); + + if (ngap_gNB_encode_pdu(pdu, &ba.buf, (uint32_t *)&ba.len) < 0) { + NGAP_ERROR("Failed to encode NG Handover Notify message\n"); + ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu); + return -1; + } + + /* UE associated signalling -> use the allocated 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; +} + void *ngap_gNB_process_itti_msg(void *notUsed) { MessageDef *received_msg = NULL; int result; @@ -509,6 +553,10 @@ void *ngap_gNB_process_itti_msg(void *notUsed) { free_ng_handover_req_ack(&NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(received_msg)); break; + case NGAP_HANDOVER_NOTIFY: + ngap_gNB_handover_notify(instance, &NGAP_HANDOVER_NOTIFY(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_encoder.c b/openair3/NGAP/ngap_gNB_encoder.c index 056aea2fe6..16954744e2 100644 --- a/openair3/NGAP/ngap_gNB_encoder.c +++ b/openair3/NGAP/ngap_gNB_encoder.c @@ -53,7 +53,8 @@ static inline int ngap_gNB_encode_initiating(NGAP_NGAP_PDU_t *pdu, uint8_t **buf NGAP_ProcedureCode_id_UEContextReleaseRequest, NGAP_ProcedureCode_id_PathSwitchRequest, NGAP_ProcedureCode_id_PDUSessionResourceModifyIndication, - NGAP_ProcedureCode_id_HandoverPreparation}; + NGAP_ProcedureCode_id_HandoverPreparation, + NGAP_ProcedureCode_id_HandoverNotification}; int i; for (i = 0; i < sizeofArray(tmp); i++) if (pdu->choice.initiatingMessage->procedureCode == tmp[i]) diff --git a/openair3/NGAP/ngap_gNB_mobility_management.c b/openair3/NGAP/ngap_gNB_mobility_management.c index 68a6823d98..565be7acd5 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.c +++ b/openair3/NGAP/ngap_gNB_mobility_management.c @@ -560,3 +560,60 @@ void free_ng_handover_command(ngap_handover_command_t *msg) { free_byte_array(msg->handoverCommand); } + +NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg) +{ + NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu)); + + // Message Type (M) + pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, head); + head->procedureCode = NGAP_ProcedureCode_id_HandoverNotification; + head->criticality = NGAP_Criticality_ignore; + head->value.present = NGAP_InitiatingMessage__value_PR_HandoverNotify; + NGAP_HandoverNotify_t *out = &head->value.choice.HandoverNotify; + + // AMF UE NGAP ID (M) + { + asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverNotifyIEs_t, ie); + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_HandoverNotifyIEs__value_PR_AMF_UE_NGAP_ID; + asn_uint642INTEGER(&ie->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id); + } + + // RAN UE NGAP ID (M) + { + asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverNotifyIEs_t, ie); + ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_HandoverNotifyIEs__value_PR_RAN_UE_NGAP_ID; + ie->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id; + } + + // User Location Information (M) + { + asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverNotifyIEs_t, ie); + ie->id = NGAP_ProtocolIE_ID_id_UserLocationInformation; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_HandoverNotifyIEs__value_PR_UserLocationInformation; + ie->value.choice.UserLocationInformation.present = NGAP_UserLocationInformation_PR_userLocationInformationNR; + asn1cCalloc(ie->value.choice.UserLocationInformation.choice.userLocationInformationNR, userinfo_nr_p); + // NR User Location Information + const target_ran_node_id_t *target = &msg->user_info.target_ng_ran; + // CGI (M) + MACRO_GNB_ID_TO_CELL_IDENTITY(target->targetgNBId, msg->user_info.nrCellIdentity, &userinfo_nr_p->nR_CGI.nRCellIdentity); + MCC_MNC_TO_TBCD(target->plmn_identity.mcc, + target->plmn_identity.mnc, + target->plmn_identity.mnc_digit_length, + &userinfo_nr_p->nR_CGI.pLMNIdentity); + // TAI (M) + INT24_TO_OCTET_STRING(target->tac, &userinfo_nr_p->tAI.tAC); + MCC_MNC_TO_PLMNID(target->plmn_identity.mcc, + target->plmn_identity.mnc, + target->plmn_identity.mnc_digit_length, + &userinfo_nr_p->tAI.pLMNIdentity); + } + + return pdu; +} diff --git a/openair3/NGAP/ngap_gNB_mobility_management.h b/openair3/NGAP/ngap_gNB_mobility_management.h index 4ed3229a84..d31bdb0caa 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.h +++ b/openair3/NGAP/ngap_gNB_mobility_management.h @@ -33,5 +33,6 @@ NGAP_NGAP_PDU_t *encode_ng_handover_request_ack(ngap_handover_request_ack_t *msg void free_ng_handover_req_ack(ngap_handover_request_ack_t *msg); int decode_ng_handover_command(ngap_handover_command_t *msg, NGAP_NGAP_PDU_t *pdu); void free_ng_handover_command(ngap_handover_command_t *msg); +NGAP_NGAP_PDU_t *encode_ng_handover_notify(const ngap_handover_notify_t *msg); #endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */ diff --git a/openair3/NGAP/ngap_msg_includes.h b/openair3/NGAP/ngap_msg_includes.h index a80df29401..4224660974 100644 --- a/openair3/NGAP/ngap_msg_includes.h +++ b/openair3/NGAP/ngap_msg_includes.h @@ -101,5 +101,6 @@ #include "NGAP_HandoverRequestAcknowledgeTransfer.h" #include "NGAP_QosFlowItemWithDataForwarding.h" #include "NR_HandoverPreparationInformation.h" +#include "NGAP_HandoverNotify.h" #endif // NGAP_MSG_INCLUDES_H