diff --git a/openair2/COMMON/ngap_messages_def.h b/openair2/COMMON/ngap_messages_def.h index 9cf975d34a..53b16350e1 100644 --- a/openair2/COMMON/ngap_messages_def.h +++ b/openair2/COMMON/ngap_messages_def.h @@ -83,6 +83,7 @@ MESSAGE_DEF(NGAP_PDUSESSION_SETUP_REQ , MESSAGE_PRIORITY_MED, ngap_pd MESSAGE_DEF(NGAP_PDUSESSION_MODIFY_REQ , MESSAGE_PRIORITY_MED, ngap_pdusession_modify_req_t , ngap_pdusession_modify_req ) 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) /* 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 9a3e7a9a45..49212b257c 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -67,6 +67,7 @@ #define NGAP_PAGING_IND(mSGpTR) (mSGpTR)->ittiMsg.ngap_paging_ind #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_UE_CONTEXT_RELEASE_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_ue_release_req #define NGAP_PDUSESSION_RELEASE_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_command @@ -229,6 +230,15 @@ typedef struct ngap_mobility_restriction_s{ plmn_id_t serving_plmn; }ngap_mobility_restriction_t; +/* PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */ +typedef struct { + uint8_t nb_qos; + pdusession_level_qos_parameter_t qos[QOSFLOW_MAX_VALUE]; + pdu_session_type_t pdu_session_type; + // UPF endpoint of the NG-U (N3) transport bearer + gtpu_tunnel_t n3_incoming; +} pdusession_transfer_t; + typedef enum pdusession_qosflow_mapping_ind_e{ QOSFLOW_MAPPING_INDICATION_UL = 0, QOSFLOW_MAPPING_INDICATION_DL = 1, @@ -558,6 +568,55 @@ typedef struct { ngap_cause_t cause; } ngap_handover_failure_t; +typedef struct { + // Next-Hop NH + uint8_t next_hop[SECURITY_KEY_LENGTH]; + // Next Hop Chaining Count + uint8_t next_hop_chain_count; +} ngap_security_context_t; + +/* Handover Request (3GPP TS 38.413 9.2.3.4) + PDU Session Resource Setup Item */ +typedef struct { + // PDU Session ID + uint8_t pdusession_id; + pdu_session_type_t pdu_session_type; + // S-NSSAI + nssai_t nssai; + // Handover Required Transfer + pdusession_transfer_t pdusessionTransfer; +} ho_request_pdusession_t; + +/* 3GPP TS 38.413 9.2.3.4 */ +typedef struct { + // AMF UE NGAP ID + uint64_t amf_ue_ngap_id; + // Handover Type + ho_type_t ho_type; + // Cause + ngap_cause_t cause; + // UE Aggregate Maximum Bit Rate + ngap_ambr_t ue_ambr; + // UE Security Capabilities + ngap_security_capabilities_t security_capabilities; + // Security Context + ngap_security_context_t security_context; + // PDU Session Resource Setup List + uint8_t nb_of_pdusessions; + ho_request_pdusession_t pduSessionResourceSetupList[NGAP_MAX_PDU_SESSION]; + // Allowed NSSAI + uint8_t nb_allowed_nssais; + nssai_t allowed_nssai[8]; + // Source to Target Transparent Container contents + uint64_t nr_cell_id; + byte_array_t ue_ho_prep_info; + byte_array_t ue_cap; + // Mobility Restriction List + ngap_mobility_restriction_t *mobility_restriction; + // GUAMI + nr_guami_t guami; +} ngap_handover_request_t; + typedef struct ngap_ue_cap_info_ind_s { uint32_t gNB_ue_ngap_id; byte_array_t ue_radio_cap; @@ -628,15 +687,6 @@ typedef struct ngap_downlink_nas_s { byte_array_t nas_pdu; } ngap_downlink_nas_t; -/* PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */ -typedef struct { - uint8_t nb_qos; - pdusession_level_qos_parameter_t qos[QOSFLOW_MAX_VALUE]; - pdu_session_type_t pdu_session_type; - // UPF endpoint of the NG-U (N3) transport bearer - gtpu_tunnel_t n3_incoming; -} pdusession_transfer_t; - /* PDU Session Resource Setup/Modify Request Item */ typedef struct { int pdusession_id; diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index a1918bdd34..bbaaaf5774 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -127,5 +127,6 @@ f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qo void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration); NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish); NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish); +void activate_srb(gNB_RRC_UE_t *UE, int srb_id); #endif diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index ad36b9c10d..48ea8e760e 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -354,7 +354,7 @@ void freeSRBlist(NR_SRB_ToAddModList_t *l) ASN_STRUCT_FREE(asn_DEF_NR_SRB_ToAddModList, l); } -static void activate_srb(gNB_RRC_UE_t *UE, int srb_id) +void activate_srb(gNB_RRC_UE_t *UE, int srb_id) { AssertFatal(srb_id == 1 || srb_id == 2, "handling only SRB 1 or 2\n"); if (UE->Srb[srb_id].Active == 1) { @@ -2944,6 +2944,11 @@ void *rrc_gnb_task(void *args_p) { rrc_gNB_process_PAGING_IND(msg_p, instance); break; + case NGAP_HANDOVER_REQUEST: + rrc_gNB_process_Handover_Request(RC.nrrrc[instance], instance, &NGAP_HANDOVER_REQUEST(msg_p)); + rrc_gNB_free_Handover_Request(&NGAP_HANDOVER_REQUEST(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 9b4096da4b..68aa502f77 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -83,6 +83,8 @@ #include "rrc_messages_types.h" #include "s1ap_messages_types.h" #include "uper_encoder.h" +#include "rrc_gNB_mobility.h" +#include "rrc_gNB_du.h" #ifdef E2_AGENT #include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h" @@ -163,18 +165,24 @@ static nr_guami_t get_guami(const uint32_t amf_Id, const plmn_id_t plmn) return guami; } +/** @brief Copy NGAP PDU Session Transfer item to RRC pdusession_t struct */ +static void cp_pdusession_transfer_to_pdusession(pdusession_t *dst, const pdusession_transfer_t *src) +{ + dst->nb_qos = src->nb_qos; + for (uint8_t i = 0; i < src->nb_qos && i < QOSFLOW_MAX_VALUE; ++i) { + dst->qos[i] = src->qos[i]; + } + dst->pdu_session_type = src->pdu_session_type; + dst->n3_incoming = src->n3_incoming; +} + /** @brief Copy NGAP PDU Session Resource item to RRC pdusession_t struct */ static void cp_pdusession_resource_item_to_pdusession(pdusession_t *dst, const pdusession_resource_item_t *src) { dst->pdusession_id = src->pdusession_id; dst->nas_pdu = src->nas_pdu; - dst->nb_qos = src->pdusessionTransfer.nb_qos; - for (uint8_t i = 0; i < src->pdusessionTransfer.nb_qos && i < QOSFLOW_MAX_VALUE; ++i) { - dst->qos[i] = src->pdusessionTransfer.qos[i]; - } - dst->pdu_session_type = src->pdusessionTransfer.pdu_session_type; - dst->n3_incoming = src->pdusessionTransfer.n3_incoming; dst->nssai = src->nssai; + cp_pdusession_transfer_to_pdusession(dst, &src->pdusessionTransfer); } /** @@ -1072,6 +1080,87 @@ void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p); } +/** @brief Process NG Handover Request message (8.4.2.2 3GPP TS 38.413) */ +int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, ngap_handover_request_t *msg) +{ + LOG_I(NR_RRC, "Received Handover Request (on PCI=%lu) \n", msg->nr_cell_id); + struct nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, msg->nr_cell_id); + if (du == NULL) { + /* Cell Not Found! Return HO Request Failure*/ + LOG_E(RRC, "Failed to process Handover Request: no DU found with PCI=%lu \n", msg->nr_cell_id); + ngap_handover_failure_t fail = { + .amf_ue_ngap_id = msg->amf_ue_ngap_id, + .cause.type = NGAP_CAUSE_RADIO_NETWORK, + .cause.value = NGAP_CAUSE_RADIO_NETWORK_RADIO_RESOURCES_NOT_AVAILABLE, + }; + rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail); + return -1; + } + + // Create UE context + sctp_assoc_t curr_assoc_id = du->assoc_id; + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_create_ue_context(curr_assoc_id, UINT16_MAX, rrc, UINT64_MAX, UINT32_MAX); + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + + // allocate context for target + if (UE->ho_context != NULL) { + LOG_E(NR_RRC, "Ongoing handover for UE %d, cannot trigger new\n", UE->rrc_ue_id); + return -1; + } + UE->ho_context = alloc_ho_ctx(HO_CTX_TARGET); + + // Store IDs in UE context + UE->amf_ue_ngap_id = msg->amf_ue_ngap_id; + UE->ue_guami = msg->guami; + UE->ho_context->target->ue_ho_prep_info = copy_byte_array(msg->ue_ho_prep_info); + // store the received UE Security Capabilities in the UE context + FREE_AND_ZERO_BYTE_ARRAY(UE->ue_cap_buffer); + UE->ue_cap_buffer = copy_byte_array(msg->ue_cap); + + /* store the received Security Context in the UE context + and take it into use as defined in TS 33.501 */ + set_UE_security_algos(rrc, UE, &msg->security_capabilities); + UE->nh_ncc = msg->security_context.next_hop_chain_count; + memcpy(UE->nh, msg->security_context.next_hop, SECURITY_KEY_LENGTH); + // Reset KgNB + memset(UE->kgnb, 0, SECURITY_KEY_LENGTH); + // Derive KgNB* + const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; + uint32_t ssb_arfcn = get_ssb_arfcn(du); + nr_derive_key_ng_ran_star(cell_info->nr_pci, ssb_arfcn, UE->nh, UE->kgnb); + UE->as_security_active = true; + // Activate SRBs + activate_srb(UE, SRB1); + activate_srb(UE, SRB2); + nr_rrc_pdcp_config_security(UE, false); + + // Copy PDU Session Resource Setup item to RRC struct and do PDU Session Resource Setup procedure + pdusession_t to_setup = { + .nssai = msg->pduSessionResourceSetupList->nssai, + .pdu_session_type = msg->pduSessionResourceSetupList->pdu_session_type , + .pdusession_id = msg->pduSessionResourceSetupList->pdusession_id, + }; + cp_pdusession_transfer_to_pdusession(&to_setup, &msg->pduSessionResourceSetupList->pdusessionTransfer); + if (!trigger_bearer_setup(rrc, UE, msg->nb_of_pdusessions, &to_setup, msg->ue_ambr.br_dl)) { + LOG_E(NR_RRC, "Failed to establish PDU session: handover failed\n"); + ngap_handover_failure_t fail = { + .amf_ue_ngap_id = msg->amf_ue_ngap_id, + .cause.type = NGAP_CAUSE_RADIO_NETWORK, + .cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM, + }; + rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, &fail); + } + + return 0; +} + +void rrc_gNB_free_Handover_Request(ngap_handover_request_t *msg) +{ + free_byte_array(msg->ue_cap); + free_byte_array(msg->ue_ho_prep_info); + free(msg->mobility_restriction); +} + /* * 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 7c52843c0f..53e2b51f8c 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -96,4 +96,7 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc, void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure_t *msg); +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); + #endif diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 7b1324d6e5..c9d60422e8 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -57,6 +57,8 @@ nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type) static void free_ho_ctx(nr_handover_context_t *ho_ctx) { free(ho_ctx->source); + if (ho_ctx->target) + FREE_AND_ZERO_BYTE_ARRAY(ho_ctx->target->ue_ho_prep_info); free(ho_ctx->target); free(ho_ctx); } diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index 0bb5faf7fa..77b90ac3e0 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -63,6 +63,8 @@ typedef struct nr_ho_target_cu { uint32_t du_ue_id; /// new (target) RNTI (as for du_ue_id) rnti_t new_rnti; + /// Handover Preparation Buffer + byte_array_t ue_ho_prep_info; /// function pointer to announce handover request acknowledgment ho_req_ack_t ho_req_ack; /// function pointer to announce handover success diff --git a/openair3/NGAP/ngap_common.c b/openair3/NGAP/ngap_common.c index b51c8e80de..9e3e2e9d13 100644 --- a/openair3/NGAP/ngap_common.c +++ b/openair3/NGAP/ngap_common.c @@ -66,6 +66,43 @@ void encode_ngap_cause(NGAP_Cause_t *out, const ngap_cause_t *in) } } +ngap_cause_t decode_ngap_cause(const NGAP_Cause_t *in) +{ + ngap_cause_t out = {0}; + switch (in->present) { + case NGAP_Cause_PR_radioNetwork: + out.type = NGAP_CAUSE_RADIO_NETWORK; + out.value = in->choice.radioNetwork; + break; + + case NGAP_Cause_PR_transport: + out.type = NGAP_CAUSE_TRANSPORT; + out.value = in->choice.transport; + break; + + case NGAP_Cause_PR_nas: + out.type = NGAP_CAUSE_NAS; + out.value = in->choice.nas; + break; + + case NGAP_Cause_PR_protocol: + out.type = NGAP_CAUSE_PROTOCOL; + out.value = in->choice.protocol; + break; + + case NGAP_Cause_PR_misc: + out.type = NGAP_CAUSE_MISC; + out.value = in->choice.misc; + break; + + default: + out.type = NGAP_CAUSE_NOTHING; + NGAP_ERROR("Unknown failure cause %d\n", in->present); + break; + } + return out; +} + nr_guami_t decode_ngap_guami(const NGAP_GUAMI_t *in) { nr_guami_t out = {0}; @@ -141,3 +178,96 @@ void encode_ngap_nr_cgi(NGAP_NR_CGI_t *out, const plmn_id_t *plmn, const uint32_ MCC_MNC_TO_PLMNID(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &out->pLMNIdentity); NR_CELL_ID_TO_BIT_STRING(cell_id, &out->nRCellIdentity); } + +pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params) +{ + pdusession_level_qos_parameter_t out = {0}; + // QFI + out.qfi = qfi; + AssertFatal(params != NULL, "QoS parameters are NULL\n"); + // QosCharacteristics + const NGAP_QosCharacteristics_t *qosChar = ¶ms->qosCharacteristics; + AssertFatal(qosChar != NULL, "QoS characteristics are NULL\n"); + if (qosChar->present == NGAP_QosCharacteristics_PR_nonDynamic5QI) { + AssertFatal(qosChar->choice.nonDynamic5QI != NULL, "nonDynamic5QI is NULL\n"); + out.fiveQI_type = NON_DYNAMIC; + out.fiveQI = qosChar->choice.nonDynamic5QI->fiveQI; + } else if (qosChar->present == NGAP_QosCharacteristics_PR_dynamic5QI) { + AssertFatal(qosChar->choice.dynamic5QI != NULL, "dynamic5QI is NULL\n"); + out.fiveQI_type = DYNAMIC; + out.fiveQI = *qosChar->choice.dynamic5QI->fiveQI; + } else { + AssertFatal(0, "Unsupported QoS Characteristics present value: %d\n", qosChar->present); + } + // Allocation and Retention Priority + const NGAP_AllocationAndRetentionPriority_t *arp = ¶ms->allocationAndRetentionPriority; + out.allocation_retention_priority.priority_level = arp->priorityLevelARP; + out.allocation_retention_priority.pre_emp_capability = arp->pre_emptionCapability; + out.allocation_retention_priority.pre_emp_vulnerability = arp->pre_emptionVulnerability; + + return out; +} + +static gtpu_tunnel_t decode_TNLInformation(const NGAP_GTPTunnel_t *gTPTunnel_p) +{ + gtpu_tunnel_t out = {0}; + // Transport layer address + memcpy(out.addr.buffer, gTPTunnel_p->transportLayerAddress.buf, gTPTunnel_p->transportLayerAddress.size); + out.addr.length = gTPTunnel_p->transportLayerAddress.size - gTPTunnel_p->transportLayerAddress.bits_unused; + // GTP tunnel endpoint ID + OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, out.teid); + return out; +} + +void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf) +{ + void *decoded = NULL; + asn_codec_ctx_t ctx = {.max_stack_size = 100 * 1000}; + asn_dec_rval_t rval = aper_decode(&ctx, td, &decoded, buf.buf, buf.size, 0, 0); + if (rval.code != RC_OK) { + NGAP_ERROR("Decoding failed for %s\n", td->name); + return NULL; + } + return decoded; +} + +/** @brief Decode PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */ +bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in) +{ + void *decoded = decode_pdusession_transfer(&asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, in); + if (!decoded) { + LOG_E(NR_RRC, "Failed to decode PDUSessionResourceSetupRequestTransfer\n"); + return false; + } + + NGAP_PDUSessionResourceSetupRequestTransfer_t *pdusessionTransfer = (NGAP_PDUSessionResourceSetupRequestTransfer_t *)decoded; + for (int i = 0; i < pdusessionTransfer->protocolIEs.list.count; i++) { + NGAP_PDUSessionResourceSetupRequestTransferIEs_t *pdusessionTransfer_ies = pdusessionTransfer->protocolIEs.list.array[i]; + switch (pdusessionTransfer_ies->id) { + // UL NG-U UP TNL Information (Mandatory) + case NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLInformation: + out->n3_incoming = decode_TNLInformation(pdusessionTransfer_ies->value.choice.UPTransportLayerInformation.choice.gTPTunnel); + break; + + // PDU Session Type (Mandatory) + case NGAP_ProtocolIE_ID_id_PDUSessionType: + out->pdu_session_type = pdusessionTransfer_ies->value.choice.PDUSessionType; + break; + + // QoS Flow Setup Request List (Mandatory) + case NGAP_ProtocolIE_ID_id_QosFlowSetupRequestList: + out->nb_qos = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count; + for (int i = 0; i < out->nb_qos; i++) { + NGAP_QosFlowSetupRequestItem_t *item = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.array[i]; + out->qos[i] = fill_qos(item->qosFlowIdentifier, &item->qosFlowLevelQosParameters); + } + break; + + default: + LOG_D(NR_RRC, "Unhandled optional IE %ld\n", pdusessionTransfer_ies->id); + } + } + ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, pdusessionTransfer); + + return true; +} diff --git a/openair3/NGAP/ngap_common.h b/openair3/NGAP/ngap_common.h index 1e53d9dd1a..9afaf47c4b 100644 --- a/openair3/NGAP/ngap_common.h +++ b/openair3/NGAP/ngap_common.h @@ -83,6 +83,7 @@ typedef int (*ngap_message_decoded_callback)(sctp_assoc_t assoc_id, uint32_t str void encode_ngap_cause(NGAP_Cause_t *out, const ngap_cause_t *in); nr_guami_t decode_ngap_guami(const NGAP_GUAMI_t *in); +ngap_cause_t decode_ngap_cause(const NGAP_Cause_t *in); ngap_ambr_t decode_ngap_UEAggregateMaximumBitRate(const NGAP_UEAggregateMaximumBitRate_t *in); nssai_t decode_ngap_nssai(const NGAP_S_NSSAI_t *in); ngap_security_capabilities_t decode_ngap_security_capabilities(const NGAP_UESecurityCapabilities_t *in); @@ -90,6 +91,8 @@ ngap_mobility_restriction_t decode_ngap_mobility_restriction(const NGAP_Mobility void encode_ngap_target_id(NGAP_HandoverRequiredIEs_t *out, const target_ran_node_id_t *in); void encode_ngap_nr_cgi(NGAP_NR_CGI_t *out, const plmn_id_t *plmn, const uint32_t cell_id); pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params); +void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf); +bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in); /** @}*/ diff --git a/openair3/NGAP/ngap_gNB_decoder.c b/openair3/NGAP/ngap_gNB_decoder.c index 58c70011cf..a56357b55a 100644 --- a/openair3/NGAP/ngap_gNB_decoder.c +++ b/openair3/NGAP/ngap_gNB_decoder.c @@ -94,6 +94,12 @@ static int ngap_gNB_decode_initiating_message(NGAP_NGAP_PDU_t *pdu) { NGAP_INFO("TODO ErrorIndication initiating message\n"); break; + case NGAP_ProcedureCode_id_HandoverResourceAllocation: + res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu); + free(res.buffer); + NGAP_INFO("Handover Resource Allocation initiating message\n"); + break; + default: NGAP_ERROR("Unknown procedure ID (%d) for initiating message\n", (int)pdu->choice.initiatingMessage->procedureCode); diff --git a/openair3/NGAP/ngap_gNB_encoder.c b/openair3/NGAP/ngap_gNB_encoder.c index a6e91ba9c8..056aea2fe6 100644 --- a/openair3/NGAP/ngap_gNB_encoder.c +++ b/openair3/NGAP/ngap_gNB_encoder.c @@ -77,7 +77,8 @@ static inline int ngap_gNB_encode_successfull_outcome(NGAP_NGAP_PDU_t *pdu, uint NGAP_ProcedureCode_id_UEContextRelease, NGAP_ProcedureCode_id_PDUSessionResourceSetup, NGAP_ProcedureCode_id_PDUSessionResourceModify, - NGAP_ProcedureCode_id_PDUSessionResourceRelease}; + NGAP_ProcedureCode_id_PDUSessionResourceRelease, + NGAP_ProcedureCode_id_HandoverResourceAllocation}; int i; for (i = 0; i < sizeofArray(tmp); i++) if (pdu->choice.successfulOutcome->procedureCode == tmp[i]) @@ -98,8 +99,17 @@ static inline int ngap_gNB_encode_unsuccessfull_outcome(NGAP_NGAP_PDU_t *pdu, ui { DevAssert(pdu != NULL); - if (pdu->choice.unsuccessfulOutcome->procedureCode != NGAP_ProcedureCode_id_InitialContextSetup) { - NGAP_DEBUG("Unknown procedure ID (%d) for unsuccessfull outcome message\n", (int)pdu->choice.unsuccessfulOutcome->procedureCode); + const NGAP_ProcedureCode_t id[] = {NGAP_ProcedureCode_id_InitialContextSetup, NGAP_ProcedureCode_id_HandoverResourceAllocation}; + + int i; + for (i = 0; i < sizeofArray(id); i++) { + if (pdu->choice.unsuccessfulOutcome->procedureCode == id[i]) { + break; + } + } + + if (i == sizeofArray(id)) { + NGAP_WARN("Unknown procedure ID (%ld) for successful outcome message\n", pdu->choice.successfulOutcome->procedureCode); return -1; } diff --git a/openair3/NGAP/ngap_gNB_handlers.c b/openair3/NGAP/ngap_gNB_handlers.c index 6a92be9b79..485d37236a 100644 --- a/openair3/NGAP/ngap_gNB_handlers.c +++ b/openair3/NGAP/ngap_gNB_handlers.c @@ -44,7 +44,9 @@ #include "ngap_common.h" #include "ngap_gNB_decoder.h" #include "ngap_gNB_defs.h" +#include "ngap_gNB_nnsf.h" #include "ngap_gNB_management_procedures.h" +#include "ngap_gNB_mobility_management.h" #include "ngap_gNB_nas_procedures.h" #include "ngap_gNB_trace.h" #include "ngap_gNB_ue_context.h" @@ -693,97 +695,40 @@ static int ngap_gNB_handle_error_indication(sctp_assoc_t assoc_id, uint32_t stre return 0; } -static void *decode_pdusession_transfer(const asn_TYPE_descriptor_t *td, const OCTET_STRING_t buf) +/** @brief Handler for NGAP Handover Request message (AMF -> target NG-RAN) + * Decode the NGAP message and transfer to RRC */ +static int ngap_gNB_handle_handover_request(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu) { - void *decoded = NULL; - asn_codec_ctx_t ctx = {.max_stack_size = 100 * 1000}; - asn_dec_rval_t rval = aper_decode(&ctx, td, &decoded, buf.buf, buf.size, 0, 0); - if (rval.code != RC_OK) { - NGAP_ERROR("Decoding failed for %s\n", td->name); - return NULL; - } - return decoded; -} + DevAssert(pdu != NULL); -static pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQosParameters_t *params) -{ - pdusession_level_qos_parameter_t out = {0}; - // QFI - out.qfi = qfi; - AssertFatal(params != NULL, "QoS parameters are NULL\n"); - // QosCharacteristics - const NGAP_QosCharacteristics_t *qosChar = ¶ms->qosCharacteristics; - AssertFatal(qosChar != NULL, "QoS characteristics are NULL\n"); - if (qosChar->present == NGAP_QosCharacteristics_PR_nonDynamic5QI) { - AssertFatal(qosChar->choice.nonDynamic5QI != NULL, "nonDynamic5QI is NULL\n"); - out.fiveQI_type = NON_DYNAMIC; - out.fiveQI = qosChar->choice.nonDynamic5QI->fiveQI; - } else if (qosChar->present == NGAP_QosCharacteristics_PR_dynamic5QI) { - AssertFatal(qosChar->choice.dynamic5QI != NULL, "dynamic5QI is NULL\n"); - out.fiveQI_type = DYNAMIC; - out.fiveQI = *qosChar->choice.dynamic5QI->fiveQI; - } else { - AssertFatal(0, "Unsupported QoS Characteristics present value: %d\n", qosChar->present); - } - // Allocation and Retention Priority - const NGAP_AllocationAndRetentionPriority_t *arp = ¶ms->allocationAndRetentionPriority; - out.allocation_retention_priority.priority_level = arp->priorityLevelARP; - out.allocation_retention_priority.pre_emp_capability = arp->pre_emptionCapability; - out.allocation_retention_priority.pre_emp_vulnerability = arp->pre_emptionVulnerability; - - return out; -} - -static gtpu_tunnel_t decode_TNLInformation(const NGAP_GTPTunnel_t *gTPTunnel_p) -{ - gtpu_tunnel_t out = {0}; - // Transport layer address - memcpy(out.addr.buffer, gTPTunnel_p->transportLayerAddress.buf, gTPTunnel_p->transportLayerAddress.size); - out.addr.length = gTPTunnel_p->transportLayerAddress.size - gTPTunnel_p->transportLayerAddress.bits_unused; - // GTP tunnel endpoint ID - OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, out.teid); - return out; -} - -/** @brief Decode PDU Session Resource Setup Request Transfer (9.3.4.1 3GPP TS 38.413) */ -static bool decodePDUSessionResourceSetup(pdusession_transfer_t *out, const OCTET_STRING_t in) -{ - void *decoded = decode_pdusession_transfer(&asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, in); - if (!decoded) { - LOG_E(NR_RRC, "Failed to decode PDUSessionResourceSetupRequestTransfer\n"); - return false; + ngap_gNB_amf_data_t *amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0); + if (amf_desc_p == NULL) { + NGAP_ERROR("Received Handover Request for non existing AMF context\n"); + return -1; } - NGAP_PDUSessionResourceSetupRequestTransfer_t *pdusessionTransfer = (NGAP_PDUSessionResourceSetupRequestTransfer_t *)decoded; - for (int i = 0; i < pdusessionTransfer->protocolIEs.list.count; i++) { - NGAP_PDUSessionResourceSetupRequestTransferIEs_t *pdusessionTransfer_ies = pdusessionTransfer->protocolIEs.list.array[i]; - switch (pdusessionTransfer_ies->id) { - // UL NG-U UP TNL Information (Mandatory) - case NGAP_ProtocolIE_ID_id_UL_NGU_UP_TNLInformation: - out->n3_incoming = decode_TNLInformation(pdusessionTransfer_ies->value.choice.UPTransportLayerInformation.choice.gTPTunnel); - break; + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_REQUEST); + ngap_handover_request_t *msg = &NGAP_HANDOVER_REQUEST(message_p); + memset(msg, 0, sizeof(*msg)); - // PDU Session Type (Mandatory) - case NGAP_ProtocolIE_ID_id_PDUSessionType: - out->pdu_session_type = pdusessionTransfer_ies->value.choice.PDUSessionType; - break; - - // QoS Flow Setup Request List (Mandatory) - case NGAP_ProtocolIE_ID_id_QosFlowSetupRequestList: - out->nb_qos = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count; - for (int i = 0; i < out->nb_qos; i++) { - NGAP_QosFlowSetupRequestItem_t *item = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.array[i]; - out->qos[i] = fill_qos(item->qosFlowIdentifier, &item->qosFlowLevelQosParameters); - } - break; - - default: - LOG_D(NR_RRC, "Unhandled optional IE %ld\n", pdusessionTransfer_ies->id); - } + if (decode_ng_handover_request(msg, pdu) < 0) { + NGAP_ERROR("Failed to decode NG Handover Request\n"); + ngap_handover_failure_t fail = { + .amf_ue_ngap_id = msg->amf_ue_ngap_id, + .cause.type = NGAP_CAUSE_RADIO_NETWORK, + .cause.value = NGAP_CAUSE_RADIO_NETWORK_HO_FAILURE_IN_TARGET_5GC_NGRAN_NODE_OR_TARGET_SYSTEM, + }; + NGAP_INFO("Send NG Handover Failure message (amf_ue_ngap_id %ld) with cause %d \n ", fail.amf_ue_ngap_id, fail.cause.value); + MessageDef *msg_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_HANDOVER_FAILURE); + NGAP_HANDOVER_FAILURE(msg_p) = fail; + itti_send_msg_to_task(TASK_NGAP, amf_desc_p->ngap_gNB_instance->instance, msg_p); + return -1; } - ASN_STRUCT_FREE(asn_DEF_NGAP_PDUSessionResourceSetupRequestTransfer, pdusessionTransfer); - return true; + NGAP_INFO("Received NG Handover Request from AMF %s (ID=%lu)\n", amf_desc_p->amf_name, msg->amf_ue_ngap_id); + itti_send_msg_to_task(TASK_RRC_GNB, amf_desc_p->ngap_gNB_instance->instance, message_p); + + return 0; } static int ngap_gNB_handle_initial_context_request(sctp_assoc_t assoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu) @@ -1394,7 +1339,7 @@ const ngap_message_decoded_callback ngap_messages_callback[][3] = { {0, 0, 0}, /* HandoverCancel */ {0, 0, 0}, /* HandoverNotification */ {0, 0, 0}, /* HandoverPreparation */ - {0, 0, 0}, /* HandoverResourceAllocation */ + {ngap_gNB_handle_handover_request, 0, 0}, /* HandoverResourceAllocation */ {ngap_gNB_handle_initial_context_request, 0, 0}, /* InitialContextSetup */ {0, 0, 0}, /* InitialUEMessage */ {0, 0, 0}, /* LocationReportingControl */ diff --git a/openair3/NGAP/ngap_gNB_mobility_management.c b/openair3/NGAP/ngap_gNB_mobility_management.c index 9d84fccd09..2aa0056dfe 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.c +++ b/openair3/NGAP/ngap_gNB_mobility_management.c @@ -30,6 +30,7 @@ #include "ngap_gNB_management_procedures.h" #include "ngap_gNB_encoder.h" #include "ngap_gNB_itti_messaging.h" +#include "conversions.h" /** @brief UE Mobility Management: encode Handover Required * (9.2.3.1 of 3GPP TS 38.413) NG-RAN node → AMF */ @@ -224,3 +225,160 @@ NGAP_NGAP_PDU_t *encode_ng_handover_failure(const ngap_handover_failure_t *msg) return pdu; } + +static void free_ng_handover_request(ngap_handover_request_t *msg) +{ + free_byte_array(msg->ue_ho_prep_info); + free_byte_array(msg->ue_cap); + free(msg->mobility_restriction); +} + +int decode_ng_handover_request(ngap_handover_request_t *out, const NGAP_NGAP_PDU_t *pdu) +{ + DevAssert(pdu != NULL); + NGAP_HandoverRequest_t *container = &pdu->choice.initiatingMessage->value.choice.HandoverRequest; + NGAP_HandoverRequestIEs_t *ie; + + // Handover Type (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_HandoverType, true); + out->ho_type = ie->value.choice.HandoverType; + if (out->ho_type != HANDOVER_TYPE_INTRA5GS) { + NGAP_ERROR("Only Intra5GS Handover is supported at the moment!\n"); + return -1; + } + + // AMF UE NGAP ID (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_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); + + // GUAMI (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_GUAMI, true); + out->guami = decode_ngap_guami(&ie->value.choice.GUAMI); + + // UE Aggregate Maximum Bit Rate (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UEAggregateMaximumBitRate, true); + out->ue_ambr = decode_ngap_UEAggregateMaximumBitRate(&ie->value.choice.UEAggregateMaximumBitRate); + + // Allowed NSSAI (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_AllowedNSSAI, true); + NGAP_DEBUG("AllowedNSSAI.list.count %d\n", ie->value.choice.AllowedNSSAI.list.count); + out->nb_allowed_nssais = ie->value.choice.AllowedNSSAI.list.count; + for (int i = 0; i < out->nb_allowed_nssais; i++) { + out->allowed_nssai[i] = decode_ngap_nssai(&ie->value.choice.AllowedNSSAI.list.array[i]->s_NSSAI); + } + + // UE Security Capabilities (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UESecurityCapabilities, true); + out->security_capabilities = decode_ngap_security_capabilities(&ie->value.choice.UESecurityCapabilities); + + // Security Context (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_SecurityContext, true); + NGAP_SecurityContext_t *sc = &ie->value.choice.SecurityContext; + memcpy(&out->security_context.next_hop, sc->nextHopNH.buf, sc->nextHopNH.size); + out->security_context.next_hop_chain_count = sc->nextHopChainingCount; + + // Mobility Restriction List (O) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_MobilityRestrictionList, false); + if (ie != NULL) { + out->mobility_restriction = malloc_or_fail(sizeof(*out->mobility_restriction)); + *out->mobility_restriction = decode_ngap_mobility_restriction(&ie->value.choice.MobilityRestrictionList); + } + + // Cause (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_Cause, true); + out->cause = decode_ngap_cause(&ie->value.choice.Cause); + + // Source to Target Transparent Container + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, + ie, + container, + NGAP_ProtocolIE_ID_id_SourceToTarget_TransparentContainer, + true); + NGAP_SourceToTarget_TransparentContainer_t *choice = &ie->value.choice.SourceToTarget_TransparentContainer; + byte_array_t sourceToTargetTransparentContainer = create_byte_array(choice->size, choice->buf); + NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer_t *source2target = NULL; + asn_dec_rval_t dec_rval = aper_decode_complete(NULL, + &asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, + (void **)&source2target, + sourceToTargetTransparentContainer.buf, + sourceToTargetTransparentContainer.len); + + free_byte_array(sourceToTargetTransparentContainer); + if (dec_rval.code != RC_OK) { + free_ng_handover_request(out); + NGAP_ERROR("Failed to decode sourceToTargetTransparentContainer\n"); + return -1; + } + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) + xer_fprint(stdout, &asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target); + + // Extract Cell Identity + BIT_STRING_TO_NR_CELL_IDENTITY(&source2target->targetCell_ID.choice.nR_CGI->nRCellIdentity, out->nr_cell_id); + + // Handover Preparation Information: store and decode + out->ue_ho_prep_info = create_byte_array(source2target->rRCContainer.size, source2target->rRCContainer.buf); + NR_HandoverPreparationInformation_t *hoPrepInformation = NULL; + asn_dec_rval_t hoPrep_dec_rval = uper_decode_complete(NULL, + &asn_DEF_NR_HandoverPreparationInformation, + (void **)&hoPrepInformation, + source2target->rRCContainer.buf, + source2target->rRCContainer.size); + AssertFatal(hoPrep_dec_rval.code == RC_OK && hoPrep_dec_rval.consumed > 0, "Handover Prep Info decode error\n"); + if (hoPrep_dec_rval.code != RC_OK && !hoPrep_dec_rval.consumed) { + NGAP_ERROR("Failed to decode HandoverPreparationInformation, abort Handover Request decoding\n"); + free_ng_handover_request(out); + ASN_STRUCT_FREE(asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target); + return -1; + } + + ASN_STRUCT_FREE(asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target); + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) + xer_fprint(stdout, &asn_DEF_NR_HandoverPreparationInformation, hoPrepInformation); + + // Decode UE capabilities and store + NR_HandoverPreparationInformation_IEs_t *hpi = + hoPrepInformation->criticalExtensions.choice.c1->choice.handoverPreparationInformation; + const NR_UE_CapabilityRAT_ContainerList_t *ue_CapabilityRAT_ContainerList = &hpi->ue_CapabilityRAT_List; + out->ue_cap.len = uper_encode_to_new_buffer(&asn_DEF_NR_UE_CapabilityRAT_ContainerList, + NULL, + ue_CapabilityRAT_ContainerList, + (void **)&out->ue_cap.buf); + + ASN_STRUCT_FREE(asn_DEF_NR_HandoverPreparationInformation, hoPrepInformation); + + if (out->ue_cap.len <= 0) { + free_ng_handover_request(out); + NGAP_ERROR("could not encode UE-CapabilityRAT-ContainerList\n"); + return -1; + } + + // PDU Session Resource Setup List (M) + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_HandoverRequestIEs_t, + ie, + container, + NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListHOReq, + true); + out->nb_of_pdusessions = ie->value.choice.PDUSessionResourceSetupListHOReq.list.count; + for (int pduSesIdx = 0; pduSesIdx < out->nb_of_pdusessions; ++pduSesIdx) { + NGAP_PDUSessionResourceSetupItemHOReq_t *item_p = ie->value.choice.PDUSessionResourceSetupListHOReq.list.array[pduSesIdx]; + // PDU Session ID (M) + ho_request_pdusession_t *setup = &out->pduSessionResourceSetupList[pduSesIdx]; + setup->pdusession_id = item_p->pDUSessionID; + setup->pdu_session_type = PDUSessionType_ipv4; + // S-NSSAI (M) + setup->nssai = decode_ngap_nssai(&item_p->s_NSSAI); + // Handover Request Transfer (M) + bool ret = decodePDUSessionResourceSetup(&setup->pdusessionTransfer, item_p->handoverRequestTransfer); + if (!ret) { + free_ng_handover_request(out); + NGAP_ERROR("Failed to decode pDUSessionResourceSetupRequestTransfer in NG Initial Context Setup Request\n"); + return -1; + } + } + + + + return 0; +} diff --git a/openair3/NGAP/ngap_gNB_mobility_management.h b/openair3/NGAP/ngap_gNB_mobility_management.h index 6f5cdde742..66e85e644e 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.h +++ b/openair3/NGAP/ngap_gNB_mobility_management.h @@ -28,5 +28,6 @@ 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); #endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */ diff --git a/openair3/NGAP/ngap_msg_includes.h b/openair3/NGAP/ngap_msg_includes.h index cf7d0d9f49..a80df29401 100644 --- a/openair3/NGAP/ngap_msg_includes.h +++ b/openair3/NGAP/ngap_msg_includes.h @@ -86,5 +86,20 @@ #include "NGAP_PDUSessionResourceInformationList.h" #include "NGAP_HandoverRequiredTransfer.h" #include "NGAP_SourceNGRANNode-ToTargetNGRANNode-TransparentContainer.h" +#include "NGAP_PDUSessionResourceSetupItemHOReq.h" +#include "NGAP_HandoverCommand.h" +#include "NGAP_HandoverCommandTransfer.h" +#include "NGAP_TargetNGRANNode-ToSourceNGRANNode-TransparentContainer.h" +#include "NGAP_TargetToSource-TransparentContainer.h" +#include "NGAP_UEHistoryInformation.h" +#include "NGAP_Cause.h" +#include "NGAP_PDUSessionResourceHandoverItem.h" +#include "NGAP_SourceToTarget-TransparentContainer.h" +#include "NGAP_HandoverRequest.h" +#include "NGAP_PDUSessionResourceAdmittedList.h" +#include "NGAP_PDUSessionResourceAdmittedItem.h" +#include "NGAP_HandoverRequestAcknowledgeTransfer.h" +#include "NGAP_QosFlowItemWithDataForwarding.h" +#include "NR_HandoverPreparationInformation.h" #endif // NGAP_MSG_INCLUDES_H