Skip to content

Commit

Permalink
support setting charging rule in PCC by webconsole (#12)
Browse files Browse the repository at this point in the history
* support setting charging rule in PCC by webconsole

* change charging reference

* update charging data collection

* update pdu charging data filter

* refactor: createSMPolicyProcedure

* fix ci error

* createSMPolicyProcedure: handle charging only when no error

* fix ci error

* add nil check on chargingInterface

* fix: Case-insensitive in pcf_util.go

* update GetSMPolicyDnnData()

* fix: qfi -> qosRef

* change var name: dest -> tokens

* fix: rating group is not match

* fix linter error

* fix: port handling in IP Filter

* fix: flow description's destination IP can only accept string "assigned"

* if port range is 1-65535, omit port range

* fix: use case-insensitive mongoapi

* Use Strength = 2 (cas-insensitive) when query to MongoDB

* update util's hash

* add tracelog

* move RatingGroupIdGenerator to pcf_context

* Fix: rebase and fix linter

* Fix: remove unused code

---------

Co-authored-by: Roy-Hu <[email protected]>
Co-authored-by: roy19991013 <[email protected]>
Co-authored-by: Ian Chen <[email protected]>
Co-authored-by: ianchen0119 <[email protected]>
Co-authored-by: brianchennn <[email protected]>
Co-authored-by: CTFang@WireLab <[email protected]>
  • Loading branch information
7 people authored Mar 3, 2024
1 parent fcaa539 commit 55b720d
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 28 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/cydev/zero v0.0.0-20160322155811-4a4535dd56e7
github.com/free5gc/openapi v1.0.7
github.com/free5gc/util v1.0.5
github.com/free5gc/util v1.0.5-0.20231219085815-8972321e9748
github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.4.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/free5gc/openapi v1.0.7 h1:I0HOgqRgER6DbyqB6EBxusmbeouIhcOn4eOJvL3veJA=
github.com/free5gc/openapi v1.0.7/go.mod h1:qv9KqEucoZSeENPRFGxfTe+33ZWYyiYFx1Rj+H0DoWA=
github.com/free5gc/util v1.0.5 h1:MjyEIX6gGbdS8FUAOxhE0FsMD6pmv5T+45LjBabVhS8=
github.com/free5gc/util v1.0.5/go.mod h1:jgiW/bNbNbX87CSTKXyfvkhQeDY9ThK+TtWTu4ILCS8=
github.com/free5gc/util v1.0.5-0.20231219085815-8972321e9748 h1:zx+0hkvsdwje0a3wPDz3gt9trLBRXUwpeHfQMWGY4MA=
github.com/free5gc/util v1.0.5-0.20231219085815-8972321e9748/go.mod h1:jgiW/bNbNbX87CSTKXyfvkhQeDY9ThK+TtWTu4ILCS8=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
Expand Down
4 changes: 4 additions & 0 deletions internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ type PCFContext struct {
// lock
DefaultUdrURILock sync.RWMutex

// Charging
RatingGroupIdGenerator *idgenerator.IDGenerator

OAuth2Required bool
}

Expand Down Expand Up @@ -153,6 +156,7 @@ func Init() {
pcfContext.PcfServiceUris = make(map[models.ServiceName]string)
pcfContext.PcfSuppFeats = make(map[models.ServiceName]openapi.SupportedFeature)
pcfContext.BdtPolicyIDGenerator = idgenerator.NewGenerator(1, math.MaxInt64)
pcfContext.RatingGroupIdGenerator = idgenerator.NewGenerator(1, math.MaxInt64)
InitpcfContext(&pcfContext)
}

Expand Down
2 changes: 2 additions & 0 deletions internal/context/ue.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type UeSmPolicyData struct {
PackFiltIdGenerator int32
PccRuleIdGenerator int32
ChargingIdGenerator int32

// FlowMapsToPackFiltIds map[string][]string // use Flow Description(in TS 29214) as key map to pcc rule ids
PackFiltMapToPccRuleId map[string]string // use PackFiltId as Key
// Related to GBR
Expand Down Expand Up @@ -156,6 +157,7 @@ func (ue *UeContext) NewUeSmPolicyData(
// data.RefToAmPolicy = amData
data.PccRuleIdGenerator = 1
data.ChargingIdGenerator = 1

data.PcfUe = ue
ue.SmPolicyData[key] = &data
data.InfluenceDataToPccRule = make(map[string]string)
Expand Down
2 changes: 1 addition & 1 deletion internal/sbi/consumer/influenceDataSubscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func RemoveInfluenceDataSubscription(ue *pcf_context.UeContext, subscriptionID s
}()
if httpResp.Status != localErr.Error() {
err = localErr
return nil, err
return problemDetails, err
}
problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails)
problemDetails = &problem
Expand Down
165 changes: 152 additions & 13 deletions internal/sbi/producer/smpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import (
"github.com/free5gc/pcf/internal/logger"
"github.com/free5gc/pcf/internal/sbi/consumer"
"github.com/free5gc/pcf/internal/util"
"github.com/free5gc/util/flowdesc"
"github.com/free5gc/util/httpwrapper"
"github.com/free5gc/util/mongoapi"
)

const (
flowRuleDataColl = "policyData.ues.flowRule"
qosFlowDataColl = "policyData.ues.qosFlow"
chargingDataColl = "policyData.ues.chargingData"
)

// SmPoliciesPost -
Expand All @@ -48,7 +50,7 @@ func HandleCreateSmPolicyRequest(request *httpwrapper.Request) *httpwrapper.Resp

func newQosDataWithQosFlowMap(qosFlow map[string]interface{}) *models.QosData {
qosData := &models.QosData{
QosId: strconv.Itoa(int(qosFlow["qfi"].(float64))),
QosId: strconv.Itoa(int(qosFlow["qosRef"].(float64))),
Qnc: false,
Var5qi: int32(qosFlow["5qi"].(float64)),
}
Expand All @@ -72,6 +74,7 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) (
header http.Header, response *models.SmPolicyDecision, problemDetails *models.ProblemDetails,
) {
var err error
queryStrength := 2 // 2: case-insensitive, 3: case-sensitive
logger.SmPolicyLog.Tracef("Handle Create SM Policy Request")

if request.Supi == "" || request.SliceInfo == nil || len(request.SliceInfo.Sd) != 6 {
Expand Down Expand Up @@ -207,7 +210,7 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) (
}

filter := bson.M{"ueId": ue.Supi, "snssai": util.SnssaiModelsToHex(*request.SliceInfo), "dnn": request.Dnn}
qosFlowInterface, err := mongoapi.RestfulAPIGetMany(qosFlowDataColl, filter)
qosFlowInterface, err := mongoapi.RestfulAPIGetMany(qosFlowDataColl, filter, queryStrength)
if err != nil {
logger.SmPolicyLog.Errorf("createSMPolicyProcedure error: %+v", err)
}
Expand All @@ -222,22 +225,158 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) (
}

// get flow rules from databases
flowRulesInterface, err := mongoapi.RestfulAPIGetMany(flowRuleDataColl, filter)
flowRulesInterface, err := mongoapi.RestfulAPIGetMany(flowRuleDataColl, filter, queryStrength)
if err != nil {
logger.SmPolicyLog.Errorf("createSMPolicyProcedure error: %+v", err)
}

for _, flowRule := range flowRulesInterface {
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 {
logger.SmPolicyLog.Errorf("Fail to get charging data to mongoDB err: %+v", err)
logger.SmPolicyLog.Errorf("chargingInterface %+v", chargingInterface)
util.SetPccRuleRelatedData(&decision, pcc, nil, nil, nil, nil)
} else if chargingInterface != nil {
rg, err1 := pcf_context.GetSelf().RatingGroupIdGenerator.Allocate()
if err1 != nil {
logger.SmPolicyLog.Error("rating group allocate error")
problemDetails := util.GetProblemDetail("rating group allocate error", util.ERROR_IDGENERATOR)
return nil, nil, &problemDetails
}
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)

chargingBsonM := bson.M{
"ratingGroup": chgData.RatingGroup,
}
logger.SmPolicyLog.Traceln("put ratingGroup to MongoDB")
if _, err = mongoapi.RestfulAPIPutOne(chargingDataColl, filterCharging, chargingBsonM, queryStrength); err != nil {
logger.SmPolicyLog.Errorf("Fail to put charging data to mongoDB err: %+v", err)
}

smPolicyData.ChargingIdGenerator++
}

logger.SmPolicyLog.Traceln("FlowRules for ueId:", ue.Supi, "snssai:", util.SnssaiModelsToHex(*request.SliceInfo))
for i, flowRule := range flowRulesInterface {
logger.SmPolicyLog.Tracef("flowRule %d: %s\n", i, openapi.MarshToJsonString(flowRule))
precedence := int32(flowRule["precedence"].(float64))
pccRule := util.CreatePccRule(smPolicyData.PccRuleIdGenerator, precedence, []models.FlowInformation{
{
FlowDescription: flowRule["filter"].(string),
FlowDirection: models.FlowDirectionRm_BIDIRECTIONAL,
},
}, "")
qfi := strconv.Itoa(int(flowRule["qfi"].(float64)))
util.SetPccRuleRelatedByQFI(&decision, pccRule, qfi)
smPolicyData.PccRuleIdGenerator++
if val, ok := flowRule["filter"].(string); ok {
tokens := strings.Split(val, " ")

FlowDescription := flowdesc.NewIPFilterRule()
FlowDescription.Action = flowdesc.Permit
FlowDescription.Dir = flowdesc.Out
FlowDescription.Src = tokens[0]
FlowDescription.Dst = "assigned" // Hardcode destination (TS 29.212 5.4.2)

var err1, err2 error
portLowerBound := 1
portUpperBound := 65535
if len(tokens) > 1 {
portLowerBound, err1 = strconv.Atoi(strings.Split(tokens[1], "-")[0])
portUpperBound, err2 = strconv.Atoi(strings.Split(tokens[1], "-")[1])
}

if err1 != nil || err2 != nil {
logger.SmPolicyLog.Warnln("Wrong Port format in IP Filter's setting:", tokens[1], ", set to 1-65535")
}

if !(portLowerBound <= 1 && portUpperBound >= 65535) { // Port range need to be assigned
FlowDescription.SrcPorts = flowdesc.PortRanges{
flowdesc.PortRange{
Start: uint16(portLowerBound),
End: uint16(portUpperBound),
},
}
}

var FlowDescriptionStr string
FlowDescriptionStr, err = flowdesc.Encode(FlowDescription)
if err != nil {
logger.SmPolicyLog.Errorf("Error occurs when encoding flow despcription: %s\n", err)
}

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 := pcf_context.GetSelf().RatingGroupIdGenerator.Allocate()
if err1 != nil {
logger.SmPolicyLog.Error("rating group allocate error")
problemDetails := util.GetProblemDetail("rating group allocate error", util.ERROR_IDGENERATOR)
return nil, nil, &problemDetails
}
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)
}

chargingBsonM := bson.M{
"ratingGroup": chgData.RatingGroup,
}
if _, err = mongoapi.RestfulAPIPutOne(chargingDataColl, filterCharging, chargingBsonM, 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++
}
}
qosRef := strconv.Itoa(int(flowRule["qosRef"].(float64)))
util.SetPccRuleRelatedByQosRef(&decision, pccRule, qosRef)
smPolicyData.PccRuleIdGenerator++
}
}

requestSuppFeat, err := openapi.NewSupportedFeature(request.SuppFeat)
Expand Down
14 changes: 3 additions & 11 deletions internal/util/pcc_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ func CreateDefaultPccRules(id int32) *models.PccRule {
PacketFilterUsage: true,
PackFiltId: "PackFiltId-0",
},
{
FlowDescription: "permit out ip from any to assigned",
FlowDirection: models.FlowDirectionRm_DOWNLINK,
PacketFilterUsage: true,
PackFiltId: "PackFiltId-1",
},
}
return CreatePccRule(id, 255, flowInfo, "")
}
Expand Down Expand Up @@ -176,13 +170,11 @@ func GetPccRuleByFlowInfos(pccRules map[string]*models.PccRule, flowInfos []mode
return nil
}

func SetPccRuleRelatedByQFI(decision *models.SmPolicyDecision, pccRule *models.PccRule, qfi string) {
if decision.QosDecs == nil {
return
} else if qosFlow := decision.QosDecs[qfi]; qosFlow == nil {
func SetPccRuleRelatedByQosRef(decision *models.SmPolicyDecision, pccRule *models.PccRule, qfi string) {
if decision.QosDecs == nil || decision.QosDecs[qfi] == nil {
return
}
pccRule.RefQosData = []string{qfi}
pccRule.RefQosData = append(pccRule.RefQosData, qfi)
if decision.PccRules == nil {
decision.PccRules = make(map[string]*models.PccRule)
}
Expand Down
1 change: 1 addition & 0 deletions internal/util/pcf_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var (
UNAUTHORIZED_SPONSORED_DATA_CONNECTIVITY = "UNAUTHORIZED_SPONSORED_DATA_CONNECTIVITY"
PDU_SESSION_NOT_AVAILABLE = "PDU_SESSION_NOT_AVAILABLE"
APPLICATION_SESSION_CONTEXT_NOT_FOUND = "APPLICATION_SESSION_CONTEXT_NOT_FOUND"
ERROR_IDGENERATOR = "ERROR_IDGENERATOR"
PcpErrHttpStatusMap = map[string]int32{
ERROR_REQUEST_PARAMETERS: http.StatusBadRequest,
USER_UNKNOWN: http.StatusBadRequest,
Expand Down

0 comments on commit 55b720d

Please sign in to comment.