Merge remote-tracking branch 'origin/n2-handover' into integration_2025_w38 (!3334)

N2 handover

This MR introduces full support for N2 handover, covering handover
decision, preparation, execution, and notification between source and
target NG-RAN nodes.

Key features:

1. Handover Decision & Preparation:
  - Implement N2 handover decision based on UE measurement reports.
  - Generate the Handover Preparation Information message, including
    RRCReconfiguration.
  - Send Handover Required message from the source NG-RAN to the AMF.
2. Handover Execution:
  - Decode and handle the Handover Request on the target NG-RAN.
  - Process the Handover Command on the source gNB, triggering
    RRCReconfiguration.
  - Initiate Handover Notify after successful completion.
3 NGAP & RRC Enhancements:
  - Implement NGAP encoding/decoding functions for Handover Required,
    Handover Request, Handover Command, Handover Failure, and Handover
    Notify (3GPP TS 38.413).
  - Adds NG-RAN Status Transfer support (UL/DL) for PDCP COUNT
    preservation during mobility:
    - support for NGAP Uplink and Downlink RAN Status Transfer messages
      (TS 38.413 §9.2.3.13-14) to enable proper PDCP COUNT value
      transfer during handover or recovery: NGAP encoding/decoding for
      RAN Status Transfer, PDCP helpers to extract and apply COUNT (HFN
      + SN) per DRB. This aligns with TS 38.300 §9.2.3.2.1 and TS 23.502
      §4.9.1.3.3, ensuring PDCP status preservation in AM.
4. Testing:
  - Introduce a Telnet command to manually trigger N2 handover using UE
    ID and neighbor PCI.
  - Add configuration file for testing N2 handover with a target gNB.
This commit is contained in:
Robert Schmidt
2025-09-18 11:57:44 +02:00
52 changed files with 4922 additions and 286 deletions
+20 -3
View File
@@ -283,6 +283,22 @@ typedef struct bearer_context_pdcp_config_s {
bool pDCP_Reestablishment;
} bearer_context_pdcp_config_t;
/* 9.3.1.35 3GPP TS 38.463 */
typedef struct {
// PDCP SN
uint32_t sn;
// HFN
uint32_t hfn;
} e1_pdcp_count_t;
/* 9.3.1.58 3GPP TS 38.463 */
typedef struct {
// UL Count Value
e1_pdcp_count_t ul_count;
// DL Count Value
e1_pdcp_count_t dl_count;
} e1_pdcp_status_info_t;
typedef struct drb_to_setup_s {
long drbId;
bearer_context_pdcp_config_t pdcp_config;
@@ -384,6 +400,8 @@ typedef struct DRB_nGRAN_to_modify_s {
bearer_context_pdcp_config_t *pdcp_config;
// PDCP SN Status Request (O)
bool pdcp_sn_status_requested;
// PDCP SN Status Information
e1_pdcp_status_info_t *pdcp_status;
// DL UP Transport Layer Information (O) (clause 9.3.1.13, 9.3.2.1)
int numDlUpParam;
up_params_t DlUpParamList[E1AP_MAX_NUM_UP_PARAM];
@@ -561,9 +579,8 @@ typedef struct DRB_nGRAN_modified_s {
// Flow Failed List (O)
int numQosFlowFailed;
qos_flow_failed_t qosFlowsFailed[E1AP_MAX_NUM_QOS_FLOWS];
// Old QoS Flow List (O)
int numOldQosFlow;
qos_flow_list_t oldQosFlows[E1AP_MAX_NUM_QOS_FLOWS];
// PDCP SN Status Information (0)
e1_pdcp_status_info_t *pdcp_status;
} DRB_nGRAN_modified_t;
typedef struct DRB_nGRAN_failed_s {
+10
View File
@@ -71,6 +71,13 @@ 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)
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)
MESSAGE_DEF(NGAP_HANDOVER_NOTIFY, MESSAGE_PRIORITY_MED, ngap_handover_notify_t, ngap_handover_notify)
MESSAGE_DEF(NGAP_HANDOVER_CANCEL, MESSAGE_PRIORITY_MED, ngap_handover_cancel_t, ngap_handover_cancel)
MESSAGE_DEF(NGAP_UL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_ul_ran_status_transfer)
MESSAGE_DEF(NGAP_DL_RAN_STATUS_TRANSFER, MESSAGE_PRIORITY_MED, ngap_ran_status_transfer_t, ngap_dl_ran_status_transfer)
/* NGAP -> RRC messages */
MESSAGE_DEF(NGAP_DOWNLINK_NAS , MESSAGE_PRIORITY_MED, ngap_downlink_nas_t , ngap_downlink_nas )
@@ -81,6 +88,9 @@ 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)
MESSAGE_DEF(NGAP_HANDOVER_COMMAND, MESSAGE_PRIORITY_MED, ngap_handover_command_t, ngap_handover_command)
MESSAGE_DEF(NGAP_HANDOVER_CANCEL_ACK, MESSAGE_PRIORITY_MED, ngap_handover_cancel_ack_t, ngap_handover_cancel_ack)
/* 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)
+301 -9
View File
@@ -65,11 +65,22 @@
#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_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_HANDOVER_COMMAND(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_command
#define NGAP_HANDOVER_NOTIFY(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_notify
#define NGAP_HANDOVER_CANCEL(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel
#define NGAP_HANDOVER_CANCEL_ACK(mSGpTR) (mSGpTR)->ittiMsg.ngap_handover_cancel_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
#define NGAP_PDUSESSION_RELEASE_RESPONSE(mSGpTR) (mSGpTR)->ittiMsg.ngap_pdusession_release_resp
#define NGAP_UL_RAN_STATUS_TRANSFER(mSGpTR) (mSGpTR)->ittiMsg.ngap_ul_ran_status_transfer
#define NGAP_DL_RAN_STATUS_TRANSFER(mSGpTR) (mSGpTR)->ittiMsg.ngap_dl_ran_status_transfer
//-------------------------------------------------------------------------------------------//
/* Length of the transport layer address string
@@ -227,6 +238,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,
@@ -463,6 +483,244 @@ 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;
/* 3GPP TS 38.413 9.2.3.6 */
typedef struct {
// AMF UE NGAP ID (M)
uint64_t amf_ue_ngap_id;
// Cause (M)
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;
/* 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;
/* 9.3.4.10 3GPP TS 38.413 */
typedef struct {
// QoS Flow to be Forwarded List
uint8_t nb_of_qos_flow;
pdusession_associate_qosflow_t qos_setup_list[QOSFLOW_MAX_VALUE];
// UL Forwarding UP TNL Information
uint32_t gtp_teid;
transport_layer_addr_t gNB_addr;
} ho_command_transfer_t;
typedef struct {
// PDU Session ID (M)
uint8_t pdusession_id;
// Handover Command Transfer (M)
ho_command_transfer_t ho_command_transfer;
} pdusession_resource_handover_t;
typedef struct {
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// Handover Type
ho_type_t handoverType;
// PDU Session Resource Handover List
uint8_t nb_of_pdusessions;
pdusession_resource_handover_t pdu_sessions[NGAP_MAX_PDU_SESSION];
// Target to Source Transparent Container
byte_array_t handoverCommand;
} ngap_handover_command_t;
/* 9.3.1.16 3GPP TS 38.413 */
typedef struct {
// NR user location information
target_ran_node_id_t target_ng_ran;
uint32_t nrCellIdentity;
} user_location_information_t;
/* 9.2.3.7 3GPP TS 38.413 */
typedef struct {
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
// User Location Information
user_location_information_t user_info;
} ngap_handover_notify_t;
/* 9.2.3.11 3GPP TS 38.413 */
typedef struct {
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
// Cause
ngap_cause_t cause;
} ngap_handover_cancel_t;
/* 9.2.3.12 3GPP TS 38.413 */
typedef struct {
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
} ngap_handover_cancel_ack_t;
typedef struct ngap_ue_cap_info_ind_s {
uint32_t gNB_ue_ngap_id;
byte_array_t ue_radio_cap;
@@ -533,15 +791,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;
@@ -737,4 +986,47 @@ typedef struct ngap_pdusession_release_resp_s {
} ngap_pdusession_release_resp_t;
/** 9.2.3.14 Uplink RAN Status Transfer (3GPP TS 38.413)
* COUNT value used for both UL and DL PDCP SN + HFN (12-bit or 18-bit SN) */
// Indicates PDCP SN length
typedef enum { NGAP_SN_LENGTH_12 = 0, NGAP_SN_LENGTH_18 = 1 } ngap_sn_length_t;
typedef struct {
// PDCP Sequence Number
uint32_t pdcp_sn;
// Hyper Frame Number
uint32_t hfn;
// SN length
ngap_sn_length_t sn_len;
} ngap_drb_count_value_t;
// DRBs Subject to Status Transfer Item
typedef struct {
// DRB ID
uint8_t drb_id;
// UL COUNT value
ngap_drb_count_value_t ul_count;
// DL COUNT value
ngap_drb_count_value_t dl_count;
} ngap_drb_status_t;
// RAN Status Transfer Transparent Container (9.3.1.108)
typedef struct {
// Number of DRBs in the list
uint8_t nb_drb;
// DRB Status List
ngap_drb_status_t drb_status_list[MAX_DRBS_PER_UE];
} ngap_ran_status_container_t;
// Uplink RAN Status Transfer message (9.2.3.14)
typedef struct {
// AMF UE NGAP ID (Mandatory)
uint64_t amf_ue_ngap_id;
// RAN UE NGAP ID (Mandatory)
uint32_t gnb_ue_ngap_id;
// RAN Status Transfer Transparent Container (Mandatory)
ngap_ran_status_container_t ran_status;
} ngap_ran_status_transfer_t;
#endif /* NGAP_MESSAGES_TYPES_H_ */
@@ -360,6 +360,39 @@ static E1AP_UP_Parameters_t encode_dl_up_parameters(const int numUpParam, const
return out;
}
/** @brief PDCP SN Status Information (9.3.1.58 3GPP TS 38.463) */
static E1AP_PDCP_SN_Status_Information_t encode_pdcp_status_info(const e1_pdcp_status_info_t *in)
{
E1AP_PDCP_SN_Status_Information_t pdcp = {0};
pdcp.pdcpStatusTransfer_DL.hFN = in->dl_count.hfn;
pdcp.pdcpStatusTransfer_DL.pDCP_SN = in->dl_count.sn;
pdcp.pdcpStatusTransfer_UL.countValue.hFN = in->ul_count.hfn;
pdcp.pdcpStatusTransfer_UL.countValue.pDCP_SN = in->ul_count.sn;
return pdcp;
}
/** @brief PDCP SN Status Information (9.3.1.58 3GPP TS 38.463) */
static e1_pdcp_status_info_t decode_pdcp_status_info(const E1AP_PDCP_SN_Status_Information_t* in)
{
e1_pdcp_status_info_t out = {0};
const E1AP_PDCP_Count_t *dl = &in->pdcpStatusTransfer_DL;
const E1AP_PDCP_Count_t *ul = &in->pdcpStatusTransfer_UL.countValue;
out.dl_count.hfn = dl->hFN;
out.dl_count.sn = dl->pDCP_SN;
out.ul_count.hfn = ul->hFN;
out.ul_count.sn = ul->pDCP_SN;
return out;
}
static bool eq_pdcp_info(const e1_pdcp_status_info_t *a, const e1_pdcp_status_info_t *b)
{
_E1_EQ_CHECK_INT(a->dl_count.hfn, b->dl_count.hfn);
_E1_EQ_CHECK_INT(a->dl_count.sn, b->dl_count.sn);
_E1_EQ_CHECK_INT(a->ul_count.hfn, b->ul_count.hfn);
_E1_EQ_CHECK_INT(a->ul_count.sn, b->ul_count.sn);
return true;
}
/**
* @brief Equality check for DRB_nGRAN_to_setup_t
*/
@@ -1409,6 +1442,11 @@ static E1AP_PDU_Session_Resource_To_Modify_Item_t e1_encode_pdu_session_to_mod_i
asn1cCalloc(drb2Mod->pDCP_SN_Status_Request, pDCP_SN_Status_Request);
*pDCP_SN_Status_Request = E1AP_PDCP_SN_Status_Request_requested;
}
// PDCP Status Information (O)
if (j->pdcp_status) {
asn1cCalloc(drb2Mod->pdcp_SN_Status_Information, pdcp);
*pdcp = encode_pdcp_status_info(j->pdcp_status);
}
// DL UP TNL parameters (O)
if (j->numDlUpParam > 0) {
asn1cCalloc(drb2Mod->dL_UP_Parameters, DL_UP_Param_List);
@@ -1658,7 +1696,7 @@ static bool e1_decode_pdu_session_to_mod_item(pdu_session_to_mod_t *out, const E
drb->id = drb2Mod->dRB_ID;
// PDCP Config (O)
if (drb2Mod->pDCP_Configuration) {
drb->pdcp_config = malloc_or_fail(sizeof(*drb->pdcp_config));
drb->pdcp_config = calloc_or_fail(1, sizeof(*drb->pdcp_config));
CHECK_E1AP_DEC(e1_decode_pdcp_config(drb->pdcp_config, drb2Mod->pDCP_Configuration));
}
// SDAP Config (O)
@@ -1667,9 +1705,14 @@ static bool e1_decode_pdu_session_to_mod_item(pdu_session_to_mod_t *out, const E
CHECK_E1AP_DEC(e1_decode_sdap_config(drb->sdap_config, drb2Mod->sDAP_Configuration));
}
// PDCP SN Status Request (O)
if (drb2Mod->pDCP_SN_Status_Request) {
if (drb2Mod->pDCP_SN_Status_Request && *drb2Mod->pDCP_SN_Status_Request == E1AP_PDCP_SN_Status_Request_requested) {
drb->pdcp_sn_status_requested = true;
}
// PDCP SN Status Information (O)
if (drb2Mod->pdcp_SN_Status_Information) {
drb->pdcp_status = malloc_or_fail(sizeof(*drb->pdcp_status));
*drb->pdcp_status = decode_pdcp_status_info(drb2Mod->pdcp_SN_Status_Information);
}
// DL UP TNL parameters (O)
if (drb2Mod->dL_UP_Parameters) {
E1AP_UP_Parameters_t *dl_up_paramList = drb2Mod->dL_UP_Parameters;
@@ -1843,6 +1886,7 @@ static DRB_nGRAN_to_mod_t cp_drb_to_mod_item(const DRB_nGRAN_to_mod_t *msg)
cp = *msg;
_E1_CP_OPTIONAL_IE(&cp, msg, sdap_config);
_E1_CP_OPTIONAL_IE(&cp, msg, pdcp_config);
_E1_CP_OPTIONAL_IE(&cp, msg, pdcp_status);
return cp;
}
@@ -1904,12 +1948,15 @@ static bool eq_drb_to_mod(const DRB_nGRAN_to_mod_t *a, const DRB_nGRAN_to_mod_t
_E1_EQ_CHECK_INT(a->numDlUpParam, b->numDlUpParam);
for (int i = 0; i < a->numDlUpParam; i++) {
_E1_EQ_CHECK_INT(a->DlUpParamList[i].cell_group_id, b->DlUpParamList[i].cell_group_id);
eq_up_tl_info(&a->DlUpParamList[i].tl_info, &b->DlUpParamList[i].tl_info);
result &= eq_up_tl_info(&a->DlUpParamList[i].tl_info, &b->DlUpParamList[i].tl_info);
}
if (a->pdcp_config && b->pdcp_config)
result &= eq_pdcp_config(a->pdcp_config, b->pdcp_config);
if (a->sdap_config && b->sdap_config)
result &= eq_sdap_config(a->sdap_config, b->sdap_config);
_E1_EQ_CHECK_OPTIONAL_PTR(a, b, pdcp_status);
if (a->pdcp_status && b->pdcp_status)
result &= eq_pdcp_info(a->pdcp_status, b->pdcp_status);
return result;
}
@@ -1984,6 +2031,7 @@ static void free_drb_to_mod_item(const DRB_nGRAN_to_mod_t *msg)
{
free(msg->pdcp_config);
free(msg->sdap_config);
free(msg->pdcp_status);
}
/* Free PDU Session to modify item */
@@ -2111,6 +2159,13 @@ struct E1AP_E1AP_PDU *encode_E1_bearer_context_mod_response(const e1ap_bearer_mo
asn1cSequenceAdd(drb_mod->flow_Failed_List->list, E1AP_QoS_Flow_Failed_Item_t, fail);
encode_qos_flow_failed_item(fail, &drb->qosFlowsFailed[q]);
}
// PDCP Status Information (O)
if (drb->pdcp_status) {
asn1cCalloc(drb_mod->pDCP_SN_Status_Information, pdcp);
*pdcp = encode_pdcp_status_info(drb->pdcp_status);
}
// UL UP Parameters (O)
if (drb->numUpParam)
drb_mod->uL_UP_Transport_Parameters = calloc_or_fail(1, sizeof(*drb_mod->uL_UP_Transport_Parameters));
@@ -2310,6 +2365,11 @@ bool decode_E1_bearer_context_mod_response(e1ap_bearer_modif_resp_t *out, const
drbMod->qosFlowsFailed[i] = decode_qos_flow_failed_item(drbIE->flow_Failed_List->list.array[i]);
}
}
// PDCP Status Info (O)
if (drbIE->pDCP_SN_Status_Information) {
drbMod->pdcp_status = malloc_or_fail(sizeof(*drbMod->pdcp_status));
*drbMod->pdcp_status = decode_pdcp_status_info(drbIE->pDCP_SN_Status_Information);
}
}
}
// DRB Failed to Mod list
@@ -2347,8 +2407,12 @@ e1ap_bearer_modif_resp_t cp_bearer_context_mod_response(const e1ap_bearer_modif_
// Shallow copy
cp = *msg;
// Deep copy PDU sessions modified items
for (int i = 0; i < msg->numPDUSessionsMod; i++)
for (int i = 0; i < msg->numPDUSessionsMod; i++) {
cp.pduSessionMod[i] = cp_pdu_session_mod_item(&msg->pduSessionMod[i]);
for (int j = 0; j < msg->pduSessionMod[i].numDRBModified; j++) {
_E1_CP_OPTIONAL_IE(&cp.pduSessionMod[i].DRBnGRanModList[j], &msg->pduSessionMod[i].DRBnGRanModList[j], pdcp_status);
}
}
return cp;
}
@@ -2359,6 +2423,8 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
{
if (!a || !b) return false; // Null-check both inputs
bool result = true;
// Basic members
_E1_EQ_CHECK_INT(a->gNB_cu_cp_ue_id, b->gNB_cu_cp_ue_id);
_E1_EQ_CHECK_INT(a->gNB_cu_up_ue_id, b->gNB_cu_up_ue_id);
@@ -2375,7 +2441,7 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
_E1_EQ_CHECK_OPTIONAL_PTR(pduA, pduB, ng_DL_UP_TL_info);
if (pduA->ng_DL_UP_TL_info && pduB->ng_DL_UP_TL_info)
eq_up_tl_info(pduA->ng_DL_UP_TL_info, pduB->ng_DL_UP_TL_info);
result &= eq_up_tl_info(pduA->ng_DL_UP_TL_info, pduB->ng_DL_UP_TL_info);
// DRB Modified List
_E1_EQ_CHECK_INT(pduA->numDRBModified, pduB->numDRBModified);
@@ -2387,6 +2453,10 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
for (int j = 0; j < drbModA->numQosFlowSetup; j++) {
_E1_EQ_CHECK_LONG(drbModA->qosFlows[j].qfi, drbModB->qosFlows[j].qfi);
}
_E1_EQ_CHECK_OPTIONAL_PTR(drbModA, drbModB, pdcp_status);
if (drbModA->pdcp_status && drbModB->pdcp_status) {
result &= eq_pdcp_info(drbModA->pdcp_status, drbModA->pdcp_status);
}
}
// DRB Failed to Modify List
@@ -2420,7 +2490,7 @@ bool eq_bearer_context_mod_response(const e1ap_bearer_modif_resp_t *a, const e1a
}
return true;
return result;
}
/**
@@ -2432,6 +2502,9 @@ void free_e1ap_context_mod_response(const e1ap_bearer_modif_resp_t *msg)
free(msg->pduSessionMod[i].confidentialityProtectionIndication);
free(msg->pduSessionMod[i].integrityProtectionIndication);
free(msg->pduSessionMod[i].ng_DL_UP_TL_info);
for (int j = 0; j < msg->pduSessionMod[i].numDRBModified; j++) {
free(msg->pduSessionMod[i].DRBnGRanModList[j].pdcp_status);
}
}
}
+18
View File
@@ -490,6 +490,13 @@ static void test_bearer_context_modification_request(void)
.qos_params.qos_characteristics.non_dynamic.fiveqi = 9,
};
const e1_pdcp_status_info_t dummy_pdcp_status = {
.dl_count.hfn = 12,
.dl_count.sn = 34,
.ul_count.hfn = 56,
.ul_count.sn = 78,
};
DRB_nGRAN_to_mod_t drb_to_mod = {
.numDlUpParam = 1,
.DlUpParamList[0].cell_group_id = MCG,
@@ -497,10 +504,12 @@ static void test_bearer_context_modification_request(void)
.id = 1,
.pdcp_config = malloc_or_fail(sizeof(*drb_to_mod.pdcp_config)),
.pdcp_sn_status_requested = true,
.pdcp_status = malloc_or_fail(sizeof(*drb_to_mod.pdcp_status)),
.numQosFlow2Setup = 1,
.qosFlows[0] = dummy_qos_flows,
};
*drb_to_mod.pdcp_config = dummy_pdcp_config;
*drb_to_mod.pdcp_status = dummy_pdcp_status;
pdu_session_to_mod_t pdusession_mod_item = {
.sessionId = 1,
@@ -592,12 +601,21 @@ const DRB_nGRAN_failed_t dummy_drb_failed = {
*/
static void test_bearer_context_modification_response(void)
{
const e1_pdcp_status_info_t dummy_pdcp_status = {
.dl_count.hfn = 12,
.dl_count.sn = 2,
.ul_count.hfn = 12,
.ul_count.sn = 3,
};
// DRB Modified List
DRB_nGRAN_modified_t drb_mod = {
.id = 1,
.numQosFlowSetup = 1,
.qosFlows[0].qfi = 1,
.pdcp_status = malloc_or_fail(sizeof(*drb_mod.pdcp_status)),
};
*drb_mod.pdcp_status = dummy_pdcp_status;
// DRB Setup List
DRB_nGRAN_setup_t setup = {
+27 -13
View File
@@ -722,6 +722,8 @@ void RCconfig_verify(configmodule_interface_t *cfg, ngran_node_t node_type)
verify_section_notset(cfg, GNB_CONFIG_STRING_GNB_LIST ".[0]", GNB_CONFIG_STRING_AMF_IP_ADDRESS);
verify_section_notset(cfg, NULL, CONFIG_STRING_SECURITY);
verify_section_notset(cfg, NULL, CONFIG_STRING_NR_PDCP_LIST);
verify_section_notset(cfg, NULL, GNB_CONFIG_STRING_NEIGHBOUR_LIST);
verify_section_notset(cfg, NULL, GNB_CONFIG_STRING_MEASUREMENT_CONFIGURATION);
} // else nothing to be checked
/* other possible verifications: PNF, VNF, CU-CP, CU-UP, ...? */
@@ -1858,21 +1860,21 @@ static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc
GNBNEIGHBOURCELLPARAMS_DESC,
GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST,
neighbourpath);
LOG_D(GNB_APP, "HO LOG: For the Cell: %ld Neighbour Cell ELM NUM: %d\n", cell.nr_cell_id, NeighbourCellParamList.numelt);
LOG_I(GNB_APP, "Cell %ld has %d neighbours\n", cell.nr_cell_id, NeighbourCellParamList.numelt);
if (NeighbourCellParamList.numelt < 1)
continue;
cell.neighbour_cells = malloc_or_fail(sizeof(seq_arr_t));
seq_arr_init(cell.neighbour_cells, sizeof(nr_neighbour_cell_t));
for (int l = 0; l < NeighbourCellParamList.numelt; ++l) {
nr_neighbour_cell_t neighbourCell = {0};
neighbourCell.gNB_ID = *(NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr);
neighbourCell.nrcell_id = (uint64_t) * (NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr);
neighbourCell.physicalCellId = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr;
neighbourCell.subcarrierSpacing = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_SCS_IDX].uptr;
neighbourCell.band = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_BAND_IDX].uptr;
neighbourCell.absoluteFrequencySSB = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr;
neighbourCell.tac = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_TAC_IDX].uptr;
nr_neighbour_cell_t n = {0};
n.gNB_ID = *(NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr);
n.nrcell_id = (uint64_t) * (NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr);
n.physicalCellId = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr;
n.subcarrierSpacing = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_SCS_IDX].uptr;
n.band = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_BAND_IDX].uptr;
n.absoluteFrequencySSB = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr;
n.tac = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_TAC_IDX].uptr;
char neighbour_plmn_path[CONFIG_MAXOPTLENGTH];
snprintf(neighbour_plmn_path,
@@ -1884,10 +1886,22 @@ static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc
GNB_CONFIG_STRING_NEIGHBOUR_PLMN);
GET_PARAMS(NeighbourPlmn, GNBPLMNPARAMS_DESC, neighbour_plmn_path);
neighbourCell.plmn.mcc = *NeighbourPlmn[GNB_MOBILE_COUNTRY_CODE_IDX].uptr;
neighbourCell.plmn.mnc = *NeighbourPlmn[GNB_MOBILE_NETWORK_CODE_IDX].uptr;
neighbourCell.plmn.mnc_digit_length = *NeighbourPlmn[GNB_MNC_DIGIT_LENGTH].uptr;
seq_arr_push_back(cell.neighbour_cells, &neighbourCell, sizeof(neighbourCell));
n.plmn.mcc = *NeighbourPlmn[GNB_MOBILE_COUNTRY_CODE_IDX].uptr;
n.plmn.mnc = *NeighbourPlmn[GNB_MOBILE_NETWORK_CODE_IDX].uptr;
n.plmn.mnc_digit_length = *NeighbourPlmn[GNB_MNC_DIGIT_LENGTH].uptr;
seq_arr_push_back(cell.neighbour_cells, &n, sizeof(n));
LOG_I(GNB_APP,
" [%d] neighbor ID %d cellId %ld PCI %d SCS %d SSB ARFCN %u TAC %u PLMN %03u.%0*u\n",
l,
n.gNB_ID,
n.nrcell_id,
n.physicalCellId,
n.subcarrierSpacing,
n.absoluteFrequencySSB,
n.tac,
n.plmn.mcc,
n.plmn.mnc_digit_length,
n.plmn.mnc);
}
seq_arr_push_back(rrc->neighbour_cell_configuration, &cell, sizeof(cell));
}
@@ -3461,11 +3461,10 @@ void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot
continue;
NR_timer_t *t = &UE->UE_sched_ctrl.transm_interrupt;
interrupt_followup_action_t a = nr_timer_is_active(t) ? UE->interrupt_action : FOLLOW_INSYNC;
// start a timer to stop scheduling UE during MeasGap, or extend timer for
// duration of measGap with existing follow-up action
if (!nr_timer_is_active(t) || nr_timer_remaining_time(t) < mgc->mgl_slots) {
nr_mac_interrupt_ue_transmission(nr_mac, UE, a, mgc->mgl_slots);
nr_mac_interrupt_ue_transmission(nr_mac, UE, mgc->mgl_slots);
}
}
}
@@ -3549,7 +3548,7 @@ int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs)
return (delay_ms << scs) + sl_ahead;
}
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interrupt_followup_action_t action, int slots)
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots)
{
DevAssert(mac != NULL);
DevAssert(UE != NULL);
@@ -3557,7 +3556,6 @@ int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interr
nr_timer_setup(&UE->UE_sched_ctrl.transm_interrupt, slots, 1);
nr_timer_start(&UE->UE_sched_ctrl.transm_interrupt);
UE->interrupt_action = action;
// it might happen that timing advance command should be sent during the UE
// inactivity time. To prevent this, set a variable as if we would have just
@@ -3565,14 +3563,27 @@ int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interr
// frames, after the inactivity of the UE.
UE->UE_sched_ctrl.ta_frame = (mac->frame - 1 + 1024) % 1024;
LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots) action %d\n", UE->rnti, slots, UE->interrupt_action);
LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots)\n", UE->rnti, slots);
return 0;
}
static void nr_mac_ue_transmission_timeout(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots)
{
DevAssert(mac != NULL);
DevAssert(UE != NULL);
NR_SCHED_ENSURE_LOCKED(&mac->sched_lock);
nr_timer_setup(&UE->UE_sched_ctrl.transm_timeout, slots, 1);
nr_timer_start(&UE->UE_sched_ctrl.transm_timeout);
LOG_I(NR_MAC, "UE %04x: Timeout for UE transmission (%d slots)\n", UE->rnti, slots);
return;
}
int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info)
{
int delay = nr_mac_get_reconfig_delay_slots(UE_info->current_DL_BWP.scs);
nr_mac_interrupt_ue_transmission(mac, UE_info, FOLLOW_OUTOFSYNC, delay);
nr_mac_ue_transmission_timeout(mac, UE_info, delay);
LOG_I(NR_MAC, "gNB-DU received the TransmissionActionIndicator with Stop value for UE %04x\n", UE_info->rnti);
return 0;
}
@@ -3641,9 +3652,11 @@ void nr_mac_update_timers(module_id_t module_id, frame_t frame, slot_t slot)
if (nr_timer_tick(&sched_ctrl->transm_interrupt)) {
/* expired */
nr_timer_stop(&sched_ctrl->transm_interrupt);
if (UE->interrupt_action == FOLLOW_OUTOFSYNC)
nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs);
/* else: default FOLLOW_INSYNC: nothing to do (UE is now active again) */
}
if (nr_timer_tick(&sched_ctrl->transm_timeout)) {
nr_timer_stop(&sched_ctrl->transm_timeout);
LOG_W(NR_MAC, "UE %04x UL failure after transmission timeout\n", UE->rnti);
nr_mac_trigger_ul_failure(sched_ctrl, UE->current_DL_BWP.scs);
}
}
}
+1 -1
View File
@@ -65,7 +65,7 @@ void nr_mac_prepare_ra_ue(gNB_MAC_INST *nrmac, NR_UE_info_t *UE);
bool add_new_UE_RA(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE);
int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs);
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, interrupt_followup_action_t action, int slots);
int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots);
int nr_transmission_action_indicator_stop(gNB_MAC_INST *mac, NR_UE_info_t *UE_info);
void clear_nr_nfapi_information(gNB_MAC_INST *gNB,
+4 -3
View File
@@ -688,6 +688,10 @@ typedef struct {
/// Timer for RRC processing procedures and transmission activity
NR_timer_t transm_interrupt;
/// Timer for timeout before UE is set to UL failure (e.g.,
/// "TransmissionActionIndicator" handling
NR_timer_t transm_timeout;
/// sri, ul_ri and tpmi based on SRS
nr_srs_feedback_t srs_feedback;
@@ -762,8 +766,6 @@ typedef struct measgap_config {
int mgl_slots;
} measgap_config_t;
typedef enum interrupt_followup_action { FOLLOW_INSYNC, FOLLOW_OUTOFSYNC } interrupt_followup_action_t;
/*! \brief UE list used by gNB to order UEs/CC for scheduling*/
typedef struct {
rnti_t rnti;
@@ -780,7 +782,6 @@ typedef struct {
/// in case of reestablishment, old spCellConfig to apply after
/// reconfiguration
NR_SpCellConfig_t *reconfigSpCellConfig;
interrupt_followup_action_t interrupt_action;
NR_UE_NR_Capability_t *capability;
measgap_config_t measgap_config;
// UE selected beam index
@@ -291,6 +291,32 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
&security_parameters);
}
// PDCP Status Information requested
if (to_modif->pdcp_sn_status_requested) {
modified->pdcp_status = malloc_or_fail(sizeof(*modified->pdcp_status));
nr_pdcp_count_t dl_count = {0};
nr_pdcp_count_t ul_count = {0};
nr_pdcp_get_drb_count_values(req->gNB_cu_cp_ue_id, to_modif->id, &ul_count, &dl_count);
modified->pdcp_status->dl_count.hfn = dl_count.hfn;
modified->pdcp_status->dl_count.sn = dl_count.sn;
modified->pdcp_status->ul_count.hfn = ul_count.hfn;
modified->pdcp_status->ul_count.sn = ul_count.sn;
}
// PDCP Status received (from source CU-UP)
if (to_modif->pdcp_status) {
DevAssert(to_modif->pdcp_config);
DevAssert(to_modif->pdcp_config->pDCP_SN_Size_DL == to_modif->pdcp_config->pDCP_SN_Size_UL);
bool is_sn_len_18 = to_modif->pdcp_config->pDCP_SN_Size_DL == NR_PDCP_Config__drb__pdcp_SN_SizeDL_len18bits;
nr_pdcp_count_t dl_count = { .hfn = to_modif->pdcp_status->dl_count.hfn, .sn = to_modif->pdcp_status->dl_count.sn};
nr_pdcp_count_t ul_count = { .hfn = to_modif->pdcp_status->ul_count.hfn, .sn = to_modif->pdcp_status->ul_count.sn};
nr_pdcp_count_update(req->gNB_cu_cp_ue_id,
to_modif->id,
dl_count,
ul_count,
is_sn_len_18 ? LONG_SN_SIZE : SHORT_SN_SIZE);
}
if (f1inst < 0) // no F1-U?
continue; // nothing to do
@@ -300,6 +326,7 @@ void e1_bearer_context_modif(const e1ap_bearer_mod_req_t *req)
in_addr_t addr = to_modif->DlUpParamList[k].tl_info.tlAddress;
GtpuUpdateTunnelOutgoingAddressAndTeid(f1inst, req->gNB_cu_cp_ue_id, to_modif->id, addr, to_modif->DlUpParamList[k].tl_info.teId);
}
}
}
+94
View File
@@ -601,6 +601,94 @@ static void nr_pdcp_entity_get_stats(nr_pdcp_entity_t *entity,
*out = entity->stats;
}
/** @note: this function applies to the gNB only */
static nr_pdcp_count_t nr_pdcp_entity_get_count_dl(nr_pdcp_entity_t *entity)
{
nr_pdcp_count_t count = {0};
const int sn_bits = entity->sn_size;
const uint32_t sn_mask = entity->sn_max;
count.hfn = entity->rx_deliv >> sn_bits;
count.sn = entity->rx_deliv & sn_mask;
return count;
}
/** @note: this function applies to the gNB only */
static nr_pdcp_count_t nr_pdcp_entity_get_count_ul(nr_pdcp_entity_t *entity)
{
nr_pdcp_count_t count = {0};
const int sn_bits = entity->sn_size;
const uint32_t sn_mask = entity->sn_max;
count.hfn = entity->tx_next >> sn_bits;
count.sn = entity->tx_next & sn_mask;
return count;
}
/** @note: this function applies to the gNB only */
static void nr_pdcp_entity_set_count_dl(nr_pdcp_entity_t *entity, nr_pdcp_count_t pdcp_count, int sn_size)
{
// SN length in bits
const uint32_t sn_mask = (1 << sn_size) - 1;
// Compose COUNT values for tx_next (DL) and rx_next (UL)
uint32_t count = (pdcp_count.hfn << sn_size) | (pdcp_count.sn & sn_mask);
// Apply the COUNT values
entity->rx_next = count + 1;
entity->rx_deliv = count;
LOG_I(PDCP,
"DRB %d: Applied PDCP Count\n"
" DL COUNT - SN: %d HFN: %d (COUNT=%u)\n"
" SN length: %s\n"
" PDCP entity state after update:\n"
" rx_next = %u\n"
" rx_deliv = %u\n"
" sn_size = %d\n"
" sn_max = %d\n"
" window = %d\n",
entity->rb_id,
pdcp_count.sn,
pdcp_count.hfn,
count,
sn_size == LONG_SN_SIZE ? "18-bit" : "12-bit",
entity->rx_next,
entity->rx_deliv,
entity->sn_size,
entity->sn_max,
entity->window_size);
}
/** @note: this function applies to the gNB only */
static void nr_pdcp_entity_set_count_ul(nr_pdcp_entity_t *entity, nr_pdcp_count_t pdcp_count, int sn_size)
{
// SN length in bits
const uint32_t sn_mask = (1 << sn_size) - 1;
// Compose COUNT values for tx_next (DL) and rx_next (UL)
uint32_t count = (pdcp_count.hfn << sn_size) | (pdcp_count.sn & sn_mask);
// Apply the COUNT values
entity->tx_next = count;
LOG_I(PDCP,
"DRB %d: Applied PDCP Count\n"
" UL COUNT - SN: %d HFN: %d (COUNT=%u)\n"
" SN length: %s\n"
" PDCP entity state after update:\n"
" tx_next = %u\n"
" sn_size = %d\n"
" sn_max = %d\n"
" window = %d\n",
entity->rb_id,
pdcp_count.sn,
pdcp_count.hfn,
count,
sn_size == LONG_SN_SIZE ? "18-bit" : "12-bit",
entity->tx_next,
entity->sn_size,
entity->sn_max,
entity->window_size);
}
nr_pdcp_entity_t *new_nr_pdcp_entity(
nr_pdcp_entity_type_t type,
@@ -675,5 +763,11 @@ nr_pdcp_entity_t *new_nr_pdcp_entity(
nr_pdcp_entity_set_security(ret, security_parameters);
/* PDCP Count API */
ret->get_pdcp_count_dl = nr_pdcp_entity_get_count_dl;
ret->get_pdcp_count_ul = nr_pdcp_entity_get_count_ul;
ret->set_pdcp_count_dl = nr_pdcp_entity_set_count_dl;
ret->set_pdcp_count_ul = nr_pdcp_entity_set_count_ul;
return ret;
}
+12 -1
View File
@@ -55,6 +55,11 @@ typedef struct {
uint8_t ciphering_key[NR_K_KEY_SIZE];
} nr_pdcp_entity_security_keys_and_algos_t;
typedef struct {
uint32_t sn;
uint32_t hfn;
} nr_pdcp_count_t;
typedef struct {
//nr_pdcp_entity_type_t mode;
/* PDU stats */
@@ -87,7 +92,6 @@ typedef struct {
uint8_t mode; /* 0: PDCP AM, 1: PDCP UM, 2: PDCP TM */
} nr_pdcp_statistics_t;
typedef struct nr_pdcp_entity_t {
nr_pdcp_entity_type_t type;
@@ -180,6 +184,13 @@ typedef struct nr_pdcp_entity_t {
/* Keep tracks of whether the PDCP entity was suspended or not */
bool entity_suspended;
/* PDCP Count API */
nr_pdcp_count_t (*get_pdcp_count_dl)(struct nr_pdcp_entity_t *entity);
nr_pdcp_count_t (*get_pdcp_count_ul)(struct nr_pdcp_entity_t *entity);
void (*set_pdcp_count_dl)(struct nr_pdcp_entity_t *entity, nr_pdcp_count_t count, int sn_size);
void (*set_pdcp_count_ul)(struct nr_pdcp_entity_t *entity, nr_pdcp_count_t count, int sn_size);
} nr_pdcp_entity_t;
nr_pdcp_entity_t *new_nr_pdcp_entity(
+54
View File
@@ -1072,3 +1072,57 @@ int nr_pdcp_get_num_ues(ue_id_t *ue_list, int len)
return num_ues;
}
void nr_pdcp_count_update(ue_id_t ue_id,
rb_id_t drb_id,
nr_pdcp_count_t dl_count,
nr_pdcp_count_t ul_count,
int sn_size)
{
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id);
if (ue == NULL) {
LOG_E(PDCP, "UE %ld not found in PDCP manager\n", ue_id);
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
return;
}
nr_pdcp_entity_t *entity = nr_pdcp_get_rb(ue, drb_id, false);
if (entity == NULL) {
LOG_E(PDCP, "No PDCP entity found for UE %ld DRB %ld\n", ue_id, drb_id);
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
return;
}
entity->set_pdcp_count_dl(entity, dl_count, sn_size);
entity->set_pdcp_count_ul(entity, ul_count, sn_size);
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
}
/** * @brief Get PDCP COUNT values (PDCP-SN and HFN) for UL and DL from the PDCP entity
* @param[in] ue_id Unique UE ID
* @param[in] rb_id Radio Bearer ID
* @param[out] ul_count UL PDCP count (SN, HFN)
* @param[out] dl_count DL PDCP count (SN, HFN)
*/
void nr_pdcp_get_drb_count_values(ue_id_t ue_id, rb_id_t rb_id, nr_pdcp_count_t *ul_count, nr_pdcp_count_t *dl_count)
{
nr_pdcp_manager_lock(nr_pdcp_ue_manager);
nr_pdcp_ue_t *ue = nr_pdcp_manager_get_ue(nr_pdcp_ue_manager, ue_id);
nr_pdcp_entity_t *entity = nr_pdcp_get_rb(ue, rb_id, false);
if (!entity) {
LOG_E(PDCP, "No entity found (DRB %ld)\n", rb_id);
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
return;
}
*dl_count = entity->get_pdcp_count_dl(entity);
*ul_count = entity->get_pdcp_count_ul(entity);
nr_pdcp_manager_unlock(nr_pdcp_ue_manager);
}
@@ -125,4 +125,12 @@ int nr_pdcp_get_num_ues(ue_id_t *ue_list, int len);
bool nr_pdcp_get_statistics(ue_id_t ue_id, int srb_flag, int rb_id, nr_pdcp_statistics_t *out);
void nr_pdcp_count_update(ue_id_t ue_id,
rb_id_t drb_id,
nr_pdcp_count_t dl_count,
nr_pdcp_count_t ul_count,
int sn_size);
void nr_pdcp_get_drb_count_values(ue_id_t ue_id, rb_id_t rb_id, nr_pdcp_count_t *ul_count, nr_pdcp_count_t *dl_count);
#endif /* NR_PDCP_OAI_API_H */
+271
View File
@@ -64,6 +64,10 @@
#include "NR_LogicalChannelConfig.h"
#include "NR_PDCP-Config.h"
#include "NR_MAC-CellGroupConfig.h"
#include "NR_HandoverPreparationInformation.h"
#include "NR_HandoverCommand.h"
#include "NR_HandoverCommand-IEs.h"
#include "NR_AS-Config.h"
#include "NR_SecurityModeCommand.h"
#include "NR_CipheringAlgorithm.h"
#include "NR_RRCReconfiguration-IEs.h"
@@ -82,6 +86,8 @@
#include "NR_UE-CapabilityRequestFilterNR.h"
#include "NR_HandoverPreparationInformation.h"
#include "NR_HandoverPreparationInformation-IEs.h"
#include "NR_HandoverCommand.h"
#include "NR_UE-CapabilityRAT-ContainerList.h"
#include "common/utils/nr/nr_common.h"
#if defined(NR_Rel16)
#include "NR_SCS-SpecificCarrier.h"
@@ -197,6 +203,48 @@ int xer_nr_sprint(char *string, size_t string_size, asn_TYPE_descriptor_t *td, v
return er.encoded;
}
struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len)
{
if (!buf || len == 0) return NULL;
NR_UE_CapabilityRAT_ContainerList_t *clist = NULL;
asn_dec_rval_t rval = uper_decode(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList,
(void **)&clist, buf, len, 0, 0);
if (rval.code != RC_OK) {
LOG_W(NR_MAC, "UE RNTI %04x: Failed to decode container list\n", rnti);
return NULL;
}
NR_UE_NR_Capability_t *cap = decode_nr_ue_capability(rnti, clist);
ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, clist);
return cap;
}
NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist)
{
if (!clist) return NULL;
for (int i = 0; i < clist->list.count; i++) {
const NR_UE_CapabilityRAT_Container_t *c = clist->list.array[i];
if (c->rat_Type != NR_RAT_Type_nr)
continue;
NR_UE_NR_Capability_t *cap = NULL;
asn_dec_rval_t rval = uper_decode(NULL, &asn_DEF_NR_UE_NR_Capability,
(void **)&cap,
c->ue_CapabilityRAT_Container.buf,
c->ue_CapabilityRAT_Container.size,
0, 0);
if (rval.code == RC_OK)
return cap;
LOG_W(NR_MAC, "UE RNTI %04x: Failed to decode NR capability (%zu bytes)\n", rnti, rval.consumed);
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, cap);
}
return NULL;
}
//------------------------------------------------------------------------------
int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc)
@@ -564,6 +612,43 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re
return ie;
}
static byte_array_t do_HO_RRCReconfiguration(nr_rrc_reconfig_param_t *params)
{
NR_RRCReconfiguration_IEs_t *ie = build_RRCReconfiguration_IEs(params);
byte_array_t msg = {.buf = NULL, .len = 0};
// Create the standalone RRCReconfiguration message
NR_RRCReconfiguration_t rrcReconf = {0};
rrcReconf.rrc_TransactionIdentifier = params->transaction_id;
rrcReconf.criticalExtensions.present = NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration;
rrcReconf.criticalExtensions.choice.rrcReconfiguration = ie;
// Encode the message
int val = uper_encode_to_new_buffer(&asn_DEF_NR_RRCReconfiguration, NULL, &rrcReconf, (void **)&msg.buf);
if (val <= 0) {
LOG_E(NR_RRC, "ASN1 RRCReconfiguration message encoding failed\n");
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_RRCReconfiguration, &rrcReconf);
return msg;
}
msg.len = val;
LOG_D(NR_RRC, "RRCReconfiguration: Encoded (%ld bytes)\n", msg.len);
// don't free what we did not allocate, so set fields with pointers to NULL
// if memory comes from outside
ie->measConfig = NULL;
if (ie->radioBearerConfig) {
ie->radioBearerConfig->srb3_ToRelease = NULL;
ie->radioBearerConfig->drb_ToReleaseList = NULL;
ie->radioBearerConfig->drb_ToAddModList = NULL;
ie->radioBearerConfig->srb_ToAddModList = NULL;
ie->radioBearerConfig->securityConfig = NULL;
}
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_RRCReconfiguration, &rrcReconf);
return msg;
}
byte_array_t do_RRCReconfiguration(const nr_rrc_reconfig_param_t *params)
{
byte_array_t msg = {.buf = NULL, .len = 0};
@@ -1158,6 +1243,192 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
return mc;
}
/** @brief Prepares removal of measurement configurations related to the source gNB.
* This function decodes the HandoverPreparationInformation received from the source gNB,
* extracts the source MeasConfig, and marks those entries (measurement objects, report
* configurations, and measurement IDs) for removal by populating the corresponding
* ToRemoveList fields in the UE's current measConfig.
* This ensures that obsolete or source-specific measurement configurations are removed
* from the UE after handover and allows the target gNB to install its own valid configuration.
* @param measConfig Target gNB's current measurement configuration to be updated.
* @param prep_info Encoded HandoverPreparationInformation received from the source gNB.*/
void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *currentMC, byte_array_t prep_info)
{
if (currentMC == NULL) {
LOG_E(NR_RRC, "HO LOG: UE's Measurement Configuration is NULL!\n");
return;
}
// Decodes the HandoverPreparationInformation message
NR_HandoverPreparationInformation_t *hpi = NULL;
asn_dec_rval_t hoPrep_dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_HandoverPreparationInformation,
(void **)&hpi,
(uint8_t *)prep_info.buf,
prep_info.len);
if (hoPrep_dec_rval.code != RC_OK || hoPrep_dec_rval.consumed < 0) {
LOG_E(NR_RRC, "Handover Prep Info decode error while removing source gnb measurement configuration!\n");
return;
}
/* Decodes the RRCReconfiguration message within the HandoverPreparationInformation
and extract the measConfig provided by the source gNB */
if (!hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation->sourceConfig) {
LOG_W(NR_RRC, "Missing sourceConfig: in source gNB rrcReconfiguration\n");
return;
}
NR_RRCReconfiguration_t *rrcReconf = NULL;
NR_AS_Config_t *sourceConfig = hpi->criticalExtensions.choice.c1->choice.handoverPreparationInformation->sourceConfig;
asn_dec_rval_t rrcReconf_dec_rval = uper_decode_complete(NULL,
&asn_DEF_NR_RRCReconfiguration,
(void **)&rrcReconf,
(uint8_t *)sourceConfig->rrcReconfiguration.buf,
sourceConfig->rrcReconfiguration.size);
if (rrcReconf_dec_rval.code != RC_OK || rrcReconf_dec_rval.consumed < 0) {
LOG_E(NR_RRC, "Failed to decode source gNB rrcReconfiguration!\n");
return;
}
if (rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig == NULL)
return;
NR_MeasConfig_t *sourceMC = rrcReconf->criticalExtensions.choice.rrcReconfiguration->measConfig;
/* Add measurement objects, report configurations, and measurement IDs
to the UE's measConfig removal lists, and update UE->measConfig */
if (sourceMC->measObjectToAddModList->list.count > 0) {
currentMC->measObjectToRemoveList = calloc_or_fail(1, sizeof(*currentMC->measObjectToRemoveList));
for (int i = 0; i < sourceMC->measObjectToAddModList->list.count; i++) {
NR_MeasObjectId_t *measObjId = calloc_or_fail(1, sizeof(NR_MeasObjectId_t));
*measObjId = sourceMC->measObjectToAddModList->list.array[i]->measObjectId;
asn1cSeqAdd(&currentMC->measObjectToRemoveList->list, measObjId);
}
}
if (sourceMC->reportConfigToAddModList->list.count > 0) {
currentMC->reportConfigToRemoveList = calloc_or_fail(1, sizeof(*currentMC->reportConfigToRemoveList));
for (int i = 0; i < sourceMC->reportConfigToAddModList->list.count; i++) {
NR_ReportConfigId_t *reportConfigId = calloc_or_fail(1, sizeof(NR_ReportConfigId_t));
*reportConfigId = sourceMC->reportConfigToAddModList->list.array[i]->reportConfigId;
asn1cSeqAdd(&currentMC->reportConfigToRemoveList->list, reportConfigId);
}
}
if (sourceMC->measIdToAddModList->list.count > 0) {
currentMC->measIdToRemoveList = calloc_or_fail(1, sizeof(*currentMC->measIdToRemoveList));
for (int i = 0; i < sourceMC->measIdToAddModList->list.count; i++) {
NR_MeasId_t *measId = calloc_or_fail(1, sizeof(NR_MeasId_t));
*measId = sourceMC->measIdToAddModList->list.array[i]->measId;
asn1cSeqAdd(&currentMC->measIdToRemoveList->list, measId);
}
}
}
int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, const byte_array_t handoverCommand)
{
// Decode Handover Command
NR_HandoverCommand_t *hoCommand = NULL;
asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_HandoverCommand, (void **)&hoCommand, handoverCommand.buf, handoverCommand.len);
if (dec_rval.code != RC_OK && dec_rval.consumed == 0) {
LOG_E(NR_RRC, "Can not decode Handover Command!\n");
return -1;
}
// Prepare DL DCCH message for RRCReconfiguration
NR_DL_DCCH_Message_t dl_dcch_msg = {0};
asn_enc_rval_t enc_rval;
dl_dcch_msg.message.present = NR_DL_DCCH_MessageType_PR_c1;
asn1cCalloc(dl_dcch_msg.message.choice.c1, c1);
c1->present = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration;
asn1cCalloc(c1->choice.rrcReconfiguration, rrcReconf);
// Decode RRCReconfiguration from handoverCommandMessage
OCTET_STRING_t *ho = &hoCommand->criticalExtensions.choice.c1->choice.handoverCommand->handoverCommandMessage;
uper_decode_complete(NULL, &asn_DEF_NR_RRCReconfiguration, (void **)&rrcReconf, ho->buf, ho->size);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg);
// Encode DL DCCH message in buffer
enc_rval = uper_encode_to_buffer(&asn_DEF_NR_DL_DCCH_Message, NULL, &dl_dcch_msg, ba->buf, ba->len);
AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_DL_DCCH_Message, &dl_dcch_msg);
ASN_STRUCT_FREE(asn_DEF_NR_HandoverCommand, hoCommand);
return ((enc_rval.encoded + 7) / 8);
}
byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params)
{
// Encode RRCReconfiguration to buffer
byte_array_t msg = do_HO_RRCReconfiguration(params);
// Add RRCReconfiguration to handoverCommand
NR_HandoverCommand_t HoCommand = {0};
HoCommand.criticalExtensions.present = NR_HandoverCommand__criticalExtensions_PR_c1;
asn1cCalloc(HoCommand.criticalExtensions.choice.c1, c1);
c1->present = NR_HandoverCommand__criticalExtensions__c1_PR_handoverCommand;
asn1cCalloc(HoCommand.criticalExtensions.choice.c1->choice.handoverCommand, hc);
OCTET_STRING_fromBuf(&hc->handoverCommandMessage, (const char *)msg.buf, msg.len);
free_byte_array(msg);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_HandoverCommand, (void *)&HoCommand);
// Encode handoverCommand to buffer
byte_array_t buffer = {.len = -1, .buf = NULL};
buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_HandoverCommand, NULL, (void *)&HoCommand, (void **)&buffer.buf);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_HandoverCommand, &HoCommand);
return buffer;
}
/** @brief HandoverPreparationInformation message (11.2.2 of TS 38.331)
* 1) generate RRCReconfiguration to be transferred
* 2) encodes UE Capabilities from UE Context
* 3) encodes AS context
* 4) generates HO Preparation Info */
byte_array_t get_HandoverPreparationInformation(nr_rrc_reconfig_param_t *params, int scell_pci)
{
// Buffer to return
byte_array_t buffer = {.buf = NULL, .len = 0};
/* Prepare handoverPreparationInformation IEs */
NR_HandoverPreparationInformation_t hoPrepInfo = {0};
hoPrepInfo.criticalExtensions.present = NR_HandoverPreparationInformation__criticalExtensions_PR_c1;
asn1cCalloc(hoPrepInfo.criticalExtensions.choice.c1, c1);
c1->present = NR_HandoverPreparationInformation__criticalExtensions__c1_PR_handoverPreparationInformation;
asn1cCalloc(c1->choice.handoverPreparationInformation, hpi);
/* Decode stored NR_UE_CapabilityRAT_ContainerList and add to IEs */
NR_UE_CapabilityRAT_ContainerList_t *clist = NULL;
asn_dec_rval_t dec_rval =
uper_decode(NULL, &asn_DEF_NR_UE_CapabilityRAT_ContainerList, (void **)&clist, params->ue_cap.buf, params->ue_cap.len, 0, 0);
if (dec_rval.code != RC_OK) {
LOG_W(NR_RRC, "Failed to decode UE capability container list for HandoverPreparationInformation, ignoring capabilities\n");
return buffer;
}
hpi->ue_CapabilityRAT_List = *clist;
free(clist);
/* sourceConfig: Encode RRCReconfiguration as used in the source cell */
byte_array_t msg = do_HO_RRCReconfiguration(params);
asn1cCalloc(hpi->sourceConfig, sourceConfig);
OCTET_STRING_fromBuf(&sourceConfig->rrcReconfiguration, (const char *)msg.buf, msg.len);
free_byte_array(msg);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_HandoverPreparationInformation, (void **)&hoPrepInfo);
/* encode */
buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_HandoverPreparationInformation, NULL, (void *)&hoPrepInfo, (void *)&buffer.buf);
AssertFatal(buffer.len > 0, "ASN1 message encoding failed (%ld)!\n", buffer.len);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NR_HandoverPreparationInformation, &hoPrepInfo);
return buffer;
}
void free_MeasConfig(NR_MeasConfig_t *mc)
{
ASN_STRUCT_FREE(asn_DEF_NR_MeasConfig, mc);
+10
View File
@@ -52,6 +52,7 @@
#include "NR_SecurityConfig.h"
#include "NR_MeasurementReport.h"
#include "NR_MeasurementTimingConfiguration.h"
#include "NR_UE-CapabilityRAT-ContainerList.h"
#include "ds/seq_arr.h"
#include "ds/byte_array.h"
#include "rrc_messages_types.h"
@@ -71,6 +72,7 @@ typedef struct {
NR_CellGroupConfig_t *cell_group_config;
bool masterKeyUpdate;
int nextHopChainingCount;
byte_array_t ue_cap;
} nr_rrc_reconfig_param_t;
/*
@@ -172,4 +174,12 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt,
void free_MeasConfig(NR_MeasConfig_t *mc);
int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi);
byte_array_t get_HandoverPreparationInformation(nr_rrc_reconfig_param_t *params, int scell_pci);
byte_array_t get_HandoverCommandMessage(nr_rrc_reconfig_param_t *params);
void fill_removal_lists_from_source_measConfig(NR_MeasConfig_t *measConfig, byte_array_t prep_info);
int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, byte_array_t handoverCommand);
struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len);
NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist);
#endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */
+1 -1
View File
@@ -315,7 +315,7 @@ typedef struct {
int absoluteFrequencySSB;
int subcarrierSpacing;
int band;
plmn_identity_t plmn;
plmn_id_t plmn;
uint32_t tac;
bool isIntraFrequencyNeighbour;
} nr_neighbour_cell_t;
+5
View File
@@ -125,4 +125,9 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC
pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession);
f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param);
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);
void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs, const ngap_drb_status_t *drb_status);
#endif
+111 -40
View File
@@ -98,6 +98,7 @@
#include "NR_DL-DCCH-Message.h"
#include "ds/byte_array.h"
#include "alg/find.h"
#include "NR_HandoverCommand.h"
#ifdef E2_AGENT
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
@@ -307,7 +308,7 @@ unsigned int rrc_gNB_get_next_transaction_identifier(module_id_t gnb_mod_idP)
* for the radio bearer changes, with some expections for SRB1 (i.e. when resuming
* an RRC connection, or at the first reconfiguration after RRC connection
* reestablishment in NR, do not re-establish PDCP) */
static NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish)
NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish)
{
if (!ue->Srb[SRB1].Active) {
LOG_E(NR_RRC, "Call SRB list while SRB1 doesn't exist\n");
@@ -328,7 +329,7 @@ static NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablis
return list;
}
static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
{
NR_DRB_ToAddMod_t *DRB_config = NULL;
NR_DRB_ToAddModList_t *DRB_configList = CALLOC(sizeof(*DRB_configList), 1);
@@ -349,12 +350,12 @@ static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
return DRB_configList;
}
static void freeSRBlist(NR_SRB_ToAddModList_t *l)
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) {
@@ -1383,6 +1384,7 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc,
int servingCellRSRP = 0;
int neighbourCellRSRP = 0;
int scell_pci = -1;
int best_rsrp = -10000;
switch (event_triggered->eventId.present) {
case NR_EventTriggerConfig__eventId_PR_eventA2:
@@ -1443,14 +1445,20 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc,
// No F1 connection but static neighbour configuration is available
const nr_a3_event_t *a3_event_configuration = get_a3_configuration(rrc, neighbour->physicalCellId);
// Additional check - This part can be modified according to additional cell specific Handover Margin
// a3-Offset: The actual value is field value * 0.5 dB.
if (a3_event_configuration
&& ((a3_event_configuration->a3_offset + a3_event_configuration->hysteresis)
&& ((a3_event_configuration->a3_offset * 0.5 + a3_event_configuration->hysteresis)
< (neighbourCellRSRP - servingCellRSRP))) {
if (neighbourCellRSRP > best_rsrp) {
// UE can send multiple neighbour cells A3 event report in 1 Meas Report. So, we need to find the best neighbour
best_rsrp = neighbourCellRSRP;
LOG_I(NR_RRC, "HO LOG: Serving Cell RSRP: %d - Best Neighbor RSRP: %d ! Trigger N2 HO\n", servingCellRSRP, best_rsrp);
nr_rrc_trigger_n2_ho(rrc, ue, scell_pci, neighbour);
}
LOG_D(NR_RRC, "HO LOG: Trigger N2 HO for the neighbour gnb: %u cell: %lu\n", neighbour->gNB_ID, neighbour->nrcell_id);
}
} else if (neigh_cell && neighbour) {
/* we know the cell and are connected to the DU! */
gNB_RRC_INST *rrc = RC.nrrrc[0];
nr_rrc_du_container_t *source_du = get_du_by_cell_id(rrc, serving_cell->nr_cellid);
DevAssert(source_du);
nr_rrc_du_container_t *target_du = get_du_by_cell_id(rrc, neigh_cell->nr_cellid);
@@ -1459,7 +1467,6 @@ static void process_Event_Based_Measurement_Report(gNB_RRC_INST *rrc,
LOG_W(NR_RRC, "UE %d: received A3 event for stronger neighbor PCI %d, but no such neighbour in configuration\n", ue->rrc_ue_id, neighbour_pci);
}
}
} break;
default:
LOG_D(NR_RRC, "NR_EventTriggerConfig__eventId_PR_NOTHING or Other event report\n");
@@ -1600,21 +1607,14 @@ static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE,
UE->UE_Capability_nr = 0;
}
asn_dec_rval_t dec_rval = uper_decode(NULL,
&asn_DEF_NR_UE_NR_Capability,
(void **)&UE->UE_Capability_nr,
ue_cap_container->ue_CapabilityRAT_Container.buf,
ue_cap_container->ue_CapabilityRAT_Container.size,
0,
0);
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
UE->UE_Capability_nr = decode_nr_ue_capability(UE->rnti, ue_CapabilityRAT_ContainerList);
if (!UE->UE_Capability_nr) {
LOG_E(NR_RRC, "UE %d: NR capability decoding failed\n", UE->rrc_ue_id);
return;
}
if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
LOG_E(NR_RRC, "UE %d: Failed to decode nr UE capabilities (%zu bytes)\n", UE->rrc_ue_id, dec_rval.consumed);
ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
UE->UE_Capability_nr = 0;
if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
}
const NR_RedCapParameters_r17_t *redcap_p = get_redcapparam_r17(UE->UE_Capability_nr);
@@ -2076,9 +2076,28 @@ static void store_du_f1u_tunnel(const f1ap_drb_setup_t *drbs, int n, gNB_RRC_UE_
}
}
/* \brief use list of DRBs and send the corresponding bearer update message via
* E1 to the CU of this UE. Also updates TEID info internally */
static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs)
static void fill_e1_bearer_modif_pdcp_status(gNB_RRC_UE_t *UE,
DRB_nGRAN_to_mod_t *drb_to_mod,
const int drb_id,
const bool um_on_default_drb,
const ngap_drb_status_t *drb_status)
{
DevAssert(drb_status);
drb_to_mod->id = drb_id;
drb_to_mod->pdcp_sn_status_requested = false;
// PDCP SN Status Information
drb_to_mod->pdcp_config = calloc_or_fail(1, sizeof(*drb_to_mod->pdcp_config));
set_bearer_context_pdcp_config(drb_to_mod->pdcp_config, get_drb(UE, drb_id), um_on_default_drb);
drb_to_mod->pdcp_status = calloc_or_fail(1, sizeof(*drb_to_mod->pdcp_status));
drb_to_mod->pdcp_status->dl_count.hfn = drb_status->dl_count.hfn;
drb_to_mod->pdcp_status->dl_count.sn = drb_status->dl_count.pdcp_sn;
drb_to_mod->pdcp_status->ul_count.hfn = drb_status->ul_count.hfn;
drb_to_mod->pdcp_status->ul_count.sn = drb_status->ul_count.pdcp_sn;
}
/** @brief Fill and send Bearer Context Modification Request with:
* (1) from F1 UE Context DRB to setup list, or (2) with PDCP Status Request */
void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_setup_t *drbs, const ngap_drb_status_t *drb_status)
{
// Quit bearer updates if no CU-UP is associated
if (!is_cuup_associated(rrc)) {
@@ -2091,20 +2110,37 @@ static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f
.gNB_cu_up_ue_id = UE->rrc_ue_id,
};
for (int i = 0; i < n; i++) {
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
rrc_pdu_session_param_t *pdu_ue = find_pduSession_from_drbId(UE, drb_f1->id);
if (pdu_ue == NULL) {
LOG_E(RRC, "UE %d: UE Context Modif Response: no PDU session for DRB ID %d\n", UE->rrc_ue_id, drb_f1->id);
continue;
bool is_inter_cu_ho = UE->ho_context && UE->ho_context->source && !UE->ho_context->target;
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
int drb_id = i + 1;
DRB_nGRAN_to_mod_t drb_to_mod = {0};
if (n > 0) {
/* DRBs to Setup in F1 UE Context Modifcation Response */
if (i == n)
break;
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
drb_id = drb_f1->id;
fill_e1_bearer_modif(&drb_to_mod, drb_f1);
} else if (is_inter_cu_ho) {
/* On-going handover: send PDCP Status Request */
if (UE->established_drbs[i].status == DRB_INACTIVE)
continue;
drb_to_mod.id = drb_id;
// PDCP SN Status Request
drb_to_mod.pdcp_sn_status_requested = true;
LOG_I(NR_RRC, "PDCP Status requested (drb_id=%d)\n", drb_id);
} else if (drb_status) {
/* On-going handover: send PDCP Status */
fill_e1_bearer_modif_pdcp_status(UE, &drb_to_mod, drb_id, rrc->configuration.um_on_default_drb, drb_status);
}
pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, pdu_ue->param.pdusession_id);
DevAssert(pdu_e1 != NULL);
pdu_e1->sessionId = pdu_ue->param.pdusession_id;
DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify];
/* Fill E1 bearer context modification */
fill_e1_bearer_modif(drb_e1, drb_f1);
pdu_e1->numDRB2Modify += 1;
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(UE, drb_id);
if (!pdu)
continue;
pdu_session_to_mod_t *to_mod = find_or_next_pdu_session(&req, pdu->param.pdusession_id);
DevAssert(to_mod);
to_mod->sessionId = pdu->param.pdusession_id;
// Fill E1 DRB to Modify item
to_mod->DRBnGRanModList[to_mod->numDRB2Modify++] = drb_to_mod;
}
DevAssert(req.numPDUSessionsMod > 0);
DevAssert(req.numPDUSessions == 0);
@@ -2169,7 +2205,7 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance
AssertFatal(UE->Srb[1].Active && UE->Srb[2].Active, "SRBs 1 and 2 must be active during DRB Establishment");
store_du_f1u_tunnel(resp->drbs, resp->drbs_len, UE);
if (num_drb == 0)
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs);
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs, NULL);
else
cuup_notify_reestablishment(rrc, UE);
}
@@ -2300,9 +2336,10 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
if (resp->drbs_len > 0) {
bool is_inter_cu_ho = UE->ho_context && UE->ho_context->source && !UE->ho_context->target;
if (resp->drbs_len > 0 || is_inter_cu_ho) {
store_du_f1u_tunnel(resp->drbs, resp->drbs_len, UE);
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs);
e1_send_bearer_updates(rrc, UE, resp->drbs_len, resp->drbs, NULL);
}
if (resp->du_to_cu_rrc_info) {
@@ -2550,6 +2587,13 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp
}
}
// If HO Preparation Info is stored, N2 handover is ongoing
if (UE->ho_context) {
LOG_I(NR_RRC, "Received Bearer Context Setup Response for UE %d with valid HO Context\n", UE->rrc_ue_id);
UE->ho_context->target->ho_trigger(rrc, UE);
return;
}
if (!UE->f1_ue_context_active)
rrc_gNB_generate_UeContextSetupRequest(rrc, ue_context_p, resp);
else
@@ -2578,10 +2622,23 @@ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t
return;
}
// there is not really anything to do here as of now
int n_drb_mod = 0;
e1_pdcp_status_info_t pdcp_status[MAX_DRBS_PER_UE] = {0};
for (int i = 0; i < resp->numPDUSessionsMod; ++i) {
const pdu_session_modif_t *pdu = &resp->pduSessionMod[i];
LOG_I(RRC, "UE %d: PDU session ID %ld modified %d bearers\n", resp->gNB_cu_cp_ue_id, pdu->id, pdu->numDRBModified);
for (int j = 0; j < pdu->numDRBModified; j++) {
// Trigger UL RAN Status Transfer
if (pdu->DRBnGRanModList[j].pdcp_status) {
pdcp_status[n_drb_mod++] = *pdu->DRBnGRanModList[j].pdcp_status;
}
}
}
if (n_drb_mod) {
LOG_I(NR_RRC, "UE %d: received PDU Status Info - send UL RAN Status Transfer\n", resp->gNB_cu_cp_ue_id);
gNB_RRC_UE_t *ue = &ue_context_p->ue_context;
if (ue->ho_context && ue->ho_context->source)
ue->ho_context->source->ho_status_transfer(rrc, ue, n_drb_mod, pdcp_status);
}
}
@@ -2819,6 +2876,10 @@ void *rrc_gnb_task(void *args_p) {
rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(msg_p, instance);
break;
case NGAP_DL_RAN_STATUS_TRANSFER:
rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(msg_p, instance);
break;
/* Messages from F1AP task */
case F1AP_SETUP_REQ:
AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type), "should not receive F1AP_SETUP_REQUEST in DU!\n");
@@ -2938,6 +2999,16 @@ 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;
case NGAP_HANDOVER_COMMAND:
rrc_gNB_process_HandoverCommand(RC.nrrrc[instance], &NGAP_HANDOVER_COMMAND(msg_p));
rrc_gNB_free_Handover_Command(&NGAP_HANDOVER_COMMAND(msg_p)); // Free transfered NG message
break;
default:
LOG_E(NR_RRC, "[gNB %ld] Received unexpected message %s\n", instance, msg_name_p);
break;
+378 -7
View File
@@ -39,6 +39,9 @@
#include <arpa/inet.h>
#include "E1AP_ConfidentialityProtectionIndication.h"
#include "E1AP_IntegrityProtectionIndication.h"
#include "NR_UE-CapabilityRAT-ContainerList.h"
#include "NR_HandoverCommand.h"
#include "NR_HandoverCommand-IEs.h"
#include "NGAP_CauseRadioNetwork.h"
#include "NGAP_Dynamic5QIDescriptor.h"
#include "NGAP_GTPTunnel.h"
@@ -49,6 +52,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"
@@ -82,6 +86,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"
@@ -162,18 +168,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);
}
/**
@@ -1062,7 +1074,168 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_REQ(MessageDef *msg_p, instance_t in
}
}
//-----------------------------------------------------------------------------
/** @brief Sends the NG Handover Failure from the Target NG-RAN to the AMF */
void rrc_gNB_send_NGAP_HANDOVER_FAILURE(gNB_RRC_INST *rrc, ngap_handover_failure_t *msg)
{
LOG_I(NR_RRC, "Send NG Handover Failure message (amf_ue_ngap_id %ld) with cause %d \n ", msg->amf_ue_ngap_id, msg->cause.value);
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_FAILURE);
NGAP_HANDOVER_FAILURE(msg_p) = *msg;
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);
UE->ho_context->target->ho_trigger = nr_rrc_trigger_n2_ho_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);
}
/** @brief Send NG Uplink RAN Status Transfer message (8.4.6 3GPP TS 38.413)
* Direction: source NG-RAN node -> AMF */
int rrc_gNB_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const int n_to_mod, const e1_pdcp_status_info_t *pdcp_status)
{
AssertFatal(UE != NULL, "UE context is NULL\n");
LOG_I(NR_RRC,
"Sending NGAP Uplink RAN Status Transfer (AMF_UE_NGAP_ID=%" PRIu64 ", GNB_UE_NGAP_ID=%u)\n",
UE->amf_ue_ngap_id,
UE->rrc_ue_id);
ngap_ran_status_transfer_t msg = {
.amf_ue_ngap_id = UE->amf_ue_ngap_id,
.gnb_ue_ngap_id = UE->rrc_ue_id,
};
// Loop through DRBs and extract COUNT values
for (int i = 0; i < n_to_mod; ++i) {
drb_t *drb = &UE->established_drbs[i];
if (!drb->status)
continue;
bool sn_length_18 = rrc->pdcp_config.drb.sn_size == 18;
ngap_drb_status_t *item = &msg.ran_status.drb_status_list[msg.ran_status.nb_drb++];
item->drb_id = drb->drb_id;
const e1_pdcp_count_t *ul_pdcp = &pdcp_status[i].ul_count;
const e1_pdcp_count_t *dl_pdcp = &pdcp_status[i].dl_count;
item->ul_count.pdcp_sn = ul_pdcp->sn;
item->ul_count.hfn = ul_pdcp->hfn;
item->ul_count.sn_len = sn_length_18 ? NGAP_SN_LENGTH_18 : NGAP_SN_LENGTH_12;
item->dl_count.pdcp_sn = dl_pdcp->sn;
item->dl_count.hfn = dl_pdcp->hfn;
item->dl_count.sn_len = sn_length_18 ? NGAP_SN_LENGTH_18 : NGAP_SN_LENGTH_12;
}
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UL_RAN_STATUS_TRANSFER);
NGAP_UL_RAN_STATUS_TRANSFER(msg_p) = msg;
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
return 0;
}
/** @brief Process NG Handover Command on Source gNB */
void rrc_gNB_process_HandoverCommand(gNB_RRC_INST *rrc, const ngap_handover_command_t *msg)
{
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, msg->gNB_ue_ngap_id);
if (ue_context_p == NULL) {
LOG_W(NR_RRC, "Unknown UE context associated to gNB_ue_ngap_id (%u)\n", msg->gNB_ue_ngap_id);
return;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
uint8_t buffer[NR_RRC_BUF_SIZE];
byte_array_t ba = {.buf = buffer, .len = sizeof(buffer)};
int enc = doRRCReconfiguration_from_HandoverCommand(&ba, msg->handoverCommand);
DevAssert(enc > 0);
rrc_gNB_trigger_reconfiguration_for_handover(rrc, UE, buffer, enc);
LOG_A(NR_RRC, "Send reconfiguration (HO Command) to UE %u/RNTI %04x\n", UE->rrc_ue_id, UE->rnti);
}
void rrc_gNB_free_Handover_Command(ngap_handover_command_t *msg)
{
free_byte_array(msg->handoverCommand);
}
/*
* 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.
@@ -1172,6 +1345,88 @@ 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);
}
/** @brief Prepare NG Handover Notify message and inform NGAP */
void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
LOG_I(NR_RRC, "Triggering NGAP Handover Notify\n");
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_NOTIFY);
ngap_handover_notify_t *ho_notify = &NGAP_HANDOVER_NOTIFY(msg_p);
memset(ho_notify, 0, sizeof(*ho_notify));
nr_rrc_du_container_t *du = get_du_by_cell_id(rrc, rrc->nr_cellid);
ho_notify->gNB_ue_ngap_id = UE->rrc_ue_id;
ho_notify->amf_ue_ngap_id = UE->amf_ue_ngap_id;
ho_notify->user_info.nrCellIdentity = rrc->nr_cellid;
ho_notify->user_info.target_ng_ran.tac = *du->setup_req->cell->info.tac;
ho_notify->user_info.target_ng_ran.targetgNBId = rrc->node_id;
ho_notify->user_info.target_ng_ran.plmn_identity.mcc = du->setup_req->cell->info.plmn.mcc;
ho_notify->user_info.target_ng_ran.plmn_identity.mnc = du->setup_req->cell->info.plmn.mnc;
ho_notify->user_info.target_ng_ran.plmn_identity.mnc_digit_length = du->setup_req->cell->info.plmn.mnc_digit_length;
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
}
/** @brief Prepare NG Handover Cancel (source NG-RAN) and inform NGAP (N2-based HO only)
* 3GPP TS 38.413 §8.4.5 (Handover Cancellation) - source NG-RAN -> AMF
* 3GPP TS 38.413 §9.2.3.11 (HANDOVER CANCEL)
* @param rrc CU/RRC instance
* @param UE UE context
* @param cause cancellation cause */
void rrc_gNB_send_NGAP_HANDOVER_CANCEL(int module_id, gNB_RRC_UE_t *UE, ngap_cause_t cause)
{
DevAssert(UE != NULL);
LOG_I(NR_RRC, "Triggering NGAP Handover Cancel (gNB_ue_ngap_id=%d, amf_ue_ngap_id=%ld)\n", UE->rrc_ue_id, UE->amf_ue_ngap_id);
MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_HANDOVER_CANCEL);
ngap_handover_cancel_t *ho_cancel = &NGAP_HANDOVER_CANCEL(msg_p);
memset(ho_cancel, 0, sizeof(*ho_cancel));
/* Mandatory IEs (38.413 §9.2.3.11) */
ho_cancel->gNB_ue_ngap_id = UE->rrc_ue_id;
ho_cancel->amf_ue_ngap_id = UE->amf_ue_ngap_id;
ho_cancel->cause = cause;
itti_send_msg_to_task(TASK_NGAP, 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;
@@ -1297,3 +1552,119 @@ 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);
}
int rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(MessageDef *msg_p, instance_t instance)
{
const ngap_ran_status_transfer_t *cmd = &NGAP_DL_RAN_STATUS_TRANSFER(msg_p);
gNB_RRC_INST *rrc = RC.nrrrc[instance];
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, cmd->gnb_ue_ngap_id);
if (!ue_context_p) {
LOG_E(NR_RRC, "[gNB %ld] No UE context for gNB_ue_ngap_id %u\n", instance, cmd->gnb_ue_ngap_id);
return -1;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
LOG_I(NR_RRC,
"[gNB %ld] DL RAN Status Transfer for gNB_ue_ngap_id %u AMF_UE_NGAP_ID %lu\n",
instance,
cmd->gnb_ue_ngap_id,
cmd->amf_ue_ngap_id);
for (int i = 0; i < cmd->ran_status.nb_drb; ++i) {
const ngap_drb_status_t *s = &cmd->ran_status.drb_status_list[i];
LOG_I(NR_RRC,
"DL RAN Status Transfer - DRB ID %d:\n"
" UL COUNT: PDCP SN = %u, HFN = %u (%s)\n"
" DL COUNT: PDCP SN = %u, HFN = %u (%s)\n",
s->drb_id,
s->ul_count.pdcp_sn,
s->ul_count.hfn,
s->ul_count.sn_len == NGAP_SN_LENGTH_18 ? "18-bit" : "12-bit",
s->dl_count.pdcp_sn,
s->dl_count.hfn,
s->dl_count.sn_len == NGAP_SN_LENGTH_18 ? "18-bit" : "12-bit");
// Send to PDCP layer
e1_send_bearer_updates(rrc, UE, 0, NULL, s);
}
return 0;
}
+22
View File
@@ -89,4 +89,26 @@ 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);
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);
void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, byte_array_t ho_command);
void rrc_gNB_process_HandoverCommand(gNB_RRC_INST *rrc, const ngap_handover_command_t *msg);
void rrc_gNB_free_Handover_Command(ngap_handover_command_t *msg);
void rrc_gNB_send_NGAP_HANDOVER_NOTIFY(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE);
void rrc_gNB_send_NGAP_HANDOVER_CANCEL(int module_id, gNB_RRC_UE_t *UE, ngap_cause_t cause);
int rrc_gNB_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const int n_to_mod, const e1_pdcp_status_info_t *pdcp_status);
int rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(MessageDef *msg_p, instance_t instance);
#endif
+241 -30
View File
@@ -42,8 +42,7 @@
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
#endif
typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t;
static nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type)
nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type)
{
nr_handover_context_t *ho_ctx = calloc_or_fail(1, sizeof(*ho_ctx));
if (type == HO_CTX_SOURCE || type == HO_CTX_BOTH) {
@@ -58,6 +57,8 @@ static 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);
}
@@ -121,18 +122,16 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
byte_array_t *ho_prep_info,
ho_req_ack_t ack,
ho_success_t success,
ho_cancel_t cancel)
ho_cancel_t cancel,
ho_failure_t failure)
{
DevAssert(rrc != NULL);
DevAssert(ue != NULL);
DevAssert(target_du != NULL);
// source_du might be NULL -> inter-CU handover
DevAssert(ho_prep_info->buf != NULL && ho_prep_info->len > 0);
DevAssert(ue->ho_context);
if (ue->ho_context != NULL) {
LOG_E(NR_RRC, "handover for UE %u ongoing, cannot trigger new\n", ue->rrc_ue_id);
return;
}
// if any reconfiguration is ongoing, abort handover request
for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) {
if (ue->xids[i] != RRC_ACTION_NONE) {
@@ -141,15 +140,13 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
}
}
// if we know the source DU (F1 handover), allocate the context for it,
// otherwise only for the target DU (N2, Xn)
ho_ctx_type_t ctx_type = source_du != NULL ? HO_CTX_BOTH : HO_CTX_TARGET;
nr_handover_context_t *ho_ctx = alloc_ho_ctx(ctx_type);
nr_handover_context_t *ho_ctx = ue->ho_context;
ho_ctx->target->du = target_du;
// we will know target->{du_ue_id,new_rnti} once we have UE ctxt setup
// response
ho_ctx->target->ho_req_ack = ack;
ho_ctx->target->ho_success = success;
ho_ctx->target->ho_failure = failure;
const f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);
if (source_du != NULL) {
@@ -164,7 +161,7 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
int result = asn_copy(&asn_DEF_NR_CellGroupConfig, (void **)&ho_ctx->source->old_cellGroupConfig, ue->masterCellGroup);
AssertFatal(result == 0, "error during asn_copy() of CellGroupConfig\n");
}
ue->ho_context = ho_ctx;
LOG_A(NR_RRC,
"Handover triggered for UE %u/RNTI %04x towards DU %ld/assoc_id %d/PCI %d\n",
ue->rrc_ue_id,
@@ -222,27 +219,12 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc,
free_ue_context_setup_req(&ue_context_setup_req);
}
/*
void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc, int nr_cgi, uint8_t *ho_prep_info, uint32_t *ho_prep_len, void *pdu_session)
{
// call from outside (via NGAP message), with the request for handover for a
// (any) UE. This handler should, in order:
//
// 1. look up the target DU =du= via get_du_by_cell_id(nr_cgi)
// (and reject the request if it can't find the DU)
// 2. create a context for a new UE =UE=, based on pdu_session information
// 3. call nr_initiate_handover(rrc, UE, NULL, du, ho_prep_info, ho_prep_len);
//
// after success of the DU, send the result via =success_ptr= to the source
// CU.
}
*/
typedef struct deliver_ue_ctxt_modification_data_t {
gNB_RRC_INST *rrc;
f1ap_ue_context_mod_req_t *modification_req;
sctp_assoc_t assoc_id;
} deliver_ue_ctxt_modification_data_t;
static void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
DevAssert(deliver_pdu_data != NULL);
@@ -251,7 +233,8 @@ static void rrc_deliver_ue_ctxt_modif_req(void *deliver_pdu_data, ue_id_t ue_id,
data->modification_req->rrc_container = &ba;
data->rrc->mac_rrc.ue_context_modification_request(data->assoc_id, data->modification_req);
}
static void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len)
void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len)
{
f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id);
@@ -355,6 +338,13 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain
uint8_t buf[NR_RRC_BUF_SIZE];
int size = do_NR_HandoverPreparationInformation(ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len, buf, sizeof buf);
// Allocate handover context (both CU and DU)
if (ue->ho_context != NULL) {
LOG_E(NR_RRC, "Ongoing handover for UE %d, cannot trigger new\n", ue->rrc_ue_id);
return;
}
ue->ho_context = alloc_ho_ctx(HO_CTX_BOTH);
// corresponds to a "handover request", 38.300 Sec 9.3.2.3
// see also 38.413 Sec 9.3.1.29 for information on source-CU to target-CU
// information (Source NG-RAN Node to Target NG-RAN Node Transparent Container)
@@ -365,7 +355,7 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain
ho_success_t success = nr_rrc_f1_ho_complete;
ho_cancel_t cancel = nr_rrc_cancel_f1_ho;
byte_array_t hpi = {.buf = buf, .len = size};
nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel);
nr_initiate_handover(rrc, ue, source_du, target_du, &hpi, ack, success, cancel, NULL);
}
void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue)
@@ -399,3 +389,224 @@ void nr_HO_F1_trigger_telnet(gNB_RRC_INST *rrc, uint32_t rrc_ue_id)
nr_rrc_trigger_f1_ho(rrc, ue, source_du, target_du);
}
/** @brief Generate the HandoverPreparationInformation to be carried
* in the RRC Container (9.3.1.29 of 3GPP TS 38.413) of the Source
* NG-RAN Node to Target NG-RAN Node Transparent Container IE */
static byte_array_t rrc_gNB_generate_HandoverPreparationInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, int serving_pci)
{
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue, 0, false);
params.ue_cap = ue->ue_cap_buffer;
byte_array_t hoPrepInfo = get_HandoverPreparationInformation(&params, serving_pci);
free_RRCReconfiguration_params(params);
if (hoPrepInfo.len < 0) {
LOG_E(NR_RRC, "HandoverPreparationInformation generation failed for UE %d\n", ue->rrc_ue_id);
return hoPrepInfo;
}
LOG_D(NR_RRC, "HO LOG: Handover Preparation for UE %lu Encoded (%zd bytes)\n", ue->amf_ue_ngap_id, hoPrepInfo.len);
return hoPrepInfo;
}
static byte_array_t rrc_gNB_encode_HandoverCommand(gNB_RRC_UE_t *UE, gNB_RRC_INST *rrc)
{
DevAssert(UE->ho_context);
DevAssert(UE->ho_context->target);
NR_SecurityConfig_t *sec = calloc_or_fail(1, sizeof(*sec));
sec->keyToUse = calloc_or_fail(1, sizeof(*sec->keyToUse));
*sec->keyToUse = NR_SecurityConfig__keyToUse_master;
sec->securityAlgorithmConfig = calloc_or_fail(1, sizeof(*sec->securityAlgorithmConfig));
struct NR_SecurityAlgorithmConfig *alg = sec->securityAlgorithmConfig;
alg->cipheringAlgorithm = UE->ciphering_algorithm;
alg->integrityProtAlgorithm = calloc_or_fail(1, sizeof(*alg->integrityProtAlgorithm));
*alg->integrityProtAlgorithm = UE->integrity_algorithm;
// Mark source gNB's measurement configuration for removal in UE->measConfig
if (UE->ho_context->target->ue_ho_prep_info.len) {
fill_removal_lists_from_source_measConfig(UE->measConfig, UE->ho_context->target->ue_ho_prep_info);
}
/* 3GPP TS 38.331 RadioBearerConfig: re-establish PDCP whenever
the security key used for this radio bearer changes, e.g. for SRB2
when receiving reconfiguration with sync [...]. Similarly, for DRBs
PDCP re-establishment is necessary with derivation of new UP keys */
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, UE, (1 << 1) | (1 << 2), true);
UE->xids[params.transaction_id] = RRC_DEDICATED_RECONF;
params.security_config = sec;
params.masterKeyUpdate = true;
params.nextHopChainingCount = UE->nh_ncc;
byte_array_t out = get_HandoverCommandMessage(&params);
// Free remove lists in UE->measConfig
ASN_STRUCT_FREE(asn_DEF_NR_MeasIdToRemoveList, UE->measConfig->measIdToRemoveList);
ASN_STRUCT_FREE(asn_DEF_NR_ReportConfigToRemoveList, UE->measConfig->reportConfigToRemoveList);
ASN_STRUCT_FREE(asn_DEF_NR_MeasObjectToRemoveList, UE->measConfig->measObjectToRemoveList);
free_RRCReconfiguration_params(params);
return out;
}
/** @brief This callback is used by the target gNB
* to trigger the Handover Request Acknowledge towards the AMF */
static void nr_rrc_n2_ho_acknowledge(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
AssertFatal(cu_exists_f1_ue_data(UE->rrc_ue_id), "No CU found for rrc_ue_id %d\n", UE->rrc_ue_id);
f1_ue_data_t previous_data = cu_get_f1_ue_data(UE->rrc_ue_id);
/* this is the callback for N2 handover after F1 UE context setup response in
* the handover case. Check that there was no associated DU, then set it */
AssertFatal(previous_data.secondary_ue == -1, "there was already a DU present\n");
previous_data.secondary_ue = UE->ho_context->target->du_ue_id;
bool success = cu_update_f1_ue_data(UE->rrc_ue_id, &previous_data);
DevAssert(success);
LOG_I(NR_RRC, "Updated CU F1AP Context for UE %d, DU Id : %u\n", UE->rrc_ue_id, UE->ho_context->target->du_ue_id);
byte_array_t hoCommand = rrc_gNB_encode_HandoverCommand(UE, rrc);
if (hoCommand.len < 0) {
LOG_E(NR_RRC, "ASN1 message encoding failed: failed to generate Handover Command Message\n");
ngap_handover_failure_t fail = {.amf_ue_ngap_id = UE->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};
UE->ho_context->target->ho_failure(rrc, UE->rrc_ue_id, &fail);
return;
} else {
LOG_D(NR_RRC, "HO LOG: Handover Command for UE %u Encoded (%ld bytes)\n", UE->rrc_ue_id, hoCommand.len);
}
rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(rrc, UE, hoCommand);
free_byte_array(hoCommand);
}
/** @brief This callback is used by the target gNB
* to trigger the Handover Notify towards the AMF */
static void nr_rrc_n2_ho_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
rrc_gNB_send_NGAP_HANDOVER_NOTIFY(rrc, UE);
}
/** @brief This callback is used by the source gNB to inform the AMF
* about the cancellation of an ongoing handover */
static void nr_rrc_n2_ho_cancel(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
DevAssert(UE->ho_context);
DevAssert(UE->ho_context->source);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_HANDOVER_CANCELLED};
rrc_gNB_send_NGAP_HANDOVER_CANCEL(rrc->module_id, UE, cause);
}
/** @brief Callback function to trigger NG Handover Failure on the target gNB, to inform the AMF
* that the preparation of resources has failed (e.g. unsatisfied criteria, gNB is already loaded).
* This message represents an Unsuccessful Outcome of the Handover Resource Allocation */
void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg)
{
LOG_I(NR_RRC, "Triggering N2 Handover Failure\n");
rrc_gNB_send_NGAP_HANDOVER_FAILURE(rrc, msg);
LOG_I(NR_RRC, "Send UE Context Release for gnb_ue_id %d\n", gnb_ue_id);
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, gnb_ue_id);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(rrc->module_id, ue_context_p, msg->cause);
return;
}
/** @brief Trigger N2 Handover on the target gNB:
* 1) set up callbacks
* 2) initiate N2 handover on target NG-RAN, which triggers the set up
* of HO context and UE Context Setup procedures */
void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue)
{
ho_req_ack_t ack = nr_rrc_n2_ho_acknowledge;
ho_success_t success = nr_rrc_n2_ho_complete;
ho_failure_t failure = nr_rrc_n2_ho_failure;
const nr_rrc_du_container_t *target_du = get_du_for_ue(rrc, ue->rrc_ue_id);
nr_initiate_handover(rrc, ue, NULL, target_du, &ue->ho_context->target->ue_ho_prep_info, ack, success, NULL, failure);
FREE_AND_ZERO_BYTE_ARRAY(ue->ho_context->target->ue_ho_prep_info);
NR_UE_NR_Capability_t *ue_cap = get_ue_nr_capability(ue->rnti, ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len);
ue->UE_Capability_nr = ue_cap;
ue->UE_Capability_size = ue->ue_cap_buffer.len;
}
/** @brief Trigger N2 handover on source gNB:
* 1) Prepare RRC Container with HandoverPreparationInformation message
* 2) send NGAP Handover Required message */
void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *ue,
int serving_pci,
const nr_neighbour_cell_t *neighbour_config)
{
byte_array_t hoPrepInfo = rrc_gNB_generate_HandoverPreparationInformation(rrc, ue, serving_pci);
if (hoPrepInfo.len < 0) {
free_byte_array(hoPrepInfo);
LOG_E(NR_RRC, "Failed to trigger N2 handover on source gNB for UE %x\n", ue->rrc_ue_id);
return;
}
// allocate context for source
if (ue->ho_context != NULL) {
LOG_E(NR_RRC, "Ongoing handover for UE %d, cannot trigger new\n", ue->rrc_ue_id);
return;
}
ue->ho_context = alloc_ho_ctx(HO_CTX_SOURCE);
ue->ho_context->source->du = get_du_for_ue(rrc, ue->rrc_ue_id);
ue->ho_context->source->ho_status_transfer = rrc_gNB_send_NGAP_ul_ran_status_transfer;
ue->ho_context->source->ho_cancel = nr_rrc_n2_ho_cancel;
rrc_gNB_send_NGAP_HANDOVER_REQUIRED(rrc, ue, neighbour_config, hoPrepInfo);
free_byte_array(hoPrepInfo);
}
extern const nr_neighbour_cell_t *get_neighbour_cell_by_pci(const neighbour_cell_configuration_t *cell, int pci);
extern const neighbour_cell_configuration_t *get_neighbour_cell_config(const gNB_RRC_INST *rrc, int cell_id);
void nr_HO_N2_trigger_telnet(gNB_RRC_INST *rrc, uint32_t neighbour_pci, uint32_t rrc_ue_id)
{
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, rrc_ue_id);
if (ue_context_p == NULL) {
LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: cannot find UE context\n", rrc_ue_id);
return;
}
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
struct nr_rrc_du_container_t *du = get_du_for_ue(rrc, rrc_ue_id);
if (du == NULL) {
LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: Unknown DU\n", rrc_ue_id);
return;
}
uint16_t scell_pci = du->setup_req->cell[0].info.nr_pci;
// Simulate handover on the same cell (testing purposes)
if (neighbour_pci == scell_pci) {
LOG_I(NR_RRC, "UE %d: trigger handover on the same cell PCI=%d\n", rrc_ue_id, neighbour_pci);
nr_neighbour_cell_t neighbourConfig = {
.isIntraFrequencyNeighbour = true,
.gNB_ID = du->setup_req->gNB_DU_id,
.nrcell_id = du->setup_req->cell[0].info.nr_cellid,
.physicalCellId = du->setup_req->cell[0].info.nr_pci,
.plmn = du->setup_req->cell[0].info.plmn,
.subcarrierSpacing = du->setup_req->cell[0].info.tdd.tbw.scs,
};
nr_rrc_trigger_n2_ho(rrc, UE, neighbour_pci, &neighbourConfig);
return;
}
const f1ap_served_cell_info_t *scell_du = get_cell_information_by_phycellId(scell_pci);
DevAssert(scell_du);
LOG_I(NR_RRC, "UE %d: triggered N2 HO, source PCI=%d to target PCI=%d\n", rrc_ue_id, scell_pci, neighbour_pci);
const neighbour_cell_configuration_t *cell = get_neighbour_cell_config(rrc, scell_du->nr_cellid);
const nr_neighbour_cell_t *neighbour = get_neighbour_cell_by_pci(cell, neighbour_pci);
if (neighbour == NULL) {
LOG_E(NR_RRC, "N2 HO trigger failed for UE %d: could not find neighbour cell with PCI=%d\n", rrc_ue_id, neighbour_pci);
return;
}
nr_rrc_trigger_n2_ho(rrc, UE, scell_du->nr_pci, neighbour);
}
+27
View File
@@ -23,6 +23,7 @@
#include <stdint.h>
#include "common/utils/ds/byte_array.h"
#include "nr_rrc_defs.h"
/* forward declarations */
typedef struct gNB_RRC_INST_s gNB_RRC_INST;
@@ -32,6 +33,8 @@ typedef struct nr_rrc_du_container_t nr_rrc_du_container_t;
typedef struct NR_CellGroupConfig NR_CellGroupConfig_t;
typedef void (*ho_cancel_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef int (*ho_status_transfer_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const int n_to_mod, const e1_pdcp_status_info_t *pdcp_status);
typedef struct nr_ho_source_cu {
/// pointer to the (source) DU structure
const nr_rrc_du_container_t *du;
@@ -47,11 +50,16 @@ typedef struct nr_ho_source_cu {
/// function pointer to announce the handover cancellation, e.g.,
/// reestablishment
ho_cancel_t ho_cancel;
/// status transfer
ho_status_transfer_t ho_status_transfer;
} nr_ho_source_cu_t;
/* acknowledgement of handover request. buf+len is the RRC Reconfiguration */
typedef void (*ho_req_ack_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef void (*ho_success_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef void (*ho_failure_t)(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg);
typedef void (*ho_trigger_t)(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
typedef struct nr_ho_target_cu {
/// pointer to the (target) DU structure
const nr_rrc_du_container_t *du;
@@ -61,10 +69,16 @@ 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 trigger handover on target gNB
ho_trigger_t ho_trigger;
/// function pointer to announce handover request acknowledgment
ho_req_ack_t ho_req_ack;
/// function pointer to announce handover success
ho_success_t ho_success;
/// function pointer to announce the handover failure
ho_failure_t ho_failure;
} nr_ho_target_cu_t;
typedef struct nr_handover_context_s {
@@ -72,7 +86,20 @@ typedef struct nr_handover_context_s {
nr_ho_target_cu_t *target;
} nr_handover_context_t;
typedef enum { HO_CTX_BOTH, HO_CTX_SOURCE, HO_CTX_TARGET } ho_ctx_type_t;
nr_handover_context_t *alloc_ho_ctx(ho_ctx_type_t type);
void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du);
void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue);
void nr_rrc_n2_ho_failure(gNB_RRC_INST *rrc, uint32_t gnb_ue_id, ngap_handover_failure_t *msg);
void nr_rrc_trigger_n2_ho(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *ue,
int serving_pci,
const nr_neighbour_cell_t *neighbour_config);
void rrc_gNB_trigger_reconfiguration_for_handover(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, uint8_t *rrc_reconf, int rrc_reconf_len);
void nr_rrc_trigger_n2_ho_target(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue);
#endif /* RRC_GNB_MOBILITY_H_ */
+89 -19
View File
@@ -532,19 +532,74 @@ static void nr_rrc_process_sib1(NR_UE_RRC_INST_t *rrc, NR_UE_RRC_SI_INFO *SI_inf
nr_rrc_send_msg_to_mac(rrc, &rrc_msg);
}
static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530, int gNB_index)
static void nr_rrc_nh_update(NR_UE_RRC_INST_t *rrc, uint8_t *kamf, uint8_t *sync_input)
{
if (rec_1530->fullConfig) {
// TODO perform the full configuration procedure as specified in 5.3.5.11 of 331
LOG_E(NR_RRC, "RRCReconfiguration includes fullConfig but this is not implemented yet\n");
uint8_t nh[SECURITY_KEY_LEN] = {0};
nr_derive_nh(kamf, sync_input, nh);
log_hex_buffer("Sync input = stored NH", rrc->nh, SECURITY_KEY_LEN);
memcpy(rrc->nh, nh, SECURITY_KEY_LEN);
rrc->nhcc++; // Increase stored nextHopChainingCount
}
/** @brief AS security key update procedure (5.3.5.7 3GPP TS 38.331) */
void as_security_key_update(NR_UE_RRC_INST_t *rrc, NR_MasterKeyUpdate_t *mku)
{
if (mku->nas_Container) {
LOG_E(NR_RRC, "forward the nas-Container to the upper layers: not implemented yet\n");
}
if (rec_1530->masterCellGroup)
nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index);
if (rec_1530->masterKeyUpdate) {
// TODO perform AS security key update procedure as specified in 5.3.5.7
LOG_E(NR_RRC, "RRCReconfiguration includes masterKeyUpdate but this is not implemented yet\n");
if (mku->keySetChangeIndicator) {
LOG_E(NR_RRC, "derive or update the K gNB key based on the K AMF key, as specified in TS 33.501: not implemented yet\n");
} else {
/* derive or update the K gNB key based on the current K gNB key or the NH, using the nextHopChainingCount
value indicated in the received masterKeyUpdate, as specified in 6.9.2.3.3 3GPP TS 33.501 */
if (mku->nextHopChainingCount != rrc->nhcc) {
// - If the UE received an NCC value that was different from the NCC associated with the currently active
// K gNB/K eNB, the UE shall first synchronize the locally kept NH parameter by computing the function defined in
// Annex A.10 iteratively (and increasing the NCC value until it matches the NCC value received from the source
// ng-eNB/gNB via the HO command message.
LOG_A(NR_RRC, "Received masterKeyUpdate (nextHopChainingCount %ld): update security keys\n", mku->nextHopChainingCount);
/** @todo: The KAMF should be obtained from NAS. This exchange over ITTI must be synchronized
* with the rest of the RRCReconfiguration procedure, in particular, the RadioBearerConfig
* processing that triggers bearer modifications. Security configueration of bearers must
* complete using the newly derived keys. As a workaround NAS is directly accessed here. */
nr_ue_nas_t *nas = get_ue_nas_info(rrc->ue_id);
uint8_t *kamf = nas->security.kamf;
log_hex_buffer("Stored kamf", kamf, SECURITY_KEY_LEN);
if (rrc->nhcc == 0) { // First derivation
derive_kgnb(kamf, 0, rrc->kgnb);
log_hex_buffer("Sync input = derived kgnb", rrc->kgnb, SECURITY_KEY_LEN);
nr_rrc_nh_update(rrc, kamf, rrc->kgnb);
}
for (int i = rrc->nhcc; i < mku->nextHopChainingCount; i++) { // Following derivations
LOG_D(NR_RRC, "Derive keys for ChainingCount = %d\n", i);
nr_rrc_nh_update(rrc, kamf, rrc->nh);
}
nr_derive_key_ng_ran_star(rrc->phyCellID, rrc->arfcn_ssb, rrc->nh, rrc->kgnb);
// When the NCC values match, the UE shall compute the K NG-RAN *
// from the synchronized NH parameter and the target PCI and its frequency ARFCN-DL/EARFCN-DL using the
// function defined in Annex A.11 and A.12.
// The UE shall use the KNG-RAN * as the K gNB when communicating with the target gNB and as the KeNB when
// communicating with the target ng-eNB.
} else {
nr_derive_key_ng_ran_star(rrc->phyCellID, rrc->arfcn_ssb, rrc->kgnb, rrc->kgnb);
}
log_hex_buffer("Derived kgnb", rrc->kgnb, SECURITY_KEY_LEN);
}
/* Check if there is dedicated NAS information to forward to NAS */
}
static nr_pdcp_entity_security_keys_and_algos_t get_security_rrc_parameters(NR_UE_RRC_INST_t *ue, bool cp)
{
nr_pdcp_entity_security_keys_and_algos_t out = {0};
out.ciphering_algorithm = ue->cipheringAlgorithm;
out.integrity_algorithm = ue->integrityProtAlgorithm;
nr_derive_key(cp ? RRC_ENC_ALG : UP_ENC_ALG, ue->cipheringAlgorithm, ue->kgnb, out.ciphering_key);
nr_derive_key(cp ? RRC_INT_ALG : UP_INT_ALG, ue->integrityProtAlgorithm, ue->kgnb, out.integrity_key);
return out;
}
/** @brief Check if there is dedicated NAS information to forward to NAS */
static void nr_rrc_process_dedicatedNAS_MessageList(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530)
{
if (rec_1530->dedicatedNAS_MessageList) {
struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *tmp = rec_1530->dedicatedNAS_MessageList;
for (int i = 0; i < tmp->list.count; i++) {
@@ -557,6 +612,21 @@ static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCRe
}
tmp->list.count = 0; // to prevent the automatic free by ASN1_FREE
}
}
static void nr_rrc_process_reconfiguration_v1530(NR_UE_RRC_INST_t *rrc, NR_RRCReconfiguration_v1530_IEs_t *rec_1530, int gNB_index)
{
if (rec_1530->fullConfig) {
// TODO perform the full configuration procedure as specified in 5.3.5.11 of 331
LOG_E(NR_RRC, "RRCReconfiguration includes fullConfig but this is not implemented yet\n");
}
if (rec_1530->masterCellGroup)
nr_rrc_ue_process_masterCellGroup(rrc, rec_1530->masterCellGroup, rec_1530->fullConfig, gNB_index);
if (rec_1530->masterKeyUpdate) {
as_security_key_update(rrc, rec_1530->masterKeyUpdate);
nr_pdcp_entity_security_keys_and_algos_t sp = get_security_rrc_parameters(rrc, true);
nr_pdcp_config_set_security(rrc->ue_id, 1, true, &sp);
}
NR_UE_RRC_SI_INFO *SI_info = &rrc->perNB[gNB_index].SInfo;
if (rec_1530->dedicatedSIB1_Delivery) {
NR_SIB1_t *sib1 = NULL;
@@ -633,6 +703,9 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_
case NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration: {
NR_RRCReconfiguration_IEs_t *ie = reconfiguration->criticalExtensions.choice.rrcReconfiguration;
if (ie->nonCriticalExtension)
nr_rrc_process_reconfiguration_v1530(rrc, ie->nonCriticalExtension, gNB_index);
if (ie->radioBearerConfig) {
LOG_I(NR_RRC, "RRCReconfiguration includes radio Bearer Configuration\n");
nr_rrc_ue_process_RadioBearerConfig(rrc, ie->radioBearerConfig);
@@ -640,8 +713,11 @@ static void nr_rrc_ue_process_rrcReconfiguration(NR_UE_RRC_INST_t *rrc, int gNB_
xer_fprint(stdout, &asn_DEF_NR_RadioBearerConfig, (const void *)ie->radioBearerConfig);
}
/** @note This triggers PDU Session Establishment Accept which sets up the TUN interface.
* SDAP entity is a pre-requisite, therefore the radioBearerConfig has to be processed
* (in nr_rrc_ue_process_RadioBearerConfig, add_drb) early enough, or it may cause a race condition in SDAP. */
if (ie->nonCriticalExtension)
nr_rrc_process_reconfiguration_v1530(rrc, ie->nonCriticalExtension, gNB_index);
nr_rrc_process_dedicatedNAS_MessageList(rrc, ie->nonCriticalExtension);
if (ie->secondaryCellGroup) {
NR_CellGroupConfig_t *cellGroupConfig = NULL;
@@ -1959,14 +2035,8 @@ static void nr_rrc_ue_process_RadioBearerConfig(NR_UE_RRC_INST_t *ue_rrc,
ue_rrc->integrityProtAlgorithm = *radioBearerConfig->securityConfig->securityAlgorithmConfig->integrityProtAlgorithm;
}
}
security_rrc_parameters.ciphering_algorithm = ue_rrc->cipheringAlgorithm;
security_rrc_parameters.integrity_algorithm = ue_rrc->integrityProtAlgorithm;
nr_derive_key(RRC_ENC_ALG, ue_rrc->cipheringAlgorithm, ue_rrc->kgnb, security_rrc_parameters.ciphering_key);
nr_derive_key(RRC_INT_ALG, ue_rrc->integrityProtAlgorithm, ue_rrc->kgnb, security_rrc_parameters.integrity_key);
security_up_parameters.ciphering_algorithm = ue_rrc->cipheringAlgorithm;
security_up_parameters.integrity_algorithm = ue_rrc->integrityProtAlgorithm;
nr_derive_key(UP_ENC_ALG, ue_rrc->cipheringAlgorithm, ue_rrc->kgnb, security_up_parameters.ciphering_key);
nr_derive_key(UP_INT_ALG, ue_rrc->integrityProtAlgorithm, ue_rrc->kgnb, security_up_parameters.integrity_key);
security_rrc_parameters = get_security_rrc_parameters(ue_rrc, true);
security_up_parameters = get_security_rrc_parameters(ue_rrc, false);
}
if (radioBearerConfig->srb_ToAddModList != NULL) {
+4
View File
@@ -248,6 +248,10 @@ typedef struct NR_UE_RRC_INST_s {
e_NR_IntegrityProtAlgorithm integrityProtAlgorithm;
long keyToUse;
bool as_security_activated;
/// Next Hop Chaining Count
uint8_t nh[32];
uint64_t nhcc;
bool detach_after_release;
NR_timer_t release_timer;
NR_RRCRelease_t *RRCRelease;