Free5GC源码研究(9) - PCF研究(下)
前文再续书接上一回,继续研究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 UDR p.Consumer().RemoveInfluenceDataSubscription(ue, smPolicy.SubscriptionID) delete(ue.SmPolicyData, smPolicyId) // Release related App Session terminationInfo := 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 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"` } // 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.SmPolicyData smPolicyID := 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 Decision decision := models.SmPolicyDecision{ SessRules: make(map[string]*models.SessionRule), PccRules: make(map[string]*models.PccRule), TraffContDecs: make(map[string]*models.TrafficControlData), } // decide session rules SessRuleId := fmt.Sprintf("SessRuleId-%d", request.PduSessionId) sessRule := models.SessionRule{ AuthSessAmbr: request.SubsSessAmbr, SessRuleId: SessRuleId, } defQos := request.SubsDefQos if defQos != nil { sessRule.AuthDefQos = &models.AuthorizedDefaultQos{ Var5qi: defQos.Var5qi, Arp: defQos.Arp, PriorityLevel: defQos.PriorityLevel, } } decision.SessRules[SessRuleId] = &sessRule // make data network related decisions dnnData := util.GetSMPolicyDnnData(smData, request.SliceInfo, request.Dnn) if dnnData != nil { decision.Online = dnnData.Online decision.Offline = dnnData.Offline // ...... } else { decision.Online = request.Online decision.Offline = request.Offline } // make QoS related decisions filter := 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 rules pcc := 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 = true chgData.Offline = false case "Offline": chgData.Online = false chgData.Offline = true } util.SetPccRuleRelatedData(&decision, pcc, nil, nil, chgData, nil) chargingInterface["ratingGroup"] = chgData.RatingGroup mongoapi.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 = true chgData.Offline = false case "Offline": chgData.Online = false chgData.Offline = true } if decision.ChgDecs == nil { decision.ChgDecs = make(map[string]*models.ChargingData) } chargingInterface["ratingGroup"] = chgData.RatingGroup if _, 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 decisions reqParam := 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 poliocy var precedence int32 = 23 for _, 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 UDR subscriptionID, problemDetail, err := p.Consumer().CreateInfluenceDataSubscription(ue, request) smPolicyData.SubscriptionID = subscriptionID c.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.AppSessionData val, 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.AppSessionData if val, ok := pcfSelf.AppSessionPool.Load(appSessionId); ok { appSession = val.(*pcf_context.AppSessionData) } // Remove related pcc rule resource smPolicy := appSession.SmPolicyData deletedSmPolicyDec := 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 removal notification := 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 request MpsId 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 rules for _, 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 qos for _, 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 information pccRule := provisioningOfTrafficRoutingInfo(smPolicy, ascReqData.AfAppId, ascReqData.AfRoutReq, "") relatedPccRuleIds := make(map[string]string) key := fmt.Sprintf("appID-%s", ascReqData.AfAppId) relatedPccRuleIds[key] = pccRule.PccRuleId // Event Subscription eventSubs := make(map[models.AfEvent]models.AfNotifMethod) for _, subs := range ascReqData.EvSubsc.Events { eventSubs[subs.Event] = subs.NotifMethod var trig models.PolicyControlRequestTrigger switch 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 information umID := 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 Id appSessID := ue.AllocUeAppSessionId(pcfSelf) // Associate App Session to SMPolicy smPolicy.AppSessions[appSessID] = true data := pcf_context.AppSessionData{ AppSessionId: appSessID, AppSessionContext: appSessCtx, SmPolicyData: smPolicy, } if len(relatedPccRuleIds) > 0 { data.RelatedPccRuleIds = relatedPccRuleIds data.PccRuleIdMapToCompId = reverseStringMap(relatedPccRuleIds) } appSessCtx.EvsNotif = &models.EventsNotification{} // Set Event Subsciption related Data ... pcfSelf.AppSessionPool.Store(appSessID, &data) // Send Notification to SMF go p.SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) return appSessCtx, nil }
至此我研究了一遍PCF的机制及其源代码,但我总还有一些云里雾里的感觉。我认为这是因为PCF深度介入AMF以及SMF,尤其是SMF。所以想要全面理解PCF就必须等到研究完AMF和SMF才能做到,至少要知道PCF所指定的policy是怎么被应用和实施的。所以下文先从SMF开始。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY