diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index 3add62a338..f3fd5edda2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -634,6 +634,59 @@ static void handle_ue_context_rrc_container(const byte_array_t *rrc_container, c nr_rlc_srb_recv_sdu(rnti, id, rrc_container->buf, rrc_container->len); } +/** @brief Get and clone CellGroupConfig for UE Context Setup/Modification response. + * The source depends on re-establishment state: + * - CellGroup: Currently active/runtime CellGroupConfig used by MAC for scheduling. + * During re-establishment, spCellConfig is removed from it (per TS 38.331 ยง5.3.7.2). + * - reconfigCellGroup: Staging area that holds a complete CellGroupConfig saved before + * modifications. During re-establishment, it still contains spCellConfig (saved before + * removal). It becomes the new CellGroup when reconfiguration completes. + * During re-establishment, we must clone from reconfigCellGroup to get a complete + * CellGroupConfig with spCellConfig for transparent forwarding to the UE. + * @param UE UE context + * @return Cloned CellGroupConfig */ +static NR_CellGroupConfig_t *get_cellgroup_config(NR_UE_info_t *UE) +{ + if (UE->reestablish_rlc && UE->reconfigCellGroup != NULL) { + return clone_CellGroupConfig(UE->reconfigCellGroup); + } else { + return clone_CellGroupConfig(UE->CellGroup); + } +} + +/** @brief Update CellGroupConfig for RRC re-establishment procedure. + * This function prepares a complete CellGroupConfig for gNB-DU Configuration Query response + * during RRC re-establishment. + * The function sets reestablishRLC flags for all RLC bearers except SRB1. + * SRB1 is removed from the bearer list since it is already re-established during + * RRCReestablishment and should not be included. */ +static void update_cellgroup_for_reestablishment(NR_UE_info_t *UE, NR_CellGroupConfig_t *new_CellGroup) +{ + DevAssert(new_CellGroup); + DevAssert(new_CellGroup->spCellConfig); + DevAssert(UE->reestablish_rlc); + LOG_I(NR_MAC, "UE %04x: Re-establishment detected, setting reestablishRLC flags\n", UE->rnti); + struct NR_CellGroupConfig__rlc_BearerToAddModList *addmod = new_CellGroup->rlc_BearerToAddModList; + if (addmod && addmod->list.count > 0) { + LOG_I(NR_MAC, "UE %04x: CellGroupConfig has %d bearers:\n", UE->rnti, addmod->list.count); + for (int i = 0; i < addmod->list.count; ++i) { + NR_RLC_BearerConfig_t *bearer = addmod->list.array[i]; + int lcid = bearer->logicalChannelIdentity; + int rb_type = bearer->servedRadioBearer->present; + int rb_id = (rb_type == NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity) + ? bearer->servedRadioBearer->choice.srb_Identity + : bearer->servedRadioBearer->choice.drb_Identity; + if (rb_type == NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity && rb_id == 1) { + asn_sequence_del(&addmod->list, i, 1); + --i; + continue; + } + LOG_I(NR_MAC, "UE %04x: Re-establishing RLC for LCID %d\n", UE->rnti, lcid); + asn1cCallocOne(addmod->list.array[i]->reestablishRLC, NR_RLC_BearerConfig__reestablishRLC_true); + } + } +} + void ue_context_setup_request(const f1ap_ue_context_setup_req_t *req) { const bool is_SA = IS_SA_MODE(get_softmodem_params()); @@ -766,7 +819,7 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req) return; } - NR_CellGroupConfig_t *new_CellGroup = clone_CellGroupConfig(UE->CellGroup); + NR_CellGroupConfig_t *new_CellGroup = get_cellgroup_config(UE); if (req->srbs_len > 0) { resp.srbs_len = handle_ue_context_srbs_setup(UE, req->srbs_len, req->srbs, &resp.srbs, new_CellGroup, &mac->rlc_config); @@ -804,6 +857,7 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req) for (int i = 1; i < seq_arr_size(&UE->UE_sched_ctrl.lc_config); ++i) { nr_lc_config_t *c = seq_arr_at(&UE->UE_sched_ctrl.lc_config, i); c->suspended = false; + LOG_I(NR_MAC, "UE %04x: Re-establishing RLC for LCID %d\n", UE->rnti, c->lcid); nr_rlc_reestablish_entity(req->gNB_DU_ue_id, c->lcid); } UE->reestablish_rlc = false; @@ -821,12 +875,21 @@ void ue_context_modification_request(const f1ap_ue_context_mod_req_t *req) update_cellGroupConfig(new_CellGroup, UE->uid, UE->capability, &mac->radio_config, scc); } - // 3GPP TS 38.473 Clause 8.3.4: If gNB-DU Configuration Query is present, include CellGroupConfig + /* 3GPP TS 38.473 Clause 8.3.4: If gNB-DU Configuration Query is present, include CellGroupConfig + * (CU requested CellGroupConfig for transparent forwarding) */ if (req->gNB_DU_Configuration_Query != NULL && *req->gNB_DU_Configuration_Query) { LOG_I(NR_MAC, "UE %04x: gNB-DU Configuration Query received, will include CellGroupConfig in response\n", UE->rnti); + + if (UE->reestablish_rlc) { + update_cellgroup_for_reestablishment(UE, new_CellGroup); + } + + /* Encode CellGroupConfig for transparent forwarding in the DU to CU trasnfer IE + * CU will forward these encoded bytes directly to UE without decode/re-encode cycles */ resp.du_to_cu_rrc_info = calloc_or_fail(1, sizeof(du_to_cu_rrc_information_t)); resp.du_to_cu_rrc_info->cell_group_config = encode_cellgroup_config(new_CellGroup); + // Replace reconfigCellGroup with new_CellGroup (now contains complete config with spCellConfig restored) ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->reconfigCellGroup); UE->reconfigCellGroup = new_CellGroup; configure_UE_BWP(mac, scc, UE, false, NR_SearchSpace__searchSpaceType_PR_common, -1, -1); diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 67172cccbe..7a80ff57d2 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -532,7 +532,7 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re ie->measConfig = params->meas_config; /* nonCriticalExtension, RRCReconfiguration-v1530-IEs */ - if (params->cell_group_config || params->num_nas_msg) { + if (params->cgc || params->num_nas_msg) { // Allocate memory for extension IE ie->nonCriticalExtension = calloc_or_fail(1, sizeof(*ie->nonCriticalExtension)); } @@ -549,37 +549,13 @@ static NR_RRCReconfiguration_IEs_t *build_RRCReconfiguration_IEs(const nr_rrc_re } } - /* masterCellGroup */ - if (params->cell_group_config) { - uint8_t temp[4096]; - asn_enc_rval_t enc = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, NULL, params->cell_group_config, temp, sizeof(temp)); - - if (enc.encoded <= 0) { - LOG_E(NR_RRC, "ASN.1 encoding failed for NR_CellGroupConfig (encoded=%ld)\n", enc.encoded); - if (enc.failed_type) { - LOG_E(NR_RRC, "Failed at ASN.1 type: %s\n", enc.failed_type->name); - } - if (enc.structure_ptr) { - LOG_E(NR_RRC, "Failed at structure element: %p\n", enc.structure_ptr); - } - // Print diagnostic information to help debug the issue - LOG_E(NR_RRC, "CellGroupConfig structure that failed to encode:\n"); - xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *)params->cell_group_config); - ASN_STRUCT_FREE(asn_DEF_NR_RRCReconfiguration_IEs, ie); - return NULL; - } - DevAssert(enc.encoded <= sizeof(temp) * 8); // Encoded data must fit in temp buffer - // Allocate buffer for the encoded data and copy it - // enc.encoded is in bits, convert to bytes - size_t encoded_bytes = (enc.encoded + 7) / 8; - uint8_t *buf = calloc_or_fail(1, encoded_bytes); - memcpy(buf, temp, encoded_bytes); - - if (LOG_DEBUGFLAG(DEBUG_ASN1)) { - xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *)params->cell_group_config); - } + /* masterCellGroup - Transparent forwarding per TS 38.473 */ + if (params->cgc) { + // Copy pre-encoded CellGroupConfig bytes directly without decode/re-encode (TS 38.473 transparency) + // CU receives encoded bytes from DU and forwards to UE without modification + LOG_D(NR_RRC, "Transparent forwarding CellGroupConfig (len=%ld bytes)\n", params->cgc->len); ie->nonCriticalExtension->masterCellGroup = calloc_or_fail(1, sizeof(*ie->nonCriticalExtension->masterCellGroup)); - *ie->nonCriticalExtension->masterCellGroup = (OCTET_STRING_t){.buf = buf, .size = encoded_bytes}; + OCTET_STRING_fromBuf(ie->nonCriticalExtension->masterCellGroup, (const char *)params->cgc->buf, params->cgc->len); } /* masterKeyUpdate */ @@ -630,7 +606,7 @@ static byte_array_t do_HO_RRCReconfiguration(nr_rrc_reconfig_param_t *params) return msg; } -static void dump_cgc(const uint8_t *buf, size_t len) +void dump_cgc(const uint8_t *buf, size_t len) { // Decode the encoded CellGroupConfig for debugging NR_CellGroupConfig_t *temp_cellGroupConfig = NULL; diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 2e09397e60..ee6cfc8414 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -70,7 +70,7 @@ typedef struct { NR_MeasConfig_t *meas_config; byte_array_t dedicated_NAS_msg_list[MAX_DRBS_PER_UE]; int num_nas_msg; - NR_CellGroupConfig_t *cell_group_config; + byte_array_t *cgc; bool masterKeyUpdate; int nextHopChainingCount; byte_array_t ue_cap; @@ -179,5 +179,6 @@ int doRRCReconfiguration_from_HandoverCommand(byte_array_t *ba, byte_array_t han struct NR_UE_NR_Capability *get_ue_nr_capability(int rnti, uint8_t *buf, uint32_t len); NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityRAT_ContainerList_t *clist); +void dump_cgc(const uint8_t *buf, size_t len); #endif /* __RRC_NR_MESSAGES_ASN1_MSG__H__ */ diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 4d24a771c0..46e1ef3384 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -186,7 +186,8 @@ typedef struct gNB_RRC_UE_s { NR_UE_MRDC_Capability_t* UE_Capability_MRDC; int UE_MRDC_Capability_size; - NR_CellGroupConfig_t *masterCellGroup; + // Transparent forwarding of CellGroupConfig + byte_array_t mcg; NR_RadioBearerConfig_t *rb_config; /* KgNB as derived from KASME received from EPC */ diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 3323261c91..073fc0c34f 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -861,7 +861,7 @@ nr_rrc_reconfig_param_t get_RRCReconfiguration_params(gNB_RRC_INST *rrc, gNB_RRC NR_SRB_ToAddModList_t *SRBs = createSRBlist(UE, srb_reest_bitmap); NR_DRB_ToAddModList_t *DRBs = createDRBlist(UE, drb_reestablish, do_integrity, do_ciphering); - nr_rrc_reconfig_param_t params = {.cell_group_config = UE->masterCellGroup, + nr_rrc_reconfig_param_t params = {.cgc = &UE->mcg, .transaction_id = xid, .drb_config_list = DRBs, .meas_config = UE->measConfig, @@ -918,10 +918,22 @@ byte_array_t rrc_gNB_encode_RRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t * return msg; } -static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) +/** @brief Generate and send RRC Reconfiguration message. + * Handles both normal reconfiguration and first reconfiguration after re-establishment. + * @param rrc RRC instance + * @param ue_p UE context + * @param is_reestablishment True if this is the first reconfiguration after re-establishment */ +static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, bool is_reestablishment) { - /* do not re-establish PDCP for any bearer */ - nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue_p, 0, false); + /** Get RRC Reconfiguration parameters + * For the first reconfiguration after a re-establishment: re-establish PDCP for SRB2 + * (SRB1 already re-established in RRCReestablishment) and all DRBs, + * For the subsequent reconfigurations: do not re-establish PDCP for any bearer */ + uint8_t srb_reest_bitmap = is_reestablishment ? (1 << SRB2) : 0; + bool drb_reestablish = is_reestablishment; + nr_rrc_reconfig_param_t params = get_RRCReconfiguration_params(rrc, ue_p, srb_reest_bitmap, drb_reestablish); + + // Handle PDU session status updates FOR_EACH_SEQ_ARR(rrc_pdu_session_param_t *, item, &ue_p->pduSessions) { if (item->status == PDU_SESSION_STATUS_NEW) { item->status = PDU_SESSION_STATUS_DONE; @@ -932,15 +944,27 @@ static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_ } // Set xid for RRC transaction - ue_p->xids[params.transaction_id] = params.n_drb_rel > 0 ? RRC_PDUSESSION_RELEASE : RRC_PDUSESSION_ESTABLISH; + if (is_reestablishment) { + ue_p->xids[params.transaction_id] = RRC_REESTABLISH_COMPLETE; + } else { + ue_p->xids[params.transaction_id] = params.n_drb_rel > 0 ? RRC_PDUSESSION_RELEASE : RRC_PDUSESSION_ESTABLISH; + } byte_array_t msg = rrc_gNB_encode_RRCReconfiguration(rrc, ue_p, params); if (msg.len <= 0) { - LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id); + LOG_E(NR_RRC, + "UE %d: Failed to generate RRCReconfiguration%s\n", + ue_p->rrc_ue_id, + is_reestablishment ? " for re-establishment" : ""); + free_RRCReconfiguration_params(params); return; } - LOG_UE_DL_EVENT(ue_p, "Generate RRCReconfiguration (bytes %ld, xid %d)\n", msg.len, params.transaction_id); + LOG_UE_DL_EVENT(ue_p, + "Generate RRCReconfiguration%s (bytes %ld, xid %d)\n", + is_reestablishment ? " after re-establishment" : "", + msg.len, + params.transaction_id); const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len); free_RRCReconfiguration_params(params); @@ -1230,47 +1254,10 @@ static void rrc_gNB_process_RRCReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RR { LOG_I(NR_RRC, "UE %d Processing NR_RRCReestablishmentComplete from UE\n", ue_p->rrc_ue_id); - int i = 0; - ue_p->xids[xid] = RRC_ACTION_NONE; ue_p->Srb[1].Active = 1; - NR_CellGroupConfig_t *cellGroupConfig = calloc(1, sizeof(NR_CellGroupConfig_t)); - - cellGroupConfig->spCellConfig = ue_p->masterCellGroup->spCellConfig; - cellGroupConfig->mac_CellGroupConfig = ue_p->masterCellGroup->mac_CellGroupConfig; - cellGroupConfig->physicalCellGroupConfig = ue_p->masterCellGroup->physicalCellGroupConfig; - cellGroupConfig->rlc_BearerToReleaseList = NULL; - cellGroupConfig->rlc_BearerToAddModList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToAddModList)); - - /* - * Get SRB2, DRB configuration from the existing UE context, - * also start from SRB2 (i=1) and not from SRB1 (i=0). - */ - for (i = 1; i < ue_p->masterCellGroup->rlc_BearerToAddModList->list.count; ++i) - asn1cSeqAdd(&cellGroupConfig->rlc_BearerToAddModList->list, ue_p->masterCellGroup->rlc_BearerToAddModList->list.array[i]); - - /* - * At this stage, PDCP entity are re-established and reestablishRLC is flagged - * with RRCReconfiguration to complete RLC re-establishment of remaining bearers - */ - for (i = 0; i < cellGroupConfig->rlc_BearerToAddModList->list.count; i++) { - asn1cCallocOne(cellGroupConfig->rlc_BearerToAddModList->list.array[i]->reestablishRLC, - NR_RLC_BearerConfig__reestablishRLC_true); - } - - /* TODO: remove a reconfigurationWithSync, we don't need it for - * reestablishment. The whole reason why this might be here is that we store - * the CellGroupConfig (after handover), and simply reuse it for - * reestablishment, instead of re-requesting the CellGroupConfig from the DU. - * Hence, add below hack; the solution would be to request the - * CellGroupConfig from the DU when doing reestablishment. */ - if (cellGroupConfig->spCellConfig && cellGroupConfig->spCellConfig->reconfigurationWithSync) { - ASN_STRUCT_FREE(asn_DEF_NR_ReconfigurationWithSync, cellGroupConfig->spCellConfig->reconfigurationWithSync); - cellGroupConfig->spCellConfig->reconfigurationWithSync = NULL; - } - /* Re-establish SRB2 according to clause 5.3.5.6.3 of 3GPP TS 38.331 * (SRB1 is re-established with RRCReestablishment message) */ @@ -1287,37 +1274,26 @@ static void rrc_gNB_process_RRCReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RR /* PDCP Reestablishment of DRBs according to 5.3.5.6.5 of 3GPP TS 38.331 (over E1) */ cuup_notify_reestablishment(rrc, ue_p); - /* Create srb-ToAddModList, 3GPP TS 38.331 RadioBearerConfig: - * do not re-establish PDCP for SRB1, when resuming an RRC connection, - * 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 */ - 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); + // Request DU to provide CellGroupConfig with reestablishRLC flags for re-establishment + // According to TS 38.473 transparency requirements, CU should not construct or re-encode CellGroupConfig + 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); + + f1ap_ue_context_mod_req_t ue_context_modif_req = { + .gNB_CU_ue_id = ue_p->rrc_ue_id, + .gNB_DU_ue_id = ue_data.secondary_ue, + }; + + // Request CellGroupConfig from DU in the response + ue_context_modif_req.gNB_DU_Configuration_Query = calloc_or_fail(1, sizeof(bool)); + *ue_context_modif_req.gNB_DU_Configuration_Query = true; + + /** Send UE context modification request to DU, which will respond with CellGroupConfig + * containing reestablishRLC flags for re-establishment. + * The response will be handled in rrc_CU_process_ue_context_modification_response() + * which will store the encoded CellGroupConfig and trigger RRC Reconfiguration */ + rrc->mac_rrc.ue_context_modification_request(ue_data.du_assoc_id, &ue_context_modif_req); - uint8_t new_xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id); - ue_p->xids[new_xid] = RRC_REESTABLISH_COMPLETE; - nr_rrc_reconfig_param_t params = {.cell_group_config = cellGroupConfig, - .meas_config = ue_p->measConfig, - .drb_config_list = DRBs, - .srb_config_list = SRBs, - .transaction_id = new_xid}; - byte_array_t msg = do_RRCReconfiguration(¶ms); - if (msg.len <= 0) { - LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id); - return; - } - LOG_DUMPMSG(NR_RRC, DEBUG_RRC, msg.buf, msg.len, "[MSG] RRC Reconfiguration\n"); - LOG_I(NR_RRC, - "UE %d RNTI %04x: Generate NR_RRCReconfiguration after reestablishment complete (bytes %ld)\n", - ue_p->rrc_ue_id, - ue_p->rnti, - msg.len); - const uint32_t msg_id = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; - nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DL_SCH_LCID_DCCH, msg_id, msg.buf, msg.len); - free_RRCReconfiguration_params(params); - free_byte_array(msg); } int nr_rrc_reconfiguration_req(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, const int dl_bwp_id, const int ul_bwp_id) @@ -1326,16 +1302,7 @@ int nr_rrc_reconfiguration_req(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, const int ue_p->xids[xid] = RRC_DEDICATED_RECONF; ue_p->ongoing_reconfiguration = true; - NR_CellGroupConfig_t *masterCellGroup = ue_p->masterCellGroup; - if (dl_bwp_id > 0) { - *masterCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = dl_bwp_id; - *masterCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = dl_bwp_id; - } - if (ul_bwp_id > 0) { - *masterCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = ul_bwp_id; - } - - nr_rrc_reconfig_param_t params = {.cell_group_config = masterCellGroup, .transaction_id = xid}; + nr_rrc_reconfig_param_t params = {.cgc = &ue_p->mcg, .transaction_id = xid}; byte_array_t msg = do_RRCReconfiguration(¶ms); if (msg.len <= 0) { LOG_E(NR_RRC, "UE %d: Failed to generate RRCReconfiguration\n", ue_p->rrc_ue_id); @@ -1348,6 +1315,19 @@ int nr_rrc_reconfiguration_req(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, const int return 0; } +/** @brief Store encoded CellGroupConfig for transparent forwarding (TS 38.473 requirement). + * CU must not decode/re-encode CellGroupConfig, only forward encoded bytes to UE. */ +static void store_cgc(gNB_RRC_UE_t *UE, const byte_array_t *cgc) +{ + LOG_D(RRC, "UE %04x storing CellGroupConfig encoded bytes for transparent forwarding (len=%ld)\n", UE->rnti, cgc->len); + + free_byte_array(UE->mcg); + UE->mcg = copy_byte_array(*cgc); + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) + dump_cgc(UE->mcg.buf, UE->mcg.len); +} + static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id, const NR_RRCSetupRequest_IEs_t *rrcSetupRequest, @@ -1405,19 +1385,12 @@ static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc, rrc_gNB_generate_RRCReject(rrc, ue_context_p); return; } - NR_CellGroupConfig_t *cellGroupConfig = NULL; - asn_dec_rval_t dec_rval = uper_decode_complete(NULL, - &asn_DEF_NR_CellGroupConfig, - (void **)&cellGroupConfig, - msg->du2cu_rrc_container, - msg->du2cu_rrc_container_length); - AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n"); gNB_RRC_UE_t *UE = &ue_context_p->ue_context; - UE = &ue_context_p->ue_context; + byte_array_t cgc = {.buf = msg->du2cu_rrc_container, .len = msg->du2cu_rrc_container_length}; + store_cgc(UE, &cgc); UE->establishment_cause = rrcSetupRequest->establishmentCause; UE->nr_cellid = msg->nr_cellid; - UE->masterCellGroup = cellGroupConfig; UE->ongoing_reconfiguration = false; UE->measConfig = nr_rrc_get_measconfig(rrc, UE->nr_cellid); activate_srb(UE, 1); @@ -1561,9 +1534,7 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, f1u_dl_gtp_rollback(UE); /* we need the original CellGroupConfig */ - ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); - UE->masterCellGroup = source_ctx->old_cellGroupConfig; - source_ctx->old_cellGroupConfig = NULL; + store_cgc(UE, &source_ctx->old_cgc); /* update to old DU assoc id -- RNTI + secondary DU UE ID further below */ ue_data.du_assoc_id = source_ctx->du->assoc_id; @@ -2449,6 +2420,61 @@ void e1_notify_pdcp_status(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const ngap_drb_s free_e1ap_context_mod_request(&req); } +static NR_MeasGapConfig_t *get_meas_gap_config(const f1ap_du_to_cu_rrc_info_t *du_to_cu_rrc_info) +{ + if (!du_to_cu_rrc_info->meas_gap_config) { + return NULL; + } + + NR_MeasGapConfig_t *measGapConfig = NULL; + asn_dec_rval_t dec_rval = uper_decode_complete(NULL, + &asn_DEF_NR_MeasGapConfig, + (void **)&measGapConfig, + (uint8_t *)du_to_cu_rrc_info->meas_gap_config->buf, + du_to_cu_rrc_info->meas_gap_config->len); + AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "measGapConfig decode error\n"); + return measGapConfig; +} + +/** @brief Detect re-establishment by checking reestablishRLC flags in CellGroupConfig. + * Temporary decode only for detection (not for modification, to preserve transparency). + * DU sets reestablishRLC=true for bearers needing RLC re-establishment (except SRB1). + * @param cgc Encoded CellGroupConfig from DU to CU transfer IE + * @return True if re-establishment is detected, false otherwise */ +static bool rrc_detect_reestablishment(const byte_array_t *cgc) +{ + if (!cgc || !cgc->buf || cgc->len == 0) { + return false; + } + + NR_CellGroupConfig_t *temp_cellGroupConfig = NULL; + asn_dec_rval_t dec_rval = + uper_decode_complete(NULL, &asn_DEF_NR_CellGroupConfig, (void **)&temp_cellGroupConfig, cgc->buf, cgc->len); + if (dec_rval.code != RC_OK || dec_rval.consumed == 0) { + LOG_W(NR_RRC, + "Failed to decode CellGroupConfig for re-establishment detection (code=%d, consumed=%zu)\n", + dec_rval.code, + dec_rval.consumed); + return false; + } + + bool is_reestablishment = false; + // Check if any RLC bearer has reestablishRLC flag set + if (temp_cellGroupConfig->rlc_BearerToAddModList && temp_cellGroupConfig->rlc_BearerToAddModList->list.count > 0) { + for (int i = 0; i < temp_cellGroupConfig->rlc_BearerToAddModList->list.count; i++) { + if (temp_cellGroupConfig->rlc_BearerToAddModList->list.array[i]->reestablishRLC + && *temp_cellGroupConfig->rlc_BearerToAddModList->list.array[i]->reestablishRLC + == NR_RLC_BearerConfig__reestablishRLC_true) { + is_reestablishment = true; + break; + } + } + } + ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, temp_cellGroupConfig); + return is_reestablishment; +} + + static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance_t instance) { f1ap_ue_context_setup_resp_t *resp = &F1AP_UE_CONTEXT_SETUP_RESP(msg_p); @@ -2461,31 +2487,16 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance gNB_RRC_UE_t *UE = &ue_context_p->ue_context; UE->f1_ue_context_active = true; - NR_CellGroupConfig_t *cellGroupConfig = NULL; + // Store the encoded CellGroupConfig for transparent forwarding byte_array_t *cgc = &resp->du_to_cu_rrc_info.cell_group_config; - asn_dec_rval_t dec_rval = - uper_decode_complete(NULL, &asn_DEF_NR_CellGroupConfig, (void **)&cellGroupConfig, (uint8_t *)cgc->buf, cgc->len); - AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n"); + store_cgc(UE, cgc); - if (resp->du_to_cu_rrc_info.meas_gap_config) { - NR_MeasGapConfig_t *measGapConfig = NULL; - asn_dec_rval_t dec_rval_mgc = uper_decode_complete(NULL, - &asn_DEF_NR_MeasGapConfig, - (void **)&measGapConfig, - (uint8_t *)resp->du_to_cu_rrc_info.meas_gap_config->buf, - resp->du_to_cu_rrc_info.meas_gap_config->len); - AssertFatal(dec_rval_mgc.code == RC_OK && dec_rval_mgc.consumed > 0, "measGapConfig decode error\n"); + // Process measurement gap config if present + NR_MeasGapConfig_t *measGapConfig = get_meas_gap_config(&resp->du_to_cu_rrc_info); + if (measGapConfig) { UE->measConfig->measGapConfig = measGapConfig; } - if (UE->masterCellGroup) { - ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); - LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti); - } - UE->masterCellGroup = cellGroupConfig; - if (LOG_DEBUGFLAG(DEBUG_ASN1)) - xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); - if (!IS_SA_MODE(get_softmodem_params())) { rrc_add_nsa_user_resp(rrc, UE, resp); return; @@ -2506,7 +2517,9 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance } if (UE->ho_context == NULL) { - rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE); + // Check if this is a re-establishment scenario by examining the CellGroupConfig + bool is_reestablishment = rrc_detect_reestablishment(cgc); + rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE, is_reestablishment); } else { // case of handover // handling of "target CU" information @@ -2578,7 +2591,7 @@ static void rrc_delete_ue_data(gNB_RRC_UE_t *UE) if (UE->ho_context) nr_rrc_finalize_ho(UE); ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr); - ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); + free_byte_array(UE->mcg); ASN_STRUCT_FREE(asn_DEF_NR_MeasResults, UE->measResults); FREE_AND_ZERO_BYTE_ARRAY(UE->ue_cap_buffer); free_MeasConfig(UE->measConfig); @@ -2641,22 +2654,18 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i } if (resp->du_to_cu_rrc_info) { - NR_CellGroupConfig_t *cellGroupConfig = NULL; byte_array_t *cgc = &resp->du_to_cu_rrc_info->cell_group_config; - asn_dec_rval_t dec_rval = - uper_decode_complete(NULL, &asn_DEF_NR_CellGroupConfig, (void **)&cellGroupConfig, (uint8_t *)cgc->buf, cgc->len); - AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n"); /* hack for interoperability with srsRAN: ignore cell group config if empty */ /* cell group config is empty if buffer length is 2 and content is all 0 */ bool cgc_is_empty = cgc->len == 2 && cgc->buf[0] == 0 && cgc->buf[1] == 0; if (!cgc_is_empty) { - if (UE->masterCellGroup) { - ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); - LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti); - } - UE->masterCellGroup = cellGroupConfig; - rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE); + // Store the encoded CellGroupConfig for transparent forwarding + store_cgc(UE, cgc); + + // Check if this is a re-establishment scenario by examining the CellGroupConfig + bool is_reestablishment = rrc_detect_reestablishment(cgc); + rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE, is_reestablishment); } else { LOG_W(NR_RRC, "hack: UE %d: ignore empty CellGroupConfig in UEContextModificationResponse\n", UE->rrc_ue_id); } @@ -2704,31 +2713,12 @@ static void rrc_CU_process_ue_modification_required(MessageDef *msg_p, instance_ UE->rrc_ue_id, UE->rnti); - NR_CellGroupConfig_t *cellGroupConfig = NULL; - asn_dec_rval_t dec_rval = uper_decode_complete(NULL, - &asn_DEF_NR_CellGroupConfig, - (void **)&cellGroupConfig, - (uint8_t *)required->du_to_cu_rrc_information->cellGroupConfig, - required->du_to_cu_rrc_information->cellGroupConfig_length); - if (dec_rval.code != RC_OK && dec_rval.consumed == 0) { - LOG_E(RRC, "Cell group config decode error, refusing reconfiguration\n"); - f1ap_ue_context_modif_refuse_t refuse = { - .gNB_CU_ue_id = required->gNB_CU_ue_id, - .gNB_DU_ue_id = required->gNB_DU_ue_id, - .cause = F1AP_CAUSE_PROTOCOL, - .cause_value = F1AP_CauseProtocol_transfer_syntax_error, - }; - rrc->mac_rrc.ue_context_modification_refuse(msg_p->ittiMsgHeader.originInstance, &refuse); - return; - } + // Store the encoded CellGroupConfig for transparent forwarding + uint8_t *cgc_buf = required->du_to_cu_rrc_information->cellGroupConfig; + size_t cgc_len = required->du_to_cu_rrc_information->cellGroupConfig_length; - if (UE->masterCellGroup) { - ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); - LOG_I(RRC, "UE %d/RNTI %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rrc_ue_id, UE->rnti); - } - UE->masterCellGroup = cellGroupConfig; - if (LOG_DEBUGFLAG(DEBUG_ASN1)) - xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); + byte_array_t temp_cgc = {.buf = cgc_buf, .len = cgc_len}; + store_cgc(UE, &temp_cgc); /* trigger reconfiguration */ if (!UE->ongoing_reconfiguration) diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index c2495ad879..a2230c1146 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -148,8 +148,8 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, ho_ctx->source->old_du_tunnel_config = drb->du_tunnel_config; } - int result = asn_copy(&asn_DEF_NR_CellGroupConfig, (void **)&ho_ctx->source->old_cellGroupConfig, ue->masterCellGroup); - AssertFatal(result == 0, "error during asn_copy() of CellGroupConfig\n"); + // Store the old CellGroupConfig for reestablishment + ho_ctx->source->old_cgc = copy_byte_array(ue->mcg); } LOG_A(NR_RRC, @@ -305,7 +305,7 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain void nr_rrc_finalize_ho(gNB_RRC_UE_t *ue) { if (ue->ho_context->source) - ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, ue->ho_context->source->old_cellGroupConfig); + free_byte_array(ue->ho_context->source->old_cgc); free_ho_ctx(ue->ho_context); ue->ho_context = NULL; } diff --git a/openair2/RRC/NR/rrc_gNB_mobility.h b/openair2/RRC/NR/rrc_gNB_mobility.h index 54691c2edd..456730060e 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -50,7 +50,7 @@ typedef struct nr_ho_source_cu { rnti_t old_rnti; /// old (source) CellGroupConfig (to send the cellGroupConfig in case of /// reestablishment) - NR_CellGroupConfig_t *old_cellGroupConfig; + byte_array_t old_cgc; /// function pointer to announce the handover cancellation, e.g., /// reestablishment ho_cancel_t ho_cancel; diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index 3e3c462145..2241ca9468 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -386,18 +386,15 @@ void rrc_add_nsa_user(gNB_RRC_INST *rrc, x2ap_ENDC_sgnb_addition_req_t *m, sctp_ free_ue_context_setup_req(&req); } -static NR_RRCReconfiguration_IEs_t *get_default_reconfig(const NR_CellGroupConfig_t *secondaryCellGroup) +static NR_RRCReconfiguration_IEs_t *get_default_reconfig(const byte_array_t cgc) { NR_RRCReconfiguration_IEs_t *reconfig = calloc(1, sizeof(NR_RRCReconfiguration_IEs_t)); AssertFatal(reconfig != NULL, "out of memory\n"); - AssertFatal(secondaryCellGroup != NULL, "secondaryCellGroup is null\n"); + AssertFatal(cgc.buf != NULL, "cgc is null\n"); reconfig->radioBearerConfig = NULL; - - char scg_buffer[1024]; - asn_enc_rval_t enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, NULL, (void *)secondaryCellGroup, scg_buffer, 1024); - AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %jd)!\n", enc_rval.failed_type->name, enc_rval.encoded); - reconfig->secondaryCellGroup = calloc(1, sizeof(*reconfig->secondaryCellGroup)); - OCTET_STRING_fromBuf(reconfig->secondaryCellGroup, (const char *)scg_buffer, (enc_rval.encoded + 7) >> 3); + // Copy the stored CellGroupConfig to the RRCReconfiguration message + reconfig->secondaryCellGroup = calloc_or_fail(1, sizeof(*reconfig->secondaryCellGroup)); + OCTET_STRING_fromBuf(reconfig->secondaryCellGroup, (const char *)cgc.buf, cgc.len); reconfig->measConfig = NULL; reconfig->lateNonCriticalExtension = NULL; reconfig->nonCriticalExtension = NULL; @@ -469,7 +466,7 @@ void rrc_add_nsa_user_resp(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const f1ap_ue_co NR_RRCReconfiguration_t *reconfig = calloc(1, sizeof(NR_RRCReconfiguration_t)); reconfig->rrc_TransactionIdentifier = 0; reconfig->criticalExtensions.present = NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration; - reconfig->criticalExtensions.choice.rrcReconfiguration = get_default_reconfig(UE->masterCellGroup); + reconfig->criticalExtensions.choice.rrcReconfiguration = get_default_reconfig(UE->mcg); NR_CG_Config_t *CG_Config = generate_CG_Config(reconfig, UE->rb_config); ASN_STRUCT_FREE(asn_DEF_NR_RRCReconfiguration, reconfig);