From 7e1c3e4213078ee24cd1c73187990f9dde81ca47 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 11 Feb 2025 18:26:22 +0100 Subject: [PATCH 1/4] Fix double-E1 bearer context setup req in E1 handler If two PDU sessions are to be setup, the CU-CP sends two bearer context setup requests. When running only the CU-UP, we inconditionally tried to set up the CU-UP UE ID identifiers upon reception of the bearer context setup request, leading to an assert. Check if the IDs are not already set up, to avoid assertions in that case. --- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index 96701dbb83..b96d38030c 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -149,7 +149,7 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) /* mirror the CU-CP UE ID for CU-UP */ uint32_t cu_up_ue_id = req->gNB_cu_cp_ue_id; f1_ue_data_t ued = {.secondary_ue = req->gNB_cu_cp_ue_id}; - if (need_ue_id_mgmt) { + if (need_ue_id_mgmt && !cu_exists_f1_ue_data(cu_up_ue_id)) { bool success = cu_add_f1_ue_data(cu_up_ue_id, &ued); DevAssert(success); LOG_I(E1AP, "adding UE with CU-CP UE ID %d and CU-UP UE ID %d\n", req->gNB_cu_cp_ue_id, cu_up_ue_id); From 5625bc63d11c0cc8f8395f4bfa4d5b689873ea94 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 11 Feb 2025 18:31:10 +0100 Subject: [PATCH 2/4] Add PDU session add log in E1 handler bearer context modification and release print a message, so do the same here as well. --- openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c index b96d38030c..2fc2159fe1 100644 --- a/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c +++ b/openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c @@ -164,8 +164,10 @@ void e1_bearer_context_setup(const e1ap_bearer_setup_req_t *req) }; resp.numPDUSessions = req->numPDUSessions; for (int i = 0; i < resp.numPDUSessions; ++i) { - pdu_session_setup_t *resp_pdu = resp.pduSession + i; const pdu_session_to_setup_t *req_pdu = req->pduSession + i; + LOG_I(E1AP, "UE %d: add PDU session ID %ld (%d bearers)\n", cu_up_ue_id, req_pdu->sessionId, req_pdu->numDRB2Setup); + + pdu_session_setup_t *resp_pdu = resp.pduSession + i; resp_pdu->id = req_pdu->sessionId; AssertFatal(req_pdu->numDRB2Setup == 1, "can only handle one DRB per PDU session\n"); From 59575b9cde026169d5520955e14599a82a607bb0 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 11 Feb 2025 18:54:02 +0100 Subject: [PATCH 3/4] Handle PDU session resource setup for existing PDU session 38.413 section 8.2.1.4 says that if PDU session resource setup req contains an existing PDU session, that one should be reported as failed. That is rather complicated for us to do; simply reject all PDU sessions and let the core deal with that. --- openair2/RRC/NR/rrc_gNB_NGAP.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index c1715c438c..a7dc0a4c5f 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -872,6 +872,21 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins return; } + // Reject PDU session if at least one exists already with that ID. + // At least one because marking one as existing, and setting up another, that + // 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_t *p = &msg->pdusession_setup_params[i]; + rrc_pdu_session_param_t *exist = find_pduSession(UE, p->pdusession_id, false /* don't create */); + 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}; + send_ngap_pdu_session_setup_resp_fail(instance, msg, cause); + rrc_forward_ue_nas_message(rrc, UE); + return; + } + } + UE->amf_ue_ngap_id = msg->amf_ue_ngap_id; if (!trigger_bearer_setup(rrc, UE, msg->nb_pdusessions_tosetup, msg->pdusession_setup_params, msg->ueAggMaxBitRateDownlink)) { From 277f8da010e56020627d2db8884c84789223bdf6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 11 Feb 2025 18:45:14 +0100 Subject: [PATCH 4/4] Workaround to delay multiple concurrent PDU session This is a hack. With some UEs/CN combinations, PDU session requests might come in quick succession. Since 38.331 asks us to only have one reconfiguration at once, we need to delay a second, third, ... PDU session request until the first has passed, to make one reconfiguration at a time. --- openair2/RRC/NR/nr_rrc_defs.h | 4 +++ openair2/RRC/NR/rrc_gNB.c | 11 +++++-- openair2/RRC/NR/rrc_gNB_NGAP.c | 47 ++++++++++++++++++++++++++-- openair2/RRC/NR/rrc_gNB_UE_context.c | 2 ++ 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 7c02ad4f9f..c30f8856bc 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -255,6 +255,10 @@ typedef struct gNB_RRC_UE_s { /* Nas Pdu */ ngap_pdu_t nas_pdu; + /* hack, see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() for more info */ + int max_delays_pdu_session; + bool ongoing_pdusession_setup_request; + } gNB_RRC_UE_t; typedef struct rrc_gNB_ue_context_s { diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 22bf766716..3d57dc0960 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1667,6 +1667,10 @@ static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *U LOG_E(RRC, "UE %d: Received unexpected transaction type %d for xid %d\n", UE->rrc_ue_id, UE->xids[xid], xid); break; } + + if (UE->xids[xid] == RRC_PDUSESSION_ESTABLISH) + UE->ongoing_pdusession_setup_request = false; + UE->xids[xid] = RRC_ACTION_NONE; for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) { if (UE->xids[i] != RRC_ACTION_NONE) { @@ -2555,9 +2559,10 @@ void *rrc_gnb_task(void *args_p) { break; case TIMER_HAS_EXPIRED: - /* only this one handled for now */ - DevAssert(TIMER_HAS_EXPIRED(msg_p).timer_id == stats_timer_id); - write_rrc_stats(RC.nrrrc[0]); + if (TIMER_HAS_EXPIRED(msg_p).timer_id == stats_timer_id) + write_rrc_stats(RC.nrrrc[0]); + else + itti_send_msg_to_task(TASK_RRC_GNB, 0, TIMER_HAS_EXPIRED(msg_p).arg); /* see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() */ break; case F1AP_INITIAL_UL_RRC_MESSAGE: diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index a7dc0a4c5f..e01d5ab9dc 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -816,6 +816,29 @@ void rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE return; } +/* \brief checks if any transaction is ongoing for any xid of this UE */ +static bool transaction_ongoing(const gNB_RRC_UE_t *UE) +{ + for (int xid = 0; xid < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++xid) { + if (UE->xids[xid] != RRC_ACTION_NONE) + return true; + } + return false; +} + +/* \brief delays the ongoing transaction (in msg_p) by setting a timer to wait + * 10ms; upon expiry, delivers to RRC, which sends the message to itself */ +static void delay_transaction(MessageDef *msg_p, int wait_us) +{ + MessageDef *new = itti_alloc_new_message(TASK_RRC_GNB, 0, NGAP_PDUSESSION_SETUP_REQ); + ngap_pdusession_setup_req_t *n = &NGAP_PDUSESSION_SETUP_REQ(new); + *n = NGAP_PDUSESSION_SETUP_REQ(msg_p); + + int instance = msg_p->ittiMsgHeader.originInstance; + long timer_id; + timer_setup(0, wait_us, TASK_RRC_GNB, instance, TIMER_ONE_SHOT, new, &timer_id); +} + /** * @brief Fill PDU Session Resource Setup Response with a list of PDU Session Resources Failed to Setup * and send ITTI message to TASK_NGAP @@ -889,15 +912,35 @@ void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(MessageDef *msg_p, instance_t ins UE->amf_ue_ngap_id = msg->amf_ue_ngap_id; + /* This is a hack. We observed that with some UEs, PDU session requests might + * come in quick succession, faster than the RRC reconfiguration for the PDU + * session requests can be carried out (UE is doing reconfig, and second PDU + * session request arrives). We don't have currently the means to "queue up" + * these transactions, which would probably involve some rework of the RRC. + * To still allow these requests to come in and succeed, we below check and delay transactions + * for 10ms. However, to not accidentally end up in infinite loops, the + * maximum number is capped on a per-UE basis as indicated in variable + * max_delays_pdu_session. */ + if (!UE->ongoing_pdusession_setup_request) + UE->max_delays_pdu_session = 100; + + if (UE->max_delays_pdu_session > 0 && (transaction_ongoing(UE) || UE->ongoing_pdusession_setup_request)) { + int wait_us = 10000; + LOG_I(RRC, "UE %d: delay PDU session setup by %d us, pending %d retries\n", UE->rrc_ue_id, wait_us, UE->max_delays_pdu_session); + delay_transaction(msg_p, wait_us); + UE->max_delays_pdu_session--; + return; + } + if (!trigger_bearer_setup(rrc, UE, msg->nb_pdusessions_tosetup, msg->pdusession_setup_params, msg->ueAggMaxBitRateDownlink)) { // Reject PDU Session Resource setup if there's no CU-UP associated LOG_W(NR_RRC, "UE %d: reject PDU Session Setup in PDU Session Resource Setup Response\n", UE->rrc_ue_id); ngap_cause_t cause = {.type = NGAP_CAUSE_RADIO_NETWORK, .value = NGAP_CAUSE_RADIO_NETWORK_RESOURCES_NOT_AVAILABLE_FOR_THE_SLICE}; send_ngap_pdu_session_setup_resp_fail(instance, msg, cause); rrc_forward_ue_nas_message(rrc, UE); + } else { + UE->ongoing_pdusession_setup_request = true; } - - return; } static void fill_qos2(NGAP_QosFlowAddOrModifyRequestList_t *qos, pdusession_t *session) diff --git a/openair2/RRC/NR/rrc_gNB_UE_context.c b/openair2/RRC/NR/rrc_gNB_UE_context.c index 9b02eaa1f4..b39abd9a30 100644 --- a/openair2/RRC/NR/rrc_gNB_UE_context.c +++ b/openair2/RRC/NR/rrc_gNB_UE_context.c @@ -206,6 +206,8 @@ rrc_gNB_ue_context_t *rrc_gNB_create_ue_context(sctp_assoc_t assoc_id, ue->rrc_ue_id); bool success = cu_add_f1_ue_data(ue->rrc_ue_id, &ue_data); DevAssert(success); + ue->max_delays_pdu_session = 20; /* see rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ() */ + ue->ongoing_pdusession_setup_request = false; RB_INSERT(rrc_nr_ue_tree_s, &rrc_instance_pP->rrc_ue_head, ue_context_p); LOG_UE_EVENT(ue,