feat: update QER when PCF notifies AMBR change#191
Conversation
When SMF receives SmPolicyUpdateNotification from PCF with updated session-AMBR, propagate the change to UPF via PFCP SessionModification by updating the corresponding QER (MBR_UL/MBR_DL). Previously, PCF AMBR notification was processed by SMF but the QER was never updated, so UPF rate limiting remained at the original value. Changes: - sm_context_policy.go: add updateAMBRQer() to find and update AMBR QER - build.go: add qerToUpdateQER() and handle RULE_UPDATE in BuildPfcpSessionModificationRequest()
There was a problem hiding this comment.
Pull request overview
This PR adds end-to-end support for enforcing PCF-triggered session-AMBR changes by updating UPF rate limiting via PFCP Session Modification (UpdateQER), aligning SMF behavior with 3GPP TS 29.512 expectations.
Changes:
- Add QER→UpdateQER conversion and include
UpdateQERIEs in PFCP SessionModificationRequest when QER state isRULE_UPDATE. - Update SM policy application flow to detect session-AMBR changes and mark the AMBR QER(s) for PFCP update.
- Introduce AMBR QER update helper that walks activated datapaths and updates QER MBR values.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
internal/pfcp/message/build.go |
Adds qerToUpdateQER() and emits UpdateQER during PFCP session modification when QER state is RULE_UPDATE. |
internal/context/sm_context_policy.go |
Parses updated session-AMBR from the selected session rule and updates/marks AMBR QERs as RULE_UPDATE for the next PFCP modification. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| visited := make(map[uint32]bool) // avoid updating the same QER twice | ||
| for _, dataPath := range c.Tunnel.DataPathPool { | ||
| if !dataPath.Activated { | ||
| continue | ||
| } | ||
| for node := dataPath.FirstDPNode; node != nil; node = node.Next() { | ||
| qerID, ok := c.AMBRQerMap[node.UPF.uuid] | ||
| if !ok { | ||
| continue | ||
| } | ||
| if visited[qerID] { | ||
| continue | ||
| } | ||
| visited[qerID] = true |
There was a problem hiding this comment.
visited is keyed only by qerID, but QER IDs are allocated per-UPF and can collide across different UPFs (e.g., both UPF1 and UPF2 may have QERID=1). This can cause AMBR QER updates to be skipped on subsequent UPFs. Key the visited map by UPF UUID (or a composite key of UPF UUID + QERID), or remove visited and instead ensure each UPF is updated once per call.
| qer.MBR = &pfcpType.MBR{ | ||
| ULMBR: ulMbrkbps, | ||
| DLMBR: dlMbrkbps, | ||
| } | ||
| qer.State = RULE_UPDATE | ||
| c.Log.Infof("updateAMBRQer: QER[%d] updated to UL=%d DL=%d kbps, state=RULE_UPDATE", | ||
| qerID, ulMbrkbps, dlMbrkbps) | ||
| } |
There was a problem hiding this comment.
updateAMBRQer always overwrites qer.MBR and forces qer.State = RULE_UPDATE even when the requested UL/DL MBR values are unchanged. This can trigger unnecessary PFCP SessionModificationRequests (and extra UpdateQER calls) on every policy update. Consider comparing the existing qer.MBR values first and only marking RULE_UPDATE (and logging) when UL/DL actually change.
| for _, qer := range qerList { | ||
| if qer.State == context.RULE_INITIAL { | ||
| switch qer.State { | ||
| case context.RULE_INITIAL: | ||
| msg.CreateQER = append(msg.CreateQER, qerToCreateQER(qer)) | ||
| case context.RULE_UPDATE: | ||
| msg.UpdateQER = append(msg.UpdateQER, qerToUpdateQER(qer)) | ||
| } | ||
| qer.State = context.RULE_CREATE | ||
| } |
There was a problem hiding this comment.
The new RULE_UPDATE handling for QERs is not covered by the existing PFCP message builder unit tests. Add a test case that sets a QER to RULE_UPDATE with a non-nil MBR, calls BuildPfcpSessionModificationRequest, and asserts that UpdateQER contains exactly one entry with the expected QERID/MBR fields (and that the QER state transitions to RULE_CREATE).
Background
Per 3GPP TS 29.512, PCF may send SmPolicyUpdateNotification to update
session-AMBR at any time during an active PDU session. SMF should enforce
this by updating UPF rate limiting via PFCP.
Problem
SMF processed the PCF notification and updated internal context, but did
not send PFCP SessionModificationRequest to update the QER (MBR_UL/MBR_DL).
As a result, UPF continued enforcing the original AMBR value.
Solution
updateAMBRQer(): finds the AMBR QER in the SMF context and marks itfor update when PCF notifies a new session-AMBR
qerToUpdateQER(): converts a QER to PFCP UpdateQER IEBuildPfcpSessionModificationRequest(): handles RULE_UPDATE to includeUpdateQER IE in the modification request
Testing
Tested with free5gc v4.2.0 + UERANSIM + vendor PCF:
Related Fixes
This PR requires the following additional fixes to work end-to-end:
free5gc/util —
httpwrapper/httpwrapper.go:IdleTimeoutis setto
1ms, causing the h2c connection to be closed before PCF can deliverthe notify callback. Should be changed to a reasonable value (e.g.
30or0).free5gc/gtp5g —
src/genl/genl_qer.c: missingrcu_read_unlock()and
rtnl_unlock()in the QER UPDATE path causes kernel deadlock on thesecond
UpdateQERcall, making UPF completely unresponsive. A separatebug fix PR will be submitted to free5gc/gtp5g.