前文再续书接上一回,继续研究Free5GC中所实现的PCF的另外两组服务:SMPolicy和PolicyAuthorization
SMPolicy
PCF中与SMF的交互,对session的控制有着很重的的分量,甚至连TS23.503中对与Policy Control的定义都是指PCF指示SMF去控制QoS流的过程。
Policy control: The process whereby the PCF indicates to the SMF how to control the QoS Flow. Policy control includes QoS control and/or gating control.
因为PCF的SMPolicy深度涉及了SMF的管理,因此想要研究SMPolicy就得先了解SMF相关的一些重要概念
- QoS Flow 首先是上面提及的QoS流,全称Quality of Service Flow,是一种服务质量得到保证的传输数据流,这里的服务质量包括但不咸鱼传输速率和稳定性。每个QoS流都会由自己的ID(QFI),而一个QoS流的本质就是所有包含同一个QFI的数据包,它们在传输的时候会根据QFI得到不同的对待。QoS Flow的创建、维护、删除等由SMF负责。Free5GC团队写了一篇博客介绍它们对于QoS的理解。
- PDU Session 全称Packet Data Unit Session,是用户设备与数据网络之间的一种逻辑连接,用于承载QoS流。一个PDU Session可以承载多个QoS流,且会有一个由SMF创建的默认QoS流。
- PCC rule 全称Policy and Charging Control rule,标准文档中的原文是"A set of information enabling the detection of a service data flow and providing parameters for policy control and/or charging control and/or other control or support information"。简单来说就是用来对数据流进行分类处理和收费的判断规则。
- Policy Control Request trigger 其实就是SMF应该主动发起与PCF交互的条件
了解了这些前置概念,大概就能读懂下面这张关于NBNpcf_SMPolicyControl
的表格了
虽然标准文档里没有说明要提供GET服务,但Free5GC团队还是贴心的实现了一个根据smPolicyId
找到相应的UeSmPolicyData的函数:
// https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/processor/smpolicy.go#L546
func (p *Processor) HandleGetSmPolicyContextRequest(c *gin.Context, smPolicyId string) {ue := p.Context().PCFUeFindByPolicyId(smPolicyId)smPolicyData := ue.SmPolicyData[smPolicyId] // ue.SmPolicyData里都是UeSmPolicyData类型response := &models.SmPolicyControl{Policy: smPolicyData.PolicyDecision,Context: smPolicyData.PolicyContext,}c.JSON(http.StatusOK, response)
}
删除一个smPolicy的操作不仅要删除UDR喝context里的smPolicy数据,还需要删除掉与之关联的appSession
和ratingGroup
。这个appSession
在前文说过,是“PCF用来管理和追踪第三方应用程序对网络资源请求的机制”,所以删除一个用户设备的smPolicy,就会连带删除掉相应的第三方应用提供的服务;而ratingGroup
则是用来对smPolicy所对应的QoS流计费的机制。
// https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/processor/smpolicy.go#L487
func (p *Processor) HandleDeleteSmPolicyContextRequest(c *gin.Context, smPolicyId string) {ue := p.Context().PCFUeFindByPolicyId(smPolicyId)pcfSelf := p.Context()smPolicy := ue.SmPolicyData[smPolicyId]// Unsubscrice UDRp.Consumer().RemoveInfluenceDataSubscription(ue, smPolicy.SubscriptionID)delete(ue.SmPolicyData, smPolicyId)// Release related App SessionterminationInfo := models.TerminationInfo{TermCause: models.TerminationCause_PDU_SESSION_TERMINATION,}for appSessionID := range smPolicy.AppSessions {if val, exist := pcfSelf.AppSessionPool.Load(appSessionID); exist {appSession := val.(*pcf_context.AppSessionData)p.SendAppSessionTermination(appSession, terminationInfo)pcfSelf.AppSessionPool.Delete(appSessionID)}}for _, ratingGroup := range ue.RatingGroupData[smPolicyId] {pcfSelf.RatingGroupIdGenerator.FreeID(int64(ratingGroup))filterCharging := bson.M{"ratingGroup": ratingGroup,}mongoapi.RestfulAPIDeleteMany(chargingDataColl, filterCharging)}delete(ue.RatingGroupData, smPolicyId)c.JSON(http.StatusNoContent, nil)
}
相比于GET和DELETE的简单逻辑,CREATE和UPDATE的逻辑要复杂许多,free5gc用了几百行代码来实现其操作。因为Npcf_SMPolicyControl_Create
操作不仅仅只是为一个用户和网络间的PDU session创建一个policy assoication,还需要做很多policy决策指示SMF怎么去控制这个PDU session(这好像是PCF最优决策存在感的操作)。
SmPolicyContextData, SmPolicyDecision
// https://github.com/free5gc/openapi/blob/v1.0.8/models/model_sm_policy_context_data.go
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 domainIpDomain 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"`
}// 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"`
}// https://github.com/free5gc/openapi/blob/v1.0.8/models/model_pcc_rule.go
type PccRule struct {// An array of IP flow packet filter information.FlowInfos []FlowInformation `json:"flowInfos,omitempty" yaml:"flowInfos" bson:"flowInfos" mapstructure:"FlowInfos"`// A reference to the application detection filter configured at the UPF.AppId string `json:"appId,omitempty" yaml:"appId" bson:"appId" mapstructure:"AppId"`// Represents the content version of some content.ContVer int32 `json:"contVer,omitempty" yaml:"contVer" bson:"contVer" mapstructure:"ContVer"`// Univocally identifies the PCC rule within a PDU session.PccRuleId string `json:"pccRuleId" yaml:"pccRuleId" bson:"pccRuleId" mapstructure:"PccRuleId"`Precedence int32 `json:"precedence,omitempty" yaml:"precedence" bson:"precedence" mapstructure:"Precedence"`AfSigProtocol AfSigProtocol `json:"afSigProtocol,omitempty" yaml:"afSigProtocol" bson:"afSigProtocol" mapstructure:"AfSigProtocol"`// Indication of application relocation possibility.AppReloc bool `json:"appReloc,omitempty" yaml:"appReloc" bson:"appReloc" mapstructure:"AppReloc"`// A reference to the QoSData policy type decision type. It is the qosId described in subclause 5.6.2.8. (NOTE)RefQosData []string `json:"refQosData,omitempty" yaml:"refQosData" bson:"refQosData" mapstructure:"RefQosData"`// A reference to the TrafficControlData policy decision type. It is the tcId described in subclause 5.6.2.10. (NOTE)RefTcData []string `json:"refTcData,omitempty" yaml:"refTcData" bson:"refTcData" mapstructure:"RefTcData"`// A reference to the ChargingData policy decision type. It is the chgId described in subclause 5.6.2.11. (NOTE)RefChgData []string `json:"refChgData,omitempty" yaml:"refChgData" bson:"refChgData" mapstructure:"RefChgData"`// A reference to UsageMonitoringData policy decision type. It is the umId described in subclause 5.6.2.12. (NOTE)RefUmData []string `json:"refUmData,omitempty" yaml:"refUmData" bson:"refUmData" mapstructure:"RefUmData"`// A reference to the condition data. It is the condId described in subclause 5.6.2.9.RefCondData string `json:"refCondData,omitempty" yaml:"refCondData" bson:"refCondData" mapstructure:"RefCondData"`
}// https://github.com/free5gc/openapi/blob/v1.0.8/models/model_session_rule.go
type SessionRule struct {AuthSessAmbr *Ambr `json:"authSessAmbr,omitempty" yaml:"authSessAmbr" bson:"authSessAmbr" mapstructure:"AuthSessAmbr"`AuthDefQos *AuthorizedDefaultQos `json:"authDefQos,omitempty" yaml:"authDefQos" bson:"authDefQos" mapstructure:"AuthDefQos"`// Univocally identifies the session rule within a PDU session.SessRuleId string `json:"sessRuleId" yaml:"sessRuleId" bson:"sessRuleId" mapstructure:"SessRuleId"`// A reference to UsageMonitoringData policy decision type. It is the umId described in subclause 5.6.2.12.RefUmData string `json:"refUmData,omitempty" yaml:"refUmData" bson:"refUmData" mapstructure:"RefUmData"`// A reference to the condition data. It is the condId described in subclause 5.6.2.9.RefCondData string `json:"refCondData,omitempty" yaml:"refCondData" bson:"refCondData" mapstructure:"RefCondData"`
}
与AMPolicy类似,想去内存里的context看看有没有旧的smPopliy,有的话删掉delete(ue.SmPolicyData, smPolicyID)
,然后创建新的ue.NewUeSmPolicyData(smPolicyID, request, &smData)
。这以后,就要开始做一系列复杂的决策, 包括但不限于指定适用的Session rules和PCC rules,从数据库获取QoS流规则和流量控制规则并决定流量控制、收费、和QoS参数等。决策的复杂性很大程度上导致了CREATE操作的复杂性——太多东西要决定了。
// https://github.com/free5gc/pcf/blob/v1.2.5/internal/sbi/processor/smpolicy.go#L28C1-L484C2
func (p *Processor) HandleCreateSmPolicyRequest(c *gin.Context, request models.SmPolicyContextData) {// set up pcf context, uw context, udr client ......var smData models.SmPolicyDatasmPolicyID := fmt.Sprintf("%s-%d", ue.Supi, request.PduSessionId)smPolicyData := ue.SmPolicyData[smPolicyID] // 这个`smPolicyData`是`UeSmPolicyData`类型if smPolicyData == nil || smPolicyData.SmPolicyData == nil {param := Nudr_DataRepository.PolicyDataUesUeIdSmDataGetParamOpts{Snssai: optional.NewInterface(util.MarshToJsonString(*request.SliceInfo)),Dnn: optional.NewString(request.Dnn),}smData, response, err1 = client.DefaultApi.PolicyDataUesUeIdSmDataGet(ctx, ue.Supi, ¶m)} else {smData = *smPolicyData.SmPolicyData}if smPolicyData != nil {delete(ue.SmPolicyData, smPolicyID) // 删掉旧的smPolicy}smPolicyData = ue.NewUeSmPolicyData(smPolicyID, request, &smData) // 创建新的smPolicy// 根据SMF发过来的请求以及数据库中的数据进行Policy Decisiondecision := models.SmPolicyDecision{SessRules: make(map[string]*models.SessionRule),PccRules: make(map[string]*models.PccRule),TraffContDecs: make(map[string]*models.TrafficControlData),}// decide session rulesSessRuleId := fmt.Sprintf("SessRuleId-%d", request.PduSessionId)sessRule := models.SessionRule{AuthSessAmbr: request.SubsSessAmbr,SessRuleId: SessRuleId,}defQos := request.SubsDefQosif defQos != nil {sessRule.AuthDefQos = &models.AuthorizedDefaultQos{Var5qi: defQos.Var5qi,Arp: defQos.Arp,PriorityLevel: defQos.PriorityLevel,}}decision.SessRules[SessRuleId] = &sessRule// make data network related decisionsdnnData := util.GetSMPolicyDnnData(smData, request.SliceInfo, request.Dnn)if dnnData != nil {decision.Online = dnnData.Onlinedecision.Offline = dnnData.Offline// ......} else {decision.Online = request.Onlinedecision.Offline = request.Offline}// make QoS related decisionsfilter := bson.M{"ueId": ue.Supi, "snssai": util.SnssaiModelsToHex(*request.SliceInfo), "dnn": request.Dnn}qosFlowInterface, err := mongoapi.RestfulAPIGetMany(qosFlowDataColl, filter, queryStrength)for _, qosFlow := range qosFlowInterface {qosData := newQosDataWithQosFlowMap(qosFlow)if decision.QosDecs == nil {decision.QosDecs = make(map[string]*models.QosData)}decision.QosDecs[qosData.QosId] = qosData}flowRulesInterface, err := mongoapi.RestfulAPIGetMany(flowRuleDataColl, filter, queryStrength)// decide PCC rulespcc := util.CreateDefaultPccRules(smPolicyData.PccRuleIdGenerator)smPolicyData.PccRuleIdGenerator++filterCharging := bson.M{"ueId": ue.Supi,"snssai": util.SnssaiModelsToHex(*request.SliceInfo),"dnn": "","filter": "",}chargingInterface, err := mongoapi.RestfulAPIGetOne(chargingDataColl, filterCharging, queryStrength)if err != nil {util.SetPccRuleRelatedData(&decision, pcc, nil, nil, nil, nil)} else if chargingInterface != nil {rg, err1 := p.Context().RatingGroupIdGenerator.Allocate()chgData := &models.ChargingData{ChgId: util.GetChgId(smPolicyData.ChargingIdGenerator),RatingGroup: int32(rg),ReportingLevel: models.ReportingLevel_RAT_GR_LEVEL,MeteringMethod: models.MeteringMethod_VOLUME,}switch chargingInterface["chargingMethod"].(string) {case "Online":chgData.Online = truechgData.Offline = falsecase "Offline":chgData.Online = falsechgData.Offline = true}util.SetPccRuleRelatedData(&decision, pcc, nil, nil, chgData, nil)chargingInterface["ratingGroup"] = chgData.RatingGroupmongoapi.RestfulAPIPutOne(chargingDataColl, chargingInterface, chargingInterface, queryStrength)if ue.RatingGroupData == nil {ue.RatingGroupData = make(map[string][]int32)}ue.RatingGroupData[smPolicyID] = append(ue.RatingGroupData[smPolicyID], chgData.RatingGroup)smPolicyData.ChargingIdGenerator++}for i, flowRule := range flowRulesInterface {precedence := int32(flowRule["precedence"].(float64))if val, ok := flowRule["filter"].(string); ok {tokens := strings.Split(val, " ")FlowDescription := flowdesc.NewIPFilterRule()// set FlowDescription attributes ......FlowDescriptionStr, err = flowdesc.Encode(FlowDescription)pccRule := util.CreatePccRule(smPolicyData.PccRuleIdGenerator, precedence, []models.FlowInformation{{FlowDescription: FlowDescriptionStr,FlowDirection: models.FlowDirectionRm_DOWNLINK,},}, "")filterCharging := bson.M{"ueId": ue.Supi,"snssai": util.SnssaiModelsToHex(*request.SliceInfo),"dnn": request.Dnn,"filter": val,}var chargingInterface map[string]interface{}chargingInterface, err = mongoapi.RestfulAPIGetOne(chargingDataColl, filterCharging, 2)if err != nil {logger.SmPolicyLog.Errorf("Fail to get charging data to mongoDB err: %+v", err)} else {rg, err1 := p.Context().RatingGroupIdGenerator.Allocate()chgData := &models.ChargingData{ChgId: util.GetChgId(smPolicyData.ChargingIdGenerator),RatingGroup: int32(rg),ReportingLevel: models.ReportingLevel_RAT_GR_LEVEL,MeteringMethod: models.MeteringMethod_VOLUME,}switch chargingInterface["chargingMethod"].(string) {case "Online":chgData.Online = truechgData.Offline = falsecase "Offline":chgData.Online = falsechgData.Offline = true}if decision.ChgDecs == nil {decision.ChgDecs = make(map[string]*models.ChargingData)}chargingInterface["ratingGroup"] = chgData.RatingGroupif _, err = mongoapi.RestfulAPIPutOne(chargingDataColl, chargingInterface, chargingInterface, queryStrength); err != nil {logger.SmPolicyLog.Errorf("Fail to put charging data to mongoDB err: %+v", err)} else {util.SetPccRuleRelatedData(&decision, pccRule, nil, nil, chgData, nil)smPolicyData.ChargingIdGenerator++}if ue.RatingGroupData == nil {ue.RatingGroupData = make(map[string][]int32)}ue.RatingGroupData[smPolicyID] = append(ue.RatingGroupData[smPolicyID], chgData.RatingGroup)}qosRef := strconv.Itoa(int(flowRule["qosRef"].(float64)))util.SetPccRuleRelatedByQosRef(&decision, pccRule, qosRef)smPolicyData.PccRuleIdGenerator++}}requestSuppFeat, err := openapi.NewSupportedFeature(request.SuppFeat)decision.SuppFeat = pcfSelf.PcfSuppFeats[models.ServiceName_NPCF_SMPOLICYCONTROL].NegotiateWith(requestSuppFeat).String()decision.QosFlowUsage = request.QosFlowUsage// TODO: Trigger about UMC, ADC, NetLoc,...decision.PolicyCtrlReqTriggers = util.PolicyControlReqTrigToArray(0x40780f)smPolicyData.PolicyDecision = &decision// Gmake traffica influcence related decisionsreqParam := Nudr_DataRepository.ApplicationDataInfluenceDataGetParamOpts{Dnns: optional.NewInterface([]string{request.Dnn}),Snssais: optional.NewInterface(util.MarshToJsonString([]models.Snssai{*request.SliceInfo})),InternalGroupIds: optional.NewInterface(request.InterGrpIds),Supis: optional.NewInterface([]string{request.Supi}),}trafficInfluDatas, resp, err := udrClient.InfluenceDataApi.ApplicationDataInfluenceDataGet(ctx, &reqParam)if len(trafficInfluDatas) != 0 {// UE identity in UDR appData and apply appData to sm poliocyvar precedence int32 = 23for _, tiData := range trafficInfluDatas {pccRule := util.CreatePccRule(smPolicyData.PccRuleIdGenerator, precedence, nil, tiData.AfAppId)util.SetSmPolicyDecisionByTrafficInfluData(&decision, pccRule, tiData)influenceID := getInfluenceID(tiData.ResUri)if influenceID != "" {smPolicyData.InfluenceDataToPccRule[influenceID] = pccRule.PccRuleId}smPolicyData.PccRuleIdGenerator++if precedence < Precedence_Maximum {precedence++}}}// Subscribe to Traffic Influence Data in UDRsubscriptionID, problemDetail, err := p.Consumer().CreateInfluenceDataSubscription(ue, request)smPolicyData.SubscriptionID = subscriptionIDc.JSON(http.StatusCreated, decision)
}
UPDATE操作与CREATE操作类似,在此略过。
PolicyAuthorization
从名字中只能看出来PolicyAuthorization
好像是想给某个policy授权?但实际上,PolicyAuthorization
更像是一个AF(Application Function)与PCF之间的接口,AF通过这个接口发起请求,PCF为这个应用分配合适的资源和策略。这些第三方应用包括视频流媒体服务、VoIP通话、在线游戏、企业内线服务等。
Application Function (AF): Element offering application(s) that use PDU session resources
PolicyAuthorization
与SMPolicyControl
关系相当密切:当AF通过PolicyAuthorization请求资源时,PCF会创建相应的PCC Rules,通过SMPolicyControl将这些规则下发给SMF执行,且在AppSession和SmPolicy之间建立映射关系。PolicyAuthorization
的GET和DELETE操作都与SMPolicy异曲同工:GET操作根据appSessionId
直接从Context找到数据返回,DELETE操作除了要删除Context中的数据delete(smPolicy.AppSessions, appSessionId); pcfSelf.AppSessionPool.Delete(appSessionId)
还需要删除相关的PCC Rules并通知SMF删除事宜。
// https://github.com/free5gc/pcf/blob/main/internal/sbi/processor/policyauthorization.go#L499
func (p *Processor) HandleGetAppSessionContext(c *gin.Context, appSessionId string) {pcfSelf := p.Context()var appSession *pcf_context.AppSessionDataval, ok := pcfSelf.AppSessionPool.Load(appSessionId)appSession = val.(*pcf_context.AppSessionData)c.JSON(http.StatusOK, appSession.AppSessionContext)
}// https://github.com/free5gc/pcf/blob/main/internal/sbi/processor/policyauthorization.go#L438
func (p *Processor) HandleDeleteAppSessionContext(c *gin.Context, appSessionId string,eventsSubscReqData *models.EventsSubscReqData,
) {pcfSelf := p.Context()var appSession *pcf_context.AppSessionDataif val, ok := pcfSelf.AppSessionPool.Load(appSessionId); ok {appSession = val.(*pcf_context.AppSessionData)}// Remove related pcc rule resourcesmPolicy := appSession.SmPolicyDatadeletedSmPolicyDec := models.SmPolicyDecision{}for _, pccRuleID := range appSession.RelatedPccRuleIds {smPolicy.RemovePccRule(pccRuleID, &deletedSmPolicyDec)}delete(smPolicy.AppSessions, appSessionId)pcfSelf.AppSessionPool.Delete(appSessionId)smPolicy.ArrangeExistEventSubscription()// Notify SMF About Pcc Rule removalnotification := models.SmPolicyNotification{ResourceUri: util.GetResourceUri(models.ServiceName_NPCF_SMPOLICYCONTROL, smPolicyID),SmPolicyDecision: &deletedSmPolicyDec,}go p.SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification)c.JSON(http.StatusNoContent, nil)
}
AppSessionContext, AppSessionContextReqData
// Represents an Individual Application Session Context resource.
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"`
}// Identifies the service requirements of an Individual Application Session Context.
type AppSessionContextReqData struct {// Contains an AF application identifier.AfAppId string `json:"afAppId,omitempty" yaml:"afAppId" bson:"afAppId" mapstructure:"AfAppId"`AfRoutReq *AfRoutingRequirement `json:"afRoutReq,omitempty" yaml:"afRoutReq" bson:"afRoutReq" mapstructure:"AfRoutReq"`// Contains an identity of an application service provider.AspId string `json:"aspId,omitempty" yaml:"aspId" bson:"aspId" mapstructure:"AspId"`// string identifying a BDT Reference ID as defined in subclause 5.3.3 of 3GPP TS 29.154.BdtRefId string `json:"bdtRefId,omitempty" yaml:"bdtRefId" bson:"bdtRefId" mapstructure:"BdtRefId"`Dnn string `json:"dnn,omitempty" yaml:"dnn" bson:"dnn" mapstructure:"Dnn"`EvSubsc *EventsSubscReqData `json:"evSubsc,omitempty" yaml:"evSubsc" bson:"evSubsc" mapstructure:"EvSubsc"`MedComponents map[string]MediaComponent `json:"medComponents,omitempty" yaml:"medComponents" bson:"medComponents" mapstructure:"MedComponents"`IpDomain string `json:"ipDomain,omitempty" yaml:"ipDomain" bson:"ipDomain" mapstructure:"IpDomain"`// indication of MPS service requestMpsId string `json:"mpsId,omitempty" yaml:"mpsId" bson:"mpsId" mapstructure:"MpsId"`// string providing an URI formatted according to IETF RFC 3986.NotifUri string `json:"notifUri" yaml:"notifUri" bson:"notifUri" mapstructure:"NotifUri"`SliceInfo *Snssai `json:"sliceInfo,omitempty" yaml:"sliceInfo" bson:"sliceInfo" mapstructure:"SliceInfo"`// Contains an identity of a sponsor.SponId string `json:"sponId,omitempty" yaml:"sponId" bson:"sponId" mapstructure:"SponId"`SponStatus SponsoringStatus `json:"sponStatus,omitempty" yaml:"sponStatus" bson:"sponStatus" mapstructure:"SponStatus"`Supi string `json:"supi,omitempty" yaml:"supi" bson:"supi" mapstructure:"Supi"`Gpsi string `json:"gpsi,omitempty" yaml:"gpsi" bson:"gpsi" mapstructure:"Gpsi"`SuppFeat string `json:"suppFeat" yaml:"suppFeat" bson:"suppFeat" mapstructure:"SuppFeat"`UeIpv4 string `json:"ueIpv4,omitempty" yaml:"ueIpv4" bson:"ueIpv4" mapstructure:"UeIpv4"`UeIpv6 string `json:"ueIpv6,omitempty" yaml:"ueIpv6" bson:"ueIpv6" mapstructure:"UeIpv6"`UeMac string `json:"ueMac,omitempty" yaml:"ueMac" bson:"ueMac" mapstructure:"UeMac"`
}// https://github.com/free5gc/openapi/blob/main/models/model_media_component.go
// Identifies a media component.
type MediaComponent struct {// Contains an AF application identifier.AfAppId string `json:"afAppId,omitempty" yaml:"afAppId" bson:"afAppId" mapstructure:"AfAppId"`AfRoutReq *AfRoutingRequirement `json:"afRoutReq,omitempty" yaml:"afRoutReq" bson:"afRoutReq" mapstructure:"AfRoutReq"`// Represents the content version of some content.ContVer int32 `json:"contVer,omitempty" yaml:"contVer" bson:"contVer" mapstructure:"ContVer"`Codecs []string `json:"codecs,omitempty" yaml:"codecs" bson:"codecs" mapstructure:"Codecs"`FStatus FlowStatus `json:"fStatus,omitempty" yaml:"fStatus" bson:"fStatus" mapstructure:"FStatus"`MarBwDl string `json:"marBwDl,omitempty" yaml:"marBwDl" bson:"marBwDl" mapstructure:"MarBwDl"`MarBwUl string `json:"marBwUl,omitempty" yaml:"marBwUl" bson:"marBwUl" mapstructure:"MarBwUl"`MedCompN int32 `json:"medCompN" yaml:"medCompN" bson:"medCompN" mapstructure:"MedCompN"`MedSubComps map[string]MediaSubComponent `json:"medSubComps,omitempty" yaml:"medSubComps" bson:"medSubComps" mapstructure:"MedSubComps"`MedType MediaType `json:"medType,omitempty" yaml:"medType" bson:"medType" mapstructure:"MedType"`MirBwDl string `json:"mirBwDl,omitempty" yaml:"mirBwDl" bson:"mirBwDl" mapstructure:"MirBwDl"`MirBwUl string `json:"mirBwUl,omitempty" yaml:"mirBwUl" bson:"mirBwUl" mapstructure:"MirBwUl"`ResPrio ReservPriority `json:"resPrio,omitempty" yaml:"resPrio" bson:"resPrio" mapstructure:"ResPrio"`
}// https://github.com/free5gc/openapi/blob/main/models/model_media_sub_component.go
// Identifies a media subcomponent
type MediaSubComponent struct {EthfDescs []EthFlowDescription `json:"ethfDescs,omitempty" yaml:"ethfDescs" bson:"ethfDescs" mapstructure:"EthfDescs"`FNum int32 `json:"fNum" yaml:"fNum" bson:"fNum" mapstructure:"FNum"`FDescs []string `json:"fDescs,omitempty" yaml:"fDescs" bson:"fDescs" mapstructure:"FDescs"`FStatus FlowStatus `json:"fStatus,omitempty" yaml:"fStatus" bson:"fStatus" mapstructure:"FStatus"`MarBwDl string `json:"marBwDl,omitempty" yaml:"marBwDl" bson:"marBwDl" mapstructure:"MarBwDl"`MarBwUl string `json:"marBwUl,omitempty" yaml:"marBwUl" bson:"marBwUl" mapstructure:"MarBwUl"`// 2-octet string, where each octet is encoded in hexadecimal representation. The first octet contains the IPv4 Type-of-Service or the IPv6 Traffic-Class field and the second octet contains the ToS/Traffic Class mask field.TosTrCl string `json:"tosTrCl,omitempty" yaml:"tosTrCl" bson:"tosTrCl" mapstructure:"TosTrCl"`FlowUsage FlowUsage `json:"flowUsage,omitempty" yaml:"flowUsage" bson:"flowUsage" mapstructure:"FlowUsage"`
}
// https://github.com/free5gc/pcf/blob/main/internal/sbi/processor/policyauthorization.go#L154
// Radically simplified!
func (p *Processor) postAppSessCtxProcedure(appSessCtx *models.AppSessionContext) (*models.AppSessionContext, *models.ProblemDetails,
) {// Initial BDT policy indication(the only one which is not related to session)if appSessCtx.ascReqData.BdtRefId != "" {p.handleBDTPolicyInd(pcf_context, appSessCtx)pcfSelf.AppSessionPool.Store(ascReqData.BdtRefId, appSessCtx)return appSessCtx, nil}ueSmPolicyData := pcfSelf.SessionBinding(appSessCtx.ascReqData)// Handle Pcc rulesfor _, medComp := range ascReqData.MedComponents {pccRule = util.GetPccRuleByAfAppId(smPolicy.PolicyDecision.PccRules, appID)qosData := util.CreateQosData(smPolicy.PccRuleIdGenerator, var5qi, 8)util.SetPccRuleRelatedData(ueSmPolicyData.PolicyDecision, pccRule, nil, &qosData, nil, nil)} else {// update pccRule's qosfor _, qosID := range pccRule.RefQosData {qosData = *smPolicy.PolicyDecision.QosDecs[qosID]、qosData, ul, dl = updateQosInMedComp(*smPolicy.PolicyDecision.QosDecs[qosID], &medComp)modifyRemainBitRate(smPolicy, &qosData, ul, dl)smPolicy.PolicyDecision.QosDecs[qosData.QosId] = &qosData}}// Initial provisioning of traffic routing informationpccRule := provisioningOfTrafficRoutingInfo(smPolicy, ascReqData.AfAppId, ascReqData.AfRoutReq, "")relatedPccRuleIds := make(map[string]string)key := fmt.Sprintf("appID-%s", ascReqData.AfAppId)relatedPccRuleIds[key] = pccRule.PccRuleId// Event SubscriptioneventSubs := make(map[models.AfEvent]models.AfNotifMethod)for _, subs := range ascReqData.EvSubsc.Events {eventSubs[subs.Event] = subs.NotifMethodvar trig models.PolicyControlRequestTriggerswitch subs.Event {case models.AfEvent_ACCESS_TYPE_CHANGE:trig = models.PolicyControlRequestTrigger_AC_TY_CH// more cases ...default:logger.PolicyAuthLog.Warn("AF Event is unknown")continue}if !util.CheckPolicyControlReqTrig(smPolicy.PolicyDecision.PolicyCtrlReqTriggers, trig) {smPolicy.PolicyDecision.PolicyCtrlReqTriggers = append(smPolicy.PolicyDecision.PolicyCtrlReqTriggers, trig)} }// Initial provisioning of sponsored connectivity informationumID := util.GetUmId(ascReqData.AspId, ascReqData.SponId)umData := extractUmData(umID, eventSubs, ascReqData.EvSubsc.UsgThres)handleSponsoredConnectivityInformation(smPolicy, relatedPccRuleIds, ascReqData.AspId,ascReqData.SponId, ascReqData.SponStatus, umData, &updateSMpolicy)// Allocate App Session IdappSessID := ue.AllocUeAppSessionId(pcfSelf)// Associate App Session to SMPolicysmPolicy.AppSessions[appSessID] = truedata := pcf_context.AppSessionData{AppSessionId: appSessID,AppSessionContext: appSessCtx,SmPolicyData: smPolicy,}if len(relatedPccRuleIds) > 0 {data.RelatedPccRuleIds = relatedPccRuleIdsdata.PccRuleIdMapToCompId = reverseStringMap(relatedPccRuleIds)}appSessCtx.EvsNotif = &models.EventsNotification{}// Set Event Subsciption related Data ...pcfSelf.AppSessionPool.Store(appSessID, &data)// Send Notification to SMFgo p.SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification)return appSessCtx, nil
}
至此我研究了一遍PCF的机制及其源代码,但我总还有一些云里雾里的感觉。我认为这是因为PCF深度介入AMF以及SMF,尤其是SMF。所以想要全面理解PCF就必须等到研究完AMF和SMF才能做到,至少要知道PCF所指定的policy是怎么被应用和实施的。所以下文先从SMF开始。