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:
- There is no IP layer to key on. srsRAN writes each F1-U frame as a bare GTP-U packet under a Wireshark DLT_USER link type — no Ethernet, IP, or UDP header. The two participants, CU-UP and DU, are constants; you read the direction of each message from the NR-U PDU type, not from an address.
- The capture carries 12 GTP-U tunnels (
gtp.teid) — 6 data radio bearers, each with a pair of TEIDs, one per direction. - The traffic is wildly asymmetric: 36,339 of the 37,871 frames (96%) are upstream status reports; only 1,513 carry actual downlink data. That imbalance is the whole story of F1-U flow control, and we'll see exactly why below.
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:
- a Central Unit (CU) that terminates the upper radio-protocol layers, itself split into CU-CP (control plane) and CU-UP (user plane); and
- one or more Distributed Units (DU) that own the lower, real-time layers — RLC, MAC, and the physical layer.
Here's how the pieces connect, and where this capture sits:
The F1 interface between CU and DU is divided into control and data plane:
| Interface | Plane | Transport | Carries |
|---|---|---|---|
| F1-C | Control (signaling) | F1AP over SCTP | Bearer setup, UE context, RRC transport |
| F1-U | User (data to/from the radio) | GTP-U tunnels, one pair per DRB | Downlink 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:
| Layer | What it is | 3GPP spec |
|---|---|---|
| GTP-U | The F1-U tunnel itself, keyed by TEID | TS 29.281 |
| NR RAN Container | A GTP-U extension header (type 0x84) that flags an NR-U frame | TS 29.281 |
| NR-U / NRUP | The flow-control protocol between CU-UP and DU | TS 38.425 |
| PDCP-NR | The 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 type | Direction | Role |
|---|---|---|
| 0 — DL User Data | CU-UP → DU | Carries a downlink PDCP PDU for the DU to transmit over the air |
| 1 — DL Data Delivery Status (DDDS) | DU → CU-UP | Feedback 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:
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.
Beat 1 — The first downlink burst (frames 6335–6336)
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)
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)
The pattern from Beats 1–2 now recurs:
| Data burst | Frames | PDCP SNs sent | DU then reports Highest Tx SN |
|---|---|---|---|
| 1 | 6335–6336 | 0, 1 | 1 |
| 2 | 6366–6367 | 2, 3 | 3 |
| 3 | 6395–6396 | 4, 5 | 5 |
| 4 | 6448 | 6 | 6 |
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:
-
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.
-
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.
-
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
0x06and0x18as 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
- The CU and DU talk over F1-U. GTP-U tunnels carrying NR-U with one TEID pair per DRB.
- 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.
- 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.
- 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.