Cleanup Cmake: remove some double compilation
Harmonize some cmake code to avoid double compilation, notably around
polar and smallblock. In develop, we just add their sources to other
targets, whereas now we define STATIC libraries (further changed to
shared library in !3856) to avoid double compilation.
With this command line:
cmake .. -GNinja -DENABLE_TESTS=ON && ninja
shows that before we compile 12631 files, where after it is 12583
there is additional supporting cleanup (move code, remove headers,
types, ...).
there is more harmonization to do, I limited myself to some easy wins.
For instance, we do the same with PDCP/SDAP, and this should be
refactored into STATIC libraries as well.
RRC: Multi-cell handling architecture
This MR implements a foundational multi-cell handling architecture for
RRC by separating DU management from cell management and establishing a
hierarchical cell organization within each DU container. The changes
enable proper multi-cell support where each DU can serve multiple cells,
addressing the previous limitation where DU and cell management were
tightly coupled with the assumption of one cell per DU.
Key Changes:
- Data Structure Separation: Separated for DU containers (DU-specific
info) from cell containers (cell-specific data)
- Global DU and Cell Trees: Cells are now stored in global RB trees in
RRC, similarly to DUs
- Du-specific cell seq_arr: within each DU container (du->cells) is
stored a seq_arr of cells belonging to the DU
- UE Cell Association Framework: Introduced serving_cells array to track
PCell and SCells for each UE with servCellIndex indexing
- Cell Management Library: Extracted cell management functions into
rrc_cell_management.c/h for better testability and maintainability
Other relevant changes:
- F1AP Refactoring: cell list definitions, heap allocation to prevent
stack overflow
- 5G-S-TMSI Helpers: Common helper functions for 3GPP TS 23.003
compliant construction
- Integration Tests: Comprehensive test suite for DU lookup, cell
lookup, and UE cell association
- Cell Duplication Checks: Validation in F1 DU configuration update to
prevent duplicate cells
- Documentation: Updated RRC developer documentation with multi-cell
architecture details
Validated with RFsim in re-establishment and handover scenarios.
openair2/COMMON/rrc_messages_types.h has mostly 4G types. The only 5G
(gNB) RRC type is gNB_RrcConfigurationReq, which we can move to a header
in the corresponding directory (where it is used).
This allows to separate 5G gNB RRC module from the 4G types in
rrc_messages_types.h in the next commit.
reversing bits is not NR specific. Think of it like this: LTE code might
use reverse_bits*() as well, but in the old location, it would pull in a
bunch of NR dependencies that are superfluous.
Remove VCD signal dumper from NR code
We don't test VCD in 5G NR code (and haven't tested it in 4G for years).
Remove the VCD traces in 5G code to avoid any ambiguity. 4G code remains
in place, as well as the tools to analyze existing traces.
In this case, there are a number of linker errors:
/usr/bin/ld: libL2_NR.a(rrc_gNB.c.o): in function `nr_rrc_transfer_protected_rrc_message':
/home/richie/oai/openair2/RRC/NR/rrc_gNB.c:330:(.text+0x6f6): undefined reference to `signal_rrc_msg'
However, nr_srssim does not need RRC, while it is being linked in. Thus,
link without nr_rrc and L2. For the remaining two linker problems:
- provide a definition of NR_IF_Module_init() as in most other
simulators
- move rrc_get_max_nr_csrs() to nr_common.c. It is already declared in
the corresponding header, and itself does not have anything to do with
RRC radio configuration, therefore it does not belong to
nr_radio_config.c
RRC QoS refactor: PDU Session Modify support, SDAP fixes, and code improvements
The goal of this MR is to prepare the RRC/NGAP layer for the multi-QoS
support. The MR:
- enables PDU Session Resource Modify support in RRC/NGAP/E1AP
- fixes SDAP header mapping
- enables SDAP by default
- refactors QoS flow handling in RRC/NGAP
- improves error handling, and code maintainability in RRC/NGAP
Changes:
- NGAP PDU Session Modify:
* add NGAP PDU Session Resource Modify Request/Response handling with
proper error reporting for unrecognized sessions and AMF UE NGAP ID
validation
- E1AP Bearer Context Modification:
* add DRB To Remove List IE to support DRB release during modify
* rename numQosFlow2Setup to numQosFlowsMod in DRB To Modify List
- RRC QoS flow handling:
* enable multi-QoS flow support per DRB
* add utilities to map NGAP QoS parameters to E1AP
* filter QoS flows by DRB ID
- SDAP configuration:
* Fix SDAP header UL/DL mapping between internal representation and
ASN.1 enum (invert mapping)
* change SDAP header fields from long to bool for clarity
- F1 DRB setup refactoring:
* Extract shared helpers for F1 DRB setup list filling
* reduce duplication between E1 response and direct DRB setup paths
- AMBR management:
* Store AMBR in UE context
* validate AMBR is set before bearer setup
- Configuration:
* remove unused drbs configuration parameter
* enable SDAP by default (change default from false to true)
- Change gNB RRC enable_sdap default to true
- Update RRC and RUNMODEM docs to reflect new default behavior
- Clean up CI gNB configs by removing redundant enable_sdap=1
- Keep explicit SDAP disable config for NFAPI gNB VNF
According to TS 38.413:
* AMBR is conditional to PDU Session Resource to setup in the Initial UE
Context Setup procedure
* The UE Aggregate Maximum Bit Rate IE should be sent to the NG-RAN node
if the AMF has not sent it previously. If it is included in the PDU
SESSION RESOURCE SETUP REQUEST message, the NG-RAN node shall store
it in the UE context
* Range is INTEGER [0..4,000,000,000,000]
To this purpose, changes in the commit:
* Init AMBR to out of range value when creating UE context
* Store AMBR in UE context whenever is received
* Assert if not set at the time of Bearer Setup (should be known at that stage)
Extract common code from `fill_drb_to_be_setup_from_e1_resp` and
`rrc_fill_f1_drb_to_setup` into reusable helper functions to reduce
duplication and improve maintainability. This refactoring enables
multi-QoS flow handling per DRB and prepares helpers for upcoming
PDU Session Modify changes.
Impact:
- Enables multi-QoS flow support: `rrc_fill_f1_drb_to_setup` now
collects all QFIs mapped to a DRB instead of only the first one
- Reduces code duplication between E1 response and direct DRB setup
code paths
- Improves maintainability by centralizing F1 DRB setup logic
Major changes:
- Add `fill_f1_drb_info_nr`: fills DRB information IE including QoS
flows from QFI list
- Add `fill_f1_drb_to_be_setup`: creates F1 DRB to setup structure
from RRC DRB, PDU session, and QFI list
- Add `fill_f1_drbs_from_e1`: processes DRBs from E1 response and
extracts QFIs from drb_config->qosFlows
- Refactor `fill_drb_to_be_setup_from_e1_resp` to use
`fill_f1_drbs_from_e1`
- Refactor `rrc_fill_f1_drb_to_setup` to collect all QFIs mapped to
each DRB and use `fill_f1_drb_to_be_setup`
TS 38.413 8.2.3 PDU Session Modify:
> For each PDU session which failed to be modified, the PDU Session Resource Modify
> Unsuccessful Transfer IE shall be included containing the failure cause
> If the NG-RAN node receives a PDU SESSION RESOURCE MODIFY REQUEST message containing
> some PDU Session ID IEs (in the PDU Session Resource Modify Request List IE) that the
> NG-RAN node does not recognize, the NG-RAN node shall report the corresponding invalid
> PDU sessions as failed in the PDU SESSION RESOURCE MODIFY RESPONSE message
> with an appropriate cause value
Changes in the commit:
* add rrc_gNB_ngap_pdusession_mod_failure helper function to handle
abnormal conditions and send NGAP response with (all) failed PDU sessions
* improve error handling: send NGAP response instead of silently returning -1
when UE context lookup fails (unknown RAN UE NGAP ID)
* add AMF UE NGAP ID validation: check stored AMF UE NGAP ID matches request
and send failure response if mismatch (per 3GPP TS 38.413)
* handle unrecognized PDU Session IDs per 3GPP TS 38.413 8.2.3.4: add to
failed list to be sent in the reponse message
* simplify failure path: reuse rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP
instead of manually constructing response message
* remove failed PDU sessions from RRC list in rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP
* remove redundant code and improve code organization
* add bounds checking
* improve function signature
This commit refactors the rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP function
to improve code clarity, fix bugs, and ensure compliance with 3GPP TS 38.413
section 9.2.1.6.
The main improvement is ensuring the PDU Session Resource Modify Response message
is always sent with mandatory IEs, even when there are no PDU sessions to report
in the optional lists.
Key changes:
- Initialize message structure parameters to 0 by leveraging structure
initialization, ensuring clean state
- Replace local counter variables (pdu_sessions_done, pdu_sessions_failed)
with direct access to resp->nb_of_pdusessions and resp->nb_of_pdusessions_failed,
eliminating redundant state tracking and improving code clarity
- Remove conditional message sending: always send the response message
(per 3GPP TS 38.413 9.2.1.6, PDUSessionResourceModifyResponse must always
be sent with mandatory IEs: AMF-UE-NGAP-ID and RAN-UE-NGAP-ID, even when
optional lists are empty)
- Fix duplicate assignment: remove redundant p->pdusession_id assignment
(was assigned twice in the PDU_SESSION_STATUS_DONE case)
- Improve failed PDU session handling: use direct structure assignment
(failed->cause = session->cause) instead of field-by-field copying,
making the code more maintainable
- Remove failed PDU session upon response
- Enhance logging: improve log messages for better debugging and clarity
- Add function documentation comment
Introduce fill_e1_qos_flow_to_setup to translate pdusession_level_qos_parameter_t (NGAP)
into qos_flow_to_setup_t (E1), including ARP and QoS characteristics (dynamic/non-dynamic).
Refactor fill_e1_drb_to_setup to use the new helper for building QoS flows.
* Only include QoS flows whose drb_id matches the target DRB.
* Add DevAssert(drb_ngran.numQosFlow2Setup > 0) to ensure at least one QoS flow is added.
This change centralizes and simplifies QoS mapping logic between NGAP inputs
and E1 DRB configuration. It ensures correctness by associating QoS flows
to the appropriate DRB.
Major changes:
- Extract QoS flow mapping logic into fill_e1_qos_flow_to_setup helper function
- Filter QoS flows by matching drb_id before adding to DRB setup list
- Replace inline mapping code with helper function call
- Add assertion to verify at least one QoS flow is added per DRB
This commit fixes two related issues:
1. SDAP Header Mapping Bug (cucp_cuup_handler.c):
The original code incorrectly mapped SDAP header configuration from
internal E1AP representation to ASN.1 enum values. The internal
representation (`bearer_context_sdap_config_t`) uses true=present/false=absent,
while ASN.1 enum (`E1AP_SDAP_Configuration_t`) uses
0=present/1=absent. The fix correctly inverts the mapping:
- Internal true (1) → ASN.1 0 (present)
- Internal false (0) → ASN.1 1 (absent)
The commit also clarifies internal representation in the stack.
2. Test Overhead Adjustment (nr-cuup-load-test.c):
The change in (1) led to a failure in nr_cuup_functional_test
with assertion: "size % 4 == 0" in recv_ng()
- Changed UL packet overhead from 3 bytes to 2 bytes in sender_thread()
- Before the SDAP fix, the test incorrectly accounted for 3 bytes:
* 2 bytes PDCP header (correct)
* 1 byte SDAP header (incorrectly included)
- After the SDAP fix, SDAP headers are correctly set to absent, so
only 2 bytes (PDCP header) are needed
This ensures the test sends packets with the correct header size,
matching what CU-UP expects after the SDAP configuration fix.
The load test does not enable SDAP headers, therefore the original 3-byte
overhead was possibly a workaround for the SDAP header bug.
With the bug fixed, the test correctly uses 2-byte overhead (PDCP header).
This commit refactors the PDU Session Resource Modify Response message
structures to improve naming, documentation, and compliance with 3GPP TS 38.413 9.2.1.6.
Changes in NGAP:
- Rename qos_flow_tobe_modified_t to qos_flow_addmod_response_item_t to better
reflect its purpose as a QoS Flow Add or Modify Response Item IE (as per specs)
- Add mandatory AMF UE NGAP ID field to ngap_pdusession_modify_resp_t structure
- Optimize field types: change nb_of_pdusessions and nb_of_pdusessions_failed
from uint16_t to uint8_t (NGAP_MAX_PDU_SESSION limit) (ref 3GPP TS 38.413, 9.2.1.6)
- Update PDU Session Modify Response encoder to use amf_ue_ngap_id from the
message structure (pdusession_modify_resp_p->amf_ue_ngap_id) instead of
retrieving it from UE context (ue_context_p->amf_ue_ngap_id)
- Add comprehensive comments with 3GPP TS 38.413 section references throughout
Changes in RRC/NGAP:
- Populate amf_ue_ngap_id field in PDU Session Modify Response message from
UE context
- Update type reference to qos_flow_addmod_response_item_t
A DRB To Modify Item contains a Flow Mapping Information,
which overrides previous mapping information. For consistency,
rename the count member to reflect a QoS Flow modification,
since it is not limited to setup only.
- Rename DRB flow mapping counters to `numQosFlowsMod`
for a DRB To Modify List
- Adjust encode/decode/equality + E1AP tests
Add DRB To Remove List IE to PDU Session To Modify list, which is sent
in the Bearer Context Modification request. This allows the CUCP to
inform the CUUP about DRBs to be released in a specific PDU session
during a MODIFY procedure.
Changes:
* Add struct definition
* Add enc/dec/eq/cp func
* Add test
- Delete drbs field from gNB_RrcConfigurationReq and its use in RCconfig_NRRRC()
- Remove GNB_CONFIG_STRING_DRBS, help text, index, and GNBPARAMS_CHECK entry from gnb_paramdef.h
- Drop DU-side verification and DRB count logging in gnb_config.c
This commit adds documentation comments and logging to improve
code readability and debugging capabilities for the multi-cell
architecture.
Changes:
- gnb_config.c: Add logging when configuring DU with cell ID and PCI
- rrc_gNB_du.c: Add documentation comment for ssb_arfcn_mtc()
- rrc_gNB_du.c: Add documentation comment for dump_du_info()
After introduction of new cell management design, these helper functions
are now only used internally within their respective modules and do not
need to be part of the public API. They are made static and their
declarations are removed from public headers.
Changes:
- f1ap_interface_management.c: Make free_f1ap_cell() and
copy_f1ap_served_cell_info() static
- f1ap_interface_management.h: Remove declarations for the above functions
- rrc_gNB_du.c: Make get_dl_band() and get_ssb_scs() static
- rrc_gNB_du.h: Remove declarations for get_dl_band() and get_ssb_scs()
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
Finalize the migration from single-cell-per-DU to the multi-cell
architecture: remove legacy DU/cell fields and refactor RRC procedures
to use cell containers and UE serving cells instead of DU-based lookups.
Data structure changes:
- Remove nr_cellid from gNB_RRC_UE_t; cell identity comes from the UE's
serving_cells (PCell entry via ue_get_pcell_entry(ue)). UE_LOG_ARGS
and LOG_UE_* macros take a PCell entry pointer for null-safe cell_id
in logs; all call sites pass ue_get_pcell_entry(ue).
- Remove setup_req, mib, sib1, mtc from nr_rrc_du_container_t (held in
cell containers); remove nr_cellid from gNB_RRC_INST and its
assignment in gnb_config.c.
Function and signature updates:
- get_ssb_arfcn(): take cell container instead of DU.
- label_intra_frequency_neighbours(): drop du parameter, use cell only.
- Handover: Handover context (source and target) now stores cell
pointers (source->cell, target->cell) instead of DU pointers. When
the context is created (N2: on Handover Request; F1: in
nr_rrc_trigger_f1_ho), the target cell is set in ho_context->target->cell
and, for F1, the source cell in ho_context->source->cell.
nr_initiate_handover() takes only source_cell and reads the target
from ho_ctx->target->cell, so it no longer takes a target parameter.
The target is known at handover creation time, while the UE’s
serving_cells are only updated after F1 UE Context Setup Response.
- nr_rrc_trigger_f1_ho() and the N2 handover path pass cell pointers
and set ho_context->target->cell (and for F1, source->cell) when
the handover is triggered.
Procedure refactoring:
- Lookups: replace get_du_by_cell_id() with get_cell_by_cell_id()/
get_cell_by_pci(); remove get_du_by_cell_id() and
get_cell_information_by_phycellId().
Rationale: cell-specific data (MIB, SIB1, mtc, info) now lives in
cell containers, and a DU can host multiple cells. Callers need the
cell (to use cell->info, cell->mtc, etc.), not the DU. Looking up
by cell_id therefore returns the cell container
(get_cell_by_cell_id()); when the DU is needed, use
cell->assoc_id and get_du_by_assoc_id(). Looking up by PCI is
scoped (e.g. get_cell_by_pci_for_du() for a given DU, or via the
UE’s serving cells) because PCI is not globally unique.
- Setup/Reestablishment: resolve cell, register as PCell via
rrc_add_ue_serving_cell(); reestablishment resolves cell by PCI
(rrc_get_cell_by_pci_for_du), then rrc_remove_ue_scells_from_du +
rrc_add_ue_serving_cell; validate_ue_cell_association() after update.
- Measurement config: use cell->info (SCS/band) and cell->mtc.
- Handover: use cell containers end-to-end; after UE context
modification response call nr_rrc_update_cell_assoc_after_ho().
- NGAP: cell_id and TAC from UE PCell via rrc_get_pcell_for_ue();
Handover Notify: get cell for message content, use get_du_for_ue() for
DU existence check; paging resolves UE by 5G-S-TMSI-Part1 and uses that
UE's PCell (rrc_get_pcell_for_ue) for F1AP_PAGING_IND nr_cellid.
Performance and robustness:
- Use rrc_get_pcell_for_ue() when cell data is needed; use
get_du_for_ue() when only the DU is needed (F1/N2 HO trigger,
Handover Notify DU check) for efficiency.
- Reestablishment: remove old PCell before adding new; error handling
for PCell add and validation.
Code cleanup:
- F1 setup: stop copying/storing setup request in DU container; F1
disconnect only frees cell containers via rrc_free_cell_container().
- dump_du_info(): iterate du->cells and show all cells per DU.
- telnetsrv_ci.c: use du->gNB_DU_id directly and fix format (%ld).
- UeContextSetupRequest servCellIndex set to RRC_PCELL_INDEX; remove
servCellIndex from F1AP UE context modification request.
In rrc_gNB_NGAP.c, rrc_gNB_process_Handover_Request() is updated to
resolve the target by cell instead of by DU, then resolve the DU from
the cell's assoc_id. This aligns Handover Request with the new
cell-centric model and cell management APIs.
- Resolve target with get_cell_by_cell_id(&rrc->cells, msg->nr_cell_id)
instead of get_du_by_cell_id(); on failure, log "no cell found" and
send NGAP handover failure.
- Resolve DU with get_du_by_assoc_id(rrc, cell->assoc_id); add a
failure path (log, NGAP handover failure, return -1) when no DU
exists for the cell's assoc_id.
- Use cell->info (nr_rrc_cell_info_t) for logging and for key
derivation (nr_derive_key_ng_ran_star); use du->assoc_id
for UE context creation instead of du->setup_req->cell[0].info.
get_ssb_arfcn(du) is unchanged and still takes the DU; other DU-based
lookups are kept where still required.
Migrate F1 DU configuration update procedures in openair2/RRC/NR/rrc_gNB_du.c
to use the cell management API instead of accessing cell data through
setup_req, completing the transition to the new multi-cell architecture.
1. update_cell_info:
- Refactored to use cell management API: takes RRC instance, old cell_id,
and new cell info; finds cell via get_cell_by_cell_id() internally.
- Returns cell container pointer (or NULL on failure) instead of void.
- When cell_id changes: removes cell from RB tree (old key), updates via
cp_f1_served_cell_info_to_cell(), re-inserts (new key); validates no
duplicate cell_id/PCI before allowing change.
- Frees old MTC when new measurement timing config is present.
- Encapsulates cell lookup and update logic.
2. rrc_gNB_process_f1_du_configuration_update:
- Uses update_cell_info() for cell modify; validates cell belongs to DU
(assoc_id) after update.
- Duplicate checking via get_cell_by_cell_id() and
rrc_get_cell_by_pci_for_du()
- For cells to add: validates duplicate cell_id globally and duplicate
PCI within DU; rejects with logging.
- MIB/SIB1 updated in cell container (cell->mib, cell->sib1); uses
du->gNB_DU_id; passes cell to label_intra_frequency_neighbours();
removes direct setup_req->cell[0] access.
Backward compatibility: setup_req remains until migration is completed;
cell-specific data is managed in the cell container and accessed via
the cell management API. Improves separation of concerns and enables
proper multi-cell support.
Refactor F1 setup processing to create and manage cell containers during DU
initialization, establishing the foundation for multi-cell architecture where
cells are managed independently from DUs.
Why: Cell information is currently tied to F1AP structures (f1ap_served_cell_info_t),
which complicates multi-cell support. Introducing cell containers at F1 setup
decouples RRC cell management from F1AP and allows cells to be managed
independently while keeping existing code paths working.
Key changes:
1. Cell container creation (cp_f1_served_cell_info_to_cell):
New helper copying F1AP served cell info into nr_rrc_cell_container_t (cell_id,
pci, plmn, tac, TDD/FDD frequency info). Extracts and stores MTC in the cell.
2. Function signature updates:
get_cell_neighbour_list() and label_intra_frequency_neighbours() now take
nr_rrc_cell_container_t* and use cell->info; improved logging (cell_id, PCI,
SSB ARFCN).
3. valid_du_in_neighbour_configs:
Removed ssb_arfcn parameter; MTC is extracted and freed inside the function.
4. F1 Setup (rrc_gNB_process_f1_setup_req):
Initialize DU cell tree (RB_INIT(&du->cells)). Populate DU-only fields
(gNB_DU_id, gNB_DU_name, rrc_ver).
Flow:
- create cell with cp_f1_served_cell_info_to_cell
- add to global tree via rrc_add_cell()
- add DU via rrc_add_du()
- attach cell to DU with rrc_add_cell_to_du().
Store MIB/SIB1/MTC in cell.
Use cell container for neighbour labeling and SIB encoding.
5. Duplicate cell detection:
Use rrc_add_cell() for global cell tree insert; collision return indicates
duplicate cell_id. Clearer error messages for Cell ID vs PCI conflicts.
Use F1AP_CauseRadioNetwork_cell_not_available for cell conflicts.
6. DU configuration update:
Use get_cell_by_cell_id() to find cell, check assoc_id, pass cell container
to label_intra_frequency_neighbours().
7. DU disconnection:
Use rrc_cleanup_du() to remove cells from global tree and free resources;
logging uses DU fields (gNB_DU_id, gNB_DU_name).
8. Global cell tree:
Initialize rrc->cells in openair_rrc_gNB_configuration().
Backward compatibility: setup_req still built via cp_f1ap_setup_request();
du->mib, du->sib1, du->mtc remain set (shared with cell container).
Add integration tests for RRC cell and DU management and UE serving-cell
logic so that multi-cell behaviour and regressions can be validated
without running the full stack.
* test_du_lookup() – DU container lookup by assoc_id:
- Multiple DU creation and insertion into RRC DU tree
- Lookup of existing DUs by assoc_id
- Non-existing and invalid assoc_ids
- Cleanup of DU containers and cells
* test_cell_lookup() – Cell management and UE association:
- Cell lookup by cell_id and PCI (global and per-DU)
- DU-specific lookup: rrc_get_cell_for_du(), rrc_get_cell_by_pci_for_du()
- Duplicate cell and PCI collision handling (including PCI reuse across
DUs and rejection of duplicate PCI within same DU)
- Cell–DU association (add/remove cells from DU containers)
- UE serving cell: add PCell/SCells, get by ID, get PCell, max serving
cells limit, single PCell enforcement
- validate_ue_cell_association(), rrc_remove_ue_scells_from_du()
- Edge cases and error handling
Add openair2/RRC/NR/tests/rrc_cell_management_test.c and extend
openair2/RRC/NR/tests/CMakeLists.txt to build and register the test.
Add functions to manage UE serving cells so the RRC can track which cells
a UE is using (PCell and SCells) for multi-cell UE context and future
SCell management.
- rrc_cell_management.c / rrc_cell_management.h:
* rrc_get_ue_serving_cell_by_id(): return serving cell by servCellIndex
(O(k) linear search, k ≤ 32).
* rrc_add_ue_serving_cell(ue, cell, serving_cell_id): add entry to UE
serving_cells with validation: reject duplicate servCellIndex with
different nci, enforce max 32 serving cells (TS 38.331), keep PCell
at index 0 for O(1) access; return existing entry if same
servCellIndex and nci.
* rrc_remove_ue_scells_from_du(ue, assoc_id): remove all serving cells
for the given DU (handover/DU disconnect); use stored assoc_id for
O(k) comparison.
* ue_get_pcell_entry(): return PCell entry (first element, O(1)).
* rrc_get_pcell_for_ue(): return PCell container from cell tree using
UE's PCell entry.
* Header: RRC_PCELL_INDEX (0), RRC_MAX_NUM_SERVING_CELLS (32), and
declarations for the above.
- UE context lifecycle:
* rrc_gNB_create_ue_context(): seq_arr_init(serving_cells).
* rrc_delete_ue_data(): seq_arr_free(serving_cells).
Move DU tree management from rrc_gNB_du.c into rrc_cell_management so that
DU and cell lookup logic live in one place, improving maintainability and
testability (there is no DU lib).
- rrc_cell_management: add du_compare(), RB_GENERATE(rrc_du_tree),
get_du_by_assoc_id(), rrc_add_du(), rrc_rm_du(), rrc_cleanup_du(),
rrc_free_du_container(); declare them in rrc_cell_management.h.
- rrc_gNB_du: move du_compare(), RB_GENERATE, get_du_by_assoc_id()
to rrc_cell_management
This commit introduces a new library module (rrc_cell_management.c/h)
to centralise functions that provides cell lookup, DU-specific cell
management, and cell lifecycle managements.
The design uses a dual-tree architecture:
- Global cell tree: RB-tree at RRC level (rrc->cells) keyed by cell_id,
used for O(log N_CELL) lookups and for maintaining num_cells.
- DU-specific storage: seq_arr_t per DU (du->cells) for iteration and
management of cells belonging to each DU.
API added:
- Global tree: get_cell_by_cell_id()(finds cell using global RB-tree
lookup (O(log N_CELL) complexity); rrc_add_cell(); rrc_rm_cell()
(rrc_rm_cell removes from tree, decrements num_cells, and frees the
cell via rrc_free_cell_container).
- DU-local seq_arr: rrc_get_cell_for_du(), rrc_get_cell_by_pci_for_du(),
rrc_add_cell_to_du() (with duplicate cell_id/PCI checks).
- Cleanup: rrc_free_cell_container() (frees ASN.1 MIB/SIB1/MTC and
container
Build: add rrc_cell_management static library in openair2/RRC/NR;
link it into nr_rrc from top-level CMakeLists.txt.
Add foundational data structures in nr_rrc_defs.h for multi-cell handling
in RRC, separating DU management from cell management.
Design uses a global cell tree for O(log N) lookups while keeping cell
ownership in DUs for locality and maintenance.
The implementation maintains backward compatibility and follows the same
patterns as existing DU and CUUP management in the codebase, i.e. RB-trees
with logarithmic complexity for scalable multi-cell deployments.
- Cell container structures:
* nr_rrc_cell_container_t: cell-specific data (cell_id, pci, plmn, tac,
frequency info, mib, sib1, mtc)
* nr_rrc_cell_info_t: TDD/FDD modes; frequency structs nr_rrc_freq_info_t,
nr_rrc_tdd_info_t, nr_rrc_fdd_info_t
- UE serving cell tracking:
* ue_serving_cell_t: nci, serving_cell_id (ServCellIndex TS 38.331,
0=PCell, 1-31=SCell), assoc_id for fast DU lookup
* gNB_RRC_UE_t: seq_arr_t serving_cells, with PCell always being the
first in the sequence
- DU container refactoring:
* nr_rrc_du_container_t: DU-only fields (gNB_DU_id, gNB_DU_name, rrc_ver);
seq_arr_t cells for per-DU iteration; setup_req/mib/sib1/mtc kept for
backward compatibility
- Global cell tree:
* gNB_RRC_INST_s: RB_HEAD(rrc_cell_tree, nr_rrc_cell_container_t) cells,
num_cells (data only; lookup helpers get_cell_by_cell_id/get_cell_by_pci
to be added separately)
Neighbour lists are built per serving cell in fill_neighbour_cell_configuration.
Each neighbour in a list is identified by PCI; having two entries with the same
PCI in one cell's list would be ambiguous and indicate a configuration error.
This commit adds validation so that duplicate PCIs within a cell's neighbour
list are detected at configuration load time and cause a fatal assertion with
a clear error message.
Changes:
- Add eq_neighbour_pci() and get_neighbour_by_pci() using
common/utils/alg/find.h to look up a neighbour by PCI in a seq_arr.
- In fill_neighbour_cell_configuration(), before appending each
cell's neighbour list, iterate over neighbours and assert that no PCI
appears more than once
Replace generic UE event log with explicit INFO log including UE ID and RNTI
when removing a UE context and centralize access to UE fields via a local
pointer.
rrc_delete_ue_data removes the all the list in the UE context, including the
serving cells, and therefore the cell id should not be logged in the function
responsible for the removal of the UE context, which at this point is already
emptied and while the cells themselves are still there (in the DU/global cell
structures), the lists in the UE context are empty.
* Commit applies to Setup Request, CU/DU Configuration Update
* Set standard compliant max number of cells per DU
* Allocate large lists on the heap to prevent stack overflow
due to the large size
* Update relevant free functions
* Also, fix bug in eq_f1ap_cu_configuration_update
Also, improve comments in struct definition.
Implement TS 38.473 transparency for CellGroupConfig forwarding and handle re-establishment on a different DU
This MR implements the following fixes that are relevant in RRC
procedures (e.g. re-establishment, reconfiguration):
1. Transparent CellGroupConfig forwarding
This MR implements TS 38.473 transparency requirements by ensuring that
the CellGroupConfig IE is transparently forwarded from the gNB-DU to the
UE without any re-encoding at the gNB-CU. The CU stores and forwards the
encoded bytes transparently, while the DU is responsible for all
encoding and modifications (e.g., reestablishRLC flags). Motivation
Motivation: The 3GPP TS 38.473 specification mandates that the
CellGroupConfig IE shall be transparently signaled to the UE.
Previously, the CU was re-encoding CellGroupConfig depending on the
scenario, instead of retrieving it from the DU, violating the
transparency requirement. CU may decode for internal use but must not
re-encode for forwarding. Solution
Solution: Core Principle: The CU stores the encoded CellGroupConfig
bytes received from the DU and forwards them directly to the UE without
any re-encoding. The DU handles all encoding and modifications.
Key Changes:
1. Transparent Forwarding in RRC Reconfiguration
- Modified build_RRCReconfiguration_IEs() to use pre-encoded bytes
directly
- Changed parameter from cell_group_config to cgc (encoded byte
array with buffer and size)
2. Encoded Bytes Storage
- Added byte_array_t mcg field to RRC UE structure to store encoded
CellGroupConfig
- Added store_cgc() helper function to save encoded cell group in
the RRC UE Context at gNB (from F1AP messages)
3. DU Encoding Responsibilities
- Add gNB-DU Configuration Query IE support to trigger cgc encoding
to request CellGroupConfig from DU
- DU encodes CellGroupConfig with proper reestablishRLC flags
during re-establishment
- DU removes SRB1 from rlc-BearerToAddModList during
re-establishment (SRB1 re-establishment is done before
RRCReconfiguration)
4. New functions
- Add rrc_detect_reestablishment() function to detect
re-establishment during UE Context Modification
- Extracted rrc_gNB_first_reconfiguration_after_reestablishment()
function to handle first reconfiguration after re-establishment
- Added dump_cgc() and dump_mcg() utility functions for debugging
purposes
5. Re-establishment Flow
- rrc_gNB_process_RRCReestablishmentComplete() requests
CellGroupConfig from DU via gNB_DU_Configuration_Query in UE
Context Modification Request
- DU detects re-establishment and add reestablishRLC IEs
- CU stores encoded CGC from the UE Context Modification Response
and forwards it transparently in subsequent RRC Reconfiguration
Testing
Manual Testing:
- RRC re-establishment procedure completes successfully
- RRC Reconfiguration after re-establishment includes all DU-provided information
Issues fixed
Closes issue #837
2. Trigger UE Context Setup fallback upon reestablishment on DU
different from the original one
As per 38.401 §8.7: if the UE accesses a DU different from the original
one during re-establishment, the CU must trigger UE Context Setup toward
the new DU. This refers to steps 9-10 and steps 9’-10’. Either one or
the other can be executed during re-establishment.
e.g. if this happens during handover, it shall trigger a UE Context
Setup fallback on the CU.
This MR implements the required behavior by:
- Adding a new helper function rrc_handle_f1_ho_reestab_on_target() to
handle F1 handover reestablishment on target DU. The function triggers
UE Context Setup procedure when the UE reestablishes on the target DU
during an ongoing F1 handover.
- Calling this function when detecting that the UE is reestablishing on
the target DU during handover, i.e.: (1) from
handle_rrcReestablishmentComplete(), as per 38.401 §8.7, NOTE on steps
9-10: CU gets UL RRC MESSAGE TRANSFER from a DU different than the
original (2) from rrc_CU_process_ue_modification_required, as per
38.401 §8.7, NOTE on steps 9'-10': CU gets a UE CONTEXT MODIFICATION
REQUIRED from a DU different from the original one.
Also:
- add helper rrc_fill_f1_ue_context_setup and adopt it in handover
- move DRB list filler fill_drb_to_be_setup into rrc_gNB.c and rename it
rrc_fill_f1_drb_to_setup for consistency
See also TS 38.401 Figure 8.7-1 "RRC connection reestablishment
procedure" for reference
3. DU: Handle missing old UE context in DL RRC Message Transfer
During F1 handover reestablishment, the CU may forward a DL RRC Message
Transfer to the target DU with old_gNB_DU_ue_id included, even though
the target DU does not have the old UE context yet.
The code previously used AssertFatal when the old UE context was not
found, causing a crash: "CU claims we should know UE %04x, but we don't"
According to TS 38.473, the gNB-DU shall release the old gNB-DU UE F1AP ID only:
> if the gNB-DU identifies the UE-associated logical F1-connection If
> there is no matching connection, there is nothing to release.
Fix:
- Check if old UE context exists before accessing it
- If old UE does not exist, log error, clean up any stale F1 UE data,
and return early (do not deliver RRC message since reestablishment
can't be completed)
The goal is to address a recurrent issue in the F1 pipeline, see
jenkins-oai.eurecom.fr/job/RAN-SA-Handover-CN5G/514:
Assertion (oldUE) failed!
In dl_rrc_message_transfer() /oai-ran/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c:956
CU claims we should know UE 35ce, but we don't