From f66524879db7344aad3bbe3165aa5f8723238264 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 14 Mar 2025 11:26:16 +0100 Subject: [PATCH] Add NGAP Handover Command decoder and RRC handler on source gNB * add NG Handover Command message decoder * process in RRC: encode RRCReconfiguration message from the received HandoverCommandMessage * trigger RRCReconfiguration for handover Co-authored-by: batuhan duyuler --- openair2/COMMON/ngap_messages_def.h | 1 + openair2/COMMON/ngap_messages_types.h | 32 +++++++ openair2/RRC/NR/MESSAGES/asn1_msg.c | 39 ++++++++ openair2/RRC/NR/MESSAGES/asn1_msg.h | 1 + openair2/RRC/NR/rrc_gNB.c | 6 ++ openair2/RRC/NR/rrc_gNB_NGAP.c | 28 ++++++ openair2/RRC/NR/rrc_gNB_NGAP.h | 3 + openair2/RRC/NR/rrc_gNB_mobility.c | 3 +- openair2/RRC/NR/rrc_gNB_mobility.h | 2 + openair3/NGAP/ngap_gNB_handlers.c | 56 +++++++++++- .../NGAP/ngap_gNB_management_procedures.c | 8 ++ .../NGAP/ngap_gNB_management_procedures.h | 2 + openair3/NGAP/ngap_gNB_mobility_management.c | 88 +++++++++++++++++++ openair3/NGAP/ngap_gNB_mobility_management.h | 2 + 14 files changed, 269 insertions(+), 2 deletions(-) diff --git a/openair2/COMMON/ngap_messages_def.h b/openair2/COMMON/ngap_messages_def.h index e9e185e152..826c892533 100644 --- a/openair2/COMMON/ngap_messages_def.h +++ b/openair2/COMMON/ngap_messages_def.h @@ -85,6 +85,7 @@ MESSAGE_DEF(NGAP_PDUSESSION_MODIFY_REQ , MESSAGE_PRIORITY_MED, ngap_pd MESSAGE_DEF(NGAP_PDUSESSION_RELEASE_COMMAND , MESSAGE_PRIORITY_MED, ngap_pdusession_release_command_t , ngap_pdusession_release_command) 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) /* 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) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index 5a5937500d..5d53c5c822 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -69,6 +69,7 @@ #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_HANDOVER_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_command #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 @@ -648,6 +649,37 @@ typedef struct { byte_array_t target2source; } ngap_handover_request_ack_t; +/* 9.3.4.10 3GPP TS 38.413 */ +typedef struct { + // QoS Flow to be Forwarded List + uint8_t nb_of_qos_flow; + pdusession_associate_qosflow_t qos_setup_list[QOSFLOW_MAX_VALUE]; + // UL Forwarding UP TNL Information + uint32_t gtp_teid; + transport_layer_addr_t gNB_addr; +} ho_command_transfer_t; + +typedef struct { + // PDU Session ID (M) + uint8_t pdusession_id; + // Handover Command Transfer (M) + ho_command_transfer_t ho_command_transfer; +} pdusession_resource_handover_t; + +typedef struct { + // AMF UE NGAP ID + uint64_t amf_ue_ngap_id; + // RAN UE NGAP ID + uint32_t gNB_ue_ngap_id; + // Handover Type + ho_type_t handoverType; + // PDU Session Resource Handover List + uint8_t nb_of_pdusessions; + pdusession_resource_handover_t pdu_sessions[NGAP_MAX_PDU_SESSION]; + // Target to Source Transparent Container + byte_array_t handoverCommand; +} ngap_handover_command_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/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index fc3ca002f4..20bf56390b 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -64,6 +64,10 @@ #include "NR_LogicalChannelConfig.h" #include "NR_PDCP-Config.h" #include "NR_MAC-CellGroupConfig.h" +#include "NR_HandoverPreparationInformation.h" +#include "NR_HandoverCommand.h" +#include "NR_HandoverCommand-IEs.h" +#include "NR_AS-Config.h" #include "NR_SecurityModeCommand.h" #include "NR_CipheringAlgorithm.h" #include "NR_RRCReconfiguration-IEs.h" @@ -1221,6 +1225,41 @@ void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *currentMC, byte_ } } +int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, const byte_array_t handoverCommand) +{ + // Decode Handover Command + NR_HandoverCommand_t *hoCommand = NULL; + asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_HandoverCommand, (void **)&hoCommand, handoverCommand.buf, handoverCommand.len); + + if (dec_rval.code != RC_OK && dec_rval.consumed == 0) { + LOG_E(NR_RRC, "Can not decode Handover Command!\n"); + return -1; + } + + // Prepare DL DCCH message for RRCReconfiguration + NR_DL_DCCH_Message_t dl_dcch_msg = {0}; + asn_enc_rval_t enc_rval; + dl_dcch_msg.message.present = NR_DL_DCCH_MessageType_PR_c1; + asn1cCalloc(dl_dcch_msg.message.choice.c1, c1); + c1->present = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; + asn1cCalloc(c1->choice.rrcReconfiguration, rrcReconf); + + // Decode RRCReconfiguration from handoverCommandMessage + OCTET_STRING_t *ho = &hoCommand->criticalExtensions.choice.c1->choice.handoverCommand->handoverCommandMessage; + uper_decode_complete(NULL, &asn_DEF_NR_RRCReconfiguration, (void **)&rrcReconf, ho->buf, ho->size); + if (LOG_DEBUGFLAG(DEBUG_ASN1)) + xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg); + + // Encode DL DCCH message in buffer + enc_rval = uper_encode_to_buffer(&asn_DEF_NR_DL_DCCH_Message, NULL, &dl_dcch_msg, ba->buf, ba->len); + AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_DL_DCCH_Message, &dl_dcch_msg); + ASN_STRUCT_FREE(asn_DEF_NR_HandoverCommand, hoCommand); + + return ((enc_rval.encoded + 7) / 8); +} + byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params) { // Encode RRCReconfiguration to buffer diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 668f2f8ade..665edeef4b 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -165,5 +165,6 @@ int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi); byte_array_t get_HandoverPreparationInformation(nr_rrc_reconfig_param_t *params, int scell_pci); byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params); void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *measConfig, byte_array_t prep_info); +int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, byte_array_t handoverCommand); #endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */ diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 48ea8e760e..32021d7f22 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -98,6 +98,7 @@ #include "NR_DL-DCCH-Message.h" #include "ds/byte_array.h" #include "alg/find.h" +#include "NR_HandoverCommand.h" #ifdef E2_AGENT #include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h" @@ -2949,6 +2950,11 @@ void *rrc_gnb_task(void *args_p) { rrc_gNB_free_Handover_Request(&NGAP_HANDOVER_REQUEST(msg_p)); // Free transfered NG message break; + case NGAP_HANDOVER_COMMAND: + rrc_gNB_process_HandoverCommand(RC.nrrrc[instance], &NGAP_HANDOVER_COMMAND(msg_p)); + rrc_gNB_free_Handover_Command(&NGAP_HANDOVER_COMMAND(msg_p)); // Free transfered NG message + break; + default: LOG_E(NR_RRC, "[gNB %ld] Received unexpected message %s\n", instance, msg_name_p); break; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 19cd12704f..889cd810b2 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -39,6 +39,9 @@ #include #include "E1AP_ConfidentialityProtectionIndication.h" #include "E1AP_IntegrityProtectionIndication.h" +#include "NR_UE-CapabilityRAT-ContainerList.h" +#include "NR_HandoverCommand.h" +#include "NR_HandoverCommand-IEs.h" #include "NGAP_CauseRadioNetwork.h" #include "NGAP_Dynamic5QIDescriptor.h" #include "NGAP_GTPTunnel.h" @@ -1161,6 +1164,31 @@ void rrc_gNB_free_Handover_Request(ngap_handover_request_t *msg) free(msg->mobility_restriction); } +/** @brief Process NG Handover Command on Source gNB */ +void rrc_gNB_process_HandoverCommand(gNB_RRC_INST *rrc, const ngap_handover_command_t *msg) +{ + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, msg->gNB_ue_ngap_id); + + if (ue_context_p == NULL) { + LOG_W(NR_RRC, "Unknown UE context associated to gNB_ue_ngap_id (%u)\n", msg->gNB_ue_ngap_id); + return; + } + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + + uint8_t buffer[NR_RRC_BUF_SIZE]; + byte_array_t ba = {.buf = buffer, .len = sizeof(buffer)}; + int enc = doRRCReconfiguration_from_HandoverCommand(&ba, msg->handoverCommand); + DevAssert(enc > 0); + + rrc_gNB_trigger_reconfiguration_for_handover(rrc, UE, buffer, enc); + LOG_A(NR_RRC, "Send reconfiguration (HO Command) to UE %u/RNTI %04x\n", UE->rrc_ue_id, UE->rnti); +} + +void rrc_gNB_free_Handover_Command(ngap_handover_command_t *msg) +{ + free_byte_array(msg->handoverCommand); +} + /* * 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 9d51db4186..c249711629 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -101,4 +101,7 @@ 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); +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); + #endif diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index bf6ecb5865..9e35b976e0 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -231,7 +231,8 @@ static void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id, data->modification_req->rrc_container = &ba; data->rrc->mac_rrc.ue_context_modification_request(data->assoc_id, data->modification_req); } -static void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len) + +void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len) { f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id); diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index 4f31c433ec..5a6542132c 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -89,4 +89,6 @@ void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, int serving_pci, const nr_neighbour_cell_t *neighbour_config); +void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len); + #endif /* RRC_GNB_MOBILITY_H_ */ diff --git a/openair3/NGAP/ngap_gNB_handlers.c b/openair3/NGAP/ngap_gNB_handlers.c index 485d37236a..226c8e8fa0 100644 --- a/openair3/NGAP/ngap_gNB_handlers.c +++ b/openair3/NGAP/ngap_gNB_handlers.c @@ -997,6 +997,60 @@ static int ngap_gNB_handle_pdusession_setup_request(sctp_assoc_t assoc_id, uint3 return 0; } +/** @brief Handler for the NG Handover Command + * AMF -> source NG-RAN */ +static int ngap_gNB_handle_handover_command(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu) +{ + NGAP_INFO("Received NG Handover Command\n"); + ngap_gNB_amf_data_t *amf_desc_p = NULL; + DevAssert(pdu != NULL); + + if ((amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0)) == NULL) { + NGAP_ERROR( + "[SCTP %u] Received Handover Command for non " + "existing AMF context\n", + assoc_id); + return -1; + } + + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_COMMAND); + ngap_handover_command_t *msg = &NGAP_HANDOVER_COMMAND(message_p); + memset(msg, 0, sizeof(*msg)); + if (decode_ng_handover_command(msg, pdu) < 0) { + NGAP_ERROR("Failed to decode NG Handover Command"); + free_ng_handover_command(msg); + 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] Received HO Command Msg for non existing UE context (gNB_ue_ngap_id %d) \n", + assoc_id, + msg->gNB_ue_ngap_id); + free_ng_handover_command(msg); + itti_free(TASK_NGAP, message_p); + return -1; + } + + ue_desc_p->rx_stream = stream; + + if (ue_desc_p->amf_ue_ngap_id != msg->amf_ue_ngap_id) { + NGAP_ERROR("UE context amf_ue_ngap_id is different form that of the message (%ld != %ld)", + ue_desc_p->amf_ue_ngap_id, + msg->amf_ue_ngap_id); + free_ng_handover_command(msg); + itti_free(TASK_NGAP, message_p); + return -1; + } + + NGAP_DEBUG("Handover Command is parsed with a size of: %ld. Sending to RRC GNB!\n", msg->handoverCommand.len); + itti_send_msg_to_task(TASK_RRC_GNB, amf_desc_p->ngap_gNB_instance->instance, message_p); + + 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; @@ -1338,7 +1392,7 @@ const ngap_message_decoded_callback ngap_messages_callback[][3] = { {ngap_gNB_handle_error_indication, 0, 0}, /* ErrorIndication */ {0, 0, 0}, /* HandoverCancel */ {0, 0, 0}, /* HandoverNotification */ - {0, 0, 0}, /* HandoverPreparation */ + {0, ngap_gNB_handle_handover_command, 0}, /* HandoverPreparation */ {ngap_gNB_handle_handover_request, 0, 0}, /* HandoverResourceAllocation */ {ngap_gNB_handle_initial_context_request, 0, 0}, /* InitialContextSetup */ {0, 0, 0}, /* InitialUEMessage */ diff --git a/openair3/NGAP/ngap_gNB_management_procedures.c b/openair3/NGAP/ngap_gNB_management_procedures.c index 6ffce99d9c..9d8748a88b 100644 --- a/openair3/NGAP/ngap_gNB_management_procedures.c +++ b/openair3/NGAP/ngap_gNB_management_procedures.c @@ -200,3 +200,11 @@ void tnl_to_bitstring(BIT_STRING_t *out, const transport_layer_addr_t in) out->bits_unused = 0; } } + +void bitstring_to_tnl(transport_layer_addr_t *out, const BIT_STRING_t in) +{ + if (in.size) { + memcpy(out->buffer, in.buf, in.size); + out->length = in.size; + } +} diff --git a/openair3/NGAP/ngap_gNB_management_procedures.h b/openair3/NGAP/ngap_gNB_management_procedures.h index 365fd0dd9d..28f4b24e7e 100644 --- a/openair3/NGAP/ngap_gNB_management_procedures.h +++ b/openair3/NGAP/ngap_gNB_management_procedures.h @@ -50,4 +50,6 @@ void ngap_gNB_prepare_internal_data(void); void tnl_to_bitstring(BIT_STRING_t *out, const transport_layer_addr_t in); +void bitstring_to_tnl(transport_layer_addr_t *out, const BIT_STRING_t in); + #endif /* NGAP_GNB_MANAGEMENT_PROCEDURES_H_ */ diff --git a/openair3/NGAP/ngap_gNB_mobility_management.c b/openair3/NGAP/ngap_gNB_mobility_management.c index 4c64659e01..68a6823d98 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.c +++ b/openair3/NGAP/ngap_gNB_mobility_management.c @@ -472,3 +472,91 @@ void free_ng_handover_req_ack(ngap_handover_request_ack_t *msg) { free_byte_array(msg->target2source); } + +/** @brief Decoder for the NG Handover Command */ +int decode_ng_handover_command(ngap_handover_command_t *msg, NGAP_NGAP_PDU_t *pdu) +{ + DevAssert(pdu != NULL); + NGAP_HandoverCommandIEs_t *ie; + NGAP_HandoverCommand_t *container = &pdu->choice.successfulOutcome->value.choice.HandoverCommand; + + // AMF UE NGAP ID (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true); + asn_INTEGER2ulong(&(ie->value.choice.AMF_UE_NGAP_ID), &msg->amf_ue_ngap_id); + + // RAN UE NGAP ID (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID, true); + msg->gNB_ue_ngap_id = ie->value.choice.RAN_UE_NGAP_ID; + + // Handover Type (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_HandoverType, true); + if (ie->value.choice.HandoverType != HANDOVER_TYPE_INTRA5GS) { + NGAP_ERROR("Only Intra 5GS Handover is supported at the moment!\n"); + return -1; + } + + // PDU Session Resource Handover List (O) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_HandoverType, false); + if (ie != NULL) { + msg->nb_of_pdusessions = ie->value.choice.PDUSessionResourceHandoverList.list.count; + + for (int i = 0; i < msg->nb_of_pdusessions; ++i) { + NGAP_PDUSessionResourceHandoverItem_t *item = ie->value.choice.PDUSessionResourceHandoverList.list.array[i]; + + // PDU Session ID (M) + msg->pdu_sessions[i].pdusession_id = item->pDUSessionID; + + // Handover Command Transfer (M) + NGAP_HandoverCommandTransfer_t *hoCommandTransfer = NULL; + asn_dec_rval_t dec_rval = uper_decode_complete(NULL, + &asn_DEF_NGAP_HandoverCommandTransfer, + (void **)&hoCommandTransfer, + item->handoverCommandTransfer.buf, + item->handoverCommandTransfer.size); + if (dec_rval.code != RC_OK) { + NGAP_ERROR("Failed to decode handoverCommandTransfer\n"); + continue; + } + + // DL Forwarding UP TNL Information (O) + if (hoCommandTransfer->dLForwardingUP_TNLInformation) { + NGAP_UPTransportLayerInformation_t *up_tnl = hoCommandTransfer->dLForwardingUP_TNLInformation; + if (hoCommandTransfer->dLForwardingUP_TNLInformation->present == NGAP_UPTransportLayerInformation_PR_gTPTunnel) { + OCTET_STRING_TO_INT32(&(up_tnl->choice.gTPTunnel->gTP_TEID), msg->pdu_sessions[i].ho_command_transfer.gtp_teid); + bitstring_to_tnl(&msg->pdu_sessions[i].ho_command_transfer.gNB_addr, up_tnl->choice.gTPTunnel->transportLayerAddress); + } else { + NGAP_WARN("Missing DL Forwarding UP TNL Information\n"); + } + } + ASN_STRUCT_FREE(asn_DEF_NGAP_HandoverCommandTransfer, hoCommandTransfer); + } + } + + // Target to Source Transparent Container (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverCommandIEs_t, + ie, + container, + NGAP_ProtocolIE_ID_id_TargetToSource_TransparentContainer, + true); + NGAP_TargetToSource_TransparentContainer_t *target2source = &(ie->value.choice.TargetToSource_TransparentContainer); + NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer_t *transparentContainer = NULL; + asn_dec_rval_t dec_rval = aper_decode_complete(NULL, + &asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer, + (void **)&transparentContainer, + target2source->buf, + target2source->size); + if (dec_rval.code != RC_OK) { + NGAP_ERROR("Failed to decode TargetToSource_TransparentContainer\n"); + return -1; + } + + msg->handoverCommand = create_byte_array(transparentContainer->rRCContainer.size, transparentContainer->rRCContainer.buf); + ASN_STRUCT_FREE(asn_DEF_NGAP_TargetNGRANNode_ToSourceNGRANNode_TransparentContainer, transparentContainer); + + return 0; +} + +void free_ng_handover_command(ngap_handover_command_t *msg) +{ + free_byte_array(msg->handoverCommand); +} diff --git a/openair3/NGAP/ngap_gNB_mobility_management.h b/openair3/NGAP/ngap_gNB_mobility_management.h index dd0061ca35..4ed3229a84 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.h +++ b/openair3/NGAP/ngap_gNB_mobility_management.h @@ -31,5 +31,7 @@ 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); +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); #endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */