Inside F1-U: NR-U Flow Control Between the gNB's CU and DU

Reading the F1-U user plane in a real srsRAN capture — the conversation a 5G base station has with itself, across the split between its Central Unit and its Distributed Unit. The trace was decoded and annotated with VisualEther, which turns a Wireshark PCAP into a sequence diagram.

💡 Read the walkthrough and the diagram side by side. Open the interactive diagram (or PDF) in a split-screen window so the captions stay in view as you read. In Edge, Chrome, or Firefox, right-click the link and pick your browser's split-screen option (Edge labels it "Open link in split screen window"). On macOS, prefer Chrome or Firefox — Safari has no in-browser split screen. Otherwise, open the diagram in a new tab and snap the two windows side by side.

Overview

In 5G, the gNB is not a single box — it splits into a Central Unit (CU) and one or more Distributed Units (DUs), and the interface between them, F1, carries real traffic over the wire. This capture is an F1-U (user-plane) trace taken at the DU in an srsRAN deployment, and it lets us observe the flow-control protocol that keeps the two halves in step.

The full capture is 37,871 frames over about 82 seconds. Rather than annotate all of it, this walkthrough zooms in on a 29-frame window (~189 ms) for a single data radio bearer — just enough to see one complete turn of the flow-control loop, several times over. A few facts hold for the whole trace:

A primer on the CU/DU split

Before the walkthrough, let us understand the CU/DU split.

The higher-layer split (3GPP TS 38.401) divides the gNB into:

Here's how the pieces connect, and where this capture sits:

flowchart LR accTitle: 5G NR CU/DU split architecture showing the F1-C and F1-U interfaces accDescr: The 5G core connects to the gNB, which splits into a Central Unit and a Distributed Unit. The Central Unit further splits into CU-CP, which handles RRC and control-plane PDCP, and CU-UP, which handles SDAP and user-plane PDCP; the two are connected via the E1 interface. The AMF reaches the CU-CP over N2 using NGAP, and the UPF reaches the CU-UP over N3 using GTP-U. The CU-CP connects to the Distributed Unit over F1-C, which is F1AP over SCTP, while the CU-UP connects to the Distributed Unit over F1-U, which is GTP-U and is the interface on which this capture was taken. The Distributed Unit owns the RLC, MAC, and the physical layer, and connects to the UE over the Uu radio interface. AMF["AMF<br/>(core control)"] UPF["UPF<br/>(core user plane)"] subgraph gNB["gNB"] subgraph CU["Central Unit (CU)"] CUCP["CU-CP<br/>RRC · PDCP-C"] CUUP["CU-UP<br/>SDAP · PDCP-U"] end DU["Distributed Unit (DU)<br/>RLC · MAC · PHY"] end UE["UE"] AMF ---|"N2 · NGAP"| CUCP UPF ---|"N3 · GTP-U"| CUUP CUCP ---|"E1 · E1AP"| CUUP CUCP ---|"F1-C · F1AP / SCTP"| DU CUUP ===|"F1-U · GTP-U — this capture"| DU DU ---|"Uu radio"| UE
The 5G NR higher-layer split (TS 38.401). The gNB is divided into a Central Unit — itself split into CU-CP for control and CU-UP for the user plane — and a Distributed Unit. F1-C (F1AP over SCTP) carries signaling between CU-CP and the DU; F1-U (GTP-U) carries the user plane between CU-UP and the DU. This trace sits on the F1-U leg.

The F1 interface between CU and DU is divided into control and data plane:

InterfacePlaneTransportCarries
F1-CControl (signaling)F1AP over SCTPBearer setup, UE context, RRC transport
F1-UUser (data to/from the radio)GTP-U tunnels, one pair per DRBDownlink PDCP PDUs + flow-control feedback

Our capture is the F1-U face. Every packet is a GTP-U tunnel packet, and inside each one we see a layered structure:

LayerWhat it is3GPP spec
GTP-UThe F1-U tunnel itself, keyed by TEIDTS 29.281
NR RAN ContainerA GTP-U extension header (type 0x84) that flags an NR-U frameTS 29.281
NR-U / NRUPThe flow-control protocol between CU-UP and DUTS 38.425
PDCP-NRThe downlink user-plane PDU being delivered (ciphering boundary)TS 38.323

The key protocol in this trace is NR-U (the NR User Plane Protocol, sometimes written NRUP). It defines two PDU types, and — crucially — they flow in opposite directions:

NR-U PDU typeDirectionRole
0 — DL User DataCU-UP → DUCarries a downlink PDCP PDU for the DU to transmit over the air
1 — DL Data Delivery Status (DDDS)DU → CU-UPFeedback only: highest transmitted PDCP SN, desired buffer size, final-frame flag — no user data

That directional pairing is the flow-control loop. The CU-UP pushes data down; the DU reports back how much it has managed to send and how much buffer it still has.

The flow at a glance

Each DRB uses two TEIDs — one tunnel for each NR-U direction. The window here follows a single voice bearer: DL User Data on TEID 0x06 (CU-UP → DU) and its paired DL Data Delivery Status on TEID 0x18 (DU → CU-UP). The two were confirmed to belong to the same bearer by correlating sequence numbers: the DU's reported Highest Transmitted PDCP SN tracks exactly the PDCP SN progression the CU-UP sends on 0x06.

The following sequence repeats for the life of the call:

sequenceDiagram accTitle: The F1-U NR-U flow-control loop accDescr: The CU-UP sends DL User Data carrying PDCP PDUs to the DU. The DU replies with DL Data Delivery Status, reporting the highest transmitted PDCP sequence number and the desired buffer size, then repeats it as a periodic heartbeat until the next data burst arrives. participant CUUP as CU-UP participant DU CUUP->>DU: NR-U DL User Data (PDCP SN n, n+1) Note over CUUP,DU: DU transmits the PDUs over the radio DU-->>CUUP: NR-U DL Data Delivery Status (Highest Tx SN, Desired Buffer) Note over CUUP,DU: DU repeats DDDS every ~10 ms as a heartbeat DU-->>CUUP: NR-U DL Data Delivery Status (unchanged)
One turn of the F1-U flow-control loop: the CU-UP delivers a small burst of downlink data, the DU transmits it over the air and reports its progress, then keeps that report alive as a heartbeat until the next burst.

The rest of this page walks the 29-frame window in four short beats. Frame numbers are the original capture frame numbers, so they jump — the window is a slice of a much larger trace.

sequenceDiagram accTitle: Beat 1 — the first downlink burst accDescr: The CU-UP sends two NR-U DL User Data frames on TEID 0x06 carrying PDCP SN 0 and SN 1 to the DU. participant CUUP as CU-UP participant DU CUUP->>DU: NR-U DL User Data · TEID 0x06 · PDCP SN 0 CUUP->>DU: NR-U DL User Data · TEID 0x06 · PDCP SN 1 Note over CUUP,DU: Two SDUs now queued at the DU for the air interface

Frames 6335–6336 — DL User Data. The CU-UP forwards two downlink PDCP PDUs to the DU over F1-U. On the wire, each is a bare GTP-U packet on TEID 0x06, its NR RAN Container extension header (type 0x84) marking an NR-U DL User Data (PDU Type 0). The two frames carry PDCP SN 0 and SN 1 and share a capture timestamp — downlink voice arrives in these small, paired bursts, roughly aligned with the codec's 20 ms cadence.

One field is worth a second look: the NR-U Sequence Number reads 0 on every DL User Data frame. This srsRAN build doesn't use the NR-U SN — so the meaningful sequence lives in the PDCP SN inside the tunneled PDU, which increments 0, 1, 2, … and the DU will echo back in its status reports.

Beat 2 — The delivery-status feedback and heartbeat (frames 6337–6365)

sequenceDiagram accTitle: Beat 2 — delivery-status feedback and heartbeat accDescr: The DU sends DL Data Delivery Status on TEID 0x18 reporting the highest transmitted PDCP SN 1 and a desired buffer size of about 5.9 MiB, then repeats the same status several times as a heartbeat. participant CUUP as CU-UP participant DU DU-->>CUUP: DL Data Delivery Status · Highest Tx SN 1 · Buffer 5.9 MiB DU-->>CUUP: DL Data Delivery Status · Highest Tx SN 1 (heartbeat) Note over CUUP,DU: Repeated every ~10 ms with no new data

Frame 6337 — DL Data Delivery Status. The direction reverses. The DU answers on TEID 0x18 — the uplink half of the same DRB's tunnel pair — with an NR-U DDDS (PDU Type 1). It reports the Highest Transmitted NR PDCP SN = 1 (the DU has now pushed SN 0 and SN 1 out over the radio) and advertises a Desired Buffer Size of 6,172,672 bytes (~5.9 MiB), indicating how far ahead the CU-UP may send. The Final Frame flag is not set: the tunnel stays open.

Frames 6348–6365 — the heartbeat. No new voice data has arrived, so the DU re-sends the same DDDS — Highest Tx SN still 1, buffer unchanged — roughly every 10 ms. Flow control on F1-U is a timer-driven keepalive, not purely event-driven: the DU keeps the CU-UP continuously informed that the link and its buffer are healthy, even when there is nothing new to acknowledge. This is exactly why status reports outnumber data frames ~24-to-1 across the capture.

Beat 3 — The loop repeats, and the SN climbs (frames 6366–6446)

sequenceDiagram accTitle: Beat 3 — the loop repeats and the sequence number climbs accDescr: The CU-UP sends more DL User Data pairs carrying PDCP SN 2 and 3, then SN 4 and 5. After each pair, the DU reports a Highest Transmitted PDCP SN that has increased by two, one per delivered PDU. participant CUUP as CU-UP participant DU CUUP->>DU: DL User Data · PDCP SN 2, SN 3 DU-->>CUUP: DL Data Delivery Status · Highest Tx SN 3 CUUP->>DU: DL User Data · PDCP SN 4, SN 5 DU-->>CUUP: DL Data Delivery Status · Highest Tx SN 5 (then heartbeat ×9)

The pattern from Beats 1–2 now recurs:

Data burstFramesPDCP SNs sentDU then reports Highest Tx SN
16335–63360, 11
26366–63672, 33
36395–63964, 55
4644866

Each pair of DL User Data frames bumps the reported Highest Tx SN by exactly 2 — one increment per PDU the DU actually transmitted. This is how the CU-UP confirms downlink progress without ever seeing the radio itself. After burst 3, frames 6397–6446 are the longest quiet stretch in the window: nine identical DDDS reports at Highest Tx SN 5, bridging a ~90 ms gap with no downlink voice at all.

Beat 4 — Resume after the gap (frames 6448–6461)

Frame 6448 — DL User Data, PDCP SN 6. A single SDU this time — the pairing is a scheduler artifact, not a rule — and the voice stream resumes after the gap.

Frames 6449–6461 — DDDS, Highest Tx SN 6. The report advances by only 1 now, mirroring the single PDU just delivered, then settles back into its heartbeat. The loop shown here — paired DL User Data down, a burst of DDDS up, buffer held at ~5.9 MiB — repeats for the entire call. It never closes: no DDDS in this capture ever sets Frame is final, so the F1-U tunnel stays up until the bearer is released.

What the numbers are really saying

Three observations from this window generalize to any F1-U trace:

  1. DDDS dominates the byte count, by design. 96% of the frames are upstream status reports. The DU's job is to keep the CU-UP's transmit window accurate in real time, and a timer-driven report is cheaper than the risk of the CU-UP over- or under-filling the DU's buffer. If an F1-U capture looks like "nothing but tiny GTP-U packets," this is why — most of them are 24–28-byte DDDS.

  2. The desired buffer size is the flow-control knob. Here it sits at a constant ~5.9 MiB — effectively bottomless for a low-rate voice bearer, so flow control never throttles the CU-UP. On a congested or high-throughput bearer, this is the field to watch: a shrinking desired buffer is the DU telling the CU-UP to slow down, and it's the first thing to check when downlink throughput stalls between CU and DU.

  3. The PDCP SN, not the NR-U SN, is the sequence that matters here. Because srsRAN leaves the NR-U SN at 0, delivery tracking rides entirely on the PDCP SN echoed back in the DDDS. Correlating the CU-UP's transmitted PDCP SNs against the DU's Highest Transmitted PDCP SN is what pairs the two tunnels of a DRB — and it's the technique that identified 0x06 and 0x18 as one bearer.

What's below PDCP: a hidden voice call

Everything above is the F1-U transport. But the DL User Data frames aren't empty — each carries a PDCP PDU, and in this capture PDCP uses the NEA0 null cipher, so the payload is in the clear. Strip the 3-byte PDCP header and a whole second capture appears underneath: a two-party VoNR (Voice over New Radio) IMS call — SIP registration, RTP media, IPsec, DNS. Reconstructing and reading that inner call is a story of its own, and it's the subject of a companion walkthrough built from this same trace.

Takeaways

  1. The CU and DU talk over F1-U. GTP-U tunnels carrying NR-U with one TEID pair per DRB.
  2. NR-U is a two-way flow-control loop. DL User Data flows CU-UP → DU; DL Data Delivery Status flows DU → CU-UP. Direction comes from the PDU type, not from any IP address — there are none.
  3. Watch the desired buffer size and the highest transmitted SN. Together they are the flow control: one is the credit the DU extends, the other is the DU's delivery high-water mark.
  4. A bare GTP-U trace is a stack of tunnels. Below NR-U sits PDCP, and below that — when it's null-ciphered — a fully readable user plane.

Try it on your own capture

Every arrow in this walkthrough was decoded and captioned by VisualEther from a raw Wireshark PCAP — no manual diagramming. Point it at your own 5G, LTE, or IMS trace and read it the same way.