Fix PDR Precedence-Based Matching and QFI-Ignore Mode for Uplink Traffic
Summary
This PR fixes critical uplink PDR (Packet Detection Rule) matching issues and adds support for QFI-ignore mode to handle gNB deployments that don't support multiple QFI per TEID.
Problems Fixed
1. Incorrect Precedence-Based PDR Selection
- Issue: UPF returned the first matching PDR instead of selecting the lowest precedence value
- Impact: Traffic with multiple PDRs per TEID+QFI (different SDF filters) was misrouted, leading to incorrect QoS treatment
2. QFI-Ignore Mode for Uplink Classification
- Issue: Some gNB deployments do not support multiple QFI per TEID, requiring UPF to classify uplink traffic using only TEID + SDF filters
-
Solution: Added
ignore_qfi_for_uplinkmode to match PDRs based on TEID only, ignoring QFI values, and then reclassify the packets based on SDF to find matching QoS Parameters
Solutions
Fix 1: Precedence-Based Selection
Implemented proper lowest-precedence tracking in pfcp_session_s_lookup_precedence_over_n3():
- Iterate through all matching PDRs instead of returning first match
- Track PDR with lowest precedence value
- Return best match after evaluating all candidates
- Enhanced debug logging for troubleshooting
Fix 2: SDF Filter Lookup for QFI-Ignore Mode
Implemented correct SDF filter key construction to handle both standard and QFI-ignore modes:
struct session_qfi sdf_key = {
.seid = seid,
.qfi = ignore_qfi_for_uplink ? packet_qfi : pdi.qfi.qfi,
};
Rationale:
- When
ignore_qfi_for_uplink=false(standard mode): Use PDR's configured QFI - QFI must match exactly between packet and PDR - When
ignore_qfi_for_uplink=true(TEID-only mode): Use packet's actual QFI for SDF lookup - ensures correct SDF filter selection even when PDR matching ignores QFI, allowing proper default QoS flow handling
Testing
Test Setup
- Deploy core network using
oai-cn5g-fed/docker-compose/docker-compose-basic-nrf-qos.yaml - Apply patch to create QoS rules for UE with SUPI
208950000000035multiqos.patch:-
gbr-rule-5qi-1= 3 Mbps (default QoS flow) -
gbr-rule-iperf3-8080= 20 Mbps (port 8080) -
gbr-rule-iperf3-8081= 10 Mbps (port 8081)
-
Verification Commands
# Start IPerf3 server on UE
docker exec -it ueransim-ue-5qi-1 iperf3 -s -B 12.1.1.10 -p 8080
# (also individually start it for other ports below)
# Test different ports from DN
docker exec -it oai-ext-dn iperf3 -c 12.1.1.10 -p 8080 # Should get ~20 Mbps
docker exec -it oai-ext-dn iperf3 -c 12.1.1.10 -p 8081 # Should get ~10 Mbps
docker exec -it oai-ext-dn iperf3 -c 12.1.1.10 # Should get ~3 Mbps (default)
Results
With Fix (Correct Precedence-Based QoS):
- Port 8080: 21.1 Mbps (matches 20 Mbps rule)
✅ - Port 8081: 10.6 Mbps (matches 10 Mbps rule)
✅ - Port 5201: 3.84 Mbps (matches 3 Mbps default rule)
✅
Without Fix (All Traffic Uses Default QoS):
- Port 8080: 3.84 Mbps (incorrectly uses default rule)
❌ - Port 8081: 3.48 Mbps (incorrectly uses default rule)
❌ - Port 5201: 3.66 Mbps (correctly uses default rule)
✅
Conclusion
The fix ensures that traffic is correctly classified to the appropriate QoS flow based on SDF filters and precedence values, enabling differentiated QoS treatment per application.
Files Changed
-
src/upf_app/bpf/rules/pdr_and_far/pfcp_session_lookup_xdp_kernel.c- Precedence-based selection logic
- SDF filter key fix for QFI-ignore mode
- Enhanced debug logging and 3GPP compliance comments
- eBPF rodata configuration structure
-
src/upf_app/programs/pfcp_session_lookup_xdp_user.cpp- Userspace rodata configuration -
src/upf_app/include/types.h- Shared configuration struct definition -
src/upf_app/upf_config.hpp- Configuration field addition -
src/upf_app/upf_config_yaml.hpp- YAML configuration interface -
src/upf_app/upf_config_yaml.cpp- YAML parsing and mapping -
src/upf_app/SessionManager.cpp- Enhanced logging -
src/upf_app/SessionProgramManager.cpp- Fix and ensures PDRs are processed in precedence order
Configuration
QFI-ignore mode can be configured via YAML configuration file:
support_features:
ignore_qfi_for_uplink: yes # For gNB without multiple QFI per TEID support
Default Value: true (enabled by default)
The configuration is implemented using eBPF rodata, allowing runtime configuration without recompilation:
- Defined in
struct pdr_lookup_config(shared between BPF and userspace) - Configured from userspace before BPF program load
Backwards Compatibility
Fully backwards compatible - QFI-ignore mode is enabled by default (ignore_qfi_for_uplink: true). Operators can disable it by setting ignore_qfi_for_uplink: no in the YAML configuration if their gNB supports multiple QFI per TEID.