E1AP interface architecture
8 minute read
Target audience: RAN engineers. Scope: the E1AP (E1) interface implementation in OCUDU — class ownership, procedure layout, bearer context management (Setup, Modification, Release), connection and transport, and concurrency. Reference: 3GPP TS 38.463.
1. What E1AP Does
E1 is the interface between the Central Unit Control Plane (CU-CP) and the Central Unit User Plane (CU-UP). The E1AP protocol (3GPP TS 38.463) runs over this interface and carries one category of messages: bearer context management. The CU-CP directs the CU-UP to create, modify, or release PDCP/SDAP entities for a UE; the CU-UP executes those instructions and reports back.
In OCUDU the two sides are separate compilation units:
e1ap_cu_cp_impl(lib/e1ap/cu_cp/e1ap_cu_cp_impl.h) — lives inside the CU-CP process, owns the initiating side of all procedures.e1ap_cu_up_impl(lib/e1ap/cu_up/e1ap_cu_up_impl.h) — lives inside the CU-UP process, executes bearer operations and reports metrics.
When both run in the same process (the typical single-binary deployment) they connect through e1_local_connector_factory, which wires the two sides together without any SCTP hop. In split deployments each side connects to a separate SCTP endpoint.
2. Design Principles
Strict CU-CP initiator model. All bearer-context procedures (Setup, Modification, Release) are started by the CU-CP side. The CU-UP may emit unsolicited notifications (Release Request, Inactivity Notification, DL Data Notification), but it never initiates a transactional exchange.
Global transaction manager per side. Each implementation holds a single
e1ap_event_manager(CU-CP calls ite1ap_transaction_manager) with 256 transaction slots. This is a shared pool across all UEs, not one manager per UE.Per-UE bearer transaction manager on the CU-CP side. In addition to the global pool,
e1ap_cu_cp_implcreates onee1ap_bearer_transaction_managerper UE. This manager tracks the three per-UE transactional outcomes:context_setup_outcome,context_modification_outcome, andcontext_release_complete. This design lets the global pool handle the wire-level transaction ID while the per-UE object correlates outcomes to specific UE state.Coroutine-based procedures. Initiating procedures are
async_task<R>coroutines usingCORO_BEGIN/CORO_AWAIT_VALUE/CORO_RETURN. They suspend at the point of sending the request and resume when the response arrives or the transaction times out.Two distinct UE ID namespaces. The CU-CP allocates
gnb_cu_cp_ue_e1ap_idusing a sequential hole-filling scan. The CU-UP allocatesgnb_cu_up_ue_e1ap_idusing a simple monotonic counter. Both IDs are exchanged and must appear in every subsequent message for that UE.Transport abstraction.
e1_connection_server(CU-CP),e1_connection_client(CU-UP), ande1_local_connector_factoryimplement the same gateway pattern used by F1AP. The application picks the transport at startup based on whether it is running in split or co-located mode.Metrics on the CU-UP side. The CU-UP implementation maintains
e1ap_cu_up_metrics_collectorand a periodicmetrics_timer. The CU-CP side has no equivalent metrics object.
3. Class Structures
3.1 CU-CP Side — e1ap_cu_cp_impl
e1ap_cu_cp_impl
├── cu_up_index_t one instance per CU-UP
├── e1ap_transaction_manager global 256-slot transaction pool
├── e1ap_ue_context_list map: gnb_cu_cp_ue_e1ap_id → e1ap_ue_context
│ └── e1ap_ue_context
│ ├── gnb_cu_cp_ue_e1ap_id sequential, hole-filling
│ ├── gnb_cu_up_ue_e1ap_id received from CU-UP
│ └── e1ap_bearer_transaction_manager
│ ├── context_setup_outcome
│ ├── context_modification_outcome
│ └── context_release_complete
└── e1_release_in_progress bool flag, prevents duplicate release
ID Allocation — CU-CP Side. gnb_cu_cp_ue_e1ap_id is assigned during Bearer Context Setup. The allocator scans the used-ID set from 0 upward and assigns the first gap (hole-filling). This keeps IDs compact when UEs leave and new ones arrive.
3.2 CU-UP Side — e1ap_cu_up_impl
e1ap_cu_up_impl
├── e1ap_cu_up_connection_handler SCTP or local connector
├── e1ap_event_manager global 256-slot transaction pool
├── e1ap_ue_context_list map: gnb_cu_up_ue_e1ap_id → e1ap_cu_up_ue_context
│ └── e1ap_cu_up_ue_context
│ ├── gnb_cu_up_ue_e1ap_id monotonic counter (++)
│ └── gnb_cu_cp_ue_e1ap_id received from CU-CP
├── metrics_timer periodic trigger
└── e1ap_cu_up_metrics_collector per-bearer metrics aggregation
ID Allocation — CU-UP Side. gnb_cu_up_ue_e1ap_id is a simple next_id++ counter. There is no hole-filling; IDs grow monotonically for the lifetime of the CU-UP process.
4. ASN.1 Layer
Namespace: asn1::e1ap
Root PDU type: e1ap_pdu_c — a discriminated union of init_msg, successful_outcome, unsuccessful_outcome, each wrapping the procedure-specific IE set.
Packers:
e1ap_asn1_packer— common encoder/decoder sitting on the SCTP transport.e1ap_cu_cp_asn1_helpers.h— helper functions for CU-CP message construction.e1ap_cu_up_asn1_helpers.h— helper functions for CU-UP message construction.
All PDUs are encoded as aligned PER (Packed Encoding Rules) per the standard.
5. E1 Setup
E1 Setup bootstraps the association between one CU-CP and one CU-UP. The CU-UP is always the initiator.
sequenceDiagram
participant CU_UP as CU-UP<br/>e1ap_cu_up_impl
participant CU_CP as CU-CP<br/>e1ap_cu_cp_impl
CU_UP->>CU_CP: E1SetupRequest
Note over CU_UP: e1ap_cu_up_setup_procedure<br/>async_task<cu_up_e1_setup_response><br/>timeout = 3000 ms
alt Setup accepted
CU_CP-->>CU_UP: E1SetupResponse
Note over CU_UP: CORO_AWAIT resolves success
else Setup rejected with time_to_wait
CU_CP-->>CU_UP: E1SetupFailure (time_to_wait IE)
Note over CU_UP: Procedure retries after wait
endCU-UP procedure class: e1ap_cu_up_setup_procedure (lib/e1ap/cu_up/procedures/e1ap_cu_up_setup_procedure.h). It is a coroutine (async_task<cu_up_e1_setup_response>). The transaction timeout is 3000 ms. On time_to_wait in the failure response, the procedure waits the indicated duration and retries.
CU-CP response: Handled inline — there is no procedure class on the CU-CP side for E1 Setup. The CU-CP validates the request and responds synchronously within the message handler.
6. E1 Reset
Reset is bidirectional: either side may initiate it. Both sides have dedicated procedure classes.
| Direction | Initiator Class | Responder Class |
|---|---|---|
| CU-CP initiates | cu_cp_e1_reset_procedure | e1ap_cu_up_reset_procedure |
| CU-UP initiates | e1ap_cu_up_reset_procedure (initiator) | cu_cp_e1_reset_procedure (responder) |
Reset tears down UE contexts on both sides. On the CU-CP, a reset cascades through the UE context list and triggers release of any active bearer transaction managers.
7. E1 Removal
E1 Removal gracefully tears down the CU-UP association. The CU-UP initiates.
- CU-UP side:
e1ap_cu_up_release_procedure— sends E1RemovalRequest, awaits E1RemovalResponse. - CU-CP side:
e1_release_procedure— responds inline. Thee1_release_in_progressflag is set before the response is sent to prevent any parallel bearer procedures from starting.
8. Bearer Context Procedures
8.1 Bearer Context Setup
The CU-CP instructs the CU-UP to create PDCP/SDAP entities for a new UE or new bearer.
sequenceDiagram
participant CU_CP as CU-CP
participant CU_UP as CU-UP
CU_CP->>CU_UP: BearerContextSetupRequest<br/>(gnb_cu_cp_ue_e1ap_id, bearer list)
Note over CU_CP: bearer_context_setup_procedure<br/>async_task, CORO_AWAIT(txn)
Note over CU_UP: Allocates gnb_cu_up_ue_e1ap_id<br/>Creates PDCP/SDAP entities
CU_UP-->>CU_CP: BearerContextSetupResponse<br/>(gnb_cu_up_ue_e1ap_id, tunnel endpoints)
Note over CU_CP: CORO_AWAIT resolves<br/>Stores gnb_cu_up_ue_e1ap_id- CU-CP:
bearer_context_setup_procedure— coroutine, allocatesgnb_cu_cp_ue_e1ap_id, sends request, awaitscontext_setup_outcome. - CU-UP: Inline handler — no separate procedure class. Allocates
gnb_cu_up_ue_e1ap_id, creates bearer context, sends response synchronously.
8.2 Bearer Context Modification
The CU-CP modifies bearers for an existing UE (add/modify/release DRBs).
Both sides have dedicated procedure classes:
- CU-CP: Coroutine that sends
BearerContextModificationRequest, awaitscontext_modification_outcome. - CU-UP: Coroutine that receives the request, modifies the bearer entities, sends
BearerContextModificationResponseorBearerContextModificationFailure.
8.3 Bearer Context Release (CU-CP Initiated)
The CU-CP releases all bearers for a UE.
Both sides have dedicated procedure classes:
- CU-CP: Coroutine that sends
BearerContextReleaseCommand, awaitscontext_release_complete. - CU-UP: Coroutine that receives the command, tears down PDCP/SDAP entities, sends
BearerContextReleaseComplete.
9. Non-Transactional Notifications (CU-UP → CU-CP)
The following messages flow from CU-UP to CU-CP and carry no transaction correlation — the CU-UP sends them and does not await a response.
| Message | Trigger | Handler |
|---|---|---|
| Bearer Context Release Request | CU-UP detects a reason to release (e.g., lower-layer failure) | Inline handler on CU-CP; cascades to UE release |
| Bearer Context Inactivity Notification | Inactivity timer fires on a PDCP entity | Inline handler on CU-CP; may trigger release or modification |
| DL Data Notification | Downlink data arrives for a UE in RRC_INACTIVE | Inline handler on CU-CP; triggers paging |
None of these messages has a matching procedure class on either side.
10. Object Lifecycle: UE E1AP Context
11. Connection and Transport
Three gateway implementations exist, selected at startup by the application:
| Mode | CU-CP | CU-UP |
|---|---|---|
| Split | e1_connection_server — listens on SCTP | e1_connection_client — connects to CU-CP |
| Co-located | e1_local_connector_factory | e1_local_connector_factory |
The local connector wires the two e1ap_message_handler interfaces directly in memory with no serialization overhead. The SCTP paths run through e1ap_asn1_packer on both ends.
12. Concurrency
- The global
e1ap_transaction_manager(256 slots) is shared across all UEs. Transaction ID 0–255 is allocated per request and released when the response arrives or the transaction times out. - Per-UE
e1ap_bearer_transaction_manageris single-threaded because the CU-CP UE task executor serializes all operations for a given UE. - The CU-UP event manager is accessed from the CU-UP task executor; no locking is needed inside the CU-UP implementation.
- The
e1_release_in_progressflag on the CU-CP side is checked before any new bearer procedure begins; this is a single-bool guard, not a mutex, relying on the strand semantics of the UE executor.
13. Not Implemented
| Procedure | 3GPP Reference | Status |
|---|---|---|
| gNB-CU-UP Config Update | TS 38.463 Section 8.2.4 | Not implemented — no procedure class, no handler |
| gNB-CU-CP Config Update | TS 38.463 Section 8.2.5 | Not implemented — no procedure class, no handler |
| Bearer Context Modification Required | TS 38.463 Section 8.3.3 | Not implemented — no CU-UP-initiated modification class |
14. Procedure Cross-Reference
| Procedure | 3GPP Ref | CU-CP Class | CU-UP Class | Pattern |
|---|---|---|---|---|
| E1 Setup | Section 8.2.1 | Inline handler | e1ap_cu_up_setup_procedure | CU-UP coroutine, 3000 ms timeout, retry on time_to_wait |
| E1 Reset | Section 8.2.2 | cu_cp_e1_reset_procedure | e1ap_cu_up_reset_procedure | Both sides bidirectional |
| E1 Removal | Section 8.2.3 | e1_release_procedure | e1ap_cu_up_release_procedure | CU-UP initiates, CU-CP responds |
| gNB-CU-UP Config Update | Section 8.2.4 | — | — | Not implemented |
| gNB-CU-CP Config Update | Section 8.2.5 | — | — | Not implemented |
| Bearer Context Setup | Section 8.3.1 | bearer_context_setup_procedure | Inline handler | CU-CP coroutine, CORO_AWAIT(context_setup_outcome) |
| Bearer Context Modification | Section 8.3.2 | Procedure class | Procedure class | Both sides coroutine |
| Bearer Context Modification Required | Section 8.3.3 | — | — | Not implemented |
| Bearer Context Release (CP) | Section 8.3.4 | Procedure class | Procedure class | CU-CP initiates, both coroutine |
| Bearer Context Release Request | Section 8.3.5 | Inline handler | Inline send | Non-transactional |
| Bearer Context Inactivity Notification | Section 8.3.6 | Inline handler | Inline send | Non-transactional |
| DL Data Notification | Section 8.4.1 | Inline handler | Inline send | Non-transactional |
| Error Indication | Section 8.5 | Inline handler | Inline send | Non-transactional |