Getting Started with go-gtp5gnl: Userspace Control of GTP-U for free5GC UPF
Note
Author: Kai-Xu, Zhan
Date: 2025/11/12
Introduction
The go-gtp5gnl project bridges user-space control and kernel-space GTP-U data forwarding by providing a Go-native Netlink interface for the Linux gtp5g kernel module. This enables free5GC’s UPF—or any custom UPF implementation—to manipulate GTP-U tunnels, Packet Detection Rules (PDRs), Forwarding Action Rules (FARs), and QoS Enforcement Rules (QERs) directly from Go code.
This article provides a hands-on walkthrough of using go-gtp5gnl to observe, modify, and verify PFCP-controlled GTP-U state in free5GC. You will learn how to list, edit, and recreate rules in real time to understand how the UPF enforces 5G user-plane logic.
Goal: By the end, you will understand how go-gtp5gnl connects the 3GPP PFCP control plane to kernel-level forwarding on the N3/N6 interfaces—making it an indispensable tool for debugging and research.
Environment Setup
Prerequisites
- Install free5GC: https://free5gc.org/guide/3-install-free5gc/
- Install UERANSIM: https://free5gc.org/guide/5-install-ueransim/
- Install gtp5g kernel module: https://github.com/free5gc/gtp5g
Building go-gtp5gnl
Clone and build the project to obtain the command-line utility gtp5g-tunnel:
git clone https://github.com/free5gc/go-gtp5gnl.git
cd go-gtp5gnl/
go build -o gtp5g-tunnel ./cmd/gogtp5g-tunnel
Understanding Go-gtp5gnl Commands
Command Overview
The go-gtp5gnl library and CLI expose Netlink interfaces to manipulate kernel objects representing PDR, FAR, and QER entities.
- PDR (Packet Detection Rule) – Identifies packets belonging to a PDU Session.
- FAR (Forwarding Action Rule) – Specifies what action is applied.
- QER (QoS Enforcement Rule) – Defines QoS parameters.
Each rule type supports the operations add, get, mod, del, and list.
| Operation | Description |
|---|---|
| add | Create a new rule entry in the kernel. |
| get | Retrieve current rule(s) from kernel. |
| mod | Modify attributes of an existing rule. |
| del | Delete a rule from the kernel. |
| list | Display all rules of a given type. |
Usage
- List all PDR/FAR/QER:
sudo ./gtp5g-tunnel list [pdr/far/qer] - Get/Del/Add/Mod PDR/FAR/QER:
sudo ./gtp5g-tunnel [get/del/add/mod] [PDR/FAR/QER] [interface_name] [seid] [id] [option]
Practical Usage of Go-gtp5gnl
Before testing go-gtp5gnl, verify that you can successfully create a UE instance using UERANSIM and attach it to the free5GC core network. A successful registration and PDU Session establishment indicate that the environment is ready for go-gtp5gnl testing.
Inspecting PDRs
Use sudo ./gtp5g-tunnel list pdr to list all PDRs. The output will look like this:
{
"ID": 3,
"Precedence": 128,
"PDI": {
"SrcIntf": null,
"UEAddr": "10.60.0.1",
"FTEID": {
"TEID": 2,
"GTPuAddr": "192.168.56.102"
},
"SDF": {
"FD": {
"Action": 1,
"Dir": 2,
"Proto": 255,
"Src": {
"IP": "0.0.0.0",
"Mask": "AAAAAA=="
},
"Dst": {
"IP": "1.1.1.1",
"Mask": null
},
"SrcPorts": null,
"DstPorts": null
},
"TTC": null,
"SPI": null,
"FL": null,
"BID": null
},
"EPFs": null
},
"OuterHdrRemoval": 0,
"FARID": 3,
"QERID": [
1,
3
],
"URRID": [
1,
2,
8,
7
],
"SEID": 1,
"PDNType": null
},
{
"ID": 4,
"Precedence": 128,
"PDI": {
"SrcIntf": null,
"UEAddr": "10.60.0.1",
"FTEID": null,
"SDF": {
"FD": {
"Action": 1,
"Dir": 2,
"Proto": 255,
"Src": {
"IP": "1.1.1.1",
"Mask": null
},
"Dst": {
"IP": "0.0.0.0",
"Mask": "AAAAAA=="
},
"SrcPorts": null,
"DstPorts": null
},
"TTC": null,
"SPI": null,
"FL": null,
"BID": null
},
"EPFs": null
},
"OuterHdrRemoval": null,
"FARID": 4,
"QERID": [
1,
3
],
"URRID": [
1,
2,
8,
7
],
"SEID": 1,
"PDNType": null
}
Example truncated for brevity — other PDR entries omitted.
PDR Information Elements
| Parameter | Description |
|---|---|
| ID | Unique identifier of the PDR. Must be unique within the same SEID. |
| Precedence | Rule priority (smaller numbers have higher priority). |
| SEID | Session Endpoint ID that identifies the PFCP Session this rule belongs to. All PDRs for the same UE PDU Session share the same SEID. |
| PDI | Packet Detection Information defines which packets match this rule. |
PDI Details
| Parameter | Description |
|---|---|
| SrcIntf | The source interface of the incoming packet. |
| UEAddr | UE IPv4 address. This PDR handles traffic to or from this IP. |
| FTEID | Defines the GTP-U tunnel endpoint by combining a TEID and an IP address |
| SDF | Specifies the service data flow filter(s) used for packet detection. |
FTEID (Fully Qualified Tunnel Endpoint Identifier) Details
| Parameter | Description |
|---|---|
| TEID | Tunnel Endpoint ID is an identifier for the GTP-U tunnel. Used to recognize packets received from the gNB. |
| GTPuAddr | Local GTP-U address. The N3 interface IP address of the UPF that listens for GTP-U packets from the gNB. |
Important Rule:
- FTEID present → Uplink traffic (GTP-U packets from gNB/UE).
- FTEID null → Downlink traffic (plain IP packets from N6/Internet).
SDF (Service Data Flow) Details
| Parameter | Description |
|---|---|
| Action | SDF action.1 = This rule permits matching traffic. |
| Dir | Traffic direction.2 = Out |
| Proto | IP protocol number |
| Src | Source Address
|
| Dst | Destination Address
|
Inspecting FARs
The PDR defines which packets are detected by the UPF. Packets that match this PDR are then handled according to their associated Forwarding Action Rule (FAR), which specifies how the UPF should process or forward them. Now, let’s use sudo ./gtp5g-tunnel list far to examine the configured FARs and see how these packets are processed.
{
"ID": 3,
"Action": 2,
"Param": null,
"PDRIDs": [
3
],
"BARID": null,
"SEID": 1
},
{
"ID": 1,
"Action": 2,
"Param": null,
"PDRIDs": [
1
],
"BARID": null,
"SEID": 1
},
{
"ID": 4,
"Action": 2,
"Param": {
"Creation": {
"Desc": 256,
"TEID": 1,
"PeerAddr": "192.168.56.101",
"Port": 2152
},
"Policy": null,
"TosTc": 0
},
"PDRIDs": [
4
],
"BARID": null,
"SEID": 1
},
Example truncated for brevity — other FAR entries omitted.
FAR Information Elements
| Parameter | Description |
|---|---|
| ID | Unique identifier of the FAR. Must be unique within the same SEID. |
| Action | Apply action.1 = drop the pakcets2 = Forward the packets. |
| SEID | Session Endpoint ID that identifies the PFCP Session this rule belongs to. All FARs for the same UE PDU Session share the same SEID. |
| Param | Forwarding Parameters. |
From the above demonstration, PDR with ID 3 is an uplink rule, matching GTP-U packets arriving from the RAN. Its corresponding FAR (FAR ID 3) has no forwarding parameters, meaning the packets are decapsulated and delivered locally toward the data network. Conversely, PDR with ID 4 is a downlink rule, where the associated FAR (FAR ID 4) contains forwarding parameters (TEID and peer address) used to encapsulate and send packets back to the gNB through GTP-U.
Inspecting QERs
Next, let's use sudo ./gtp5g-tunnel list qer to examine the configured QERs.
{
"ID": 3,
"Gate": 0,
"MBR": {
"ULHigh": 812,
"ULLow": 128,
"UL_Kbps": 208000,
"DLHigh": 812,
"DLLow": 128,
"DL_Kbps": 208000
},
"GBR": {
"ULHigh": 0,
"ULLow": 0,
"UL_Kbps": 0,
"DLHigh": 0,
"DLLow": 0,
"DL_Kbps": 0
},
"CorrID": 0,
"RQI": 0,
"QFI": 2,
"PPI": 0,
"PDRIDs": [
4,
3
],
"SEID": 1
},
Example truncated for brevity — other QER entries omitted.
QER Information Elements
| Parameter | Description |
|---|---|
| ID | Unique identifier of the QER. Must be unique within the same SEID. |
| QFI | QoS Flow Identifier. Identifies the QoS Flow within a PDU Session. Value range: (0 ~ 63) |
| RQI | Reflective QoS Indicator. Indicates whether reflective QoS is triggered. 0 = Not triggered.1 = Triggered. |
| PPP | Packet Per Flow presence flag. Specifies whether the Packet Per Flow Indicator (PPI) is included in the QER. 0 = Not present.1 = Present. |
| PPI | Packet Per Flow Indicator. Marks packets with a priority level or DSCP value for differentiated handling. Value range: (0 ~ 7) |
| SEID | Session Endpoint Identifier identifying the PFCP session this QER belongs to. |
MBR and GBR Details
| Parameter | Description |
|---|---|
| ULHigh / ULLow | Encoded uplink maximum (or guaranteed) bit rate split into two fields per 3GPP spec (High/Low octets). |
| UL_Kbps | Readable uplink bit rate in Kbps derived from the encoded values. |
| DLHigh / DLLow | Encoded downlink maximum (or guaranteed) bit rate. |
| DL_Kbps | Readable downlink bit rate in Kbps derived from the encoded values. |
Modifying FAR
After understanding how to inspect PDRs, FARs, and QERs, let’s move on to modify an existing FAR (Forwarding Action Rule) to block traffic and then restore it.
Step 1: Verify initial state (UE should be reachable)
# Executed on UERANSIM
ping 1.1.1.1 -I ueransim0 -c 5
If the UPF and UERANSIM are properly connected, you should see successful ping replies from 1.1.1.1.
Step 2: Modify FAR 3 – change action from 2 (Forward) to 1 (Drop)
# Executed on free5GC
sudo ./gtp5g-tunnel mod far upfgtp 1:3 --action 1
This updates FAR ID 3 under SEID 1 to drop packets instead of forwarding them.
Step 3: Re-test connectivity (ping should fail or timeout)
# Executed on UERANSIM
ping 1.1.1.1 -I ueransim0 -c 5
Since this PDR defines the uplink traffic path from the UE (via gNB) toward the data network, removing it prevents packets from reaching the external destination.
Step 4: Restore FAR 3 to forwarding state
# Executed on free5GC
sudo ./gtp5g-tunnel mod far upfgtp 1:3 --action 2
Step 5: Test again (connectivity should be restored)
# Executed on UERANSIM
ping 1.1.1.1 -I ueransim0 -c 5
If pings succeed again, it confirms that the mod operation has correctly re-applied the forwarding rule.
Deleting and Re-Adding PDRs
Next, you’ll experiment with removing and re-adding PDRs (Packet Detection Rules) to observe how the UPF handles missing or re-created rules.
Step 1: Delete PDR 1 and PDR 3
These rules typically correspond to the uplink flow for your UE session.
# Executed on free5GC
sudo ./gtp5g-tunnel del pdr upfgtp 1:1
sudo ./gtp5g-tunnel del pdr upfgtp 1:3
Step 2: Test connectivity again (should fail)
# Executed on UERANSIM
ping 1.1.1.1 -I ueransim0 -c 5
With no matching uplink PDRs, the UPF cannot identify sending traffic, causing packet drops or timeouts.
Step 3: Re-add PDR 3 based on its original configuration
# Executed on free5GC
sudo ./gtp5g-tunnel add pdr upfgtp 1:3 \
--pcd 128 \
--hdr-rm 0 \
--far-id 3 \
--ue-ipv4 10.60.0.1 \
--f-teid 2 192.168.56.102 \
--qer-id 1 \
--qer-id 3
This command recreates PDR ID 3 with its corresponding parameters — linking it again to FAR 3 and QER 1/3 for the same UE.
Step 4: Test once more (connectivity should recover)
# Executed on UERANSIM
ping 1.1.1.1 -I ueransim0 -c 5
A successful ping confirms that the add operation has restored proper forwarding behavior.
Conclusion
The go-gtp5gnl framework offers full userspace visibility and control of kernel-based GTP-U tunnels. Through the experiments above, you have:
- Inspected PFCP-translated kernel rules (PDR, FAR, QER).
- Modified actions and observed instant traffic impact.
- Re-created rules to validate real-time UPF behavior.
By unifying Go’s ease of use with Netlink’s power, go-gtp5gnl becomes an ideal platform for 5G UPF debugging, QoS experimentation, and academic research on user-plane optimization.
References
- 3GPP TS 29.244
- 3GPP TS 23.212
- free5GC Project
- gtp5g Kernel Module
- go-gtp5gnl Library
- Linux Netlink and Generic Netlink
About
Hello, I'm Kai-Xu Zhan. I'm honored to be a new member of the free5GC project under the Linux Foundation. As someone who is still learning and growing in the field of 5G core network development, I'm enthusiastic about contributing to the community and expanding my knowledge in telecommunications technologies. I welcome any guidance or feedback as I continue to familiarize myself with the project.
Connect with Me
- GitHub: KASHZKX