Understanding PCF

Theory

Intro to PCF

PCF is an abbreviation for Policy Control Function. As stated in the name, PCF responsible for managing network policies, ensuring efficient and proper network operations. PCF play a vital role in network slicing, policy enforcement, Charging rules and Quality of service management. Due to the existance of PCF, a network could:

a. Enhance User Experience by managing QoS and ensure that resource are allocated efficiently.

b. Increase Network Utilization by helping to optimize the use of network resources by dynamically adjusting policies based on network conditions.

c. Support for Network Slicing by create and manage QoS for each slices that of course has it's own specific QoS.

Illustrations

Consider this scenario, there are two customer let's say A and B. Customer A want to backup terabytes of file to a cloud while consumer B operate a Wireless Sensor Network. Both use case need different QoS. PCF will handle both customer by:

  1. Policy Creation, provide a storage and API to create and store policies created by network operators

  2. Apply policies by evaluating request based on predefined policies and real-time network conditions

  3. Enforcing Policy by communicating to other network such as AMF and SMF that enforce these policies. It ensures each use case requirement is satisfied.

Service Provided by PCF

AM Policy Association Management

  1. AM Policy Association Establishment

image

Steps:
a. The AMF receives the registration request from the AN. Based on local policy, the AMF selects to contact the (V-) PCF to create the policy association with the (V-) PCF and to retrieve Access and Mobility control policy. The AMF selects the PCF as described in subclause 8.2 and invokes the Npcf_AMPolicyControl_Create service operation by sending the HTTP POST request to the "AM Policy Associations" resource as defined in subclause 4.2.2 and subclause 5.3.2.3.1 of 3GPP TS 29.507. The request operation provides the SUPI and the allowed NSSAI if applicable, and if received from the UDM, the Service Area Restrictions, RFSP index, GPSI and a list of Internal Group Identifiers, and may provide the access type, the PEI if received in the AMF, the User Location Information if available, the UE Time Zone if available, Serving Network, RAT type, GUAMI of AMF, alternative or backup address(es) of AMF and trace control and configuration parameters information. The request includes a Notification URI to indicate to the PCF where to send a notification when the policy is updated.

b.If the PCF does not have the subscription data, it invokes the Nudr_DataRepository_Query service operation to the UDR by sending an HTTP GET request to the "AccessAndMobilityPolicyData" resource as specified in TS 29.519.

c. The UDR sends an HTTP "200 OK" response to the PCF with the subscription data.

d. The PCF may request notifications from the UDR on changes in the subscription information by invoking Nudr_DataRepository_Subscribe service operation by sending an HTTP POST request to the
"PolicyDataSubscriptions" resource as specified in 3GPP TS 29.519.

e. The UDR sends an HTTP "201 Created" response to acknowledge the subscription from the PCF.

f. The (V-)PCF makes the requested policy decision including Access and Mobility control policy information, and may determine applicable Policy Control Request Trigger(s).

g. The (V)PCF sends an HTTP "201 Created" response to the AMF with the determined policies as described in subclause 4.2.2 of 3GPP TS 29.507:

  • Access and Mobility control Policy including Service Area Restrictions, and/or a RAT Frequency Selection
    Priority (RFSP) Index; and/or
  • Policy Control Request Triggers and related policy information;

h. The AMF deploys the Access and Mobility control policy information if received which includes storing the Service Area Restrictions, provisioning the Service Area Restrictions to the UE and/ or provisioning the RFSP index and Service Area Restrictions to the NG-RAN.

  1. AM Policy Association Modification

image

Steps:
a. The AMF detects a Policy Control Request Trigger condition is met or other condition is met, e.g. trace control configuration needs to be updated, as defined in subclause 4.2.3.1 of 3GPP TS 29.507.

b. The AMF invokes the Npcf_AMPolicyControl_Update service operation to the (V-) PCF by sending the HTTP POST request to the "Individual AM Policy Association" resource with information on the conditions that have
changed.

c. The (V)PCF stores the information received in step 2 and makes the policy decision.

d. The (V)PCF sends an HTTP "200 OK" response to the AMF with the updated Access and Mobility control policy information and/ or the updated Policy Control Request Trigger parameters.

e. The AMF deploys the Access and Mobility control policy if received, which includes, e.g. storing the Service Area Restrictions, provisioning the Service Area Restrictions to the NG-RAN and UE, and/or provisioning the RFSP index to the NG-RAN.

  1. AM Policy Association Termination

image

Step:
a. The AMF invokes the Npcf_AMPolicyControl_Delete service operation to delete the policy context in the (V-) PCF by sending the HTTP DELETE request to the "Individual AM Policy Association" resource.

b. The AMF removes the UE context for this UE, including the Access and Mobility Control Policy related to the UE and/or policy control request triggers.

c. The (V-)PCF removes the policy context for the UE and sends an HTTP "204 No Content" response to the AMF.

d. The PCF invokes the Nudr_DataRepository_Unsubscribe service operation to unsubscribe the notification of
subscriber policy data modification from the UDR by sending the HTTP DELETE request to the
"IndividualPolicyDataSubscription" resource if it has subscribed such notification.5. The UDR sends an HTTP "204 No Content" response to the PCF.

SM Policy Association Management

  1. SM Policy Association Establishment

image

a. The SMF receives a PDU session establishment request from the UE. The SMF selects the PCF as described in subclause 8.3 and invokes the Npcf_SMPolicyControl_Create service operation by sending the HTTP POST request to the "SM Policies" resource as defined in subclause 4.2.2.2 of 3GPP TS 29.512. The request
operation provides the SUPI, the PDU session ID, PDU Session Type, DNN, and S-NSSAI, and may provide the GPSI, the Internal Group Identifier, the Access Type (and additional access type, in case of MA PDU session), the IPv4 address or the IPv6 network prefix (if available), the MA PDU session indication and the ATSSS capability, if available, the PEI if received in the SMF, the User Location Information, the UE Time Zone, Serving Network, RAT type, charging information, the Session-AMBR, the DN-AAA authorization profile index if available, one or more framed routes if available, the subscribed default QoS, if available, etc, as defined in subclause 4.2.2 of 3GPP TS 29.512. The request operation also includes a Notification URI to indicate to the PCF where to send a notification when the SM related policies are updated.

b. If PCF does not have the subscription data for the SUPI, DNN and S-NSSAI, the PCF invokes the Nudr_DataRepository_Query service operation to the UDR by sending the HTTP GET request to the "SessionManagementPolicyData" resource as specified in 3GPP TS 29.519. The UDR sends an HTTP "200 OK" response to the PCF with the policy control subscription data. If BDT Reference ID(s) is included in the response from the UDR, the PCF shall invoke the Nudr_DataRepository_Query service operation to the UDR toretrieve the Background Data Transfer policy corresponding to the BDT Reference ID(s) by sending the HTTP GET request to the " IndividualBdtData" resource or the "BdtData" collection resource with the URI query parameter "bdt-ref-ids" as specified in 3GPP TS 29.519, and the UDR sends an HTTP "200 OK" response to the PCF with the Background Data Transfer policy. Additionally, if the TSC feature defined in 3GPP TS 29.512 is supported, the PCF invokes the Nudr_DataRepository_Query service operation to retrieve the stored AF influence data in the UDR by sending the HTTP GET request to the "Influence Data" resource as specified in 3GPP TS 29.519. The UDR sends an HTTP "200 OK" response with the stored AF request. Additionally, if the ATSSS feature defined in 3GPP TS 29.512 is supported, and the SDF template of the PCC rule includes an application identifier, the PCF invokes the Nudr_DataRepository_Query service operation
to retrieve the stored OS Id(s) supported by the UE from the UDR by sending the HTTP GET request to the "UePolicySet" resource as specified in 3GPP TS 29.519 . The UDR sends an HTTP "200 OK" response with the stored UE Policy data. The PCF determines the application descriptors based on the retrieved OS Id(s), if available, and local configuration, as specified in 3GPP TS 29.512. Additionally, if the WWC feature defined in 3GPP TS 29.512 is supported, the PCF invokes the Nudr_DataRepository_Query service operation to retrieve the stored IPTV configuration from the UDR by sending the HTTP GET request to the "IPTV Configurations" resource as specified in 3GPP TS 29.519.The UDR sends an HTTP "200 OK" response with the stored IPTV configuration. The PCF determines Multicast Access Control information (i.e., whether the multicast channel represented by the SDF of the PCC rule is allowed or not) based on the retrieved IPTV configuration as specified in 3GPP TS 29.512.

c. To request notifications from the UDR on changes in the subscription information, the PCF invokes the Nudr_DataRepository_Subscribe service operation by sending an HTTP POST request to the "PolicyDataSubscriptions" resource. The UDR sends an HTTP "201 Created" response to acknowledge the subscription. Additionally, if the TSC feature defined in 3GPP TS 29.512 is supported, to request notifications from the UDR on changes in the AF influence data, the PCF invokes the Nudr_DataRepository_Subscribe service operation by sending an HTTP POST request to the "Influence Data Subscription" resource. The UDR sends an HTTP "201 Created" response to acknowledge the subscription. Additionally, if the WWC feature defined in 3GPP TS 29.512 is supported, to request notifications from the UDR on changes in the IPTV configuration, the PCF invokes the Nudr_DataRepository_Subscribe service operation by sending an HTTP POST request to the "ApplicationDataSubscriptions" resource. The UDR sends an HTTP "201 Created" response to acknowledge the subscription.

d.If the PCF determines that the policy decision depends on the status of the policy counters available at the CHF, and such reporting is not established for the subscriber, the PCF initiates an Initial Spending Limit Report Retrieval as defined in subclause 5.3.2. If policy counter status reporting is already established for the subscriber, and the PCF determines that the status of additional policy counters are required, the PCF initiates an Intermediate Spending Limit Report Retrieval as defined in subclause 5.3.3.

e. The PCF makes the policy decision to determine the information provided in step 10.
When the feature "TimeSensitiveNetworking" is supported and the PCF detects that the request relates to TSC traffic based on the received DNN and S-NSSAI, the PCF determines to provide the the "TSN_BRIDGE_INFO" policy control request trigger in step 10.

f. In the case that the BSF is to be used and that either the IP address/prefix or MAC address is available, the PCF invokes the Nbsf_Management_Register service operation by sending HTTP POST request to create the PDU session binding information for a UE in the BSF as detailed in subclause 8 .5.2.

g. The PCF receives an HTTP "201 Created" response from the BSF with the created binding information as detailed in subclause 8.5.2.

h. The PCF sends an HTTP "201 Created" response to the SMF with the determined policies as described in subclause 4.2.2 of 3GPP TS 29.512.

  1. SM Policy Association Modification

image

a. The PCF receives an internal or external trigger to re-evaluate PCC Rules and policy decision for a PDU Session. Possible external trigger events are described in subclause 5.2.2.2.2. In addition, this procedure is triggered by the following cases:

  • The UDR notifies the PCF about a policy subscription change (e.g. change in MPS EPS Priority, MPS
    Priority Level, MCS Priority Level and/or IMS Signalling Priority, or change in user profile configuration indicating whether supporting application detection and control).
  • The UDR notifies the PCF about application data change (e.g. change in AF influence data or IPTV
    configuration data).
  • The PCF receives an internal or external trigger to re-evaluate PCC Rules and policy decision for a PDU Session. Possible external trigger events are described in subclause 5.2.2.2.2. In addition, this procedure is triggered by the following cases:
  • The UDR notifies the PCF about a policy subscription change (e.g. change in MPS EPS Priority, MPS
    Priority Level, MCS Priority Level and/or IMS Signalling Priority, or change in user profile configuration
    indicating whether supporting application detection and control).
  • The UDR notifies the PCF about application data change (e.g. change in AF influence data or IPTV
    configuration data).

b. If the PCF determines that the policy decision depends on the status of the policy counters available at the CHF and such reporting is not established for the subscriber, the PCF initiates an Initial Spending Limit Report as defined in subclause 5.3.2. If policy counter status reporting is already established for the subscriber, and the PCF decides to modify the list of subscribed policy counters, the PCF sends an Intermediate Spending Limit Report as defined in subclause 5.3.3. If the PCF decides to unsubscribe any future status notification of policy counters, it sends a Final Spending Limit Report Request to cancel the request for reporting the change of the status of the policy counters available at the CHF as defined in subclause 5.3.4.

c. The PCF makes a policy decision. The PCF can determine that updated or new policy information need to be sent to the SMF.

d. The PCF invokes the Npcf_SMPolicyControl_UpdateNotify service operation by sending the HTTP POST
request with "{notificationUri}/update" as the resource URI to the SMF that has previously subscribed. The request operation provides the PDU session ID and the updated policies, as described in subclause 4.2.3 of 3GPP TS 29.512.

e. The SMF sends an HTTP "200 OK" to the PCF

  1. SM Policy Association Termination

image

a. The SMF invokes the Npcf_SMPolicyControl_Delete service operation by sending the HTTP POST request to the "Individual SM Policy" resource to request the PCF to delete the context of the SM related policy as defined in subclause 4.2.5.2 of 3GPP TS 29.512. The request operation may include usage monitoring information (if applicable) and access network information.

b. Upon receipt of Npcf_SMPolicyControl_Delete service operation, the PCF identifies the PCC Rules that require an AF to be notified and removes PCC Rules for the PDU Session.

c. The SMF removes all the PCC Rules which are applied to the PDU session.

d. The PCF invokes the Npcf_PolicyAuthorization_Notify service operation by sending the HTTP POST request with "{notifUri}/terminate" as the resource URI to the AF to trigger the AF to request the application session context termination.

e. The AF sends an HTTP "204 No Content" response to the PCF

f. The AF invokes the Npcf_PolicyAuthorization_Delete service operation by sending the HTTP POST request to the "Individual Application Session Context" resource. The request may include the events to subscribe to

g. The PCF removes the AF application session context and sends an HTTP "204 No Content" response to the AF. If the PCF needs to report usage data or the access network information, it sends an HTTP "200 OK" response. If usage thresholds were provided by the AF earlier, and the PCF has usage data that has not yet been reported to the AF, the PCF informs the AF about the resources that have been consumed by the user since the last report. If the SMF in step 1 reports the access network information and if the AF requested the PCF to report access network information in step 6 and/or the RAN-NAS-Cause feature is supported, the PCF informs the AF about the access network information. The PCF also deletes the subscription to PCF detected events for that AF application Session.

h. If this is the last PDU session for this subscriber the Final Spending Limit Report Request as defined in subclause 5.3.4 is sent. If any existing PDU sessions for this subscriber require policy counter status reporting, the Intermediate Spending Limit Report Request as defined in subclause 5.3.3 can be sent to alter the list of subscribed policy counters.

i. The PCF removes PCC Rules for the terminated PDU Session and sends an HTTP "204 No Content" response to the SMF.

j. The PCF invokes the Nudr_DataRepository_Update service operation by sending the HTTP PATCH request to the "SessionManagementPolicyData" resource to store the remaining usage allowance in the UDR, if all PDU sessions of the user to the same DNN and S-NSSAI are terminated.

k. The UDR sends an HTTP "204 No Content" response to the PCF.

l. To unsubscribe the notification of the PDU session related data modification from the UDR, the PCF invokes the Nudr_DataRepository_Unsubscribe service operation by sending the HTTP DELETE request to the "IndividualPolicyDataSubscription" resource if it has subscribed such notification. The UDR sends an HTTP "204 No Content" response to the PCF. Additionally, to unsubscribe the notification of the AF influence data from the UDR, the PCF invokes the Nudr_DataRepository_Unsubscribe service operation by sending the HTTP DELETE request to the "Individual Influence Data Subscription" resource if it has subscribed such notification. The UDR sends an HTTP "204 No Content" response to the PCF.

m. In the case that binding information has been previously registered in the BSF the PCF invokes the
Nbsf_Management_Deregister service operation by sending an HTTP DELETE request to the BSF to delete
binding information as detailed in subclause 8.5.3.

n. The PCF receives an HTTP "204 No Content response from the BSF as detailed in subclause 8.5.3.

Code Review

Directory Structure

- pcf
  - cmd
    - main.go
  - internal
    - context
      - context.go
      - ue.go
    - logger
      - logger.go    
    - sbi
      - consumer
        - amf_service.go
        - consumer.go
        - nrf_service.go
        - udr_service.go
      - processor
        - ampolicy.go
        - bdtpolicy.go
        - notifier.go
        - oam.go
        - policyauthorization.go
        - processor.go
        - smpolicy.go
      - api_ampolicy.go
      - api_bdtpolicy.go
      - api_httpcallback.go
      - api_oam.go
      - api_policyauthorization.go
      - api_smpolicy.go
      - api_uepolicy.go
      - server.go
    - util
      - convert.go
      - init_context.go
      - pcc_rule.go
      - pcf_util.go
      - router_auth_check.go
      - router_auth_check_test.go
      - search_nf_service.go
  - pkg
    - app
      - app.go
    - factory
      - config.go
      - factory.go
    - service
      - init.go

a. cmd/main.go

package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"
	"path/filepath"
	"runtime/debug"
	"syscall"

	"github.com/urfave/cli"

	"github.com/free5gc/pcf/internal/logger"
	"github.com/free5gc/pcf/pkg/factory"
	"github.com/free5gc/pcf/pkg/service"
	logger_util "github.com/free5gc/util/logger"
	"github.com/free5gc/util/version"
)

var PCF *service.PcfApp

func main() {
	defer func() {
		if p := recover(); p != nil {
			// Print stack for panic to log. Fatalf() will let program exit.
			logger.MainLog.Fatalf("panic: %v\n%s", p, string(debug.Stack()))
		}
	}()

	app := cli.NewApp()
	app.Name = "pcf"
	app.Usage = "5G Policy Control Function (PCF)"
	app.Action = action
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "config, c",
			Usage: "Load configuration from `FILE`",
		},
		cli.StringSliceFlag{
			Name:  "log, l",
			Usage: "Output NF log to `FILE`",
		},
	}
	if err := app.Run(os.Args); err != nil {
		fmt.Printf("PCF Run Error: %v\n", err)
	}
}

func action(cliCtx *cli.Context) error {
	tlsKeyLogPath, err := initLogFile(cliCtx.StringSlice("log"))
	if err != nil {
		return err
	}

	logger.MainLog.Infoln(cliCtx.App.Name)
	logger.MainLog.Infoln("PCF version: ", version.GetVersion())

	ctx, cancel := context.WithCancel(context.Background())
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)

	go func() {
		<-sigCh  // Wait for interrupt signal to gracefully shutdown UPF
		cancel() // Notify each goroutine and wait them stopped
	}()

	cfg, err := factory.ReadConfig(cliCtx.String("config"))
	if err != nil {
		sigCh <- nil
		return err
	}
	factory.PcfConfig = cfg

	pcf, err := service.NewApp(ctx, cfg, tlsKeyLogPath)
	if err != nil {
		sigCh <- nil
		return err
	}
	PCF = pcf
	if pcf == nil {
		logger.MainLog.Infoln("pcf is nil")
	}
	pcf.Start()

	return nil
}

func initLogFile(logNfPath []string) (string, error) {
	logTlsKeyPath := ""

	for _, path := range logNfPath {
		if err := logger_util.LogFileHook(logger.Log, path); err != nil {
			return "", err
		}

		if logTlsKeyPath != "" {
			continue
		}

		nfDir, _ := filepath.Split(path)
		tmpDir := filepath.Join(nfDir, "key")
		if err := os.MkdirAll(tmpDir, 0o775); err != nil {
			logger.InitLog.Errorf("Make directory %s failed: %+v", tmpDir, err)
			return "", err
		}
		_, name := filepath.Split(factory.PcfDefaultTLSKeyLogPath)
		logTlsKeyPath = filepath.Join(tmpDir, name)
	}

	return logTlsKeyPath, nil
}

This file contain 3 functions:

  1. main
  • sets up panic recovery mechanism
  • initializes and configures CLI application
  • runs the CLI application and handles errors

logic explanation:
app variable is an object that has attributes such as: name, usage, action and flags. The action attributes are a pointer to function that start the PCF. The action function works explain below

  1. action
  • initializes log files
  • emitt application name and version
  • sets a cancellable context and signal handler for graceful shutdown
  • reads configuration from a file
  • create and start PCF application instance
  1. initLogFile
  • Initializes log files and directories based on provided paths
  • return the path for the TSL key log file

b. pkg/app/app.go

package app

import (
	pcf_context "github.com/free5gc/pcf/internal/context"
	"github.com/free5gc/pcf/pkg/factory"
)

type App interface {
	SetLogEnable(enable bool)
	SetLogLevel(level string)
	SetReportCaller(reportCaller bool)

	Start()
	Terminate()

	Context() *pcf_context.PCFContext
	Config() *factory.Config
}

This file containing an interface that provide a common type for detailed implementation of PCF components. Each PCF components should have these function:

  • setLogEnable: to enable logging
  • setLogLevel : to set log levels (WARN,INFO,ERROR,DEBUG)
  • start : to start a service
  • terminate : to end a service
  • Context : to return the current context of application
  • Config : to return the current configuration of the application

c. pkg/factory/config.go

/*
 * PCF Configuration Factory
 */

package factory

import (
	"fmt"
	"os"
	"strconv"
	"sync"

	"github.com/asaskevich/govalidator"

	"github.com/free5gc/pcf/internal/logger"
)

const (
	PcfTimeFormatLayout         = "2006-01-02 15:04:05"
	PcfDefaultTLSKeyLogPath     = "./log/pcfsslkey.log"
	PcfDefaultCertPemPath       = "./cert/pcf.pem"
	PcfDefaultPrivateKeyPath    = "./cert/pcf.key"
	PcfDefaultConfigPath        = "./config/pcfcfg.yaml"
	PcfSbiDefaultIPv4           = "127.0.0.7"
	PcfSbiDefaultPort           = 8000
	PcfSbiDefaultScheme         = "https"
	PcfDefaultNrfUri            = "https://127.0.0.10:8000"
	PcfPolicyAuthResUriPrefix   = "/npcf-policyauthorization/v1"
	PcfAMpolicyCtlResUriPrefix  = "/npcf-am-policy-control/v1"
	PcfCallbackResUriPrefix     = "/npcf-callback/v1"
	PcfSMpolicyCtlResUriPrefix  = "/npcf-smpolicycontrol/v1"
	PcfBdtPolicyCtlResUriPrefix = "/npcf-bdtpolicycontrol/v1"
	PcfOamResUriPrefix          = "/npcf-oam/v1"
	PcfUePolicyCtlResUriPrefix  = "/npcf-ue-policy-control/v1/"
)

type Config struct {
	Info          *Info          `yaml:"info" valid:"required"`
	Configuration *Configuration `yaml:"configuration" valid:"required"`
	Logger        *Logger        `yaml:"logger" valid:"required"`
	sync.RWMutex
}

func (c *Config) Validate() (bool, error) {
	if configuration := c.Configuration; configuration != nil {
		if result, err := configuration.validate(); err != nil {
			return result, err
		}
	}

	result, err := govalidator.ValidateStruct(c)
	return result, appendInvalid(err)
}

type Info struct {
	Version     string `yaml:"version,omitempty" valid:"required,in(1.0.2)"`
	Description string `yaml:"description,omitempty" valid:"type(string)"`
}

type Configuration struct {
	PcfName         string    `yaml:"pcfName,omitempty" valid:"required, type(string)"`
	Sbi             *Sbi      `yaml:"sbi,omitempty" valid:"required"`
	TimeFormat      string    `yaml:"timeFormat,omitempty" valid:"required"`
	DefaultBdtRefId string    `yaml:"defaultBdtRefId,omitempty" valid:"required, type(string)"`
	NrfUri          string    `yaml:"nrfUri,omitempty" valid:"required, url"`
	NrfCertPem      string    `yaml:"nrfCertPem,omitempty" valid:"optional"`
	ServiceList     []Service `yaml:"serviceList,omitempty" valid:"required"`
	Mongodb         *Mongodb  `yaml:"mongodb" valid:"required"`
	Locality        string    `yaml:"locality,omitempty" valid:"-"`
}

type Logger struct {
	Enable       bool   `yaml:"enable" valid:"type(bool)"`
	Level        string `yaml:"level" valid:"required,in(trace|debug|info|warn|error|fatal|panic)"`
	ReportCaller bool   `yaml:"reportCaller" valid:"type(bool)"`
}

func (c *Configuration) validate() (bool, error) {
	if c.Sbi != nil {
		if _, err := c.Sbi.validate(); err != nil {
			return false, err
		}
	}

	if result := govalidator.IsTime(c.TimeFormat, PcfTimeFormatLayout); !result {
		err := fmt.Errorf("Invalid TimeFormat: %s, should be in 2019-01-02 15:04:05 format.", c.TimeFormat)
		return false, err
	}

	if c.ServiceList != nil {
		var errs govalidator.Errors
		for _, v := range c.ServiceList {
			if _, err := v.validate(); err != nil {
				errs = append(errs, err)
			}
		}
		if len(errs) > 0 {
			return false, error(errs)
		}
	}

	if c.Mongodb != nil {
		if _, err := c.Mongodb.validate(); err != nil {
			return false, err
		}
	}

	if _, err := govalidator.ValidateStruct(c); err != nil {
		return false, appendInvalid(err)
	}

	return true, nil
}

type Service struct {
	ServiceName string `yaml:"serviceName" valid:"required, service"`
	SuppFeat    string `yaml:"suppFeat,omitempty" valid:"-"`
}

func (s *Service) validate() (bool, error) {
	govalidator.TagMap["service"] = govalidator.Validator(func(str string) bool {
		switch str {
		case "npcf-am-policy-control":
		case "npcf-smpolicycontrol":
		case "npcf-bdtpolicycontrol":
		case "npcf-policyauthorization":
		case "npcf-eventexposure":
		case "npcf-ue-policy-control":
		default:
			return false
		}
		return true
	})

	if s.ServiceName == "npcf-smpolicycontrol" {
		if sf, e := strconv.ParseUint(s.SuppFeat, 16, 40); e != nil {
			err := fmt.Errorf("Invalid SuppFeat: %s, range of the value should be 0~3fff", s.SuppFeat)
			return false, err
		} else {
			if sf2, e := strconv.ParseUint("3fff", 16, 20); e == nil {
				if sf > sf2 {
					err := fmt.Errorf("Invalid SuppFeat: %s, range of the value should be 0~3fff", s.SuppFeat)
					return false, err
				}
			}
		}
	} else if s.ServiceName == "npcf-policyauthorization" {
		if s.SuppFeat != "0" && s.SuppFeat != "1" && s.SuppFeat != "2" && s.SuppFeat != "3" {
			err := fmt.Errorf("Invalid SuppFeat: %s, range of the value should be 0~3", s.SuppFeat)
			return false, err
		}
	}

	if _, err := govalidator.ValidateStruct(s); err != nil {
		return false, appendInvalid(err)
	}

	return true, nil
}

type Sbi struct {
	Scheme       string `yaml:"scheme" valid:"required,scheme"`
	RegisterIPv4 string `yaml:"registerIPv4,omitempty" valid:"required,host"` // IP that is registered at NRF.
	// IPv6Addr  string `yaml:"ipv6Addr,omitempty"`
	BindingIPv4 string `yaml:"bindingIPv4,omitempty" valid:"required,host"` // IP used to run the server in the node.
	Port        int    `yaml:"port,omitempty" valid:"required,port"`
	Tls         *Tls   `yaml:"tls,omitempty" valid:"optional"`
}

func (s *Sbi) validate() (bool, error) {
	govalidator.TagMap["scheme"] = govalidator.Validator(func(str string) bool {
		return str == "https" || str == "http"
	})

	if tls := s.Tls; tls != nil {
		if result, err := tls.validate(); err != nil {
			return result, err
		}
	}

	if _, err := govalidator.ValidateStruct(s); err != nil {
		return false, appendInvalid(err)
	}

	return true, nil
}

type Tls struct {
	Pem string `yaml:"pem,omitempty" valid:"type(string),minstringlength(1),required"`
	Key string `yaml:"key,omitempty" valid:"type(string),minstringlength(1),required"`
}

func (t *Tls) validate() (bool, error) {
	result, err := govalidator.ValidateStruct(t)
	return result, err
}

type Mongodb struct {
	Name string `yaml:"name" valid:"required, type(string)"`
	Url  string `yaml:"url" valid:"required"`
}

func (m *Mongodb) validate() (bool, error) {
	pattern := `[-a-zA-Z0-9@:%._\+~#=]{1,256}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)`
	if result := govalidator.StringMatches(m.Url, pattern); !result {
		err := fmt.Errorf("Invalid Url: %s", m.Url)
		return false, err
	}
	if _, err := govalidator.ValidateStruct(m); err != nil {
		return false, appendInvalid(err)
	}

	return true, nil
}

func appendInvalid(err error) error {
	var errs govalidator.Errors

	if err == nil {
		return nil
	}

	es := err.(govalidator.Errors).Errors()
	for _, e := range es {
		errs = append(errs, fmt.Errorf("Invalid %w", e))
	}

	return error(errs)
}

func (c *Config) GetSbiBindingIP() string {
	c.RLock()
	defer c.RUnlock()
	bindIP := "0.0.0.0"
	if c.Configuration == nil || c.Configuration.Sbi == nil {
		return bindIP
	}
	if c.Configuration.Sbi.BindingIPv4 != "" {
		if bindIP = os.Getenv(c.Configuration.Sbi.BindingIPv4); bindIP != "" {
			logger.CfgLog.Infof("Parsing ServerIPv4 [%s] from ENV Variable", bindIP)
		} else {
			bindIP = c.Configuration.Sbi.BindingIPv4
		}
	}
	return bindIP
}

func (c *Config) GetSbiPort() int {
	c.RLock()
	defer c.RUnlock()
	if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.Port != 0 {
		return c.Configuration.Sbi.Port
	}
	return PcfSbiDefaultPort
}

func (c *Config) GetSbiBindingAddr() string {
	c.RLock()
	defer c.RUnlock()
	return c.GetSbiBindingIP() + ":" + strconv.Itoa(c.GetSbiPort())
}

func (c *Config) GetSbiScheme() string {
	c.RLock()
	defer c.RUnlock()
	if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.Scheme != "" {
		return c.Configuration.Sbi.Scheme
	}
	return PcfSbiDefaultScheme
}

func (c *Config) GetCertPemPath() string {
	c.RLock()
	defer c.RUnlock()
	return c.Configuration.Sbi.Tls.Pem
}

func (c *Config) GetCertKeyPath() string {
	c.RLock()
	defer c.RUnlock()
	return c.Configuration.Sbi.Tls.Key
}

func (c *Config) GetVersion() string {
	c.RLock()
	defer c.RUnlock()

	if c.Info.Version != "" {
		return c.Info.Version
	}
	return ""
}

func (c *Config) SetLogEnable(enable bool) {
	c.Lock()
	defer c.Unlock()

	if c.Logger == nil {
		logger.CfgLog.Warnf("Logger should not be nil")
		c.Logger = &Logger{
			Enable: enable,
			Level:  "info",
		}
	} else {
		c.Logger.Enable = enable
	}
}

func (c *Config) SetLogLevel(level string) {
	c.Lock()
	defer c.Unlock()

	if c.Logger == nil {
		logger.CfgLog.Warnf("Logger should not be nil")
		c.Logger = &Logger{
			Level: level,
		}
	} else {
		c.Logger.Level = level
	}
}

func (c *Config) SetLogReportCaller(reportCaller bool) {
	c.Lock()
	defer c.Unlock()

	if c.Logger == nil {
		logger.CfgLog.Warnf("Logger should not be nil")
		c.Logger = &Logger{
			Level:        "info",
			ReportCaller: reportCaller,
		}
	} else {
		c.Logger.ReportCaller = reportCaller
	}
}

func (c *Config) GetLogEnable() bool {
	c.RLock()
	defer c.RUnlock()
	if c.Logger == nil {
		logger.CfgLog.Warnf("Logger should not be nil")
		return false
	}
	return c.Logger.Enable
}

func (c *Config) GetLogLevel() string {
	c.RLock()
	defer c.RUnlock()
	if c.Logger == nil {
		logger.CfgLog.Warnf("Logger should not be nil")
		return "info"
	}
	return c.Logger.Level
}

func (c *Config) GetLogReportCaller() bool {
	c.RLock()
	defer c.RUnlock()
	if c.Logger == nil {
		logger.CfgLog.Warnf("Logger should not be nil")
		return false
	}
	return c.Logger.ReportCaller
}

This file contains:

  1. Constants that:
  • Configure time format
  • Configure Certification and key path
  • Set a default config path
  • Default SBI IPv4 address
  • Default SBI port
  • Default SBI scheme
  • Default URI for services (eg: ampolicy, smpolicy, etc)
  1. Structs
  • config : to encapsulate necessary configuration so each necessary object accessed through a single object
  • info : struct to store informational data such as Version and Description
  • configuration : struct to store detailed configuration for PCF application. This struct contain name, sbi object,service list, mongodb, NRF uri etc
  • logger : struct to store logging configuration such as enable flag, logging level, etc
  • service : this struct represent service provided by PCF. This struct contains service name and supported feature, both of the field is string.
  • sbi : this struct is represents SBI object, this struct contain scheme (such as HTTPS,HTTP), SBI ip address, SBI port number, etc
  • mongodb : this struct represent the mongodb database to store policy information, this struct contain the database name and database url.
  1. Methods

Each of the struct/object method called "Validate". This method are called to check whether an object is valid to be created or not.

  1. Summary:

This file contain object to store configuration of the PCF service and apps.

d. pkg/factory/factory.go

package factory

import (
	"fmt"
	"os"

	"github.com/asaskevich/govalidator"
	"gopkg.in/yaml.v2"

	"github.com/free5gc/pcf/internal/logger"
)

var PcfConfig *Config

// TODO: Support configuration update from REST api
func InitConfigFactory(f string, cfg *Config) error {
	if f == "" {
		// Use default config path
		f = PcfDefaultConfigPath
	}

	if content, err := os.ReadFile(f); err != nil {
		return fmt.Errorf("[Factory] %+v", err)
	} else {
		logger.CfgLog.Infof("Read config from [%s]", f)
		if yamlErr := yaml.Unmarshal(content, cfg); yamlErr != nil {
			return fmt.Errorf("[Factory] %+v", yamlErr)
		}
	}

	return nil
}

func ReadConfig(cfgPath string) (*Config, error) {
	cfg := &Config{}
	if err := InitConfigFactory(cfgPath, cfg); err != nil {
		return nil, fmt.Errorf("ReadConfig [%s] Error: %+v", cfgPath, err)
	}
	if _, err := cfg.Validate(); err != nil {
		validErrs := err.(govalidator.Errors).Errors()
		for _, validErr := range validErrs {
			logger.CfgLog.Errorf("%+v", validErr)
		}
		logger.CfgLog.Errorf("[-- PLEASE REFER TO SAMPLE CONFIG FILE COMMENTS --]")
		return nil, fmt.Errorf("Config validate Error")
	}

	return cfg, nil
}

This file contains two functions:

  1. InitConfig, this function is called to check the yaml file format, ensuring there is no syntax error in the configuration, hence validate the configuration file

  2. ReadConfig, this function is called to load value from config file to the config object. As seen in the code, the "config" object is used in the function

e. pkg/service/init.go

package service

import (
	"context"
	"io"
	"os"
	"runtime/debug"
	"sync"

	"github.com/sirupsen/logrus"

	pcf_context "github.com/free5gc/pcf/internal/context"
	"github.com/free5gc/pcf/internal/logger"
	"github.com/free5gc/pcf/internal/sbi"
	"github.com/free5gc/pcf/internal/sbi/consumer"
	"github.com/free5gc/pcf/internal/sbi/processor"
	"github.com/free5gc/pcf/pkg/app"
	"github.com/free5gc/pcf/pkg/factory"
)

var PCF *PcfApp  //defined at service.go

var _ app.App = &PcfApp{}

type PcfApp struct {
	app.App
	cfg    *factory.Config
	pcfCtx *pcf_context.PCFContext //defined at context.go
	ctx    context.Context
	cancel context.CancelFunc

	consumer  *consumer.Consumer
	processor *processor.Processor
	sbiServer *sbi.Server
	wg        sync.WaitGroup
}

func NewApp(
	ctx context.Context,
	cfg *factory.Config,
	tlsKeyLogPath string,
) (*PcfApp, error) {
	pcf := &PcfApp{
		cfg: cfg,
		wg:  sync.WaitGroup{},
	}
	pcf.SetLogEnable(cfg.GetLogEnable())
	pcf.SetLogLevel(cfg.GetLogLevel())
	pcf.SetReportCaller(cfg.GetLogReportCaller())

	pcf.ctx, pcf.cancel = context.WithCancel(ctx)
	pcf_context.Init() //create pcf context
	pcf.pcfCtx = pcf_context.GetSelf()
    //context defined at internal/context/context.go
	// initialize consumer
	consumer, err := consumer.NewConsumer(pcf)
	if err != nil {
		return pcf, err
	}
	pcf.consumer = consumer

	// initialize processor
	p, err := processor.NewProcessor(pcf, consumer)
	if err != nil {
		return pcf, err
	}
	pcf.processor = p
//NewServer defined at sbi/server.go
	if pcf.sbiServer, err = sbi.NewServer(pcf, tlsKeyLogPath); err != nil {
		return nil, err
	}
	PCF = pcf

	return pcf, nil
}

func (a *PcfApp) Config() *factory.Config {
	return a.cfg
}

func (a *PcfApp) Context() *pcf_context.PCFContext {
	return a.pcfCtx
}

func (a *PcfApp) CancelContext() context.Context {
	return a.ctx
}

func (a *PcfApp) Consumer() *consumer.Consumer {
	return a.consumer
}

func (a *PcfApp) Processor() *processor.Processor {
	return a.processor
}

func (a *PcfApp) SetLogEnable(enable bool) {
	logger.MainLog.Infof("Log enable is set to [%v]", enable)
	if enable && logger.Log.Out == os.Stderr {
		return
	} else if !enable && logger.Log.Out == io.Discard {
		return
	}

	a.cfg.SetLogEnable(enable)
	if enable {
		logger.Log.SetOutput(os.Stderr)
	} else {
		logger.Log.SetOutput(io.Discard)
	}
}

func (a *PcfApp) SetLogLevel(level string) {
	lvl, err := logrus.ParseLevel(level)
	if err != nil {
		logger.MainLog.Warnf("Log level [%s] is invalid", level)
		return
	}

	logger.MainLog.Infof("Log level is set to [%s]", level)
	if lvl == logger.Log.GetLevel() {
		return
	}

	a.cfg.SetLogLevel(level)
	logger.Log.SetLevel(lvl)
}

func (a *PcfApp) SetReportCaller(reportCaller bool) {
	logger.MainLog.Infof("Report Caller is set to [%v]", reportCaller)
	if reportCaller == logger.Log.ReportCaller {
		return
	}

	a.cfg.SetLogReportCaller(reportCaller)
	logger.Log.SetReportCaller(reportCaller)
}

func (a *PcfApp) Start() { //called by cmd/init.go
	logger.InitLog.Infoln("Server started")
	a.wg.Add(1)
	go a.listenShutdownEvent()
	if err := a.sbiServer.Run(context.Background(), &a.wg); err != nil {
		logger.InitLog.Fatalf("Run SBI server failed: %+v", err)
	}
	a.WaitRoutineStopped()
}

func (a *PcfApp) listenShutdownEvent() {
	defer func() {
		if p := recover(); p != nil {
			// Print stack for panic to log. Fatalf() will let program exit.
			logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack()))
		}
		a.wg.Done()
	}()

	<-a.ctx.Done()
	a.terminateProcedure()
}

func (a *PcfApp) CallServerStop() {
	if a.sbiServer != nil {
		a.sbiServer.Shutdown(context.Background())
	}
}

func (a *PcfApp) Terminate() {
	a.cancel()
}

func (a *PcfApp) terminateProcedure() {
	logger.MainLog.Infof("Terminating PCF...")
	a.CallServerStop()
	// deregister with NRF
	problemDetails, err := a.Consumer().SendDeregisterNFInstance()
	if problemDetails != nil {
		logger.InitLog.Errorf("Deregister NF instance Failed Problem[%+v]", problemDetails)
	} else if err != nil {
		logger.InitLog.Errorf("Deregister NF instance Error[%+v]", err)
	} else {
		logger.InitLog.Infof("Deregister from NRF successfully")
	}
	logger.InitLog.Infof("PCF terminated")
}

func (a *PcfApp) WaitRoutineStopped() {
	a.wg.Wait()
	logger.MainLog.Infof("PCF App is terminated")
}

This file contains:

  1. Global variable called PCF that holds *PcfApp object pointer

  2. PcfApp struct holf the information of PCF. Those information include: PCF configuration (cfg), context (pcfCtx, ctx, cancel), consumer, processor, SBI server

  3. NewApp function: this function receive parameter with type of context, config and path to TLSKeyLog. This function return the app instance and the error if failed. This function parse the parameter to the pcfApp structs and return the instance or error. This method will create PCFContext (defined at /internal/context/context.go) set a consumer object, a processor object and create the SBI server using NewServer().

  4. Getter methods, this methods provide a secure way to retrieve pcfApp object fields such as Config, Context, cancel context, consumer and processor.

  5. Logger methods, these methods provide the implementation of enable logging, setting a logging level, and set the report caller.

  6. Lifecycle methods such as: Start() to starts the PCF application ,listenShutdownEvent() to wait for the context to be canceled then initiate termination procedure, CallServerStop() to stop sbi server, Terminate() to cancels the context to trigger shutdown , terminateProcedure() to handles NF deregistration process, and WaitRoutineStopped() to wait for all go routines to be terminated.

f. internal/logger/logger.go

package logger

import (
	"github.com/sirupsen/logrus"

	logger_util "github.com/free5gc/util/logger"
)

var (
	Log           *logrus.Logger
	NfLog         *logrus.Entry
	MainLog       *logrus.Entry
	InitLog       *logrus.Entry
	CfgLog        *logrus.Entry
	CtxLog        *logrus.Entry
	GinLog        *logrus.Entry
	SBILog        *logrus.Entry
	AmPolicyLog   *logrus.Entry
	BdtPolicyLog  *logrus.Entry
	ConsumerLog   *logrus.Entry
	CallbackLog   *logrus.Entry
	OamLog        *logrus.Entry
	PolicyAuthLog *logrus.Entry
	ProcLog       *logrus.Entry
	SmPolicyLog   *logrus.Entry
	UtilLog       *logrus.Entry
)

func init() {
	fieldsOrder := []string{
		logger_util.FieldNF,
		logger_util.FieldCategory,
	}

	Log = logger_util.New(fieldsOrder)
	NfLog = Log.WithField(logger_util.FieldNF, "PCF")
	MainLog = NfLog.WithField(logger_util.FieldCategory, "Main")
	InitLog = NfLog.WithField(logger_util.FieldCategory, "Init")
	CfgLog = NfLog.WithField(logger_util.FieldCategory, "CFG")
	CtxLog = NfLog.WithField(logger_util.FieldCategory, "CTX")
	GinLog = NfLog.WithField(logger_util.FieldCategory, "GIN")
	SBILog = NfLog.WithField(logger_util.FieldCategory, "SBI")
	AmPolicyLog = NfLog.WithField(logger_util.FieldCategory, "AmPol")
	BdtPolicyLog = NfLog.WithField(logger_util.FieldCategory, "BdtPol")
	ConsumerLog = NfLog.WithField(logger_util.FieldCategory, "Consumer")
	CallbackLog = NfLog.WithField(logger_util.FieldCategory, "Callback")
	OamLog = NfLog.WithField(logger_util.FieldCategory, "Oam")
	PolicyAuthLog = NfLog.WithField(logger_util.FieldCategory, "PolAuth")
	ProcLog = NfLog.WithField(logger_util.FieldCategory, "Proc")
	SmPolicyLog = NfLog.WithField(logger_util.FieldCategory, "SMpolicy")
	UtilLog = NfLog.WithField(logger_util.FieldCategory, "Util")
}

This file contains method to initiate logging feature for the PCF services. There is variable initiation for each service under var section and function init to create logging for each service.

g. internal/context/context.go

package context

import (
	"context"
	"fmt"
	"math"
	"os"
	"strconv"
	"strings"
	"sync"

	"github.com/google/uuid"

	"github.com/free5gc/openapi"
	"github.com/free5gc/openapi/models"
	"github.com/free5gc/openapi/oauth"
	"github.com/free5gc/pcf/internal/logger"
	"github.com/free5gc/pcf/pkg/factory"
	"github.com/free5gc/util/idgenerator"
	"github.com/free5gc/util/mongoapi"
)

type PCFContext struct {
	NfId            string
	Name            string
	UriScheme       models.UriScheme
	BindingIPv4     string
	RegisterIPv4    string
	SBIPort         int
	TimeFormat      string
	DefaultBdtRefId string
	NfService       map[models.ServiceName]models.NfService
	PcfServiceUris  map[models.ServiceName]string
	PcfSuppFeats    map[models.ServiceName]openapi.SupportedFeature
	NrfUri          string
	NrfCertPem      string
	DefaultUdrURI   string
	Locality        string
	// UePool          map[string]*UeContext
	UePool sync.Map
	// Bdt Policy related
	BdtPolicyPool        sync.Map
	BdtPolicyIDGenerator *idgenerator.IDGenerator
	// App Session related
	AppSessionPool sync.Map
	// AMF Status Change Subscription related
	AMFStatusSubsData sync.Map // map[string]AMFStatusSubscriptionData; subscriptionID as key

	// lock
	DefaultUdrURILock sync.RWMutex

	// Charging
	RatingGroupIdGenerator *idgenerator.IDGenerator

	OAuth2Required bool
}

type AMFStatusSubscriptionData struct {
	AmfUri       string
	AmfStatusUri string
	GuamiList    []models.Guami
}

type AppSessionData struct {
	AppSessionId      string
	AppSessionContext *models.AppSessionContext
	// (compN/compN-subCompN/appId-%s) map to PccRule
	RelatedPccRuleIds    map[string]string
	PccRuleIdMapToCompId map[string]string
	// EventSubscription
	Events   map[models.AfEvent]models.AfNotifMethod
	EventUri string
	// related Session
	SmPolicyData *UeSmPolicyData
}

var pcfContext = PCFContext{}

type NFContext interface {
	AuthorizationCheck(token string, serviceName models.ServiceName) error
}

var _ NFContext = &PCFContext{}

func InitPcfContext(context *PCFContext) {
	config := factory.PcfConfig
	logger.UtilLog.Infof("pcfconfig Info: Version[%s] Description[%s]", config.Info.Version, config.Info.Description)
	configuration := config.Configuration
	context.NfId = uuid.New().String()
	if configuration.PcfName != "" {
		context.Name = configuration.PcfName
	}

	mongodb := config.Configuration.Mongodb
	// Connect to MongoDB
	if err := mongoapi.SetMongoDB(mongodb.Name, mongodb.Url); err != nil {
		logger.UtilLog.Errorf("InitpcfContext err: %+v", err)
		return
	}

	sbi := configuration.Sbi
	context.NrfUri = configuration.NrfUri
	context.NrfCertPem = configuration.NrfCertPem
	context.UriScheme = ""
	context.RegisterIPv4 = factory.PcfSbiDefaultIPv4 // default localhost
	context.SBIPort = factory.PcfSbiDefaultPort      // default port
	if sbi != nil {
		if sbi.Scheme != "" {
			context.UriScheme = models.UriScheme(sbi.Scheme)
		}
		if sbi.RegisterIPv4 != "" {
			context.RegisterIPv4 = sbi.RegisterIPv4
		}
		if sbi.Port != 0 {
			context.SBIPort = sbi.Port
		}
		if sbi.Scheme == "https" {
			context.UriScheme = models.UriScheme_HTTPS
		} else {
			context.UriScheme = models.UriScheme_HTTP
		}

		context.BindingIPv4 = os.Getenv(sbi.BindingIPv4)
		if context.BindingIPv4 != "" {
			logger.UtilLog.Info("Parsing ServerIPv4 address from ENV Variable.")
		} else {
			context.BindingIPv4 = sbi.BindingIPv4
			if context.BindingIPv4 == "" {
				logger.UtilLog.Warn("Error parsing ServerIPv4 address as string. Using the 0.0.0.0 address as default.")
				context.BindingIPv4 = "0.0.0.0"
			}
		}
	}
	serviceList := configuration.ServiceList
	context.InitNFService(serviceList, config.Info.Version)
	context.TimeFormat = configuration.TimeFormat
	context.DefaultBdtRefId = configuration.DefaultBdtRefId
	for _, service := range context.NfService {
		var err error
		context.PcfServiceUris[service.ServiceName] = service.ApiPrefix +
			"/" + string(service.ServiceName) + "/" + (*service.Versions)[0].ApiVersionInUri
		context.PcfSuppFeats[service.ServiceName], err = openapi.NewSupportedFeature(service.SupportedFeatures)
		if err != nil {
			logger.UtilLog.Errorf("openapi NewSupportedFeature error: %+v", err)
		}
	}
	context.Locality = configuration.Locality
}

func Init() {
	pcfContext.Name = "pcf"
	pcfContext.UriScheme = models.UriScheme_HTTPS
	pcfContext.TimeFormat = "2006-01-02 15:04:05"
	pcfContext.DefaultBdtRefId = "BdtPolicyId-"
	pcfContext.NfService = make(map[models.ServiceName]models.NfService)
	pcfContext.PcfServiceUris = make(map[models.ServiceName]string)
	pcfContext.PcfSuppFeats = make(map[models.ServiceName]openapi.SupportedFeature)
	pcfContext.BdtPolicyIDGenerator = idgenerator.NewGenerator(1, math.MaxInt64)
	pcfContext.RatingGroupIdGenerator = idgenerator.NewGenerator(1, math.MaxInt64)
	InitPcfContext(&pcfContext)
}

// Create new PCF context
func GetSelf() *PCFContext {
	return &pcfContext
}

func GetTimeformat() string {
	return pcfContext.TimeFormat
}

func GetUri(name models.ServiceName) string {
	return pcfContext.PcfServiceUris[name]
}

var (
	PolicyAuthorizationUri       = factory.PcfPolicyAuthResUriPrefix + "/app-sessions/"
	SmUri                        = factory.PcfSMpolicyCtlResUriPrefix
	IPv4Address                  = "192.168."
	IPv6Address                  = "ffab::"
	PolicyDataChangeNotifyUri    = factory.PcfCallbackResUriPrefix + "/nudr-notify/policy-data"
	InfluenceDataUpdateNotifyUri = factory.PcfCallbackResUriPrefix + "/nudr-notify/influence-data"
	Ipv4_pool                    = make(map[string]string)
	Ipv6_pool                    = make(map[string]string)
)

// BdtPolicy default value
const DefaultBdtRefId = "BdtPolicyId-"

func (c *PCFContext) GetIPv4Uri() string {
	return fmt.Sprintf("%s://%s:%d", c.UriScheme, c.RegisterIPv4, c.SBIPort)
}

// Init NfService with supported service list ,and version of services
func (c *PCFContext) InitNFService(serviceList []factory.Service, version string) {
	tmpVersion := strings.Split(version, ".")
	versionUri := "v" + tmpVersion[0]
	for index, service := range serviceList {
		name := models.ServiceName(service.ServiceName)
		c.NfService[name] = models.NfService{
			ServiceInstanceId: strconv.Itoa(index),
			ServiceName:       name,
			Versions: &[]models.NfServiceVersion{
				{
					ApiFullVersion:  version,
					ApiVersionInUri: versionUri,
				},
			},
			Scheme:          c.UriScheme,
			NfServiceStatus: models.NfServiceStatus_REGISTERED,
			ApiPrefix:       c.GetIPv4Uri(),
			IpEndPoints: &[]models.IpEndPoint{
				{
					Ipv4Address: c.RegisterIPv4,
					Transport:   models.TransportProtocol_TCP,
					Port:        int32(c.SBIPort),
				},
			},
			SupportedFeatures: service.SuppFeat,
		}
	}
}

// Allocate PCF Ue with supi and add to pcf Context and returns allocated ue
func (c *PCFContext) NewPCFUe(Supi string) (*UeContext, error) {
	if strings.HasPrefix(Supi, "imsi-") {
		newUeContext := &UeContext{}
		newUeContext.SmPolicyData = make(map[string]*UeSmPolicyData)
		newUeContext.AMPolicyData = make(map[string]*UeAMPolicyData)
		newUeContext.PolAssociationIDGenerator = 1
		newUeContext.AppSessionIDGenerator = idgenerator.NewGenerator(1, math.MaxInt64)
		newUeContext.Supi = Supi
		c.UePool.Store(Supi, newUeContext)
		return newUeContext, nil
	} else {
		return nil, fmt.Errorf(" add Ue context fail ")
	}
}

// Return Bdt Policy Id with format "BdtPolicyId-%d" which be allocated
func (c *PCFContext) AllocBdtPolicyID() (bdtPolicyID string, err error) {
	var allocID int64
	if allocID, err = c.BdtPolicyIDGenerator.Allocate(); err != nil {
		logger.CtxLog.Warnf("Allocate pathID error: %+v", err)
		return "", err
	}

	bdtPolicyID = fmt.Sprintf("BdtPolicyId-%d", allocID)
	return bdtPolicyID, nil
}

// Find PcfUe which the policyId belongs to
func (c *PCFContext) PCFUeFindByPolicyId(PolicyId string) *UeContext {
	index := strings.LastIndex(PolicyId, "-")
	if index == -1 {
		return nil
	}
	supi := PolicyId[:index]
	if supi != "" {
		if value, ok := c.UePool.Load(supi); ok {
			ueContext := value.(*UeContext)
			return ueContext
		}
	}
	return nil
}

// Find PcfUe which the AppSessionId belongs to
func (c *PCFContext) PCFUeFindByAppSessionId(appSessionId string) *UeContext {
	index := strings.LastIndex(appSessionId, "-")
	if index == -1 {
		return nil
	}
	supi := appSessionId[:index]
	if supi != "" {
		if value, ok := c.UePool.Load(supi); ok {
			ueContext := value.(*UeContext)
			return ueContext
		}
	}
	return nil
}

// Find PcfUe which Ipv4 belongs to
func (c *PCFContext) PcfUeFindByIPv4(v4 string) *UeContext {
	var ue *UeContext
	c.UePool.Range(func(key, value interface{}) bool {
		ue = value.(*UeContext)
		if ue.SMPolicyFindByIpv4(v4) != nil {
			return false
		} else {
			return true
		}
	})

	return ue
}

// Find PcfUe which Ipv6 belongs to
func (c *PCFContext) PcfUeFindByIPv6(v6 string) *UeContext {
	var ue *UeContext
	c.UePool.Range(func(key, value interface{}) bool {
		ue = value.(*UeContext)
		if ue.SMPolicyFindByIpv6(v6) != nil {
			return false
		} else {
			return true
		}
	})

	return ue
}

// Find SMPolicy with AppSessionContext
func ueSMPolicyFindByAppSessionContext(ue *UeContext, req *models.AppSessionContextReqData) (*UeSmPolicyData, error) {
	var policy *UeSmPolicyData
	var err error

	if req.UeIpv4 != "" {
		policy = ue.SMPolicyFindByIdentifiersIpv4(req.UeIpv4, req.SliceInfo, req.Dnn, req.IpDomain)
		if policy == nil {
			err = fmt.Errorf("Can't find Ue with Ipv4[%s]", req.UeIpv4)
		}
	} else if req.UeIpv6 != "" {
		policy = ue.SMPolicyFindByIdentifiersIpv6(req.UeIpv6, req.SliceInfo, req.Dnn)
		if policy == nil {
			err = fmt.Errorf("Can't find Ue with Ipv6 prefix[%s]", req.UeIpv6)
		}
	} else {
		// TODO: find by MAC address
		err = fmt.Errorf("Ue finding by MAC address does not support")
	}
	return policy, err
}

// SessionBinding from application request to get corresponding Sm policy
func (c *PCFContext) SessionBinding(req *models.AppSessionContextReqData) (*UeSmPolicyData, error) {
	var selectedUE *UeContext
	var policy *UeSmPolicyData
	var err error

	if req.Supi != "" {
		if val, exist := c.UePool.Load(req.Supi); exist {
			selectedUE = val.(*UeContext)
		}
	}

	if req.Gpsi != "" && selectedUE == nil {
		c.UePool.Range(func(key, value interface{}) bool {
			ue := value.(*UeContext)
			if ue.Gpsi == req.Gpsi {
				selectedUE = ue
				return false
			} else {
				return true
			}
		})
	}

	if selectedUE != nil {
		policy, err = ueSMPolicyFindByAppSessionContext(selectedUE, req)
	} else {
		c.UePool.Range(func(key, value interface{}) bool {
			ue := value.(*UeContext)
			policy, err = ueSMPolicyFindByAppSessionContext(ue, req)
			return true
		})
	}
	if policy == nil && err == nil {
		err = fmt.Errorf("No SM policy found")
	}
	return policy, err
}

// SetDefaultUdrURI ... function to set DefaultUdrURI
func (c *PCFContext) SetDefaultUdrURI(uri string) {
	c.DefaultUdrURILock.Lock()
	defer c.DefaultUdrURILock.Unlock()
	c.DefaultUdrURI = uri
}

func Ipv4Pool(ipindex int32) string {
	ipv4address := IPv4Address + fmt.Sprint((int(ipindex)/255)+1) + "." + fmt.Sprint(int(ipindex)%255)
	return ipv4address
}

func Ipv4Index() int32 {
	if len(Ipv4_pool) == 0 {
		Ipv4_pool["1"] = Ipv4Pool(1)
	} else {
		for i := 1; i <= len(Ipv4_pool); i++ {
			if Ipv4_pool[fmt.Sprint(i)] == "" {
				Ipv4_pool[fmt.Sprint(i)] = Ipv4Pool(int32(i))
				return int32(i)
			}
		}

		Ipv4_pool[fmt.Sprint(int32(len(Ipv4_pool)+1))] = Ipv4Pool(int32(len(Ipv4_pool) + 1))
		return int32(len(Ipv4_pool))
	}
	return 1
}

func GetIpv4Address(ipindex int32) string {
	return Ipv4_pool[fmt.Sprint(ipindex)]
}

func DeleteIpv4index(Ipv4index int32) {
	delete(Ipv4_pool, fmt.Sprint(Ipv4index))
}

func Ipv6Pool(ipindex int32) string {
	ipv6address := IPv6Address + fmt.Sprintf("%x\n", ipindex)
	return ipv6address
}

func Ipv6Index() int32 {
	if len(Ipv6_pool) == 0 {
		Ipv6_pool["1"] = Ipv6Pool(1)
	} else {
		for i := 1; i <= len(Ipv6_pool); i++ {
			if Ipv6_pool[fmt.Sprint(i)] == "" {
				Ipv6_pool[fmt.Sprint(i)] = Ipv6Pool(int32(i))
				return int32(i)
			}
		}

		Ipv6_pool[fmt.Sprint(int32(len(Ipv6_pool)+1))] = Ipv6Pool(int32(len(Ipv6_pool) + 1))
		return int32(len(Ipv6_pool))
	}
	return 1
}

func GetIpv6Address(ipindex int32) string {
	return Ipv6_pool[fmt.Sprint(ipindex)]
}

func DeleteIpv6index(Ipv6index int32) {
	delete(Ipv6_pool, fmt.Sprint(Ipv6index))
}

func (c *PCFContext) NewAmfStatusSubscription(subscriptionID string, subscriptionData AMFStatusSubscriptionData) {
	c.AMFStatusSubsData.Store(subscriptionID, subscriptionData)
}

func (c *PCFContext) GetTokenCtx(serviceName models.ServiceName, targetNF models.NfType) (
	context.Context, *models.ProblemDetails, error,
) {
	if !c.OAuth2Required {
		return context.TODO(), nil, nil
	}
	return oauth.GetTokenCtx(models.NfType_PCF, targetNF,
		c.NfId, c.NrfUri, string(serviceName))
}

func (c *PCFContext) AuthorizationCheck(token string, serviceName models.ServiceName) error {
	if !c.OAuth2Required {
		logger.UtilLog.Debugf("PCFContext::AuthorizationCheck: OAuth2 not required\n")
		return nil
	}

	logger.UtilLog.Debugf("PCFContext::AuthorizationCheck: token[%s] serviceName[%s]\n", token, serviceName)
	return oauth.VerifyOAuth(token, string(serviceName), c.NrfCertPem)
}

This file contains

  1. PCFContext struct with fields:
  • NfId, unique identifier for the network function
  • Name, name of the PCF Instance
  • UriScheme, URI scheme (HTTP/HTTPS) used by the PCF
  • BindingIPv4, IPv4 address for binding
  • RegisterIPv4, Registered IPv4 address
  • SBIPort, SBI Port number
  • TimeFormat, Format to represent time
  • DefaultBdtRefId, default reference ID for BDT(bulk data transfer) policies
  • NfService, dictionary/map of service provided by the PCF
  • PcfServiceUris, dictionary/map of URIs for PCF services
  • PcfSuppFeats, dictionary/map supported features for PCF Services
  • NrfUri, URI of the Network Repository Function
  • NrfCertPem, PEM Certificate for NRF
  • DefaultUdrURI, Default URI for UDR
  • Locality, locality information for the PCF
  • UePool, Pool of UE contexts
  • BdtPolicyPool, Pool of BDT policies
  • BdtPolicyIDGenerator, ID generator for BDT policies
  • AppSessionPool, Pool of application sessions
  • AMFStatusSubsData, AMF status subscription data
  • DefaultUdrURILock, RWMutex for locking default UDR URI
  • RatingGroupIdGenerator, ID generator for rating groups
  • OAuth2Required, boolean indicate if OAuth2 is required
  1. AMFStatusSubscriptionData struct that contain:
  • AmfUri, Uri of the AMF
  • AmfStatusUri, Uri for AMF status notification
  • GuamiList, List of GUAMI(Globally Unique AMF Identifier) associated with the subscription
  1. AppSessionData struct that contain
  • AppSessionId, Application Session Id
  • AppSessionContext, Context of the application session
  • RelatedPccRuleIds, Dictionary/maps of PCC (Policy Control and Charging) Rule related to the application session
  • PccRuleIdMapToCompId, Dictionary/maps of PCC rule IDs to component IDs
  • Events, Dictionary/maps of events and their notification methods
  • EventUri, URI for event notifications
  • SmPolicyData, Session Management policy data related to the session
  1. pcfContext variable declaration with PCFContext as data type

  2. InitPcfContext(), this function will create a pcfConfig object called config(a variable defined at pkg/factory/factory.go with type config) as a buffer for configuration data, emitt Version and description that stored in config. The configuration variable also passed from config variable, generate NfId, connect to mongodb server, load NRF URI, NRF cert pem, load default registerIPv4 and SBI Port from pkg/factory/config.go. Then the function, will load the SBI configuration from config object. The function also load serviceList, initialize NF services, configure the time format and default BDT ref ID. Iterate the NF service and put each service to the PcfServiceUris map

  3. Init(), this function contain default value of each pcfContext field value, and map object creation

  4. Getter function, there are GetSelf() that return PCFContext object, GetTimeFormat() that return time format string, GetUri() that return URI for a specific service name and GetIPv4Uri() that return PCF URI.

  5. Var clause that define the constant for URI string concatenation, and ip addres pool map definition

  6. InitNFService(), this function initialize service with service list that passed on to the function (the service object definition located at pkg/factory/config.go). This function also set up the service details such as name, version, scheme, status, API prefix, and supported features.

  7. NewPCFUe(), this function initialize an object that represents user equipment. A User Equipment has Context, SM Policy, AM Policy, SUPI, and IDs. This function also store UeContext and it's SUPI on a UePool

  8. UE Context Searching Method such as PCFUeFindByPolicyId that retrieve an UEContext based on SUPI, PCFUeFindByAppSessionId that retrieve UEContext based on App Session, PcfUeFindByIPv4 that return UEContext by IPv4 Address.

  9. Method to retrieve UE SM Policy based on App Session Context.

  10. SessionBinding() method to binds the session by finding the corresponding SM Policy for the application session request data and search for UE Context using SUPI or GPSI and retrieve the SM policy data

  11. IPv4Pool(), IPv4Index(), GetIPv4Address(), DeleteIPv4index(). The IPv4Pool are used to generate and return IPv4 address based on provided index, IPv4Index will allocate new IPv4 index and return it, GetIPv4Address to retrieve IPv4 address for the given index and DeleteIPv4index to delete the IPv4 address given the index

p.

q.

r. Summary:

This file contains the main implementation of PCF. If developer wants to move the PCF to another NF, they should look at this file and copy this file to another context.go to maintain code structure consistency.

h. internal/context/ue.go

package context

import (
	"fmt"
	"math"
	"reflect"
	"strconv"
	"strings"

	"github.com/free5gc/openapi/models"
	"github.com/free5gc/pcf/internal/logger"
	"github.com/free5gc/util/idgenerator"
)

// key is supi
type UeContext struct {
	// Ue Context
	Supi                      string
	Gpsi                      string
	Pei                       string
	GroupIds                  []string
	PolAssociationIDGenerator uint32
	AMPolicyData              map[string]*UeAMPolicyData // use PolAssoId(ue.Supi-numPolId) as key

	// Udr Ref
	UdrUri string
	// SMPolicy
	SmPolicyData map[string]*UeSmPolicyData // use smPolicyId(ue.Supi-pduSessionId) as key
	// App Session Related
	// AppSessionIDGenerator uint64
	AppSessionIDGenerator *idgenerator.IDGenerator
	// PolicyAuth
	AfRoutReq *models.AfRoutingRequirement
	AspId     string
	// Policy Decision
	AppSessionIdStore           *AppSessionIdStore
	PolicyDataSubscriptionStore *models.PolicyDataSubscription
	PolicyDataChangeStore       *models.PolicyDataChangeNotification

	// ChargingRatingGroup
	RatingGroupData map[string][]int32 // use smPolicyId(ue.Supi-pduSessionId) as key
}

type UeAMPolicyData struct {
	PolAssoId         string
	AccessType        models.AccessType
	NotificationUri   string
	ServingPlmn       *models.NetworkId
	AltNotifIpv4Addrs []string
	AltNotifIpv6Addrs []string
	// TODO: AMF Status Change
	AmfStatusUri string
	Guami        *models.Guami
	ServiveName  string
	// TraceReq *TraceData
	// Policy Association
	Triggers    []models.RequestTrigger
	ServAreaRes *models.ServiceAreaRestriction
	Rfsp        int32
	UserLoc     *models.UserLocation
	TimeZone    string
	SuppFeat    string
	// about AF request
	Pras map[string]models.PresenceInfo
	// related to UDR Subscription Data
	AmPolicyData *models.AmPolicyData // Svbscription Data
	// Corresponding UE
	PcfUe *UeContext
}

type UeSmPolicyData struct {
	// PduSessionId    int32
	// DNN             string
	// NotificationUri string
	// Snssai                 models.Snssai
	// PduSessionType         models.PduSessionType
	// IPAddress              models.IpAddress
	// IPDomain               string
	// Var3gppPsDataOffStatus bool
	// SmfId                  string
	// TraceReq *TraceData
	// RecoveryTime     *time.Time
	PackFiltIdGenerator int32
	PccRuleIdGenerator  int32
	ChargingIdGenerator int32

	// FlowMapsToPackFiltIds  map[string][]string // use Flow Description(in TS 29214) as key map to pcc rule ids
	PackFiltMapToPccRuleId map[string]string // use PackFiltId as Key
	// Related to GBR
	RemainGbrUL *float64
	RemainGbrDL *float64
	// related to UDR Subscription Data
	SmPolicyData *models.SmPolicyData // Svbscription Data
	// related to Policy
	PolicyContext  *models.SmPolicyContextData
	PolicyDecision *models.SmPolicyDecision
	// related to AppSession
	AppSessions map[string]bool // related appSessionId
	// Corresponding UE
	PcfUe                  *UeContext
	InfluenceDataToPccRule map[string]string
	SubscriptionID         string
}

// NewUeAMPolicyData returns created UeAMPolicyData data and insert this data to Ue.AMPolicyData with assolId as key
func (ue *UeContext) NewUeAMPolicyData(assolId string, req models.PolicyAssociationRequest) *UeAMPolicyData {
	ue.Gpsi = req.Gpsi
	ue.Pei = req.Pei
	ue.GroupIds = req.GroupIds
	ue.AMPolicyData[assolId] = &UeAMPolicyData{
		PolAssoId:         assolId,
		ServAreaRes:       req.ServAreaRes,
		AltNotifIpv4Addrs: req.AltNotifIpv4Addrs,
		AltNotifIpv6Addrs: req.AltNotifIpv6Addrs,
		AccessType:        req.AccessType,
		NotificationUri:   req.NotificationUri,
		ServingPlmn:       req.ServingPlmn,
		TimeZone:          req.TimeZone,
		Rfsp:              req.Rfsp,
		Guami:             req.Guami,
		UserLoc:           req.UserLoc,
		ServiveName:       req.ServiveName,
		PcfUe:             ue,
	}
	ue.AMPolicyData[assolId].Pras = make(map[string]models.PresenceInfo)
	return ue.AMPolicyData[assolId]
}

// returns UeSmPolicyData and insert related info to Ue with smPolId
func (ue *UeContext) NewUeSmPolicyData(
	key string, request models.SmPolicyContextData, smData *models.SmPolicyData,
) *UeSmPolicyData {
	if smData == nil {
		return nil
	}
	data := UeSmPolicyData{}
	data.PolicyContext = &request
	// data.DNN = request.Dnn
	// data.Snssai = *request.SliceInfo
	// data.PduSessionId = request.PduSessionId
	// data.PduSessionType = request.PduSessionType
	// switch request.PduSessionType {
	// case models.PduSessionType_IPV4:
	// 	data.IPAddress.Ipv4Addr = request.Ipv4Address
	// 	data.IPDomain = request.IpDomain
	// case models.PduSessionType_IPV6:
	// 	data.IPAddress.Ipv6Prefix = request.Ipv6AddressPrefix
	// case models.PduSessionType_IPV4_V6:
	// 	data.IPAddress.Ipv4Addr = request.Ipv4Address
	// 	data.IPAddress.Ipv6Prefix = request.Ipv6AddressPrefix
	// 	data.IPDomain = request.IpDomain
	// }
	// data.NotificationUri = request.NotificationUri
	// data.SmfId = request.SmfId
	// data.Var3gppPsDataOffStatus = request.Var3gppPsDataOffStatus
	data.SmPolicyData = smData
	data.PackFiltIdGenerator = 1
	data.PackFiltMapToPccRuleId = make(map[string]string)
	data.AppSessions = make(map[string]bool)
	// data.RefToAmPolicy = amData
	data.PccRuleIdGenerator = 1
	data.ChargingIdGenerator = 1

	data.PcfUe = ue
	ue.SmPolicyData[key] = &data
	data.InfluenceDataToPccRule = make(map[string]string)
	return &data
}

// Remove Pcc rule which PccRuleId in the policy
func (policy *UeSmPolicyData) RemovePccRule(pccRuleId string, deletedSmPolicyDec *models.SmPolicyDecision) error {
	decision := policy.PolicyDecision
	if decision == nil {
		return fmt.Errorf("Can't find the Policy Decision")
	}
	if rule, exist := decision.PccRules[pccRuleId]; exist {
		if deletedSmPolicyDec != nil {
			if deletedSmPolicyDec.PccRules == nil {
				deletedSmPolicyDec.PccRules = make(map[string]*models.PccRule)
			}
			deletedSmPolicyDec.PccRules[pccRuleId] = nil
		}
		for _, info := range rule.FlowInfos {
			delete(policy.PackFiltMapToPccRuleId, info.PackFiltId)
		}
		for _, id := range rule.RefQosData {
			if decision.QosDecs != nil {
				policy.IncreaseRemainGBR(id)
				delete(decision.QosDecs, id)
				if len(decision.QosDecs) == 0 {
					decision.QosDecs = nil
				}
			} else {
				break
			}
		}
		if rule.RefCondData != "" {
			if decision.Conds != nil {
				delete(decision.Conds, rule.RefCondData)
				if len(decision.Conds) == 0 {
					decision.Conds = nil
				}
				if deletedSmPolicyDec != nil {
					if deletedSmPolicyDec.Conds == nil {
						deletedSmPolicyDec.Conds = make(map[string]*models.ConditionData)
					}
					deletedSmPolicyDec.Conds[rule.RefCondData] = nil
				}
			}
		}
		for _, id := range rule.RefChgData {
			if decision.ChgDecs != nil {
				delete(decision.ChgDecs, id)
				if len(decision.ChgDecs) == 0 {
					decision.ChgDecs = nil
				}
				if deletedSmPolicyDec != nil {
					if deletedSmPolicyDec.ChgDecs == nil {
						deletedSmPolicyDec.ChgDecs = make(map[string]*models.ChargingData)
					}
					deletedSmPolicyDec.ChgDecs[id] = nil
				}
			} else {
				break
			}
		}
		for _, id := range rule.RefTcData {
			if decision.TraffContDecs != nil {
				delete(decision.TraffContDecs, id)
				if len(decision.TraffContDecs) == 0 {
					decision.TraffContDecs = nil
				}
				if deletedSmPolicyDec != nil {
					if deletedSmPolicyDec.TraffContDecs == nil {
						deletedSmPolicyDec.TraffContDecs = make(map[string]*models.TrafficControlData)
					}
					deletedSmPolicyDec.TraffContDecs[id] = nil
				}
			} else {
				break
			}
		}
		for _, id := range rule.RefUmData {
			if decision.UmDecs != nil {
				delete(decision.UmDecs, id)
				if len(decision.UmDecs) == 0 {
					decision.UmDecs = nil
				}
				if deletedSmPolicyDec != nil {
					if deletedSmPolicyDec.UmDecs == nil {
						deletedSmPolicyDec.UmDecs = make(map[string]*models.UsageMonitoringData)
					}
					deletedSmPolicyDec.UmDecs[id] = nil
				}
			} else {
				break
			}
		}
		delete(decision.PccRules, pccRuleId)
	} else {
		return fmt.Errorf("Can't find the pccRuleId[%s] in Session[%d]", pccRuleId, policy.PolicyContext.PduSessionId)
	}
	return nil
}

// Check if the afEvent exists in smPolicy
func (policy *UeSmPolicyData) CheckRelatedAfEvent(event models.AfEvent) (found bool) {
	for appSessionId := range policy.AppSessions {
		if val, ok := GetSelf().AppSessionPool.Load(appSessionId); ok {
			appSession := val.(*AppSessionData)
			for afEvent := range appSession.Events {
				if afEvent == event {
					return true
				}
			}
		}
	}
	return false
}

// Arrange Exist Event policy Sm policy about afevents and return if it changes or not and
func (policy *UeSmPolicyData) ArrangeExistEventSubscription() (changed bool) {
	triggers := []models.PolicyControlRequestTrigger{}
	for _, trigger := range policy.PolicyDecision.PolicyCtrlReqTriggers {
		var afEvent models.AfEvent
		switch trigger {
		case models.PolicyControlRequestTrigger_PLMN_CH: // PLMN Change
			afEvent = models.AfEvent_PLMN_CHG
		case models.PolicyControlRequestTrigger_QOS_NOTIF:
			// SMF notify PCF when receiving from RAN that QoS can/can't be guaranteed (subsclause 4.2.4.20 in TS29512) (always)
			afEvent = models.AfEvent_QOS_NOTIF
		case models.PolicyControlRequestTrigger_SUCC_RES_ALLO:
			// Successful resource allocation (subsclause 4.2.6.5.5, 4.2.4.14 in TS29512)
			afEvent = models.AfEvent_SUCCESSFUL_RESOURCES_ALLOCATION
		case models.PolicyControlRequestTrigger_AC_TY_CH: // Change of RatType
			afEvent = models.AfEvent_ACCESS_TYPE_CHANGE
		case models.PolicyControlRequestTrigger_US_RE: // UMC
			afEvent = models.AfEvent_USAGE_REPORT
		}
		if afEvent != "" && !policy.CheckRelatedAfEvent(afEvent) {
			changed = true
		} else {
			triggers = append(triggers, trigger)
		}
	}
	policy.PolicyDecision.PolicyCtrlReqTriggers = triggers
	return
}

// Increase remain GBR of this policy and returns original UL DL GBR for resume case
func (policy *UeSmPolicyData) IncreaseRemainGBR(qosId string) (origUl, origDl *float64) {
	decision := policy.PolicyDecision
	if decision == nil {
		return
	}
	if qos, exist := decision.QosDecs[qosId]; exist {
		if qos.Var5qi <= 4 {
			// Add GBR
			origUl = IncreaseRamainBitRate(policy.RemainGbrUL, qos.GbrUl)
			origDl = IncreaseRamainBitRate(policy.RemainGbrDL, qos.GbrDl)
		}
	}
	return
}

// Increase remain Bit Rate and returns original Bit Rate
func IncreaseRamainBitRate(remainBitRate *float64, reqBitRate string) (orig *float64) {
	if remainBitRate != nil && reqBitRate != "" {
		bitRate, err := ConvertBitRateToKbps(reqBitRate)
		if err == nil {
			orig = new(float64)
			*orig = *remainBitRate
			*remainBitRate += bitRate
		}
	}
	return
}

// Decrease remain GBR of this policy and returns UL DL GBR
func (policy *UeSmPolicyData) DecreaseRemainGBR(req *models.RequestedQos) (gbrDl, gbrUl string, err error) {
	if req == nil {
		return "", "", nil
	}
	if req.Var5qi <= 4 {
		err = DecreaseRamainBitRate(policy.RemainGbrDL, req.GbrDl)
		if err != nil {
			return
		}
		gbrDl = req.GbrDl
		err = DecreaseRamainBitRate(policy.RemainGbrUL, req.GbrUl)
		if err != nil {
			return
		}
		gbrUl = req.GbrUl
	}
	return
}

// Decrease remain Bit Rate
func DecreaseRamainBitRate(remainBitRate *float64, reqBitRate string) error {
	if reqBitRate != "" {
		bitRate, err := ConvertBitRateToKbps(reqBitRate)
		if err != nil {
			return err
		}
		if remainBitRate != nil {
			if *remainBitRate < bitRate {
				return fmt.Errorf("Request BitRate exceed Dnn Aggregate BitRate of UE")
			}
			*remainBitRate -= bitRate
		}
	}
	return nil
}

// Returns remin Bit rate string and decrease ir to zero
func DecreaseRamainBitRateToZero(remainBitRate *float64) string {
	if remainBitRate != nil {
		bitRate := ConvertBitRateToString(*remainBitRate)
		*remainBitRate = 0
		return bitRate
	}
	return ""
}

// returns AM Policy which AccessType and plmnId match
func (ue *UeContext) FindAMPolicy(anType models.AccessType, plmnId *models.NetworkId) *UeAMPolicyData {
	if ue == nil || plmnId == nil {
		return nil
	}
	for _, amPolicy := range ue.AMPolicyData {
		if amPolicy.AccessType == anType && reflect.DeepEqual(*amPolicy.ServingPlmn, *plmnId) {
			return amPolicy
		}
	}
	return nil
}

// Return App Session Id with format "ue.Supi-%d" which be allocated
func (ue *UeContext) AllocUeAppSessionId(context *PCFContext) string {
	var allocID int64
	var err error
	if allocID, err = ue.AppSessionIDGenerator.Allocate(); err != nil {
		logger.CtxLog.Warnf("Allocate AppSessionId error: %+v", err)
		return ""
	}
	appSessionID := fmt.Sprintf("%s-%d", ue.Supi, allocID)
	return appSessionID
}

// returns SM Policy by IPv4
func (ue *UeContext) SMPolicyFindByIpv4(v4 string) *UeSmPolicyData {
	for _, smPolicy := range ue.SmPolicyData {
		if smPolicy.PolicyContext.Ipv4Address == v4 {
			return smPolicy
		}
	}
	return nil
}

// returns SM Policy by IPv6
func (ue *UeContext) SMPolicyFindByIpv6(v6 string) *UeSmPolicyData {
	for _, smPolicy := range ue.SmPolicyData {
		if smPolicy.PolicyContext.Ipv6AddressPrefix == v6 {
			return smPolicy
		}
	}
	return nil
}

// returns SM Policy by IPv4
func (ue *UeContext) SMPolicyFindByIdentifiersIpv4(
	v4 string, sNssai *models.Snssai, dnn string, ipDomain string,
) *UeSmPolicyData {
	for _, smPolicy := range ue.SmPolicyData {
		policyContext := smPolicy.PolicyContext
		if policyContext.Ipv4Address == v4 {
			if dnn != "" && policyContext.Dnn != dnn {
				continue
			}
			if ipDomain != "" && policyContext.IpDomain != "" && policyContext.IpDomain != ipDomain {
				continue
			}
			if sNssai != nil && !reflect.DeepEqual(sNssai, policyContext.SliceInfo) {
				continue
			}
			return smPolicy
		}
	}
	return nil
}

// returns SM Policy by IPv6
func (ue *UeContext) SMPolicyFindByIdentifiersIpv6(v6 string, sNssai *models.Snssai, dnn string) *UeSmPolicyData {
	for _, smPolicy := range ue.SmPolicyData {
		policyContext := smPolicy.PolicyContext
		if policyContext.Ipv6AddressPrefix == v6 {
			if dnn != "" && policyContext.Dnn != dnn {
				continue
			}
			if sNssai != nil && !reflect.DeepEqual(sNssai, policyContext.SliceInfo) {
				continue
			}
			return smPolicy
		}
	}
	return nil
}

// AppSessionIdStore -
type AppSessionIdStore struct {
	AppSessionId      string
	AppSessionContext models.AppSessionContext
}

var AppSessionContextStore []AppSessionIdStore

// BdtPolicyData_store -
var (
	BdtPolicyData_store    []models.BdtPolicyData
	CreateFailBdtDateStore []models.BdtData
)

// Convert bitRate string to float64 with uint Kbps
func ConvertBitRateToKbps(bitRate string) (kBitRate float64, err error) {
	list := strings.Split(bitRate, " ")
	if len(list) != 2 {
		err = fmt.Errorf("bitRate format error")
		return 0, err
	}
	// parse exponential value with 2 as base
	exp := 0.0
	switch list[1] {
	case "Tbps":
		exp = 30.0
	case "Gbps":
		exp = 20.0
	case "Mbps":
		exp = 10.0
	case "Kbps":
		exp = 0.0
	case "bps":
		exp = -10.0
	default:
		err = fmt.Errorf("bitRate format error")
		return 0, err
	}
	// parse value from string to float64
	kBitRate, err = strconv.ParseFloat(list[0], 64)
	if err == nil {
		kBitRate = kBitRate * math.Pow(2, exp)
	} else {
		kBitRate = 0.0
	}
	return kBitRate, err
}

// Convert bitRate from float64 to String
func ConvertBitRateToString(kBitRate float64) (bitRate string) {
	return fmt.Sprintf("%f Kbps", kBitRate)
}

This file contains:

  1. UeContext struc, this struct represents UE in PCF point of view. According to PCF, the UE has:
  • SUPI, Hold the Subscription Permanent Identifier
  • GPSI, hold the Generic Public Subscription
  • PEI, hold Permanen Equipment Identifier
  • GroupId, group identifier for the user
  • PolAssociationIdGenerator, Policy Association ID
  • AMPolicyData, dictionary of AM Policy associated to the UE
  • UdrUri, URI to UDR
  • SmPolicyData, dictionary of SM Policy associated to the UE
  • AppSessionIdGenerator
  • AfRoutReq, AF routing requirements
  • AspId, Application Service Provider ID
  • AppSessionIdStore, dic
  • PolicyDataSubscriptionStore, struct that store UE policy data subscription
​type PolicyDataSubscription struct {
​   // string providing an URI formatted according to IETF RFC 3986.
​   NotificationUri       string   `json:"notificationUri" bson:"notificationUri"`
​   MonitoredResourceUris []string `json:"monitoredResourceUris" bson:"monitoredResourceUris"`
​   SupportedFeatures     string   `json:"supportedFeatures,omitempty" bson:"supportedFeatures"`
}
  • PolicyDataChangeStore, struct that holds policy data change notifications
type PolicyDataChangeNotification struct {
	AmPolicyData            *AmPolicyData            `json:"amPolicyData,omitempty" yaml:"amPolicyData" bson:"amPolicyData" mapstructure:"AmPolicyData"`
	UePolicySet             *UePolicySet             `json:"uePolicySet,omitempty" yaml:"uePolicySet" bson:"uePolicySet" mapstructure:"UePolicySet"`
	SmPolicyData            *SmPolicyData            `json:"smPolicyData,omitempty" yaml:"smPolicyData" bson:"smPolicyData" mapstructure:"SmPolicyData"`
	UsageMonData            *UsageMonData            `json:"usageMonData,omitempty" yaml:"usageMonData" bson:"usageMonData" mapstructure:"UsageMonData"`
	SponsorConnectivityData *SponsorConnectivityData `json:"SponsorConnectivityData,omitempty" yaml:"SponsorConnectivityData" bson:"SponsorConnectivityData" mapstructure:"SponsorConnectivityData"`
	BdtData                 *BdtData                 `json:"bdtData,omitempty" yaml:"bdtData" bson:"bdtData" mapstructure:"BdtData"`
	UeId                    string                   `json:"ueId,omitempty" yaml:"ueId" bson:"ueId" mapstructure:"UeId"`
	SponsorId               string                   `json:"sponsorId,omitempty" yaml:"sponsorId" bson:"sponsorId" mapstructure:"SponsorId"`
	BdtRefId                string                   `json:"bdtRefId,omitempty" yaml:"bdtRefId" bson:"bdtRefId" mapstructure:"BdtRefId"`
	UsageMonId              string                   `json:"usageMonId,omitempty" yaml:"usageMonId" bson:"usageMonId" mapstructure:"UsageMonId"`
}
  • RatingGroupData, Data related to charging rating groups, keyed by policy ID
  1. UeAMPolicyData, struct that hold Access and Mobility Policy representation. This struct has:
  • PolAssoId, policy association id
  • AccessType, 3GPP/non-3GPP
  • NotificationUri, URI for notification
  • ServingPlmn, Identifier to Public Land Mobile Network
  • AltNotifIpv4Addrs, alternative IPv4 address for notification
  • AltNotifIpv6Addrs, alternative IPv6 address for notification
  • AmfStatusUri, URI for AMF status changes
  • GUAMI, globally unique AMF Identifier
  • ServiceName, name of the service
  • Triggers, list of request triggers
  • ServAreaRes, Service area restriction
type ServiceAreaRestriction struct {
	RestrictionType               RestrictionType `json:"restrictionType,omitempty" yaml:"restrictionType" bson:"restrictionType" mapstructure:"RestrictionType"`
	Areas                         []Area          `json:"areas,omitempty" yaml:"areas" bson:"areas" mapstructure:"Areas"`
	MaxNumOfTAs                   int32           `json:"maxNumOfTAs,omitempty" yaml:"maxNumOfTAs" bson:"maxNumOfTAs" mapstructure:"MaxNumOfTAs"`
	MaxNumOfTAsForNotAllowedAreas int32           `json:"maxNumOfTAsForNotAllowedAreas,omitempty" yaml:"maxNumOfTAsForNotAllowedAreas" bson:"maxNumOfTAsForNotAllowedAreas" mapstructure:"MaxNumOfTAsForNotAllowedAreas"`
}
  • Rfsp, Radio Frequency Sharing Policy
  • UserLoc, User Location
type UserLocation struct {
	EutraLocation *EutraLocation `json:"eutraLocation,omitempty" yaml:"eutraLocation" bson:"eutraLocation" mapstructure:"EutraLocation"`
	NrLocation    *NrLocation    `json:"nrLocation,omitempty" yaml:"nrLocation" bson:"nrLocation" mapstructure:"NrLocation"`
	N3gaLocation  *N3gaLocation  `json:"n3gaLocation,omitempty" yaml:"n3gaLocation" bson:"n3gaLocation" mapstructure:"N3gaLocation"`
	UtraLocation  *UtraLocation  `json:"utraLocation,omitempty" yaml:"utraLocation" bson:"utraLocation" mapstructure:"UtraLocation"`
	GeraLocation  *GeraLocation  `json:"geraLocation,omitempty" yaml:"geraLocation" bson:"geraLocation" mapstructure:"GeraLocation"`
}
  • SuppFeat, supported feature
  • AmPolicyData, AM policy data from UDR
type AmPolicyData struct {
	SubscCats []string `json:"subscCats,omitempty" bson:"subscCats"`
}
  • PcfUe, Corresponding UE Context
  1. UeSmPolicyData, struct that represents Session Management Policy associated to the UE. This struct has:
  • PackFiltIdGenerator, packet filter ID
  • PccRuleIdGenerator, PCC rule ID
  • ChargingIdGenerator, charging ID
  • PackFiltMapToPccRuleId, mapping between packet filter IDs to PCC rule IDs
  • RemainGbrUL, remaining guaranteed bit rate for uplink
  • RemainGbrDL, remaining guaranteed bit rate for downlink
  • SmPolicyData, SM Policy Data from UDR
type SmPolicyData struct {
	SmPolicySnssaiData map[string]SmPolicySnssaiData `json:"smPolicySnssaiData" bson:"smPolicySnssaiData"`
	UmDataLimits       map[string]UsageMonDataLimit  `json:"umDataLimits,omitempty" bson:"umDataLimits"`
	UmData             map[string]UsageMonData       `json:"umData,omitempty" bson:"umData"`
}
  • PolicyContext
type SmPolicyContextData struct {
	AccNetChId              *AccNetChId            `json:"accNetChId,omitempty" yaml:"accNetChId" bson:"accNetChId" mapstructure:"AccNetChId"`
	ChargEntityAddr         *AccNetChargingAddress `json:"chargEntityAddr,omitempty" yaml:"chargEntityAddr" bson:"chargEntityAddr" mapstructure:"ChargEntityAddr"`
	Gpsi                    string                 `json:"gpsi,omitempty" yaml:"gpsi" bson:"gpsi" mapstructure:"Gpsi"`
	Supi                    string                 `json:"supi" yaml:"supi" bson:"supi" mapstructure:"Supi"`
	InterGrpIds             []string               `json:"interGrpIds,omitempty" yaml:"interGrpIds" bson:"interGrpIds" mapstructure:"InterGrpIds"`
	PduSessionId            int32                  `json:"pduSessionId" yaml:"pduSessionId" bson:"pduSessionId" mapstructure:"PduSessionId"`
	PduSessionType          PduSessionType         `json:"pduSessionType" yaml:"pduSessionType" bson:"pduSessionType" mapstructure:"PduSessionType"`
	Chargingcharacteristics string                 `json:"chargingcharacteristics,omitempty" yaml:"chargingcharacteristics" bson:"chargingcharacteristics" mapstructure:"Chargingcharacteristics"`
	Dnn                     string                 `json:"dnn" yaml:"dnn" bson:"dnn" mapstructure:"Dnn"`
	NotificationUri         string                 `json:"notificationUri" yaml:"notificationUri" bson:"notificationUri" mapstructure:"NotificationUri"`
	AccessType              AccessType             `json:"accessType,omitempty" yaml:"accessType" bson:"accessType" mapstructure:"AccessType"`
	RatType                 RatType                `json:"ratType,omitempty" yaml:"ratType" bson:"ratType" mapstructure:"RatType"`
	ServingNetwork          *NetworkId             `json:"servingNetwork,omitempty" yaml:"servingNetwork" bson:"servingNetwork" mapstructure:"ServingNetwork"`
	UserLocationInfo        *UserLocation          `json:"userLocationInfo,omitempty" yaml:"userLocationInfo" bson:"userLocationInfo" mapstructure:"UserLocationInfo"`
	UeTimeZone              string                 `json:"ueTimeZone,omitempty" yaml:"ueTimeZone" bson:"ueTimeZone" mapstructure:"UeTimeZone"`
	Pei                     string                 `json:"pei,omitempty" yaml:"pei" bson:"pei" mapstructure:"Pei"`
	Ipv4Address             string                 `json:"ipv4Address,omitempty" yaml:"ipv4Address" bson:"ipv4Address" mapstructure:"Ipv4Address"`
	Ipv6AddressPrefix       string                 `json:"ipv6AddressPrefix,omitempty" yaml:"ipv6AddressPrefix" bson:"ipv6AddressPrefix" mapstructure:"Ipv6AddressPrefix"`
	// Indicates the IPv4 address domain
	IpDomain     string                `json:"ipDomain,omitempty" yaml:"ipDomain" bson:"ipDomain" mapstructure:"IpDomain"`
	SubsSessAmbr *Ambr                 `json:"subsSessAmbr,omitempty" yaml:"subsSessAmbr" bson:"subsSessAmbr" mapstructure:"SubsSessAmbr"`
	SubsDefQos   *SubscribedDefaultQos `json:"subsDefQos,omitempty" yaml:"subsDefQos" bson:"subsDefQos" mapstructure:"SubsDefQos"`
	// Contains the number of supported packet filter for signalled QoS rules.
	NumOfPackFilter int32 `json:"numOfPackFilter,omitempty" yaml:"numOfPackFilter" bson:"numOfPackFilter" mapstructure:"NumOfPackFilter"`
	// If it is included and set to true, the online charging is applied to the PDU session.
	Online bool `json:"online,omitempty" yaml:"online" bson:"online" mapstructure:"Online"`
	// If it is included and set to true, the offline charging is applied to the PDU session.
	Offline bool `json:"offline,omitempty" yaml:"offline" bson:"offline" mapstructure:"Offline"`
	// If it is included and set to true, the 3GPP PS Data Off is activated by the UE.
	Var3gppPsDataOffStatus bool `json:"3gppPsDataOffStatus,omitempty" yaml:"3gppPsDataOffStatus" bson:"3gppPsDataOffStatus" mapstructure:"Var3gppPsDataOffStatus"`
	// If it is included and set to true, the reflective QoS is supported by the UE.
	RefQosIndication bool               `json:"refQosIndication,omitempty" yaml:"refQosIndication" bson:"refQosIndication" mapstructure:"RefQosIndication"`
	TraceReq         *TraceData         `json:"traceReq,omitempty" yaml:"traceReq" bson:"traceReq" mapstructure:"TraceReq"`
	SliceInfo        *Snssai            `json:"sliceInfo" yaml:"sliceInfo" bson:"sliceInfo" mapstructure:"SliceInfo"`
	QosFlowUsage     QosFlowUsage       `json:"qosFlowUsage,omitempty" yaml:"qosFlowUsage" bson:"qosFlowUsage" mapstructure:"QosFlowUsage"`
	ServNfId         *ServingNfIdentity `json:"servNfId,omitempty" yaml:"servNfId" bson:"servNfId" mapstructure:"ServNfId"`
	SuppFeat         string             `json:"suppFeat,omitempty" yaml:"suppFeat" bson:"suppFeat" mapstructure:"SuppFeat"`
	SmfId            string             `json:"smfId,omitempty" yaml:"smfId" bson:"smfId" mapstructure:"SmfId"`
	RecoveryTime     *time.Time         `json:"recoveryTime,omitempty" yaml:"recoveryTime" bson:"recoveryTime" mapstructure:"RecoveryTime"`
}
  • PolicyDecision
type SmPolicyDecision struct {
	// A map of Sessionrules with the content being the SessionRule as described in subclause 5.6.2.7.
	SessRules map[string]*SessionRule `json:"sessRules,omitempty" yaml:"sessRules" bson:"sessRules" mapstructure:"SessRules"`
	// A map of PCC rules with the content being the PCCRule as described in subclause 5.6.2.6.
	PccRules map[string]*PccRule `json:"pccRules,omitempty" yaml:"pccRules" bson:"pccRules" mapstructure:"PccRules"`
	// If it is included and set to true, it indicates the P-CSCF Restoration is requested.
	PcscfRestIndication bool `json:"pcscfRestIndication,omitempty" yaml:"pcscfRestIndication" bson:"pcscfRestIndication" mapstructure:"PcscfRestIndication"`
	// Map of QoS data policy decisions.
	QosDecs map[string]*QosData `json:"qosDecs,omitempty" yaml:"qosDecs" bson:"qosDecs" mapstructure:"QosDecs"`
	// Map of Charging data policy decisions.
	ChgDecs      map[string]*ChargingData `json:"chgDecs,omitempty" yaml:"chgDecs" bson:"chgDecs" mapstructure:"ChgDecs"`
	ChargingInfo *ChargingInformation     `json:"chargingInfo,omitempty" yaml:"chargingInfo" bson:"chargingInfo" mapstructure:"ChargingInfo"`
	// Map of Traffic Control data policy decisions.
	TraffContDecs map[string]*TrafficControlData `json:"traffContDecs,omitempty" yaml:"traffContDecs" bson:"traffContDecs" mapstructure:"TraffContDecs"`
	// Map of Usage Monitoring data policy decisions.
	UmDecs map[string]*UsageMonitoringData `json:"umDecs,omitempty" yaml:"umDecs" bson:"umDecs" mapstructure:"UmDecs"`
	// Map of QoS characteristics for non standard 5QIs. This map uses the 5QI values as keys.
	QosChars           map[string]*QosCharacteristics `json:"qosChars,omitempty" yaml:"qosChars" bson:"qosChars" mapstructure:"QosChars"`
	ReflectiveQoSTimer int32                          `json:"reflectiveQoSTimer,omitempty" yaml:"reflectiveQoSTimer" bson:"reflectiveQoSTimer" mapstructure:"ReflectiveQoSTimer"`
	// A map of condition data with the content being as described in subclause 5.6.2.9.
	Conds            map[string]*ConditionData `json:"conds,omitempty" yaml:"conds" bson:"conds" mapstructure:"Conds"`
	RevalidationTime *time.Time                `json:"revalidationTime,omitempty" yaml:"revalidationTime" bson:"revalidationTime" mapstructure:"RevalidationTime"`
	// Indicates the offline charging is applicable to the PDU session or PCC rule.
	Offline bool `json:"offline,omitempty" yaml:"offline" bson:"offline" mapstructure:"Offline"`
	// Indicates the online charging is applicable to the PDU session or PCC rule.
	Online bool `json:"online,omitempty" yaml:"online" bson:"online" mapstructure:"Online"`
	// Defines the policy control request triggers subscribed by the PCF.
	PolicyCtrlReqTriggers []PolicyControlRequestTrigger `json:"policyCtrlReqTriggers,omitempty" yaml:"policyCtrlReqTriggers" bson:"policyCtrlReqTriggers" mapstructure:"PolicyCtrlReqTriggers"`
	// Defines the last list of rule control data requested by the PCF.
	LastReqRuleData  []RequestedRuleData `json:"lastReqRuleData,omitempty" yaml:"lastReqRuleData" bson:"lastReqRuleData" mapstructure:"LastReqRuleData"`
	LastReqUsageData *RequestedUsageData `json:"lastReqUsageData,omitempty" yaml:"lastReqUsageData" bson:"lastReqUsageData" mapstructure:"LastReqUsageData"`
	// Map of PRA information.
	PraInfos     map[string]*PresenceInfoRm `json:"praInfos,omitempty" yaml:"praInfos" bson:"praInfos" mapstructure:"PraInfos"`
	Ipv4Index    int32                      `json:"ipv4Index,omitempty" yaml:"ipv4Index" bson:"ipv4Index" mapstructure:"Ipv4Index"`
	Ipv6Index    int32                      `json:"ipv6Index,omitempty" yaml:"ipv6Index" bson:"ipv6Index" mapstructure:"Ipv6Index"`
	QosFlowUsage QosFlowUsage               `json:"qosFlowUsage,omitempty" yaml:"qosFlowUsage" bson:"qosFlowUsage" mapstructure:"QosFlowUsage"`
	SuppFeat     string                     `json:"suppFeat,omitempty" yaml:"suppFeat" bson:"suppFeat" mapstructure:"SuppFeat"`
}
  • AppSessions, Dictionary of application sessions
  • PcfUe, Corresponding UE Context
  • InfluenceDataToPccRule,
  • SubscriptionID, subscription ID
  1. NewUeAMPolicyData(), this function create a New UE AM Policy by parsing data from request and put it into the AMPolicyData map

  2. NewUeSmPolicyData(), this function create a New UE SM policy data by parsing smPolicyContextData object and SmPolicyData object. This function will return the recently created SM Policy Data

  3. RemovePccRule(), this function will remove PCC rule based on SmPolicyDecision passed in argument, PCC rule Id. This function will return nil if the PCC rule deletion succeeded.

  4. CheckRelatedAfEvents(), this function check if related AF events exists in smPolicy. The function will return True if it's find related event and return False if it doesn't

  5. ArrangeExistEventSubscription, this function will iterate the policy request triggers and determine whether the triggers is happened or not (using CheckRelatedAfEvents()) and set the changed flag to true, else, it will append a trigger to trigger list.

  6. IncreaseRemainGBR and decreaseRemainGBR, these two function will adjust the GBR based on the 5QI (Qos ID) value and rewrite the value to UeSmPolicy struct

  7. IncreaseRemainBitRate and DecreaseRemainBitRate, these two function provide the actual implementation and logic to adjust the bit rate.

  8. FindAMPolicy this function will return UeAmPolicy object if there is match in AccessType and PLMN id

  9. AllocUeSessionId this function will generate App Session ID and return the recently created session ID.

  10. SMPolicyFindByIpv4 and SMPolicyFindByIpv6 these functions will return UeSmPolicy if there is IP address match.

  11. AppSessionIdStore this struct contain:

  • AppSessionId, unique identifier
  • AppSessionContext
type AppSessionContext struct {
	AscReqData  *AppSessionContextReqData  `json:"ascReqData,omitempty" yaml:"ascReqData" bson:"ascReqData" mapstructure:"AscReqData"`
	AscRespData *AppSessionContextRespData `json:"ascRespData,omitempty" yaml:"ascRespData" bson:"ascRespData" mapstructure:"AscRespData"`
	EvsNotif    *EventsNotification        `json:"evsNotif,omitempty" yaml:"evsNotif" bson:"evsNotif" mapstructure:"EvsNotif"`
}

i. internal/sbi/server.go

j. internal/sbi/api_*.go

k. internal/sbi/consumer/consumer.go

l. internal/sbi/consumer/amf_service.go

m. internal/sbi/consumer/nrf_service.go

n. internal/sbi/consumer/udr_service.go

o. internal/sbi/processor/processor.go

p. internal/sbi/processor/ampolicy.go

q. internal/sbi/processor/bdtpolicy.go

r. internal/sbi/processor/notifier.go

s. internal/sbi/processor/oam.go

t. internal/sbi/processor/policyauthorization.go

u. internal/sbi/processor/smpolicy.go

v. internal/util

Code Modification Procedure and Standard

Copy the Import clause, function, var, struct, from a .go file to another .go file with similar name

example

src : pcf/internal/context/ue.go
dst : smf/internal/context

Edit the cmake file and run.sh

Reference

https://www.etsi.org/deliver/etsi_ts/129500_129599/129513/16.06.00_60/ts_129513v160600p.pdf