diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 04e2a3d02f..6c642cb582 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1072,6 +1072,20 @@ static const char *get_reestab_cause(NR_ReestablishmentCause_t c) return "UNKNOWN Failure (ASN.1 decoder error?)"; } +static rrc_gNB_ue_context_t *rrc_gNB_get_ue_context_source_cell(gNB_RRC_INST *rrc_instance_pP, long pci, rnti_t rntiP) +{ + rrc_gNB_ue_context_t *ue_context_p; + RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc_instance_pP->rrc_ue_head) { + gNB_RRC_UE_t *ue = &ue_context_p->ue_context; + if (!ue->ho_context || !ue->ho_context->source) + continue; + nr_ho_source_cu_t *source_ctx = ue->ho_context->source; + if (source_ctx->old_rnti == rntiP && source_ctx->du->setup_req->cell[0].info.nr_pci == pci) + return ue_context_p; + } + return NULL; +} + static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, sctp_assoc_t assoc_id, const NR_RRCReestablishmentRequest_IEs_t *req, @@ -1082,7 +1096,12 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, const long physCellId = req->ue_Identity.physCellId; long ngap_cause = NGAP_CAUSE_RADIO_NETWORK_UNSPECIFIED; /* cause in case of NGAP release req */ rrc_gNB_ue_context_t *ue_context_p = NULL; - LOG_I(NR_RRC, "UE %04x physCellId %ld NR_RRCReestablishmentRequest cause %s\n", msg->crnti, physCellId, scause); + LOG_I(NR_RRC, + "Reestablishment RNTI %04x req C-RNTI %04lx physCellId %ld cause %s\n", + msg->crnti, + req->ue_Identity.c_RNTI, + physCellId, + scause); const nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, assoc_id); if (du == NULL) { @@ -1117,13 +1136,47 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, rnti_t old_rnti = req->ue_Identity.c_RNTI; ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, assoc_id, old_rnti); if (ue_context_p == NULL) { - LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n"); - goto fallback_rrc_setup; + ue_context_p = rrc_gNB_get_ue_context_source_cell(rrc, physCellId, old_rnti); + if (ue_context_p == NULL) { + LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n"); + goto fallback_rrc_setup; + } } gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + /* should check phys cell ID to identify the correct cell */ const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; - if (physCellId != cell_info->nr_pci) { + const f1ap_served_cell_info_t *previous_cell_info = get_cell_information_by_phycellId(physCellId); + + nr_ho_source_cu_t *source_ctx = UE->ho_context ? UE->ho_context->source : NULL; + DevAssert(!source_ctx || source_ctx->du->setup_req->num_cells_available == 1); + bool ho_reestab_on_source = source_ctx && previous_cell_info->nr_cellid == source_ctx->du->setup_req->cell[0].info.nr_cellid; + + if (ho_reestab_on_source) { + /* the UE came back on the source DU while doing handover, release at + * target DU and and update the association to the initial DU one */ + LOG_W(NR_RRC, "handover for UE %d/RNTI %04x failed, rollback to original cell\n", UE->rrc_ue_id, UE->rnti); + + // find the transaction of handover (the corresponding reconfig) and abort it + for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) { + if (UE->xids[i] == RRC_DEDICATED_RECONF) + UE->xids[i] = RRC_ACTION_NONE; + } + + source_ctx->ho_cancel(rrc, 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; + + /* update to old DU assoc id -- RNTI + secondary DU UE ID further below */ + f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id); + ue_data.du_assoc_id = source_ctx->du->assoc_id; + cu_remove_f1_ue_data(UE->rrc_ue_id); + cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data); + nr_rrc_finalize_ho(UE); + } else if (physCellId != cell_info->nr_pci) { /* UE was moving from previous cell so quickly that RRCReestablishment for previous cell was received in this cell */ LOG_I(NR_RRC, "RRC Reestablishment Request from different physCellId (%ld) than current physCellId (%d), fallback to RRC setup\n", diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index 6f6a034aa3..137e7010c2 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -115,6 +115,9 @@ static void nr_initiate_handover(const gNB_RRC_INST *rrc, ho_ctx->source->du = source_du; ho_ctx->source->du_ue_id = ue_data.secondary_ue; ho_ctx->source->old_rnti = ue->rnti; + + 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"); } ue->ho_context = ho_ctx; LOG_A(NR_RRC, @@ -289,6 +292,20 @@ static void nr_rrc_f1_ho_complete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) LOG_I(NR_RRC, "UE %d Handover: trigger release on DU assoc_id %d\n", UE->rrc_ue_id, source_ctx->du->assoc_id); } +static void nr_rrc_cancel_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE) +{ + DevAssert(UE->ho_context != NULL); + nr_ho_target_cu_t *target_ctx = UE->ho_context->target; + DevAssert(target_ctx != NULL); + f1ap_ue_context_release_cmd_t cmd = { + .gNB_CU_ue_id = UE->rrc_ue_id, + .gNB_DU_ue_id = target_ctx->du_ue_id, + .cause = F1AP_CAUSE_RADIO_NETWORK, // better + .cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure + .srb_id = DCCH, + }; + rrc->mac_rrc.ue_context_release_command(target_ctx->du->assoc_id, &cmd); +} void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_container_t *source_du, nr_rrc_du_container_t *target_du) { @@ -306,12 +323,14 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue, nr_rrc_du_contain // handover preparation information ho_req_ack_t ack = nr_rrc_f1_ho_acknowledge; ho_success_t success = nr_rrc_f1_ho_complete; - ho_cancel_t cancel = NULL; + ho_cancel_t cancel = nr_rrc_cancel_f1_ho; nr_initiate_handover(rrc, ue, source_du, target_du, buf, size, ack, success, cancel); } 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_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 1078cffe4e..5046176c1d 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.h +++ b/openair2/RRC/NR/rrc_gNB_mobility.h @@ -28,6 +28,8 @@ typedef struct gNB_RRC_INST_s gNB_RRC_INST; typedef struct gNB_RRC_UE_s gNB_RRC_UE_t; 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 struct nr_ho_source_cu { /// pointer to the (source) DU structure @@ -38,6 +40,9 @@ typedef struct nr_ho_source_cu { uint32_t du_ue_id; /// old (source) RNTI (to recognize a UE during reestablishment) rnti_t old_rnti; + /// old (source) CellGroupConfig (to send the cellGroupConfig in case of + /// reestablishment) + NR_CellGroupConfig_t *old_cellGroupConfig; /// function pointer to announce the handover cancellation, e.g., /// reestablishment ho_cancel_t ho_cancel;