Forward user-plane traffic
Run the UPF with its userspace datapath so it really forwards packets over a TUN and GTP-U, then push traffic and read the metrics.
The default lab UPF runs with -datapath stub: the PFCP control plane works end to end, but the forwarder only records calls and never touches a packet. That is fine for the registration walkthrough, but it means no user-plane traffic actually moves. This tutorial switches to the userspace datapath — a real forwarder that opens a Linux TUN for N6 and a GTP-U UDP socket for N3 — so a UE can ping the Internet through the core. For the concepts, see User plane; for the full flag reference, see d3x-core-upf CLI.
/dev/net/tun, which requires
CAP_NET_ADMIN — so run the UPF as root or grant the capability to the binary. It
also needs a real GTP-U sender (a gNB simulator such as UERANSIM that builds a UE TUN), which
only runs on Linux. None of this works in the macOS Docker lab, and the dflux Runner gNB
simulator drives the control plane only — it does not push user-plane packets. So this tutorial
runs the required NFs natively on a Linux host rather than in the Docker lab. See
Running natively and
UPF datapath.1. Grant the capability
With the required NFs installed, either run the UPF as root or grant the installed binary the TUN capability once:
2. Start the UPF on the userspace datapath
Run the UPF with -datapath userspace. Unlike the stub, this opens the TUN named by -tun-name on N6 and binds the GTP-U socket given by -n3-addr on N3:
The other three functions (NRF, DPF, DMF) run as in any native bring-up; point the DMF's SMF at this UPF's N4 address (-upf 127.0.0.2:8805). The full four-shell startup is in Running natively.
ip addr or iptables for you. Once it
is up, give the TUN an address in the UE-IP pool, bring it up, and set up egress — either
host-side MASQUERADE or in-process NAT44 with -nat44-addr. See
UPF datapath for both options.3. Bring up a UE and a PDU session
Connect a Linux gNB+UE simulator that builds a real UE interface — a typical lab uses UERANSIM. Configure it with the lab's PLMN (001/01), TAC, and slice (sst 1), and a seeded subscriber (for example imsi-001010000000001, key 465B5CE8B199B49FAA5F0A2EE238A6BC, OPc E8ED289DEBA952E4283B54E88E6183CA), exactly as in Register a UE and open a session. Once the PDU session establishes, the simulator creates a UE interface with an address from the pool:
4. Push and observe traffic
Send packets from the UE interface through the core:
Uplink packets arrive at the UPF over GTP-U on N3, are decapsulated, and leave on the TUN; downlink packets are re-encapsulated and sent back to the gNB. You can watch both halves:
5. Read the UPF metrics
The UPF serves Prometheus metrics on its -metrics-addr (:9095 here). After a session is up and traffic is flowing, the key counters move:
If the ping hangs, the tcpdump commands above plus upf_pfcp_messages_total tell you which leg is missing: no ICMP on the TUN means uplink decap is not happening; no GTP-U on the wire means downlink encap or the route back to the gNB is wrong.
Where to go next
For higher throughput options and the trade-offs between the stub, userspace, and DPDK datapaths, see UPF datapath. To wire metrics into a dashboard, see Observability.