Free5GC源码研究(8) - PCF研究(上)

本文研究 Policy Control Function(PCF)主要实现的功能

前面研究过的AUSF、NRF、UDM、UDR、NSSF等,都是相对比较简单的NF。现在开始要着手研究一下更复杂的NF了。他们复杂即既是因为代码量大,也因为他们的逻辑与其他实体互相交织。比如PCF,就与AMF和SMF高度相关,也与UE和UPF紧密相连。因此,想要理解PCF的原理,就会不得不涉及其他NF、甚至用户设备的工作流程,而研究其他复杂NF时也会如此。因此,对后面各个复杂NF的研究会更加激进地略过多数细节,之关注整体架构和关键细节。

PCF的概念

Policy Control Function, 就是使用各种policy去control通信网络和用户设备行为的网络功能。那么PCF的Policy是怎样的?它们又如何control?如果我们打算找一个定义PCF的标准文档,我们会发现找不到这么个东西,倒是发现与PCF相关的内容散落在一堆文档里。比如

  • 描述5G系统架构的TS23.501(5.14)和5G系统过程的TS23.502(4.16)
  • 定义5G网络中策略控制和计费框架的TS23.503
  • 控制设备接入和移动管理(AMPolicy)29.512、控制设备与网络会话(SMPolicy)的TS29.512,控制背景数据传输(BDTPolicy)的TS29.554、控制用户设备(UEPOolicy)的TS 29.525、描述Policy对其他AF(Application Function)授权细节(PolicyAuthorization)的TS 29.514
  • 还有“策略与计费控制信号流及QoS参数映射”TS29.513。尽管这份文档的标题看上去有点奇怪,似乎是讲某个很具体的点,但是free5gc/pcf的代码结构就是照着这份文档的目录写的。

TS23.501中的这个表格给出了一个比较详细的PCF内部服务索引:
img

所谓Policy是什么,这些文档里都没有清晰明确的定义,不过给出了Policy and Charging Control decision的定义:

PCC decision: A PCF decision for policy and charging control provided to the SMF (consisting of PCC rules and PDU Session related attributes), a PCF decision for access and mobility related policy control provided to the AMF, a PCF decision for UE policy information provided to the UE or a PCF decision for service related policy (e.g. background data transfer policy) provided to the AF.

可以看出,PCF要控制AMF、SMF、UE、和其他AF的服务。其实PCF给我一种一箩筐,啥都可以往里面装的感觉。也正因如此,很难严格定义PCF中的Policy究竟是什么,因为控制不同的实体用到的具体信息和手段都不一样。现在我们只好笼统地理解为Policy是一组控制信息,比如规则和指引。PCF决定好Policy后会告知其他实体,这些实体按照Policy去执行应该做什么不该做什么,这便是PCF去control的方式。

想要更深入理解Policy到底是什么,还是得研究一下与Policy相关的数据类型。下面列举了控制接入与移动性的AmPolicyData,控制背景数据迁移服务的BdtPolicy,以及控制会话管理的SmPolicyData。这些Policy类型中,除了类型名类似以外,基本上找不到什么相似点。这也坐实了PCF是一箩筐的比喻。要想理解PCF的机制,还是得单独研究PCF对各个AMF、SMF、UE、和其他AF的服务是怎么工作的。


/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_am_policy_data.go */
// Contains the AM policy data for a given subscriber.
type AmPolicyData struct {
    // Subscription Categoties,也就是用户的订阅套餐,比如是预付prepaid,还是后付postpaid
	SubscCats []string `json:"subscCats,omitempty" bson:"subscCats"`
}

/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_am_policy_data.go */
// Represents an Individual BDT policy resource.
type BdtPolicy struct {
	BdtPolData *BdtPolicyData `json:"bdtPolData,omitempty" yaml:"bdtPolData" bson:"bdtPolData" mapstructure:"BdtPolData"`
	BdtReqData *BdtReqData    `json:"bdtReqData,omitempty" yaml:"bdtReqData" bson:"bdtReqData" mapstructure:"BdtReqData"`
}
/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_bdt_policy_data.go */
// Describes the authorization data of an Individual BDT policy resource.
type BdtPolicyData struct {
	// string identifying a BDT Reference ID as defined in subclause 5.3.3 of 3GPP TS 29.154.
	BdtRefId string `json:"bdtRefId" yaml:"bdtRefId" bson:"bdtRefId" mapstructure:"BdtRefId"`
	// Contains transfer policies.
	TransfPolicies []TransferPolicy `json:"transfPolicies" yaml:"transfPolicies" bson:"transfPolicies" mapstructure:"TransfPolicies"`
	// Contains an identity of the selected transfer policy.
	SelTransPolicyId int32  `json:"selTransPolicyId,omitempty" yaml:"selTransPolicyId" bson:"selTransPolicyId" mapstructure:"SelTransPolicyId"`
	SuppFeat         string `json:"suppFeat,omitempty" yaml:"suppFeat" bson:"suppFeat" mapstructure:"SuppFeat"`
}

/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_sm_policy_data.go */
// Contains the SM policy data for a given subscriber.
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"`
}
点击查看更多Policy相关数据类型

与BDT Policy相关的数据类型

/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_transfer_policy.go */
// Describes a transfer policy.
type TransferPolicy struct {
	MaxBitRateDl string `json:"maxBitRateDl,omitempty" yaml:"maxBitRateDl" bson:"maxBitRateDl" mapstructure:"MaxBitRateDl"`
	MaxBitRateUl string `json:"maxBitRateUl,omitempty" yaml:"maxBitRateUl" bson:"maxBitRateUl" mapstructure:"MaxBitRateUl"`
	// Indicates a rating group for the recommended time window.
	RatingGroup int32       `json:"ratingGroup" yaml:"ratingGroup" bson:"ratingGroup" mapstructure:"RatingGroup"`
	RecTimeInt  *TimeWindow `json:"recTimeInt" yaml:"recTimeInt" bson:"recTimeInt" mapstructure:"RecTimeInt"`
	// Contains an identity of a transfer policy.
	TransPolicyId int32 `json:"transPolicyId" yaml:"transPolicyId" bson:"transPolicyId" mapstructure:"TransPolicyId"`
}
/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_bdt_req_data.go */
// Contains service requirements for creation a new Individual BDT policy resource.
type BdtReqData struct {
	// Contains an identity of an application service provider.
	AspId      string           `json:"aspId" yaml:"aspId" bson:"aspId" mapstructure:"AspId"`
	DesTimeInt *TimeWindow      `json:"desTimeInt" yaml:"desTimeInt" bson:"desTimeInt" mapstructure:"DesTimeInt"`
	NwAreaInfo *NetworkAreaInfo `json:"nwAreaInfo,omitempty" yaml:"nwAreaInfo" bson:"nwAreaInfo" mapstructure:"NwAreaInfo"`
	// Indicates a number of UEs.
	NumOfUes int32           `json:"numOfUes" yaml:"numOfUes" bson:"numOfUes" mapstructure:"NumOfUes"`
	VolPerUe *UsageThreshold `json:"volPerUe" yaml:"volPerUe" bson:"volPerUe" mapstructure:"VolPerUe"`
	SuppFeat string          `json:"suppFeat,omitempty" yaml:"suppFeat" bson:"suppFeat" mapstructure:"SuppFeat"`
}

与SMF Policy相关的数据类型

/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_sm_policy_snssai_data.go */
// Contains the SM policy data for a given subscriber and S-NSSAI.
type SmPolicySnssaiData struct {
	Snssai          *Snssai                    `json:"snssai" bson:"snssai"`
	SmPolicyDnnData map[string]SmPolicyDnnData `json:"smPolicyDnnData,omitempty" bson:"smPolicyDnnData"`
}
/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_sm_policy_dnn_data.go */
// Contains the SM policy data for a given DNN (and S-NSSAI).
type SmPolicyDnnData struct {
	Dnn                 string                            `json:"dnn" bson:"dnn"`
	AllowedServices     []string                          `json:"allowedServices,omitempty" bson:"allowedServices"`
	SubscCats           []string                          `json:"subscCats,omitempty" bson:"subscCats"`
	GbrUl               string                            `json:"gbrUl,omitempty" bson:"gbrUl"`
	GbrDl               string                            `json:"gbrDl,omitempty" bson:"gbrDl"`
	AdcSupport          bool                              `json:"adcSupport,omitempty" bson:"adcSupport"`
	SubscSpendingLimits bool                              `json:"subscSpendingLimits,omitempty" bson:"subscSpendingLimits"`
	Ipv4Index           int32                             `json:"ipv4Index,omitempty" bson:"ipv4Index"`
	Ipv6Index           int32                             `json:"ipv6Index,omitempty" bson:"ipv6Index"`
	Offline             bool                              `json:"offline,omitempty" bson:"offline"`
	Online              bool                              `json:"online,omitempty" bson:"online"`
	ChfInfo             *ChargingInformation              `json:"chfInfo,omitempty" bson:"chfInfo"`
	RefUmDataLimitIds   map[string]LimitIdToMonitoringKey `json:"refUmDataLimitIds,omitempty" bson:"refUmDataLimitIds"`
	MpsPriority         bool                              `json:"mpsPriority,omitempty" bson:"mpsPriority"`
	ImsSignallingPrio   bool                              `json:"imsSignallingPrio,omitempty" bson:"imsSignallingPrio"`
	MpsPriorityLevel    int32                             `json:"mpsPriorityLevel,omitempty" bson:"mpsPriorityLevel"`
}
/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_usage_mon_data_limit.go */
// Contains usage monitoring control data for a subscriber.
type UsageMonDataLimit struct {
	LimitId     string                       `json:"limitId" bson:"limitId"`
	Scopes      map[string]UsageMonDataScope `json:"scopes,omitempty" bson:"scopes"`
	UmLevel     UsageMonLevel                `json:"umLevel,omitempty" bson:"umLevel"`
	StartDate   *time.Time                   `json:"startDate,omitempty" bson:"startDate"`
	EndDate     *time.Time                   `json:"endDate,omitempty" bson:"endDate"`
	UsageLimit  *UsageThreshold              `json:"usageLimit,omitempty" bson:"usageLimit"`
	ResetPeriod *time.Time                   `json:"resetPeriod,omitempty" bson:"resetPeriod"`
}
/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_usage_mon_data.go */
// Contains remain allowed usage data for a subscriber.
type UsageMonData struct {
	LimitId      string                       `json:"limitId" bson:"limitId"`
	Scopes       map[string]UsageMonDataScope `json:"scopes,omitempty" bson:"scopes"`
	UmLevel      UsageMonLevel                `json:"umLevel,omitempty" bson:"umLevel"`
	AllowedUsage *UsageThreshold              `json:"allowedUsage,omitempty" bson:"allowedUsage"`
	ResetTime    *TimePeriod                  `json:"resetTime,omitempty" bson:"resetTime"`
}

/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_sm_policy_decision.go */
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"`
}

与UE Policy相关的数据类型

/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_ue_policy_set.go */
// Contains the UE policy data for a given subscriber.
type UePolicySet struct {
	SubscCats        []string                   `json:"subscCats,omitempty" bson:"subscCats"`
	UePolicySections map[string]UePolicySection `json:"uePolicySections,omitempty" bson:"uePolicySections"`
	Upsis            []string                   `json:"upsis,omitempty" bson:"upsis"`
}
/* https://github.com/free5gc/openapi/blob/v1.0.8/models/model_ue_policy_section.go */
// Contains the UE policy section.
type UePolicySection struct {
	UePolicySectionInfo string `json:"uePolicySectionInfo" bson:"uePolicySectionInfo"`
	Upsi                string `json:"upsi" bson:"upsi"`
}

PCF的实现

如果我们查看一下pcf/internal/sbi/processor/中所有代码文件的行数

/home/ricky/free5gc-3.4.3/NFs/pcf/internal/sbi/processor$ wc -l * | sort 
    23 processor.go
    67 oam.go
   106 notifier.go
   277 bdtpolicy.go
   428 ampolicy.go
  1202 smpolicy.go
  1770 policyauthorization.go
  3873 total

就会发现smpolicy.gopolicyauthorization.go两个文件的代码量远高于其它,而这两个文件都与网络会话,具体来说是PDU Session以及QoS Flow强相关。因此,这两者的内容值得单独研究。除此以外,bdtpolicy.go是对BDT服务的实现,ampolicy.go是对AMF的支持。notifier.go是消息通知机制,里面实现的都是PCF让其它NF在事件发生时时通知它的用的回调函数。oam.go这份代码有点奇怪,从名字上看是支持后台管理的(Operation, Administration, Management),但里面只有一个HandleOAMGetAmPolicyRequest(c *gin.Context, supi string)函数,作用是获取一个用户的AM Policy。也许以后会扩展更多功能?这里面似乎缺了一个管UE的uepolicy.go。查阅internal/sbi/api_uepolicy.go,会看到所有的处理函数都没有实现,也就是PCF中与用户设备有关的功能尚未实现。

// https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/api_uepolicy.go#L43C1-L61C2
func (s *Server) PoliciesPolAssoIdDelete(c *gin.Context) {
	c.JSON(http.StatusNotImplemented, nil)
}
func (s *Server) PoliciesPolAssoIdGet(c *gin.Context) {
	c.JSON(http.StatusNotImplemented, nil)
}
func (s *Server) PoliciesPolAssoIdUpdatePost(c *gin.Context) {
	c.JSON(http.StatusNotImplemented, nil)
}
func (s *Server) PoliciesPost(c *gin.Context) {
	c.JSON(http.StatusNotImplemented, nil)
}

PCF的Context

PCF在数据管理方面既会用到内存Context,又会用到本地数据库的MongoDB,还会用到统一数据仓库UDR,不过更多还是用的Context。由于PCF管理者各种实体的Policy,所以其Context的设计也比较复杂。下面是PCF的主要Context:

// https://github.com/free5gc/pcf/blob/v1.2.5/internal/context/context.go#L23
type PCFContext struct {
	// 省略正常的NF属性......

    DefaultBdtRefId 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
}

我们知道PCF能控制AMF、SMF、UE(尚未实现),还有BDT服务。在上面的Context中很明显可以看出,BdtPolicyPool存储着各种和BDT服务相关的Ploicy,而AppSessionPool则保存各种AF Application Context:

// https://github.com/free5gc/pcf/blob/v1.2.5/internal/context/context.go#L64
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
}

当一些提供应用的网络功能(AF,不在5G标准中定义的,第三方开发的NF)需要像核心网申请资源时,会向PCF申请并创建一些Policy来管理AF绑定的资源。这个AppSession就是PCF用来管理和追踪应用程序对网络资源请求的机制,确保应用程序获得所需的网络服务质量,同时也便于网络进行资源的合理分配和管理。AF申请的资源里包括了网络与设备的会话,所以AppSession会绑定一个smPolicyData

与用户设备有关的Policy会存储在UePoolUeContext中,当然也包括与用户设备关联的AMPolicyDataSmPolicyData、以及AppSession

// https://github.com/free5gc/pcf/blob/v1.2.5/internal/context/ue.go
// 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 {
	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
}

BDT Policy

Background data transfer: feature that enables a 3rd party service provider to keep their costs lower by favouring time windows for data transfer to specific UEs in a geographical area during non-busy hours that are less costly and able to handle larger bitrates

BDT, 全程Background Data Transfer,是一种智能化的数据传输服务,能让数据传输在网络中相对不那么繁忙的时段进行,以最优化网络资源利用,同时也最小化网费,同时也能协调网络中各个设备的数据传输,避免出现网络拥堵。这对物联网场景就特别有用,比如车联网里的各种智能汽车之间,重要的安全和导航相关数据能够及时传递,而级别较低的高清路况图片等数据就可以延后传输。PCF对BDT服务的支持,就包括向第三方NF提供咨询服务,告诉他们什么情况下用什么Policy合适,以及必要时发出警告提示。不过目前free5gc/pcf只实现了最基本的功能,亦即对policy的管理,没有发出BDT告警的功能。

HandleCreateBDTPolicyContextRequest这个函数的名字有点误导性,乍看之下好像是在PCF创建一个BDT Policy,但其实它的功能是告诉其他NF应当使用怎样的Policy,重点是怎样的Tansfer policy,也就是最大的上传、下载速率,推荐的进行数据传输的时间窗口,以及这次传输会怎么计费(e.g., RatingGroup 1: 普通上网流量、2: 视频流量、3: 游戏流量)。

// https://github.com/free5gc/openapi/blob/v1.0.8/models/model_transfer_policy.go
type TransferPolicy struct {
	MaxBitRateDl string `json:"maxBitRateDl,omitempty" yaml:"maxBitRateDl" bson:"maxBitRateDl" mapstructure:"MaxBitRateDl"`
	MaxBitRateUl string `json:"maxBitRateUl,omitempty" yaml:"maxBitRateUl" bson:"maxBitRateUl" mapstructure:"MaxBitRateUl"`
	// Indicates a rating group for the recommended time window.
	RatingGroup int32       `json:"ratingGroup" yaml:"ratingGroup" bson:"ratingGroup" mapstructure:"RatingGroup"`
	RecTimeInt  *TimeWindow `json:"recTimeInt" yaml:"recTimeInt" bson:"recTimeInt" mapstructure:"RecTimeInt"`
	// Contains an identity of a transfer policy.
	TransPolicyId int32 `json:"transPolicyId" yaml:"transPolicyId" bson:"transPolicyId" mapstructure:"TransPolicyId"`
}

当一些第三方应用想要使用BDT服务时,它们会把这次传输的各种参数,如涉及的设备数量NumOfUes,每个设备用到的数据量VolPerUe,这次传输偏好的时间窗口desTimeInt、这次传输的覆盖的网络范围NwAreaInfo等,打包成一个BdtReqData:

// 
// Contains service requirements for creation a new Individual BDT policy resource.
type BdtReqData struct {
	// Contains an identity of an application service provider.
	AspId      string           `json:"aspId" yaml:"aspId" bson:"aspId" mapstructure:"AspId"`
	DesTimeInt *TimeWindow      `json:"desTimeInt" yaml:"desTimeInt" bson:"desTimeInt" mapstructure:"DesTimeInt"`
	NwAreaInfo *NetworkAreaInfo `json:"nwAreaInfo,omitempty" yaml:"nwAreaInfo" bson:"nwAreaInfo" mapstructure:"NwAreaInfo"`
	// Indicates a number of UEs.
	NumOfUes int32           `json:"numOfUes" yaml:"numOfUes" bson:"numOfUes" mapstructure:"NumOfUes"`
	VolPerUe *UsageThreshold `json:"volPerUe" yaml:"volPerUe" bson:"volPerUe" mapstructure:"VolPerUe"`
	SuppFeat string          `json:"suppFeat,omitempty" yaml:"suppFeat" bson:"suppFeat" mapstructure:"SuppFeat"`
}

然后调用这个函数HandleCreateBDTPolicyContextRequest。PCF会先去UDR查一下这个第三方应用AspId以前是不是来问过,有的话把之前的policy拿过来改一改时间窗口就行;没有的话就要为这个应用新建一个默认policy返回给来咨询的第三方应用。这个新创建的policy也要保存会Context和UDR中以备后用。

func (p *Processor) HandleCreateBDTPolicyContextRequest(c *gin.Context, requestMsg models.BdtReqData) {
	client := util.GetNudrClient(p.getDefaultUdrUri(p.Context()))
	bdtDatas, httpResponse, err := client.DefaultApi.PolicyDataBdtDataGet(ctx)
    response := &models.BdtPolicy{}
	response.BdtReqData = deepcopy.Copy(requestMsg).(*models.BdtReqData)
	var bdtData *models.BdtData
	var bdtPolicyData models.BdtPolicyData
	for _, data := range bdtDatas {
		// If the Application Service Provider has existed, use its background data policy
		if requestMsg.AspId == data.AspId {
			bdtData = &data
			break
		}
	}
	// Only support one bdt policy, TODO: more policy for decision
	if bdtData != nil {
		// found
		// modify policy according to new request
		bdtData.TransPolicy.RecTimeInt = requestMsg.DesTimeInt
	} else {
		// use default bdt policy, TODO: decide bdt transfer data policy
		bdtData = &models.BdtData{
			AspId:       requestMsg.AspId,
			BdtRefId:    uuid.New().String(),
			TransPolicy: getDefaultTransferPolicy(1, *requestMsg.DesTimeInt),
		}
	}
	if requestMsg.NwAreaInfo != nil {
		bdtData.NwAreaInfo = *requestMsg.NwAreaInfo
	}
	bdtPolicyData.SelTransPolicyId = bdtData.TransPolicy.TransPolicyId
	// no support feature in subclause 5.8 of TS29554
	bdtPolicyData.BdtRefId = bdtData.BdtRefId
	bdtPolicyData.TransfPolicies = append(bdtPolicyData.TransfPolicies, bdtData.TransPolicy)
	response.BdtPolData = &bdtPolicyData
	bdtPolicyID, err := pcfSelf.AllocBdtPolicyID()
	pcfSelf.BdtPolicyPool.Store(bdtPolicyID, response)
	// Update UDR BDT Data(PUT)
	param := Nudr_DataRepository.PolicyDataBdtDataBdtReferenceIdPutParamOpts{
		BdtData: optional.NewInterface(*bdtData),
	}
	client.DefaultApi.PolicyDataBdtDataBdtReferenceIdPut(ctx, bdtPolicyData.BdtRefId, &param)
	c.JSON(http.StatusCreated, response)
}

在以后知道bdtPolicyID的话,就可以凭此到PCF的Context中直接拿到相应的BdtPolicy

// https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/processor/bdtpolicy.go#L20
func (p *Processor) HandleGetBDTPolicyContextRequest(c *gin.Context, bdtPolicyID string) {
	value, ok := p.Context().BdtPolicyPool.Load(bdtPolicyID); ok {
    bdtPolicy := value.(*models.BdtPolicy)
    c.JSON(http.StatusOK, bdtPolicy)
}

上述的HandleCreateBDTPolicyContextRequest只会新建一个默认policy,如果觉得默认设置不够很好的话),就可以对其update。

// https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/processor/bdtpolicy.go#L44
func (p *Processor) HandleUpdateBDTPolicyContextProcedure(c *gin.Context, bdtPolicyID string,
    bdtPolicyDataPatch models.BdtPolicyDataPatch,
) {
	var bdtPolicy *models.BdtPolicy
	value, ok := p.Context().BdtPolicyPool.Load(bdtPolicyID)
	bdtPolicy = value.(*models.BdtPolicy)
	for _, policy := range bdtPolicy.BdtPolData.TransfPolicies {
		if policy.TransPolicyId == bdtPolicyDataPatch.SelTransPolicyId {
			polData := bdtPolicy.BdtPolData
			polReq := bdtPolicy.BdtReqData
            // 修改selected TansPolicy
			polData.SelTransPolicyId = bdtPolicyDataPatch.SelTransPolicyId
			bdtData := models.BdtData{
				AspId:       polReq.AspId,
				TransPolicy: policy,
				BdtRefId:    polData.BdtRefId,
			}
			if polReq.NwAreaInfo != nil {
				bdtData.NwAreaInfo = *polReq.NwAreaInfo
			}
			param := Nudr_DataRepository.PolicyDataBdtDataBdtReferenceIdPutParamOpts{
				BdtData: optional.NewInterface(bdtData),
			}
			client := util.GetNudrClient(p.getDefaultUdrUri(p.Context()))
			ctx, pd, err := p.Context().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR)
			rsp, err := client.DefaultApi.PolicyDataBdtDataBdtReferenceIdPut(ctx, bdtData.BdtRefId, &param)
			c.JSON(http.StatusOK, bdtPolicy)
			return
		}
	}
}

然而这更新仅限调整其selected Tansfer Policy,无法修改其他,比如一个TransferPolicyMaxBitRateDl。我快速查看了一下其他NF的代码,也没发现哪里能够直接修改TransferPolicy.MaxBitRateDl。也许这需要运营商在后台操作来修改吧。

TransferPolicy, BdtData, BdtPolicyData, BdtPolicy

在一开始研究BDT的这些数据类型时我越看越糊涂。有必要辨析一下它们的关系。

// Describes a transfer policy.
type TransferPolicy struct {
	MaxBitRateDl string `json:"maxBitRateDl,omitempty" yaml:"maxBitRateDl" bson:"maxBitRateDl" mapstructure:"MaxBitRateDl"`
	MaxBitRateUl string `json:"maxBitRateUl,omitempty" yaml:"maxBitRateUl" bson:"maxBitRateUl" mapstructure:"MaxBitRateUl"`
	// Indicates a rating group for the recommended time window.
	RatingGroup int32       `json:"ratingGroup" yaml:"ratingGroup" bson:"ratingGroup" mapstructure:"RatingGroup"`
	RecTimeInt  *TimeWindow `json:"recTimeInt" yaml:"recTimeInt" bson:"recTimeInt" mapstructure:"RecTimeInt"`
	// Contains an identity of a transfer policy.
	TransPolicyId int32 `json:"transPolicyId" yaml:"transPolicyId" bson:"transPolicyId" mapstructure:"TransPolicyId"`
}

// Contains the background data transfer data.
type BdtData struct {
	AspId       string          `json:"aspId,omitempty" bson:"aspId"`
	TransPolicy TransferPolicy  `json:"transPolicy" bson:"transPolicy"`
	BdtRefId    string          `json:"bdtRefId,omitempty" bson:"bdtRefId"`
	NwAreaInfo  NetworkAreaInfo `json:"nwAreaInfo,omitempty" bson:"nwAreaInfo"`
}

// Describes the authorization data of an Individual BDT policy resource.
type BdtPolicyData struct {
	// string identifying a BDT Reference ID as defined in subclause 5.3.3 of 3GPP TS 29.154.
	BdtRefId string `json:"bdtRefId" yaml:"bdtRefId" bson:"bdtRefId" mapstructure:"BdtRefId"`
	// Contains transfer policies.
	TransfPolicies []TransferPolicy `json:"transfPolicies" yaml:"transfPolicies" bson:"transfPolicies" mapstructure:"TransfPolicies"`
	// Contains an identity of the selected transfer policy.
	SelTransPolicyId int32  `json:"selTransPolicyId,omitempty" yaml:"selTransPolicyId" bson:"selTransPolicyId" mapstructure:"SelTransPolicyId"`
	SuppFeat         string `json:"suppFeat,omitempty" yaml:"suppFeat" bson:"suppFeat" mapstructure:"SuppFeat"`
}

// Represents an Individual BDT policy resource.
type BdtPolicy struct {
	BdtPolData *BdtPolicyData `json:"bdtPolData,omitempty" yaml:"bdtPolData" bson:"bdtPolData" mapstructure:"BdtPolData"`
	BdtReqData *BdtReqData    `json:"bdtReqData,omitempty" yaml:"bdtReqData" bson:"bdtReqData" mapstructure:"BdtReqData"`
}

首先是TransferPolicy,他是BDT最基本的单位。然后是BdtPolicy,它是Create和Update函数的返回给调用者的类型。关键是BdtDataBdtPolicyData,前者对应一个BdtRefId一个TansferPolicy;后者对应一个BdtRefId多个TansferPolicy。他们俩有什么关系和区别?

一个明显的区别是BdtData保存在UDR中,而BdtPolicyData保存在PCFContext。进而,一个解释是BdtPolicyData作为本地运行时数据,可以包含多个可选的TransferPolicy,第三方应用通过SelTransPolicyId指示当前选中使用哪个策略。本地更新完毕后,将选中的策略同步到UDR的BdtData中。这是一个典型的通过不同层次的数据结构来管理和维护本地与远程中心化信息的设计。

然而,这个解释说不通的地方在于BdtPolicyData需要包含多个可选的TransferPolicy,但整份bdtpolicy.go代码中只有HandleCreateBDTPolicyContextRequest函数在新建BdtPolicyData时顺带把一个默认TransferPolicy添加到了一个空的切片中append(bdtPolicyData.TransfPolicies, bdtData.TransPolicy),其他地方再也没有对这个切片进行改动。也就是说bdtPolicyData.TransfPolicies永远只有一个可选项!我并没有在PCF模块的其他地方找到能为这个切片新增policy的代码,更没有在free5gc代码库其他地方找到(理论上也不可能,因为其他模块不能调用PCF的Context)。虽然这又说不过去的地方,但我目前没找到更合理的解释,只能认为BDT的功能还不完善,以后的版本会改进吧。


AM Policy

PCF的第二组重要功能时当用户设备接入网络时,也就是我们每次开机或者重新打开数据开关连接移动网络时,为AMF提供policy,告知AMF应该怎样管理设备的接入和移动性。确切来说,PostPoliciesProcedure函数在PCF的context中为(新的)AMF传来的policy与用户设备UE构建一个联系PolicyAssociation。当核心网内负责管理某个设备的AMF发生变化时(旧的AMF替代新的AMF),这个函数也会被调用。

简单来说,PCF首先创建或获取用户设备的UeContext,然后从UDR获取该设备的AmPolicyData(其实就只是用户的订阅类型),据此设置policy特性和参数(实际上没啥可做),为用户设备与AMF传过来的数据创建一个关联类型UeAMPolicyData,订阅一下这个AMF的状态,最后返回创建好的PolicyAssociation

PolicyAssociation, PolicyAssociationRequest, UeAMPolicyData
// https://github.com/free5gc/openapi/blob/v1.0.8/models/model_policy_association.go
type PolicyAssociation struct {
	Request *PolicyAssociationRequest `json:"request,omitempty" yaml:"request" bson:"request" mapstructure:"Request"`
	// Request Triggers that the PCF subscribes. Only values \"LOC_CH\" and \"PRA_CH\" are permitted.
	Triggers    []RequestTrigger        `json:"triggers,omitempty" yaml:"triggers" bson:"triggers" mapstructure:"Triggers"`
	ServAreaRes *ServiceAreaRestriction `json:"servAreaRes,omitempty" yaml:"servAreaRes" bson:"servAreaRes" mapstructure:"ServAreaRes"`
	Rfsp        int32                   `json:"rfsp,omitempty" yaml:"rfsp" bson:"rfsp" mapstructure:"Rfsp"`
	Pras        map[string]PresenceInfo `json:"pras,omitempty" yaml:"pras" bson:"pras" mapstructure:"Pras"`
	SuppFeat    string                  `json:"suppFeat" yaml:"suppFeat" bson:"suppFeat" mapstructure:"SuppFeat"`
}

// https://github.com/free5gc/openapi/blob/v1.0.8/models/model_request_trigger.go
type RequestTrigger string
const (
	RequestTrigger_LOC_CH       RequestTrigger = "LOC_CH"  // 位置变化
	RequestTrigger_PRA_CH       RequestTrigger = "PRA_CH"  // 存在状态变化
	RequestTrigger_SERV_AREA_CH RequestTrigger = "SERV_AREA_CH"  // 服务范围变化
	RequestTrigger_RFSP_CH      RequestTrigger = "RFSP_CH"      // RFSP 变化
)

// https://github.com/free5gc/openapi/blob/v1.0.8/models/model_policy_association_request.go
type PolicyAssociationRequest struct {
	NotificationUri string `json:"notificationUri" yaml:"notificationUri" bson:"notificationUri" mapstructure:"NotificationUri"`
	// Alternate or backup IPv4 Address(es) where to send Notifications.
	AltNotifIpv4Addrs []string `json:"altNotifIpv4Addrs,omitempty" yaml:"altNotifIpv4Addrs" bson:"altNotifIpv4Addrs" mapstructure:"AltNotifIpv4Addrs"`
	// Alternate or backup IPv6 Address(es) where to send Notifications.
	AltNotifIpv6Addrs []string                `json:"altNotifIpv6Addrs,omitempty" yaml:"altNotifIpv6Addrs" bson:"altNotifIpv6Addrs" mapstructure:"AltNotifIpv6Addrs"`
	Supi              string                  `json:"supi" yaml:"supi" bson:"supi" mapstructure:"Supi"`
	Gpsi              string                  `json:"gpsi,omitempty" yaml:"gpsi" bson:"gpsi" mapstructure:"Gpsi"`
	AccessType        AccessType              `json:"accessType,omitempty" yaml:"accessType" bson:"accessType" mapstructure:"AccessType"`
	Pei               string                  `json:"pei,omitempty" yaml:"pei" bson:"pei" mapstructure:"Pei"`
	UserLoc           *UserLocation           `json:"userLoc,omitempty" yaml:"userLoc" bson:"userLoc" mapstructure:"UserLoc"`
	TimeZone          string                  `json:"timeZone,omitempty" yaml:"timeZone" bson:"timeZone" mapstructure:"TimeZone"`
	ServingPlmn       *NetworkId              `json:"servingPlmn,omitempty" yaml:"servingPlmn" bson:"servingPlmn" mapstructure:"ServingPlmn"`
	RatType           RatType                 `json:"ratType,omitempty" yaml:"ratType" bson:"ratType" mapstructure:"RatType"`
	GroupIds          []string                `json:"groupIds,omitempty" yaml:"groupIds" bson:"groupIds" mapstructure:"GroupIds"`
	ServAreaRes       *ServiceAreaRestriction `json:"servAreaRes,omitempty" yaml:"servAreaRes" bson:"servAreaRes" mapstructure:"ServAreaRes"`
	Rfsp              int32                   `json:"rfsp,omitempty" yaml:"rfsp" bson:"rfsp" mapstructure:"Rfsp"`
	Guami             *Guami                  `json:"guami,omitempty" yaml:"guami" bson:"guami" mapstructure:"Guami"`
	// If the NF service consumer is an AMF, it should provide the name of a service produced by the AMF that makes use of information received within the Npcf_AMPolicyControl_UpdateNotify service operation.
	ServiveName string     `json:"serviveName,omitempty" yaml:"serviveName" bson:"serviveName" mapstructure:"ServiveName"`
	TraceReq    *TraceData `json:"traceReq,omitempty" yaml:"traceReq" bson:"traceReq" mapstructure:"TraceReq"`
	SuppFeat    string     `json:"suppFeat" yaml:"suppFeat" bson:"suppFeat" mapstructure:"SuppFeat"`
}

// https://github.com/free5gc/pcf/blob/v1.2.5/internal/context/ue.go#L44C1-L69C2
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
}
// https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/processor/ampolicy.go#L190
func (p *Processor) PostPoliciesProcedure(polAssoId string,
	policyAssociationRequest models.PolicyAssociationRequest,
) (*models.PolicyAssociation, string, *models.ProblemDetails) {
    var response models.PolicyAssociation
	pcfSelf := p.Context()
	var ue *pcf_context.UeContext
	if val, ok := pcfSelf.UePool.Load(policyAssociationRequest.Supi); ok {
		ue = val.(*pcf_context.UeContext)
	} else {
		ue, err := pcfSelf.NewPCFUe(policyAssociationRequest.Supi)
	}
    response.Request = deepcopy.Copy(&policyAssociationRequest).(*models.PolicyAssociationRequest)
	assolId := fmt.Sprintf("%s-%d", ue.Supi, ue.PolAssociationIDGenerator)
	amPolicy := ue.AMPolicyData[assolId]

	ctx, pd, err := p.Context().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR)
	if amPolicy == nil || amPolicy.AmPolicyData == nil {
		client := util.GetNudrClient(udrUri)
		amData, response, err := client.DefaultApi.PolicyDataUesUeIdAmDataGet(ctx, ue.Supi)
        if amPolicy == nil {
			amPolicy = ue.NewUeAMPolicyData(assolId, policyAssociationRequest)
		}
		amPolicy.AmPolicyData = &amData
	}
    // TODO: according to PCF Policy to determine ServAreaRes, Rfsp, SuppFeat
	// amPolicy.ServAreaRes =
	// amPolicy.Rfsp =
    
    ue.PolAssociationIDGenerator++ // increment the counter

	// if consumer is AMF then subscribe this AMF Status
	if policyAssociationRequest.Guami != nil {
		// if policyAssociationRequest.Guami has been subscribed, then no need to subscribe again
		needSubscribe := true
		pcfSelf.AMFStatusSubsData.Range(func(key, value interface{}) bool {
			data := value.(pcf_context.AMFStatusSubscriptionData)
			for _, guami := range data.GuamiList {
				if reflect.DeepEqual(guami, *policyAssociationRequest.Guami) {
					needSubscribe = false
					break
				}
			}
			// if no need to subscribe => stop iteration
			return needSubscribe
		})
		if needSubscribe {
        // 但目前没什么卵用,因为即便AMF的状态发生变化,PCF什么也不会做——相关处理函数尚未实现
        // https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/processor/notifier.go#L15
			amfUri := p.Consumer().SendNFInstancesAMF(pcfSelf.NrfUri, *policyAssociationRequest.Guami, models.ServiceName_NAMF_COMM)
            if amfUri != "" {
				p.Consumer().AmfStatusChangeSubscribe(amfUri, []models.Guami{*policyAssociationRequest.Guami})
				amPolicy.Guami = policyAssociationRequest.Guami
			}
		} 
	}
	return &response, locationHeader, nil
}

说是PCF告诉AMF该怎么做,但细看下来PostPoliciesProcedure函数只是把AMF传过来的PolicyAssociationRequest与自己context中的UE关联起来:ue.NewUeAMPolicyData(assolId, policyAssociationRequest),最后把PolicyAssociationRequest打包成PolicyAssociation就返回了。唯一有实际意义的事情时从UDR把用户设备得到Subscription Categories添加入policy中。以后的版本应该会做更多policy决策。

ue.NewUeAMPolicyData
// https://github.com/free5gc/pcf/blob/v1.2.5/internal/context/ue.go#L106
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]
}

相比其Post的逻辑,Get和Delete的逻辑异常简单。

// https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/processor/ampolicy.go#L18
func (p *Processor) HandleDeletePoliciesPolAssoId(c *gin.Context, polAssoId string) {
	ue := p.Context().PCFUeFindByPolicyId(polAssoId)
	delete(ue.AMPolicyData, polAssoId)
	c.JSON(http.StatusNoContent, nil)
}

// // https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/processor/ampolicy.go#L35
func (p *Processor) HandleGetPoliciesPolAssoId(c *gin.Context, polAssoId string) {
	ue := p.Context().PCFUeFindByPolicyId(polAssoId)
	amPolicyData := ue.AMPolicyData[polAssoId]  // 注意,这个是`UeAMPolicyData`类型!!!
	rsp := models.PolicyAssociation{
		SuppFeat: amPolicyData.SuppFeat,
	}
	// check other fields, set default values to rsp ......
	c.JSON(http.StatusOK, rsp)
}

更新的逻辑是综合了查找和新增的逻辑,在此略过。

ampolicy.go剩下的函数是在AM Policy发生(不是由AMF发起的)变更时通知AMF。下面时简化版的代码,逻辑相对简单。和之前NRF的通知机制类似,我没发现这两个通知函数在哪里被调用了。应该在后续版本会完善这个变更机制吧。

// https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/processor/ampolicy.go#L311
// Send AM Policy Update to AMF if policy has changed
func (p *Processor) SendAMPolicyUpdateNotification(ue *pcf_context.UeContext,
	PolId string, request models.PolicyUpdate,
) {
    amPolicyData := ue.AMPolicyData[PolId]
	ctx, _, err := p.Context().GetTokenCtx(models.ServiceName_NPCF_AM_POLICY_CONTROL, models.NfType_PCF)
	client := util.GetNpcfAMPolicyCallbackClient()
	uri := amPolicyData.NotificationUri
	for uri != "" {
		rsp, err := client.DefaultCallbackApi.PolicyUpdateNotification(ctx, uri, request)
	}
}

// Send AM Policy Update to AMF if policy has been terminated
func (p *Processor) SendAMPolicyTerminationRequestNotification(ue *pcf_context.UeContext,
	PolId string, request models.TerminationNotification,
) {
	amPolicyData := ue.AMPolicyData[PolId]
	client := util.GetNpcfAMPolicyCallbackClient()
	uri := amPolicyData.NotificationUri
	ctx, _, err := p.Context().GetTokenCtx(models.ServiceName_NPCF_AM_POLICY_CONTROL, models.NfType_PCF)
	for uri != "" {
		rsp, err := client.DefaultCallbackApi.PolicyAssocitionTerminationRequestNotification(ctx, uri, request)
	}
}

由于smpolicy.gopolicyauthorization.go两个文件的代码量远高于其它,而这两个文件都与SMF强相关。因此,这两者内容值得单独研究。

posted @ 2024-11-05 18:00  zrq96  阅读(27)  评论(0编辑  收藏  举报