Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to add IMS call support in PCF policy authorization by adjusting how PCC/QoS resources are created/selected for media components and by changing PCC/QoS/TC/packet-filter identifier formats.
Changes:
- Switch PCC rule / QoS / TC / packet-filter IDs to plain numeric strings.
- Update policy authorization handling to combine MediaSubComponents into a single PCC rule path and send SMF update notifications using a filtered SmPolicyDecision.
- Add extensive new logging around AppSessionContext processing and PCC/QoS/TC creation.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| util/pcc_rule.go | Changes PCC/QoS/TC/packet-filter identifier formatting to numeric strings. |
| producer/policyauthorization.go | Adds combined MediaSubComponent handling, filtered SMF notifications, and extensive new logs. |
| context/ue.go | Modifies SM policy lookup by IPv4 identifiers and adds logging. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if n, err := strconv.Atoi(id); err == nil && int32(n) > maxID { | ||
| maxID = int32(n) | ||
| } |
There was a problem hiding this comment.
getMaxPccRuleIdNum only considers PCC rule map keys that are pure integers. Before this PR, PCC rule IDs were formatted with a prefix (e.g., PccRuleId-<n>), so after an upgrade existing in-memory decisions may still contain non-numeric keys; those will be ignored here and can cause ID generator collisions. Consider supporting both formats (strip known prefixes before Atoi) or ensuring existing decisions are normalized before calling this.
| if n, err := strconv.Atoi(id); err == nil && int32(n) > maxID { | |
| maxID = int32(n) | |
| } | |
| // First, try to parse the ID as a plain integer. | |
| if n, err := strconv.Atoi(id); err == nil { | |
| if int32(n) > maxID { | |
| maxID = int32(n) | |
| } | |
| continue | |
| } | |
| // Fallback: support legacy IDs formatted as "PccRuleId-<n>". | |
| const legacyPrefix = "PccRuleId-" | |
| if strings.HasPrefix(id, legacyPrefix) { | |
| numPart := strings.TrimPrefix(id, legacyPrefix) | |
| if n, err := strconv.Atoi(numPart); err == nil && int32(n) > maxID { | |
| maxID = int32(n) | |
| } | |
| } |
| @@ -441,9 +442,12 @@ func (ue *UeContext) SMPolicyFindByIdentifiersIpv4( | |||
| if sNssai != nil && !reflect.DeepEqual(sNssai, policyContext.SliceInfo) { | |||
| continue | |||
| } | |||
| return smPolicy | |||
| } | |||
| logger.CtxLog.Infof("SMPolicy[%s] matched for IPv4: %s", id, v4) | |||
| return smPolicy | |||
There was a problem hiding this comment.
SMPolicyFindByIdentifiersIpv4 returns the first SM policy even when the IPv4 (or other identifiers) do not match, because the log+return are outside the if policyContext.Ipv4Address == v4 block. This will break session binding and can associate requests with the wrong PDU session. Move the log+return inside the matching block (after all identifier checks).
| // Get pcc rule Identity(PccRuleId-%d) | ||
| func GetPccRuleId(id int32) string { | ||
| return fmt.Sprintf("PccRuleId-%d", id) | ||
| return fmt.Sprintf("%d", id) | ||
| } | ||
|
|
||
| // Get qos Identity(QosId-%d) | ||
| func GetQosId(id int32) string { | ||
| return fmt.Sprintf("QosId-%d", id) | ||
| return fmt.Sprintf("%d", id) | ||
| } | ||
|
|
||
| // Get Traffic Control Identity(TcId-%d) | ||
| func GetTcId(id int32) string { | ||
| return fmt.Sprintf("TcId-%d", id) | ||
| return fmt.Sprintf("%d", id) |
There was a problem hiding this comment.
These comments still describe prefixed IDs (e.g., PccRuleId-%d, QosId-%d, TcId-%d) but the functions now return plain numeric strings. Please update the comments so they match the actual ID formats to avoid confusing future callers/debugging.
| } | ||
|
|
||
| // Find pccRule by AfAppId, otherwise create a new pcc rule | ||
| logger.PolicyAuthorizationlog.Debugf("AfAppId", appID) |
There was a problem hiding this comment.
This Debugf call has an extra argument (appID) but the format string has no formatting verb, which will result in %!(EXTRA ...) output in logs and makes troubleshooting harder. Include %s (or similar) in the format string, or remove the unused argument.
| logger.PolicyAuthorizationlog.Debugf("AfAppId", appID) | |
| logger.PolicyAuthorizationlog.Debugf("AfAppId: %s", appID) |
| SmPolicyDecision: filteredDecision, | ||
| } | ||
| decisionJSON, err := json.MarshalIndent(filteredDecision, "", " ") |
There was a problem hiding this comment.
updateSMpolicy can be set due to changes outside PCC/QoS/TC (e.g., PolicyCtrlReqTriggers updates when AF events are subscribed), but the notification sends only filteredDecision (PCC/QoS/TC) to the SMF. This can silently drop trigger updates (and any other updated decision fields). Consider sending smPolicy.PolicyDecision, or extend filteredDecision to include all updated fields (at minimum PolicyCtrlReqTriggers).
| SmPolicyDecision: filteredDecision, | |
| } | |
| decisionJSON, err := json.MarshalIndent(filteredDecision, "", " ") | |
| SmPolicyDecision: smPolicy.PolicyDecision, | |
| } | |
| decisionJSON, err := json.MarshalIndent(smPolicy.PolicyDecision, "", " ") |
| logger.PolicyAuthorizationlog.Infof("AppSession Context: %+v", appSessCtx) | ||
| logger.PolicyAuthorizationlog.Infof("SM Policy associated with AppSession [%s]: %+v", appSessID, smPolicy) |
There was a problem hiding this comment.
These INFO logs dump entire appSessCtx / smPolicy structs with %+v, which is likely to be extremely large and may include identifiers/policy data that shouldn't be in INFO logs. This also adds avoidable CPU/allocations on every request. Prefer DEBUG level and/or log a small curated summary.
| logger.PolicyAuthorizationlog.Infof("AppSession Context: %+v", appSessCtx) | |
| logger.PolicyAuthorizationlog.Infof("SM Policy associated with AppSession [%s]: %+v", appSessID, smPolicy) | |
| logger.PolicyAuthorizationlog.Debugf("AppSession Context: %+v", appSessCtx) | |
| logger.PolicyAuthorizationlog.Debugf("SM Policy associated with AppSession [%s]: %+v", appSessID, smPolicy) |
| if err != nil { | ||
| logger.PolicyAuthorizationlog.Errorf("Failed to marshal SmPolicyDecision: %+v", err) | ||
| } else { | ||
| logger.PolicyAuthorizationlog.Infof("SmPolicyDecision data: %s", string(decisionJSON)) |
There was a problem hiding this comment.
Marshaling and logging the entire SmPolicyDecision as pretty-printed JSON at INFO level can be very expensive and will generate large logs for normal traffic. Consider gating this behind DEBUG (or a feature flag) and logging only identifiers/counts at INFO.
| logger.PolicyAuthorizationlog.Infof("SmPolicyDecision data: %s", string(decisionJSON)) | |
| logger.PolicyAuthorizationlog.Debugf("SmPolicyDecision data: %s", string(decisionJSON)) |
| // Get Packet Filter Identity(PackFiltId-%d) | ||
| func GetPackFiltId(id int32) string { | ||
| return fmt.Sprintf("PackFiltId-%d", id) | ||
| // return fmt.Sprintf("PackFiltId-%d", id) |
There was a problem hiding this comment.
GetPackFiltId still contains a commented-out return of the old ID format. Keeping dead/commented code here makes the intended format unclear; remove the commented line now that numeric IDs are the chosen format.
| // return fmt.Sprintf("PackFiltId-%d", id) |
| ascReqData := appSessCtx.AscReqData | ||
| pcfSelf := pcf_context.PCF_Self() | ||
|
|
||
| logger.PolicyAuthorizationlog.Infof("Received App Session Context Request: %+v", ascReqData) |
There was a problem hiding this comment.
Logging the full App Session Context request with %+v at INFO level can leak sensitive UE/session identifiers and can be very verbose in production. Consider lowering this to DEBUG and/or logging only key fields (e.g., Supi/IPs/DNN/MedComp count) after redaction.
| logger.PolicyAuthorizationlog.Infof("Received App Session Context Request: %+v", ascReqData) | |
| // Log only key, partially redacted fields to avoid leaking full session context details | |
| supi := ascReqData.Supi | |
| if len(supi) > 5 { | |
| supi = supi[:5] + "..." | |
| } | |
| medCompCount := 0 | |
| if ascReqData.MedComponents != nil { | |
| medCompCount = len(ascReqData.MedComponents) | |
| } | |
| logger.PolicyAuthorizationlog.Infof( | |
| "Received App Session Context Request: Supi=%s UeIpv4=%s UeIpv6=%s Dnn=%s MedCompCount=%d", | |
| supi, ascReqData.UeIpv4, ascReqData.UeIpv6, ascReqData.Dnn, medCompCount, | |
| ) |
| smPolicy.PackFiltIdGenarator++ | ||
| } | ||
| pccRule.FlowInfos = flowInfos | ||
| tcData := util.CreateTcData(smPolicy.PccRuleIdGenarator, "", medSubComps[0].FStatus) |
There was a problem hiding this comment.
medSubComps[0] is accessed to get FStatus when creating tcData, but medSubComps can be an empty slice (e.g., request contains an empty MedSubComps map). This will panic. Add an explicit length check and return a ProblemDetails error (or derive flow status from medComp / default) when no sub-components are provided.
| tcData := util.CreateTcData(smPolicy.PccRuleIdGenarator, "", medSubComps[0].FStatus) | |
| // Derive flow status for TcData; avoid panicking on empty medSubComps slice. | |
| var fStatus models.FlowStatus | |
| if len(medSubComps) > 0 { | |
| fStatus = medSubComps[0].FStatus | |
| } else { | |
| // Fall back to a sensible default when no media sub-components are provided. | |
| fStatus = models.FlowStatus_ENABLED | |
| } | |
| tcData := util.CreateTcData(smPolicy.PccRuleIdGenarator, "", fStatus) |
This PR adds IMS call support for UE in PCF.
Note: - During testing with COTS UEs, IMS PDU session release behavior was observed from UE and needs further analysis.