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_uplink mode 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

  1. Deploy core network using oai-cn5g-fed/docker-compose/docker-compose-basic-nrf-qos.yaml
  2. Apply patch to create QoS rules for UE with SUPI 208950000000035 multiqos.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.

Edited by Tariro Mukute

Merge request reports

Loading