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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user