From 0ea07517fb2e3927a640de8706f532e75d8fb27a Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 28 Oct 2025 14:48:42 +0100 Subject: [PATCH] Refactor (RRC/F1): extract common F1 UE context setup code Improve maintainability by centralizing common F1AP UE context setup logic into reusable helper functions, enabling consistent handling and clear serving/target DU entry points for different scenarios. Code duplication prevention is a beneficial side effect, since at least one helper function is adopted in subsequent commits. Major changes: - Move DRB list filler from external file into rrc_gNB.c and rename fill_drb_to_be_setup to rrc_fill_f1_drb_to_setup for naming consistency - Add static helper rrc_fill_f1_ue_context_setup to populate common fields shared between serving DU and target DU scenarios: - Rename rrc_gNB_generate_UeContextSetupRequest to rrc_f1_ue_context_setup_from_e1_response to clarify it prepares F1AP context setup from E1AP bearer setup response for serving DU - Add rrc_f1_ue_context_setup_for_target_du (for target DU in handover/reestablishment scenarios) - Adopt helper rrc_fill_f1_ue_context_setup in both functions --- openair2/RRC/NR/nr_rrc_proto.h | 11 +- openair2/RRC/NR/rrc_gNB.c | 155 ++++++++++++++++++++++------- openair2/RRC/NR/rrc_gNB_mobility.c | 89 +---------------- 3 files changed, 130 insertions(+), 125 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index a36a0778c6..0f8c262e89 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -100,9 +100,9 @@ int rrc_gNB_generate_pcch_msg(sctp_assoc_t assoc_id, const NR_SIB1_t *sib, uint3 /* UE Management Procedures */ -void rrc_gNB_generate_UeContextSetupRequest(const gNB_RRC_INST *rrc, - rrc_gNB_ue_context_t *const ue_context_pP, - const e1ap_bearer_setup_resp_t *resp); +void rrc_f1_ue_context_setup_from_e1_response(const gNB_RRC_INST *rrc, + rrc_gNB_ue_context_t *const ue_context_pP, + const e1ap_bearer_setup_resp_t *resp); void rrc_gNB_generate_UeContextModificationRequest(const gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *const ue_context_pP, @@ -124,4 +124,9 @@ void activate_srb(gNB_RRC_UE_t *UE, int srb_id); void e1_notify_pdcp_status(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const ngap_drb_status_t *drb_status); void init_delayed_action(delayed_action_state_t *delayed_action); +void rrc_f1_ue_context_setup_for_target_du(const gNB_RRC_INST *rrc, + gNB_RRC_UE_t *ue, + const nr_rrc_du_container_t *du, + const byte_array_t *ho_prep_info); + #endif diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 7a1c96e33b..6f59a1956b 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -529,6 +529,37 @@ void activate_srb(gNB_RRC_UE_t *UE, int srb_id) freeSRBlist(list); } +static f1ap_ue_context_setup_req_t rrc_fill_f1_ue_context_setup(gNB_RRC_UE_t *ue, + const nr_rrc_du_container_t *du, + const uint32_t *gNB_DU_ue_id) +{ + f1ap_ue_context_setup_req_t req = { + .gNB_CU_ue_id = ue->rrc_ue_id, + .servCellIndex = 0, + }; + + req.gnb_du_ue_agg_mbr_ul = malloc_or_fail(sizeof(*req.gnb_du_ue_agg_mbr_ul)); + *req.gnb_du_ue_agg_mbr_ul = 1000000000; + req.cu_to_du_rrc_info.meas_timing_config = get_meas_timing_config(du->mtc, ue->measConfig); + if (ue->ue_cap_buffer.len > 0) { + req.cu_to_du_rrc_info.ue_cap = malloc_or_fail(sizeof(byte_array_t)); + *req.cu_to_du_rrc_info.ue_cap = copy_byte_array(ue->ue_cap_buffer); + } + if (gNB_DU_ue_id) { + req.gNB_DU_ue_id = malloc_or_fail(sizeof(*req.gNB_DU_ue_id)); + *req.gNB_DU_ue_id = *gNB_DU_ue_id; + } + + /* PLMN and cell ID from DU setup request */ + const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; + req.plmn.mcc = cell_info->plmn.mcc; + req.plmn.mnc = cell_info->plmn.mnc; + req.plmn.mnc_digit_length = cell_info->plmn.mnc_digit_length; + req.nr_cellid = cell_info->nr_cellid; + + return req; +} + //----------------------------------------------------------------------------- static void rrc_gNB_generate_RRCSetup(instance_t instance, rnti_t rnti, @@ -2815,7 +2846,7 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp } if (!UE->f1_ue_context_active) - rrc_gNB_generate_UeContextSetupRequest(rrc, ue_context_p, resp); + rrc_f1_ue_context_setup_from_e1_response(rrc, ue_context_p, resp); else rrc_gNB_generate_UeContextModificationRequest(rrc, ue_context_p, resp, 0, NULL); } @@ -3397,25 +3428,96 @@ int rrc_gNB_generate_pcch_msg(sctp_assoc_t assoc_id, const NR_SIB1_t *sib1, uint /* F1AP UE Context Management Procedures */ -//----------------------------------------------------------------------------- -void rrc_gNB_generate_UeContextSetupRequest(const gNB_RRC_INST *rrc, - rrc_gNB_ue_context_t *const ue_context_pP, - const e1ap_bearer_setup_resp_t *resp) -//----------------------------------------------------------------------------- +/** @brief Fill DRB to Be Setup List for F1 UE Context Setup Request + * Returns number of DRBs filled, 0 if none */ +static int rrc_fill_f1_drb_to_setup(const gNB_RRC_INST *rrc, const gNB_RRC_UE_t *ue, f1ap_drb_to_setup_t drbs[MAX_DRBS_PER_UE]) +{ + int nb_drb = 0; + + 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((gNB_RRC_UE_t *)ue, rrc_drb->drb_id); + AssertFatal(pdu != NULL, "no PDU session for DRB ID %d\n", rrc_drb->drb_id); + + 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)); + + /* QoS flow associated with this DRB: use 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: reuse the ones from the first flow */ + drb->nr.drb_qos = drb->nr.flows[0].param; + + memcpy(&drb->up_ul_tnl[0].tl_address, &rrc_drb->cuup_tunnel_config.addr.buffer, sizeof(uint8_t) * 4); + drb->up_ul_tnl[0].teid = rrc_drb->cuup_tunnel_config.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 = 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 = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; + } + return nb_drb; +} + +void rrc_f1_ue_context_setup_for_target_du(const gNB_RRC_INST *rrc, + gNB_RRC_UE_t *ue, + const nr_rrc_du_container_t *du, + const byte_array_t *ho_prep_info) +{ + DevAssert(du != NULL); + byte_array_t *hpi = NULL; + if (ho_prep_info) { + hpi = malloc_or_fail(sizeof(*hpi)); + *hpi = copy_byte_array(*ho_prep_info); + } + int nb_srb = 2; + f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs)); + f1ap_drb_to_setup_t *drbs = calloc_or_fail(MAX_DRBS_PER_UE, sizeof(*drbs)); + int nb_drb = rrc_fill_f1_drb_to_setup(rrc, ue, drbs); + srbs[0].id = SRB1; + srbs[1].id = SRB2; + free_MeasConfig(ue->measConfig); + ue->measConfig = nr_rrc_get_measconfig(rrc, du->setup_req->cell[0].info.nr_cellid); + byte_array_t *meas_config = calloc_or_fail(1, sizeof(*meas_config)); + meas_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE); + meas_config->len = do_NR_MeasConfig(ue->measConfig, meas_config->buf, NR_RRC_BUF_SIZE); + f1ap_ue_context_setup_req_t req = rrc_fill_f1_ue_context_setup(ue, du, NULL); + req.srbs_len = 2; + req.srbs = srbs; + req.drbs_len = nb_drb; + req.drbs = drbs; + req.cu_to_du_rrc_info.meas_config = meas_config; + req.cu_to_du_rrc_info.ho_prep_info = hpi; + + RETURN_IF_INVALID_ASSOC_ID(du->assoc_id); + rrc->mac_rrc.ue_context_setup_request(du->assoc_id, &req); + free_ue_context_setup_req(&req); +} + +void rrc_f1_ue_context_setup_from_e1_response(const gNB_RRC_INST *rrc, + rrc_gNB_ue_context_t *const ue_context_pP, + const e1ap_bearer_setup_resp_t *resp) { gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; AssertFatal(!ue_p->f1_ue_context_active, "logic error: ue context already active\n"); AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n"); - f1ap_cu_to_du_rrc_info_t cu2du = {0}; - if (ue_p->ue_cap_buffer.len > 0) { - cu2du.ue_cap = malloc_or_fail(sizeof(byte_array_t)); - *cu2du.ue_cap = copy_byte_array(ue_p->ue_cap_buffer); - } - nr_rrc_du_container_t *du = get_du_for_ue((gNB_RRC_INST *)rrc, ue_p->rrc_ue_id); - cu2du.meas_timing_config = get_meas_timing_config(du->mtc, ue_p->measConfig); int nb_srb = 1; f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs)); @@ -3426,29 +3528,14 @@ void rrc_gNB_generate_UeContextSetupRequest(const gNB_RRC_INST *rrc, f1ap_drb_to_setup_t *drbs = calloc_or_fail(32, sizeof(*drbs)); // maximum DRB can be 32 int n_drbs = fill_drb_to_be_setup_from_e1_resp(rrc, ue_p, resp->pduSession, resp->numPDUSessions, drbs); - /* gNB-DU UE Aggregate Maximum Bit Rate Uplink is C- ifDRBSetup */ - uint64_t *agg_mbr = malloc_or_fail(sizeof(*agg_mbr)); - *agg_mbr = 1000000000 /* bps */; - f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id); RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); - uint32_t *du_ue_id = malloc_or_fail(sizeof(*du_ue_id)); - *du_ue_id = ue_data.secondary_ue; - f1ap_ue_context_setup_req_t ue_context_setup_req = { - .gNB_CU_ue_id = ue_p->rrc_ue_id, - .gNB_DU_ue_id = du_ue_id, - .plmn.mcc = rrc->configuration.plmn[0].mcc, - .plmn.mnc = rrc->configuration.plmn[0].mnc, - .plmn.mnc_digit_length = rrc->configuration.plmn[0].mnc_digit_length, - .nr_cellid = rrc->nr_cellid, - .servCellIndex = 0, /* TODO: correct value? */ - .srbs_len = nb_srb, - .srbs = srbs, - .drbs_len = n_drbs, - .drbs = drbs, - .cu_to_du_rrc_info = cu2du, - .gnb_du_ue_agg_mbr_ul = agg_mbr, - }; + const uint32_t du_ue_id = ue_data.secondary_ue; + f1ap_ue_context_setup_req_t ue_context_setup_req = rrc_fill_f1_ue_context_setup(ue_p, du, &du_ue_id); + ue_context_setup_req.srbs_len = nb_srb; + ue_context_setup_req.srbs = srbs; + ue_context_setup_req.drbs_len = n_drbs; + ue_context_setup_req.drbs = drbs; rrc->mac_rrc.ue_context_setup_request(ue_data.du_assoc_id, &ue_context_setup_req); free_ue_context_setup_req(&ue_context_setup_req); diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 1fc274db5b..c2495ad879 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -91,52 +91,6 @@ void nr_rrc_apply_target_context(gNB_RRC_UE_t *UE) UE->nr_cellid = target_ctx->du->setup_req->cell[0].info.nr_cellid; } -/** @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_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); - - 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)); - - // 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; - - memcpy(&drb->up_ul_tnl[0].tl_address, &rrc_drb->cuup_tunnel_config.addr.buffer, sizeof(uint8_t) * 4); - drb->up_ul_tnl[0].teid = rrc_drb->cuup_tunnel_config.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 = 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 = pdcp->drb.sn_size == 18 ? F1AP_PDCP_SN_18B : F1AP_PDCP_SN_12B; - } - return nb_drb; -} - /* \brief Initiate a handover of UE to a specific target cell handled by this * CU. * \param ue a UE context for which the handover should be triggered. The UE @@ -206,48 +160,7 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, target_du->assoc_id, target_du->setup_req->cell[0].info.nr_pci); - f1ap_drb_to_setup_t *drbs = calloc_or_fail(MAX_DRBS_PER_UE, sizeof(*drbs)); - int nb_drb = fill_drb_to_be_setup(rrc, ue, drbs); - - int nb_srb = 2; - f1ap_srb_to_setup_t *srbs = calloc_or_fail(nb_srb, sizeof(*srbs)); - srbs[0].id = 1; - srbs[1].id = 2; - - free_MeasConfig(ue->measConfig); - ue->measConfig = nr_rrc_get_measconfig(rrc, target_du->setup_req->cell[0].info.nr_cellid); - byte_array_t *meas_config = calloc_or_fail(1, sizeof(*meas_config)); - meas_config->buf = calloc_or_fail(1, NR_RRC_BUF_SIZE); - meas_config->len = do_NR_MeasConfig(ue->measConfig, meas_config->buf, NR_RRC_BUF_SIZE); - - byte_array_t *meas_timing_config = get_meas_timing_config(target_du->mtc, ue->measConfig); - - byte_array_t *hpi = malloc_or_fail(sizeof(*hpi)); - *hpi = copy_byte_array(*ho_prep_info); - - uint64_t *ue_agg_mbr = malloc_or_fail(sizeof(*ue_agg_mbr)); - *ue_agg_mbr = 1000000000 /*bps*/; - - f1ap_served_cell_info_t *cell_info = &target_du->setup_req->cell[0].info; - RETURN_IF_INVALID_ASSOC_ID(target_du->assoc_id); - f1ap_ue_context_setup_req_t ue_context_setup_req = { - .gNB_CU_ue_id = ue->rrc_ue_id, - .plmn.mcc = cell_info->plmn.mcc, - .plmn.mnc = cell_info->plmn.mnc, - .plmn.mnc_digit_length = cell_info->plmn.mnc_digit_length, - .nr_cellid = cell_info->nr_cellid, - .servCellIndex = 0, // TODO: correct value? - .srbs_len = nb_srb, - .srbs = srbs, - .drbs_len = nb_drb, - .drbs = drbs, - .cu_to_du_rrc_info.ho_prep_info = hpi, - .cu_to_du_rrc_info.meas_config = meas_config, - .cu_to_du_rrc_info.meas_timing_config = meas_timing_config, - .gnb_du_ue_agg_mbr_ul = ue_agg_mbr, - }; - rrc->mac_rrc.ue_context_setup_request(target_du->assoc_id, &ue_context_setup_req); - free_ue_context_setup_req(&ue_context_setup_req); + rrc_f1_ue_context_setup_for_target_du(rrc, ue, target_du, ho_prep_info); } typedef struct deliver_ue_ctxt_modification_data_t {