diff --git a/CMakeLists.txt b/CMakeLists.txt index 306a6c2839..7baa18c3c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/common/5g_platform_types.h b/common/5g_platform_types.h index b46490a2e8..4ed7f33e47 100644 --- a/common/5g_platform_types.h +++ b/common/5g_platform_types.h @@ -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 diff --git a/common/platform_constants.h b/common/platform_constants.h index 00c68bd5b8..4000ffb001 100644 --- a/common/platform_constants.h +++ b/common/platform_constants.h @@ -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__ */ diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index ef25f6997d..2c4b0f8a0a 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -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) diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c index 158c126c39..4e30773313 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c @@ -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; } } diff --git a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c index aa68ba7d9d..02760c4984 100644 --- a/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c +++ b/openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c @@ -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); diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 6f33946d93..45532572cd 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -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; diff --git a/openair2/RRC/NR/CMakeLists.txt b/openair2/RRC/NR/CMakeLists.txt index 7537bd661d..658bfeee5a 100644 --- a/openair2/RRC/NR/CMakeLists.txt +++ b/openair2/RRC/NR/CMakeLists.txt @@ -1 +1,5 @@ add_subdirectory(MESSAGES) + +if(ENABLE_TESTS) + add_subdirectory(tests) +endif() diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 51618d1f3e..0ed1750d6b 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -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, diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 607fa95994..f347f3428c 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -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, diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index f670dc4f86..e76802338c 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -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; diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 0c545575ba..fbd09d20a1 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -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); diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 797aa19d88..cc766b9d81 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -38,6 +38,7 @@ #include #include #include +#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(¶ms); @@ -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: "); diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index be7dce5ca1..34666e3538 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -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; } } diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h index b90d9a1493..91c7722c52 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -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); diff --git a/openair2/RRC/NR/rrc_gNB_UE_context.c b/openair2/RRC/NR/rrc_gNB_UE_context.c index cb3a04fbce..412c5231cc 100644 --- a/openair2/RRC/NR/rrc_gNB_UE_context.c +++ b/openair2/RRC/NR/rrc_gNB_UE_context.c @@ -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", diff --git a/openair2/RRC/NR/rrc_gNB_asn1.c b/openair2/RRC/NR/rrc_gNB_asn1.c new file mode 100644 index 0000000000..17f4bb8af5 --- /dev/null +++ b/openair2/RRC/NR/rrc_gNB_asn1.c @@ -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 +#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; +} diff --git a/openair2/RRC/NR/rrc_gNB_asn1.h b/openair2/RRC/NR/rrc_gNB_asn1.h new file mode 100644 index 0000000000..b706a68a15 --- /dev/null +++ b/openair2/RRC/NR/rrc_gNB_asn1.h @@ -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 +#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 diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 07b742e16c..552cc881d6 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -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; } diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index 40072abb2d..7010cc83ba 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -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 diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index aa97a026e0..f0f7201517 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -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); diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index d91c866cd4..5af84f0399 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -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; } diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h index 1eac60a609..f6821e314b 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -24,61 +24,38 @@ #include #include -#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 diff --git a/openair2/RRC/NR/tests/CMakeLists.txt b/openair2/RRC/NR/tests/CMakeLists.txt new file mode 100644 index 0000000000..f7a4a95b1b --- /dev/null +++ b/openair2/RRC/NR/tests/CMakeLists.txt @@ -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) diff --git a/openair2/RRC/NR/tests/rrc_bearers_test.c b/openair2/RRC/NR/tests/rrc_bearers_test.c new file mode 100644 index 0000000000..982a1b0cd9 --- /dev/null +++ b/openair2/RRC/NR/tests/rrc_bearers_test.c @@ -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 +#include +#include +#include +#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, ¶m); + 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; +} diff --git a/openair2/SDAP/nr_sdap/nr_sdap_configuration.h b/openair2/SDAP/nr_sdap/nr_sdap_configuration.h new file mode 100644 index 0000000000..f590278bf8 --- /dev/null +++ b/openair2/SDAP/nr_sdap/nr_sdap_configuration.h @@ -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_ */ diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 7f2f3dd07c..6860fba6e6 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -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) diff --git a/openair3/NGAP/ngap_common.c b/openair3/NGAP/ngap_common.c index 9e3e2e9d13..c3865cff10 100644 --- a/openair3/NGAP/ngap_common.c +++ b/openair3/NGAP/ngap_common.c @@ -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 = ¶ms->allocationAndRetentionPriority; - out.allocation_retention_priority.priority_level = arp->priorityLevelARP; - out.allocation_retention_priority.pre_emp_capability = arp->pre_emptionCapability; - out.allocation_retention_priority.pre_emp_vulnerability = arp->pre_emptionVulnerability; + out.arp.priority_level = arp->priorityLevelARP; + out.arp.pre_emp_capability = arp->pre_emptionCapability; + out.arp.pre_emp_vulnerability = arp->pre_emptionVulnerability; return out; } diff --git a/openair3/NGAP/ngap_gNB_mobility_management.c b/openair3/NGAP/ngap_gNB_mobility_management.c index 3d281be7c9..a6ff4b39a1 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.c +++ b/openair3/NGAP/ngap_gNB_mobility_management.c @@ -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); diff --git a/openair3/NGAP/ngap_gNB_nas_procedures.c b/openair3/NGAP/ngap_gNB_nas_procedures.c index b5db821e36..fe86d7c5e0 100644 --- a/openair3/NGAP/ngap_gNB_nas_procedures.c +++ b/openair3/NGAP/ngap_gNB_nas_procedures.c @@ -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; }