Add NGAP Handover Request decoding and RRC handler
This commit introduces the NG Mobility Management Procedure known as
Handover Resource Allocation. The inbound message is sent by the AMF
and is known as Handover Request.
Major changes:
(0) Add Handover Resource Allocation initiating message decoding
* add case for NGAP initiating message and unsuccessful message decoding
(1) handle Handover Request on the target NG-RAN
(2) decode NG Handover Request message
(3) process payload, e.g. create UE context,
store IDs, set UP security and trigger bearer setup
Co-authored-by: batuhan duyuler <batuhan.duyuler@firecell.io>
This commit is contained in:
co-authored by
batuhan duyuler
parent
92abefb815
commit
461cfb7911
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
/** @}*/
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user