Skip to content

NEF PFD Management Implementation and Testing

Note

Author: Yu-Chen, Chan
Date: 2025/11/19

1. Introduction

This experiment conducts in-depth research and testing on the Network Exposure Function (NEF) in the free5GC open-source 5G core network system, with a particular focus on the implementation and validation of Packet Flow Description (PFD) Management functionality. PFD Management is a critical feature defined in the 3GPP standards, allowing Application Functions (AF) to describe application traffic characteristics to the core network through NEF, thereby enabling application-level traffic control and optimization.

In this experiment, we established a complete testing environment to simulate interactions between AF and NEF, implementing PFD transaction creation, query, and update operations. During the experiment, we discovered and fixed three critical bugs in the free5GC source code, including NEF's lack of automatic AF creation mechanism, UDR response logic errors, and API version mismatches. Through these fixes, we successfully verified that NEF's PFD Management functionality complies with the 3GPP TS 29.522 standard and can correctly perform data persistence operations with the UDR (Unified Data Repository).

This experiment not only validates the functional correctness of free5GC but also provides complete automated test scripts and detailed operation guides, laying an important foundation for subsequent 5G network application development and testing. The experimental results demonstrate that the repaired system can stably handle PFD management requests, establishing a foundation for implementing application-aware 5G network services.

Test Scope Statement: All tests in this document are conducted on a single-machine loopback (127.0.0.x) deployment without RAN/UE connection, validating only the NEF↔UDR data plane and persistence mechanism, excluding actual user-plane traffic steering. This experiment focuses on validating the correctness of the PFD Management API and the data persistence workflow.

2. Background and Motivation

2.1 5G Network Exposure Function (NEF)

In the 5G core network architecture, the Network Exposure Function (NEF) plays a critical bridging role, responsible for securely exposing core network capabilities to external applications. According to the 3GPP TS 23.501 standard, the main functions of NEF include:

  1. Capability Exposure: Provides standardized RESTful API interfaces (defined in TS 29.522), allowing third-party applications to use network functions
  2. Security Control: Performs authentication, authorization, and traffic management on external requests to protect the core network. According to 3GPP standards, NEF northbound API should use OAuth2 Client Credentials or CAPIF (Common API Framework) for authorization verification
  3. Information Translation: Performs data format conversion and parameter mapping between external APIs and internal network functions
  4. Event Notification: Notifies subscribed applications of network events (such as QoS changes, location updates)

Security Statement for This Experiment: This test environment is based on a closed laboratory network (127.0.0.x loopback), without OAuth2/CAPIF authorization mechanisms and TLS encryption enabled. NEF does not authenticate AF. This configuration is only suitable for development and functional verification and is not recommended for production environments. In production deployments, complete security mechanisms should be implemented according to 3GPP TS 29.522 specifications.

NEF is an important network function in the 5G Service-Based Architecture (SBA), interacting with other network functions through Service-Based Interfaces.

2.2 Packet Flow Description (PFD) Management

PFD Management is one of the important services provided by NEF, defined in the 3GPP TS 29.522 specification. This service allows Application Functions (AF) to provide application traffic description information to the 5G core network, enabling the network to identify and process data flows of specific applications.

Main Purposes:

  • Application Detection: Allows AF to describe application traffic characteristics to the core network, enabling SMF/UPF to identify application traffic
  • Traffic Optimization: Performs more precise QoS control and routing decisions based on application layer information
  • Dynamic Update: Supports applications to dynamically update their traffic descriptions at runtime (through PUT/PATCH operations)
  • Cross-Domain Management: Implements information exchange between the application layer (AF) and network layer (SMF/PCF)

PFD Components (according to TS 29.522):

  • Flow Descriptions: Traffic filtering rules based on IP 5-tuple (source/destination IP, port, protocol), defined in TS 29.212
  • URLs: URL patterns used by applications, supporting regular expression matching
  • Domain Names: Domain names accessed by applications, supporting wildcards (such as *.example.com)

Application Scenarios:

  • Traffic optimization for video streaming services
  • Low-latency routing for gaming applications
  • Dedicated network slices for enterprise applications

2.3 Experimental Motivation

The main motivations for this experiment include:

  1. Functional Verification: Verify whether NEF's PFD Management functionality in free5GC complies with 3GPP standards
  2. Interoperability Testing: Test interactions between NEF and other network functions (UDR, PCF)
  3. Issue Discovery: Identify and fix errors that may exist in the implementation
  4. Test Automation: Establish reusable test environments and scripts
  5. Documentation Improvement: Provide developers with detailed operation guides and best practices

3. Data Flow

  1. AF → NEF: Application sends PFD management requests through the 3gpp-pfd-management API
  2. NEF → NRF: NEF queries NRF for UDR service discovery information (Service Discovery)
  3. NEF → UDR: NEF forwards PFD data to UDR for persistent storage through the Nudr_DataRepository (TS 29.510) service
  4. UDR → MongoDB: UDR writes PFD data to the applicationData.pfds collection in the MongoDB database
  5. NEF ← UDR: UDR returns operation results to NEF (201 Created or 200 OK)
  6. AF ← NEF: NEF returns results to AF

Service-Based Architecture (SBA) Explanation: This workflow is a typical example of 5G SBA architecture. NEF does not hardcode the UDR URL but performs service discovery through NRF to obtain UDR's nudr-dr service base URI (e.g., http://127.0.0.4:8000/nudr-dr/v1), then calls /application-data/pfds/{appId} to store PFD. This dynamic discovery mechanism is the core design philosophy of SBA, enabling flexible deployment and scaling of network functions.

4. Core Concepts

4.1 Packet Flow Description (PFD)

Definition: According to 3GPP TS 29.522, PFD is a set of rules used to identify application traffic. Each PFD is identified by a unique pfdId and can contain one or more of the following attributes:

4.1.1 Flow Descriptions

Traffic description based on IP 5-tuple, defined in 3GPP TS 29.212 (Policy and Charging Control):

permit <direction> <protocol> from <source> <port> to <destination> <port>

Syntax Elements:

  • <direction>: in (downlink) or out (uplink)
  • <protocol>: ip, tcp, udp, etc.
  • <source>/<destination>: IP address or CIDR notation
  • <port>: Port number or any

Examples:

  • permit in ip from 192.168.1.0/24 to any
  • permit out tcp from any to 10.0.0.0/8 8080

Note: The flowDescriptions syntax follows the format of 3GPP TS 29.212 (PCC rules / traffic filters). free5GC directly accepts this string format and passes it down to SMF/UPF for traffic identification and policy control. This explains why you see this firewall-rule-like string format in the JSON payload rather than structured five-tuple objects.

4.1.2 URLs

URL pattern matching using regular expressions (PCRE format):

^https://example\.com(/\S*)?$

Purpose: Used to identify HTTP/HTTPS-based application traffic

4.1.3 Domain Names

List of domain names used by applications, supporting wildcards:

["example.com", "*.example.org", "api.service.net"]

Purpose: Used for DNS query interception and traffic identification

4.2 PFD Transaction

PFD Transaction is the basic unit for NEF to manage PFD data, containing:

  • Transaction ID: Unique identifier, automatically generated by NEF
  • AF ID (scsAsID): Application Function identifier that initiates the request. In 3GPP TS 29.522, it's called scsAsId (SCS/AS Identifier). In this experiment, "AF001" is used as the example AF identifier
  • PFD Datas: Mapping of PFD data for one or more applications

Structure example:

{
  "self": "http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions/6",
  "pfdDatas": {
    "app_1761902997": {
      "externalAppId": "app_1761902997",
      "self": "http://127.0.0.5:8000/.../applications/app_1761902997",
      "pfds": {
        "pfd001": { ... },
        "pfd002": { ... }
      }
    }
  }
}

4.3 Application ID (External Application ID)

External Application ID is a globally unique identifier used to identify applications:

  • Provided by AF, remains unique throughout the core network
  • Used to correlate application information between different network functions
  • This experiment uses timestamps to ensure uniqueness: app_$(date +%s)

4.4 NEF Roles

In PFD Management, NEF plays the following roles:

4.4.1 API Gateway

  • Provides standardized RESTful API interfaces
  • Handles HTTP/JSON format requests and responses
  • Implements API specifications defined in 3GPP TS 29.522

4.4.2 Data Translator

  • Converts external API's PfdData to internal PfdDataForApp format
  • Handles mapping relationships between different data models

Conversion example:

// External format (PfdData) - Used for Nnef API
type PfdData struct {
    ExternalAppId string          // External application identifier
    Pfds          map[string]Pfd  // PFD mapping, keyed by pfdId
}

// Internal format (PfdDataForApp) - Used for Nudr API
type PfdDataForApp struct {
    ApplicationId string         // Application identifier
    Pfds          []PfdContent   // PFD array (not map)
}

Conversion Explanation:

  • NEF receives map[string]Pfd format data from AF
  • NEF sends []PfdContent format data to UDR
  • externalAppId maps to applicationId
  • Map structure converts to Array structure to conform to UDR's data model

4.4.3 Transaction Manager

  • Creates and manages PFD Transactions
  • Generates unique Transaction IDs
  • Maintains associations between AF and Transactions

4.4.4 Service Consumer

  • Interacts with NRF for service discovery
  • Interacts with UDR for data persistence
  • Handles authentication and authorization between services

4.5 Service-Based Interface (SBI)

4.5.1 Nnef_PFDManagement Service

NEF's externally provided PFD management service, compliant with 3GPP TS 29.522 (5G System; Network Exposure Function Northbound APIs) specifications, including the following operations:

Operation HTTP Method URI Pattern Function Success Status Code
Create Transaction POST /{scsAsId}/transactions Create new PFD transaction 201 Created
Get Transactions GET /{scsAsId}/transactions Get all transactions 200 OK
Get Transaction GET /{scsAsId}/transactions/{transId} Get specific transaction 200 OK
Update Transaction PUT /{scsAsId}/transactions/{transId} Update transaction content 200 OK
Delete Transaction DELETE /{scsAsId}/transactions/{transId} Delete transaction 204 No Content
Update Application PUT /{scsAsId}/transactions/{transId}/applications/{appId} Update application PFD 200 OK
Patch Application PATCH /{scsAsId}/transactions/{transId}/applications/{appId} Partially update PFD 200 OK
Delete Application DELETE /{scsAsId}/transactions/{transId}/applications/{appId} Delete application PFD 204 No Content

Note: scsAsId is the AF (Application Function) identifier. In this experiment, "AF001" is used.

4.5.2 Nudr_DataRepository Service

Data storage service provided by UDR, compliant with 3GPP TS 29.504 (5G System; Unified Data Repository Services) / TS 29.510 (Network Function Repository Services) specifications:

Operation HTTP Method URI Pattern Function Success Status Code
Create/Update PFD PUT /application-data/pfds/{appId} Store/update PFD data 201 Created / 200 OK
Query PFD GET /application-data/pfds/{appId} Query specific application's PFD 200 OK
Query Multiple PFDs GET /application-data/pfds?appId={id1}&appId={id2} Batch query multiple applications 200 OK
Delete PFD DELETE /application-data/pfds/{appId} Delete PFD data 204 No Content

Note: PUT operation returns different status codes based on whether the resource already exists:

  • 201 Created: Resource created for the first time
  • 200 OK: Resource already exists and is updated

Important Note: MongoDB is not a 3GPP-defined network function but rather the backend database implementation for UDR in free5GC. UDR, as a 3GPP NF, provides the standardized Nudr_DataRepository API, while the actual data is stored in MongoDB.

4.6 Data Persistence

4.6.1 MongoDB Data Structure

// Collection: applicationData.pfds
{
  "_id": ObjectId("..."),
  "applicationId": "app_1761902997",
  "pfds": [
    {
      "pfdId": "pfd001",
      "flowDescriptions": [
        "permit in ip from 192.168.1.0/24 to any",
        "permit out ip from any to 10.0.0.0/8"
      ],
      "urls": ["https://example.com/app"],
      "domainNames": ["example.com"]
    },
    {
      "pfdId": "pfd002",
      "flowDescriptions": [
        "permit in ip from 172.16.0.0/12 to any"
      ]
    }
  ]
}

4.6.2 Data Lifecycle

  1. Create: AF sends POST request, NEF creates Transaction, UDR stores to MongoDB
  2. Update: AF sends PUT/PATCH request, UDR updates data in MongoDB
  3. Query: Network functions (such as SMF) can query PFD data from UDR for traffic identification
  4. Delete: AF sends DELETE request, UDR deletes data from MongoDB

4.7 Error Handling

4.7.1 HTTP Status Code Definitions

According to 3GPP TS 29.522 and REST standards, NEF PFD Management uses the following status codes:

HTTP Status Code Type Meaning Use Case
200 OK Success Operation completed successfully PUT/GET update or query succeeded
201 Created Success Resource created successfully POST creates transaction, PUT creates new PFD
204 No Content Success Deletion successful, no return content DELETE request succeeded
400 Bad Request Client Error Request format error JSON format error, parameter validation failed
403 Forbidden Client Error Insufficient permissions AF not authorized to access resource
404 Not Found Client Error Resource does not exist Transaction/Application ID does not exist
409 Conflict Client Error Resource conflict Application ID duplicate
500 Internal Server Error Server Error Internal processing error Database connection failed, internal logic error
503 Service Unavailable Server Error Service temporarily unavailable UDR service unreachable

4.7.2 PFD Report (Error Report)

When PFD data processing fails, NEF returns a PfdReport:

{
  "pfdReports": {
    "APP_ID_DUPLICATED": {
      "externalAppIds": ["app001"],
      "failureCode": "APP_ID_DUPLICATED"
    }
  }
}

Failure Codes:

  • APP_ID_DUPLICATED: Application ID is already used by another AF/Transaction
  • MALFUNCTION: System internal error (such as UDR connection failure)

Note: Implementation details (error code naming, return fields, specific behavior) may vary slightly depending on free5GC version and commit. The above description is based on the v4.0.1 version tested in this experiment.

5. Experiment and Testing

5.1 Experimental Environment Preparation

5.1.1 System Requirements

# Operating System Information
OS: Ubuntu 25.04
Kernel: Linux 6.14.0-34-generic
Shell: /bin/bash

# Software Requirements

- Go 1.22.5
- MongoDB 7.x
- mongosh 2.5.8
- free5GC v4.0.1 (based on main branch as of 2025-10-31)
- curl 8.12.1
- jq (JSON processing tool)

Version Notes:

  • This experiment is based on free5GC v4.0.1 for testing and patching.
  • This experiment uses bare-metal deployment, manually starting all network functions using ./run.sh, rather than docker-compose or free5gc-compose containerized deployment.
  • If readers use different versions or deployment methods, they may need to adjust relevant configurations and scripts.

5.1.2 Starting free5GC

# Enter free5GC directory
cd /free5gc

# Start all network functions
./run.sh

# Verify service status
ps aux | grep -E "nef|pcf|udr|nrf" | grep -v grep

Expected Output:

ubuntu25   65043  0.0  0.2 1244292 22048 ?  Sl  09:04  0:00 ./bin/nrf -c ./config/nrfcfg.yaml
ubuntu25   65100  0.0  0.2 1246576 22408 ?  Sl  09:04  0:00 ./bin/pcf -c ./config/pcfcfg.yaml
ubuntu25   65184  0.0  0.1 1239492 16412 ?  Sl  09:04  0:00 ./bin/nef -c ./config/nefcfg.yaml
ubuntu25   65200  0.2  0.2 1244400 21588 ?  Sl  09:04  0:00 ./bin/udr -c ./config/udrcfg.yaml

5.2 Discovered and Fixed Bugs

5.2.1 Bug #1: NEF Lacks Automatic AF Creation Mechanism

Problem Description:
In the original code, NEF checks whether the AF exists when processing POST requests. If it doesn't exist, it directly returns a 404 error, preventing first-time AFs from creating PFD Transactions.

File Location: nef/internal/sbi/processor/pfd.go

Original Code:

af := nefCtx.GetAf(scsAsID)
if af == nil {
    return &HandlerResponse{http.StatusNotFound, nil, openapi.ProblemDetailsDataNotFound(DetailNoAF)}
}

Problem Analysis:

  • AF should be automatically created on first use, not pre-registered
  • This does not align with the design philosophy of 3GPP standards
  • Requires manual pre-creation of AF during testing

Fixed Code:

af := nefCtx.GetAf(scsAsID)
if af == nil {
    // Auto-create AF if it doesn't exist
    af = nefCtx.NewAf(scsAsID)
    nefCtx.AddAf(af)
}

Reasons for Fix:

  1. Compliant with Standards: 3GPP TS 29.522 does not require AF pre-registration
  2. Simplified Operations: Reduces testing and deployment complexity
  3. Improved Usability: AF can dynamically start using NEF services

Considerations:

  • This fix is suitable for development and testing environments, simplifying the AF usage workflow
  • In production environments, the following issues need to be considered:
  • Concurrency Safety: If two POST requests are initiated for the same AF ID almost simultaneously, there may be a race condition
  • State Persistence: AF context may be lost after NEF restart, requiring database persistence or AF re-registration mechanism
  • Authorization Control: Should be combined with OAuth2/CAPIF mechanisms to ensure only legitimate AFs can create transactions

5.2.2 Bug #2: UDR Response Handling Logic Error

Problem Description:
The UDR's PutApplicationDataIndividualPfdToDBProcedure function, when processing PUT requests, executes c.JSON() call twice regardless of whether the data is newly created or updated, causing the second call to fail (because the HTTP response has already been sent).

File Location: udr/internal/sbi/processor/default.go

Original Code:

if existed {
    c.JSON(http.StatusOK, data)
}
c.JSON(http.StatusCreated, data)  // ← Always executes, causing error

Problem Analysis:

  • Missing else branch, causing logic error
  • When data already exists, it first returns 200 OK, then attempts to return 201 Created again
  • Go's gin framework generates a warning on the second c.JSON() call: [GIN] Headers were already written
  • Although the response has been sent to the client, the gin framework logs the error, affecting log readability
  • The second call does not change the sent HTTP response but violates correct programming practices

Fixed Code:

if existed {
    c.JSON(http.StatusOK, data)
} else {
    c.JSON(http.StatusCreated, data)
}

Reasons for Fix:

  1. Compliant with HTTP Semantics: Newly created resource returns 201, updated existing resource returns 200
  2. Fixes Response Error: Avoids errors caused by double responses
  3. Compliant with REST Standards: Correct behavior for PUT requests

5.2.3 Bug #3: API Version Mismatch

Problem Description:
UDR uses the API path prefix /nudr-dr/v2, while NEF as a UDR client uses /nudr-dr/v1, causing all API calls to UDR to return 404.

File Location: udr/pkg/factory/config.go

Original Code:

UdrDrResUriPrefix = "/nudr-dr/v2"

Problem Analysis:

  • NEF's OpenAPI client generated code uses v1 version
  • UDR's routing configuration uses v2 version
  • Version mismatch causes route not found, returning 404
  • This is a configuration inconsistency issue

Verifying the Problem:

# Test v1 endpoint (fails)
curl -X GET http://127.0.0.4:8000/nudr-dr/v1/application-data/pfds
# Returns: 404 page not found

# Test v2 endpoint (succeeds)
curl -X GET http://127.0.0.4:8000/nudr-dr/v2/application-data/pfds
# Returns: []

Fixed Code:

UdrDrResUriPrefix = "/nudr-dr/v1"  // Changed to v1 to match NEF

Reasons for Fix:

  1. Maintain Consistency: All components use the same API version
  2. Match OpenAPI Specifications: NEF's client code is generated based on v1 specifications
  3. Avoid Regenerating Code: Modifying UDR's configuration is simpler than regenerating NEF's client code

Important Notes:

  • The spirit of this fix is "to make UDR compatible with the current NEF-generated v1 API client implementation", not to indicate that v2 API is definitely wrong
  • In the future, free5GC may uniformly upgrade all components to v2 API, which may require reverse adjustment or synchronous upgrade
  • This is a temporary measure during the version transition period; ultimately, the free5GC project should uniformly decide which API version to use

Verifying the Fix:

# Test v1 endpoint (should succeed now)
curl -X GET http://127.0.0.4:8000/nudr-dr/v1/application-data/pfds
# Returns: []

5.3 Creating Test Scripts

5.3.1 NEF Log Monitoring Script

File: /monitor_nef.sh

#!/bin/bash

# NEF Log Monitor Script
# Automatically find the latest log file
LOG_DIR=$(ls -td /home/ubuntu25/free5gc/log/*/ 2>/dev/null | head -1)
LOG_FILE="${LOG_DIR}free5gc.log"

echo "=========================================="
echo "Monitoring NEF Logs"
echo "Log File: $LOG_FILE"
echo "=========================================="
echo ""
echo "Waiting for NEF activity..."
echo ""

# Monitor NEF logs in real-time
tail -f "$LOG_FILE" | grep --line-buffered -E "NEF|nnef-pfdmanagement|PFD" | while read line; do
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $line"
done

Usage:

chmod +x /monitor_nef.sh

# Execute in a new terminal window
/monitor_nef.sh

Functional Description:

  • Real-time monitoring of NEF log output
  • Filters out logs related to PFD Management
  • Adds timestamps for easier analysis

5.3.2 PCF Log Monitoring Script (Optional)

Important Note: PCF is not involved in this experiment's PFD Management workflow. PCF is primarily used for NEF's another service: Traffic Influence (traffic routing control for specific UEs), interacting with NEF through the Npcf_PolicyAuthorization interface. This experiment only focuses on PFD Management + UDR persistence, so no related activity will be seen in PCF logs.

The following script is for reference only, applicable for future expansion of the experiment to per-UE traffic influence scenarios.

File: /monitor_pcf.sh

#!/bin/bash

# PCF Log Monitor Script
# Automatically find the latest log file
LOG_DIR=$(ls -td /home/ubuntu25/free5gc/log/*/ 2>/dev/null | head -1)
LOG_FILE="${LOG_DIR}free5gc.log"

echo "=========================================="
echo "Monitoring PCF Logs"
echo "Log File: $LOG_FILE"
echo "=========================================="
echo ""
echo "Waiting for PCF activity..."
echo ""

# Monitor PCF logs in real-time
tail -f "$LOG_FILE" | grep --line-buffered -E "PCF|npcf-policyauthorization|PolicyAuthorization" | while read line; do
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $line"
done

Usage:

chmod +x /monitor_pcf.sh

# Execute in a new terminal window (only for traffic influence experiments)
/monitor_pcf.sh

5.3.3 AF PFD Test Main Script

File: /af_pfd_test.sh

#!/bin/bash

# AF PFD Management Test Script
# This script demonstrates the PFD transaction creation and data upload via NEF

NEF_BASE_URL="http://127.0.0.5:8000"
AF_ID="AF001"
APP_ID="app_$(date +%s)"  # Use timestamp to make it unique

echo "=========================================="
echo "AF PFD Management Experiment"
echo "=========================================="
echo "App ID: $APP_ID"
echo ""

# Step 2: Create PFD Transaction (POST)
echo "[Step 2] Creating PFD Transaction..."
echo "Endpoint: POST ${NEF_BASE_URL}/3gpp-pfd-management/v1/${AF_ID}/transactions"
echo ""

POST_RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" -X POST \
  "${NEF_BASE_URL}/3gpp-pfd-management/v1/${AF_ID}/transactions" \
  -H "Content-Type: application/json" \
  -d "{
    \"pfdDatas\": {
      \"$APP_ID\": {
        \"externalAppId\": \"$APP_ID\",
        \"pfds\": {
          \"pfd001\": {
            \"pfdId\": \"pfd001\",
            \"flowDescriptions\": [
              \"permit in ip from 192.168.1.0/24 to any\"
            ]
          }
        }
      }
    }
  }")

HTTP_STATUS=$(echo "$POST_RESPONSE" | grep "HTTP_STATUS" | cut -d: -f2)
POST_BODY=$(echo "$POST_RESPONSE" | sed '/HTTP_STATUS/d')

echo "Response Status: $HTTP_STATUS"
echo "Response Body:"
echo "$POST_BODY" | jq . 2>/dev/null || echo "$POST_BODY"
echo ""

if [ "$HTTP_STATUS" == "201" ] || [ "$HTTP_STATUS" == "200" ]; then
    echo " POST Transaction Created Successfully!"
    # Extract transaction ID from the self URI
    TRANSACTION_ID=$(echo "$POST_BODY" | jq -r '.self' | grep -oP '/transactions/\K[^/]+$')
    echo "Transaction ID: $TRANSACTION_ID"
else
    echo " POST Transaction Failed!"
    exit 1
fi

echo ""
echo "=========================================="
echo ""

# Step 3: Upload PFD Data (PUT)
echo "[Step 3] Uploading PFD Data..."
echo "Endpoint: PUT ${NEF_BASE_URL}/3gpp-pfd-management/v1/${AF_ID}/transactions/${TRANSACTION_ID}"
echo ""

PUT_RESPONSE=$(curl -s -w "\nHTTP_STATUS:%{http_code}" -X PUT \
  "${NEF_BASE_URL}/3gpp-pfd-management/v1/${AF_ID}/transactions/${TRANSACTION_ID}" \
  -H "Content-Type: application/json" \
  -d "{
    \"pfdDatas\": {
      \"$APP_ID\": {
        \"externalAppId\": \"$APP_ID\",
        \"pfds\": {
          \"pfd001\": {
            \"pfdId\": \"pfd001\",
            \"flowDescriptions\": [
              \"permit in ip from 192.168.1.0/24 to any\",
              \"permit out ip from any to 10.0.0.0/8\"
            ],
            \"urls\": [
              \"https://example.com/app\"
            ],
            \"domainNames\": [
              \"example.com\"
            ]
          },
          \"pfd002\": {
            \"pfdId\": \"pfd002\",
            \"flowDescriptions\": [
              \"permit in ip from 172.16.0.0/12 to any\"
            ]
          }
        }
      }
    }
  }")

HTTP_STATUS=$(echo "$PUT_RESPONSE" | grep "HTTP_STATUS" | cut -d: -f2)
PUT_BODY=$(echo "$PUT_RESPONSE" | sed '/HTTP_STATUS/d')

echo "Response Status: $HTTP_STATUS"
echo "Response Body:"
echo "$PUT_BODY" | jq . 2>/dev/null || echo "$PUT_BODY"
echo ""

if [ "$HTTP_STATUS" == "200" ] || [ "$HTTP_STATUS" == "204" ]; then
    echo " PUT Data Uploaded Successfully!"
else
    echo " PUT Data Upload Failed!"
fi

echo ""
echo "=========================================="
echo "Experiment Completed!"
echo "=========================================="
echo ""
LOG_DIR=$(ls -td /home/ubuntu25/free5gc/log/*/ 2>/dev/null | head -1)
LOG_FILE="${LOG_DIR}free5gc.log"
echo "Please check the following logs:"
echo "  - Log File: ${LOG_FILE}"
echo "  - You can use: tail -100 ${LOG_FILE} | grep -E 'NEF|UDR'"
echo ""
echo "To verify data in MongoDB:"
echo "  mongosh --eval \"use free5gc; db.getCollection('applicationData.pfds').find({applicationId: '$APP_ID'}).pretty();\""
echo ""

Usage:

chmod +x /af_pfd_test.sh
/af_pfd_test.sh

5.4 Executing the Experiment

5.4.1 Preparation Phase

# 1. Ensure free5GC is running
ps aux | grep -E "nef|udr|nrf" | grep -v grep

# 2. Ensure MongoDB is running
ps aux | grep mongod | grep -v grep

# 3. Start log monitoring (open new terminal windows)
# Terminal 2:
/monitor_nef.sh > /tmp/nef_monitor.log 2>&1 &

# Note: PCF monitoring is not needed in this experiment (PCF is not involved in PFD Management workflow)
# For subsequent traffic influence experiments, optionally use:
# /monitor_pcf.sh > /tmp/pcf_monitor.log 2>&1 &

5.4.2 Executing Tests

# Terminal 1: Execute test script
/af_pfd_test.sh

Expected Output:

==========================================
AF PFD Management Experiment
==========================================
App ID: app_1761902997
[Step 2] Creating PFD Transaction...
Endpoint: POST http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions
Response Status: 201
Response Body:
{
  "self": "http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions/6",
  "pfdDatas": {
    "app_1761902997": {
      "externalAppId": "app_1761902997",
      "self": "http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions/6/applications/app_1761902997",
      "pfds": {
        "pfd001": {
          "pfdId": "pfd001",
          "flowDescriptions": [
            "permit in ip from 192.168.1.0/24 to any"
          ]
        }
      }
    }
  }
}
 POST Transaction Created Successfully!
Transaction ID: 6
==========================================
[Step 3] Uploading PFD Data...
Endpoint: PUT http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions/6
Response Status: 200
Response Body:
{
  "self": "http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions/6",
  "pfdDatas": {
    "app_1761902997": {
      "externalAppId": "app_1761902997",
      "self": "http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions/6/applications/app_1761902997",
      "pfds": {
        "pfd001": {
          "pfdId": "pfd001",
          "flowDescriptions": [
            "permit in ip from 192.168.1.0/24 to any",
            "permit out ip from any to 10.0.0.0/8"
          ],
          "urls": [
            "https://example.com/app"
          ],
          "domainNames": [
            "example.com"
          ]
        },
        "pfd002": {
          "pfdId": "pfd002",
          "flowDescriptions": [
            "permit in ip from 172.16.0.0/12 to any"
          ]
        }
      }
    }
  }
}
 PUT Data Uploaded Successfully!
==========================================
Experiment Completed!
==========================================

5.5 Results Verification

5.5.1 NRF Service Discovery Verification

Before NEF processes PFD Management requests, NEF first queries NRF for UDR service endpoints. This is a core mechanism of 5G SBA architecture.

# View NEF and NRF interaction logs
LOG_FILE=$(ls -t /home/ubuntu25/free5gc/log/*/free5gc.log 2>/dev/null | head -1)
tail -100 "$LOG_FILE" | grep -E "NEF.*Discovery|NEF.*Nnrf|SearchNFInstances.*UDR"

Key Log Examples:

time="..." level="info" msg="SearchNFInstances with nfType[UDR]" CAT="Nnrf" NF="NEF"
time="..." level="debug" msg="URI from NRF: http://127.0.0.4:8000" NF="NEF"

These logs prove that NEF doesn't hardcode UDR's URL but dynamically discovers UDR's nudr-dr service endpoint through NRF.

5.5.2 NEF Log Verification

# View NEF logs for processing POST requests
# Automatically get the latest log file
LOG_FILE=$(ls -t /home/ubuntu25/free5gc/log/*/free5gc.log 2>/dev/null | head -1)
tail -100 "$LOG_FILE" | grep -A 5 "PostPFDManagementTransactions"

Key Logs:

time="2025-10-31T09:29:57.449161053Z" level="info" msg="PostPFDManagementTransactions - scsAsID[AF001]" CAT="PFDMng" NF="NEF"
time="2025-10-31T09:29:57.449259200Z" level="info" msg="New pfd transcation" AFID="AF:AF001" CAT="CTX" NF="NEF" PfdTRID="PFDT:6"
time="2025-10-31T09:29:57.449273884Z" level="info" msg="appID[app_1761902997] is added" AFID="AF:AF001" CAT="CTX" NF="NEF" PfdTRID="PFDT:6"
time="2025-10-31T09:29:57.453961420Z" level="info" msg="PFD Management Transaction is added" AFID="AF:AF001" CAT="CTX" NF="NEF" PfdTRID="PFDT:6"
time="2025-10-31T09:29:57.454032294Z" level="info" msg="AF is added" AFID="AF:AF001" CAT="CTX" NF="NEF"
time="2025-10-31T09:29:57.454071110Z" level="info" msg="| 201 |       127.0.0.1 | POST    | /3gpp-pfd-management/v1/AF001/transactions | " CAT="GIN" NF="NEF"

# View NEF logs for processing PUT requests
# Automatically get the latest log file
LOG_FILE=$(ls -t /home/ubuntu25/free5gc/log/*/free5gc.log 2>/dev/null | head -1)
tail -100 "$LOG_FILE" | grep -A 3 "PutIndividualPFDManagementTransaction"

Key Logs:

time="2025-10-31T09:29:57.466704798Z" level="info" msg="PutIndividualPFDManagementTransaction - scsAsID[AF001], transID[6]" CAT="PFDMng" NF="NEF"
time="2025-10-31T09:29:57.466762809Z" level="info" msg="appID[app_1761902997] is added" AFID="AF:AF001" CAT="CTX" NF="NEF" PfdTRID="PFDT:6"
time="2025-10-31T09:29:57.472831410Z" level="info" msg="| 200 |       127.0.0.1 | PUT     | /3gpp-pfd-management/v1/AF001/transactions/6 | " CAT="GIN" NF="NEF"

5.5.3 UDR Log Verification

# View UDR logs for processing PFD storage requests
# Automatically get the latest log file
LOG_FILE=$(ls -t /home/ubuntu25/free5gc/log/*/free5gc.log 2>/dev/null | head -1)
tail -100 "$LOG_FILE" | grep -E "UDR.*application-data/pfds"

Key Logs:

time="2025-10-31T09:29:57.453494418Z" level="info" msg="| 201 |       127.0.0.1 | PUT     | /nudr-dr/v1/application-data/pfds/app_1761902997 | " CAT="GIN" NF="UDR"
time="2025-10-31T09:29:57.472671073Z" level="info" msg="| 200 |       127.0.0.1 | PUT     | /nudr-dr/v1/application-data/pfds/app_1761902997 | " CAT="GIN" NF="UDR"

Explanation: The first PUT returns 201 Created (new resource), the second PUT returns 200 OK (updated existing resource), compliant with REST PUT semantics.

5.5.4 MongoDB Data Verification

# Connect to MongoDB and query PFD data
mongosh --eval "
  use free5gc;
  db.getCollection('applicationData.pfds').find(
    {applicationId: 'app_1761902997'}
  ).pretty();
"

Expected Output:

{
  _id: ObjectId("67237f2d8c8a9f0001234567"),
  applicationId: 'app_1761902997',
  pfds: [
    {
      pfdId: 'pfd001',
      flowDescriptions: [
        'permit in ip from 192.168.1.0/24 to any',
        'permit out ip from any to 10.0.0.0/8'
      ],
      urls: [ 'https://example.com/app' ],
      domainNames: [ 'example.com' ]
    },
    {
      pfdId: 'pfd002',
      flowDescriptions: [ 'permit in ip from 172.16.0.0/12 to any' ]
    }
  ]
}

5.5.5 API Interoperability Verification

# Test GET operation: Query PFD Transaction
curl -X GET http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions/6 | jq .

Expected Response: 200 OK, containing complete PFD data

# Test GET operation: Query specific application's PFD
curl -X GET "http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions/6/applications/app_1761902997" | jq .

Expected Response: 200 OK, containing that application's PFD data

5.5.6 DELETE Operation Complete Verification

This section verifies the completeness of PFD Transaction deletion operations, including data consistency across NEF, UDR, and MongoDB.

# Step 1: Delete PFD Transaction
echo "=== Step 1: Delete Transaction ==="
curl -v -X DELETE http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions/6

Expected Response: 204 No Content

# Step 2: Check NEF logs
echo "=== Step 2: Check NEF deletion logs ==="
LOG_FILE=$(ls -t /home/ubuntu25/free5gc/log/*/free5gc.log 2>/dev/null | head -1)
tail -50 "$LOG_FILE" | grep -E "DELETE.*transactions/6"

Expected Logs:

time="..." level="info" msg="| 204 |  127.0.0.1 | DELETE  | /3gpp-pfd-management/v1/AF001/transactions/6 | " CAT="GIN" NF="NEF"

# Step 3: Check if UDR executed DELETE operation
echo "=== Step 3: Check UDR deletion logs ==="
tail -50 "$LOG_FILE" | grep -E "DELETE.*application-data/pfds/app_1761902997"

Expected Logs:

time="..." level="info" msg="| 204 |  127.0.0.1 | DELETE  | /nudr-dr/v1/application-data/pfds/app_1761902997 | " CAT="GIN" NF="UDR"

# Step 4: Verify data has been deleted from MongoDB
echo "=== Step 4: Verify MongoDB data cleanup ==="
mongosh --eval "
  use free5gc;
  const count = db.getCollection('applicationData.pfds').countDocuments(
    {applicationId: 'app_1761902997'}
  );
  print('Records found: ' + count);
  if (count === 0) {
    print(' DELETE operation successful: MongoDB data cleaned');
  } else {
    print(' DELETE operation failed: MongoDB still has residual data');
  }
"

Expected Output:

Records found: 0
 DELETE operation successful: MongoDB data cleaned

Verification Explanation:

  • After NEF receives a DELETE request, it sends a corresponding DELETE request to UDR
  • UDR deletes the corresponding applicationData.pfds record from MongoDB
  • The entire deletion workflow ensures data consistency between NEF Transaction and UDR/MongoDB

Note: In MongoDB 5.0+, .count() has been deprecated; .countDocuments() method should be used instead.

5.6 Test Execution Summary

5.6.1 Test Results Confirmation

Actual Test Output:

==========================================
AF PFD Management Experiment
==========================================
App ID: app_1761922382
[Step 2] Creating PFD Transaction...
Response Status: 201
 POST Transaction Created Successfully!
Transaction ID: 17
[Step 3] Uploading PFD Data...
Response Status: 200
 PUT Data Uploaded Successfully!
==========================================

5.7 Performance Testing

Important Note: The following performance test results are based on a single-machine loopback (127.0.0.x) environment, without TLS or OAuth2 verification enabled, and without connection to actual RAN/UE or generation of real user traffic. This test is only for verifying the basic response time of NEF↔UDR↔MongoDB and cannot be directly used as a production environment SLA reference.

5.7.1 Single Operation Latency Test

# Test POST operation latency
time curl -s -X POST http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions \
  -H "Content-Type: application/json" \
  -d '{"pfdDatas":{"test_app":{"externalAppId":"test_app","pfds":{"pfd1":{"pfdId":"pfd1","flowDescriptions":["permit in ip from any to any"]}}}}}' \
  > /dev/null

Measured Results:

real    0m0.015s
user    0m0.003s
sys     0m0.003s

Analysis: POST operation average latency is approximately 15ms, as expected

5.7.2 Consecutive Operations Test

# Test creating 10 Transactions consecutively
for i in {1..10}; do
  APP_ID="perf_test_$i"
  curl -s -X POST http://127.0.0.5:8000/3gpp-pfd-management/v1/AF001/transactions \
    -H "Content-Type: application/json" \
    -d "{\"pfdDatas\":{\"$APP_ID\":{\"externalAppId\":\"$APP_ID\",\"pfds\":{\"pfd1\":{\"pfdId\":\"pfd1\",\"flowDescriptions\":[\"permit in ip from any to any\"]}}}}}" \
    | jq -r '.self'
done

Results: All 10 requests succeeded, average response time 20ms

5.8 Script Improvements

During actual testing, the following improvements were made to the original scripts:

5.8.1 Dynamic Log Path Detection

The original script used hardcoded log paths, unsuitable for different execution times. The improved script automatically detects the latest log directory:

# Before improvement
LOG_FILE="/free5gc/log/20251031_090404/free5gc.log"

# After improvement
LOG_DIR=$(ls -td /home/ubuntu25/free5gc/log/*/ 2>/dev/null | head -1)
LOG_FILE="${LOG_DIR}free5gc.log"

Advantages:

  • No need to manually modify log paths
  • Automatically uses the latest log file
  • Improves script reusability

5.8.2 Enhanced Output Messages

More detailed verification instructions were added at the end of the test script:

echo "To verify data in MongoDB:"
echo "  mongosh --eval \"use free5gc; db.getCollection('applicationData.pfds').find({applicationId: '$APP_ID'}).pretty();\""

This allows users to immediately verify whether data is correctly stored.

6. Conclusion

6.1 Summary of Experimental Results

This experiment successfully implemented complete testing and verification of NEF PFD Management functionality in free5GC, achieving the following important results:

6.1.1 Functional Verification

  • Verified that NEF's PFD Management API complies with 3GPP TS 29.522 standards
  • Confirmed correct data interaction workflow between NEF and UDR
  • Verified PFD data persistence mechanism in MongoDB
  • Tested complete CRUD operations (Create, Read, Update, Delete)

6.1.2 Bug Fixes and Improvements

  • Bug #1: Fixed NEF's lack of automatic AF creation mechanism
  • Bug #2: Fixed UDR response handling logic error
  • Bug #3: Resolved API version mismatch between NEF and UDR

These three bug fixes make free5GC's NEF functionality more complete and user-friendly, improving overall system stability.

References

About

Hello! I'm Yu-Chen Chan, and I've recently started exploring 5G technology and engaging with the free5GC community. I hope you find this blog post useful, and don't hesitate to reach out if you spot any mistakes or have recommendations for improvement.

Connect with Me