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_ */