本文分析 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)
}
}