Free5GC源码研究(11) - SMF研究(下)

news/2024/11/29 12:59:38/文章来源:https://www.cnblogs.com/zrq96/p/18546685

前文已经研究过SMF的概念和Nsmf_PDUSession的创建和释放,本文继续研究其更新,以及SMF的其他服务。

SMF实现

Nsmf_PDUSession

SMContext的更新是SMF中的一个核心功能,负责处理PDU会话的各种状态更新和转换。它需要处理多种不同类型的更新请求,并确保会话状态的正确转换。理解与之相关的HandlePDUSessionSMContextUpdate函数也需要一些前置知识,比如AMF与SMF之间的N1接口和N2接口,各种状态机的状态转移等。

N1、N2接口

回顾5G系统的架构设计,可以发现用户设备UE与核心网控制面的的连接只有一个,那就是N1接口,而接入网RAN与核心网控制面的连接也只有一个,那就是N2,且N1与N2接口在控制面的一端都是AMF。也就是说,UE与RAN任何与控制面的交流都智能通过AMF,如果希望与SMF或其他NF交互,就只能委托AMF将消息转发出去,而其他NF的回复也需要靠AMF中继。

UE会通过N1接口把对PDU Session的管理请求发给SMF,RAN也会通过N2接口把对PDU session资源的管理、无线资源的管理、移动性管理、UE上下文管理等命令发送给SMF。

为什么不直接让UE和RAN与SMF直接通信,非要让AMF中继?我认为这是出于两个考虑

  1. 安全性:AMF负责用户认证,通过AMF中继可以确保所有消息都经过安全检查,防止未经授权的访问和攻击
  2. 移动性:当UE移动时,可能需要切换到不同的SMF,当然也需要切换不同AMF,但AMF本身就负责追踪UE的位置和移动性管理。让AMF代为转发消息就可以利用AMF本身的移动性管理功能,而无需让SMF和其他NF也一同追踪UE的位置,避免不必要的夫复杂性。

PDU Session状态机

根据TS24.501,一个PDU Session的全生命周期可以有四个状态:InactiveActiveInactivePending、和ModificationPending,他们之间相互转换的关系和过程由下面的状态机图表示:

img

在free5gc中对于PDU Session的状态定义与标准文档有所不同

// https://github.com/free5gc/smf/blob/v1.2.5/internal/context/sm_context.go#L69
type SMContextState uint32
const (InActive SMContextState = iotaActivePendingActiveInActivePendingModificationPendingPFCPModification
)

ActivePending不是在TS24.501中定义的标准PDU Session状态之一。在UE侧倒是有这么一个状态,但由于PDU Session的建立虽由UE发起,却由核心网主导,所以网络端不需要ActivePending这个状态。我认为这是free5gc实现中的一个内部状态,用来方便程序编写。

同理,PFCPModification这个状态也不是标准状态,而是free5gc实现中的一个内部状态,用于处理PFCP Session修改的中间态。在free5gc的实现中,当SMF需要修改UPF中的PFCP Session(例如更新PDR、FAR规则)时,会将PDU Session状态设置为``PFCPModification`。这个状态表明SMF正在等待UPF对PFCP Session Modification请求的响应。根据UPF的响应结果,会话状态会转移到:

  • 如果修改成功(SessionUpdateSuccess)-> Active
  • 如果修改失败(SessionUpdateFailed)-> Active(同时返回错误)
  • 如果是释放操作且成功(SessionReleaseSuccess)-> InActivePending
  • 如果是释放操作但失败(SessionReleaseFailed)-> Active(同时返回错误)
    这个状态的引入主要是为了处理PFCP协议交互的异步特性。虽然不是标准定义的状态,但它帮助SMF更好地管理与UPF之间的PFCP会话修改过程。

Handover状态机

Handover指的是UE因为位置移动触发的接入网的切换,以及PDU Session从一个RAN移交(Handover)到另一个RAN过程中的状态变化

  • NONE:初始状态,表示PDU会话当前没有进行任何切换操作。每个新建立的PDU会话都处于这个状态。
  • PREPARING: 旧的RAN发现需要进行切换,SMF准备建立到目标RAN的资源
  • PREPARED: 目标RAN的资源已经准备好,SMF处理切换请求确认信息,如果配置了间接转发(Indirect Forwarding),会建立数据转发通道
  • COMPLETED: UE已经成功切换到目标接入网,清理原有路径,如果有间接转发通道,这时也会被释放
  • CANCELLED: 切换由于各种原因被取消,此时PDU session回退到原来的RAN继续服务,清理为切换预留的资源

我没有在标准文档中找到一张描述Handover的状态机图示,所以自己画了一个:

stateDiagram-v2 direction LR[*] --> NONENONE --> PREPARING: 开始切换准备PREPARING --> PREPARED: 准备完成PREPARED --> COMPLETED: 切换成功PREPARING --> CANCELLED: 切换取消PREPARED --> CANCELLED: 切换取消COMPLETED --> [*]CANCELLED --> [*]

UP Connection 状态机

SMF在管理PDU Session的同时,也还要维护相应的与用户面的数据连接。UpCnxState(User Plane Connection State)就是用于表示用户面连接状态的类型

// https://github.com/free5gc/openapi/blob/v1.0.8/models/model_up_cnx_state.go
type UpCnxState string
const (UpCnxState_ACTIVATED   UpCnxState = "ACTIVATED"UpCnxState_DEACTIVATED UpCnxState = "DEACTIVATED"UpCnxState_ACTIVATING  UpCnxState = "ACTIVATING"
)

可以看到这只是一个仅包含3个状态的简单模型,不过新版的TS29.502@v18.08引入一个新的状态SUSPENDED

img

所以最新的UP连接状态机状态转移图变成了这个样子(忽略掉SISPENDED相关的路径,就是代码中实现的状态转移逻辑):

stateDiagram-v2 direction LR[*] --> DEACTIVATEDDEACTIVATED --> ACTIVATING: 开始激活ACTIVATING --> ACTIVATED: 激活成功ACTIVATED --> DEACTIVATED: 停用ACTIVATED --> SUSPENDED: 挂起SUSPENDED --> ACTIVATING: 恢复

掌握以上信息,基本就能理解下面(简化版)的HandlePDUSessionSMContextUpdate源代码了

// https://github.com/free5gc/smf/blob/v1.2.5/internal/sbi/processor/pdu_session.go#L245
func (p *Processor) HandlePDUSessionSMContextUpdate(c *gin.Context,body models.UpdateSmContextRequest,smContextRef string,
) {smContext := smf_context.GetSMContextByRef(smContextRef)var sendPFCPModification boolvar pfcpResponseStatus smf_context.PFCPSessionResponseStatusvar response models.UpdateSmContextResponseresponse.JsonData = new(models.SmContextUpdatedData)smContextUpdateData := body.JsonData// 处理N1接口的消息(AMF转发)if body.BinaryDataN1SmMessage != nil {m := nas.NewMessage()switch m.GsmHeader.GetMessageType() {case nas.MsgTypePDUSessionReleaseRequest:// release PDU SessionsmContext.SetState(smf_context.PFCPModification)pfcpResponseStatus = releaseSession(smContext)case nas.MsgTypePDUSessionReleaseComplete:// PDU Session released, wrap up thingssmContext.SetState(smf_context.InActive)response.JsonData.UpCnxState = models.UpCnxState_DEACTIVATEDsmContext.StopT3592()// If CN tunnel resource is released, shouldif smContext.Tunnel.ANInformation.IPAddress == nil {p.RemoveSMContextFromAllNF(smContext, true)}case nas.MsgTypePDUSessionModificationRequest:// modify PDU sessionp.HandlePDUSessionModificationRequest(smContext, m.PDUSessionModificationRequest)p.sendGSMPDUSessionModificationCommand(smContext, buf)smf_context.BuildPDUSessionResourceModifyRequestTransfer(smContext)c.Render(http.StatusOK, openapi.MultipartRelatedRender{Data: response})returncase nas.MsgTypePDUSessionModificationComplete:smContext.StopT3591()case nas.MsgTypePDUSessionModificationReject:smContext.StopT3591()}}/* ================================================================ */// 变更与用户面连接的状态(UE与UPF之间的数据传输通道)switch smContextUpdateData.UpCnxState {case models.UpCnxState_ACTIVATING:smContext.SetState(smf_context.ModificationPending)n2Buf, err = smf_context.BuildPDUSessionResourceSetupRequestTransfer(smContext)smContext.UpCnxState = models.UpCnxState_ACTIVATINGcase models.UpCnxState_DEACTIVATED:smContext.SetState(smf_context.ModificationPending)smContext.UpCnxState = body.JsonData.UpCnxState// Set FAR and AN, N3 Release Info// 修改FAR规则,停止转发,开启缓存farList = []*smf_context.FAR{}for _, dataPath := range smContext.Tunnel.DataPathPool {ANUPF := dataPath.FirstDPNodeDLPDR := ANUPF.DownLinkTunnel.PDRif DLPDR == nil {smContext.Log.Warnf("Access network resource is released")} else {DLPDR.FAR.State = smf_context.RULE_UPDATEDLPDR.FAR.ApplyAction.Forw = falseDLPDR.FAR.ApplyAction.Buff = trueDLPDR.FAR.ApplyAction.Nocp = truefarList = append(farList, DLPDR.FAR)sendPFCPModification = truesmContext.SetState(smf_context.PFCPModification)}}}/* ================================================================ */// 处理N2接口的消息(AMF转发)switch smContextUpdateData.N2SmInfoType {// setup PDU session resourcecase models.N2SmInfoType_PDU_RES_SETUP_RSP:smContext.SetState(smf_context.ModificationPending)pdrList = []*smf_context.PDR{}farList = []*smf_context.FAR{}for _, dataPath := range tunnel.DataPathPool {if dataPath.Activated {ANUPF := dataPath.FirstDPNodeDLPDR := ANUPF.DownLinkTunnel.PDRDLPDR.FAR.ApplyAction = pfcpType.ApplyAction{Buff: false,Drop: false,Dupl: false,Forw: true,Nocp: false,}DLPDR.FAR.ForwardingParameters = &smf_context.ForwardingParameters{DestinationInterface: pfcpType.DestinationInterface{InterfaceValue: pfcpType.DestinationInterfaceAccess,},NetworkInstance: &pfcpType.NetworkInstance{NetworkInstance: smContext.Dnn,FQDNEncoding:    factory.SmfConfig.Configuration.NwInstFqdnEncoding,},}DLPDR.State = smf_context.RULE_UPDATEDLPDR.FAR.State = smf_context.RULE_UPDATEpdrList = append(pdrList, DLPDR)farList = append(farList, DLPDR.FAR)}}smf_context.HandlePDUSessionResourceSetupResponseTransfer(body.BinaryDataN2SmInformation, smContext)sendPFCPModification = truesmContext.SetState(smf_context.PFCPModification)case models.N2SmInfoType_PDU_RES_SETUP_FAIL:smf_context.HandlePDUSessionResourceSetupUnsuccessfulTransfer(body.BinaryDataN2SmInformation, smContext)case models.N2SmInfoType_PDU_RES_MOD_RSP:smf_context.HandlePDUSessionResourceModifyResponseTransfer(body.BinaryDataN2SmInformation, smContext)// release PDU session resourcecase models.N2SmInfoType_PDU_RES_REL_RSP:// remove an tunnel infosmContext.Tunnel.ANInformation = struct {IPAddress net.IPTEID      uint32}{nil, 0}p.RemoveSMContextFromAllNF(smContext, true)case models.N2SmInfoType_PATH_SWITCH_REQ:smf_context.HandlePathSwitchRequestTransfer(body.BinaryDataN2SmInformation, smContext)smf_context.BuildPathSwitchRequestAcknowledgeTransfer(smContext)for _, dataPath := range tunnel.DataPathPool {if dataPath.Activated {ANUPF := dataPath.FirstDPNodeDLPDR := ANUPF.DownLinkTunnel.PDRpdrList = append(pdrList, DLPDR)farList = append(farList, DLPDR.FAR)}}smContext.SetState(smf_context.PFCPModification)case models.N2SmInfoType_PATH_SWITCH_SETUP_FAIL:smContext.SetState(smf_context.ModificationPending)smf_context.HandlePathSwitchRequestSetupFailedTransfer(body.BinaryDataN2SmInformation, smContext)case models.N2SmInfoType_HANDOVER_REQUIRED:smContext.SetState(smf_context.ModificationPending)response.JsonData.N2SmInfo = &models.RefToBinaryData{ContentId: "Handover"}}/* ================================================================ */// 处理HoState(Handover State,指UE在不同基站/接入网之间迁移过程中的状态)switch smContextUpdateData.HoState {case models.HoState_PREPARING:smContext.SetState(smf_context.ModificationPending)smContext.HoState = models.HoState_PREPARINGsmf_context.HandleHandoverRequiredTransfer(body.BinaryDataN2SmInformation, smContext)smf_context.BuildPDUSessionResourceSetupRequestTransfer(smContext)response.JsonData.HoState = models.HoState_PREPARINGcase models.HoState_PREPARED:smContext.SetState(smf_context.ModificationPending)smContext.HoState = models.HoState_PREPAREDresponse.JsonData.HoState = models.HoState_PREPAREDsmf_context.HandleHandoverRequestAcknowledgeTransfer(body.BinaryDataN2SmInformation, smContext)// request UPF establish indirect forwarding path for DLif smContext.DLForwardingType == smf_context.IndirectForwarding {ANUPF := smContext.IndirectForwardingTunnel.FirstDPNodeIndirectForwardingPDR := smContext.IndirectForwardingTunnel.FirstDPNode.UpLinkTunnel.PDRpdrList = append(pdrList, IndirectForwardingPDR)farList = append(farList, IndirectForwardingPDR.FAR)// release indirect forwading pathif err = ANUPF.UPF.RemovePDR(IndirectForwardingPDR); err != nil {logger.PduSessLog.Errorln("release indirect path: ", err)}sendPFCPModification = truesmContext.SetState(smf_context.PFCPModification)}smf_context.BuildHandoverCommandTransfer(smContext)response.JsonData.HoState = models.HoState_PREPARINGcase models.HoState_COMPLETED:for _, dataPath := range tunnel.DataPathPool {if dataPath.Activated {ANUPF := dataPath.FirstDPNodeDLPDR := ANUPF.DownLinkTunnel.PDRpdrList = append(pdrList, DLPDR)farList = append(farList, DLPDR.FAR)}}// remove indirect forwarding pathif smContext.DLForwardingType == smf_context.IndirectForwarding {indirectForwardingPDR := smContext.IndirectForwardingTunnel.FirstDPNode.GetUpLinkPDR()indirectForwardingPDR.State = smf_context.RULE_REMOVEindirectForwardingPDR.FAR.State = smf_context.RULE_REMOVEpdrList = append(pdrList, indirectForwardingPDR)farList = append(farList, indirectForwardingPDR.FAR)}smContext.SetState(smf_context.PFCPModification)smContext.HoState = models.HoState_COMPLETEDresponse.JsonData.HoState = models.HoState_COMPLETED}/* ================================================================ */// 最后,根据PDU Session状态机的转移采取行动switch smContext.State() {case smf_context.PFCPModification:pfcpResponseStatus = p.updateAnUpfPfcpSession(smContext, pdrList, farList, barList, qerList, urrList)// 处理PFCP更新的结果switch pfcpResponseStatus {case smf_context.SessionUpdateSuccess:smContext.SetState(smf_context.Active)c.Render(http.StatusOK, openapi.MultipartRelatedRender{Data: response})case smf_context.SessionUpdateFailed:smContext.SetState(smf_context.Active)updateSmContextError := models.UpdateSmContextErrorResponse{JsonData: &models.SmContextUpdateError{Error: &Nsmf_PDUSession.N1SmError,},} // Depends on the reason why N4 failc.JSON(http.StatusForbidden, updateSmContextError)case smf_context.SessionReleaseSuccess:p.ReleaseChargingSession(smContext)smContext.SetState(smf_context.InActivePending)c.Render(http.StatusOK, openapi.MultipartRelatedRender{Data: response})case smf_context.SessionReleaseFailed:// Update SmContext Request(N1 PDU Session Release Request)// Send PDU Session Release RejectsmContext.SetState(smf_context.Active)// problemDetail := models.ProblemDetails{// 。。。。。。c.JSON(int(problemDetail.Status), errResponse)}smContext.PostRemoveDataPath()case smf_context.ModificationPending:smContext.SetState(smf_context.Active)c.Render(http.StatusOK, openapi.MultipartRelatedRender{Data: response})case smf_context.InActive, smf_context.InActivePending:c.Render(http.StatusOK, openapi.MultipartRelatedRender{Data: response})default:c.Render(http.StatusOK, openapi.MultipartRelatedRender{Data: response})}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/843391.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

定义和基本导数规则(斜率截距,指数,对数)

率截距形式简介 方程的斜率和 y 截距 y=mx+b m是斜率 b是截距 来自图的斜率-截距方程 证明m是斜率,x=1减去x=0的那一块 例子: 例子:m的斜率 来自斜率和点的斜率-截距方程 例子:先得出斜率,再代入求b的值 点-斜率形式简介 例题:

vxe-grid table 实现表格中弹窗选择数据

当需要再表格中的某个字段是关联另外一张表示,需要将从另外表选择数据,并将数据保存在当前行中 官网:https://vxetable.cn<template><div><vxe-grid v-bind="gridOptions"><template #action="{ row }"><vxe-button mode=&q…

vue项目中如何加载markdown

场景 今天忽然临时接到一个需求: 就是将markdown文件直接在vue项目中进行加载,并正常显示出来。 这......,我知道是可以进行加载markdown文件的。 但是我之前没有做过,答复的是:可以做的,但是这个需要一点时间。 领导:那行,你先调研一下。 简单介绍 vue-markdown-loader vue-…

【web安全】waf部署模式

一、dns解析模式 图中意思就说,咱给要防护的网站设置一个cname记录,比如www指向我们的WAF ip 1.1.1.1,这样www网站的流量会解析到WAF那里。 这种模式下,找到真实ip可能直接绕过检测。二、串联模式 像串葫芦一样串起来 这种模式当数据溢出WAF临界值,WAF可能就无法检测了。 …

【日记】自己内心戏好多(笑(968 字)

正文本来想手写来着,结果找了快一个小时的图。没找到。放弃了。时间也不大够用了,就不手写了。找图主要是因为一件事——今天忽然通知要拍证件照。我特别纳闷,之前不是拍过了吗,而且也没怎么用到,这东西。好像必须要重新拍,不知道为什么。而且中午才通知。还必须要打领带…

【wab安全】waf部署模式

一、dns解析模式 图中意思就说,咱给要防护的网站设置一个cname记录,比如www指向我们的WAF ip 1.1.1.1,这样www网站的流量会解析到WAF那里。 这种模式下,找到真实ip可能直接绕过检测。二、串联模式 像串葫芦一样串起来 这种模式当数据溢出WAF临界值,WAF可能就无法检测了。 …

【应急响应】Linux 计划任务与 DFIR 排查实践(三)

原创 Y1x1n Y1X1n安全 2024年11月29日 07:04免责声明 本公众号文章中的程序、方法、信息和工具仅用于安全研究、教学、网络安全人员对网站及服务器的检测维护,禁止用于其他非法用途。使用者对不当使用造成的后果承担全部法律及连带责任,作者和公众号不担责。文中安全漏洞情报…

20222416 2024-2025-1 《网络与系统攻防技术》实验五实验报告

1.实验内容 1.1 本周学习内容 Nessus是一款广泛使用的网络漏洞扫描工具。Nessus 可以执行自动化的漏洞扫描,通过扫描目标系统、识别和评估可能存在的安全漏洞和弱点。它可以检测操作系统、应用程序、服务和网络设备中的各种安全问题,如已知的漏洞、配置错误、弱密码、未经授权…

20222315 2024-2025 《网络与系统攻防技术》实验七实验报告

1、实验内容 本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法。具体实践有 (1)简单应用SET工具建立冒名网站 (2)ettercap DNS spoof (3)结合应用两种技术,用DNS spoof引导特定访问到冒名网站。 2、实验过程 2.1简单应用SET工具建立冒名网站…

20222417 2021-2022-2 《网络与系统攻防技术》实验七实验报告

1.实验内容 1.1本周学习内容 本周学习了网络欺诈背后的原理,让我深刻认识到网络欺骗的危害。同时,学习了中间人攻击以及arp欺骗与回话挟持等攻击原理中间人攻击就是通过欺骗目标主机和路由器之间的通信,使其流量经过攻击者控制的中继节点,从而窃取或篡改通信内容。ARP欺骗则…

UE5模拟交互篇|可交互流体风场实现

【USparkle专栏】如果你深怀绝技,爱“搞点研究”,乐于分享也博采众长,我们期待你的加入,让智慧的火花碰撞交织,让知识的传递生生不息!一、原理介绍1. 思路分析先来说观察介质模拟的两种视角:拉格朗日视角和欧拉视角。 拉格朗日视角一般将介质视为粒子(或微小网格)的集…

层板孔位自动镜像

横板垂直孔正反面设置(在设计中需要去开启功能)