前文再續書接上一回,繼續研究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開始。