Add NGAP Handover Required message NGAP encoder and RRC callback on source NG-RAN
This commit introduces the NG Mobility Management Procedure known as Handover Preparation. The outbound message is sent by the source NG-RAN and is known as Handover Required. * introduce NGAP library for Mobility Management * introduce NGAP IEs encoder functions for Handover Required and relevant common IEs * handle RRC trigger in NGAP and Send NGAP Handover Required to the AMF Co-authored-by: batuhan duyuler <batuhan.duyuler@firecell.io>
This commit is contained in:
co-authored by
batuhan duyuler
parent
cfb654e77f
commit
20e534d483
@@ -363,6 +363,7 @@ add_library(ngap
|
||||
${NGAP_DIR}/ngap_gNB.c
|
||||
${NGAP_DIR}/ngap_common.c
|
||||
${NGAP_DIR}/ngap_gNB_context_management_procedures.c
|
||||
${NGAP_DIR}/ngap_gNB_mobility_management.c
|
||||
${NGAP_DIR}/ngap_gNB_decoder.c
|
||||
${NGAP_DIR}/ngap_gNB_encoder.c
|
||||
${NGAP_DIR}/ngap_gNB_handlers.c
|
||||
|
||||
@@ -71,6 +71,7 @@ MESSAGE_DEF(NGAP_UE_CTXT_MODIFICATION_FAIL , MESSAGE_PRIORITY_MED, ngap_ue_ctxt_
|
||||
MESSAGE_DEF(NGAP_PDUSESSION_SETUP_RESP , MESSAGE_PRIORITY_MED, ngap_pdusession_setup_resp_t , ngap_pdusession_setup_resp)
|
||||
MESSAGE_DEF(NGAP_PDUSESSION_MODIFY_RESP , MESSAGE_PRIORITY_MED, ngap_pdusession_modify_resp_t , ngap_pdusession_modify_resp)
|
||||
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)
|
||||
|
||||
/* NGAP -> RRC messages */
|
||||
MESSAGE_DEF(NGAP_DOWNLINK_NAS , MESSAGE_PRIORITY_MED, ngap_downlink_nas_t , ngap_downlink_nas )
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
#define NGAP_PDUSESSION_SETUP_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_setup_req
|
||||
#define NGAP_PDUSESSION_MODIFY_REQ(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_modify_req
|
||||
#define NGAP_PAGING_IND(mSGpTR) (mSGpTR)->ittiMsg.ngap_paging_ind
|
||||
|
||||
#define NGAP_HANDOVER_REQUIRED(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_required
|
||||
#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
|
||||
#define NGAP_PDUSESSION_RELEASE_RESPONSE(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_resp
|
||||
@@ -463,6 +463,91 @@ typedef struct ngap_uplink_nas_s {
|
||||
byte_array_t nas_pdu;
|
||||
} ngap_uplink_nas_t;
|
||||
|
||||
typedef struct target_cell_id_s {
|
||||
plmn_id_t plmn_identity;
|
||||
uint32_t nrCellIdentity;
|
||||
} cell_id_t;
|
||||
|
||||
typedef struct target_ran_node_id_s {
|
||||
uint32_t targetgNBId;
|
||||
plmn_id_t plmn_identity;
|
||||
uint32_t tac;
|
||||
} target_ran_node_id_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.3.1.29 */
|
||||
typedef struct {
|
||||
// QoS Flow Identifier
|
||||
uint8_t qfi;
|
||||
} qosflow_info_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.3.1.29 */
|
||||
typedef struct {
|
||||
// PDU Session ID
|
||||
uint8_t pdusession_id;
|
||||
// QoS Flow Information List
|
||||
uint8_t nb_of_qos_flow;
|
||||
qosflow_info_t qos_flow_info[QOSFLOW_MAX_VALUE];
|
||||
} pdusession_resource_info_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.3.1.97 */
|
||||
typedef struct {
|
||||
// Global Cell ID
|
||||
cell_id_t id;
|
||||
// Cell type
|
||||
uint8_t type;
|
||||
// Time UE Stayed in Cell
|
||||
long time_in_cell;
|
||||
// Cause
|
||||
ngap_cause_t *cause;
|
||||
} last_visited_ngran_cell_info_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.3.1.29 */
|
||||
typedef struct {
|
||||
// RRC Container: HandoverPreparationInformation message
|
||||
byte_array_t handoverInfo;
|
||||
// Target Cell ID
|
||||
cell_id_t targetCellId;
|
||||
// PDU Session Resource Information List
|
||||
uint16_t nb_pdu_session_resource;
|
||||
pdusession_resource_info_t pdu_session_resource[NGAP_MAX_PDU_SESSION];
|
||||
// UE History Information
|
||||
last_visited_ngran_cell_info_t ue_history_info;
|
||||
} source_to_target_transparent_container_t;
|
||||
|
||||
typedef enum {
|
||||
HANDOVER_TYPE_INTRA5GS, // Intra5GS: NG-RAN node to NG-RAN node
|
||||
HANDOVER_TYPE_5GSTOEPS, // 5GStoEPS: NG-RAN node to eNB
|
||||
HANDOVER_TYPE_EPSTO5GS, // EPSto5GS: eNB to NG-RAN node
|
||||
HANDOVER_TYPE_5GSTOUTRA, // 5GStoUTRA: NG-RAN node to UTRA
|
||||
} ho_type_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.2.3.1 */
|
||||
typedef struct {
|
||||
// PDU Session ID
|
||||
uint8_t pdusession_id;
|
||||
// Handover Required Transfer
|
||||
byte_array_t ho_required_transfer;
|
||||
} pdusession_resource_t;
|
||||
|
||||
/* 3GPP TS 38.413 9.2.3.1 */
|
||||
typedef struct {
|
||||
// RAN UE NGAP ID
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
// AMF UE NGAP ID
|
||||
uint64_t amf_ue_ngap_id;
|
||||
// Handover Type
|
||||
ho_type_t handoverType;
|
||||
// Cause
|
||||
ngap_cause_t cause;
|
||||
// Target ID
|
||||
target_ran_node_id_t target_gnb_id;
|
||||
// PDU Session Resource List
|
||||
uint8_t nb_of_pdusessions;
|
||||
pdusession_resource_t pdusessions[NGAP_MAX_PDU_SESSION];
|
||||
// Source to Target Transparent Container
|
||||
source_to_target_transparent_container_t *source2target;
|
||||
} ngap_handover_required_t;
|
||||
|
||||
typedef struct ngap_ue_cap_info_ind_s {
|
||||
uint32_t gNB_ue_ngap_id;
|
||||
byte_array_t ue_radio_cap;
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "NGAP_QosFlowSetupRequestItem.h"
|
||||
#include "NGAP_asn_constant.h"
|
||||
#include "NGAP_ProtocolIE-Field.h"
|
||||
#include "NGAP_CellSize.h"
|
||||
#include "NR_UE-NR-Capability.h"
|
||||
#include "NR_UERadioAccessCapabilityInformation.h"
|
||||
#include "MAC/mac.h"
|
||||
@@ -1297,3 +1298,80 @@ int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @brief Callback for NGAP Handover Required message (3GPP TS 38.413 9.2.3.1)
|
||||
* Direction: source gNB -> AMF */
|
||||
void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
|
||||
gNB_RRC_UE_t *UE,
|
||||
const nr_neighbour_cell_t *neighbour,
|
||||
const byte_array_t hoPrepInfo)
|
||||
{
|
||||
LOG_I(NR_RRC, "Handover Preparation: send Handover Required (target gNB ID=%d, PCI=%d)\n", neighbour->gNB_ID, neighbour->physicalCellId);
|
||||
|
||||
const plmn_id_t plmn = {.mcc = neighbour->plmn.mnc,
|
||||
.mnc = neighbour->plmn.mnc,
|
||||
.mnc_digit_length = neighbour->plmn.mnc_digit_length};
|
||||
|
||||
const target_ran_node_id_t target = {
|
||||
.plmn_identity = plmn,
|
||||
.tac = neighbour->tac,
|
||||
.targetgNBId = neighbour->gNB_ID,
|
||||
};
|
||||
|
||||
ngap_handover_required_t msg = {
|
||||
.amf_ue_ngap_id = UE->amf_ue_ngap_id,
|
||||
.gNB_ue_ngap_id = UE->rrc_ue_id,
|
||||
.nb_of_pdusessions = UE->nb_of_pdusessions,
|
||||
.cause.type = NGAP_CAUSE_RADIO_NETWORK,
|
||||
.cause.value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_DESIRABLE_FOR_RADIO_REASON,
|
||||
.handoverType = HANDOVER_TYPE_INTRA5GS,
|
||||
.target_gnb_id = target,
|
||||
};
|
||||
|
||||
cell_id_t target_cell = {
|
||||
.nrCellIdentity = neighbour->nrcell_id,
|
||||
.plmn_identity = plmn,
|
||||
};
|
||||
|
||||
// Source to Target Transparent Container (M)
|
||||
msg.source2target = calloc_or_fail(1, sizeof(*msg.source2target));
|
||||
// Target Cell ID (M)
|
||||
msg.source2target->targetCellId = target_cell;
|
||||
// RRC Container (M)
|
||||
msg.source2target->handoverInfo = copy_byte_array(hoPrepInfo);
|
||||
// UE History Info (M)
|
||||
msg.source2target->ue_history_info.cause = malloc_or_fail(sizeof(*msg.source2target->ue_history_info.cause));
|
||||
msg.source2target->ue_history_info.cause->type = NGAP_CAUSE_RADIO_NETWORK;
|
||||
msg.source2target->ue_history_info.cause->value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_DESIRABLE_FOR_RADIO_REASON;
|
||||
msg.source2target->ue_history_info.type = NGAP_CellSize_small;
|
||||
msg.source2target->ue_history_info.time_in_cell = 500; // dummy number for now
|
||||
msg.source2target->ue_history_info.id = target_cell;
|
||||
|
||||
/* Fill both PDU Session Resource List IE (M) in Handover Required
|
||||
and PDU Session Resource Information List IE (O) in the Source NG-RAN
|
||||
Node to Target NG-RAN Node Transparent Container */
|
||||
for (int i = 0; i < UE->nb_of_pdusessions; ++i) {
|
||||
if (UE->pduSession[i].status == PDU_SESSION_STATUS_DONE || UE->pduSession[i].status == PDU_SESSION_STATUS_ESTABLISHED) {
|
||||
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, UE->pduSession[i].param.pdusession_id, false);
|
||||
if (!pduSession)
|
||||
continue;
|
||||
// Handover Required Transfer (M)
|
||||
uint8_t ho_required_transfer[128] = {0};
|
||||
msg.pdusessions[i].ho_required_transfer.buf = ho_required_transfer;
|
||||
msg.pdusessions[i].ho_required_transfer.len = sizeof(ho_required_transfer);
|
||||
// PDU Session ID
|
||||
msg.pdusessions[i].pdusession_id = pduSession->param.pdusession_id;
|
||||
|
||||
// PDU Session Resource Information List (O)
|
||||
msg.source2target->pdu_session_resource[i].nb_of_qos_flow = pduSession->param.nb_qos;
|
||||
for (int j = 0; j < pduSession->param.nb_qos; ++j) {
|
||||
msg.source2target->pdu_session_resource[i].qos_flow_info[j].qfi = pduSession->param.qos[j].qfi;
|
||||
}
|
||||
msg.source2target->nb_pdu_session_resource++;
|
||||
}
|
||||
}
|
||||
|
||||
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_REQUIRED);
|
||||
NGAP_HANDOVER_REQUIRED(msg_p) = msg;
|
||||
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
|
||||
}
|
||||
|
||||
@@ -89,4 +89,9 @@ void nr_rrc_pdcp_config_security(gNB_RRC_UE_t *UE, bool enable_ciphering);
|
||||
|
||||
int rrc_gNB_process_PAGING_IND(MessageDef *msg_p, instance_t instance);
|
||||
|
||||
void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
|
||||
gNB_RRC_UE_t *UE,
|
||||
const nr_neighbour_cell_t *neighbourCellConfiguration,
|
||||
const byte_array_t hoPrepInfo);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -112,3 +112,32 @@ ngap_mobility_restriction_t decode_ngap_mobility_restriction(const NGAP_Mobility
|
||||
TBCD_TO_MCC_MNC(&in->servingPLMN, out.serving_plmn.mcc, out.serving_plmn.mnc, out.serving_plmn.mnc_digit_length);
|
||||
return out;
|
||||
}
|
||||
|
||||
void encode_ngap_target_id(NGAP_HandoverRequiredIEs_t *out, const target_ran_node_id_t *in)
|
||||
{
|
||||
out->id = NGAP_ProtocolIE_ID_id_TargetID;
|
||||
out->criticality = NGAP_Criticality_reject;
|
||||
out->value.present = NGAP_HandoverRequiredIEs__value_PR_TargetID;
|
||||
// CHOICE Target ID: NG-RAN (M)
|
||||
out->value.choice.TargetID.present = NGAP_TargetID_PR_targetRANNodeID;
|
||||
asn1cCalloc(out->value.choice.TargetID.choice.targetRANNodeID, targetRan);
|
||||
// Global RAN Node ID (M)
|
||||
targetRan->globalRANNodeID.present = NGAP_GlobalRANNodeID_PR_globalGNB_ID;
|
||||
asn1cCalloc(targetRan->globalRANNodeID.choice.globalGNB_ID, globalGnbId);
|
||||
globalGnbId->gNB_ID.present = NGAP_GNB_ID_PR_gNB_ID;
|
||||
MACRO_GNB_ID_TO_BIT_STRING(in->targetgNBId, &globalGnbId->gNB_ID.choice.gNB_ID);
|
||||
MCC_MNC_TO_PLMNID(in->plmn_identity.mcc, in->plmn_identity.mnc, in->plmn_identity.mnc_digit_length, &globalGnbId->pLMNIdentity);
|
||||
// Selected TAI (M)
|
||||
INT24_TO_OCTET_STRING(in->tac, &targetRan->selectedTAI.tAC);
|
||||
MCC_MNC_TO_PLMNID(in->plmn_identity.mcc,
|
||||
in->plmn_identity.mnc,
|
||||
in->plmn_identity.mnc_digit_length,
|
||||
&targetRan->selectedTAI.pLMNIdentity);
|
||||
}
|
||||
|
||||
void encode_ngap_nr_cgi(NGAP_NR_CGI_t *out, const plmn_id_t *plmn, const uint32_t cell_id)
|
||||
{
|
||||
out->iE_Extensions = NULL;
|
||||
MCC_MNC_TO_PLMNID(plmn->mcc, plmn->mnc, plmn->mnc_digit_length, &out->pLMNIdentity);
|
||||
NR_CELL_ID_TO_BIT_STRING(cell_id, &out->nRCellIdentity);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,9 @@ ngap_ambr_t decode_ngap_UEAggregateMaximumBitRate(const NGAP_UEAggregateMaximumB
|
||||
nssai_t decode_ngap_nssai(const NGAP_S_NSSAI_t *in);
|
||||
ngap_security_capabilities_t decode_ngap_security_capabilities(const NGAP_UESecurityCapabilities_t *in);
|
||||
ngap_mobility_restriction_t decode_ngap_mobility_restriction(const NGAP_MobilityRestrictionList_t *in);
|
||||
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);
|
||||
|
||||
/** @}*/
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
#include "ngap_gNB_management_procedures.h"
|
||||
#include "ngap_gNB_nas_procedures.h"
|
||||
#include "ngap_messages_types.h"
|
||||
#include "ngap_gNB_mobility_management.h"
|
||||
#include "ngap_gNB_ue_context.h"
|
||||
#include "oai_asn1.h"
|
||||
#include "openair3/SECU/kdf.h"
|
||||
#include "queue.h"
|
||||
@@ -275,6 +277,57 @@ void ngap_gNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind) {
|
||||
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
|
||||
}
|
||||
|
||||
/** @brief UE Mobility Management: callback for Handover Required */
|
||||
int ngap_handover_required(instance_t instance, ngap_handover_required_t *msg)
|
||||
{
|
||||
DevAssert(msg != NULL);
|
||||
NGAP_DEBUG("Triggered Handover Required\n");
|
||||
/* Retrieve the NGAP gNB instance associated with Mod_id */
|
||||
ngap_gNB_instance_t *ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
|
||||
DevAssert(ngap_gNB_instance_p != NULL);
|
||||
|
||||
ngap_gNB_ue_context_t *ue_context_p = NULL;
|
||||
if ((ue_context_p = ngap_get_ue_context(msg->gNB_ue_ngap_id)) == NULL) {
|
||||
/* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
|
||||
NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%08x\n", msg->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((ue_context_p->gNB_ue_ngap_id != msg->gNB_ue_ngap_id)) {
|
||||
NGAP_ERROR("ue_context_p->gNB_ue_ngap_id %d does not match msg->gNB_ue_ngap_id %d\n",
|
||||
ue_context_p->gNB_ue_ngap_id,
|
||||
msg->gNB_ue_ngap_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
NGAP_NGAP_PDU_t *pdu = encode_ng_handover_required(msg);
|
||||
if (!pdu) {
|
||||
NGAP_ERROR("Failed to encode Handover Required\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, &pdu);
|
||||
|
||||
byte_array_t out = { .buf = NULL, .len = 0 };
|
||||
if (ngap_gNB_encode_pdu(pdu, &out.buf, (uint32_t *)&out.len) < 0) {
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
NGAP_ERROR("Failed to encode Handover Required message\n");
|
||||
return -1;
|
||||
}
|
||||
free(pdu);
|
||||
|
||||
/* UE associated signalling -> use the allocated stream */
|
||||
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
|
||||
ue_context_p->amf_ref->assoc_id,
|
||||
out.buf,
|
||||
out.len,
|
||||
ue_context_p->tx_stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ngap_gNB_init(void) {
|
||||
NGAP_DEBUG("Starting NGAP layer\n");
|
||||
ngap_gNB_prepare_internal_data();
|
||||
@@ -356,6 +409,12 @@ void *ngap_gNB_process_itti_msg(void *notUsed) {
|
||||
ngap_gNB_pdusession_release_resp(instance, &NGAP_PDUSESSION_RELEASE_RESPONSE(received_msg));
|
||||
break;
|
||||
|
||||
case NGAP_HANDOVER_REQUIRED:
|
||||
if (ngap_handover_required(instance, &NGAP_HANDOVER_REQUIRED(received_msg)) < 0) {
|
||||
NGAP_ERROR("Handover Required failure: indication to RRC is not sent!\n");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
NGAP_ERROR("Received unhandled message: %d:%s\n", ITTI_MSG_ID(received_msg), ITTI_MSG_NAME(received_msg));
|
||||
break;
|
||||
|
||||
@@ -70,6 +70,12 @@ static int ngap_gNB_decode_initiating_message(NGAP_NGAP_PDU_t *pdu) {
|
||||
NGAP_INFO("PDUSESSIONSetup initiating message\n");
|
||||
break;
|
||||
|
||||
case NGAP_ProcedureCode_id_HandoverPreparation:
|
||||
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
free(res.buffer);
|
||||
NGAP_INFO("Handover Preparation initiating message\n");
|
||||
break;
|
||||
|
||||
case NGAP_ProcedureCode_id_PDUSessionResourceModify:
|
||||
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
free(res.buffer);
|
||||
@@ -119,6 +125,10 @@ static int ngap_gNB_decode_successful_outcome(NGAP_NGAP_PDU_t *pdu) {
|
||||
free(res.buffer);
|
||||
break;
|
||||
|
||||
case NGAP_ProcedureCode_id_HandoverPreparation:
|
||||
res = asn_encode_to_new_buffer(NULL, ATS_CANONICAL_XER, &asn_DEF_NGAP_NGAP_PDU, pdu);
|
||||
free(res.buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
NGAP_ERROR("Unknown procedure ID (%d) for successfull outcome message\n",
|
||||
|
||||
@@ -52,7 +52,8 @@ static inline int ngap_gNB_encode_initiating(NGAP_NGAP_PDU_t *pdu, uint8_t **buf
|
||||
NGAP_ProcedureCode_id_NASNonDeliveryIndication,
|
||||
NGAP_ProcedureCode_id_UEContextReleaseRequest,
|
||||
NGAP_ProcedureCode_id_PathSwitchRequest,
|
||||
NGAP_ProcedureCode_id_PDUSessionResourceModifyIndication};
|
||||
NGAP_ProcedureCode_id_PDUSessionResourceModifyIndication,
|
||||
NGAP_ProcedureCode_id_HandoverPreparation};
|
||||
int i;
|
||||
for (i = 0; i < sizeofArray(tmp); i++)
|
||||
if (pdu->choice.initiatingMessage->procedureCode == tmp[i])
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "ngap_common.h"
|
||||
#include "ngap_msg_includes.h"
|
||||
#include "ngap_gNB_defs.h"
|
||||
#include "ngap_gNB_ue_context.h"
|
||||
#include "oai_asn1.h"
|
||||
#include "ngap_gNB_management_procedures.h"
|
||||
#include "ngap_gNB_encoder.h"
|
||||
#include "ngap_gNB_itti_messaging.h"
|
||||
|
||||
/** @brief UE Mobility Management: encode Handover Required
|
||||
* (9.2.3.1 of 3GPP TS 38.413) NG-RAN node → AMF */
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_required(const ngap_handover_required_t *msg)
|
||||
{
|
||||
NGAP_NGAP_PDU_t *pdu = malloc_or_fail(sizeof(*pdu));
|
||||
|
||||
/* Prepare the NGAP message to encode */
|
||||
pdu->present = NGAP_NGAP_PDU_PR_initiatingMessage;
|
||||
asn1cCalloc(pdu->choice.initiatingMessage, head);
|
||||
head->procedureCode = NGAP_ProcedureCode_id_HandoverPreparation;
|
||||
head->criticality = NGAP_Criticality_reject;
|
||||
head->value.present = NGAP_InitiatingMessage__value_PR_HandoverRequired;
|
||||
NGAP_HandoverRequired_t *out = &head->value.choice.HandoverRequired;
|
||||
|
||||
// AMF UE NGAP ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie1);
|
||||
ie1->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
|
||||
ie1->criticality = NGAP_Criticality_reject;
|
||||
ie1->value.present = NGAP_HandoverRequiredIEs__value_PR_AMF_UE_NGAP_ID;
|
||||
asn_uint642INTEGER(&ie1->value.choice.AMF_UE_NGAP_ID, msg->amf_ue_ngap_id);
|
||||
|
||||
// RAN UE NGAP ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie2);
|
||||
ie2->id = NGAP_ProtocolIE_ID_id_RAN_UE_NGAP_ID;
|
||||
ie2->criticality = NGAP_Criticality_reject;
|
||||
ie2->value.present = NGAP_HandoverRequiredIEs__value_PR_RAN_UE_NGAP_ID;
|
||||
ie2->value.choice.RAN_UE_NGAP_ID = msg->gNB_ue_ngap_id;
|
||||
|
||||
// Handover Type (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie3);
|
||||
ie3->id = NGAP_ProtocolIE_ID_id_HandoverType;
|
||||
ie3->criticality = NGAP_Criticality_reject;
|
||||
ie3->value.present = NGAP_HandoverRequiredIEs__value_PR_HandoverType;
|
||||
ie3->value.choice.HandoverType = msg->handoverType;
|
||||
|
||||
// Cause (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie4);
|
||||
ie4->id = NGAP_ProtocolIE_ID_id_Cause;
|
||||
ie4->criticality = NGAP_Criticality_ignore;
|
||||
ie4->value.present = NGAP_HandoverRequiredIEs__value_PR_Cause;
|
||||
encode_ngap_cause(&ie4->value.choice.Cause, &msg->cause);
|
||||
|
||||
// Target ID (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie5);
|
||||
encode_ngap_target_id(ie5, &msg->target_gnb_id);
|
||||
|
||||
// PDU Session Resource List (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie6);
|
||||
ie6->id = NGAP_ProtocolIE_ID_id_PDUSessionResourceListHORqd;
|
||||
ie6->criticality = NGAP_Criticality_reject;
|
||||
ie6->value.present = NGAP_HandoverRequiredIEs__value_PR_PDUSessionResourceListHORqd;
|
||||
for (int i = 0; i < msg->nb_of_pdusessions; ++i) {
|
||||
asn1cSequenceAdd(ie6->value.choice.PDUSessionResourceListHORqd.list, NGAP_PDUSessionResourceItemHORqd_t, hoRequiredPduSession);
|
||||
// PDU Session ID (M)
|
||||
hoRequiredPduSession->pDUSessionID = msg->pdusessions[i].pdusession_id;
|
||||
// Handover Required Transfer (M)
|
||||
NGAP_HandoverRequiredTransfer_t hoRequiredTransfer = {0};
|
||||
uint8_t ho_req_transfer_transparent_container_buffer[128] = {0};
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NGAP_HandoverRequiredTransfer, &hoRequiredTransfer);
|
||||
asn_enc_rval_t enc_rval = aper_encode_to_buffer(&asn_DEF_NGAP_HandoverRequiredTransfer,
|
||||
NULL,
|
||||
&hoRequiredTransfer,
|
||||
ho_req_transfer_transparent_container_buffer,
|
||||
128);
|
||||
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
|
||||
hoRequiredPduSession->handoverRequiredTransfer.buf = CALLOC(1, (enc_rval.encoded + 7) / 8);
|
||||
memcpy(hoRequiredPduSession->handoverRequiredTransfer.buf,
|
||||
ho_req_transfer_transparent_container_buffer,
|
||||
(enc_rval.encoded + 7) / 8);
|
||||
hoRequiredPduSession->handoverRequiredTransfer.size = (enc_rval.encoded + 7) / 8;
|
||||
|
||||
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_HandoverRequiredTransfer, &hoRequiredTransfer);
|
||||
}
|
||||
|
||||
// Source NG-RAN Node to Target NG-RAN Node Transparent Container (M)
|
||||
asn1cSequenceAdd(out->protocolIEs.list, NGAP_HandoverRequiredIEs_t, ie);
|
||||
ie->id = NGAP_ProtocolIE_ID_id_SourceToTarget_TransparentContainer;
|
||||
ie->criticality = NGAP_Criticality_reject;
|
||||
ie->value.present = NGAP_HandoverRequiredIEs__value_PR_SourceToTarget_TransparentContainer;
|
||||
|
||||
NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer_t *source2target = calloc_or_fail(1, sizeof(*source2target));
|
||||
|
||||
// RRC Container (M) (HandoverPreparationInformation)
|
||||
source2target->rRCContainer.size = msg->source2target->handoverInfo.len;
|
||||
source2target->rRCContainer.buf = malloc_or_fail(msg->source2target->handoverInfo.len);
|
||||
memcpy(source2target->rRCContainer.buf, msg->source2target->handoverInfo.buf, source2target->rRCContainer.size);
|
||||
|
||||
// PDU Session Resource Information List (O)
|
||||
asn1cCalloc(source2target->pDUSessionResourceInformationList, pduSessionList);
|
||||
for (uint8_t i = 0; i < msg->nb_of_pdusessions; ++i) {
|
||||
const pdusession_resource_t *pduSession = &msg->pdusessions[i];
|
||||
NGAP_DEBUG("Handover Required: preparing PDU Session Resource Information List for PDU Session ID %d\n",
|
||||
pduSession->pdusession_id);
|
||||
asn1cSequenceAdd(pduSessionList->list, NGAP_PDUSessionResourceInformationItem_t, item);
|
||||
// PDU Session ID (M)
|
||||
item->pDUSessionID = msg->source2target->pdu_session_resource[i].pdusession_id;
|
||||
// QoS Flow Information List (M)
|
||||
for (int q = 0; q < msg->source2target->pdu_session_resource[i].nb_of_qos_flow; ++q) {
|
||||
asn1cSequenceAdd(item->qosFlowInformationList.list, NGAP_QosFlowInformationItem_t, qosFlowInfo);
|
||||
qosFlowInfo->qosFlowIdentifier = msg->source2target->pdu_session_resource[i].qos_flow_info[q].qfi;
|
||||
}
|
||||
}
|
||||
|
||||
// Target Cell ID (NG-RAN CGI) (M)
|
||||
source2target->targetCell_ID.present = NGAP_NGRAN_CGI_PR_nR_CGI;
|
||||
asn1cCalloc(source2target->targetCell_ID.choice.nR_CGI, tNrCGI);
|
||||
encode_ngap_nr_cgi(tNrCGI, &msg->target_gnb_id.plmn_identity, msg->source2target->targetCellId.nrCellIdentity);
|
||||
|
||||
// UE history Information (M)
|
||||
asn1cSequenceAdd(source2target->uEHistoryInformation.list, NGAP_LastVisitedCellItem_t, lastVisitedCell);
|
||||
lastVisitedCell->iE_Extensions = NULL;
|
||||
// Last Visited Cell Information (M)
|
||||
lastVisitedCell->lastVisitedCellInformation.present = NGAP_LastVisitedCellInformation_PR_nGRANCell;
|
||||
// CHOICE (M): NG-RAN Cell
|
||||
asn1cCalloc(lastVisitedCell->lastVisitedCellInformation.choice.nGRANCell, lastVisitedNR);
|
||||
// Cell Type (M)
|
||||
lastVisitedNR->cellType.cellSize = msg->source2target->ue_history_info.type;
|
||||
// Global Cell ID (M)
|
||||
lastVisitedNR->globalCellID.present = NGAP_NGRAN_CGI_PR_nR_CGI;
|
||||
asn1cCalloc(lastVisitedNR->globalCellID.choice.nR_CGI, lastVisitedNrCGI);
|
||||
cell_id_t *cell = &msg->source2target->ue_history_info.id;
|
||||
encode_ngap_nr_cgi(lastVisitedNrCGI, &cell->plmn_identity, cell->nrCellIdentity);
|
||||
// HO Cause Value (O)
|
||||
if (msg->source2target->ue_history_info.cause) {
|
||||
asn1cCalloc(lastVisitedNR->hOCauseValue, lastVisitedCause);
|
||||
encode_ngap_cause(lastVisitedCause, msg->source2target->ue_history_info.cause);
|
||||
}
|
||||
// Time UE Stayed in Cell (M)
|
||||
lastVisitedNR->timeUEStayedInCell = msg->source2target->ue_history_info.time_in_cell;
|
||||
|
||||
if (LOG_DEBUGFLAG(DEBUG_ASN1))
|
||||
xer_fprint(stdout, &asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
|
||||
|
||||
uint8_t source_to_target_transparent_container_buf[16384] = {0};
|
||||
asn_enc_rval_t enc_rval = aper_encode_to_buffer(&asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer,
|
||||
NULL,
|
||||
(void *)source2target,
|
||||
(void *)&source_to_target_transparent_container_buf,
|
||||
16384);
|
||||
ASN_STRUCT_FREE(asn_DEF_NGAP_SourceNGRANNode_ToTargetNGRANNode_TransparentContainer, source2target);
|
||||
if (enc_rval.encoded < 0) {
|
||||
AssertFatal(enc_rval.encoded > 0,
|
||||
"HO LOG: Source to Transparent ASN1 message encoding failed (%s, %lu)!\n",
|
||||
enc_rval.failed_type->name,
|
||||
enc_rval.encoded);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int total_bytes = (enc_rval.encoded + 7) / 8;
|
||||
int ret = OCTET_STRING_fromBuf(&ie->value.choice.SourceToTarget_TransparentContainer,
|
||||
(const char *)&source_to_target_transparent_container_buf,
|
||||
total_bytes);
|
||||
if (ret != 0) {
|
||||
LOG_E(NR_RRC, "HO LOG: Can not perform OCTET_STRING_fromBuf for the SourceToTarget_TransparentContainer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pdu;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#ifndef NGAP_GNB_MOBILITY_MANAGEMENT_H_
|
||||
#define NGAP_GNB_MOBILITY_MANAGEMENT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common/platform_types.h"
|
||||
#include "ngap_messages_types.h"
|
||||
|
||||
NGAP_NGAP_PDU_t *encode_ng_handover_required(const ngap_handover_required_t *msg);
|
||||
|
||||
#endif /* NGAP_GNB_MOBILITY_MANAGEMENT_H_ */
|
||||
@@ -78,5 +78,13 @@
|
||||
#include "NGAP_Dynamic5QIDescriptor.h"
|
||||
#include "NGAP_PDUSessionResourceModifyRequestTransfer.h"
|
||||
#include "NGAP_QosFlowAddOrModifyRequestItem.h"
|
||||
#include "NGAP_PDUSessionResourceItemHORqd.h"
|
||||
#include "NGAP_PDUSessionResourceInformationItem.h"
|
||||
#include "NGAP_QosFlowInformationItem.h"
|
||||
#include "NGAP_LastVisitedCellItem.h"
|
||||
#include "NGAP_LastVisitedNGRANCellInformation.h"
|
||||
#include "NGAP_PDUSessionResourceInformationList.h"
|
||||
#include "NGAP_HandoverRequiredTransfer.h"
|
||||
#include "NGAP_SourceNGRANNode-ToTargetNGRANNode-TransparentContainer.h"
|
||||
|
||||
#endif // NGAP_MSG_INCLUDES_H
|
||||
|
||||
Reference in New Issue
Block a user