From 7083e08175c8ee78ffbf5d88fe4642aa7759d525 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Mon, 22 Sep 2025 22:30:09 +0200 Subject: [PATCH 01/19] Fix variable shadowing in E1 bearer context setup - Changed inner loop variable from 'i' to 'j' in rrc_gNB_process_e1_bearer_context_setup_resp - Eliminates variable shadowing between outer PDU session loop and inner DRB loop - Improves code readability and prevents potential bugs --- openair2/RRC/NR/rrc_gNB.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 797aa19d88..2ade816666 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2577,8 +2577,8 @@ 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); From 69f0bb2b966e4e5c8571dec6fd804b5ac71e2a46 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 23 Sep 2025 17:13:20 +0200 Subject: [PATCH 02/19] Fix PDU session counter overflow issues: max number of PDU Sessions is 256 - Upgrade PDU session counters from uint8_t to uint16_t in ngap_messages_types.h Co-authored-by: Assistant --- openair2/COMMON/ngap_messages_types.h | 42 +++++++++----------- openair2/RRC/NR/rrc_gNB_NGAP.c | 4 +- openair3/NGAP/ngap_gNB_mobility_management.c | 2 +- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index ef25f6997d..c4bafcee85 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -562,7 +562,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 +610,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; @@ -650,7 +650,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; @@ -680,7 +680,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 +730,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 +823,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 +871,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 +885,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 +914,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 +928,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 +938,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 +959,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]; @@ -969,14 +969,10 @@ typedef struct ngap_pdusession_release_command_s { typedef struct ngap_pdusession_release_resp_s { /* AMF UE 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 */ diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index be7dce5ca1..bdaa3e170e 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -944,8 +944,8 @@ 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; msg_p = itti_alloc_new_message (TASK_RRC_GNB, rrc->module_id, NGAP_PDUSESSION_MODIFY_RESP); if (msg_p == NULL) { diff --git a/openair3/NGAP/ngap_gNB_mobility_management.c b/openair3/NGAP/ngap_gNB_mobility_management.c index 3d281be7c9..bfea97c8ff 100644 --- a/openair3/NGAP/ngap_gNB_mobility_management.c +++ b/openair3/NGAP/ngap_gNB_mobility_management.c @@ -123,7 +123,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); From 609700207fc2959c07c55f9f1f49473d9a78fdfc Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 23 Sep 2025 23:12:43 +0200 Subject: [PATCH 03/19] Fix handover required: set pdusession_id in pdu_info structure for consistency --- openair2/RRC/NR/rrc_gNB_NGAP.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index bdaa3e170e..6b6c579071 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1609,17 +1609,19 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc, rrc_pdu_session_param_t *pduSession = find_pduSession(UE, UE->pduSession[i].param.pdusession_id, false); if (!pduSession) continue; + pdusession_t *session = &pduSession->param; // 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; - + msg.pdusessions[i].pdusession_id = session->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; + pdusession_resource_info_t *pdu_info = &msg.source2target->pdu_session_resource[msg.source2target->nb_pdu_session_resource++]; + pdu_info->pdusession_id = session->pdusession_id; + pdu_info->nb_of_qos_flow = session->nb_qos; + for (int j = 0; j < session->nb_qos; ++j) { + pdu_info->qos_flow_info[j].qfi = session->qos[j].qfi; } msg.source2target->nb_pdu_session_resource++; } From 9a4fd1bf769f7162101ecf0cffc4fa3bf0b4403b Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 24 Sep 2025 14:34:46 +0200 Subject: [PATCH 04/19] Cleanup handover required transfer IE Previously, the RRC layer was incorrectly allocating stack memory for the ho_required_transfer field and passing it to NGAP, however it was not used. The IE is mandatory but contains only 1 optional IE, therefore is encoded in the NG message with empty content. Changes: - Remove unnecessary ho_required_transfer field from pdusession_resource_t definition, the IE is encoded anyway with empty content. If Direct Forwarding Path Availability is needed at any stage in the future it can be added to pdusession_resource_t and filled in RRC. - Add clarifying comment about empty HandoverRequiredTransfer structure --- openair2/COMMON/ngap_messages_types.h | 2 -- openair2/RRC/NR/rrc_gNB_NGAP.c | 4 ---- openair3/NGAP/ngap_gNB_mobility_management.c | 4 +++- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index c4bafcee85..d5a5aca5aa 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -545,8 +545,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 */ diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 6b6c579071..dba6fde8ae 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1610,10 +1610,6 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc, if (!pduSession) continue; pdusession_t *session = &pduSession->param; - // 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 = session->pdusession_id; // PDU Session Resource Information List (O) diff --git a/openair3/NGAP/ngap_gNB_mobility_management.c b/openair3/NGAP/ngap_gNB_mobility_management.c index bfea97c8ff..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)) From 42a9f2f08fef426b8da326dca99793101927e2e8 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Mon, 22 Sep 2025 22:42:33 +0200 Subject: [PATCH 05/19] Enable multiple PDU Sessions in handover request logic - Process all PDU sessions in handover request instead of only the first one - Add DevAssert for bounds checking on nb_of_pdusessions - Fix logic error where multiple PDU sessions were ignored in handover scenarios --- openair2/RRC/NR/rrc_gNB_NGAP.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index dba6fde8ae..77682998f4 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1138,14 +1138,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, From ae1de1aeaa244da8fb064f53ea3adb3be69f49ff Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 22 Jul 2025 16:28:25 +0200 Subject: [PATCH 06/19] Refactoring: fix inconsistency in nr_sdap_delete_entity The function was printing a "not found" error message even in the case the return value was set to "true". Return directly true/false. This is relevant in PDU Session Release procedures. --- openair2/SDAP/nr_sdap/nr_sdap_entity.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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) From bd41e8f883f9b15b3d8328ac272dbb087bc9bdd4 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 14 Aug 2025 12:57:04 +0200 Subject: [PATCH 07/19] Refactoring: limit scope of config_pdcp to config module --- openair2/GNB_APP/gnb_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 55110fc449..292fbfdab0 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; From e4a910f0bbfc465f33516f49662140725ca464d7 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 28 Aug 2025 09:58:22 +0200 Subject: [PATCH 08/19] Refactoring: add utility function to fill NG PDU Session Setup item * fills from stored setup PDU Session in RRC list * reduces duplicated code --- openair2/RRC/NR/rrc_gNB_NGAP.c | 44 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 77682998f4..a087942837 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -493,6 +493,25 @@ 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); + out.nb_of_qos_flow = session->nb_qos; + for (int q = 0; q < session->nb_qos; q++) { + out.associated_qos_flows[q].qfi = session->qos[q].qfi; + out.associated_qos_flows[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; @@ -506,14 +525,7 @@ void rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_ for (int pdusession = 0; pdusession < UE->nb_of_pdusessions; pdusession++) { rrc_pdu_session_param_t *session = &UE->pduSession[pdusession]; 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}; @@ -696,22 +708,8 @@ void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE for (int pdusession = 0; pdusession < UE->nb_of_pdusessions; pdusession++) { rrc_pdu_session_param_t *session = &UE->pduSession[pdusession]; 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]; From 5bcc9b9e5e5c524a75ab3cfaa5584bc72ee643ce Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 22 Jul 2025 16:25:41 +0200 Subject: [PATCH 09/19] Adopt dynamic seq_arr in PDU Session handling (RRC PDU Session) The RRC PDU Session struct is converted from a fixed-size array to a dynamically sized seq_arr. This improves flexibility and simplifies session management by eliminating arbitrary size limits and enabling dynamic addition/removal of PDU sessions. Helper functions and loops have been adapted to use the seq_arr API. This change is a step toward more robust and maintainable handling of PDU sessions in the gNB RRC layer. The "status" state machine remains untouched in this commit and is refactored in a later commit. Also: * add macro to push to seq_arr and return the new element * move initialization of xid (RRC PDU Session struct) to find_pduSession (temporary, will be finalized in a later commit) since with the new seq_arr design the initialization is no longer possible in the UE context generation (also does not belong to it, initialize when adding a new PDU Session) --- CMakeLists.txt | 1 + .../RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c | 16 +- .../E2AP/RAN_FUNCTION/O-RAN/ran_func_kpm.c | 5 +- openair2/RRC/NR/nr_rrc_defs.h | 4 +- openair2/RRC/NR/rrc_gNB.c | 105 +++---- openair2/RRC/NR/rrc_gNB_NGAP.c | 256 +++++++++--------- openair2/RRC/NR/rrc_gNB_NGAP.h | 5 +- openair2/RRC/NR/rrc_gNB_UE_context.c | 7 +- openair2/RRC/NR/rrc_gNB_mobility.c | 1 + openair2/RRC/NR/rrc_gNB_radio_bearers.c | 80 ++++-- openair2/RRC/NR/rrc_gNB_radio_bearers.h | 15 +- 11 files changed, 261 insertions(+), 234 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 058bfd7a39..528b3e6482 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1893,6 +1893,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/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c index 158c126c39..4213ea8aa3 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; + int nb_qos_flow = session->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; + gtp->msg.ngut[i].qfi = session->param.qos[nb_qos_flow - 1].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/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index f670dc4f86..4f06fbd0d0 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -238,8 +238,8 @@ 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; + rrc_action_t xids[NR_RRC_TRANSACTION_IDENTIFIER_NUMBER]; uint8_t e_rab_release_command_flag; uint32_t ue_rrc_inactivity_timer; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 2ade816666..ca8a46a705 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -656,14 +656,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 +698,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 +719,41 @@ 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; + item->xid = params.transaction_id; + item->status = PDU_SESSION_STATUS_FAILED; continue; } // search exist DRB_config int j; - for (j = 0; i < MAX_DRBS_PER_UE; j++) { + for (j = 0; j < 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) + && ue_p->established_drbs[j].cnAssociation.sdap_config.pdusession_id == session->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; + item->cause = cause; 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 (qos_flow_index = 0; qos_flow_index < session->nb_qos; qos_flow_index++) { + pdusession_level_qos_parameter_t *qos = &session->qos[qos_flow_index]; + switch (qos->fiveQI) { case 1: //100ms case 2: //150ms case 3: //50ms @@ -765,18 +767,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->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->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 +801,18 @@ 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); + int i = 0; + FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { + if ((item->status == PDU_SESSION_STATUS_TORELEASE) && item->xid == xid) { + asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release); + *DRB_release = ++i; // 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; @@ -1783,8 +1786,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); @@ -2288,6 +2295,7 @@ 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); } void rrc_remove_ue(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *ue_context_p) @@ -2318,9 +2326,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); } } @@ -2500,7 +2506,7 @@ 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]; @@ -2569,7 +2575,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; @@ -2774,11 +2780,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 a087942837..b1f1e3fc85 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -88,6 +88,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" @@ -278,9 +279,14 @@ 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); + 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]); + LOG_I(NR_RRC, "Add PDU Session %d (total nb of sessions = %ld)\n", session->pdusession_id, seq_arr_size(&UE->pduSessions)); + // 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; @@ -312,7 +318,7 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession int drb_id = get_next_available_drb_id(UE); drb_t *rrc_drb = generateDRB(UE, drb_id, - pduSession, + session, rrc->configuration.enable_sdap, rrc->security.do_drb_integrity, rrc->security.do_drb_ciphering, @@ -522,8 +528,8 @@ 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) { resp->pdusessions[pdu_sessions_done++] = fill_ngap_pdusession_setup(&session->param); } else if (session->status != PDU_SESSION_STATUS_ESTABLISHED) { @@ -705,8 +711,7 @@ 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) { resp->pdusessions[pdu_sessions_done++] = fill_ngap_pdusession_setup(&session->param); session->status = PDU_SESSION_STATUS_ESTABLISHED; @@ -727,8 +732,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; @@ -816,7 +821,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}; @@ -883,33 +888,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; + cp_pdusession_resource_item_to_pdusession(&session->param, sessMod); } } @@ -927,11 +920,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); @@ -945,6 +939,11 @@ int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE 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) { LOG_E(NR_RRC, "itti_alloc_new_message failed, msg_p is NULL \n"); @@ -955,63 +954,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); + resp->pdusessions[pdu_sessions_done].pdusession_id = session->param.pdusession_id; + for (int qos_flow_index = 0; qos_flow_index < session->param.nb_qos; qos_flow_index++) { + resp->pdusessions[pdu_sessions_done].qos[qos_flow_index].qfi = session->param.qos[qos_flow_index].qfi; } - } else if ((UE->pduSession[i].status == PDU_SESSION_STATUS_NEW) || (UE->pduSession[i].status == PDU_SESSION_STATUS_ESTABLISHED)) { + resp->pdusessions[pdu_sessions_done].pdusession_id = session->param.pdusession_id; + resp->pdusessions[pdu_sessions_done].nb_of_qos_flow = session->param.nb_qos; + 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), + resp->pdusessions[pdu_sessions_done].pdusession_id); + pdu_sessions_done++; + } 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); @@ -1036,8 +1026,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); @@ -1255,7 +1247,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; } @@ -1286,25 +1278,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); } @@ -1361,14 +1352,14 @@ 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; @@ -1439,19 +1430,19 @@ void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE 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; + FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) { + if (xid == session->xid) { + const pdusession_t *pdusession = &session->param; + resp->pdusession_release[pdu_sessions_released].pdusession_id = pdusession->pdusession_id; + session->status = PDU_SESSION_STATUS_RELEASED; 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"); } } resp->nb_of_pdusessions_released = pdu_sessions_released; resp->nb_of_pdusessions_failed = 0; + DevAssert(resp->nb_of_pdusessions_released < NGAP_MAX_PDU_SESSION); + 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); itti_send_msg_to_task (TASK_NGAP, rrc->module_id, msg_p); } @@ -1479,14 +1470,15 @@ 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; + rrc_pdu_session_param_t item = {0}; + item.status = PDU_SESSION_STATUS_FAILED; + item.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; + item.cause = cause; + seq_arr_push_back(&UE->pduSessions, &item, sizeof(item)); continue; } if (pduSession->status == PDU_SESSION_STATUS_FAILED) { @@ -1578,7 +1570,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, @@ -1607,22 +1598,21 @@ 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; - pdusession_t *session = &pduSession->param; - // PDU Session ID - msg.pdusessions[i].pdusession_id = session->pdusession_id; - // PDU Session Resource Information List (O) - pdusession_resource_info_t *pdu_info = &msg.source2target->pdu_session_resource[msg.source2target->nb_pdu_session_resource++]; - pdu_info->pdusession_id = session->pdusession_id; - pdu_info->nb_of_qos_flow = session->nb_qos; - for (int j = 0; j < session->nb_qos; ++j) { - pdu_info->qos_flow_info[j].qfi = session->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; + pdu_info->nb_of_qos_flow = session->nb_qos; + for (int j = 0; j < pdu_info->nb_of_qos_flow; ++j) { + pdu_info->qos_flow_info[j].qfi = session->qos[j].qfi; } } diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h index b90d9a1493..c07cb04be0 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); diff --git a/openair2/RRC/NR/rrc_gNB_UE_context.c b/openair2/RRC/NR/rrc_gNB_UE_context.c index cb3a04fbce..d8d83505b9 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,9 @@ 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)); + 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_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 07b742e16c..40ff7d8ab2 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -76,6 +76,7 @@ static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_ /* 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"); diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index d91c866cd4..0192203af1 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -31,6 +31,14 @@ #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" + +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 Deep copy pdusession_t */ void cp_pdusession(pdusession_t *dst, const pdusession_t *src) @@ -41,19 +49,49 @@ void cp_pdusession(pdusession_t *dst, const pdusession_t *src) dst->nas_pdu = copy_byte_array(src->nas_pdu); } -rrc_pdu_session_param_t *find_pduSession(gNB_RRC_UE_t *ue, int id, bool create) +/** @brief Free pdusession_t */ +void free_pdusession(void *ptr) { - 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; + rrc_pdu_session_param_t *elem = ptr; + 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; } - return ue->pduSession + j; + + 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; } rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id) @@ -65,13 +103,7 @@ rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id return NULL; } int id = drb->cnAssociation.sdap_config.pdusession_id; - return find_pduSession(ue, id, false); -} - -void get_pduSession_array(gNB_RRC_UE_t *ue, uint32_t pdu_sessions[NGAP_MAX_PDU_SESSION]) -{ - for (int i = 0; i < ue->nb_of_pdusessions && i < NGAP_MAX_PDU_SESSION; ++i) - pdu_sessions[i] = ue->pduSession[i].param.pdusession_id; + return find_pduSession(&ue->pduSessions, id); } drb_t *get_drb(gNB_RRC_UE_t *ue, uint8_t drb_id) @@ -117,7 +149,7 @@ void set_bearer_context_pdcp_config(bearer_context_pdcp_config_t *pdcp_config, d drb_t *generateDRB(gNB_RRC_UE_t *ue, uint8_t drb_id, - const rrc_pdu_session_param_t *pduSession, + const pdusession_t *pduSession, bool enable_sdap, int do_drb_integrity, int do_drb_ciphering, @@ -125,7 +157,7 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue, { 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); + LOG_I(NR_RRC, "UE %d: configure DRB ID %d for PDU session ID %d\n", ue->rrc_ue_id, drb_id, pduSession->pdusession_id); drb_t *est_drb = &ue->established_drbs[drb_id - 1]; DevAssert(est_drb->status == DRB_INACTIVE); @@ -136,7 +168,7 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue, /* SDAP Configuration */ est_drb->cnAssociation.present = NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config; - est_drb->cnAssociation.sdap_config.pdusession_id = pduSession->param.pdusession_id; + est_drb->cnAssociation.sdap_config.pdusession_id = pduSession->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; @@ -144,9 +176,9 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue, 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) + for (int qos_flow_index = 0; qos_flow_index < pduSession->nb_qos; qos_flow_index++) { + est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index] = pduSession->qos[qos_flow_index].qfi; + if (pduSession->qos[qos_flow_index].fiveQI > 5) est_drb->status = DRB_ACTIVE_NONGBR; else est_drb->status = DRB_ACTIVE; diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h index 1eac60a609..fd37f82de0 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -53,7 +53,7 @@ drb_t *get_drb(gNB_RRC_UE_t *ue, uint8_t drb_id); /// @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, + const pdusession_t *pduSession, bool enable_sdap, int do_drb_integrity, int do_drb_ciphering, @@ -65,20 +65,21 @@ 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 retrieve PDU session of UE ue with ID id +rrc_pdu_session_param_t *find_pduSession(seq_arr_t *seq, int id); + +/// @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 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); /// @brief Deep copy an instance of struct pdusession_t void cp_pdusession(pdusession_t *dst, const pdusession_t *src); +void free_pdusession(void *ptr); + #endif From f15af7729718ae5c71117e180bd338798af1e232 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 29 Aug 2025 18:54:56 +0200 Subject: [PATCH 10/19] Cleanup: remove unused cp_pdusession The function is no longer relevant in the new design. --- openair2/RRC/NR/rrc_gNB_radio_bearers.c | 9 --------- openair2/RRC/NR/rrc_gNB_radio_bearers.h | 3 --- 2 files changed, 12 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index 0192203af1..b55ce4e6ae 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -40,15 +40,6 @@ static bool eq_pdu_session_id(const void *vval, const void *vit) return elem->param.pdusession_id == id; } -/** @brief Deep copy pdusession_t */ -void cp_pdusession(pdusession_t *dst, const pdusession_t *src) -{ - // Shallow copy - *dst = *src; - // nas_pdu - dst->nas_pdu = copy_byte_array(src->nas_pdu); -} - /** @brief Free pdusession_t */ void free_pdusession(void *ptr) { diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h index fd37f82de0..0a76c9d796 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -77,9 +77,6 @@ rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id /// @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); -/// @brief Deep copy an instance of struct pdusession_t -void cp_pdusession(pdusession_t *dst, const pdusession_t *src); - void free_pdusession(void *ptr); #endif From 7fa0ebb6b9a933f9114fb21840e23ecc03375afe Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 4 Jul 2025 17:23:14 +0200 Subject: [PATCH 11/19] Adopt dynamic seq_arr in DRB handling (RRC PDU Session) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous DRB management relied on a fixed-size array (established_drbs[MAX_DRBS_PER_UE]) and manual status tracking using DRB_ACTIVE/INACTIVE flags. This approach was inefficient, and made assumptions about DRB ID allocation that do not generalize well to dynamic procedures like bearer modification or removal. * Replace fixed-size array established_drbs[] with dynamic seq_arr_t *drbs in gNB_RRC_UE_t * Refactor get_drb() and added nr_rrc_add_drb() with clean abstractions using find and seq_arr lib * Updated all DRB-related logic (e.g., generateDRB, F1AP/E1AP tunnel config, reestablishment, mobility) to use seq_arr. * Removed status field and DRB_ACTIVE/INACTIVE logic: list presence implies active * DRB ID allocation now tracked via seq_arr size : simplifies DRB management and avoids DRB ID–indexed assumptions: code assumed DRB IDs mapped directly to array indices: established_drbs[drb_id - 1]. This could break when: DRB IDs are not sequential, DRBs are removed and re-added, or there are gaps in IDs --- openair2/RRC/NR/nr_rrc_defs.h | 3 +- openair2/RRC/NR/rrc_gNB.c | 59 ++++++------- openair2/RRC/NR/rrc_gNB_NGAP.c | 24 ++++-- openair2/RRC/NR/rrc_gNB_NGAP.h | 6 +- openair2/RRC/NR/rrc_gNB_UE_context.c | 2 + openair2/RRC/NR/rrc_gNB_mobility.c | 8 +- openair2/RRC/NR/rrc_gNB_mobility.h | 6 +- openair2/RRC/NR/rrc_gNB_radio_bearers.c | 108 ++++++++++++++---------- openair2/RRC/NR/rrc_gNB_radio_bearers.h | 21 ++--- 9 files changed, 133 insertions(+), 104 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 4f06fbd0d0..43909e0cd0 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -182,7 +182,6 @@ 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]; @@ -239,6 +238,8 @@ typedef struct gNB_RRC_UE_s { //SA block 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; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index ca8a46a705..1919ccaa5c 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -334,14 +334,12 @@ NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish) NR_DRB_ToAddMod_t *DRB_config = NULL; NR_DRB_ToAddModList_t *DRB_configList = CALLOC(sizeof(*DRB_configList), 1); - 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) { + DRB_config = generateDRB_ASN1(drb); + if (reestablish) { + asn1cCallocOne(DRB_config->reestablishPDCP, NR_DRB_ToAddMod__reestablishPDCP_true); } + asn1cSeqAdd(&DRB_configList->list, DRB_config); } if (DRB_configList->list.count == 0) { free(DRB_configList); @@ -734,22 +732,6 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t continue; } - // search exist DRB_config - int j; - for (j = 0; j < 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->pdusession_id) - break; - } - - if (j == MAX_DRBS_PER_UE) { - ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CauseRadioNetwork_unspecified}; - item->xid = params.transaction_id; - item->status = PDU_SESSION_STATUS_FAILED; - item->cause = cause; - continue; - } - // Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping for (qos_flow_index = 0; qos_flow_index < session->nb_qos; qos_flow_index++) { pdusession_level_qos_parameter_t *qos = &session->qos[qos_flow_index]; @@ -873,8 +855,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); @@ -2078,7 +2060,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); } } @@ -2094,7 +2077,7 @@ 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); + set_bearer_context_pdcp_config(drb_to_mod->pdcp_config, get_drb(&UE->drbs, drb_id), um_on_default_drb); drb_to_mod->pdcp_status = calloc_or_fail(1, sizeof(*drb_to_mod->pdcp_status)); drb_to_mod->pdcp_status->dl_count.hfn = drb_status->dl_count.hfn; drb_to_mod->pdcp_status->dl_count.sn = drb_status->dl_count.pdcp_sn; @@ -2130,8 +2113,11 @@ void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb 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; @@ -2204,7 +2190,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 @@ -2296,6 +2282,7 @@ static void rrc_delete_ue_data(gNB_RRC_UE_t *UE) 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) @@ -2510,9 +2497,10 @@ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc, 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); 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; @@ -2587,7 +2575,11 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp 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); } @@ -2629,6 +2621,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]; @@ -2636,6 +2629,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; } } @@ -2644,7 +2639,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); } } diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index b1f1e3fc85..ddf97b2499 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -315,9 +315,7 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession /* 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, session, rrc->configuration.enable_sdap, rrc->security.do_drb_integrity, @@ -1162,9 +1160,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", @@ -1178,14 +1182,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.pdcp_SN_SizeDL == NR_PDCP_Config__drb__pdcp_SN_SizeUL_len18bits; + 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; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h index c07cb04be0..91c7722c52 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -104,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 d8d83505b9..412c5231cc 100644 --- a/openair2/RRC/NR/rrc_gNB_UE_context.c +++ b/openair2/RRC/NR/rrc_gNB_UE_context.c @@ -211,6 +211,8 @@ rrc_gNB_ue_context_t *rrc_gNB_create_ue_context(sctp_assoc_t assoc_id, // 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, diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 40ff7d8ab2..3c17f45189 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -63,14 +63,14 @@ 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]; nb_drb++; /* fetch an existing PDU session for this 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_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index b55ce4e6ae..1f2f721eaa 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -85,26 +85,70 @@ rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusessio 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) +{ + DevAssert(drb_ptr); + + // 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, .cnAssociation.sdap_config.pdusession_id = pdusession_id}; + 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->pduSessions, 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, @@ -139,7 +183,6 @@ void set_bearer_context_pdcp_config(bearer_context_pdcp_config_t *pdcp_config, d } drb_t *generateDRB(gNB_RRC_UE_t *ue, - uint8_t drb_id, const pdusession_t *pduSession, bool enable_sdap, int do_drb_integrity, @@ -148,13 +191,13 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue, { 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->pdusession_id); + drb_t *est_drb = nr_rrc_add_drb(&ue->drbs, pduSession->pdusession_id); + if (!est_drb) { + LOG_E(NR_RRC, "UE %d: failed to add DRB for PDU session ID %d\n", ue->rrc_ue_id, pduSession->pdusession_id); + return NULL; + } + LOG_I(NR_RRC, "UE %d: configure DRB ID %d for PDU session ID %d\n", ue->rrc_ue_id, est_drb->drb_id, pduSession->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 */ @@ -169,16 +212,12 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue, } for (int qos_flow_index = 0; qos_flow_index < pduSession->nb_qos; qos_flow_index++) { est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index] = pduSession->qos[qos_flow_index].qfi; - if (pduSession->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 */ + drb_t *rrc_drb = get_drb(&ue->drbs, est_drb->drb_id); + DevAssert(rrc_drb); return rrc_drb; } @@ -229,22 +268,3 @@ NR_DRB_ToAddMod_t *generateDRB_ASN1(const drb_t *drb_asn1) 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; -} diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h index 0a76c9d796..982e7ea42d 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -28,19 +28,13 @@ #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 @@ -52,19 +46,12 @@ drb_t *get_drb(gNB_RRC_UE_t *ue, uint8_t drb_id); /// @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 pdusession_t *pduSession, bool enable_sdap, int do_drb_integrity, int do_drb_ciphering, const nr_pdcp_configuration_t *pdcp_config); -/// @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 rrc_pdu_session_param_t *find_pduSession(seq_arr_t *seq, int id); @@ -79,4 +66,10 @@ void set_bearer_context_pdcp_config(bearer_context_pdcp_config_t *pdcp_config, d void free_pdusession(void *ptr); +/// @brief Add DRB to RRC list +drb_t *nr_rrc_add_drb(seq_arr_t *drb_ptr, int pdusession_id); + +/// @brief Function to free DRB in RRC +void free_drb(void *ptr); + #endif From 45d989ec7b555a3ad994b15f4b040bcf5d07d23b Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 27 Aug 2025 17:29:46 +0200 Subject: [PATCH 12/19] Adopt dynamic seq_arr in QoS Flow list (RRC PDU Session) --- .../RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c | 6 +- openair2/RRC/NR/nr_rrc_defs.h | 8 +- openair2/RRC/NR/nr_rrc_proto.h | 1 - openair2/RRC/NR/rrc_gNB.c | 35 +++--- openair2/RRC/NR/rrc_gNB_NGAP.c | 100 +++++++++++++----- openair2/RRC/NR/rrc_gNB_mobility.c | 7 +- openair2/RRC/NR/rrc_gNB_radio_bearers.c | 63 ++++++++++- openair2/RRC/NR/rrc_gNB_radio_bearers.h | 6 ++ 8 files changed, 162 insertions(+), 64 deletions(-) diff --git a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c index 4213ea8aa3..4e30773313 100644 --- a/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c +++ b/openair2/E2AP/RAN_FUNCTION/CUSTOMIZED/ran_func_gtp.c @@ -67,9 +67,9 @@ bool read_gtp_sm(void * data) 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 = session->param.nb_qos; - if (nb_qos_flow > 0) { - gtp->msg.ngut[i].qfi = session->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/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 43909e0cd0..3491154923 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -103,9 +103,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 @@ -176,6 +175,11 @@ 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; diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 0c545575ba..d7c1c0efce 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -122,7 +122,6 @@ 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); diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 1919ccaa5c..5e95ad690a 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -733,9 +733,8 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t } // Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping - for (qos_flow_index = 0; qos_flow_index < session->nb_qos; qos_flow_index++) { - pdusession_level_qos_parameter_t *qos = &session->qos[qos_flow_index]; - switch (qos->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 @@ -749,14 +748,14 @@ void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t break; default: - LOG_E(NR_RRC, "not supported 5qi %lu\n", qos->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}; item->status = PDU_SESSION_STATUS_FAILED; item->xid = params.transaction_id; item->cause = cause; continue; } - LOG_I(NR_RRC, "PDU Session ID %d, QOS flow %d, 5QI %ld \n", session->pdusession_id, qos_flow_index, qos->fiveQI); + LOG_I(NR_RRC, "PDU Session ID %d, QOS flow %d, 5QI %ld \n", session->pdusession_id, qos_flow_index, qos->qos.fiveQI); } item->status = PDU_SESSION_STATUS_DONE; item->xid = params.transaction_id; @@ -2101,14 +2100,15 @@ 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) { @@ -2134,6 +2134,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); @@ -2448,17 +2450,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) { @@ -2516,11 +2507,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; diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index ddf97b2499..e584912a38 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -172,20 +172,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 < QOSFLOW_MAX_VALUE); + // 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); } @@ -343,11 +353,10 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession *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; - + FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) { + pdusession_level_qos_parameter_t *qos_session = &qos->qos; + DevAssert(drb->numQosFlow2Setup < QOSFLOW_MAX_VALUE); + qos_flow_to_setup_t *qos_flow = &drb->qosFlows[drb->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; @@ -505,10 +514,11 @@ static pdusession_setup_t fill_ngap_pdusession_setup(pdusession_t *session) out.pdusession_id = session->pdusession_id; out.pdu_session_type = session->pdu_session_type; out.n3_outgoing = cp_gtp_tunnel(session->n3_outgoing); - out.nb_of_qos_flow = session->nb_qos; - for (int q = 0; q < session->nb_qos; q++) { - out.associated_qos_flows[q].qfi = session->qos[q].qfi; - out.associated_qos_flows[q].qos_flow_mapping_ind = QOSFLOW_MAPPING_INDICATION_DL; + FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &session->qos) { + DevAssert(out.nb_of_qos_flow < QOSFLOW_MAX_VALUE); + 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)); @@ -868,6 +878,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 < QOSFLOW_MAX_VALUE); + 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) //------------------------------------------------------------------------------ @@ -900,7 +940,7 @@ int rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(MessageDef *msg_p, instance_t ins all_failed = false; session->status = PDU_SESSION_STATUS_NEW; session->cause.type = NGAP_CAUSE_NOTHING; - cp_pdusession_resource_item_to_pdusession(&session->param, sessMod); + nr_rrc_update_pdusession(&session->param, sessMod); } } @@ -969,20 +1009,20 @@ int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE session->cause.type = NGAP_CAUSE_NOTHING; // Fill response DevAssert(pdu_sessions_done < NGAP_MAX_PDU_SESSION); - resp->pdusessions[pdu_sessions_done].pdusession_id = session->param.pdusession_id; - for (int qos_flow_index = 0; qos_flow_index < session->param.nb_qos; qos_flow_index++) { - resp->pdusessions[pdu_sessions_done].qos[qos_flow_index].qfi = session->param.qos[qos_flow_index].qfi; + 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 < QOSFLOW_MAX_VALUE); + qos_flow_tobe_modified_t *q = &p->qos[p->nb_of_qos_flow++]; + q->qfi = qos_session->qos.qfi; } - resp->pdusessions[pdu_sessions_done].pdusession_id = session->param.pdusession_id; - resp->pdusessions[pdu_sessions_done].nb_of_qos_flow = session->param.nb_qos; 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), - resp->pdusessions[pdu_sessions_done].pdusession_id); - pdu_sessions_done++; + 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 (session->status == PDU_SESSION_STATUS_FAILED) { @@ -1373,10 +1413,11 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_U 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 < QOSFLOW_MAX_VALUE); + 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 @@ -1620,9 +1661,10 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc, 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; - pdu_info->nb_of_qos_flow = session->nb_qos; - for (int j = 0; j < pdu_info->nb_of_qos_flow; ++j) { - pdu_info->qos_flow_info[j].qfi = session->qos[j].qfi; + FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) { + DevAssert(pdu_info->nb_of_qos_flow < QOSFLOW_MAX_VALUE); + 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_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 3c17f45189..0d97e5d30a 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -77,11 +77,8 @@ static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_ 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); + nr_rrc_qos_t *qos_param = find_qos(&pdu->param.qos, qfi); drb->id = rrc_drb->drb_id; @@ -91,7 +88,7 @@ static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_ 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); + 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; diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index 1f2f721eaa..ab68a4c367 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -33,6 +33,61 @@ #include "openair2/LAYER2/nr_pdcp/nr_pdcp_asn1_utils.h" #include "common/utils/alg/find.h" +static bool eq_qfi(const void *vval, const void *vit) +{ + const int id = *(const int *)vval; + const nr_rrc_qos_t *elem = (const nr_rrc_qos_t *)vit; + return elem->qos.qfi == id; +} + +/** @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) +{ + 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) == QOSFLOW_MAX_VALUE) { + LOG_W(NR_RRC, "Reached maximum number of QoS flows = %ld\n", seq_arr_size(qos)); + return NULL; + } + + 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; @@ -44,6 +99,7 @@ static bool eq_pdu_session_id(const void *vval, const void *vit) 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); } @@ -210,8 +266,11 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue, 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->nb_qos; qos_flow_index++) { - est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index] = pduSession->qos[qos_flow_index].qfi; + int qos_flow_index = 0; + FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &pduSession->qos) { + qos_session->drb_id = est_drb->drb_id; // Map DRB to QoS + est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index++] = qos_session->qos.qfi; + DevAssert(qos_flow_index <= QOSFLOW_MAX_VALUE); } /* PDCP Configuration */ set_default_drb_pdcp_config(&est_drb->pdcp_config, do_drb_integrity, do_drb_ciphering, pdcp_config, ue->redcap_cap); diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h index 982e7ea42d..e5ec90c5c6 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -72,4 +72,10 @@ drb_t *nr_rrc_add_drb(seq_arr_t *drb_ptr, int pdusession_id); /// @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 From 1711b1b4b8f2fbc19c6c5811032c60115df921a3 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 24 Sep 2025 14:55:52 +0200 Subject: [PATCH 13/19] Refactor ASN.1 PDCP configuration building - Limit scope of get_default_rbconfig function: from asn1_msg.c/.h to rrc_gNB_nsa.c where it's used - Create new rrc_gNB_asn1.c/h files for ASN.1 utility functions: they can be used in other RRC files - Add nr_rrc_build_pdcp_config_ie function for building PDCP configuration IEs - Update get_default_rbconfig to use the new PDCP config builder - Add rrc_gNB_asn1.c to CMakeLists.txt build configuration --- CMakeLists.txt | 1 + openair2/RRC/NR/MESSAGES/asn1_msg.c | 45 --------------------------- openair2/RRC/NR/MESSAGES/asn1_msg.h | 6 ---- openair2/RRC/NR/rrc_gNB_asn1.c | 48 +++++++++++++++++++++++++++++ openair2/RRC/NR/rrc_gNB_asn1.h | 33 ++++++++++++++++++++ openair2/RRC/NR/rrc_gNB_nsa.c | 27 ++++++++++++++++ 6 files changed, 109 insertions(+), 51 deletions(-) create mode 100644 openair2/RRC/NR/rrc_gNB_asn1.c create mode 100644 openair2/RRC/NR/rrc_gNB_asn1.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 528b3e6482..314ee0ee0b 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 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/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_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); From 7254e59df911af627ee7410b7bf7eb5ae432af55 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 24 Sep 2025 15:34:01 +0200 Subject: [PATCH 14/19] Introduced MAX_QOS_FLOWS (64) and replaced QOSFLOW_MAX_VALUE The maximum number of QoS flows is 64. - Replace QOSFLOW_MAX_VALUE with MAX_QOS_FLOWS constant - Move MAX_QOS_FLOWS definition to common/platform_constants.h - Remove duplicate QOSFLOW_MAX_VALUE definition from ngap_messages_types.h This improves maintainability by centralizing the QoS flow limit constant and eliminates duplicate definitions across different header files. --- common/platform_constants.h | 3 +++ openair2/COMMON/ngap_messages_types.h | 14 ++++++-------- openair2/RRC/NR/nr_rrc_defs.h | 2 +- openair2/RRC/NR/rrc_gNB_NGAP.c | 14 +++++++------- openair2/RRC/NR/rrc_gNB_radio_bearers.c | 6 +++--- 5 files changed, 20 insertions(+), 19 deletions(-) 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 d5a5aca5aa..a07f798005 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 @@ -241,7 +239,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 +270,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 +284,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 +504,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 */ @@ -627,7 +625,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; @@ -657,7 +655,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; diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 3491154923..65bb3095e7 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -132,7 +132,7 @@ typedef struct drb_s { int pdusession_id; int sdap_HeaderDL; int sdap_HeaderUL; - int mappedQoS_FlowsToAdd[QOSFLOW_MAX_VALUE]; + int mappedQoS_FlowsToAdd[MAX_QOS_FLOWS]; } sdap_config; } cnAssociation; struct pdcp_config_s { diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index e584912a38..4effc47935 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -176,7 +176,7 @@ static void cp_pdusession_transfer_to_pdusession(pdusession_t *dst, const pduses dst->n3_incoming = src->n3_incoming; /* QoS handling */ DevAssert(!dst->qos.data); - DevAssert(src->nb_qos < QOSFLOW_MAX_VALUE); + 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 @@ -355,7 +355,7 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) { pdusession_level_qos_parameter_t *qos_session = &qos->qos; - DevAssert(drb->numQosFlow2Setup < QOSFLOW_MAX_VALUE); + DevAssert(drb->numQosFlow2Setup < MAX_QOS_FLOWS); qos_flow_to_setup_t *qos_flow = &drb->qosFlows[drb->numQosFlow2Setup++]; qos_characteristics_t *qos_char = &qos_flow->qos_params.qos_characteristics; qos_flow->qfi = qos_session->qfi; @@ -515,7 +515,7 @@ static pdusession_setup_t fill_ngap_pdusession_setup(pdusession_t *session) 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 < QOSFLOW_MAX_VALUE); + 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; @@ -882,7 +882,7 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins 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 < QOSFLOW_MAX_VALUE); + 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) { @@ -1012,7 +1012,7 @@ int rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE 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 < QOSFLOW_MAX_VALUE); + 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; } @@ -1414,7 +1414,7 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUEST_ACKNOWLEDGE(gNB_RRC_INST *rrc, gNB_RRC_U 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; FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->param.qos) { - DevAssert(transfer->nb_of_qos_flow < QOSFLOW_MAX_VALUE); + 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; @@ -1662,7 +1662,7 @@ void rrc_gNB_send_NGAP_HANDOVER_REQUIRED(gNB_RRC_INST *rrc, 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 < QOSFLOW_MAX_VALUE); + 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_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index ab68a4c367..ccf3fbe99f 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -60,7 +60,7 @@ 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) == QOSFLOW_MAX_VALUE) { + 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; } @@ -270,7 +270,7 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue, FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &pduSession->qos) { qos_session->drb_id = est_drb->drb_id; // Map DRB to QoS est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index++] = qos_session->qos.qfi; - DevAssert(qos_flow_index <= QOSFLOW_MAX_VALUE); + DevAssert(qos_flow_index <= MAX_QOS_FLOWS); } /* PDCP Configuration */ set_default_drb_pdcp_config(&est_drb->pdcp_config, do_drb_integrity, do_drb_ciphering, pdcp_config, ue->redcap_cap); @@ -299,7 +299,7 @@ NR_DRB_ToAddMod_t *generateDRB_ASN1(const drb_t *drb_asn1) 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++) { + for (int qos_flow_index = 0; qos_flow_index < MAX_QOS_FLOWS; 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]; From 50923823c0bd295e9ffa6b0a0ffa80a060038d88 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 24 Sep 2025 15:48:40 +0200 Subject: [PATCH 15/19] Refactor QoS parameter types and consolidate definitions - Move QoS-related enums and structures from ngap_messages_types.h to 5g_platform_types.h - Replace ngap_* QoS types with generic qos_* types for better reusability - Update field names from allocation_retention_priority to arp for consistency - Update all references across RRC and NGAP modules to use new type names - Remove duplicate QoS type definitions from NGAP-specific header This improves code organization by centralizing QoS parameter definitions in a common platform types header and eliminates NGAP-specific naming in favor of more generic, reusable type definitions. --- common/5g_platform_types.h | 49 +++++++++++++++++++++++++++ openair2/COMMON/ngap_messages_types.h | 45 ------------------------ openair2/RRC/NR/rrc_gNB.c | 11 +++--- openair2/RRC/NR/rrc_gNB_NGAP.c | 10 +++--- openair3/NGAP/ngap_common.c | 6 ++-- 5 files changed, 62 insertions(+), 59 deletions(-) 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/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index a07f798005..1c5fa90868 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -139,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; @@ -203,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; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 5e95ad690a..6c1c80f30e 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" @@ -2464,13 +2465,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; } diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 4effc47935..7afc7afa23 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -368,11 +368,11 @@ bool trigger_bearer_setup(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, pdusession 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; + 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; } } } 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; } From 6ed08f15271e178a77850e973ea18c3f3e395f92 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 24 Sep 2025 16:04:23 +0200 Subject: [PATCH 16/19] Refactor DRB and QoS handling: centralize configuration logic Motivation: The previous architecture had several issues: - Duplicate DRB generation logic in generateDRB() and generateDRB_ASN1() - Embedded SDAP/PDCP configs in drb_t created tight coupling - No clear separation between RRC internal state and ASN.1 encoding - Maintenance burden due to duplicated configuration logic New Architecture: - drb_t now only stores DRB-specific data (ID, PDU session ID, PDCP config) - SDAP configuration moved to PDU session level (following PDCP design pattern) - ASN.1 encoding helpers (nr_rrc_build_*_config_ie()) handle conversion - Single source of truth for DRB creation in RRC via nr_rrc_add_drb() Key changes: - Store nr_sdap_configuration_t per PDU session - Simplified drb_t structure: removed embedded SDAP/PDCP configs, added pdusession_id - New helper: nr_rrc_build_sdap_config_ie() - Updated createDRBlist() to use new helper functions and proper DRB ID lookup - Refactored trigger_bearer_setup() to use nr_rrc_add_drb() and fill_e1_drb_to_setup() - Removed generateDRB() and generateDRB_ASN1() functions - Updated set_bearer_context_pdcp_config() to return config by value - Added SDAP configuration header file for type definitions This eliminates code duplication, improves maintainability, and creates a cleaner separation between RRC state management and ASN.1 encoding. --- openair2/RRC/NR/nr_rrc_defs.h | 32 +--- openair2/RRC/NR/nr_rrc_proto.h | 2 +- openair2/RRC/NR/rrc_gNB.c | 82 +++++++++-- openair2/RRC/NR/rrc_gNB_NGAP.c | 117 ++++++++------- openair2/RRC/NR/rrc_gNB_mobility.c | 18 ++- openair2/RRC/NR/rrc_gNB_radio_bearers.c | 137 +++--------------- openair2/RRC/NR/rrc_gNB_radio_bearers.h | 30 +--- openair2/SDAP/nr_sdap/nr_sdap_configuration.h | 33 +++++ 8 files changed, 209 insertions(+), 242 deletions(-) create mode 100644 openair2/SDAP/nr_sdap/nr_sdap_configuration.h diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 65bb3095e7..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, @@ -112,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 { @@ -124,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[MAX_QOS_FLOWS]; - } 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 { @@ -405,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 d7c1c0efce..fbd09d20a1 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -125,7 +125,7 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC 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 6c1c80f30e..4af378dd81 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -87,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" @@ -100,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" @@ -330,17 +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_EACH_SEQ_ARR(drb_t *, drb, &ue->drbs) { - DRB_config = generateDRB_ASN1(drb); - if (reestablish) { - asn1cCallocOne(DRB_config->reestablishPDCP, NR_DRB_ToAddMod__reestablishPDCP_true); + 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; } - asn1cSeqAdd(&DRB_configList->list, DRB_config); + 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); @@ -644,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, @@ -882,9 +932,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; } @@ -1048,7 +1097,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; @@ -2077,7 +2128,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->drbs, 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; @@ -2488,6 +2540,7 @@ static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc, 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->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); @@ -2522,10 +2575,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++; } diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 7afc7afa23..90a0522886 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" @@ -264,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. * @@ -289,13 +342,14 @@ 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++) { + // 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; - LOG_I(NR_RRC, "Add PDU Session %d (total nb of sessions = %ld)\n", session->pdusession_id, seq_arr_size(&UE->pduSessions)); // Fill E1 Bearer Context Modification Request bearer_req.gNB_cu_cp_ue_id = UE->rrc_ue_id; security_information_t *secInfo = &bearer_req.secInfo; @@ -323,57 +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. */ - drb_t *rrc_drb = generateDRB(UE, - session, - 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; - } - - FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos, &session->qos) { - pdusession_level_qos_parameter_t *qos_session = &qos->qos; - DevAssert(drb->numQosFlow2Setup < MAX_QOS_FLOWS); - qos_flow_to_setup_t *qos_flow = &drb->qosFlows[drb->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; + 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 @@ -1231,7 +1246,7 @@ int rrc_gNB_send_NGAP_ul_ran_status_transfer(gNB_RRC_INST *rrc, continue; } - bool sn_length_18 = drb->pdcp_config.pdcp_SN_SizeDL == NR_PDCP_Config__drb__pdcp_SN_SizeUL_len18bits; + 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++]; diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 0d97e5d30a..552cc881d6 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -72,22 +72,25 @@ static int fill_drb_to_be_setup(const gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, f1ap_ 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); - int qfi = rrc_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[0]; - nr_rrc_qos_t *qos_param = find_qos(&pdu->param.qos, qfi); - drb->id = rrc_drb->drb_id; drb->qos_choice = F1AP_QOS_CHOICE_NR; 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; + + // 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; @@ -97,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_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index ccf3fbe99f..5af84f0399 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -142,9 +142,11 @@ rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusessio } /** @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) +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; @@ -162,7 +164,7 @@ drb_t *nr_rrc_add_drb(seq_arr_t *drb_ptr, int pdusession_id) } // Add item to the list - drb_t in = {.drb_id = drb_id, .cnAssociation.sdap_config.pdusession_id = pdusession_id}; + 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); @@ -201,129 +203,26 @@ rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id 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; + int id = drb->pdusession_id; return find_pduSession(&ue->pduSessions, id); } -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) +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) { - 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, - const pdusession_t *pduSession, - bool enable_sdap, - int do_drb_integrity, - int do_drb_ciphering, - const nr_pdcp_configuration_t *pdcp_config) -{ - DevAssert(ue != NULL); - - drb_t *est_drb = nr_rrc_add_drb(&ue->drbs, pduSession->pdusession_id); - if (!est_drb) { - LOG_E(NR_RRC, "UE %d: failed to add DRB for PDU session ID %d\n", ue->rrc_ue_id, pduSession->pdusession_id); - return NULL; - } - LOG_I(NR_RRC, "UE %d: configure DRB ID %d for PDU session ID %d\n", ue->rrc_ue_id, est_drb->drb_id, pduSession->pdusession_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->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; - } - int qos_flow_index = 0; - FOR_EACH_SEQ_ARR(nr_rrc_qos_t *, qos_session, &pduSession->qos) { - qos_session->drb_id = est_drb->drb_id; // Map DRB to QoS - est_drb->cnAssociation.sdap_config.mappedQoS_FlowsToAdd[qos_flow_index++] = qos_session->qos.qfi; - DevAssert(qos_flow_index <= MAX_QOS_FLOWS); - } - /* 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->drbs, est_drb->drb_id); - DevAssert(rrc_drb); - 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 < MAX_QOS_FLOWS; 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; + 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 e5ec90c5c6..f6821e314b 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -24,34 +24,12 @@ #include #include -#include "NR_DRB-ToAddMod.h" #include "e1ap_messages_types.h" #include "nr_rrc_defs.h" -/// @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(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, - const pdusession_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); @@ -61,13 +39,15 @@ rrc_pdu_session_param_t *add_pduSession(seq_arr_t *sessions_ptr, const pdusessio /// @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 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); +/// @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); void free_pdusession(void *ptr); /// @brief Add DRB to RRC list -drb_t *nr_rrc_add_drb(seq_arr_t *drb_ptr, int pdusession_id); +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); 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_ */ From 9bf79cd2c92a66b90360a072631230c8cf0ef05f Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 22 Jul 2025 15:35:16 +0200 Subject: [PATCH 17/19] PDU session release: refactor NGAP PDU Session Release command/response * There is no IE corresponding to PDU sessions failed to release in the PDU SESSION RESOURCE RELEASE RESPONSE message. This was removed and the corresponding code was refactored and cleaned up. * In the Release Command handler, only established PDU Session are set to be released. The Response message is filled accordingly. --- openair2/COMMON/ngap_messages_types.h | 8 +------- openair2/RRC/NR/rrc_gNB_NGAP.c | 21 ++++++--------------- openair3/NGAP/ngap_gNB_nas_procedures.c | 5 ++--- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index 1c5fa90868..2c4b0f8a0a 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -918,19 +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; // 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/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 90a0522886..34666e3538 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1489,7 +1489,6 @@ 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); @@ -1499,17 +1498,15 @@ void rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(gNB_RRC_INST *rrc, gNB_RRC_UE FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, session, &UE->pduSessions) { if (xid == session->xid) { const pdusession_t *pdusession = &session->param; - resp->pdusession_release[pdu_sessions_released].pdusession_id = pdusession->pdusession_id; + 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; - pdu_sessions_released++; } } - resp->nb_of_pdusessions_released = pdu_sessions_released; - resp->nb_of_pdusessions_failed = 0; - DevAssert(resp->nb_of_pdusessions_released < NGAP_MAX_PDU_SESSION); - - 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); } @@ -1538,13 +1535,7 @@ int rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(MessageDef *msg_p, instance_ for (int pdusession = 0; pdusession < cmd->nb_pdusessions_torelease; pdusession++) { 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); - rrc_pdu_session_param_t item = {0}; - item.status = PDU_SESSION_STATUS_FAILED; - item.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}; - item.cause = cause; - seq_arr_push_back(&UE->pduSessions, &item, sizeof(item)); + 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) { 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; } From c99f836ad4e1b6a23c40385f7fc4b8a8fcabf532 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 24 Sep 2025 14:01:41 +0200 Subject: [PATCH 18/19] Refactor DRB release handling to use actual DRB IDs - Replace simple counter-based DRB ID assignment with actual DRB ID lookup - Add nested loop to iterate through DRBs for each PDU session to be release - Improve logic flow with early continue for non-matching PDU sessions This ensures DRB release list contains correct DRB IDs that correspond to actual DRB entities --- openair2/RRC/NR/rrc_gNB.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 4af378dd81..cc766b9d81 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -836,11 +836,14 @@ void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc, nr_rrc_reconfig_param_t params = {.transaction_id = xid}; NR_DRB_ToReleaseList_t *to_release = CALLOC(sizeof(*to_release), 1); - int i = 0; FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { - if ((item->status == PDU_SESSION_STATUS_TORELEASE) && item->xid == xid) { - asn1cSequenceAdd(to_release->list, NR_DRB_Identity_t, DRB_release); - *DRB_release = ++i; // DRB ID + 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; From e6d8d6a07209e957629027deab6a263ff715e133 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 24 Sep 2025 16:34:52 +0200 Subject: [PATCH 19/19] Add tests for RRC radio bearers lib --- openair2/RRC/NR/CMakeLists.txt | 4 + openair2/RRC/NR/tests/CMakeLists.txt | 7 + openair2/RRC/NR/tests/rrc_bearers_test.c | 173 +++++++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 openair2/RRC/NR/tests/CMakeLists.txt create mode 100644 openair2/RRC/NR/tests/rrc_bearers_test.c 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/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; +}