Merge remote-tracking branch 'origin/rrc-pdusession-handling-improvements' into integration_2025_w39 (!3513)

Improvements to RRC PDU session and DRB handling logic

This merge request introduces a major simplification and cleanup of the gNB RRC
PDU Sessions and DRBs handling logic. The core goals of this work are to:

- Replace fixed-size PDU Session, DRBs and QoS arrays with dynamic containers
- Simplify and modularize helper functions for session/DRB management
- Prepare for further refactoring of the PDU Session state machine
- Improve code safety and maintainability through better bounds checking
This commit is contained in:
Jaroslava Fiedlerova
2025-09-25 10:07:38 +02:00
30 changed files with 1124 additions and 786 deletions
+2
View File
@@ -1167,6 +1167,7 @@ set(L2_NR_SRC
${NR_RRC_DIR}/rrc_gNB.c
${NR_RRC_DIR}/mac_rrc_dl_direct.c
${NR_RRC_DIR}/mac_rrc_dl_f1ap.c
${NR_RRC_DIR}/rrc_gNB_asn1.c
${NR_RRC_DIR}/rrc_gNB_nsa.c
${NR_RRC_DIR}/rrc_gNB_UE_context.c
${NR_RRC_DIR}/rrc_gNB_NGAP.c
@@ -1893,6 +1894,7 @@ target_link_libraries(nr-cuup PRIVATE
CONFIG_LIB ITTI SCTP_CLIENT
GTPV1U e1ap f1ap
time_management
alg
z dl pthread shlib_loader ${T_LIB})
target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs)
if(E2_AGENT)
+49
View File
@@ -55,4 +55,53 @@ typedef enum {
typedef enum { NON_DYNAMIC, DYNAMIC } fiveQI_t;
/* QoS Priority Level */
typedef enum {
QOS_PRIORITY_SPARE = 0,
QOS_PRIORITY_HIGHEST,
QOS_PRIORITY_2,
QOS_PRIORITY_3,
QOS_PRIORITY_4,
QOS_PRIORITY_5,
QOS_PRIORITY_6,
QOS_PRIORITY_7,
QOS_PRIORITY_8,
QOS_PRIORITY_9,
QOS_PRIORITY_10,
QOS_PRIORITY_11,
QOS_PRIORITY_12,
QOS_PRIORITY_13,
QOS_PRIORITY_LOWEST,
QOS_NO_PRIORITY
} qos_priority_t;
/* Pre-emption Capability */
typedef enum {
PEC_SHALL_NOT_TRIGGER_PREEMPTION = 0,
PEC_MAY_TRIGGER_PREEMPTION,
PEC_MAX,
} qos_pec_t;
/* Pre-emption Vulnerability */
typedef enum {
PEV_NOT_PREEMPTABLE = 0,
PEV_PREEMPTABLE = 1,
PEV_MAX,
} qos_pev_t;
/* Allocation Retention Priority */
typedef struct {
qos_priority_t priority_level;
qos_pec_t pre_emp_capability;
qos_pev_t pre_emp_vulnerability;
} qos_arp_t;
typedef struct pdusession_level_qos_parameter_s {
uint8_t qfi;
uint64_t fiveQI;
uint64_t qos_priority;
fiveQI_t fiveQI_type;
qos_arp_t arp;
} pdusession_level_qos_parameter_t;
#endif
+3
View File
@@ -97,4 +97,7 @@
#define UNUSED_VARIABLE(vARIABLE) (void)(vARIABLE)
#define NUM_PRACH_RX_FOR_NOISE_ESTIMATE 100
// SDAP
#define MAX_QOS_FLOWS 64
#endif /* __PLATFORM_CONSTANTS_H__ */
+26 -85
View File
@@ -91,8 +91,6 @@
#define NGAP_MAX_NB_AMF_IP_ADDRESS 10
#define NGAP_IMSI_LENGTH 16
#define QOSFLOW_MAX_VALUE 64
#define NGAP_MAX_NO_TAI_PAGING 16 // 9.2.4.1 3GPP TS 38.413
/* Security key length used within gNB
@@ -141,43 +139,6 @@ typedef struct ngap_ambr_s {
bitrate_t br_dl;
} ngap_ambr_t;
typedef enum ngap_priority_level_s {
NGAP_PRIORITY_LEVEL_SPARE = 0,
NGAP_PRIORITY_LEVEL_HIGHEST = 1,
NGAP_PRIORITY_LEVEL_2 = 2,
NGAP_PRIORITY_LEVEL_3 = 3,
NGAP_PRIORITY_LEVEL_4 = 4,
NGAP_PRIORITY_LEVEL_5 = 5,
NGAP_PRIORITY_LEVEL_6 = 6,
NGAP_PRIORITY_LEVEL_7 = 7,
NGAP_PRIORITY_LEVEL_8 = 8,
NGAP_PRIORITY_LEVEL_9 = 9,
NGAP_PRIORITY_LEVEL_10 = 10,
NGAP_PRIORITY_LEVEL_11 = 11,
NGAP_PRIORITY_LEVEL_12 = 12,
NGAP_PRIORITY_LEVEL_13 = 13,
NGAP_PRIORITY_LEVEL_LOWEST = 14,
NGAP_PRIORITY_LEVEL_NO_PRIORITY = 15
} ngap_priority_level_t;
typedef enum ngap_pre_emp_capability_e {
NGAP_PRE_EMPTION_CAPABILITY_SHALL_NOT_TRIGGER_PREEMPTION = 0,
NGAP_PRE_EMPTION_CAPABILITY_MAY_TRIGGER_PREEMPTION = 1,
NGAP_PRE_EMPTION_CAPABILITY_MAX,
} ngap_pre_emp_capability_t;
typedef enum ngap_pre_emp_vulnerability_e {
NGAP_PRE_EMPTION_VULNERABILITY_NOT_PREEMPTABLE = 0,
NGAP_PRE_EMPTION_VULNERABILITY_PREEMPTABLE = 1,
NGAP_PRE_EMPTION_VULNERABILITY_MAX,
} ngap_pre_emp_vulnerability_t;
typedef struct ngap_allocation_retention_priority_s {
ngap_priority_level_t priority_level;
ngap_pre_emp_capability_t pre_emp_capability;
ngap_pre_emp_vulnerability_t pre_emp_vulnerability;
} ngap_allocation_retention_priority_t;
typedef struct ngap_security_capabilities_s {
uint16_t nRencryption_algorithms;
uint16_t nRintegrity_algorithms;
@@ -205,14 +166,6 @@ typedef enum ngap_rrc_establishment_cause_e {
NGAP_RRC_CAUSE_LAST
} ngap_rrc_establishment_cause_t;
typedef struct pdusession_level_qos_parameter_s {
uint8_t qfi;
uint64_t fiveQI;
uint64_t qos_priority;
fiveQI_t fiveQI_type;
ngap_allocation_retention_priority_t allocation_retention_priority;
} pdusession_level_qos_parameter_t;
typedef struct fiveg_s_tmsi_s {
uint16_t amf_set_id;
uint8_t amf_pointer;
@@ -241,7 +194,7 @@ typedef struct ngap_mobility_restriction_s{
/* 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];
pdusession_level_qos_parameter_t qos[MAX_QOS_FLOWS];
pdu_session_type_t pdu_session_type;
// UPF endpoint of the NG-U (N3) transport bearer
gtpu_tunnel_t n3_incoming;
@@ -272,7 +225,7 @@ typedef struct pdusession_setup_s {
uint8_t nb_of_qos_flow;
/* qos flow list(1 ~ 64) */
pdusession_associate_qosflow_t associated_qos_flows[QOSFLOW_MAX_VALUE];
pdusession_associate_qosflow_t associated_qos_flows[MAX_QOS_FLOWS];
} pdusession_setup_t;
typedef struct qos_flow_tobe_modified_s {
@@ -286,7 +239,7 @@ typedef struct pdusession_modify_s {
uint8_t nb_of_qos_flow;
// qos_flow_add_or_modify
qos_flow_tobe_modified_t qos[QOSFLOW_MAX_VALUE];
qos_flow_tobe_modified_t qos[MAX_QOS_FLOWS];
} pdusession_modify_t;
/* Cause (9.3.1.2 of 3GPP TS 38.413) */
@@ -506,7 +459,7 @@ typedef struct {
uint8_t pdusession_id;
// QoS Flow Information List
uint8_t nb_of_qos_flow;
qosflow_info_t qos_flow_info[QOSFLOW_MAX_VALUE];
qosflow_info_t qos_flow_info[MAX_QOS_FLOWS];
} pdusession_resource_info_t;
/* 3GPP TS 38.413 9.3.1.97 */
@@ -545,8 +498,6 @@ typedef enum {
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 */
@@ -562,7 +513,7 @@ typedef struct {
// Target ID
target_ran_node_id_t target_gnb_id;
// PDU Session Resource List
uint8_t nb_of_pdusessions;
uint16_t nb_of_pdusessions;
pdusession_resource_t pdusessions[NGAP_MAX_PDU_SESSION];
// Source to Target Transparent Container
source_to_target_transparent_container_t *source2target;
@@ -610,7 +561,7 @@ typedef struct {
// Security Context
ngap_security_context_t security_context;
// PDU Session Resource Setup List
uint8_t nb_of_pdusessions;
uint16_t nb_of_pdusessions;
ho_request_pdusession_t pduSessionResourceSetupList[NGAP_MAX_PDU_SESSION];
// Allowed NSSAI
uint8_t nb_allowed_nssais;
@@ -629,7 +580,7 @@ typedef struct {
typedef struct {
// QoS Flow Setup Response List
uint8_t nb_of_qos_flow;
pdusession_associate_qosflow_t qos_setup_list[QOSFLOW_MAX_VALUE];
pdusession_associate_qosflow_t qos_setup_list[MAX_QOS_FLOWS];
// DL NG-U UP TNL Information
uint32_t gtp_teid;
transport_layer_addr_t gNB_addr;
@@ -650,7 +601,7 @@ typedef struct {
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;
uint16_t nb_of_pdusessions;
// Target to Source Transparent Container
byte_array_t target2source;
} ngap_handover_request_ack_t;
@@ -659,7 +610,7 @@ typedef struct {
typedef struct {
// QoS Flow to be Forwarded List
uint8_t nb_of_qos_flow;
pdusession_associate_qosflow_t qos_setup_list[QOSFLOW_MAX_VALUE];
pdusession_associate_qosflow_t qos_setup_list[MAX_QOS_FLOWS];
// UL Forwarding UP TNL Information
uint32_t gtp_teid;
transport_layer_addr_t gNB_addr;
@@ -680,7 +631,7 @@ typedef struct {
// Handover Type
ho_type_t handoverType;
// PDU Session Resource Handover List
uint8_t nb_of_pdusessions;
uint16_t nb_of_pdusessions;
pdusession_resource_handover_t pdu_sessions[NGAP_MAX_PDU_SESSION];
// Target to Source Transparent Container
byte_array_t handoverCommand;
@@ -730,12 +681,12 @@ typedef struct ngap_initial_context_setup_resp_s {
uint32_t gNB_ue_ngap_id;
/* Number of pdusession setup-ed in the list */
uint8_t nb_of_pdusessions;
uint16_t nb_of_pdusessions;
/* list of pdusession setup-ed by RRC layers */
pdusession_setup_t pdusessions[NGAP_MAX_PDU_SESSION];
/* Number of pdusession failed to be setup in list */
uint8_t nb_of_pdusessions_failed;
uint16_t nb_of_pdusessions_failed;
/* list of pdusessions that failed to be setup */
pdusession_failed_t pdusessions_failed[NGAP_MAX_PDU_SESSION];
} ngap_initial_context_setup_resp_t;
@@ -823,7 +774,7 @@ typedef struct ngap_initial_context_setup_req_s {
uint8_t security_key[SECURITY_KEY_LENGTH];
/* Number of pdusession to be setup in the list */
uint8_t nb_of_pdusessions;
uint16_t nb_of_pdusessions;
// PDU Session Resource Setup Request List
pdusession_resource_item_t pdusession[NGAP_MAX_PDU_SESSION];
@@ -871,7 +822,7 @@ typedef struct ngap_pdusession_setup_req_s {
nssai_t allowed_nssai[8];
/* Number of pdusession to be setup in the list */
uint8_t nb_pdusessions_tosetup;
uint16_t nb_pdusessions_tosetup;
// PDU Session Resource Setup Request List
pdusession_resource_item_t pdusession[NGAP_MAX_PDU_SESSION];
@@ -885,12 +836,12 @@ typedef struct ngap_pdusession_setup_req_s {
typedef struct ngap_pdusession_setup_resp_s {
uint32_t gNB_ue_ngap_id;
/* Number of pdusession setup-ed in the list */
uint8_t nb_of_pdusessions;
uint16_t nb_of_pdusessions;
/* list of pdusession setup-ed by RRC layers */
pdusession_setup_t pdusessions[NGAP_MAX_PDU_SESSION];
/* Number of pdusession failed to be setup in list */
uint8_t nb_of_pdusessions_failed;
uint16_t nb_of_pdusessions_failed;
/* list of pdusessions that failed to be setup */
pdusession_failed_t pdusessions_failed[NGAP_MAX_PDU_SESSION];
} ngap_pdusession_setup_resp_t;
@@ -914,7 +865,7 @@ typedef struct pdusession_release_s {
typedef struct ngap_ue_release_req_s {
uint32_t gNB_ue_ngap_id;
/* Number of pdusession resource in the list */
uint8_t nb_of_pdusessions;
uint16_t nb_of_pdusessions;
/* list of pdusession resource by RRC layers */
pdusession_release_t pdusessions[NGAP_MAX_PDU_SESSION];
ngap_cause_t cause;
@@ -928,7 +879,7 @@ typedef struct ngap_pdusession_modify_req_s {
uint32_t gNB_ue_ngap_id;
/* Number of pdusession to be modify in the list */
uint8_t nb_pdusessions_tomodify;
uint16_t nb_pdusessions_tomodify;
// PDU Session Resource Modify Request List
pdusession_resource_item_t pdusession[NGAP_MAX_PDU_SESSION];
@@ -938,12 +889,12 @@ typedef struct ngap_pdusession_modify_resp_s {
uint32_t gNB_ue_ngap_id;
/* Number of pdusession modify-ed in the list */
uint8_t nb_of_pdusessions;
uint16_t nb_of_pdusessions;
/* list of pdusession modify-ed by RRC layers */
pdusession_modify_t pdusessions[NGAP_MAX_PDU_SESSION];
/* Number of pdusession failed to be modify in list */
uint8_t nb_of_pdusessions_failed;
uint16_t nb_of_pdusessions_failed;
/* list of pdusessions that failed to be modify */
pdusession_failed_t pdusessions_failed[NGAP_MAX_PDU_SESSION];
} ngap_pdusession_modify_resp_t;
@@ -959,7 +910,7 @@ typedef struct ngap_pdusession_release_command_s {
byte_array_t nas_pdu;
/* Number of pdusession to be released in the list */
uint8_t nb_pdusessions_torelease;
uint16_t nb_pdusessions_torelease;
/* PDUSession release command */
pdusession_release_t pdusession_release_params[NGAP_MAX_PDU_SESSION];
@@ -967,23 +918,13 @@ typedef struct ngap_pdusession_release_command_s {
} ngap_pdusession_release_command_t;
typedef struct ngap_pdusession_release_resp_s {
/* AMF UE id */
// AMF UE NGAP ID
uint64_t amf_ue_ngap_id;
/* gNB ue ngap id as initialized by NGAP layer */
uint32_t gNB_ue_ngap_id;
/* Number of pdusession released in the list */
uint8_t nb_of_pdusessions_released;
/* list of pdusessions released */
// RAN UE NGAP ID
uint32_t gNB_ue_ngap_id;
// PDU Session Resource Released List
uint16_t nb_of_pdusessions_released;
pdusession_release_t pdusession_release[NGAP_MAX_PDU_SESSION];
/* Number of pdusession failed to be released in list */
uint8_t nb_of_pdusessions_failed;
/* list of pdusessions that failed to be released */
pdusession_failed_t pdusessions_failed[NGAP_MAX_PDU_SESSION];
} ngap_pdusession_release_resp_t;
/** 9.2.3.14 Uplink RAN Status Transfer (3GPP TS 38.413)
@@ -27,10 +27,11 @@
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
#include "openair2/E2AP/flexric/src/util/time_now_us.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h"
#include "ds/seq_arr.h"
#if defined (NGRAN_GNB_CUCP)
#include "openair2/RRC/NR/rrc_gNB_UE_context.h"
#include "openair2/RRC/NR/rrc_gNB_radio_bearers.h"
#endif
bool read_gtp_sm(void * data)
@@ -62,16 +63,15 @@ bool read_gtp_sm(void * data)
rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[0], ue_id_list[i]);
gtp->msg.ngut[i].rnti = ue_id_list[i];
int nb_pdu_session = ue_context_p->ue_context.nb_of_pdusessions;
if (nb_pdu_session > 0) {
int nb_pdu_idx = nb_pdu_session - 1;
gtp->msg.ngut[i].teidgnb = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.n3_outgoing.teid;
gtp->msg.ngut[i].teidupf = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.n3_incoming.teid;
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t*, session, &ue_context_p->ue_context.pduSessions) {
gtp->msg.ngut[i].teidgnb = session->param.n3_outgoing.teid;
gtp->msg.ngut[i].teidupf = session->param.n3_incoming.teid;
// TODO: one PDU session has multiple QoS Flow
int nb_qos_flow = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.nb_qos;
if (nb_qos_flow > 0) {
gtp->msg.ngut[i].qfi = ue_context_p->ue_context.pduSession[nb_pdu_idx].param.qos[nb_qos_flow - 1].qfi;
DevAssert(seq_arr_size(&session->param.qos) == 1);
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->param.qos) {
gtp->msg.ngut[i].qfi = qos->qos.qfi;
}
break;
}
}
@@ -27,6 +27,7 @@
#include "openair2/E1AP/e1ap_common.h"
#include "openair2/E2AP/flexric/src/util/time_now_us.h"
#include "openair2/F1AP/f1ap_ids.h"
#include "ds/seq_arr.h"
static pthread_once_t once_kpm_mutex = PTHREAD_ONCE_INIT;
@@ -212,8 +213,8 @@ static arr_ue_id_t filter_ues_by_s_nssai_in_cu(const test_info_lst_t test_info)
/* UE has no AMF UE NGAP ID yet => can't send message */
if (ue->amf_ue_ngap_id >= (1LL << 40))
continue;
for (int p = 0; p < ue->nb_of_pdusessions; ++p) {
pdusession_t *pdu = &ue->pduSession[p].param;
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t*, item, &ue->pduSessions) {
pdusession_t* pdu = &item->param;
if (nssai_matches(pdu->nssai, sst, sd)) {
arr_ue_id.ue_id[arr_ue_id.sz] = fill_ue_id_data[ngran_gNB_CU](ue, 0, 0);
+1 -1
View File
@@ -1344,7 +1344,7 @@ void config_rlc(configmodule_interface_t *cfg, nr_rlc_configuration_t *rlc_confi
rlc_config->drb_um.sn_field_length = config_get_processedint(cfg, &rlc_drb_um_params[CONFIG_NR_RLC_DRB_UM_SN_FIELD_LENGTH_IDX]);
}
void config_pdcp(configmodule_interface_t *cfg, nr_pdcp_configuration_t *pdcp_config)
static void config_pdcp(configmodule_interface_t *cfg, nr_pdcp_configuration_t *pdcp_config)
{
/* DRB configuration */
paramdef_t pdcp_params[] = NR_PDCP_DRB_GLOBALPARAMS_DESC;
+4
View File
@@ -1 +1,5 @@
add_subdirectory(MESSAGES)
if(ENABLE_TESTS)
add_subdirectory(tests)
endif()
-45
View File
@@ -319,51 +319,6 @@ int do_RRCReject(uint8_t *const buffer)
return (enc_rval.encoded + 7) / 8;
}
/* returns a default radio bearer config suitable for NSA etc */
NR_RadioBearerConfig_t *get_default_rbconfig(int eps_bearer_id,
int rb_id,
e_NR_CipheringAlgorithm ciphering_algorithm,
e_NR_SecurityConfig__keyToUse key_to_use,
const nr_pdcp_configuration_t *pdcp_config)
{
NR_RadioBearerConfig_t *rbconfig = calloc(1, sizeof(*rbconfig));
rbconfig->srb_ToAddModList = NULL;
rbconfig->srb3_ToRelease = NULL;
rbconfig->drb_ToAddModList = calloc(1,sizeof(*rbconfig->drb_ToAddModList));
NR_DRB_ToAddMod_t *drb_ToAddMod = calloc(1,sizeof(*drb_ToAddMod));
drb_ToAddMod->cnAssociation = calloc(1,sizeof(*drb_ToAddMod->cnAssociation));
drb_ToAddMod->cnAssociation->present = NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity;
drb_ToAddMod->cnAssociation->choice.eps_BearerIdentity= eps_bearer_id;
drb_ToAddMod->drb_Identity = rb_id;
drb_ToAddMod->reestablishPDCP = NULL;
drb_ToAddMod->recoverPDCP = NULL;
drb_ToAddMod->pdcp_Config = calloc(1,sizeof(*drb_ToAddMod->pdcp_Config));
asn1cCalloc(drb_ToAddMod->pdcp_Config->drb, drb);
asn1cCallocOne(drb->discardTimer, encode_discard_timer(pdcp_config->drb.discard_timer));
asn1cCallocOne(drb->pdcp_SN_SizeUL, encode_sn_size_ul(pdcp_config->drb.sn_size));
asn1cCallocOne(drb->pdcp_SN_SizeDL, encode_sn_size_dl(pdcp_config->drb.sn_size));
drb->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed;
drb->headerCompression.choice.notUsed = 0;
drb->integrityProtection = NULL;
drb->statusReportRequired = NULL;
drb->outOfOrderDelivery = NULL;
drb_ToAddMod->pdcp_Config->moreThanOneRLC = NULL;
asn1cCallocOne(drb_ToAddMod->pdcp_Config->t_Reordering, encode_t_reordering(pdcp_config->drb.t_reordering));
drb_ToAddMod->pdcp_Config->ext1 = NULL;
asn1cSeqAdd(&rbconfig->drb_ToAddModList->list,drb_ToAddMod);
rbconfig->drb_ToReleaseList = NULL;
asn1cCalloc(rbconfig->securityConfig, secConf);
asn1cCalloc(secConf->securityAlgorithmConfig, secConfAlgo);
secConfAlgo->cipheringAlgorithm = ciphering_algorithm;
secConfAlgo->integrityProtAlgorithm = NULL;
asn1cCallocOne(secConf->keyToUse, key_to_use);
return rbconfig;
}
//------------------------------------------------------------------------------
int do_RRCSetup(uint8_t *const buffer,
size_t buffer_size,
-6
View File
@@ -89,12 +89,6 @@ int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc);
int do_RRCReject(uint8_t *const buffer);
NR_RadioBearerConfig_t *get_default_rbconfig(int eps_bearer_id,
int rb_id,
e_NR_CipheringAlgorithm ciphering_algorithm,
e_NR_SecurityConfig__keyToUse key_to_use,
const nr_pdcp_configuration_t *pdcp_config);
int do_RRCSetup(uint8_t *const buffer,
size_t buffer_size,
const uint8_t transaction_id,
+17 -30
View File
@@ -56,6 +56,7 @@
#include "intertask_interface.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_configuration.h"
#include "openair2/SDAP/nr_sdap/nr_sdap_configuration.h"
typedef enum {
NR_RRC_OK=0,
@@ -103,9 +104,8 @@ typedef struct pdusession_s {
/* Unique pdusession_id for the UE. */
int pdusession_id;
byte_array_t nas_pdu;
uint8_t nb_qos;
/* Quality of service for this pdusession */
pdusession_level_qos_parameter_t qos[QOSFLOW_MAX_VALUE];
seq_arr_t qos;
/* The transport layer address for the IP packets */
pdu_session_type_t pdu_session_type;
// NG-RAN endpoint of the NG-U (N3) transport bearer
@@ -113,6 +113,8 @@ typedef struct pdusession_s {
// UPF endpoint of the NG-U (N3) transport bearer
gtpu_tunnel_t n3_incoming;
nssai_t nssai;
// PDU Session specific SDAP configuration
nr_sdap_configuration_t sdap_config;
} pdusession_t;
typedef struct pdu_session_param_s {
@@ -125,35 +127,13 @@ typedef struct pdu_session_param_s {
typedef struct drb_s {
int status;
int drb_id;
struct cnAssociation_s {
int present;
int eps_BearerIdentity;
struct sdap_config_s {
bool defaultDRB;
int pdusession_id;
int sdap_HeaderDL;
int sdap_HeaderUL;
int mappedQoS_FlowsToAdd[QOSFLOW_MAX_VALUE];
} sdap_config;
} cnAssociation;
struct pdcp_config_s {
int discardTimer;
int pdcp_SN_SizeUL;
int pdcp_SN_SizeDL;
int t_Reordering;
int integrityProtection;
struct headerCompression_s {
int NotUsed;
int present;
} headerCompression;
struct ext1_s {
int cipheringDisabled;
} ext1;
} pdcp_config;
int pdusession_id;
// F1-U Downlink Tunnel Config (on DU side)
gtpu_tunnel_t du_tunnel_config;
// F1-U Uplink Tunnel Config (on CU-UP side)
gtpu_tunnel_t cuup_tunnel_config;
// DRB-specific PDCP configuration
nr_pdcp_configuration_t pdcp_config;
} drb_t;
typedef enum {
@@ -176,13 +156,17 @@ typedef struct nr_redcap_ue_cap {
bool rlc_am_drb_long_sn_redcap_r17;
} nr_redcap_ue_cap_t;
typedef struct {
int drb_id;
pdusession_level_qos_parameter_t qos;
} nr_rrc_qos_t;
/* forward declaration */
typedef struct nr_handover_context_s nr_handover_context_t;
typedef struct gNB_RRC_UE_s {
time_t last_seen; // last time this UE has been accessed
drb_t established_drbs[MAX_DRBS_PER_UE];
NR_DRB_ToReleaseList_t *DRB_ReleaseList;
NR_SRB_INFO_TABLE_ENTRY Srb[NR_NUM_SRB];
@@ -238,8 +222,10 @@ typedef struct gNB_RRC_UE_s {
rb_id_t nsa_gtp_psi[S1AP_MAX_E_RAB];
//SA block
int nb_of_pdusessions;
rrc_pdu_session_param_t pduSession[NGAP_MAX_PDU_SESSION];
seq_arr_t pduSessions;
// Established DRBs
seq_arr_t drbs;
rrc_action_t xids[NR_RRC_TRANSACTION_IDENTIFIER_NUMBER];
uint8_t e_rab_release_command_flag;
uint32_t ue_rrc_inactivity_timer;
@@ -400,6 +386,7 @@ typedef struct gNB_RRC_INST_s {
RB_HEAD(rrc_cuup_tree, nr_rrc_cuup_container_t) cuups; // CU-UPs, indexed by assoc_id
size_t num_cuups;
// PDCP configuration parameters loaded during startup
nr_pdcp_configuration_t pdcp_config;
nr_rlc_configuration_t rlc_config;
} gNB_RRC_INST;
+1 -2
View File
@@ -122,11 +122,10 @@ byte_array_t rrc_gNB_encode_RRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t *
nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t srb_reest_bitmap, bool drb_reestablish);
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);
NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do_integrity, bool do_ciphering);
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);
+157 -112
View File
@@ -38,6 +38,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "5g_platform_types.h"
#include "openair2/RRC/NR/nr_rrc_proto.h"
#include "openair2/RRC/NR/rrc_gNB_UE_context.h"
#include "openair3/SECU/key_nas_deriver.h"
@@ -86,6 +87,7 @@
#include "rrc_gNB_mobility.h"
#include "rrc_gNB_radio_bearers.h"
#include "rrc_messages_types.h"
#include "rrc_gNB_asn1.h"
#include "seq_arr.h"
#include "tree.h"
#include "uper_decoder.h"
@@ -99,6 +101,7 @@
#include "ds/byte_array.h"
#include "alg/find.h"
#include "NR_HandoverCommand.h"
#include "openair2/SDAP/nr_sdap/nr_sdap_configuration.h"
#ifdef E2_AGENT
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
@@ -329,19 +332,63 @@ NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, uint8_t reestablish)
return list;
}
NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
static NR_SDAP_Config_t *nr_rrc_build_sdap_config_ie(const int pdusession_id,
uint8_t n_flows,
pdusession_level_qos_parameter_t *qos,
const nr_sdap_configuration_t *sdap_config)
{
DevAssert(n_flows < MAX_QOS_FLOWS);
// SDAP
NR_SDAP_Config_t *sc = calloc_or_fail(1, sizeof(*sc));
sc->defaultDRB = true;
sc->pdu_Session = pdusession_id;
sc->sdap_HeaderDL = sdap_config->header_dl_absent ? NR_SDAP_Config__sdap_HeaderDL_absent : NR_SDAP_Config__sdap_HeaderDL_present;
sc->sdap_HeaderUL = sdap_config->header_ul_absent ? NR_SDAP_Config__sdap_HeaderUL_absent : NR_SDAP_Config__sdap_HeaderUL_present;
// QoS
asn1cCalloc(sc->mappedQoS_FlowsToAdd, mappedQoS_FlowsToAdd);
for (uint8_t i = 0; i < n_flows; ++i) {
DevAssert(i < MAX_QOS_FLOWS);
NR_QFI_t *qfi = calloc_or_fail(1, sizeof(*qfi));
*qfi = qos[i].qfi;
LOG_D(NR_RRC, "Adding QFI %ld to PDU Session %d\n", *qfi, pdusession_id);
asn1cSeqAdd(&mappedQoS_FlowsToAdd->list, qfi);
}
return sc;
}
NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish, bool do_integrity, bool do_ciphering)
{
NR_DRB_ToAddMod_t *DRB_config = NULL;
NR_DRB_ToAddModList_t *DRB_configList = CALLOC(sizeof(*DRB_configList), 1);
for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
if (ue->established_drbs[i].status != DRB_INACTIVE) {
DRB_config = generateDRB_ASN1(&ue->established_drbs[i]);
if (reestablish) {
asn1cCallocOne(DRB_config->reestablishPDCP, NR_DRB_ToAddMod__reestablishPDCP_true);
}
asn1cSeqAdd(&DRB_configList->list, DRB_config);
FOR_EACH_SEQ_ARR(drb_t *, drb, &ue->drbs) {
rrc_pdu_session_param_t *pduSession = find_pduSession(&ue->pduSessions, drb->pdusession_id);
if (!pduSession) {
LOG_D(NR_RRC, "PDU Session %d not found, skip\n", drb->pdusession_id);
continue;
}
pdusession_t *session = &pduSession->param;
NR_DRB_ToAddMod_t *drb_ToAddMod = calloc_or_fail(1, sizeof(*drb_ToAddMod));
drb_ToAddMod->drb_Identity = drb->drb_id;
// PDCP config
drb_ToAddMod->pdcp_Config = nr_rrc_build_pdcp_config_ie(do_integrity, do_ciphering, &drb->pdcp_config);
if (reestablish) {
asn1cCallocOne(drb_ToAddMod->reestablishPDCP, NR_DRB_ToAddMod__reestablishPDCP_true);
}
// cn-association: SDAP config
// Get all QoS flows mapped to this DRB
pdusession_level_qos_parameter_t flows_to_add[MAX_QOS_FLOWS] = {0};
uint8_t n_flows = 0;
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, q, &session->qos)
{
if (q->drb_id == drb->drb_id) {
DevAssert(n_flows < MAX_QOS_FLOWS);
flows_to_add[n_flows++] = q->qos;
}
}
asn1cCalloc(drb_ToAddMod->cnAssociation, cn_association);
cn_association->present = NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config;
cn_association->choice.sdap_Config = nr_rrc_build_sdap_config_ie(drb->pdusession_id, n_flows, flows_to_add, &session->sdap_config);
asn1cSeqAdd(&DRB_configList->list, drb_ToAddMod);
}
if (DRB_configList->list.count == 0) {
free(DRB_configList);
@@ -645,8 +692,10 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC
uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
// Re-establish PDCP for SRB2 only
bool do_integrity = rrc->security.do_drb_integrity;
bool do_ciphering = rrc->security.do_drb_ciphering;
NR_SRB_ToAddModList_t *SRBs = createSRBlist(UE, srb_reest_bitmap);
NR_DRB_ToAddModList_t *DRBs = createDRBlist(UE, drb_reestablish);
NR_DRB_ToAddModList_t *DRBs = createDRBlist(UE, drb_reestablish, do_integrity, do_ciphering);
nr_rrc_reconfig_param_t params = {.cell_group_config = UE->masterCellGroup,
.transaction_id = xid,
@@ -656,14 +705,16 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC
.srb_config_list = SRBs};
UE->DRB_ReleaseList = NULL; // pointer transferred to params
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
if (UE->pduSession[i].param.nas_pdu.len > 0) {
params.dedicated_NAS_msg_list[params.num_nas_msg++] = UE->pduSession[i].param.nas_pdu;
UE->pduSession[i].param.nas_pdu.buf = NULL;
UE->pduSession[i].param.nas_pdu.len = 0;
LOG_D(NR_RRC, "Transfer NAS info with size %ld (pdusession idx %d) to RRCReconfiguration params\n", UE->pduSession[i].param.nas_pdu.len, i);
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &UE->pduSessions) {
pdusession_t *session = &item->param;
if (session->nas_pdu.len > 0) {
params.dedicated_NAS_msg_list[params.num_nas_msg++] = session->nas_pdu;
session->nas_pdu.buf = NULL;
session->nas_pdu.len = 0;
LOG_D(NR_RRC, "Transfer NAS info with size %ld to RRCReconfiguration params\n", session->nas_pdu.len);
}
}
if (UE->nas_pdu.len > 0) {
params.dedicated_NAS_msg_list[params.num_nas_msg++] = UE->nas_pdu;
UE->nas_pdu.buf = NULL;
@@ -696,12 +747,11 @@ static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_
return;
}
for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
if (ue_p->pduSession[i].param.nas_pdu.buf != NULL) {
ue_p->pduSession[i].xid = params.transaction_id;
}
if (ue_p->pduSession[i].status < PDU_SESSION_STATUS_ESTABLISHED) {
ue_p->pduSession[i].status = PDU_SESSION_STATUS_DONE;
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) {
if (item->param.nas_pdu.buf)
item->xid = params.transaction_id;
if (item->status < PDU_SESSION_STATUS_ESTABLISHED) {
item->status = PDU_SESSION_STATUS_DONE;
}
}
@@ -718,40 +768,24 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t
nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue_p, 0, false);
ue_p->xids[params.transaction_id] = RRC_PDUSESSION_MODIFY;
for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
rrc_pdu_session_param_t *session = &ue_p->pduSession[i];
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) {
pdusession_t *session = &item->param;
// bypass the new and already configured pdu sessions
if (session->status >= PDU_SESSION_STATUS_DONE) {
session->xid = params.transaction_id;
if (item->status >= PDU_SESSION_STATUS_DONE) {
item->xid = params.transaction_id;
continue;
}
if (session->cause.type != NGAP_CAUSE_NOTHING) {
if (item->cause.type != NGAP_CAUSE_NOTHING) {
// set xid of failure pdu session
session->xid = params.transaction_id;
session->status = PDU_SESSION_STATUS_FAILED;
continue;
}
// search exist DRB_config
int j;
for (j = 0; i < MAX_DRBS_PER_UE; j++) {
if (ue_p->established_drbs[j].status != DRB_INACTIVE
&& ue_p->established_drbs[j].cnAssociation.sdap_config.pdusession_id == session->param.pdusession_id)
break;
}
if (j == MAX_DRBS_PER_UE) {
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CauseRadioNetwork_unspecified};
session->xid = params.transaction_id;
session->status = PDU_SESSION_STATUS_FAILED;
session->cause = cause;
item->xid = params.transaction_id;
item->status = PDU_SESSION_STATUS_FAILED;
continue;
}
// Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping
for (qos_flow_index = 0; qos_flow_index < session->param.nb_qos; qos_flow_index++) {
switch (session->param.qos[qos_flow_index].fiveQI) {
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) {
switch (qos->qos.fiveQI) {
case 1: //100ms
case 2: //150ms
case 3: //50ms
@@ -765,18 +799,17 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t
break;
default:
LOG_E(NR_RRC, "not supported 5qi %lu\n", session->param.qos[qos_flow_index].fiveQI);
LOG_E(NR_RRC, "not supported 5qi %lu\n", qos->qos.fiveQI);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CauseRadioNetwork_not_supported_5QI_value};
session->status = PDU_SESSION_STATUS_FAILED;
session->xid = params.transaction_id;
session->cause = cause;
item->status = PDU_SESSION_STATUS_FAILED;
item->xid = params.transaction_id;
item->cause = cause;
continue;
}
LOG_I(NR_RRC, "index %d, QOS flow %d, 5QI %ld \n", i, qos_flow_index, session->param.qos[qos_flow_index].fiveQI);
LOG_I(NR_RRC, "PDU Session ID %d, QOS flow %d, 5QI %ld \n", session->pdusession_id, qos_flow_index, qos->qos.fiveQI);
}
session->status = PDU_SESSION_STATUS_DONE;
session->xid = params.transaction_id;
item->status = PDU_SESSION_STATUS_DONE;
item->xid = params.transaction_id;
}
byte_array_t msg = do_RRCReconfiguration(&params);
@@ -800,16 +833,21 @@ void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc,
uint8_t *nas_buffer)
//-----------------------------------------------------------------------------
{
NR_DRB_ToReleaseList_t *DRB_Release_configList2 = CALLOC(sizeof(*DRB_Release_configList2), 1);
nr_rrc_reconfig_param_t params = {.transaction_id = xid};
for (int i = 0; i < NB_RB_MAX; i++) {
if ((ue_p->pduSession[i].status == PDU_SESSION_STATUS_TORELEASE) && ue_p->pduSession[i].xid == xid) {
asn1cSequenceAdd(DRB_Release_configList2->list, NR_DRB_Identity_t, DRB_release);
*DRB_release = i + 1;
NR_DRB_ToReleaseList_t *to_release = CALLOC(sizeof(*to_release), 1);
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) {
if ((item->status != PDU_SESSION_STATUS_TORELEASE) && item->xid != xid)
continue;
FOR_EACH_SEQ_ARR(drb_t *, drb, &ue_p->drbs) {
if (drb->pdusession_id == item->param.pdusession_id) {
asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release);
*DRB_release = drb->drb_id; // DRB ID
}
}
}
params.drb_release_list = to_release;
nr_rrc_reconfig_param_t params = {.drb_release_list = DRB_Release_configList2, .transaction_id = xid};
if (nas_length > 0) {
params.dedicated_NAS_msg_list[params.num_nas_msg].buf = nas_buffer;
params.dedicated_NAS_msg_list[params.num_nas_msg++].len = nas_length;
@@ -870,8 +908,8 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
return;
/* loop through active DRBs */
for (int drb_id = 1; drb_id <= MAX_DRBS_PER_UE; drb_id++) {
drb_t *drb = get_drb(ue_p, drb_id);
if (drb->status == DRB_INACTIVE)
drb_t *drb = get_drb(&ue_p->drbs, drb_id);
if (!drb)
continue;
/* fetch an existing PDU session for this DRB */
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue_p, drb_id);
@@ -897,9 +935,8 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
/* PDCP configuration */
if (!drb_e1->pdcp_config)
drb_e1->pdcp_config = malloc_or_fail(sizeof(*drb_e1->pdcp_config));
bearer_context_pdcp_config_t *pdcp_config = drb_e1->pdcp_config;
set_bearer_context_pdcp_config(pdcp_config, drb, rrc->configuration.um_on_default_drb);
pdcp_config->pDCP_Reestablishment = true;
*drb_e1->pdcp_config = set_bearer_context_pdcp_config(drb->pdcp_config, rrc->configuration.um_on_default_drb, ue_p->redcap_cap);
drb_e1->pdcp_config->pDCP_Reestablishment = true;
/* increase DRB to modify counter */
pdu_e1->numDRB2Modify += 1;
}
@@ -1063,7 +1100,9 @@ static void rrc_gNB_process_RRCReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RR
* or at the first reconfiguration after RRC connection reestablishment in NR */
NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, 1 << SRB2); // Re-establish PDCP for SRB2 only
/* Create drb-ToAddModList */
NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, true);
bool do_integrity = rrc->security.do_drb_integrity;
bool do_ciphering = rrc->security.do_drb_ciphering;
NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, true, do_integrity, do_ciphering);
uint8_t new_xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
ue_p->xids[new_xid] = RRC_REESTABLISH_COMPLETE;
@@ -1783,8 +1822,12 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U
UE->n_initial_pdu = 0;
free(UE->initial_pdus);
UE->initial_pdus = NULL;
} else if (UE->nb_of_pdusessions > 0)
} else if (seq_arr_size(&UE->pduSessions) > 0)
rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(rrc, UE, xid);
else
LOG_W(NR_RRC,
"UE %d: RRC Reconfiguration Complete for PDU session establishment, but no PDU sessions were setup\n",
UE->rrc_ue_id);
break;
case RRC_PDUSESSION_MODIFY:
rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(rrc, UE, xid);
@@ -2071,7 +2114,8 @@ static void store_du_f1u_tunnel(const f1ap_drb_setup_t *drbs, int n, gNB_RRC_UE_
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
AssertFatal(drb_f1->up_dl_tnl_len == 1, "can handle only one UP param\n");
AssertFatal(drb_f1->id < MAX_DRBS_PER_UE, "illegal DRB ID %d\n", drb_f1->id);
drb_t *drb = get_drb(ue, drb_f1->id);
drb_t *drb = get_drb(&ue->drbs, drb_f1->id);
DevAssert(drb);
drb->du_tunnel_config = f1u_gtp_update(drb_f1->up_dl_tnl[0].teid, drb_f1->up_dl_tnl[0].tl_address);
}
}
@@ -2087,7 +2131,8 @@ static void fill_e1_bearer_modif_pdcp_status(gNB_RRC_UE_t *UE,
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_t *drb = get_drb(&UE->drbs, drb_id);
*drb_to_mod->pdcp_config = set_bearer_context_pdcp_config(drb->pdcp_config, um_on_default_drb, UE->redcap_cap);
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;
@@ -2111,20 +2156,24 @@ void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb
};
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;
int drb_index = 0;
FOR_EACH_SEQ_ARR(drb_t *, drb, &UE->drbs) {
int drb_id = drb->drb_id;
DRB_nGRAN_to_mod_t drb_to_mod = {0};
if (n > 0) {
/* DRBs to Setup in F1 UE Context Modifcation Response */
if (i == n)
if (drb_index == n)
break;
const f1ap_drb_setup_t *drb_f1 = &drbs[i];
const f1ap_drb_setup_t *drb_f1 = &drbs[drb_index];
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)
drb_t *drb = get_drb(&UE->drbs, drb_id);
if (!drb) {
LOG_E(NR_RRC, "Failed to send PDCP Status Request: DRB %d not found\n", drb_id);
continue;
}
drb_to_mod.id = drb_id;
// PDCP SN Status Request
drb_to_mod.pdcp_sn_status_requested = true;
@@ -2141,6 +2190,8 @@ void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb
to_mod->sessionId = pdu->param.pdusession_id;
// Fill E1 DRB to Modify item
to_mod->DRBnGRanModList[to_mod->numDRB2Modify++] = drb_to_mod;
drb_index++;
}
DevAssert(req.numPDUSessionsMod > 0);
DevAssert(req.numPDUSessions == 0);
@@ -2197,7 +2248,7 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance
}
if (resp->drbs_len > 0) {
int num_drb = get_number_active_drbs(UE);
int num_drb = seq_arr_size(&UE->drbs);
DevAssert(num_drb == 0 || num_drb == resp->drbs_len);
/* Note: we would ideally check that SRB2 is acked, but at least LiteOn DU
@@ -2288,6 +2339,8 @@ static void rrc_delete_ue_data(gNB_RRC_UE_t *UE)
free_MeasConfig(UE->measConfig);
free(UE->redcap_cap);
UE->redcap_cap = NULL;
seq_arr_free(&UE->pduSessions, free_pdusession);
seq_arr_free(&UE->drbs, free_drb);
}
void rrc_remove_ue(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *ue_context_p)
@@ -2318,9 +2371,7 @@ static void rrc_CU_process_ue_context_release_complete(MessageDef *msg_p)
/* only trigger release if it has been requested by core
* otherwise, it might be CU that requested release on a DU during normal
* operation (i.e, handover) */
uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION];
get_pduSession_array(UE, pdu_sessions);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, &UE->pduSessions);
rrc_remove_ue(RC.nrrrc[0], ue_context_p);
}
}
@@ -2455,17 +2506,6 @@ unsigned int mask_flip(unsigned int x) {
return((((x>>8) + (x<<8))&0xffff)>>6);
}
pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession)
{
pdusession_t *pdu = &pduSession->param;
for (int i = 0; i < pdu->nb_qos; i++) {
if (qfi == pdu->qos[i].qfi)
return &pdu->qos[i];
}
AssertFatal(1 == 0, "The pdu session %d does not contain a qos flow with qfi = %d\n", pdu->pdusession_id, qfi);
return NULL;
}
/* \bref return F1AP QoS characteristics based on Qos flow parameters */
f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param)
{
@@ -2480,13 +2520,11 @@ f1ap_qos_flow_param_t get_qos_char_from_qos_flow_param(const pdusession_level_qo
qos_char.qos_type = NON_DYNAMIC;
qos_char.nondyn.fiveQI = qos_param->fiveQI;
}
const ngap_allocation_retention_priority_t *a = &qos_param->allocation_retention_priority;
const qos_arp_t *a = &qos_param->arp;
qos_char.arp.prio = a->priority_level;
qos_char.arp.preempt_cap = a->pre_emp_capability == NGAP_PRE_EMPTION_CAPABILITY_MAY_TRIGGER_PREEMPTION
? MAY_TRIGGER_PREEMPTION
: SHALL_NOT_TRIGGER_PREEMPTION;
qos_char.arp.preempt_vuln =
a->pre_emp_vulnerability == NGAP_PRE_EMPTION_VULNERABILITY_PREEMPTABLE ? PREEMPTABLE : NOT_PREEMPTABLE;
qos_char.arp.preempt_cap =
a->pre_emp_capability == PEC_MAY_TRIGGER_PREEMPTION ? MAY_TRIGGER_PREEMPTION : SHALL_NOT_TRIGGER_PREEMPTION;
qos_char.arp.preempt_vuln = a->pre_emp_vulnerability == PEV_PREEMPTABLE ? PREEMPTABLE : NOT_PREEMPTABLE;
return qos_char;
}
@@ -2500,13 +2538,15 @@ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc,
{
int nb_drb = 0;
for (int p = 0; p < numPduSession; ++p) {
rrc_pdu_session_param_t *pdu = find_pduSession(UE, pduSession[p].id, false);
rrc_pdu_session_param_t *pdu = find_pduSession(&UE->pduSessions, pduSession[p].id);
DevAssert(pdu);
for (int i = 0; i < pduSession[p].numDRBSetup; i++) {
const DRB_nGRAN_setup_t *drb_config = &pduSession[p].DRBnGRanList[i];
drb_t *rrc_drb = get_drb(UE, pduSession[p].DRBnGRanList[i].id);
drb_t *rrc_drb = get_drb(&UE->drbs, pduSession[p].DRBnGRanList[i].id);
nr_pdcp_configuration_t pdcp = rrc_drb->pdcp_config;
DevAssert(rrc_drb);
DevAssert(nb_drb < MAX_DRBS_PER_UE);
f1ap_drb_to_setup_t *drb = &drbs[nb_drb];
drb->id = rrc_drb->drb_id;
@@ -2522,11 +2562,11 @@ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc,
/* find the QoS characteristics stored at RRC based on QFI returned
* from E1. We expect this to correspond to a QFI we passed to E1
* previously, so we expect it to exist. */
pdusession_level_qos_parameter_t *qos_param = get_qos_characteristics(qfi, pdu);
nr_rrc_qos_t *qos_param = find_qos(&pdu->param.qos, qfi);
DevAssert(qos_param);
f1ap_drb_flows_mapped_t *flow = &drb->nr.flows[j];
flow->qfi = qfi;
flow->param = get_qos_char_from_qos_flow_param(qos_param);
flow->param = get_qos_char_from_qos_flow_param(&qos_param->qos);
}
/* the DRB QoS parameters: we just reuse the ones from the first flow */
drb->nr.drb_qos = drb->nr.flows[0].param;
@@ -2538,10 +2578,11 @@ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc,
drb->up_ul_tnl[0].teid = drb_config->UpParamList[0].tl_info.teId;
drb->up_ul_tnl_len = 1;
drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM;
DevAssert(pdcp.drb.sn_size == 18 || pdcp.drb.sn_size == 12);
drb->dl_pdcp_sn_len = malloc_or_fail(sizeof(*drb->dl_pdcp_sn_len));
*drb->dl_pdcp_sn_len = rrc_drb->pdcp_config.pdcp_SN_SizeDL == 1 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B;
*drb->dl_pdcp_sn_len = pdcp.drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B;
drb->ul_pdcp_sn_len = malloc_or_fail(sizeof(*drb->ul_pdcp_sn_len));
*drb->ul_pdcp_sn_len = rrc_drb->pdcp_config.pdcp_SN_SizeUL == 1 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B;
*drb->ul_pdcp_sn_len = pdcp.drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B;
nb_drb++;
}
@@ -2569,7 +2610,7 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp
// save the tunnel address for the PDU sessions
for (int i = 0; i < resp->numPDUSessions; i++) {
pdu_session_setup_t *e1_pdu = &resp->pduSession[i];
rrc_pdu_session_param_t *rrc_pdu = find_pduSession(UE, e1_pdu->id, false);
rrc_pdu_session_param_t *rrc_pdu = find_pduSession(&UE->pduSessions, e1_pdu->id);
if (rrc_pdu == NULL) {
LOG_W(RRC, "E1: received setup for PDU session %ld, but has not been requested\n", e1_pdu->id);
continue;
@@ -2577,11 +2618,15 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp
rrc_pdu->param.n3_outgoing = f1u_gtp_update(e1_pdu->tl_info.teId, e1_pdu->tl_info.tlAddress);
// save the tunnel address for the DRBs
for (int i = 0; i < e1_pdu->numDRBSetup; i++) {
DRB_nGRAN_setup_t *drb_config = &e1_pdu->DRBnGRanList[i];
for (int j = 0; j < e1_pdu->numDRBSetup; j++) {
DRB_nGRAN_setup_t *drb_config = &e1_pdu->DRBnGRanList[j];
// numUpParam only relevant in F1, but not monolithic
AssertFatal(drb_config->numUpParam <= 1, "can only up to one UP param\n");
drb_t *drb = get_drb(UE, drb_config->id);
drb_t *drb = get_drb(&UE->drbs, drb_config->id);
if (!drb) {
LOG_E(RRC, "E1: DRB %ld not found for PDU session %ld\n", drb_config->id, e1_pdu->id);
continue;
}
UP_TL_information_t *tl_info = &drb_config->UpParamList[0].tl_info;
drb->cuup_tunnel_config = f1u_gtp_update(tl_info->teId, tl_info->tlAddress);
}
@@ -2623,6 +2668,7 @@ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t
}
int n_drb_mod = 0;
int drb_ids[MAX_DRBS_PER_UE] = {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];
@@ -2630,6 +2676,8 @@ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t
for (int j = 0; j < pdu->numDRBModified; j++) {
// Trigger UL RAN Status Transfer
if (pdu->DRBnGRanModList[j].pdcp_status) {
DevAssert(n_drb_mod < MAX_DRBS_PER_UE);
drb_ids[n_drb_mod] = pdu->DRBnGRanModList[j].id;
pdcp_status[n_drb_mod++] = *pdu->DRBnGRanModList[j].pdcp_status;
}
}
@@ -2638,7 +2686,7 @@ void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t
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);
ue->ho_context->source->ho_status_transfer(rrc, ue, n_drb_mod, drb_ids, pdcp_status);
}
}
@@ -2774,11 +2822,8 @@ static bool write_rrc_stats(const gNB_RRC_INST *rrc)
time_t last_seen = now - ue_ctxt->last_seen;
fprintf(f, " last RRC activity: %ld seconds ago\n", last_seen);
if (ue_ctxt->nb_of_pdusessions == 0)
fprintf(f, " (no PDU sessions)\n");
for (int nb_pdu = 0; nb_pdu < ue_ctxt->nb_of_pdusessions; ++nb_pdu) {
const rrc_pdu_session_param_t *pdu = &ue_ctxt->pduSession[nb_pdu];
fprintf(f, " PDU session %d ID %d status %s\n", nb_pdu, pdu->param.pdusession_id, get_pdusession_status_text(pdu->status));
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, pdu, &ue_ctxt->pduSessions) {
fprintf(f, " PDU session ID %d status %s\n", pdu->param.pdusession_id, get_pdusession_status_text(pdu->status));
}
fprintf(f, " associated DU: ");
+289 -240
View File
@@ -42,6 +42,8 @@
#include "NR_UE-CapabilityRAT-ContainerList.h"
#include "NR_HandoverCommand.h"
#include "NR_HandoverCommand-IEs.h"
#include "E1AP_RLC-Mode.h"
#include "NR_PDCP-Config.h"
#include "NGAP_CauseRadioNetwork.h"
#include "NGAP_Dynamic5QIDescriptor.h"
#include "NGAP_GTPTunnel.h"
@@ -88,6 +90,7 @@
#include "uper_encoder.h"
#include "rrc_gNB_mobility.h"
#include "rrc_gNB_du.h"
#include "common/utils/alg/find.h"
#ifdef E2_AGENT
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
@@ -171,20 +174,30 @@ static nr_guami_t get_guami(const uint32_t amf_Id, const plmn_id_t plmn)
/** @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;
/* QoS handling */
DevAssert(!dst->qos.data);
DevAssert(src->nb_qos < MAX_QOS_FLOWS);
// Initialise mapped QoS list per PDU Session
seq_arr_init(&dst->qos, sizeof(nr_rrc_qos_t));
// Add QoS flow to list
for (uint8_t i = 0; i < src->nb_qos; ++i) {
if (!add_qos(&dst->qos, &src->qos[i])) {
LOG_E(NR_RRC, "Failed to add QoS flow %d for PDU session %d\n", src->qos[i].qfi, dst->pdusession_id);
continue;
}
}
}
/** @brief Copy NGAP PDU Session Resource item to RRC pdusession_t struct */
/** @brief Copy NGAP PDU Session Resource item to RRC pdusession_t struct to setup */
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->nssai = src->nssai;
// Use the PDU session transfer function to handle QoS and other PDU session transfer-specific fields
cp_pdusession_transfer_to_pdusession(dst, &src->pdusessionTransfer);
}
@@ -253,6 +266,57 @@ void rrc_gNB_send_NGAP_NAS_FIRST_REQ(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, NR_RRC
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, message_p);
}
/** @brief Returns an instance of E1AP DRB To Setup List */
static DRB_nGRAN_to_setup_t fill_e1_drb_to_setup(const drb_t *rrc_drb,
const pdusession_t *session,
bool const um_on_default_drb,
const nr_redcap_ue_cap_t *redcap_cap)
{
DRB_nGRAN_to_setup_t drb_ngran = {0};
drb_ngran.id = rrc_drb->drb_id;
drb_ngran.sdap_config.defaultDRB = true;
drb_ngran.sdap_config.sDAP_Header_UL = session->sdap_config.header_ul_absent ? 1 : 0;
drb_ngran.sdap_config.sDAP_Header_DL = session->sdap_config.header_dl_absent ? 1 : 0;
drb_ngran.pdcp_config = set_bearer_context_pdcp_config(rrc_drb->pdcp_config, um_on_default_drb, redcap_cap);
drb_ngran.numCellGroups = 1;
for (int k = 0; k < drb_ngran.numCellGroups; k++) {
drb_ngran.cellGroupList[k] = MCG; // 1 cellGroup only
}
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) {
pdusession_level_qos_parameter_t *qos_session = &qos->qos;
DevAssert(drb_ngran.numQosFlow2Setup < MAX_QOS_FLOWS);
qos_flow_to_setup_t *qos_flow = &drb_ngran.qosFlows[drb_ngran.numQosFlow2Setup++];
qos_characteristics_t *qos_char = &qos_flow->qos_params.qos_characteristics;
qos_flow->qfi = qos_session->qfi;
qos_char->qos_type = qos_session->fiveQI_type;
if (qos_char->qos_type == DYNAMIC) {
qos_char->dynamic.fiveqi = qos_session->fiveQI;
qos_char->dynamic.qos_priority_level = qos_session->qos_priority;
} else {
qos_char->non_dynamic.fiveqi = qos_session->fiveQI;
qos_char->non_dynamic.qos_priority_level = qos_session->qos_priority;
}
ngran_allocation_retention_priority_t *arp_out = &qos_flow->qos_params.alloc_reten_priority;
qos_arp_t *arp_in = &qos_session->arp;
arp_out->priority_level = arp_in->priority_level;
arp_out->preemption_capability = arp_in->pre_emp_capability;
arp_out->preemption_vulnerability = arp_in->pre_emp_vulnerability;
}
return drb_ngran;
}
static nr_sdap_configuration_t get_sdap_config(const bool enable_sdap)
{
nr_sdap_configuration_t sdap = {.header_dl_absent = !enable_sdap, .header_ul_absent = !enable_sdap};
return sdap;
}
/**
* @brief Triggers bearer setup for the specified UE.
*
@@ -278,9 +342,15 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession
e1ap_nssai_t cuup_nssai = {0};
for (int i = 0; i < n; i++) {
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, sessions[i].pdusession_id, true);
// Retrieve SDAP configuration and PDU Session to the list
sessions[i].sdap_config = get_sdap_config(rrc->configuration.enable_sdap);
rrc_pdu_session_param_t *pduSession = add_pduSession(&UE->pduSessions, &sessions[i]);
if (!pduSession) {
LOG_E(NR_RRC, "Could not add PDU Session %d for UE %d\n", sessions[i].pdusession_id, UE->rrc_ue_id);
continue;
}
pdusession_t *session = &pduSession->param;
cp_pdusession(session, &sessions[i]);
// Fill E1 Bearer Context Modification Request
bearer_req.gNB_cu_cp_ue_id = UE->rrc_ue_id;
security_information_t *secInfo = &bearer_req.secInfo;
secInfo->cipheringAlgorithm = rrc->security.do_drb_ciphering ? UE->ciphering_algorithm : 0;
@@ -307,60 +377,18 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession
inet_ntop(AF_INET, n3_incoming->addr.buffer, ip_str, sizeof(ip_str));
LOG_I(NR_RRC, "Bearer Context Setup: PDU Session ID=%d, incoming TEID=0x%08x, Addr=%s\n", session->pdusession_id, n3_incoming->teid, ip_str);
/* we assume for the moment one DRB per PDU session. Activate the bearer,
* and configure in RRC. */
int drb_id = get_next_available_drb_id(UE);
drb_t *rrc_drb = generateDRB(UE,
drb_id,
pduSession,
rrc->configuration.enable_sdap,
rrc->security.do_drb_integrity,
rrc->security.do_drb_ciphering,
&rrc->pdcp_config);
pdu->numDRB2Setup = 1; // One DRB per PDU Session. TODO: Remove hardcoding
for (int j=0; j < pdu->numDRB2Setup; j++) {
DRB_nGRAN_to_setup_t *drb = pdu->DRBnGRanList + j;
drb->id = rrc_drb->drb_id;
/* SDAP */
struct sdap_config_s *sdap_config = &rrc_drb->cnAssociation.sdap_config;
drb->sdap_config.defaultDRB = sdap_config->defaultDRB;
drb->sdap_config.sDAP_Header_UL = sdap_config->sdap_HeaderUL;
drb->sdap_config.sDAP_Header_DL = sdap_config->sdap_HeaderDL;
/* PDCP */
set_bearer_context_pdcp_config(&drb->pdcp_config, rrc_drb, rrc->configuration.um_on_default_drb);
drb->numCellGroups = 1; // assume one cell group associated with a DRB
// Set all Cell Group IDs to MCG
for (int k=0; k < drb->numCellGroups; k++) {
cell_group_id_t *cellGroup = drb->cellGroupList + k;
*cellGroup = MCG;
}
drb->numQosFlow2Setup = session->nb_qos;
for (int k=0; k < drb->numQosFlow2Setup; k++) {
qos_flow_to_setup_t *qos_flow = drb->qosFlows + k;
pdusession_level_qos_parameter_t *qos_session = session->qos + k;
qos_characteristics_t *qos_char = &qos_flow->qos_params.qos_characteristics;
qos_flow->qfi = qos_session->qfi;
qos_char->qos_type = qos_session->fiveQI_type;
if (qos_char->qos_type == DYNAMIC) {
qos_char->dynamic.fiveqi = qos_session->fiveQI;
qos_char->dynamic.qos_priority_level = qos_session->qos_priority;
} else {
qos_char->non_dynamic.fiveqi = qos_session->fiveQI;
qos_char->non_dynamic.qos_priority_level = qos_session->qos_priority;
}
ngran_allocation_retention_priority_t *rent_priority = &qos_flow->qos_params.alloc_reten_priority;
ngap_allocation_retention_priority_t *rent_priority_in = &qos_session->allocation_retention_priority;
rent_priority->priority_level = rent_priority_in->priority_level;
rent_priority->preemption_capability = rent_priority_in->pre_emp_capability;
rent_priority->preemption_vulnerability = rent_priority_in->pre_emp_vulnerability;
for (int j = 0; j < pdu->numDRB2Setup; j++) {
drb_t *rrc_drb = nr_rrc_add_drb(&UE->drbs, session->pdusession_id, &rrc->pdcp_config);
if (!rrc_drb) {
LOG_E(NR_RRC, "UE %d: failed to add DRB for PDU session ID %d\n", UE->rrc_ue_id, session->pdusession_id);
return false;
}
DevAssert(seq_arr_size(&pduSession->param.qos) == 1);
nr_rrc_qos_t *qos = (nr_rrc_qos_t *)seq_arr_at(&pduSession->param.qos, 0);
qos->drb_id = rrc_drb->drb_id; // map DRB to QFI
LOG_I(NR_RRC, "UE %d: added DRB %d for PDU session ID %d\n", UE->rrc_ue_id, rrc_drb->drb_id, rrc_drb->pdusession_id);
pdu->DRBnGRanList[0] = fill_e1_drb_to_setup(rrc_drb, session, rrc->configuration.um_on_default_drb, UE->redcap_cap);
}
}
/* Limitation: we assume one fixed CU-UP per UE. We base the selection on
@@ -493,6 +521,26 @@ static gtpu_tunnel_t cp_gtp_tunnel(const gtpu_tunnel_t in)
return out;
}
/** @brief Fill NG PDU Session Setup item from stored setup PDU Session in RRC list */
static pdusession_setup_t fill_ngap_pdusession_setup(pdusession_t *session)
{
DevAssert(session != NULL);
pdusession_setup_t out = {0};
out.pdusession_id = session->pdusession_id;
out.pdu_session_type = session->pdu_session_type;
out.n3_outgoing = cp_gtp_tunnel(session->n3_outgoing);
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &session->qos) {
DevAssert(out.nb_of_qos_flow < MAX_QOS_FLOWS);
pdusession_associate_qosflow_t *q = &out.associated_qos_flows[out.nb_of_qos_flow++];
q->qfi = qos_session->qos.qfi;
q->qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
}
char ip_str[INET_ADDRSTRLEN] = {0};
inet_ntop(AF_INET, out.n3_outgoing.addr.buffer, ip_str, sizeof(ip_str));
LOG_I(NR_RRC, "PDU Session Setup: ID=%d, outgoing TEID=0x%08x, Addr=%s\n", out.pdusession_id, out.n3_outgoing.teid, ip_str);
return out;
}
void rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
{
MessageDef *msg_p = NULL;
@@ -503,17 +551,10 @@ void rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
for (int pdusession = 0; pdusession < UE->nb_of_pdusessions; pdusession++) {
rrc_pdu_session_param_t *session = &UE->pduSession[pdusession];
// Optional: PDU Sessions to Setup
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) {
if (session->status == PDU_SESSION_STATUS_DONE) {
pdu_sessions_done++;
resp->pdusessions[pdusession].pdusession_id = session->param.pdusession_id;
resp->pdusessions[pdusession].n3_outgoing = cp_gtp_tunnel(session->param.n3_outgoing);
resp->pdusessions[pdusession].nb_of_qos_flow = session->param.nb_qos;
for (int qos_flow_index = 0; qos_flow_index < session->param.nb_qos; qos_flow_index++) {
resp->pdusessions[pdusession].associated_qos_flows[qos_flow_index].qfi = session->param.qos[qos_flow_index].qfi;
resp->pdusessions[pdusession].associated_qos_flows[qos_flow_index].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
}
resp->pdusessions[pdu_sessions_done++] = fill_ngap_pdusession_setup(&session->param);
} else if (session->status != PDU_SESSION_STATUS_ESTABLISHED) {
session->status = PDU_SESSION_STATUS_FAILED;
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID};
@@ -693,25 +734,10 @@ void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE
ngap_pdusession_setup_resp_t *resp = &NGAP_PDUSESSION_SETUP_RESP(msg_p);
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
for (int pdusession = 0; pdusession < UE->nb_of_pdusessions; pdusession++) {
rrc_pdu_session_param_t *session = &UE->pduSession[pdusession];
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) {
if (session->status == PDU_SESSION_STATUS_DONE) {
pdusession_setup_t *tmp = &resp->pdusessions[pdu_sessions_done];
tmp->pdusession_id = session->param.pdusession_id;
tmp->nb_of_qos_flow = session->param.nb_qos;
tmp->n3_outgoing = cp_gtp_tunnel(session->param.n3_outgoing);
tmp->pdu_session_type = session->param.pdu_session_type;
for (int qos_flow_index = 0; qos_flow_index < tmp->nb_of_qos_flow; qos_flow_index++) {
tmp->associated_qos_flows[qos_flow_index].qfi = session->param.qos[qos_flow_index].qfi;
tmp->associated_qos_flows[qos_flow_index].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
}
resp->pdusessions[pdu_sessions_done++] = fill_ngap_pdusession_setup(&session->param);
session->status = PDU_SESSION_STATUS_ESTABLISHED;
char ip_str[INET_ADDRSTRLEN] = {0};
inet_ntop(AF_INET, tmp->n3_outgoing.addr.buffer, ip_str, sizeof(ip_str));
LOG_I(NR_RRC, "PDU Session Setup Response: ID=%d, outgoing TEID=0x%08x, Addr=%s\n", tmp->pdusession_id, tmp->n3_outgoing.teid, ip_str);
pdu_sessions_done++;
} else if (session->status != PDU_SESSION_STATUS_ESTABLISHED) {
session->status = PDU_SESSION_STATUS_FAILED;
pdusession_failed_t *fail = &resp->pdusessions_failed[pdu_sessions_failed];
@@ -729,8 +755,8 @@ void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE
itti_send_msg_to_task(TASK_NGAP, rrc->module_id, msg_p);
}
for(int i = 0; i < NB_RB_MAX; i++) {
UE->pduSession[i].xid = -1;
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) {
session->xid = -1;
}
return;
@@ -818,7 +844,7 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins
// might be more work than is worth it. See 8.2.1.4 in 38.413
for (int i = 0; i < msg->nb_pdusessions_tosetup; ++i) {
const pdusession_resource_item_t *p = &msg->pdusession[i];
rrc_pdu_session_param_t *exist = find_pduSession(UE, p->pdusession_id, false /* don't create */);
rrc_pdu_session_param_t *exist = find_pduSession(&UE->pduSessions, p->pdusession_id);
if (exist) {
LOG_E(NR_RRC, "UE %d: already has existing PDU session %d rejecting PDU Session Resource Setup Request\n", UE->rrc_ue_id, p->pdusession_id);
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_MULTIPLE_PDU_SESSION_ID_INSTANCES};
@@ -867,6 +893,36 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins
}
}
/** @brief Update existing QoS Flow mapped in the UE context */
static void nr_rrc_update_qos(seq_arr_t *list, const int nb_qos, const pdusession_level_qos_parameter_t *in_qos)
{
DevAssert(nb_qos == 1);
DevAssert(nb_qos < MAX_QOS_FLOWS);
for (uint8_t i = 0; i < nb_qos; ++i) {
nr_rrc_qos_t *qos = find_qos(list, in_qos[i].qfi);
if (qos) {
AssertFatal(qos->qos.qfi == in_qos[i].qfi, "QoS Flow to modify must match existing one");
LOG_I(NR_RRC, "Updating QoS for QFI=%d\n", in_qos[i].qfi);
qos->qos = in_qos[i];
} else {
LOG_E(NR_RRC, "Failed to update QoS for QFI=%d: QoS flow not found\n", in_qos[i].qfi);
}
}
}
/** @brief Update stored pdusession_t in list from NGAP PDU Session Resource item */
static void nr_rrc_update_pdusession(pdusession_t *dst, const pdusession_resource_item_t *src)
{
dst->pdusession_id = src->pdusession_id;
dst->nas_pdu = src->nas_pdu;
dst->pdu_session_type = src->pdusessionTransfer.pdu_session_type;
dst->n3_incoming = src->pdusessionTransfer.n3_incoming;
dst->nssai = src->nssai;
DevAssert(dst->qos.data); // QoS list has to be initialized at this stage
// Update QoS flow
nr_rrc_update_qos(&dst->qos, src->pdusessionTransfer.nb_qos, src->pdusessionTransfer.qos);
}
//------------------------------------------------------------------------------
int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t instance)
//------------------------------------------------------------------------------
@@ -885,33 +941,21 @@ int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t ins
gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
bool all_failed = true;
for (int i = 0; i < req->nb_pdusessions_tomodify; i++) {
rrc_pdu_session_param_t *sess;
const pdusession_resource_item_t *sessMod = req->pdusession + i;
for (sess = UE->pduSession; sess < UE->pduSession + UE->nb_of_pdusessions; sess++)
if (sess->param.pdusession_id == sessMod->pdusession_id)
break;
if (sess == UE->pduSession + UE->nb_of_pdusessions) {
const pdusession_resource_item_t *sessMod = &req->pdusession[i];
rrc_pdu_session_param_t *session = find_pduSession(&UE->pduSessions, sessMod->pdusession_id);
if (!session) {
LOG_W(NR_RRC, "Requested modification of non-existing PDU session, refusing modification\n");
UE->nb_of_pdusessions++;
sess->status = PDU_SESSION_STATUS_FAILED;
sess->param.pdusession_id = sessMod->pdusession_id;
sess->cause.type = NGAP_CAUSE_RADIO_NETWORK;
UE->pduSession[i].cause.type = NGAP_CAUSE_RADIO_NETWORK;
UE->pduSession[i].cause.value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID;
rrc_pdu_session_param_t to_add = {0};
to_add.status = PDU_SESSION_STATUS_FAILED;
to_add.param.pdusession_id = sessMod->pdusession_id;
to_add.cause.type = NGAP_CAUSE_RADIO_NETWORK;
to_add.cause.value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID;
seq_arr_push_back(&UE->pduSessions, &to_add, sizeof(to_add));
} else {
all_failed = false;
sess->status = PDU_SESSION_STATUS_NEW;
sess->param.pdusession_id = sessMod->pdusession_id;
sess->cause.type = NGAP_CAUSE_RADIO_NETWORK;
sess->cause.value = NGAP_CAUSE_RADIO_NETWORK_MULTIPLE_PDU_SESSION_ID_INSTANCES;
sess->status = PDU_SESSION_STATUS_NEW;
sess->param.pdusession_id = sessMod->pdusession_id;
sess->cause.type = NGAP_CAUSE_NOTHING;
if (sessMod->nas_pdu.buf != NULL) {
UE->pduSession[i].param.nas_pdu = sessMod->nas_pdu;
}
for (int i = 0; i < req->nb_pdusessions_tomodify; ++i)
cp_pdusession_resource_item_to_pdusession(&UE->pduSession[i].param, &req->pdusession[i]);
session->status = PDU_SESSION_STATUS_NEW;
session->cause.type = NGAP_CAUSE_NOTHING;
nr_rrc_update_pdusession(&session->param, sessMod);
}
}
@@ -929,11 +973,12 @@ int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t ins
msg->gNB_ue_ngap_id = req->gNB_ue_ngap_id;
msg->nb_of_pdusessions = 0;
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
if (UE->pduSession[i].status == PDU_SESSION_STATUS_FAILED) {
msg->pdusessions_failed[i].pdusession_id = UE->pduSession[i].param.pdusession_id;
msg->pdusessions_failed[i].cause.type = UE->pduSession[i].cause.type;
msg->pdusessions_failed[i].cause.value = UE->pduSession[i].cause.value;
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) {
if (session->status == PDU_SESSION_STATUS_FAILED) {
msg->pdusessions_failed[msg->nb_of_pdusessions_failed].pdusession_id = session->param.pdusession_id;
msg->pdusessions_failed[msg->nb_of_pdusessions_failed].cause.type = session->cause.type;
msg->pdusessions_failed[msg->nb_of_pdusessions_failed].cause.value = session->cause.value;
msg->nb_of_pdusessions_failed++;
}
}
itti_send_msg_to_task(TASK_NGAP, instance, msg_fail_p);
@@ -944,8 +989,13 @@ int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t ins
int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, uint8_t xid)
{
MessageDef *msg_p = NULL;
uint8_t pdu_sessions_failed = 0;
uint8_t pdu_sessions_done = 0;
uint16_t pdu_sessions_failed = 0;
uint16_t pdu_sessions_done = 0;
if (!seq_arr_size(&UE->pduSessions)) {
LOG_W(NR_RRC, "UE %d: No PDU sessions in the list, don't send NG PDU Session Modify Response\n", UE->rrc_ue_id);
return -1;
}
msg_p = itti_alloc_new_message (TASK_RRC_GNB, rrc->module_id, NGAP_PDUSESSION_MODIFY_RESP);
if (msg_p == NULL) {
@@ -957,63 +1007,54 @@ int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
if (xid != UE->pduSession[i].xid) {
LOG_W(NR_RRC, "xid does not correspond (context pdu session index %d, status %d, xid %d/%d) \n ", i, UE->pduSession[i].status, xid, UE->pduSession[i].xid);
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) {
if (xid != session->xid) {
LOG_W(NR_RRC,
"xid does not correspond (PDU Session %d, status %d, xid %d/%d) \n ",
session->param.pdusession_id,
session->status,
xid,
session->xid);
continue;
}
if (UE->pduSession[i].status == PDU_SESSION_STATUS_DONE) {
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, UE->pduSession[i].param.pdusession_id, false);
if (pduSession) {
LOG_I(NR_RRC, "update pdu session %d \n", pduSession->param.pdusession_id);
// Update UE->pduSession
pduSession->status = PDU_SESSION_STATUS_ESTABLISHED;
pduSession->cause.type = NGAP_CAUSE_NOTHING;
for (int qos_flow_index = 0; qos_flow_index < UE->pduSession[i].param.nb_qos; qos_flow_index++) {
pduSession->param.qos[qos_flow_index] = UE->pduSession[i].param.qos[qos_flow_index];
}
resp->pdusessions[pdu_sessions_done].pdusession_id = UE->pduSession[i].param.pdusession_id;
for (int qos_flow_index = 0; qos_flow_index < UE->pduSession[i].param.nb_qos; qos_flow_index++) {
resp->pdusessions[pdu_sessions_done].qos[qos_flow_index].qfi = UE->pduSession[i].param.qos[qos_flow_index].qfi;
}
resp->pdusessions[pdu_sessions_done].pdusession_id = UE->pduSession[i].param.pdusession_id;
resp->pdusessions[pdu_sessions_done].nb_of_qos_flow = UE->pduSession[i].param.nb_qos;
LOG_I(NR_RRC,
"Modify Resp (msg index %d, pdu session index %d, status %d, xid %d): nb_of_pduSessions %d, pdusession_id %d \n ",
pdu_sessions_done,
i,
UE->pduSession[i].status,
xid,
UE->nb_of_pdusessions,
resp->pdusessions[pdu_sessions_done].pdusession_id);
pdu_sessions_done++;
} else {
LOG_W(NR_RRC, "PDU SESSION modify of a not existing pdu session %d \n", UE->pduSession[i].param.pdusession_id);
resp->pdusessions_failed[pdu_sessions_failed].pdusession_id = UE->pduSession[i].param.pdusession_id;
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID};
resp->pdusessions_failed[pdu_sessions_failed].cause = cause;
pdu_sessions_failed++;
if (session->status == PDU_SESSION_STATUS_DONE) {
LOG_I(NR_RRC, "Successfully modified PDU Session %d \n", session->param.pdusession_id);
// Update status
session->status = PDU_SESSION_STATUS_ESTABLISHED;
session->cause.type = NGAP_CAUSE_NOTHING;
// Fill response
DevAssert(pdu_sessions_done < NGAP_MAX_PDU_SESSION);
pdusession_modify_t *p = &resp->pdusessions[pdu_sessions_done++];
p->pdusession_id = session->param.pdusession_id;
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &session->param.qos) {
DevAssert(p->nb_of_qos_flow < MAX_QOS_FLOWS);
qos_flow_tobe_modified_t *q = &p->qos[p->nb_of_qos_flow++];
q->qfi = qos_session->qos.qfi;
}
} else if ((UE->pduSession[i].status == PDU_SESSION_STATUS_NEW) || (UE->pduSession[i].status == PDU_SESSION_STATUS_ESTABLISHED)) {
LOG_I(NR_RRC,
"Modify Resp (msg index %d, status %d, xid %d): nb_of_pduSessions %ld, pdusession_id %d \n ",
pdu_sessions_done,
session->status,
xid,
seq_arr_size(&UE->pduSessions),
p->pdusession_id);
} else if ((session->status == PDU_SESSION_STATUS_NEW) || (session->status == PDU_SESSION_STATUS_ESTABLISHED)) {
LOG_D(NR_RRC, "PDU SESSION is NEW or already ESTABLISHED\n");
} else if (UE->pduSession[i].status == PDU_SESSION_STATUS_FAILED) {
resp->pdusessions_failed[pdu_sessions_failed].pdusession_id = UE->pduSession[i].param.pdusession_id;
resp->pdusessions_failed[pdu_sessions_failed].cause.type = UE->pduSession[i].cause.type;
resp->pdusessions_failed[pdu_sessions_failed].cause.value = UE->pduSession[i].cause.value;
} else if (session->status == PDU_SESSION_STATUS_FAILED) {
DevAssert(pdu_sessions_failed < NGAP_MAX_PDU_SESSION);
resp->pdusessions_failed[pdu_sessions_failed].pdusession_id = session->param.pdusession_id;
resp->pdusessions_failed[pdu_sessions_failed].cause.type = session->cause.type;
resp->pdusessions_failed[pdu_sessions_failed].cause.value = session->cause.value;
pdu_sessions_failed++;
}
else
LOG_W(NR_RRC,
"Modify pdu session %d, unknown state %d \n ",
UE->pduSession[i].param.pdusession_id,
UE->pduSession[i].status);
} else
LOG_W(NR_RRC, "Modify pdu session %d, unknown state %d \n ", session->param.pdusession_id, session->status);
}
resp->nb_of_pdusessions = pdu_sessions_done;
resp->nb_of_pdusessions_failed = pdu_sessions_failed;
if (pdu_sessions_done > 0 || pdu_sessions_failed > 0) {
LOG_D(NR_RRC, "NGAP_PDUSESSION_MODIFY_RESP: sending the message (total pdu session %d)\n", UE->nb_of_pdusessions);
LOG_D(NR_RRC, "NGAP_PDUSESSION_MODIFY_RESP: sending the message (total pdu session %ld)\n", seq_arr_size(&UE->pduSessions));
itti_send_msg_to_task (TASK_NGAP, rrc->module_id, msg_p);
} else {
itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
@@ -1038,8 +1079,10 @@ void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(const module_id_t gnb_mod_idP,
req->gNB_ue_ngap_id = UE->rrc_ue_id;
req->cause.type = causeP.type;
req->cause.value = causeP.value;
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
req->pdusessions[i].pdusession_id = UE->pduSession[i].param.pdusession_id;
// PDU Session Resource List (optional)
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) {
DevAssert(req->nb_of_pdusessions < NGAP_MAX_PDU_SESSION);
req->pdusessions[req->nb_of_pdusessions].pdusession_id = session->param.pdusession_id;
req->nb_of_pdusessions++;
}
itti_send_msg_to_task(TASK_NGAP, GNB_MODULE_ID_TO_INSTANCE(gnb_mod_idP), msg);
@@ -1138,14 +1181,19 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga
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)) {
// Process all PDU Session Resource Setup items from handover request
DevAssert(msg->nb_of_pdusessions <= NGAP_MAX_PDU_SESSION);
pdusession_t to_setup[NGAP_MAX_PDU_SESSION];
for (int i = 0; i < msg->nb_of_pdusessions; i++) {
ho_request_pdusession_t *ho_pdu = &msg->pduSessionResourceSetupList[i];
pdusession_t *pdu = &to_setup[i];
pdu->nssai = ho_pdu->nssai;
pdu->pdu_session_type = ho_pdu->pdu_session_type;
pdu->pdusession_id = ho_pdu->pdusession_id;
cp_pdusession_transfer_to_pdusession(pdu, &ho_pdu->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,
@@ -1167,9 +1215,15 @@ void rrc_gNB_free_Handover_Request(ngap_handover_request_t *msg)
/** @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)
int rrc_gNB_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *UE,
const int n_to_mod,
const int *drb_ids,
const e1_pdcp_status_info_t *pdcp_status)
{
AssertFatal(UE != NULL, "UE context is NULL\n");
DevAssert(n_to_mod <= MAX_DRBS_PER_UE);
DevAssert(drb_ids);
LOG_I(NR_RRC,
"Sending NGAP Uplink RAN Status Transfer (AMF_UE_NGAP_ID=%" PRIu64 ", GNB_UE_NGAP_ID=%u)\n",
@@ -1183,14 +1237,20 @@ int rrc_gNB_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE
// 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)
int drb_id = drb_ids[i];
// Find the DRB in the UE's DRB list
drb_t *drb = get_drb(&UE->drbs, drb_id);
if (!drb) {
LOG_E(NR_RRC, "Failed to send UL RAN Status Transfer: DRB %d not found\n", drb_id);
continue;
}
bool sn_length_18 = rrc->pdcp_config.drb.sn_size == 18;
bool sn_length_18 = drb->pdcp_config.drb.sn_size == 18;
DevAssert(msg.ran_status.nb_drb < MAX_DRBS_PER_UE);
ngap_drb_status_t *item = &msg.ran_status.drb_status_list[msg.ran_status.nb_drb++];
item->drb_id = drb->drb_id;
item->drb_id = 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;
@@ -1252,7 +1312,7 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_
LOG_W(NR_RRC, "[gNB %ld] In NGAP_UE_CONTEXT_RELEASE_COMMAND: unknown UE from gNB_ue_ngap_id (%u)\n",
instance,
gNB_ue_ngap_id);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance, gNB_ue_ngap_id, 0, NULL);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance, gNB_ue_ngap_id, NULL);
return -1;
}
@@ -1283,25 +1343,24 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_
/* UE will be freed after UE context release complete */
} else {
// the DU is offline already
uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION];
get_pduSession_array(UE, pdu_sessions);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions);
rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, &UE->pduSessions);
rrc_remove_ue(rrc, ue_context_p);
}
return 0;
}
void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance,
uint32_t gNB_ue_ngap_id,
int num_pdu,
uint32_t pdu_session_id[256])
void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, uint32_t gNB_ue_ngap_id, seq_arr_t *pdusessions)
{
MessageDef *msg = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_UE_CONTEXT_RELEASE_COMPLETE);
NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg).gNB_ue_ngap_id = gNB_ue_ngap_id;
NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg).num_pdu_sessions = num_pdu;
for (int i = 0; i < num_pdu; ++i)
NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg).pdu_session_id[i] = pdu_session_id[i];
ngap_ue_release_complete_t *complete = &NGAP_UE_CONTEXT_RELEASE_COMPLETE(msg);
complete->gNB_ue_ngap_id = gNB_ue_ngap_id;
// PDU Session Resource List (Optional)
if (pdusessions && seq_arr_size(pdusessions)) {
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, pdusessions) {
complete->pdu_session_id[complete->num_pdu_sessions++] = session->param.pdusession_id;
}
}
itti_send_msg_to_task(TASK_NGAP, instance, msg);
}
@@ -1358,21 +1417,22 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_U
// 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];
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t*, session, &UE->pduSessions) {
session->status = PDU_SESSION_STATUS_ESTABLISHED;
// PDU Session ID
msg->pdusessions[i].pdu_session_id = session->param.pdusession_id;
DevAssert(msg->nb_of_pdusessions < NGAP_MAX_PDU_SESSION);
pdu_session_resource_admitted_t *pdu = &msg->pdusessions[msg->nb_of_pdusessions++];
pdu->pdu_session_id = session->param.pdusession_id;
// Handover Request Acknowledge Transfer
ho_request_ack_transfer_t *transfer = &msg->pdusessions[i].ack_transfer;
ho_request_ack_transfer_t *transfer = &pdu->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;
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->param.qos) {
DevAssert(transfer->nb_of_qos_flow < MAX_QOS_FLOWS);
pdusession_associate_qosflow_t *qos_setup = &transfer->qos_setup_list[transfer->nb_of_qos_flow++];
qos_setup->qfi = qos->qos.qfi;
qos_setup->qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL;
}
}
// Target to Source Transparent Container
@@ -1429,27 +1489,24 @@ void rrc_gNB_send_NGAP_HANDOVER_CANCEL(int module_id, gNB_RRC_UE_t *UE, ngap_cau
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;
MessageDef *msg_p;
msg_p = itti_alloc_new_message (TASK_RRC_GNB, rrc->module_id, NGAP_PDUSESSION_RELEASE_RESPONSE);
ngap_pdusession_release_resp_t *resp = &NGAP_PDUSESSION_RELEASE_RESPONSE(msg_p);
memset(resp, 0, sizeof(*resp));
resp->gNB_ue_ngap_id = UE->rrc_ue_id;
for (int i = 0; i < UE->nb_of_pdusessions; i++) {
if (xid == UE->pduSession[i].xid) {
resp->pdusession_release[pdu_sessions_released].pdusession_id = UE->pduSession[i].param.pdusession_id;
pdu_sessions_released++;
//clear
memset(&UE->pduSession[i], 0, sizeof(*UE->pduSession));
UE->pduSession[i].status = PDU_SESSION_STATUS_RELEASED;
LOG_W(NR_RRC, "Released pdu session, but code to finish to free memory\n");
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) {
if (xid == session->xid) {
const pdusession_t *pdusession = &session->param;
if (session->status == PDU_SESSION_STATUS_TORELEASE) {
DevAssert(resp->nb_of_pdusessions_released < NGAP_MAX_PDU_SESSION);
resp->pdusession_release[resp->nb_of_pdusessions_released++].pdusession_id = pdusession->pdusession_id;
}
session->status = PDU_SESSION_STATUS_RELEASED;
}
}
resp->nb_of_pdusessions_released = pdu_sessions_released;
resp->nb_of_pdusessions_failed = 0;
LOG_I(NR_RRC, "NGAP PDUSESSION RELEASE RESPONSE: rrc_ue_id %u release_pdu_sessions %d\n", resp->gNB_ue_ngap_id, pdu_sessions_released);
LOG_I(NR_RRC, "NGAP PDUSESSION RELEASE RESPONSE: rrc_ue_id %u release_pdu_sessions %d\n", resp->gNB_ue_ngap_id, resp->nb_of_pdusessions_released);
itti_send_msg_to_task (TASK_NGAP, rrc->module_id, msg_p);
}
@@ -1476,14 +1533,9 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_
uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
UE->xids[xid] = RRC_PDUSESSION_RELEASE;
for (int pdusession = 0; pdusession < cmd->nb_pdusessions_torelease; pdusession++) {
rrc_pdu_session_param_t *pduSession = find_pduSession(UE, cmd->pdusession_release_params[pdusession].pdusession_id, false);
rrc_pdu_session_param_t *pduSession = find_pduSession(&UE->pduSessions, cmd->pdusession_release_params[pdusession].pdusession_id);
if (!pduSession) {
LOG_I(NR_RRC, "no pdusession_id, AMF requested to close it id=%d\n", cmd->pdusession_release_params[pdusession].pdusession_id);
int j=UE->nb_of_pdusessions++;
UE->pduSession[j].status = PDU_SESSION_STATUS_FAILED;
UE->pduSession[j].param.pdusession_id = cmd->pdusession_release_params[pdusession].pdusession_id;
ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_UNKNOWN_PDU_SESSION_ID};
UE->pduSession[j].cause = cause;
LOG_E(NR_RRC, "Failed to release non-existing PDU Session %d\n", cmd->pdusession_release_params[pdusession].pdusession_id);
continue;
}
if (pduSession->status == PDU_SESSION_STATUS_FAILED) {
@@ -1575,7 +1627,6 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
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,
@@ -1604,24 +1655,22 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc,
/* 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++;
FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t*, pduSession, &UE->pduSessions) {
if (pduSession->status != PDU_SESSION_STATUS_DONE && pduSession->status != PDU_SESSION_STATUS_ESTABLISHED)
continue;
pdusession_t *session = &pduSession->param;
DevAssert(msg.nb_of_pdusessions < NGAP_MAX_PDU_SESSION);
pdusession_resource_t *pdu = &msg.pdusessions[msg.nb_of_pdusessions++];
// PDU Session ID
pdu->pdusession_id = session->pdusession_id;
// PDU Session Resource Information List (O)
DevAssert(msg.source2target->nb_pdu_session_resource < NGAP_MAX_PDU_SESSION);
pdusession_resource_info_t *pdu_info = &msg.source2target->pdu_session_resource[msg.source2target->nb_pdu_session_resource++];
pdu_info->pdusession_id = session->pdusession_id;
FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) {
DevAssert(pdu_info->nb_of_qos_flow < MAX_QOS_FLOWS);
qosflow_info_t *qos_flow = &pdu_info->qos_flow_info[pdu_info->nb_of_qos_flow++];
qos_flow->qfi = qos->qos.qfi;
}
}
+6 -5
View File
@@ -74,10 +74,7 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_REQ(MessageDef *msg_p, instance_t in
int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_t instance);
void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance,
uint32_t gNB_ue_ngap_id,
int num_pdu,
uint32_t pdu_session_id[256]);
void rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, uint32_t gNB_ue_ngap_id, seq_arr_t *pduSessions);
void rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_UECapabilityInformation_t *const ue_cap_info);
@@ -107,7 +104,11 @@ 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_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *UE,
const int n_to_mod,
const int *drb_ids,
const e1_pdcp_status_info_t *pdcp_status);
int rrc_gNB_process_NGAP_DL_RAN_STATUS_TRANSFER(MessageDef *msg_p, instance_t instance);
+6 -3
View File
@@ -40,6 +40,7 @@
#include "linear_alloc.h"
#include "openair2/F1AP/f1ap_ids.h"
#include "tree.h"
#include "rrc_gNB_radio_bearers.h"
static void rrc_gNB_ue_context_update_time(rrc_gNB_ue_context_t *ctxt)
{
@@ -78,9 +79,6 @@ rrc_gNB_ue_context_t *rrc_gNB_allocate_new_ue_context(gNB_RRC_INST *rrc_instance
new_p->ue_context.rrc_ue_id = uid_linear_allocator_new(&rrc_instance_pP->uid_allocator) + 1;
rrc_gNB_ue_context_update_time(new_p);
for(int i = 0; i < NB_RB_MAX; i++)
new_p->ue_context.pduSession[i].xid = -1;
LOG_D(NR_RRC, "Returning new RRC UE context RRC ue id: %d\n", new_p->ue_context.rrc_ue_id);
return(new_p);
}
@@ -211,6 +209,11 @@ rrc_gNB_ue_context_t *rrc_gNB_create_ue_context(sctp_assoc_t assoc_id,
ue->max_delays_pdu_session = 20; /* see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() */
ue->ongoing_pdusession_setup_request = false;
// Initialise setup PDU Sessions list
seq_arr_init(&ue->pduSessions, sizeof(rrc_pdu_session_param_t));
// Initialise setup DRBs list
seq_arr_init(&ue->drbs, sizeof(drb_t));
RB_INSERT(rrc_nr_ue_tree_s, &rrc_instance_pP->rrc_ue_head, ue_context_p);
LOG_UE_EVENT(ue,
"Create UE context: CU UE ID %u DU UE ID %u (rnti: %04x, random ue id %lx)\n",
+48
View File
@@ -0,0 +1,48 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include <stdbool.h>
#include "NR_DRB-ToAddMod.h"
#include "NR_RadioBearerConfig.h"
#include "common/utils/oai_asn1.h"
#include "common/utils/LOG/log.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_asn1_utils.h"
NR_PDCP_Config_t *nr_rrc_build_pdcp_config_ie(const bool integrity, const bool ciphering, const nr_pdcp_configuration_t *pdcp)
{
NR_PDCP_Config_t *out = calloc_or_fail(1, sizeof(*out));
asn1cCallocOne(out->t_Reordering, encode_t_reordering(pdcp->drb.t_reordering));
if (!ciphering) {
asn1cCalloc(out->ext1, ext1);
asn1cCallocOne(ext1->cipheringDisabled, NR_PDCP_Config__ext1__cipheringDisabled_true);
}
asn1cCalloc(out->drb, drb);
asn1cCallocOne(drb->discardTimer, encode_discard_timer(pdcp->drb.discard_timer));
asn1cCallocOne(drb->pdcp_SN_SizeUL, encode_sn_size_ul(pdcp->drb.sn_size));
asn1cCallocOne(drb->pdcp_SN_SizeDL, encode_sn_size_dl(pdcp->drb.sn_size));
drb->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed;
drb->headerCompression.choice.notUsed = 0;
if (integrity) {
asn1cCallocOne(drb->integrityProtection, NR_PDCP_Config__drb__integrityProtection_enabled);
}
return out;
}
+33
View File
@@ -0,0 +1,33 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _RRC_GNB_ASN1_H_
#define _RRC_GNB_ASN1_H_
#include <stdbool.h>
#include "seq_arr.h"
#include "NR_DRB-ToAddMod.h"
#include "NR_RadioBearerConfig.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h"
NR_PDCP_Config_t *nr_rrc_build_pdcp_config_ie(const bool integrity, const bool ciphering, const nr_pdcp_configuration_t *pdcp);
#endif
+16 -14
View File
@@ -63,24 +63,20 @@ static void free_ho_ctx(nr_handover_context_t *ho_ctx)
free(ho_ctx);
}
/** @brief Fill DRB to Be Setup List in F1 UE Context Setup Request (optional list)
* @return 0 if list is empty, list size otherwise */
static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_drb_to_setup_t drbs[MAX_DRBS_PER_UE])
{
int nb_drb = 0;
for (int i = 0; i < MAX_DRBS_PER_UE; ++i) {
drb_t *rrc_drb = &ue->established_drbs[i];
if (rrc_drb->status == DRB_INACTIVE)
continue;
FOR_EACH_SEQ_ARR(drb_t *, rrc_drb, &ue->drbs) {
DevAssert(nb_drb < MAX_DRBS_PER_UE);
f1ap_drb_to_setup_t *drb = &drbs[nb_drb];
nr_pdcp_configuration_t *pdcp = &rrc_drb->pdcp_config;
nb_drb++;
/* fetch an existing PDU session for this DRB */
rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue, rrc_drb->drb_id);
AssertFatal(pdu != NULL, "no PDU session for DRB ID %d\n", rrc_drb->drb_id);
// for the moment, we only support one QoS flow. Put a reminder in case
// this changes
AssertFatal(pdu->param.nb_qos == 1, "only 1 Qos flow supported\n");
int qfi = rrc_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[0];
pdusession_level_qos_parameter_t *qos_param = get_qos_characteristics(qfi, pdu);
drb->id = rrc_drb->drb_id;
@@ -88,9 +84,14 @@ static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_
drb->nr.nssai = pdu->param.nssai;
drb->nr.flows_len = 1;
drb->nr.flows = calloc_or_fail(1, sizeof(*drb->nr.flows));
DevAssert(qfi > 0);
drb->nr.flows[0].qfi = qfi;
drb->nr.flows[0].param = get_qos_char_from_qos_flow_param(qos_param);
// Find the QoS flow associated with this DRB
// Since we don't have QFI mapping in the new structure, we'll use the first QoS flow
AssertFatal(seq_arr_size(&pdu->param.qos) == 1, "only 1 Qos flow supported\n");
nr_rrc_qos_t *qos_param = (nr_rrc_qos_t *)seq_arr_at(&pdu->param.qos, 0);
DevAssert(qos_param->qos.qfi > 0);
drb->nr.flows[0].qfi = qos_param->qos.qfi;
drb->nr.flows[0].param = get_qos_char_from_qos_flow_param(&qos_param->qos);
/* the DRB QoS parameters: we just reuse the ones from the first flow */
drb->nr.drb_qos = drb->nr.flows[0].param;
@@ -99,10 +100,11 @@ static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_
drb->up_ul_tnl_len = 1;
drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM;
DevAssert(pdcp->drb.sn_size == 18 || pdcp->drb.sn_size == 12);
drb->dl_pdcp_sn_len = malloc_or_fail(sizeof(*drb->dl_pdcp_sn_len));
*drb->dl_pdcp_sn_len = rrc_drb->pdcp_config.pdcp_SN_SizeDL == 1 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B;
*drb->dl_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B;
drb->ul_pdcp_sn_len = malloc_or_fail(sizeof(*drb->ul_pdcp_sn_len));
*drb->ul_pdcp_sn_len = rrc_drb->pdcp_config.pdcp_SN_SizeUL == 1 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B;;
*drb->ul_pdcp_sn_len = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B;
}
return nb_drb;
}
+5 -1
View File
@@ -33,7 +33,11 @@ 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 int (*ho_status_transfer_t)(gNB_RRC_INST *rrc,
gNB_RRC_UE_t *UE,
const int n_to_mod,
const int *drb_ids,
const e1_pdcp_status_info_t *pdcp_status);
typedef struct nr_ho_source_cu {
/// pointer to the (source) DU structure
+27
View File
@@ -72,6 +72,7 @@
#include "xer_encoder.h"
#include "f1ap_common.h"
#include "lib/f1ap_ue_context.h"
#include "rrc_gNB_asn1.h"
// In case of phy-test and do-ra mode, read UE capabilities directly from file
// and put it into a CG-ConfigInfo field
@@ -134,6 +135,32 @@ static instance_t get_f1_gtp_instance(void)
return inst->gtpInst;
}
/* returns a default radio bearer config suitable for NSA etc */
static NR_RadioBearerConfig_t *get_default_rbconfig(int eps_bearer_id,
int rb_id,
e_NR_CipheringAlgorithm ciphering_algorithm,
e_NR_SecurityConfig__keyToUse key_to_use,
const nr_pdcp_configuration_t *pdcp_config)
{
NR_RadioBearerConfig_t *rbconfig = calloc_or_fail(1, sizeof(*rbconfig));
rbconfig->drb_ToAddModList = calloc_or_fail(1, sizeof(*rbconfig->drb_ToAddModList));
NR_DRB_ToAddMod_t *drb_ToAddMod = calloc_or_fail(1, sizeof(*drb_ToAddMod));
drb_ToAddMod->drb_Identity = rb_id;
drb_ToAddMod->pdcp_Config = nr_rrc_build_pdcp_config_ie(false, false, pdcp_config);
asn1cCalloc(drb_ToAddMod->cnAssociation, cn_association);
cn_association->present = NR_DRB_ToAddMod__cnAssociation_PR_eps_BearerIdentity;
cn_association->choice.eps_BearerIdentity = eps_bearer_id;
asn1cSeqAdd(&rbconfig->drb_ToAddModList->list, drb_ToAddMod);
asn1cCalloc(rbconfig->securityConfig, secConf);
asn1cCalloc(secConf->securityAlgorithmConfig, secConfAlgo);
secConfAlgo->cipheringAlgorithm = ciphering_algorithm;
secConfAlgo->integrityProtAlgorithm = NULL;
asn1cCallocOne(secConf->keyToUse, key_to_use);
if (LOG_DEBUGFLAG(DEBUG_ASN1))
xer_fprint(stdout, &asn_DEF_NR_DRB_ToAddMod, drb_ToAddMod);
return rbconfig;
}
/* generate prototypes for the tree management functions (RB_INSERT used in rrc_add_nsa_user) */
RB_PROTOTYPE(rrc_nr_ue_tree_s, rrc_gNB_ue_context_s, entries,
rrc_gNB_compare_ue_rnti_id);
+175 -174
View File
@@ -31,197 +31,198 @@
#include "ngap_messages_types.h"
#include "oai_asn1.h"
#include "openair2/LAYER2/nr_pdcp/nr_pdcp_asn1_utils.h"
#include "common/utils/alg/find.h"
/** @brief Deep copy pdusession_t */
void cp_pdusession(pdusession_t *dst, const pdusession_t *src)
static bool eq_qfi(const void *vval, const void *vit)
{
// Shallow copy
*dst = *src;
// nas_pdu
dst->nas_pdu = copy_byte_array(src->nas_pdu);
const int id = *(const int *)vval;
const nr_rrc_qos_t *elem = (const nr_rrc_qos_t *)vit;
return elem->qos.qfi == id;
}
rrc_pdu_session_param_t *find_pduSession(gNB_RRC_UE_t *ue, int id, bool create)
/** @brief Retrieves mapped QoS in UE context for the input @param qfi
* @return pointer to the found QoS, NULL if not found */
nr_rrc_qos_t *find_qos(seq_arr_t *seq, int qfi)
{
int j;
for (j = 0; j < ue->nb_of_pdusessions; j++)
if (id == ue->pduSession[j].param.pdusession_id)
break;
if (j == ue->nb_of_pdusessions) {
if (create)
ue->nb_of_pdusessions++;
else
return NULL;
DevAssert(seq);
elm_arr_t elm = find_if(seq, &qfi, eq_qfi);
if (elm.found)
return (nr_rrc_qos_t *)elm.it;
return NULL;
}
/** @brief Adds a new QoS @param in to UE context the list
* @note A UE can have up to 64 QoS flows which can be multiplexed
* into one or more DRBs. The QFI is the unique ID of the mapping.
* @return pointer to the newly added QoS */
nr_rrc_qos_t *add_qos(seq_arr_t *qos, const pdusession_level_qos_parameter_t *in)
{
DevAssert(qos);
DevAssert(in);
if (seq_arr_size(qos) == MAX_QOS_FLOWS) {
LOG_W(NR_RRC, "Reached maximum number of QoS flows = %ld\n", seq_arr_size(qos));
return NULL;
}
return ue->pduSession + j;
nr_rrc_qos_t item = {.qos = *in};
seq_arr_push_back(qos, &item, sizeof(nr_rrc_qos_t));
// Double check successful add
nr_rrc_qos_t *added = find_qos(qos, in->qfi);
DevAssert(added);
LOG_I(NR_RRC, "Added QoS flow with qfi=%d, total number of QoS flows = %ld\n", in->qfi, seq_arr_size(qos));
// Only one QoS flow is supported
AssertFatal(seq_arr_size(qos) == 1, "only 1 Qos flow supported\n");
return added;
}
/** @brief Free QoS flows list items */
static void free_qos(void *ptr) { /*nothing to do*/ }
/** @brief Free QoS flows list */
static void free_rrc_qos_list(seq_arr_t *seq)
{
seq_arr_free(seq, free_qos);
}
static bool eq_pdu_session_id(const void *vval, const void *vit)
{
const int id = *(const int *)vval;
const rrc_pdu_session_param_t *elem = vit;
return elem->param.pdusession_id == id;
}
/** @brief Free pdusession_t */
void free_pdusession(void *ptr)
{
rrc_pdu_session_param_t *elem = ptr;
free_rrc_qos_list(&elem->param.qos);
free_byte_array(elem->param.nas_pdu);
}
/** @brief Retrieves PDU Session for the input ID
* @return pointer to the found PDU Session, NULL if not found */
rrc_pdu_session_param_t *find_pduSession(seq_arr_t *seq, int id)
{
DevAssert(seq);
elm_arr_t elm = find_if(seq, &id, eq_pdu_session_id);
if (elm.found)
return (rrc_pdu_session_param_t *)elm.it;
return NULL;
}
/** @brief Adds a new PDU Session to the list
* @return pointer to the new PDU Session */
rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusession_t *in)
{
DevAssert(sessions_ptr);
DevAssert(in);
if (seq_arr_size(sessions_ptr) == NGAP_MAX_PDU_SESSION) {
LOG_W(NR_RRC, "Reached maximum number of PDU Session = %ld\n", seq_arr_size(sessions_ptr));
return NULL;
}
rrc_pdu_session_param_t *exists = find_pduSession(sessions_ptr, in->pdusession_id);
if (exists) {
LOG_E(NR_RRC, "Trying to add an already existing PDU Session with ID=%d\n", in->pdusession_id);
return NULL;
}
rrc_pdu_session_param_t new = {.param = *in, .status = PDU_SESSION_STATUS_NEW, .xid = -1};
seq_arr_push_back(sessions_ptr, &new, sizeof(rrc_pdu_session_param_t));
rrc_pdu_session_param_t *added = find_pduSession(sessions_ptr, in->pdusession_id);
DevAssert(added);
LOG_I(NR_RRC, "Added PDU Session %d, (total nb of sessions = %ld)\n", in->pdusession_id, seq_arr_size(sessions_ptr));
return added;
}
/** @brief Add drb_t item in the UE context list for @param pdusession_id */
drb_t *nr_rrc_add_drb(seq_arr_t *drb_ptr, int pdusession_id, nr_pdcp_configuration_t *pdcp)
{
DevAssert(drb_ptr);
DevAssert(pdcp != NULL);
DevAssert(pdusession_id > 0 && pdusession_id <= NGAP_MAX_PDU_SESSION);
// Get next available DRB ID
int drb_id = 0;
// First, try to find the lowest available ID (prefer smaller IDs)
for (drb_id = 1; drb_id <= MAX_DRBS_PER_UE; drb_id++) {
if (get_drb(drb_ptr, drb_id) == NULL) {
break; // Found available ID
}
}
if (drb_id > MAX_DRBS_PER_UE) {
LOG_E(NR_RRC, "Cannot set up new DRB for pdusession_id=%d - reached maximum capacity\n", pdusession_id);
return NULL;
}
// Add item to the list
drb_t in = {.drb_id = drb_id, .pdusession_id = pdusession_id, .pdcp_config = *pdcp};
seq_arr_push_back(drb_ptr, &in, sizeof(drb_t));
drb_t *out = get_drb(drb_ptr, drb_id);
DevAssert(out);
LOG_I(NR_RRC, "Added DRB %d to established list (PDU Session ID=%d, total DRBs = %ld)\n", out->drb_id, pdusession_id, seq_arr_size(drb_ptr));
return out;
}
static bool eq_drb_id(const void *vval, const void *vit)
{
const int id = *(const int *)vval;
const drb_t *elem = (const drb_t *)vit;
return elem->drb_id == id;
}
/** @brief Retrieves DRB for the input ID
* @return pointer to the found DRB, NULL if not found */
drb_t *get_drb(seq_arr_t *seq, int id)
{
DevAssert(id > 0 && id <= MAX_DRBS_PER_UE);
elm_arr_t elm = find_if(seq, &id, eq_drb_id);
if (elm.found)
return (drb_t *)elm.it;
return NULL;
}
/** @brief Free pdusession_t */
void free_drb(void *ptr)
{
// do nothing
}
rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id)
{
DevAssert(drb_id > 0 && drb_id <= 32);
const drb_t *drb = &ue->established_drbs[drb_id - 1];
if (drb->status == DRB_INACTIVE) {
LOG_E(NR_RRC, "UE %d: DRB %d inactive\n", ue->rrc_ue_id, drb_id);
const drb_t *drb = get_drb(&ue->drbs, drb_id);
if (!drb) {
LOG_E(NR_RRC, "UE %d: DRB %d not found\n", ue->rrc_ue_id, drb_id);
return NULL;
}
int id = drb->cnAssociation.sdap_config.pdusession_id;
return find_pduSession(ue, id, false);
int id = drb->pdusession_id;
return find_pduSession(&ue->pduSessions, id);
}
void get_pduSession_array(gNB_RRC_UE_t *ue, uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION])
bearer_context_pdcp_config_t set_bearer_context_pdcp_config(const nr_pdcp_configuration_t pdcp,
bool um_on_default_drb,
const nr_redcap_ue_cap_t *redcap_cap)
{
for (int i = 0; i < ue->nb_of_pdusessions && i < NGAP_MAX_PDU_SESSION; ++i)
pdu_sessions[i] = ue->pduSession[i].param.pdusession_id;
}
drb_t *get_drb(gNB_RRC_UE_t *ue, uint8_t drb_id)
{
DevAssert(drb_id > 0 && drb_id <= 32);
DevAssert(ue != NULL);
return &ue->established_drbs[drb_id - 1];
}
void set_default_drb_pdcp_config(struct pdcp_config_s *pdcp_config,
int do_drb_integrity,
int do_drb_ciphering,
const nr_pdcp_configuration_t *default_pdcp_config,
const nr_redcap_ue_cap_t *redcap_cap)
{
AssertError(pdcp_config != NULL, return, "Failed to set default PDCP configuration for DRB!\n");
pdcp_config->discardTimer = encode_discard_timer(default_pdcp_config->drb.discard_timer);
bearer_context_pdcp_config_t out = {0};
if (redcap_cap && redcap_cap->support_of_redcap_r17 && !redcap_cap->pdcp_drb_long_sn_redcap_r17) {
LOG_I(NR_RRC, "UE is RedCap without long PDCP SN support: overriding PDCP SN size to 12\n");
pdcp_config->pdcp_SN_SizeDL = NR_PDCP_Config__drb__pdcp_SN_SizeDL_len12bits;
pdcp_config->pdcp_SN_SizeUL = NR_PDCP_Config__drb__pdcp_SN_SizeUL_len12bits;
out.pDCP_SN_Size_DL = NR_PDCP_Config__drb__pdcp_SN_SizeDL_len12bits;
out.pDCP_SN_Size_UL = NR_PDCP_Config__drb__pdcp_SN_SizeUL_len12bits;
} else {
pdcp_config->pdcp_SN_SizeDL = encode_sn_size_dl(default_pdcp_config->drb.sn_size);
pdcp_config->pdcp_SN_SizeUL = encode_sn_size_ul(default_pdcp_config->drb.sn_size);
out.pDCP_SN_Size_DL = encode_sn_size_dl(pdcp.drb.sn_size);
out.pDCP_SN_Size_UL = encode_sn_size_ul(pdcp.drb.sn_size);
}
pdcp_config->t_Reordering = encode_t_reordering(default_pdcp_config->drb.t_reordering);
pdcp_config->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed;
pdcp_config->headerCompression.NotUsed = 0;
pdcp_config->integrityProtection = do_drb_integrity ? NR_PDCP_Config__drb__integrityProtection_enabled : 1;
pdcp_config->ext1.cipheringDisabled = do_drb_ciphering ? 1 : NR_PDCP_Config__ext1__cipheringDisabled_true;
}
void set_bearer_context_pdcp_config(bearer_context_pdcp_config_t *pdcp_config, drb_t *rrc_drb, bool um_on_default_drb)
{
AssertError(rrc_drb != NULL && pdcp_config != NULL, return, "Failed to set default bearer context PDCP configuration!\n");
pdcp_config->pDCP_SN_Size_UL = rrc_drb->pdcp_config.pdcp_SN_SizeUL;
pdcp_config->pDCP_SN_Size_DL = rrc_drb->pdcp_config.pdcp_SN_SizeDL;
pdcp_config->discardTimer = rrc_drb->pdcp_config.discardTimer;
pdcp_config->reorderingTimer = rrc_drb->pdcp_config.t_Reordering;
pdcp_config->rLC_Mode = um_on_default_drb ? E1AP_RLC_Mode_rlc_um_bidirectional : E1AP_RLC_Mode_rlc_am;
}
drb_t *generateDRB(gNB_RRC_UE_t *ue,
uint8_t drb_id,
const rrc_pdu_session_param_t *pduSession,
bool enable_sdap,
int do_drb_integrity,
int do_drb_ciphering,
const nr_pdcp_configuration_t *pdcp_config)
{
DevAssert(ue != NULL);
LOG_I(NR_RRC, "UE %d: configure DRB ID %d for PDU session ID %d\n", ue->rrc_ue_id, drb_id, pduSession->param.pdusession_id);
drb_t *est_drb = &ue->established_drbs[drb_id - 1];
DevAssert(est_drb->status == DRB_INACTIVE);
est_drb->status = DRB_ACTIVE;
est_drb->drb_id = drb_id;
est_drb->cnAssociation.sdap_config.defaultDRB = true;
/* SDAP Configuration */
est_drb->cnAssociation.present = NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config;
est_drb->cnAssociation.sdap_config.pdusession_id = pduSession->param.pdusession_id;
if (enable_sdap) {
est_drb->cnAssociation.sdap_config.sdap_HeaderDL = NR_SDAP_Config__sdap_HeaderDL_present;
est_drb->cnAssociation.sdap_config.sdap_HeaderUL = NR_SDAP_Config__sdap_HeaderUL_present;
} else {
est_drb->cnAssociation.sdap_config.sdap_HeaderDL = NR_SDAP_Config__sdap_HeaderDL_absent;
est_drb->cnAssociation.sdap_config.sdap_HeaderUL = NR_SDAP_Config__sdap_HeaderUL_absent;
}
for (int qos_flow_index = 0; qos_flow_index < pduSession->param.nb_qos; qos_flow_index++) {
est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index] = pduSession->param.qos[qos_flow_index].qfi;
if (pduSession->param.qos[qos_flow_index].fiveQI > 5)
est_drb->status = DRB_ACTIVE_NONGBR;
else
est_drb->status = DRB_ACTIVE;
}
/* PDCP Configuration */
set_default_drb_pdcp_config(&est_drb->pdcp_config, do_drb_integrity, do_drb_ciphering, pdcp_config, ue->redcap_cap);
drb_t *rrc_drb = get_drb(ue, drb_id);
DevAssert(rrc_drb == est_drb); /* to double check that we create the same which we would retrieve */
return rrc_drb;
}
NR_DRB_ToAddMod_t *generateDRB_ASN1(const drb_t *drb_asn1)
{
NR_DRB_ToAddMod_t *DRB_config = CALLOC(1, sizeof(*DRB_config));
NR_SDAP_Config_t *SDAP_config = CALLOC(1, sizeof(NR_SDAP_Config_t));
asn1cCalloc(DRB_config->cnAssociation, association);
asn1cCalloc(SDAP_config->mappedQoS_FlowsToAdd, sdapFlows);
asn1cCalloc(DRB_config->pdcp_Config, pdcpConfig);
asn1cCalloc(pdcpConfig->drb, drb);
DRB_config->drb_Identity = drb_asn1->drb_id;
association->present = drb_asn1->cnAssociation.present;
/* SDAP Configuration */
SDAP_config->pdu_Session = drb_asn1->cnAssociation.sdap_config.pdusession_id;
SDAP_config->sdap_HeaderDL = drb_asn1->cnAssociation.sdap_config.sdap_HeaderDL;
SDAP_config->sdap_HeaderUL = drb_asn1->cnAssociation.sdap_config.sdap_HeaderUL;
SDAP_config->defaultDRB = drb_asn1->cnAssociation.sdap_config.defaultDRB;
for (int qos_flow_index = 0; qos_flow_index < QOSFLOW_MAX_VALUE; qos_flow_index++) {
if (drb_asn1->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index] != 0) {
asn1cSequenceAdd(sdapFlows->list, NR_QFI_t, qfi);
*qfi = drb_asn1->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index];
}
}
association->choice.sdap_Config = SDAP_config;
/* PDCP Configuration */
asn1cCallocOne(drb->discardTimer, drb_asn1->pdcp_config.discardTimer);
asn1cCallocOne(drb->pdcp_SN_SizeUL, drb_asn1->pdcp_config.pdcp_SN_SizeUL);
asn1cCallocOne(drb->pdcp_SN_SizeDL, drb_asn1->pdcp_config.pdcp_SN_SizeDL);
asn1cCallocOne(pdcpConfig->t_Reordering, drb_asn1->pdcp_config.t_Reordering);
drb->headerCompression.present = drb_asn1->pdcp_config.headerCompression.present;
drb->headerCompression.choice.notUsed = drb_asn1->pdcp_config.headerCompression.NotUsed;
if (!drb_asn1->pdcp_config.integrityProtection) {
asn1cCallocOne(drb->integrityProtection, drb_asn1->pdcp_config.integrityProtection);
}
if (!drb_asn1->pdcp_config.ext1.cipheringDisabled) {
asn1cCalloc(pdcpConfig->ext1, ext1);
asn1cCallocOne(ext1->cipheringDisabled, drb_asn1->pdcp_config.ext1.cipheringDisabled);
}
return DRB_config;
}
uint8_t get_next_available_drb_id(gNB_RRC_UE_t *ue)
{
for (uint8_t drb_id = 0; drb_id < MAX_DRBS_PER_UE; drb_id++)
if (ue->established_drbs[drb_id].status == DRB_INACTIVE)
return drb_id + 1;
/* From this point, we need to handle the case that all DRBs are already used by the UE. */
LOG_E(RRC, "Error - All the DRBs are used - Handle this\n");
return DRB_INACTIVE;
}
int get_number_active_drbs(gNB_RRC_UE_t *ue)
{
int n = 0;
for (int i = 0; i < MAX_DRBS_PER_UE; ++i)
if (ue->established_drbs[i].status != DRB_INACTIVE)
n++;
return n;
out.discardTimer = encode_discard_timer(pdcp.drb.discard_timer);
out.reorderingTimer = encode_t_reordering(pdcp.drb.t_reordering);
out.rLC_Mode = um_on_default_drb ? E1AP_RLC_Mode_rlc_um_bidirectional : E1AP_RLC_Mode_rlc_am;
out.pDCP_Reestablishment = false;
return out;
}
+21 -44
View File
@@ -24,61 +24,38 @@
#include <stdbool.h>
#include <stdint.h>
#include "NR_DRB-ToAddMod.h"
#include "e1ap_messages_types.h"
#include "nr_rrc_defs.h"
#define DRB_ACTIVE_NONGBR (2) /* DRB is used for Non-GBR Flows */
#define DRB_ACTIVE (1)
#define DRB_INACTIVE (0)
#define GBR_FLOW (1)
#define NONGBR_FLOW (0)
/// @brief Generates an ASN1 DRB-ToAddMod, from the established_drbs in gNB_RRC_UE_t.
/// @param drb_t drb_asn1
/// @return Returns the ASN1 DRB-ToAddMod structs.
NR_DRB_ToAddMod_t *generateDRB_ASN1(const drb_t *drb_asn1);
/// @brief retrieve the data structure representing DRB with ID drb_id of UE ue
drb_t *get_drb(gNB_RRC_UE_t *ue, uint8_t drb_id);
drb_t *get_drb(seq_arr_t *seq, int id);
/// @brief Creates and stores a DRB in the gNB_RRC_UE_t struct
/// @param ue The gNB_RRC_UE_t struct that holds information for the UEs
/// @param drb_id The Data Radio Bearer Identity to be created for the established DRB.
/// @param pduSession The PDU Session that the DRB is created for.
/// @param enable_sdap If true the SDAP header will be added to the packet, else it will not add or search for SDAP header.
/// @param do_drb_integrity
/// @param do_drb_ciphering
/// @param pdcp_config
/// @return returns a pointer to the generated DRB structure
drb_t *generateDRB(gNB_RRC_UE_t *ue,
uint8_t drb_id,
const rrc_pdu_session_param_t *pduSession,
bool enable_sdap,
int do_drb_integrity,
int do_drb_ciphering,
const nr_pdcp_configuration_t *pdcp_config);
/// @brief retrieve PDU session of UE ue with ID id
rrc_pdu_session_param_t *find_pduSession(seq_arr_t *seq, int id);
/// @brief return the next available (inactive) DRB ID of UE ue
uint8_t get_next_available_drb_id(gNB_RRC_UE_t *ue);
/// @brief returns the number of active DRBs for this UE
int get_number_active_drbs(gNB_RRC_UE_t *ue);
/// @brief retrieve PDU session of UE ue with ID id, optionally creating it if
/// create is true
rrc_pdu_session_param_t *find_pduSession(gNB_RRC_UE_t *ue, int id, bool create);
/// @brief Add a new PDU session for UE @param ue and configuration @param in
rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusession_t *in);
/// @brief get PDU session of UE ue through the DRB drb_id
rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id);
/// @brief get the PDU sessions of this UE in a single array
void get_pduSession_array(gNB_RRC_UE_t *ue, uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION]);
/// @brief set PDCP configuration in E1 Bearer Context Management message
bearer_context_pdcp_config_t set_bearer_context_pdcp_config(const nr_pdcp_configuration_t pdcp,
bool um_on_default_drb,
const nr_redcap_ue_cap_t *redcap_cap);
/// @brief set PDCP configuration in a bearer context management message
void set_bearer_context_pdcp_config(bearer_context_pdcp_config_t *pdcp_config, drb_t *rrc_drb, bool um_on_default_drb);
void free_pdusession(void *ptr);
/// @brief Deep copy an instance of struct pdusession_t
void cp_pdusession(pdusession_t *dst, const pdusession_t *src);
/// @brief Add DRB to RRC list
drb_t *nr_rrc_add_drb(seq_arr_t *drb_ptr, int pdusession_id, nr_pdcp_configuration_t *pdcp);
/// @brief Function to free DRB in RRC
void free_drb(void *ptr);
/// @brief retrieve QoS flow associated to @param qfi
nr_rrc_qos_t *find_qos(seq_arr_t *seq, int qfi);
/// @brief Add a new QoS to the list
nr_rrc_qos_t *add_qos(seq_arr_t *qos, const pdusession_level_qos_parameter_t *in);
#endif
+7
View File
@@ -0,0 +1,7 @@
add_executable(rrc_bearers_test rrc_bearers_test.c ../rrc_gNB_radio_bearers.c ${OPENAIR2_DIR}/LAYER2/nr_pdcp/asn1_utils.c)
add_dependencies(tests rrc_bearers_test)
add_test(NAME rrc_bearers_test COMMAND rrc_bearers_test)
target_include_directories(rrc_bearers_test PRIVATE ./common/utils/ds common/utils/LOG)
target_link_libraries(rrc_bearers_test PRIVATE ds alg asn1_e1ap asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs LOG T)
target_compile_definitions(rrc_bearers_test PRIVATE ENABLE_TESTS)
+173
View File
@@ -0,0 +1,173 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "common/utils/utils.h"
#include "common/utils/assertions.h"
#include "openair2/RRC/NR/rrc_gNB_radio_bearers.h"
#include "ds/seq_arr.h"
#include "common/utils/LOG/log.h"
int nr_rlc_get_available_tx_space(int module_id, int rnti, int drb_id) { return 0; }
softmodem_params_t *get_softmodem_params(void) { return NULL; }
configmodule_interface_t *uniqCfg = NULL;
void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
{
printf("detected error at %s:%d:%s: %s\n", file, line, function, s);
abort();
}
static void test_rrc_pdu_session(void)
{
const int pdu_session_id = 3;
gNB_RRC_UE_t ue = {0};
seq_arr_init(&ue.pduSessions, sizeof(rrc_pdu_session_param_t));
/* test add */
pdusession_t p1 = {0};
seq_arr_init(&p1.qos, sizeof(nr_rrc_qos_t));
p1.pdusession_id = pdu_session_id;
p1.n3_incoming.teid = 2002;
LOG_I(NR_RRC, "Adding first PDU Session with ID %d\n", p1.pdusession_id);
rrc_pdu_session_param_t *s1 = add_pduSession(&ue.pduSessions, &p1); // add 1st PDU Session
AssertFatal(s1 != NULL, "Could not add PDU Session\n");
AssertFatal(s1->param.pdusession_id == p1.pdusession_id, "PDU Session ID mismatch in added PDU Session\n");
AssertFatal(s1->param.n3_incoming.teid == p1.n3_incoming.teid, "teid mismatch in added PDU Session\n");
LOG_A(NR_RRC, "First PDU Session added successfully\n");
/* test find */
rrc_pdu_session_param_t *a1 = find_pduSession(&ue.pduSessions, p1.pdusession_id);
AssertFatal(a1 != NULL, "Could not find PDU Session\n");
AssertFatal(a1 == s1, "Found PDU Session mismatch\n");
LOG_A(NR_RRC, "PDU Session find test passed\n");
/* test add duplicate */
pdusession_t input2 = {0};
seq_arr_init(&input2.qos, sizeof(nr_rrc_qos_t));
input2.pdusession_id = pdu_session_id;
input2.n3_incoming.teid = 9999;
rrc_pdu_session_param_t *s2 = add_pduSession(&ue.pduSessions, &input2); // add 2nd PDU Session
AssertFatal(s2 == NULL, "Duplicated PDU session was added!\n");
LOG_A(NR_RRC, "Duplicated PDU session test passed\n");
seq_arr_free(&input2.qos, NULL);
/* add DRB and fetch PDU Session */
seq_arr_init(&ue.drbs, sizeof(drb_t));
nr_pdcp_configuration_t pdcp = {.drb.discard_timer = 100, .drb.sn_size = 18, .drb.t_reordering = 50};
drb_t *added = nr_rrc_add_drb(&ue.drbs, pdu_session_id, &pdcp);
AssertFatal(added != NULL, "Failed to add DRB");
rrc_pdu_session_param_t *a2 = find_pduSession_from_drbId(&ue, added->drb_id);
AssertFatal(a2 && a2->param.pdusession_id == pdu_session_id, "find_pduSession_from_drbId failed");
seq_arr_free(&ue.pduSessions, free_pdusession);
seq_arr_free(&ue.drbs, free_drb);
}
// ---------------- DRB TESTS ----------------
static void test_rrc_drb(void)
{
LOG_I(NR_RRC, "Starting DRB test\n");
seq_arr_t pduSessions = {0};
seq_arr_t drbs = {0};
seq_arr_init(&pduSessions, sizeof(rrc_pdu_session_param_t));
seq_arr_init(&drbs, sizeof(drb_t));
/* test add 1 DRB to 1st PDU session */
const int id1 = 1;
pdusession_t s1 = {0};
seq_arr_init(&s1.qos, sizeof(nr_rrc_qos_t));
s1.pdusession_id = id1;
add_pduSession(&pduSessions, &s1); // add PDU session
nr_pdcp_configuration_t pdcp = {.drb.discard_timer = 100, .drb.sn_size = 18, .drb.t_reordering = 50};
drb_t *in1 = nr_rrc_add_drb(&drbs, id1, &pdcp); // add DRB
AssertFatal(in1, "add_rrc_drb failed");
LOG_A(NR_RRC, "First DRB created with ID %d\n", in1->drb_id);
drb_t *out1 = get_drb(&drbs, 1); // fetch DRB
AssertFatal(out1 && out1 == in1, "get_drb failed");
LOG_A(NR_RRC, "DRB retrieval test passed\n");
/* test add DRB to 2nd PDU session */
const int id2 = 2;
pdusession_t s2 = {0};
seq_arr_init(&s2.qos, sizeof(nr_rrc_qos_t));
s2.pdusession_id = id2;
add_pduSession(&pduSessions, &s2); // add 2nd PDU session
drb_t *in2 = nr_rrc_add_drb(&drbs, id2, &pdcp); // add DRB to 2nd PDU session
AssertFatal(in2, "add_rrc_drb failed");
LOG_A(NR_RRC, "Second DRB created with ID %d\n", in2->drb_id);
drb_t *out2 = get_drb(&drbs, 2);
AssertFatal(out2 && out2->drb_id == 2, "get_drb failed");
LOG_A(NR_RRC, "Second DRB retrieval test passed\n");
seq_arr_free(&pduSessions, free_pdusession);
seq_arr_free(&drbs, free_drb);
}
// ---------------- DRB TESTS ----------------
static void test_rrc_qos(void)
{
seq_arr_t pduSessions = {0};
seq_arr_init(&pduSessions, sizeof(rrc_pdu_session_param_t));
const int session_id = 70;
pdusession_t in = {0};
in.pdusession_id = session_id;
seq_arr_init(&in.qos, sizeof(nr_rrc_qos_t));
add_pduSession(&pduSessions, &in);
LOG_A(NR_RRC, "Created PDU Session %d for QoS test\n", session_id);
const int qfi = 4;
const pdusession_level_qos_parameter_t param = { .fiveQI = 9, .fiveQI_type = NON_DYNAMIC, .qfi = qfi };
LOG_A(NR_RRC, "Adding QoS flow with QFI %d\n", qfi);
nr_rrc_qos_t *added = add_qos(&in.qos, &param);
AssertFatal(added, "add_qos failed");
LOG_A(NR_RRC, "QoS flow added successfully\n");
nr_rrc_qos_t *found = find_qos(&in.qos, qfi);
AssertFatal(found && found == added, "find_qos failed");
LOG_A(NR_RRC, "QoS flow find test passed\n");
seq_arr_free(&pduSessions, free_pdusession);
}
int main()
{
// Initialize logging system
logInit();
// Set logging level for NR_RRC to show INFO messages
set_log(NR_RRC, OAILOG_INFO);
// PDU Session
test_rrc_pdu_session();
// DRB
test_rrc_drb();
// QoS
test_rrc_qos();
LOG_A(NR_RRC, "All RRC Bearers tests passed successfully!\n");
return 0;
}
@@ -0,0 +1,33 @@
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _NR_SDAP_CONFIGURATION_H_
#define _NR_SDAP_CONFIGURATION_H_
#include "stdbool.h"
typedef struct {
// SDAP Headers
bool header_dl_absent;
bool header_ul_absent;
} nr_sdap_configuration_t;
#endif /* _NR_SDAP_CONFIGURATION_H_ */
+4 -5
View File
@@ -577,12 +577,11 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id)
{
nr_sdap_entity_t *entityPtr = sdap_info.sdap_entity_llist;
nr_sdap_entity_t *entityPrev = NULL;
bool ret = false;
int upperBound = 0;
if (entityPtr == NULL && (pdusession_id) * (pdusession_id - NGAP_MAX_PDU_SESSION) > 0) {
LOG_E(SDAP, "SDAP entities not established or Invalid range of pdusession_id [0, 256].\n");
return ret;
return false;
}
LOG_D(SDAP, "Deleting SDAP entity for UE %lx and PDU Session id %d\n", ue_id, entityPtr->pdusession_id);
@@ -592,7 +591,7 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id)
remove_ip_if(entityPtr);
free(entityPtr);
LOG_D(SDAP, "Successfully deleted Entity.\n");
ret = true;
return true;
} else {
while ((entityPtr->ue_id != ue_id || entityPtr->pdusession_id != pdusession_id) && entityPtr->next_entity != NULL
&& upperBound < SDAP_MAX_NUM_OF_ENTITIES) {
@@ -608,11 +607,11 @@ bool nr_sdap_delete_entity(ue_id_t ue_id, int pdusession_id)
}
free(entityPtr);
LOG_D(SDAP, "Successfully deleted Entity.\n");
ret = true;
return true;
}
}
LOG_E(SDAP, "Entity does not exist or it was not found.\n");
return ret;
return false;
}
bool nr_sdap_delete_ue_entities(ue_id_t ue_id)
+3 -3
View File
@@ -201,9 +201,9 @@ pdusession_level_qos_parameter_t fill_qos(uint8_t qfi, const NGAP_QosFlowLevelQo
}
// Allocation and Retention Priority
const NGAP_AllocationAndRetentionPriority_t *arp = &params->allocationAndRetentionPriority;
out.allocation_retention_priority.priority_level = arp->priorityLevelARP;
out.allocation_retention_priority.pre_emp_capability = arp->pre_emptionCapability;
out.allocation_retention_priority.pre_emp_vulnerability = arp->pre_emptionVulnerability;
out.arp.priority_level = arp->priorityLevelARP;
out.arp.pre_emp_capability = arp->pre_emptionCapability;
out.arp.pre_emp_vulnerability = arp->pre_emptionVulnerability;
return out;
}
+4 -2
View File
@@ -88,7 +88,9 @@ NGAP_NGAP_PDU_t *encode_ng_handover_required(const ngap_handover_required_t *msg
asn1cSequenceAdd(ie6->value.choice.PDUSessionResourceListHORqd.list, NGAP_PDUSessionResourceItemHORqd_t, hoRequiredPduSession);
// PDU Session ID (M)
hoRequiredPduSession->pDUSessionID = msg->pdusessions[i].pdusession_id;
// Handover Required Transfer (M)
// Handover Required Transfer (M) - ASN.1 encoding of empty transfer structure
// This is a mandatory field in the NGAP Handover Required message, however it contains
// only Direct Forwarding Path Availability IE, which is optional and is not sent
NGAP_HandoverRequiredTransfer_t hoRequiredTransfer = {0};
uint8_t ho_req_transfer_transparent_container_buffer[128] = {0};
if (LOG_DEBUGFLAG(DEBUG_ASN1))
@@ -123,7 +125,7 @@ NGAP_NGAP_PDU_t *encode_ng_handover_required(const ngap_handover_required_t *msg
// PDU Session Resource Information List (O)
asn1cCalloc(source2target->pDUSessionResourceInformationList, pduSessionList);
for (uint8_t i = 0; i < msg->nb_of_pdusessions; ++i) {
for (uint16_t i = 0; i < msg->nb_of_pdusessions; ++i) {
const pdusession_resource_t *pduSession = &msg->pdusessions[i];
NGAP_DEBUG("Handover Required: preparing PDU Session Resource Information List for PDU Session ID %d\n",
pduSession->pdusession_id);
+2 -3
View File
@@ -1206,11 +1206,10 @@ int ngap_gNB_pdusession_release_resp(instance_t instance, ngap_pdusession_releas
/* UE associated signalling -> use the allocated stream */
ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, ue_context_p->amf_ref->assoc_id, buffer, length, ue_context_p->tx_stream);
NGAP_INFO("pdusession_release_response sended gNB_UE_NGAP_ID %u amf_ue_ngap_id %lu nb_of_pdusessions_released %d nb_of_pdusessions_failed %d\n",
NGAP_INFO("pdusession_release_response sended gNB_UE_NGAP_ID %u amf_ue_ngap_id %lu nb_of_pdusessions_released %d \n",
pdusession_release_resp_p->gNB_ue_ngap_id,
(uint64_t)ue_context_p->amf_ue_ngap_id,
pdusession_release_resp_p->nb_of_pdusessions_released,
pdusession_release_resp_p->nb_of_pdusessions_failed);
pdusession_release_resp_p->nb_of_pdusessions_released);
return 0;
}