本文分析 free5gc Network Triggered Service Request 網絡觸發的業務請求流程。當網絡側需要向 UE 發出信號時(例如向 UE 發出 N1 信令,移動端應用等推送(比如視頻,微信),對 PDU 會話的用戶平面連接激活以傳遞移動端用戶數據),將使用此過程。
1. UPF 向 SMF 發送 PFCP Sesssion Report request
UP 功能應使用 PFCP Session Report 流程將與 PFCP 會話有關的信息報告給 CP 功能。
1.1 SMF 處理 PFCP Sesssion Report request
func HandlePfcpSessionReportRequest(msg *pfcpUdp.Message) {
req := msg.PfcpMessage.Body.(pfcp.PFCPSessionReportRequest)
SEID := msg.PfcpMessage.Header.SEID
smContext := smf_context.GetSMContextBySEID(SEID)
seqFromUPF := msg.PfcpMessage.Header.SequenceNumber
目前只實現類型爲 DLDR(Downlink Data Report)
if req.ReportType.Dldr {
downlinkDataReport := req.DownlinkDataReport
pdrID := downlinkDataReport.PDRID.RuleId
if downlinkDataReport.DownlinkDataServiceInformation != nil {
logger.PfcpLog.Warnf("PFCP Session Report Request DownlinkDataServiceInformation handling is not implemented")
}
如果 SM 上下文的 PDR ID 相同則發送 PFCP Session Report response
ANUPF := smContext.Tunnel.DataPathPool.GetDefaultPath().FirstDPNode
DLPDR := ANUPF.DownLinkTunnel.PDR
if DLPDR.PDRID == pdrID {
// TS 23.502 4.2.3.3 2b. Send Data Notification Ack, SMF->UPF
cause.CauseValue = pfcpType.CauseRequestAccepted
// TODO fix: SEID should be the value sent by UPF but now the SEID value is from sm context
pfcp_message.SendPfcpSessionReportResponse(msg.RemoteAddr, cause, seqFromUPF, SEID)
BuildPDUSessionResourceSetupRequestTransfer 函數創建 NGAP 消息,包括:
- UL-NGU-UP-TNLInformation
- PDUSessionType
- QosFlowSetupRequestList
SMF 向 AMF 發送 N1N2 請求 /ue-contexts/{ueContextId}/n1-n2-messages
2. AMF 處理 N1N2 消息
// TS23502 4.2.3.3, 4.2.4.3, 4.3.2.2, 4.3.2.3, 4.3.3.2, 4.3.7
func HandleN1N2MessageTransferRequest(request *http_wrapper.Request) *http_wrapper.Response {
logger.ProducerLog.Infof("Handle N1N2 Message Transfer Request")
n1n2MessageTransferRequest := request.Body.(models.N1N2MessageTransferRequest)
ueContextID := request.Params["ueContextId"]
reqUri := request.Params["reqUri"]
2.1 N1N2MessageTransferProcedure 函數
// There are 4 possible return value for this function:
// - n1n2MessageTransferRspData: if AMF handle N1N2MessageTransfer Request successfully.
// - locationHeader: if response status code is 202, then it will return a non-empty string location header for
// response
// - problemDetails: if AMF reject the request due to application error, e.g. UE context not found.
// - TransferErr: if AMF reject the request due to procedure error, e.g. UE has an ongoing procedure.
// see TS 29.518 6.1.3.5.3.1 for more details.
func N1N2MessageTransferProcedure(ueContextID string, reqUri string,
n1n2MessageTransferRequest models.N1N2MessageTransferRequest) (
n1n2MessageTransferRspData *models.N1N2MessageTransferRspData,
locationHeader string, problemDetails *models.ProblemDetails,
transferErr *models.N1N2MessageTransferError) {
2.1.1 UE 處於 CM-IDLE 狀態
將 onGoing 設置爲 Paging,BuildPaging 創建 NGAP 消息,向 RAN 發送
// Case A (UE is CM-IDLE in 3GPP access and the associated access type is 3GPP access)
// in subclause 5.2.2.3.1.2 of TS29518
if anType == models.AccessType__3_GPP_ACCESS {
if requestData.SkipInd && n2Info == nil {
n1n2MessageTransferRspData.Cause = models.N1N2MessageTransferCause_N1_MSG_NOT_TRANSFERRED
} else {
n1n2MessageTransferRspData.Cause = models.N1N2MessageTransferCause_ATTEMPTING_TO_REACH_UE
message := context.N1N2Message{
Request: n1n2MessageTransferRequest,
Status: n1n2MessageTransferRspData.Cause,
ResourceUri: locationHeader,
}
ue.N1N2Message = &message
onGoing.Procedure = context.OnGoingProcedurePaging
onGoing.Ppi = requestData.Ppi
if onGoing.Ppi != 0 {
pagingPriority = new(ngapType.PagingPriority)
pagingPriority.Value = aper.Enumerated(onGoing.Ppi)
}
pkg, err := ngap_message.BuildPaging(ue, pagingPriority, false)
if err != nil {
logger.NgapLog.Errorf("Build Paging failed : %s", err.Error())
return n1n2MessageTransferRspData, locationHeader, problemDetails, transferErr
}
ngap_message.SendPaging(ue, pkg)
}
// TODO: WAITING_FOR_ASYNCHRONOUS_TRANSFER
return n1n2MessageTransferRspData, locationHeader, problemDetails, transferErr
}
AMF 回覆 SMF N1N2 消息,cause 設置爲 ATTEMPTING_TO_REACH_UE
n1n2MessageTransferRspData, locationHeader, problemDetails, transferErr := N1N2MessageTransferProcedure(
ueContextID, reqUri, n1n2MessageTransferRequest)
if n1n2MessageTransferRspData != nil {
switch n1n2MessageTransferRspData.Cause {
case models.N1N2MessageTransferCause_N1_MSG_NOT_TRANSFERRED:
fallthrough
case models.N1N2MessageTransferCause_N1_N2_TRANSFER_INITIATED:
return http_wrapper.NewResponse(http.StatusOK, nil, n1n2MessageTransferRspData)
case models.N1N2MessageTransferCause_ATTEMPTING_TO_REACH_UE:
headers := http.Header{
"Location": {locationHeader},
}
return http_wrapper.NewResponse(http.StatusAccepted, headers, n1n2MessageTransferRspData)
}
}