A "task" is a Linux thread running infinite waiting loop on one ITTI queue
app task manages the initial configuration
sctp task mamanges the SCTP Linux sockets
CU code runs in the task F1AP_CU_task. It stores it's private data context in a static variable. No mutex is used, as the single interface to CU ITTI tast in incoming ITTI messages. CU task is a single ITTI queue, but multiple CU could exist in one Linux process, using the "instance id" (called "module id" also) to create separate contextual information for each CU instance.
DU code runs in the task F1AP_DU_task. The design is similar to CU task
All GTP-U tunnels are managed in a Linux Thread, that have partially ITTI design:
1. tunnel creation/deletion is by C API functions direct calls (a mutex protects it)
2. outgoing packets are pushed in a ITTI queue to the gtp thread
3. incoming packets are sent to the tunnel creator using a C callback (the callback function is given in tunnel creation order). The callback should not block
# 3. CU F1AP messages #
## startup
CU thread starts when the CU starts. It opens listening socket on F1AP_PORT_NUMBER by sending the appropriate message to TASK_SCTP.
It will accept the first incoming connection on this port
## F1 SETUP
DU has connected, and sent this message.
The CU receives, decode it and send relevant information to RRC thread by message F1AP_SETUP_REQ
## F1 SETUP response
RRC sends two messages to CU F1 task: the information to encode the F1 setup response and a second message to encode F1AP_GNB_CU_CONFIGURATION_UPDATE
Upon reception of these two messages the CU task encodes and sends the messages to the DU
## UE entry
When CCH channel decoding finds a new UE identity, it call rrc_gNB_generate_RRCSetup() that will send F1AP_DL_RRC_MESSAGE to the CU task.
The CU encodes and sends to DU DLRRCMessageTransfer
## UE attach/detach
When rrc_gNB_process_RRCReconfigurationComplete() occurs, the RRC sends to CU task the message F1AP_UE_CONTEXT_SETUP_REQ. The CU task encodes the F1 message UEContextSetup and send it to the DU over F1AP.
This function rrc_gNB_process_RRCReconfigurationComplete() also creates the GTP-U user plane bearers.
## F1AP incoming messages
SCTP tasks sends a ITTI message SCTP_DATA_IND to the CU task.
A array of functions pointers and the F1AP standard identifier "procedureCode", the CU calls the appropriate function
Hereafter the most significant messages processing
### CU_handle_F1_SETUP_REQUEST
Transcodes information to the same message toward RRC (F1AP_SETUP_REQ)
### CU_handle_INITIAL_UL_RRC_MESSAGE_TRANSFER
Transcodes information to the same message toward RRC (NR_RRC_MAC_CCCH_DATA_IND)
### CU_handle_UL_RRC_MESSAGE_TRANSFER
Encode and send data to PDCP (calling pdcp_data_ind ()) for processing UL data.
# DU F1 Messages
## Startup
The task "gNB app" after rzading the configuration file, sends a first message F1AP_SETUP_REQ to DU task
Using this message, the uniq DU task (Linux Thread) creates a DU instance context memory space, calls SCTP task to create a socket to the CU.
When it receives from the SCTP task the socket creation success, the DU task encodes+sends the F1 setup message to the CU.
## F1AP_INITIAL_UL_RRC_MESSAGE
When MAC layer push UL data to DU task, the DU sends to CU the message InitialULRRCMessageTransfer
## F1AP_UL_RRC_MESSAGE
In case of SRB bearer, the RLC layer use this message to push rlc bearer payload to DU task, that forward to CU
## pdcp_data_ind
This function call in RLC, when it is a DRB will be replaced by F1-U transport to CU
## F1AP incoming messages
SCTP tasks sends a ITTI message SCTP_DATA_IND to the DU task.
A array of functions pointers and the F1AP standard identifier "procedureCode", the DU calls the appropriate function
Hereafter the most significant messages processing
### DU_send_F1_SETUP_RESPONSE
Decode the CU message, then encode a message to TASK_GNB_APP. The thread "APP" extracts the system information block and reconfigure the DU with: du_extract_and_decode_SI(), configure_gnb_du_mac()
### DU_handle_gNB_CU_CONFIGURATION_UPDATE
No developped
The following messages are currently implemented:
### DU_handle_UE_CONTEXT_SETUP_REQUEST
- F1 Setup Request and F1 Setup Response/Failure
- Initial UL RRC, UL/DL RRC Message Transfer
- F1 UE Context management messages for the "good case"
* Setup Request/Response
* Modification Request/Response
* Release Request/Command/Complete
Push data to RLC layer for transmisstion in DL (to the UE) by calling enqueue_rlc_data_req()
All other messages are not implemented or not working reliably:
### DU_handle_DL_RRC_MESSAGE_TRANSFER
- F1 CU/DU configuration updade
- Paging messages
- Warning Message transmissions
Depending on the content, adds UE context in DU low layers and/or send data to the UE with rlc_data_req()
# F1-U messages
The data path by itself doesn't make any difficulty:
1. for UL, rlc calls pdcp_data_ind (DRB) or sends a message to RRC (SRB). So, we can replace pdcp_data_ind() by du_pdcp_data_ind() that will push the message in the GTP tunnel. In CU, assuming the tunnel is in place, the right call back decapsulate the transport (cu_ul_recv()), then calls pdcp_data_ind() in CU
The SRB are sent in DU RRC tasks via itti F1AP_UL_RRC_MESSAGE (see above the processing)
2. For DL, pdcp (in CU for DRBs) calls rlc_data_req (by a decoupling queue and thread rlc_data_req_thread, so function du_rlc_data_req() or enqueue_rlc_data_req()). in DU, assuming the gtp-u tunnel exists, a reception call back (du_dl_recv()) decapsulate the gtp-u packet and calls du_rlc_data_req()
Hereafter the processing design, that doesn't require to setup a new context storage, as we can use the GTP-U internal tables: rnti+rb=>teid for outgoing gtp-u packets and teid=>rnti+rb for incoming gtp-u packets
In CU case, the DRB tunnel to DU and the tunnel on N3 have the same key (rnti, rb id), but they run in two different GTP-U instances.
Each instance binds on diffrent sockets.
For F1-U, both DU and CU binds on the full quadruplet (IP source, port source, IP destination, port destination) from the configuration file parameters:
CU: at cell level (same level as nr_cellid parameter), they are read if tr_s_preference = "f1"; is set
DU: in block MACRLCs, if tr_n_preference = "f1"; is set in this block
## tunnels setup
In GTP-U, TS 29.281 specifies a option header (NR RAN Container This extension header may be transmitted in a G-PDU over the X2-U, Xn-U and F1-U user plane interfaces), but it is not mandatory optional header (as is the N3 one).
So, we can use this header a something reliable with other vendors implementation.
In DL, we need to associate the packet with: rnti, rb (radio bearer) and "mui" (a OAI internal id that is optional and may be removed soon) to call rlc_data_req()
In UL, we need also the RNTI, the rb
So, we need to create a gtp-u tunnel for each rb over F1, then manage in CU and DU the association between a uniq TEid and the pair(rnti, rb). This is already what we have in existing OAI GTP-U layer interface.
In F1AP, for each "DRB to Be Setup Item IEs", we have a field TNL (transport network layer) to set a specific GTP tunnel (@IP, TEid, udp port is always 2152). This is the same for each message related to DRBs. (F1AP carries the SRB payload inside F1AP).
So, For each F1AP containing DRB setup/modification/deletion, the related GTP-U tunnels will be modified one to one.
The exception is the intialisation of new tunnel: in the call to tunnel creation, we need to send the remote TEID, but we don't know yet if we are the initial source. In this case, we issue a dummy (0xFFFF) remote teid; when we receive the remote answer, we get the source teid, that we can inform GTP-U with (using update tunnel).
## processing on GTP-U incoming message
Du_dl_recv() or cu_ul_recv() are called because we have set this callbacks in creat/update tunnel calls to gtp-u. As gtp-u maintains a pair(rnti,rbid) associated to each tunnel, the functions can be very simple stateless callback that calls pdcp_data_ind() or rlc_data_req() to get back in normal processing path
## CU/DU outgoing
DU rlc north output goes to du_pdcp_data_ind() that push a outgoing packet request to gtp-u, using the rnti+rb as ids. GTP-U must have the tunnel existing, then it simply implement the same as in N3 for exemple.
CU pdcp south output calls cu_rlc_data_ind() that do the same symetric processing.
# High-level F1-C code structure
## High-level F1-C code structure
The F1 interface is used internally between CU (mostly RRC) and DU (mostly MAC)
to exchange information. In DL, the CU sends messages as defined by the
A "task" is a Linux thread running an infinite waiting loop on one ITTI queue.
The `app` task manages the initial configuration; the `sctp` task manages the
SCTP Linux sockets.
The CU encoding/decoding runs in the task executing `F1AP_CU_task()`. It stores its private data context in a static variable.
The DU encoding/decoding runs in the task executing `F1AP_DU_task()`. The design is similar to CU task.
All GTP-U tunnels are managed in a Linux Thread, that have partially ITTI design:
1. tunnel creation/deletion is by C API functions direct calls (a mutex protects it)
2. outgoing packets are pushed in a ITTI queue to the gtp thread
3. incoming packets are sent to the tunnel creator using a C callback (the callback function is given in tunnel creation order). The callback should not block
## F1-C messages towards the CU
The CU thread starts when the CU starts. It opens listening socket on the
configuration-specified IP/port by sending the appropriate message to
`TASK_SCTP`. It will accept any incoming connection and forward any F1 message
to RRC.
You can trace any message by looking up the handler
`cu_task_handle_sctp_data_ind()`, which ultimately triggers a corresponding
ITTI message sent to RRC. All ITTI messages arriving are dispatched in the ITTI
message handling function in `rrc_gNB.c`. For your convenience, find below a
table of same messages that might arrive from the DU and how they are
forwarded.
After handling messages, the RRC uses a callback that determines if messages go
directly to the MAC or are sent to the CU ITTI task (see
[above](#high-level-f1-c-code-structure))
You might also want to consult TS 38.401 regarding the message exchange.
| Incoming F1 message | ITTI message to RRC | RRC handler | Comments |
| F1 Setup Request | `F1AP_SETUP_REQ` | `rrc_gNB_process_f1_setup_req()` | will trigger either a response or failure |
| Initial UL RRC Message Transfer | `F1AP_INITIAL_UL_RRC_MESSAGE` | `rrc_gNB_process_initial_ul_rrc_message()` | first message from UE, follows up with RRC Setup or Reestablishment |
| UL RRC Message Transfer | `F1AP_UL_RRC_MESSAGE` | `rrc_gNB_decode_dcch()` | UL RRC messages contain DCCH |
| UE Context Response | `F1AP_UE_CONTEXT_SETUP_RESP` | `rrc_CU_process_ue_context_setup_response()` | does not trigger a follow-up, as UE sends Security Response |
| UE Context Modification Response | `F1AP_UE_CONTEXT_MODIFICATION_RESP` | `rrc_CU_process_ue_context_modification_response()` | triggers Reconfiguration if CellGroup in answer |