User-plane testing
User-plane testing is separate from signalling. Once a UE has its PDU session established, the gNB drives real IP traffic through the UPF on N3 and measures throughput, latency, jitter, and drop. dflux Runner has two user-plane receiver backends: userspace kernel sockets for portability, and DPDK for line rate. This guide covers both.
Two backends
| Backend | When to pick |
|---|---|
uspace (userspace) | Functional checks, low rates (≤ a few hundred Mbps), no privileged NIC binding. Runs on any Linux. |
dpdk | Throughput / latency benchmarks. Requires huge pages + a NIC bound to a DPDK-compatible driver. |
Both backends terminate the receiver side. They sit behind the UPF on N6/N9 (plain IP, GTP-U already stripped) or, in DPDK mode with -gtp, terminate GTP tunnels themselves.
The sender side is a flow action. The shipped templates/gnb/uplane_traffic.yaml flow runs the registration and PDU session sequence, then triggers uplane_start, which arms the gNB-side traffic generator using parameters from the inbound PDUSessionResourceSetupRequest and the gNB's uplane: configuration block. The GTP-U generator encapsulates ICMP or UDP packets. For the concepts behind this, see user plane.
Configure the gNB sender
In your env's gNB block:
type: USPACE runs a userspace generator over kernel sockets. type: DPDK uses the DPDK-accelerated backend for line-rate sends.
local_gtpu on the NGAP transport is the IP the gNB advertises to the UPF as its N3 endpoint. The UPF tunnels return traffic there.
server uspace — userspace receiver
Per-protocol behaviour:
- UDP: binds one listener per port and echoes each datagram back to the sender.
- TCP: listens per port. The kernel completes the SYN/SYN-ACK, so a SYN-only client sees its RTT reply; application-layer echo runs after the full three-way handshake.
- ICMP: opens a raw ICMP socket and replies to echo requests. Most Linux kernels also reply automatically, so set
net.ipv4.icmp_echo_ignore_all=1to let this server be the responder.
Flags:
| Flag | Default | Purpose |
|---|---|---|
-protocol icmp|udp|tcp | (required) | Protocol to serve |
-listen <ip> | 0.0.0.0 | Bind IP |
-port-start <n> | 5001 | First port for UDP/TCP |
-port-num <n> | 1 | How many sequential ports to bind |
-duration <duration> | forever | Run for this then exit |
-payload-size <n> | 0 | Echo payload size override |
-metrics-port <n> | 0 (off) | Prometheus metrics endpoint |
ICMP needs CAP_NET_RAW; either run as root or grant the capability.
server dpdk — DPDK receiver
Flags specific to DPDK:
| Flag | Required | Purpose |
|---|---|---|
-protocol icmp|udp|tcp | yes | Protocol to serve |
-port-pci <pci> | yes | NIC PCI address bound to a DPDK driver |
-port-addr <ip> | yes | IP for the PCI port |
-port-gateway <ip> | yes | Gateway IP for the PCI port |
-listen <ip> | yes | Server's own bind IP (passed through to the embedded DPDK listener) |
-port-start <port> | yes | First listen port |
-port-num <n> | no (default 1) | Number of listen ports |
-cpu <n> | no (default 0) | CPU index for the DPDK lcore |
-duration <duration> | no | Run for this then exit (clamped to ≥ 5s) |
-payload-size <n> | no | Response payload size |
-keepalive <duration> | no | TCP keepalive interval (e.g. 1s) |
-client-allow <ip> + -client-num <n> | no | Permitted client IP range |
-gtp | no | Enable GTP tunnel termination |
-metrics-port <n> | no | Prometheus metrics endpoint |
The DPDK backend uses an accelerated listener that ships inside the dflux Runner binary. It requires huge pages allocated and the chosen NIC bound to a DPDK-compatible driver (typically vfio-pci or igb_uio).
DPDK prerequisites:
- Allocate huge pages:
echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages - Bind the NIC to a DPDK driver:
dpdk-devbind.py --bind=vfio-pci 0000:00:09.0 - Run
server dpdkas root (or withCAP_SYS_ADMIN)
Driving traffic from a flow
The uplane_start action arms the sender. In uplane_traffic.yaml it fires on the same transition that sends the PDUSessionResourceSetupResponse, once the PDU session is established:
When an uplane_start action fires during a run, the flow result carries a uplane_report field with the per-flow metric snapshot. Read the full sender flow in the templates repository.
What each backend measures
Both backends report the same metric shape:
- Packets in / out per second
- Bytes in / out per second
- Bidirectional latency on echo (RTT)
- Jitter on echo (stddev of RTT)
- Drop rate (sent vs echoed)
Userspace tops out at a few hundred kpps on a typical NIC; DPDK reaches line rate up to the per-core packet budget.
Troubleshooting
ICMP server receives nothing — kernel is auto-replying. Set net.ipv4.icmp_echo_ignore_all=1.
DPDK EAL: Cannot mbuf errors — huge pages aren't allocated, or the process can't see them. Verify with grep Huge /proc/meminfo.
Sender never fires uplane_start — the flow's preceding states didn't reach the action. Run with -trace to see which state the flow landed in.
N3 traffic returns to the wrong address — local_gtpu on the NGAP transport must be an IP the UPF can route packets to. NAT in between will break the return path.
Where to go next
- User plane — the concepts behind GTP-U testing and when to skip it.
- CLI reference — the full
server uspaceandserver dpdkflag set. - Running flows and suites — drive the sender flow and read its result.