A UPF is programmed over N4 (or a 4G user-plane node over Sxa/Sxb) with PFCP on UDP port 8805. Before any subscriber traffic can pass, two things have to happen in order: the control-plane and user-plane nodes form an association, then they create a session (Session Establishment) that installs packet rules. If you skip the association, a well-formed Session Establishment Request is often rejected with Cause 72, "No established PFCP Association".

This article is about that handshake, in a lab, against a file, a fake UPF, or a real lab UPF. An earlier article covers generating PFCP traffic without a live core; another covers a broader PASS/FAIL conformance suite. Here the procedure itself is the test. Keep every live exchange on an authorized, isolated network. Never point these tools at a production or third-party system.

Association first, then the session

PFCP node messages (3GPP TS 29.244, as cited in the Mobile Core CLI Reference) are exchanged between a CP function (SMF, SGW-C, PGW-C) and a UP function (UPF, SGW-U, PGW-U) as nodes, not per subscriber. Their header has S=0 and no SEID:

  • Association Setup (types 5/6) carries Node ID, Recovery Time Stamp and function features. A PFCP session belongs to an association.
  • Heartbeat (types 1/2) is the liveness check; a changed Recovery Time Stamp means the peer restarted and lost its sessions.
  • Association Release (types 9/10) ends the association and every session in it.

Only then come the per-session messages, with S=1 and a SEID: Session Establishment, Modification and Deletion (TS 29.244 clauses 7.5.2–7.5.7 in the same reference). The first Establishment Request uses SEID 0 in the header; later messages use the SEIDs the peers exchanged. PDR, FAR, QER and URR in that Establishment are what the UPF will actually forward and count.

A useful N4 test therefore checks more than "a PFCP packet was sent":

  • Association Setup succeeds before any Establishment.
  • Establishment is accepted (or is rejected with the Cause you intended).
  • Heartbeat keeps the association alive; a restart is visible.
  • Modification and Deletion, when you use them, refer to the same SEID.
  • After a release, a new Establishment without a new association fails as the node specifies.

Option 1: Watch a real SMF and UPF

Deploy an open-source SMF and UPF (for example Open5GS), attach a UE, and capture UDP 8805.

Good for: seeing the stack your lab already runs, including vendor-specific IEs.

Limits: you get the messages that implementation sends, in the order it sends them. Forcing a missing association, a wrong SEID, or a precise Establishment fault usually means extra tooling. You also have to correlate the capture yourself.

Option 2: Craft PFCP in Scapy

Scapy has contributed PFCP layers, so you can build an Association Setup or a Session Establishment by hand and write a pcap or send it.

Good for: one carefully inspected message, or a single IE you want to break.

Limits: you own sequence numbers, Node IDs, SEIDs, Recovery Time Stamps and the rule set. That is fine for a few frames and becomes a state machine for a realistic N4 dialogue.

Option 3: Mobile Core Generator — offline, then live

Mobile Core Generator can write a correctly ordered N4 capture, explain it, and, in Full Edition, play the SMF towards a UPF. According to the product page, User Manual and CLI Reference (1.19.2):

Offline (all editions, Community session cap applies):

  • gtpcore -scenario pfcp-node writes Association Setup, Heartbeat and Association Release for each CP/UP pair in a sessions file. With -pfcp-node-sessions (default true) the Session Establishment, Modification and Deletion of every session sit between Setup and Release — the order a DUT sees on a live N4/Sxb interface.
  • gtpcore -scenario pfcp-lifecycle writes only the per-session Establishment / Modification / Deletion pairs for PFCP-owned sessions.
  • Teaching labs pfcp-association and pfcp-errors walk the same ideas: compare a node message (S=0, no SEID) with the first Establishment (S=1, header SEID 0); read Recovery Time Stamp on Setup and Heartbeat; open the error lab's CSV next to the capture for Cause and Offending IE.
  • Tools > Protocol Conformance can generate further negative Establishment cases (types starting with pfcp_establishment_, and pfcp_modify_unknown_seid) for your own sessions.
gtpcore -scenario pfcp-node -sessions-json sessions.json -output n4.pcap
gtpcore -scenario pfcp-lifecycle -sessions-json sessions.json -output pfcp_sessions.pcap

Use Generate > Subscribers with PFCP ownership (or the PFCP session tutorial) to obtain sessions.json first. Explain Packets and GTP Sessions will decode and group the result.

Live (Full Edition):

  • gtpcore fake-upf -listen 127.0.0.1:8805 is a built-in UPF for tests (all editions). Optional -reject-every N and -delay help you rehearse loss and rejects.
  • gtpcore pfcp-live acts as an SMF: Association Setup, Heartbeat, then per-session Establishment, optional Modification and Deletion, and finally Association Release. It is Full Edition (Community exits with code 3). Association failure is exit code 4.
gtpcore fake-upf -listen 127.0.0.1:8805
gtpcore pfcp-live -upf 127.0.0.1:8805 -sessions 20 -rate 10 -summary-json summary.json -pcap n4-live.pcap -report-html kpi.html

Towards a lab UPF on a real interface, add -allow-remote and -node-ip, and start with a low -rate-start / -rate / -ramp. Loopback is the safe default; the flag is the consent to leave it. The summary and loosha-kpi/1 report give attempts, successes, failures, no-response, setup rate and Cause. Running gtpcore -scenario kpi-report on the -pcap gives the same numbers.

If you already have a captured N4 exchange, Full Edition probe can send the requests and match responses by PFCP sequence number, preferring a Session Establishment reply whose SEID equals the request's CP F-SEID. Outer IP/MAC rewrite does not change F-SEID or other IE contents — build those for the lab.

The built-in conformance suite (gtpcore suite) is a broader PASS/FAIL pack against a lab SGW, PGW or UPF, including cases you can list with gtpcore suite -list and rehearse with -self-test. Use it when you want a whole gate; use pfcp-live or a probe of an N4 pcap when the question is this handshake.

A practical N4 workflow

  1. Learn the two layers offline. Run the pfcp-association lab, or pfcp-node plus pfcp-lifecycle on a small sessions file. Filter pfcp in Wireshark and compare S=0 node messages with S=1 session messages.
  2. Add a negative case. Run pfcp-errors, or a Protocol Conformance Establishment fault, and read the Cause.
  3. Rehearse live on loopback. Start fake-upf, then pfcp-live with a handful of sessions. Confirm exit 0 and the summary.
  4. Only then, on an isolated lab NIC, aim at a real UPF. -allow-remote, a finite session count, a modest rate. If Establishment fails with Cause 72, the association did not succeed — fix that before you raise the rate.
  5. Keep the evidence. The pcap, summary.json and the HTML KPI report are what you will compare after a UPF change.

open5GS notes from the CLI Reference are practical here too: raise global.max.ue if you need more than about 1800 concurrent sessions; that UPF may not answer Association Release (reported as one no-response).

Which tool when?

Need Good fit
See whatever your lab SMF already sends Capture UDP 8805 on the existing core
One hand-built PFCP message Scapy
Ordered N4 pcap: association, then sessions pfcp-node (optional -pfcp-node-sessions)
Learn association vs session, and error Causes Teaching labs pfcp-association / pfcp-errors
Live SMF towards a UPF, with a summary Full: pfcp-live; start with fake-upf
Replay a captured N4 exchange and match replies Full: gtpcore probe on that pcap
Broader PASS/FAIL pack on a lab node Full: gtpcore suite (-self-test first)

Editions

Mobile Core Generator Community Edition is free for non-commercial evaluation and compact labs (up to 100 subscribers/sessions, send capped at 1000 packets/s). Offline pfcp-node, pfcp-lifecycle, teaching labs, Explain Packets and fake-upf are included. pfcp-live, probe and the conformance suite are Full Edition (one-time $89). The current release runs on Windows 10/11 64-bit; a Linux build is available on request. Npcap is required for live send.

Explore Mobile Core Generator →

Limitations

  • Community Edition cannot run pfcp-live, probe or suite (exit code 3).
  • Replay and probe rewrite outer IP/MAC only; F-SEID and other PFCP IEs stay as generated.
  • fake-upf is a test double, not a substitute for your DUT's feature set.
  • A successful Association Setup is not a guarantee that user-plane forwarding works; add GTP-U or -user-plane http (Full, on pfcp-live) only after the session is accepted.
  • Never point pfcp-live or probe at a production or third-party UPF.

FAQ

Why does Session Establishment fail with Cause 72?
The UP function has no association with this CP peer yet. Send Association Setup first (pfcp-node in a file, or pfcp-live, which does that before any session).

Why is the SEID 0 in the first Establishment Request?
The requester does not yet know the peer's SEID. The User Manual's association tutorial asks you to compare that header with later messages.

Can I try this without a UPF?
Yes. gtpcore fake-upf -listen 127.0.0.1:8805 works in every edition. pfcp-live against it needs Full Edition. Offline, pfcp-node and the teaching labs need no network.

Is this the same as the conformance suite?
No. The suite is a set of built-in cases with PASS/FAIL/ERROR/SKIP. pfcp-live is a live association-and-session driver with a KPI/summary for the run you configured. You can use both.