From bd2fc0d237ff12373a7016d6137bebdf6ac97003 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 5 Feb 2026 16:23:51 +0100 Subject: [PATCH] RRC multi-cell: enable multiple cell in F1 Setup Remove the single-cell limit in F1 Setup Request processing and refactor to loop-based cell handling. Move cell validation (PLMN check, neighbor configuration validation, system info extraction), cell creation, SIB encoding, and neighbor labeling inside a loop over all available cells. Update F1 Setup Response to include all cells instead of hardcoding to 1. Add warning log when more than one cell is received. Add proper cleanup with free_f1ap_setup_response() on error paths. Impact: - F1 Setup now accepts and processes multiple cells from a single DU, enabling multi-cell deployments - All cells are validated, created, and included in the setup response --- openair2/RRC/NR/rrc_gNB_du.c | 209 ++++++++++++++++++----------------- 1 file changed, 107 insertions(+), 102 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 5b1be47d5e..f50c7a2c6e 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -324,30 +324,9 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) f1ap_setup_failure_t fail = {.transaction_id = F1AP_get_next_transaction_identifier(0, 0)}; // check: - // - it is one cell // - PLMN and Cell ID matches // - no previous DU with the same ID // else reject - if (req->num_cells_available != 1) { - LOG_E(NR_RRC, "can only handle on DU cell, but gNB_DU %ld has %d\n", req->gNB_DU_id, req->num_cells_available); - fail.cause = F1AP_CauseRadioNetwork_gNB_CU_Cell_Capacity_Exceeded; - rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); - return; - } - f1ap_served_cell_info_t *cell_info = &req->cell[0].info; - if (!rrc_gNB_plmn_matches(rrc, cell_info)) { - LOG_E(NR_RRC, - "PLMN mismatch: CU %03d.%0*d, DU %03d%0*d\n", - rrc->configuration.plmn[0].mcc, - rrc->configuration.plmn[0].mnc_digit_length, - rrc->configuration.plmn[0].mnc, - cell_info->plmn.mcc, - cell_info->plmn.mnc_digit_length, - cell_info->plmn.mnc); - fail.cause = F1AP_CauseRadioNetwork_plmn_not_served_by_the_gNB_CU; - rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); - return; - } nr_rrc_du_container_t *it = NULL; RB_FOREACH(it, rrc_du_tree, &rrc->dus) { if (it->gNB_DU_id == req->gNB_DU_id) { @@ -361,50 +340,7 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) return; } } - - if (rrc->neighbour_cell_configuration && !valid_du_in_neighbour_configs(rrc->neighbour_cell_configuration, cell_info)) { - LOG_E(NR_RRC, "problem with DU %ld in neighbor configuration, rejecting DU\n", req->gNB_DU_id); - f1ap_setup_failure_t fail = {.cause = F1AP_CauseMisc_unspecified}; - rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); - return; - } - - const f1ap_gnb_du_system_info_t *sys_info = req->cell[0].sys_info; - NR_MIB_t *mib = NULL; - NR_SIB1_t *sib1 = NULL; - - if (sys_info != NULL && sys_info->mib != NULL && !(sys_info->sib1 == NULL && IS_SA_MODE(get_softmodem_params()))) { - if (!extract_sys_info(sys_info, &mib, &sib1)) { - LOG_W(NR_RRC, "rejecting DU ID %ld\n", req->gNB_DU_id); - fail.cause = F1AP_CauseProtocol_semantic_error; - rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); - return; - } - } - LOG_I(NR_RRC, "Accepting DU %ld (%s), sending F1 Setup Response\n", req->gNB_DU_id, req->gNB_DU_name); - LOG_I(NR_RRC, "DU uses RRC version %u.%u.%u\n", req->rrc_ver[0], req->rrc_ver[1], req->rrc_ver[2]); - - /* Create cell and add to global tree */ - nr_rrc_cell_container_t *new = calloc_or_fail(1, sizeof(*new)); - new->assoc_id = assoc_id; - cp_f1_served_cell_info_to_cell(new, cell_info); - new->mib = mib; - new->sib1 = sib1; - // Add cell to DU's cell array - nr_rrc_cell_container_t *collision = rrc_add_cell(rrc, new); - if (collision != NULL) { - nr_rrc_du_container_t *existing_du = get_du_by_assoc_id(rrc, collision->assoc_id); - const char *du_name = existing_du ? existing_du->gNB_DU_name : "unknown"; - LOG_E(NR_RRC, - "Cell ID %lu already exists in DU %s (assoc_id %d), rejecting requesting gNB-DU\n", - new->info.cell_id, - du_name, - collision->assoc_id); - rrc_free_cell_container(new); - fail.cause = F1AP_CauseRadioNetwork_cell_not_available; - rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); - return; - } + LOG_I(NR_RRC, "Accepting DU %ld (%s) (RRC version %u.%u.%u)\n", req->gNB_DU_id, req->gNB_DU_name, req->rrc_ver[0], req->rrc_ver[1], req->rrc_ver[2]); // DU is accepted, add it to tree and add cell to DU's cell array nr_rrc_du_container_t *du = calloc_or_fail(1, sizeof(*du)); @@ -418,48 +354,117 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) nr_rrc_du_container_t *du_collision = rrc_add_du(rrc, du); AssertFatal(du_collision == NULL, "rrc_add_du should succeed for new DU (assoc_id %d)", assoc_id); - nr_rrc_cell_container_t *added = rrc_add_cell_to_du(&du->cells, new); - AssertFatal(added != NULL, "Failed to add cell %ld to DU %ld\n", new->info.cell_id, du->gNB_DU_id); - LOG_I(NR_RRC, "DU %ld: Added cell %ld\n", du->gNB_DU_id, new->info.cell_id); - - served_cells_to_activate_t cell = { - .plmn = cell_info->plmn, - .nr_cellid = cell_info->nr_cellid, - .nrpci = cell_info->nr_pci, - .num_SI = 0, - }; - - // Encode CU SIBs and configure setup response with sysinfo - seq_arr_t *sibs = rrc->SIBs; - if (sibs) { - for (int i = 0; i < sibs->size; i++) { - nr_SIBs_t *sib = (nr_SIBs_t *)seq_arr_at(sibs, i); - switch (sib->SIB_type) { - case 2: { - NR_SSB_MTC_t *ssbmtc = get_ssb_mtc(new->mtc); - sib->SIB_size = do_SIB2_NR(&sib->SIB_buffer, ssbmtc); - cell.SI_msg[cell.num_SI].SI_container = sib->SIB_buffer; - cell.SI_msg[cell.num_SI].SI_container_length = sib->SIB_size; - cell.SI_msg[cell.num_SI].SI_type = sib->SIB_type; - cell.num_SI++; - } break; - default: - AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type); - } - } - } - - if (new->mib != NULL &&new->sib1 != NULL) - label_intra_frequency_neighbours(rrc, new); - + // Build F1 Setup Response f1ap_setup_resp_t resp = {.transaction_id = req->transaction_id, - .num_cells_to_activate = 1, - .cells_to_activate = calloc_or_fail(1, sizeof(*resp.cells_to_activate))}; - resp.cells_to_activate[0] = cell; + .num_cells_to_activate = req->num_cells_available, + .cells_to_activate = calloc_or_fail(req->num_cells_available, sizeof(*resp.cells_to_activate))}; int num = read_version(TO_STRING(NR_RRC_VERSION), &resp.rrc_ver[0], &resp.rrc_ver[1], &resp.rrc_ver[2]); AssertFatal(num == 3, "could not read RRC version string %s\n", TO_STRING(NR_RRC_VERSION)); if (rrc->node_name != NULL) resp.gNB_CU_name = strdup(rrc->node_name); + + if (req->num_cells_available > 1) { + LOG_W(NR_RRC, "Received F1 Setup Request with %u cells, only one cell is supported\n", req->num_cells_available); + } + for (int i = 0; i < req->num_cells_available; i++) { + f1ap_served_cell_info_t *cell_info = &req->cell[i].info; + if (!rrc_gNB_plmn_matches(rrc, cell_info)) { + LOG_E(NR_RRC, + "PLMN mismatch: CU %03d.%0*d, DU %03d%0*d\n", + rrc->configuration.plmn[0].mcc, + rrc->configuration.plmn[0].mnc_digit_length, + rrc->configuration.plmn[0].mnc, + cell_info->plmn.mcc, + cell_info->plmn.mnc_digit_length, + cell_info->plmn.mnc); + fail.cause = F1AP_CauseRadioNetwork_plmn_not_served_by_the_gNB_CU; + rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); + free_f1ap_setup_response(&resp); + return; + } + + if (rrc->neighbour_cell_configuration && !valid_du_in_neighbour_configs(rrc->neighbour_cell_configuration, cell_info)) { + LOG_E(NR_RRC, "problem with DU %ld in neighbor configuration, rejecting DU\n", req->gNB_DU_id); + f1ap_setup_failure_t fail = {.cause = F1AP_CauseMisc_unspecified}; + rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); + free_f1ap_setup_response(&resp); + return; + } + + const f1ap_gnb_du_system_info_t *sys_info = req->cell[i].sys_info; + NR_MIB_t *mib = NULL; + NR_SIB1_t *sib1 = NULL; + if (sys_info != NULL && sys_info->mib != NULL && !(sys_info->sib1 == NULL && IS_SA_MODE(get_softmodem_params()))) { + if (!extract_sys_info(sys_info, &mib, &sib1)) { + LOG_W(NR_RRC, "rejecting DU ID %ld\n", req->gNB_DU_id); + fail.cause = F1AP_CauseProtocol_semantic_error; + rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); + free_f1ap_setup_response(&resp); + return; + } + } + + /* Create cell and add to global tree */ + nr_rrc_cell_container_t *new = calloc_or_fail(1, sizeof(*new)); + new->assoc_id = assoc_id; + cp_f1_served_cell_info_to_cell(new, cell_info); + new->mib = mib; + new->sib1 = sib1; + // Add cell to DU's cell array + nr_rrc_cell_container_t *collision = rrc_add_cell(rrc, new); + if (collision != NULL) { + nr_rrc_du_container_t *existing_du = get_du_by_assoc_id(rrc, collision->assoc_id); + const char *du_name = existing_du ? existing_du->gNB_DU_name : "unknown"; + LOG_E(NR_RRC, + "Cell ID %lu already exists in DU %s (assoc_id %d), rejecting requesting gNB-DU\n", + new->info.cell_id, + du_name, + collision->assoc_id); + rrc_free_cell_container(new); + fail.cause = F1AP_CauseRadioNetwork_cell_not_available; + rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); + free_f1ap_setup_response(&resp); + return; + } + + nr_rrc_cell_container_t *added = rrc_add_cell_to_du(&du->cells, new); + AssertFatal(added != NULL, "Failed to add cell %ld to DU %ld\n", new->info.cell_id, du->gNB_DU_id); + LOG_I(NR_RRC, "DU %ld: Added cell %ld\n", du->gNB_DU_id, new->info.cell_id); + + served_cells_to_activate_t cell = { + .plmn = cell_info->plmn, + .nr_cellid = cell_info->nr_cellid, + .nrpci = cell_info->nr_pci, + .num_SI = 0, + }; + + // Encode CU SIBs and configure setup response with sysinfo + seq_arr_t *sibs = rrc->SIBs; + if (sibs) { + for (int i = 0; i < sibs->size; i++) { + nr_SIBs_t *sib = (nr_SIBs_t *)seq_arr_at(sibs, i); + switch (sib->SIB_type) { + case 2: { + NR_SSB_MTC_t *ssbmtc = get_ssb_mtc(new->mtc); + sib->SIB_size = do_SIB2_NR(&sib->SIB_buffer, ssbmtc); + cell.SI_msg[cell.num_SI].SI_container = sib->SIB_buffer; + cell.SI_msg[cell.num_SI].SI_container_length = sib->SIB_size; + cell.SI_msg[cell.num_SI].SI_type = sib->SIB_type; + cell.num_SI++; + } break; + default: + AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type); + } + } + } + + if (new->mib != NULL &&new->sib1 != NULL) + label_intra_frequency_neighbours(rrc, new); + + resp.cells_to_activate[i] = cell; + } + + LOG_I(NR_RRC, "DU %ld (%s): sending F1 Setup Response\n", req->gNB_DU_id, req->gNB_DU_name); rrc->mac_rrc.f1_setup_response(assoc_id, &resp); free_f1ap_setup_response(&resp);