From fa75484e135c40e60c57805eb3248de522b2f1d4 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 12 Mar 2025 18:00:19 +0100 Subject: [PATCH] Add NGAP Handover Request Acknowledge encoder and RRC callback This commit completes the NG Mobility Management Procedure known as Handover Resource Allocation. The outbound message is sent by the target NG-RAN and is known as Handover Request Acknowledge. Major changes: * add RRC callback to trigger the generation of NG Handover Request Acknowledge on the target NG-RAN * add NG Handover Request Acknowledge encoder Co-authored-by: batuhan duyuler --- openair2/COMMON/ngap_messages_def.h | 1 + openair2/COMMON/ngap_messages_types.h | 31 +++++++ openair2/RRC/NR/rrc_gNB_NGAP.c | 36 ++++++++ openair2/RRC/NR/rrc_gNB_NGAP.h | 2 + openair3/NGAP/ngap_gNB.c | 50 ++++++++++ openair3/NGAP/ngap_gNB_mobility_management.c | 96 +++++++++++++++++++- openair3/NGAP/ngap_gNB_mobility_management.h | 2 + 7 files changed, 215 insertions(+), 3 deletions(-) diff --git a/openair2/COMMON/ngap_messages_def.h b/openair2/COMMON/ngap_messages_def.h index 53b16350e1..e9e185e152 100644 --- a/openair2/COMMON/ngap_messages_def.h +++ b/openair2/COMMON/ngap_messages_def.h @@ -73,6 +73,7 @@ MESSAGE_DEF(NGAP_PDUSESSION_MODIFY_RESP , MESSAGE_PRIORITY_MED, ngap_pd 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) +MESSAGE_DEF(NGAP_HANDOVER_REQUEST_ACKNOWLEDGE, MESSAGE_PRIORITY_MED, ngap_handover_request_ack_t, ngap_handover_request_ack) /* 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 49212b257c..5a5937500d 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -68,6 +68,7 @@ #define NGAP_HANDOVER_REQUIRED(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_required #define NGAP_HANDOVER_FAILURE(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_failure #define NGAP_HANDOVER_REQUEST(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_request +#define NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_request_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 @@ -617,6 +618,36 @@ typedef struct { nr_guami_t guami; } ngap_handover_request_t; +/* 9.3.4.11 3GPP TS 38.413 */ +typedef struct { + // QoS Flow Setup Response List + uint8_t nb_of_qos_flow; + pdusession_associate_qosflow_t qos_setup_list[QOSFLOW_MAX_VALUE]; + // DL NG-U UP TNL Information + uint32_t gtp_teid; + transport_layer_addr_t gNB_addr; +} ho_request_ack_transfer_t; + +/* 9.2.3.5 3GPP TS 38.413 */ +typedef struct { + // PDU Session ID (M) + uint8_t pdu_session_id; + // Handover Request Acknowledge Transfer (M) + ho_request_ack_transfer_t ack_transfer; +} pdu_session_resource_admitted_t; + +typedef struct { + // AMF UE NGAP ID + uint32_t gNB_ue_ngap_id; + // RAN UE NGAP ID + uint64_t amf_ue_ngap_id; + // PDU Session Resource Admitted List + pdu_session_resource_admitted_t pdusessions[NGAP_MAX_PDU_SESSION]; + uint8_t nb_of_pdusessions; + // Target to Source Transparent Container + byte_array_t target2source; +} ngap_handover_request_ack_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 68aa502f77..19cd12704f 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1270,6 +1270,42 @@ void rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, LOG_I(NR_RRC,"Send message to ngap: NGAP_UE_CAPABILITIES_IND\n"); } +void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, byte_array_t ho_command) +{ + LOG_D(NR_RRC, "Sending Handover Request Acknowledge\n"); + + MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_REQUEST_ACKNOWLEDGE); + ngap_handover_request_ack_t *msg = &NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(msg_p); + memset(msg, 0, sizeof(*msg)); + + // RAN UE NGAP ID + msg->gNB_ue_ngap_id = UE->rrc_ue_id; + // AMF UE NGAP ID + msg->amf_ue_ngap_id = UE->amf_ue_ngap_id; + // PDU Session Resource Admitted List + msg->nb_of_pdusessions = UE->nb_of_pdusessions; + for (int i = 0; i < UE->nb_of_pdusessions; i++) { + rrc_pdu_session_param_t *session = &UE->pduSession[i]; + session->status = PDU_SESSION_STATUS_ESTABLISHED; + // PDU Session ID + msg->pdusessions[i].pdu_session_id = session->param.pdusession_id; + // Handover Request Acknowledge Transfer + ho_request_ack_transfer_t *transfer = &msg->pdusessions[i].ack_transfer; + transfer->gtp_teid = session->param.n3_outgoing.teid; + memcpy(transfer->gNB_addr.buffer, session->param.n3_outgoing.addr.buffer, session->param.n3_outgoing.addr.length); + transfer->gNB_addr.length = session->param.n3_outgoing.addr.length; + transfer->nb_of_qos_flow = session->param.nb_qos; + for (int q = 0; q < transfer->nb_of_qos_flow; q++) { + transfer->qos_setup_list[q].qfi = session->param.qos[q].qfi; + transfer->qos_setup_list[q].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL; + } + } + // Target to Source Transparent Container + msg->target2source = copy_byte_array(ho_command); + + 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 53e2b51f8c..9d51db4186 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -99,4 +99,6 @@ void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, ngap_handover_request_t *msg); void rrc_gNB_free_Handover_Request(ngap_handover_request_t *msg); +void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, byte_array_t ho_command); + #endif diff --git a/openair3/NGAP/ngap_gNB.c b/openair3/NGAP/ngap_gNB.c index 58511f307c..e80b914871 100644 --- a/openair3/NGAP/ngap_gNB.c +++ b/openair3/NGAP/ngap_gNB.c @@ -374,6 +374,51 @@ static int ngap_gNB_handover_failure(instance_t instance, const ngap_handover_fa return 0; } +static int ngap_gNB_handover_request_acknowledge(instance_t instance, ngap_handover_request_ack_t *msg) +{ + byte_array_t ba = { .buf = NULL, .len = 0 }; + + /* Retrieve the NGAP gNB instance associated with Mod_id */ + ngap_gNB_instance_t *ngap = ngap_gNB_get_instance(instance); + DevAssert(msg != NULL); + DevAssert(ngap != NULL); + + // First, create and store NGAP UE context + ngap_gNB_ue_context_t ue_context_p = { + .amf_ref = ngap_gNB_get_AMF_from_instance(ngap), + .gNB_ue_ngap_id = msg->gNB_ue_ngap_id, + .amf_ue_ngap_id = msg->amf_ue_ngap_id, + .gNB_instance = ngap, + .ue_state = NGAP_UE_CONNECTED, + }; + if (ue_context_p.amf_ref == NULL) { + NGAP_ERROR("Failed to fetch AMF for current NGAP instance\n"); + return -1; + } + ngap_store_ue_context(&ue_context_p); + + NGAP_NGAP_PDU_t *pdu = encode_ng_handover_request_ack(msg); + if (!pdu) { + NGAP_ERROR("Failed to encode NG Handover Request Acknowledge\n"); + return -1; + } + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) + xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, pdu); + + if (ngap_gNB_encode_pdu(pdu, &ba.buf, (uint32_t *)&ba.len) < 0) { + NGAP_ERROR("Failed to encode Handover Request Acknowledge 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->instance, ue_context_p.amf_ref->assoc_id, ba.buf, ba.len, ue_context_p.tx_stream); + NGAP_INFO("Sent Handover Request Acknowledge to AMF\n"); + + return 0; +} + void *ngap_gNB_process_itti_msg(void *notUsed) { MessageDef *received_msg = NULL; int result; @@ -459,6 +504,11 @@ void *ngap_gNB_process_itti_msg(void *notUsed) { ngap_gNB_handover_failure(instance, &NGAP_HANDOVER_FAILURE(received_msg)); break; + case NGAP_HANDOVER_REQUEST_ACKNOWLEDGE: + ngap_gNB_handover_request_acknowledge(instance, &NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(received_msg)); + free_ng_handover_req_ack(&NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(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 2aa0056dfe..4c64659e01 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.c +++ b/openair3/NGAP/ngap_gNB_mobility_management.c @@ -22,6 +22,7 @@ #include #include #include +#include "conversions.h" #include "ngap_common.h" #include "ngap_msg_includes.h" #include "ngap_gNB_defs.h" @@ -377,8 +378,97 @@ int decode_ng_handover_request(ngap_handover_request_t *out, const NGAP_NGAP_PDU return -1; } } - - - return 0; } + +NGAP_NGAP_PDU_t *encode_ng_handover_request_ack(ngap_handover_request_ack_t *msg) +{ + NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu)); + + pdu->present = NGAP_NGAP_PDU_PR_successfulOutcome; + asn1cCalloc(pdu->choice.successfulOutcome, head); + head->procedureCode = NGAP_ProcedureCode_id_HandoverResourceAllocation; + head->criticality = NGAP_Criticality_reject; + head->value.present = NGAP_SuccessfulOutcome__value_PR_HandoverRequestAcknowledge; + NGAP_HandoverRequestAcknowledge_t *out = &head->value.choice.HandoverRequestAcknowledge; + + // AMF UE NGAP ID (M) + { + asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie); + ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__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_HandoverRequestAcknowledgeIEs_t, ie); + ie->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_RAN_UE_NGAP_ID; + ie->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id; + } + + // Target to Source Transparent Container (M) + { + asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie); + ie->id = NGAP_ProtocolIE_ID_id_TargetToSource_TransparentContainer; + ie->criticality = NGAP_Criticality_reject; + ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_TargetToSource_TransparentContainer; + NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer_t *t2s = calloc_or_fail(1, sizeof(*t2s)); + // RRC Container (M) + { + // Encode Handover Command + t2s->rRCContainer.size = msg->target2source.len; + t2s->rRCContainer.buf = malloc_or_fail(msg->target2source.len); + memcpy(t2s->rRCContainer.buf, msg->target2source.buf, msg->target2source.len); + byte_array_t ba = { .buf = NULL, .len = 0 }; + ba.len = aper_encode_to_new_buffer(&asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer, NULL, t2s, (void **)&ba.buf); + OCTET_STRING_fromBuf(&ie->value.choice.TargetToSource_TransparentContainer, (const char *)ba.buf, ba.len); + ASN_STRUCT_FREE(asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer, t2s); + free_byte_array(ba); + } + } + + // PDU Session Resource Admitted List (M) + { + asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequestAcknowledgeIEs_t, ie); + ie->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceAdmittedList; + ie->criticality = NGAP_Criticality_ignore; + ie->value.present = NGAP_HandoverRequestAcknowledgeIEs__value_PR_PDUSessionResourceAdmittedList; + + for (int pduSesIdx = 0; pduSesIdx < msg->nb_of_pdusessions; pduSesIdx++) { + asn1cSequenceAdd(ie->value.choice.PDUSessionResourceAdmittedList.list, NGAP_PDUSessionResourceAdmittedItem_t, item); + item->pDUSessionID = msg->pdusessions[pduSesIdx].pdu_session_id; + /* dLQosFlowPerTNLInformation */ + NGAP_HandoverRequestAcknowledgeTransfer_t transfer = {0}; + transfer.dL_NGU_UP_TNLInformation.present = NGAP_UPTransportLayerInformation_PR_gTPTunnel; + asn1cCalloc(transfer.dL_NGU_UP_TNLInformation.choice.gTPTunnel, tmp); + GTP_TEID_TO_ASN1(msg->pdusessions[pduSesIdx].ack_transfer.gtp_teid, &tmp->gTP_TEID); + tnl_to_bitstring(&tmp->transportLayerAddress, msg->pdusessions[pduSesIdx].ack_transfer.gNB_addr); + + for (int j = 0; j < msg->pdusessions[pduSesIdx].ack_transfer.nb_of_qos_flow; j++) { + asn1cSequenceAdd(transfer.qosFlowSetupResponseList.list, NGAP_QosFlowItemWithDataForwarding_t, qosItem); + qosItem->qosFlowIdentifier = msg->pdusessions[pduSesIdx].ack_transfer.qos_setup_list[j].qfi; + qosItem->dataForwardingAccepted = malloc_or_fail(sizeof(*qosItem->dataForwardingAccepted)); + *qosItem->dataForwardingAccepted = NGAP_DataForwardingAccepted_data_forwarding_accepted; + } + + void *buf; + ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_NGAP_HandoverRequestAcknowledgeTransfer, NULL, &transfer, &buf); + AssertFatal(encoded > 0, "ASN1 message encoding failed !\n"); + item->handoverRequestAcknowledgeTransfer.buf = buf; + item->handoverRequestAcknowledgeTransfer.size = encoded; + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_HandoverRequestAcknowledgeTransfer, &transfer); + } + } + + return pdu; +} + +void free_ng_handover_req_ack(ngap_handover_request_ack_t *msg) +{ + free_byte_array(msg->target2source); +} diff --git a/openair3/NGAP/ngap_gNB_mobility_management.h b/openair3/NGAP/ngap_gNB_mobility_management.h index 66e85e644e..dd0061ca35 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.h +++ b/openair3/NGAP/ngap_gNB_mobility_management.h @@ -29,5 +29,7 @@ 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); int decode_ng_handover_request(ngap_handover_request_t *out, const NGAP_NGAP_PDU_t *pdu); +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); #endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */