【5G核心网】free5GC AMF源码分析

free5gc AMF 源码分析

1. AMF 程序初始化以及启动

   1.1 启动 httpcallback 服务

     httpcallback.AddService(router),实现在 afm/httpcallback 中,URL 以及 handler 如下所示:

Name

Pattern

HandlerFunc

Index
/
Index
SmContextStatusNotify
/smContextStatus/:guti/:pduSessionId
SmContextStatusNotify
AmPolicyControlUpdateNotifyUpdate
/am-policy/:polAssoId/update
AmPolicyControlUpdateNotifyUpdate
AmPolicyControlUpdateNotifyTerminate
/am-policy/:polAssoId/terminate
AmPolicyControlUpdateNotifyTerminate
N1MessageNotify
/n1-message-notify
N1MessageNotify

    1.2 根据配置文件中的服务列表启动服务

       serviceNameList: - namf-comm - namf-evts - namf-mt - namf-loc - namf-oam

for _, serviceName := range factory.AmfConfig.Configuration.ServiceNameList {
	switch models.ServiceName(serviceName) {
	case models.ServiceName_NAMF_COMM:
		communication.AddService(router)
	case models.ServiceName_NAMF_EVTS:
		eventexposure.AddService(router)
	case models.ServiceName_NAMF_MT:
		mt.AddService(router)
	case models.ServiceName_NAMF_LOC:
		location.AddService(router)
	}
}

    1.3 初始化 amf 上下文

     通过配置文件,较简单,amf 监听端口为 29518

	self := context.AMF_Self()
	util.InitAmfContext(self)

	addr := fmt.Sprintf("%s:%d", self.HttpIPv4Address, self.HttpIpv4Port)

    1.4 根据 ngap IP 列表建立 SCTP 服务

      TCP是以字节为单位传输的,SCTP是以数据块为单位传输的

      TCP通常是单路径传输,SCTP可以多路径传输

	for _, ngapAddr := range self.NgapIpList {
		sctpListener = sctp.Server(ngapAddr)
	}

     1.5 核心处理流程

      实现在 amf/handler/hander.go

go handler.Handle()

    1.6 注册到 NRF

      amf id 组成 regionId: 16bits, setId: 10bits, ptrId: 6bits    <AMF Identifier> = <AMF Region ID><AMF Set ID><AMF Pointer>

      调用 NRF Nnrf_NFManagement,注册

// Register to NRF
profile, err := consumer.BuildNFInstance(self)
if err != nil {
	initLog.Error("Build AMF Profile Error")
}

_, self.NfId, _ = consumer.SendRegisterNFInstance(self.NrfUri, self.NfId, profile)

2. RAN 发起的 NGSetupRequest 消息

     NG Setup 流程用来交换 NG-RAN 节点和 AMF 在 NG-C 接口上正确互操作所需的应用程序数据,该程序应是 TNL 关联开始运行后触发的第一个 NGAP 程序。该过程使用 非UE 相关的信令。

    2.1 NGAP 结构体

type NGAPPDU struct {
	Present             int
	InitiatingMessage   *InitiatingMessage
	SuccessfulOutcome   *SuccessfulOutcome
	UnsuccessfulOutcome *UnsuccessfulOutcome
}

    Present 设置为 NGAPPDUPresentInitiatingMessage

扫描二维码关注公众号,回复: 11548654 查看本文章

    2.2 InitiatingMessage 结构体

type InitiatingMessage struct {
	ProcedureCode ProcedureCode
	Criticality   Criticality
	Value         InitiatingMessageValue `aper:"openType,referenceFieldName:ProcedureCode"`
}

    initiatingMessage ProcedureCode 设置为 ProcedureCodeNGSetup  InitiatingMessagePresentNGSetupRequest

    当使用 NG-RAN 时,N2 参数包括所选的 PLMN ID、位置信息和与 Ue 所在小区相关的身份、Ue 上下文请求,该请求指明需要在 NG-RAN 中设置一个包含安全信息的 Ue 上下文 。也包括建立的原因,
    如果可用的话,才提供请求的 NSSAI 映射
    如果 UE 注册类型指明是定期注册更新,则省略 4 - 19 步骤
    如果 UE 包含首选的网络行为

    2.3 AMF 接收 NGSetupRequest 消息

      根据 NGAPPDUPresentInitiatingMessage 和 ProcedureCodeNGSetup 定位到 HandleNGSetupRequest

func HandleNGSetupRequest(ran *context.AmfRan, message *ngapType.NGAPPDU) {
	var globalRANNodeID *ngapType.GlobalRANNodeID
	var rANNodeName *ngapType.RANNodeName
	var supportedTAList *ngapType.SupportedTAList
	var pagingDRX *ngapType.PagingDRX

	var cause ngapType.Cause

    主要是验证信息,如果验证通过则 SendNGSetupResponse,失败则调用 SendNGSetupFailure

    如果成功则发送 NGAPPDUPresentSuccessfulOutcome,类型为 SuccessfulOutcomePresentNGSetupResponse,包括 IE:AMFName,ServedGUAMIList,relativeAMFCapacity,pLMNSupportList

4. PDU 会话建立流程

   4.1 UE 发起的 PDU 会话建立请求 (RAN -> AMF) 

    GetPduSessionEstablishmentRequest 创建 NAS-PDU,填充 NAS 消息中的 SM 消息,设置 SM 头消息类型为 MsgTypePDUSessionEstablishmentRequest,SM 头格式为:

type GsmHeader struct {
	Octet [4]uint8
}

     PDUSessionEstablishmentRequest 结构体

type PDUSessionEstablishmentRequest struct {
	nasType.ExtendedProtocolDiscriminator
	nasType.PDUSessionID
	nasType.PTI
	nasType.PDUSESSIONESTABLISHMENTREQUESTMessageIdentity
	nasType.IntegrityProtectionMaximumDataRate
	*nasType.PDUSessionType
	*nasType.SSCMode
	*nasType.Capability5GSM
	*nasType.MaximumNumberOfSupportedPacketFilters
	*nasType.AlwaysonPDUSessionRequested
	*nasType.SMPDUDNRequestContainer
	*nasType.ExtendedProtocolConfigurationOptions
}

    NAS 消息中的 MM 消息头部类型为 MsgTypeULNASTransport,设置的内容 ULNASTransport,类型为 MsgTypeULNASTransport,PayloadContainerTypeN1SMInfo

    添加 DNN,SNSSAI,PayloadContainer(上面创建的 NAS 携带 SM 消息)

type ULNASTransport struct {
	nasType.ExtendedProtocolDiscriminator
	nasType.SpareHalfOctetAndSecurityHeaderType
	nasType.ULNASTRANSPORTMessageIdentity
	nasType.SpareHalfOctetAndPayloadContainerType
	nasType.PayloadContainer
	*nasType.PduSessionID2Value
	*nasType.OldPDUSessionID
	*nasType.RequestType
	*nasType.SNSSAI
	*nasType.DNN
	*nasType.AdditionalInformation
}

    4.1.1 BuildUplinkNasTransport 函数创建 NGAP 消息

    NGAP 消息了类型为 NGAPPDUPresentInitiatingMessage,数据结构体为 InitiatingMessage,ProcedureCode 设置为 ProcedureCodeUplinkNASTransport,携带 IE 有

    // AMF UE NGAP ID

       ProtocolIEIDAMFUENGAPID     UplinkNASTransportIEsPresentAMFUENGAPID

    // RAN UE NGAP ID

       ProtocolIEIDRANUENGAPID      UplinkNASTransportIEsPresentRANUENGAPID

    // NAS-PDU

        ProtocolIEIDNASPDU                 UplinkNASTransportIEsPresentNASPDU

    // User Location Information

        ProtocolIEIDUserLocationInformation      UplinkNASTransportIEsPresentUserLocationInformation

     UPLINK NAS TRANSPORT

IE/Group Name

Presence

IE type and reference

Criticality

Assigned Criticality

Message Type

M

9.3.1.1

YES

ignore

AMF UE NGAP ID

M

9.3.3.1

YES

reject

RAN UE NGAP ID

M

9.3.3.2

YES

reject

NAS-PDU

M

9.3.3.4

YES

reject

User Location Information

M

9.3.1.16

YES

ignore

   4.2 AMF 收到 RAN 发送的 NGAP UPLINK NAS TRANSPORT 消息

    根据 NGAP 消息类型为 NGAPPDUPresentInitiatingMessage,ProcedureCode 设置为 ProcedureCodeUplinkNASTransportInitiatingMessagePresentUplinkNASTransport,定位到 HandleUplinkNasTransport

    提取出 IE 包括 AMFUENGAPID RANUENGAPID NASPDU UserLocationInformation

func HandleUplinkNasTransport(ran *context.AmfRan, message *ngapType.NGAPPDU) {

	var aMFUENGAPID *ngapType.AMFUENGAPID
	var rANUENGAPID *ngapType.RANUENGAPID
	var nASPDU *ngapType.NASPDU
	var userLocationInformation *ngapType.UserLocationInformation

    根据 3GPP 接入类型,以及 MM 消息类 MsgTypeULNASTransport,定位到函数  MsgTypeULNASTransport

    提取出 PDU session ID,Snssai,DNN,requestType(ULNASTransportRequestTypeInitialRequest)设置为 RequestType_INITIAL_REQUEST

func HandleULNASTransport(ue *context.AmfUe, anType models.AccessType, procedureCode int64, ulNasTransport *nasMessage.ULNASTransport, securityHeaderType uint8) error {
	logger.GmmLog.Infoln("Handle UL NAS Transport")

	if ue.MacFailed {
		return fmt.Errorf("NAS message integrity check failed")
	}

	switch ulNasTransport.GetPayloadContainerType() {
	case nasMessage.PayloadContainerTypeN1SMInfo:

    在处理 SM 消息根据类型为 MsgTypePDUSessionEstablishmentRequest 根据初始请求设置为:RequestType_INITIAL_REQUEST

func HandlePDUSessionEstablishmentRequest(ue *context.AmfUe, anType models.AccessType, payload []byte, pduSessionID int32, requestType models.RequestType, sNssai *models.Snssai, dnn string) error {
	// TODO Request Type Emergency requset
	var pduSession models.PduSessionContext
	pduSession.PduSessionId = pduSessionID
	pduSession.AccessType = anType
	amfSelf := context.AMF_Self()
	if requestType == models.RequestType_INITIAL_REQUEST {

    如果 UE 未提供 Snssai,则根据 UE 订阅的切片信息选择,如果 DNN 未提供,选择切片默认 DNN

    如果以上情况没有,则使用 UE Allow 切片信息

if sNssai == nil {
	if ue.SmfSelectionData != nil {
		for snssai, sNssaiInfo := range ue.SmfSelectionData.SubscribedSnssaiInfos {
			var err error
			sNssai, err = util.SnssaiHexToModels(snssai)
			if err != nil {
				return err
			}
			if dnn == "" {
				for _, dnnInfo := range sNssaiInfo.DnnInfos {
					if dnnInfo.DefaultDnnIndicator {
						dnn = dnnInfo.Dnn
						break
					}
				}
			}

		}
	}
	if sNssai == nil {
		allowedNssai := ue.AllowedNssai[anType]
		if len(allowedNssai) > 0 {
			sNssai = allowedNssai[0].AllowedSnssai
		} else {
			err := fmt.Errorf("Ue[%s] doesn't have allowedNssai\n", ue.Supi)
			logger.GmmLog.Errorf(err.Error())
			return err
		}
	}

}

       4.2.1 selectSmf 函数

    AMF 根据切片信息,DNN 等为 PDU 会话选择 SMF

    AMF->SMF:  Nnssf_NSSelection_Get 返回了网络切片 ID,AMF 根据 NSI 找到 S-NAASI 选择 SMF

     4.2.2 如果 UE 的会话上下文存在

       则调用 SendUpdateSmContextRequest 向 SMF 发送 Nsmf_PDUSession /sm-contexts/{smContextRef}/modify 请求

// Store PduSessionContext For duplicated PDU Session Id
if smContext, ok := ue.SmContextList[pduSessionID]; ok {
	ue.StoredSmContext[pduSessionID] = &context.StoredSmContext{
		SmfId:             smfID,
		SmfUri:            smfUri,
		PduSessionContext: &pduSession,
		AnType:            anType,
		Payload:           payload,
	}

    4.3 如果 UE 的会话上下文不存在

      BuildCreateSmContextRequest 创建会话上下文请求,其包括:

    - supi

    - UnauthenticatedSupi

    - pei

    - gpsi

    - pdusessionId

    - sNssai

    - DNN

    - servingNfId

    - guami

    - servingNetwork

    - requestType

    - N1Smmsg

    - Antype

    - RatType

    - [ UeLocation ] 

    - SmContextStatusUri

smContextCreateData.Supi = ue.Supi
smContextCreateData.UnauthenticatedSupi = ue.UnauthenticatedSupi
smContextCreateData.Pei = ue.Pei
smContextCreateData.Gpsi = ue.Gpsi
smContextCreateData.PduSessionId = pduSessionContext.PduSessionId
smContextCreateData.SNssai = pduSessionContext.SNssai
smContextCreateData.Dnn = pduSessionContext.Dnn
smContextCreateData.ServingNfId = context.NfId
smContextCreateData.Guami = &context.ServedGuamiList[0]
smContextCreateData.ServingNetwork = context.ServedGuamiList[0].PlmnId

      4.3.1 SendCreateSmContextRequest

      向 SMF 发送 Nsmf_PDUSession /sm-contexts 请求

     4.3.2 SMF 回复 AMF response     

 

5. N1N2MessageTransfer(SMF->AMF)步骤 11

    包裹 N1N2MessageTransfer,设置类型为 EventN1N2MessageTransfer,丢进 channel 进行处理

   HandleN1N2MessageTransferRequest 函数

    由 SMF 向 AMF 发送的 N2 信息 ngap 类型为 NgapIeType_PDU_RES_SETUP_REQ

   5.1 N1 信息类型为 NgapIeType_PDU_RES_SETUP_REQ

case models.NgapIeType_PDU_RES_SETUP_REQ:
	HttpLog.Debugln("AMF Transfer NGAP PDU Resource Setup Req from SMF")
	var nasPdu []byte
	var err error
	if n1Msg != nil {
		pduSessionId := uint8(smInfo.PduSessionId)
		nasPdu, err = gmm_message.BuildDLNASTransport(ue, nasMessage.PayloadContainerTypeN1SMInfo, n1Msg, &pduSessionId, nil, nil, 0)
		if err != nil {
			logger.HttpLog.Errorln(err.Error())
		}
	}

    5.1.1 BuildDLNASTransport 函数

    创建 NAS MM 消息,类型设置为 MsgTypeDLNASTransport

    5.1.2 AppendPDUSessionResourceSetupListSUReq 函数

    PDUSessionResourceSetupListSUReq 结构填充 PDU 会话 ID,SNSSAI,nasPDU

    5.1.3 SendPDUSessionResourceSetupRequest 函数 N2 PDU Session Request(AMF->RAN) 步骤  12

    为多个 PDU 会话和对应的 Qos 流在 Uu 和 NG-U 分配资源,来给 UE 建立相应的 DRB,这个流程使用 UE 相关的信令,由 AMF 发起 PDU SESSION RESOURCE SETUP REQUEST 消息到 NG-RAN 节点

    BuildPDUSessionResourceSetupRequest 函数实例化 NGAPPDU 对象

    Present 设置为 NGAPPDUPresentInitiatingMessage,填充的为 initiatingMessage,ProcedureCode 设置为 ProcedureCodePDUSessionResourceSetup  InitiatingMessagePresentPDUSessionResourceSetupRequest

type NGAPPDU struct {
	Present             int
	InitiatingMessage   *InitiatingMessage
	SuccessfulOutcome   *SuccessfulOutcome
	UnsuccessfulOutcome *UnsuccessfulOutcome
}

    5.1.3.1 (IE)AMF UE NGAP ID

      ProtocolIEIDAMFUENGAPID -->  PDUSessionResourceSetupRequestIEsPresentAMFUENGAPID

    5.1.3.2 (IE)RAN UE NGAP ID

     ProtocolIEIDRANUENGAPID -->  PDUSessionResourceSetupRequestIEsPresentRANUENGAPID

    5.1.3.3(IE)Ran Paging Priority (optional)

    5.1.3.4(IE)NAS-PDU (optional)

     ProtocolIEIDNASPDU -->  PDUSessionResourceSetupRequestIEsPresentNASPDU

    5.1.3.5(IE)PDU Session Resource Setup Request list

     ProtocolIEIDPDUSessionResourceSetupListSUReq -->  PDUSessionResourceSetupRequestIEsPresentPDUSessionResourceSetupListSUReq

    5.2 N2 PDU Session Response (RAN -> AMF)步骤 14    

      NGAP 消息类型为 NGAPPDUPresentSuccessfulOutcome,填充数据结构为 SuccessfulOutcome,设置为 ProcedureCodePDUSessionResourceSetup,SuccessfulOutcomePresentPDUSessionResourceSetupResponse

      AMF 收到 N2 PDU Session Response 进行处理,定位函数为 HandlePDUSessionResourceSetupResponse,包括更新到 SMF (步骤 15) Nsmf_PDUSession_UpdateSMContextRequest

    NRF 中注册的 AMF 信息:

{
    "_id" : ObjectId("5f1a9c2564d2e538cb1309d7"),
    "nfInstanceId" : "d5a9f0d5-a065-43cb-ad91-65a123351ee4",
    "nfType" : "AMF",
    "nfStatus" : "REGISTERED",
    "plmnList" : [
        {
            "mcc" : "208",
            "mnc" : "93" 
        } ],
    "sNssais" : [
        {
            "sst" : 1,
            "sd" : "010203" 
        },
        {
            "sst" : 1,
            "sd" : "112233" 
        } ],
    "ipv4Addresses" : [
        "amf" ],
    "amfInfo" : {
        "taiList" : [
            {
                "plmnId" : {
                    "mcc" : "208",
                    "mnc" : "93" 
                },
                "tac" : "000001" 
            } ],
        "amfSetId" : "3f8",
        "amfRegionId" : "ca",
        "guamiList" : [
            {
                "plmnId" : {
                    "mcc" : "208",
                    "mnc" : "93" 
                },
                "amfId" : "cafe00" 
            } ] 
    },
    "nfServices" : [
        {
            "ipEndPoints" : [
                {
                    "transport" : "TCP",
                    "port" : 29518,
                    "ipv4Address" : "amf" 
                } ],
            "apiPrefix" : "https://amf:29518",
            "serviceInstanceId" : "0",
            "serviceName" : "namf-comm",
            "versions" : [
                {
                    "apiVersionInUri" : "v1",
                    "apiFullVersion" : "1.0.0" 
                } ],
            "scheme" : "https",
            "nfServiceStatus" : "REGISTERED" 
        },
        {
            "versions" : [
                {
                    "apiVersionInUri" : "v1",
                    "apiFullVersion" : "1.0.0" 
                } ],
            "scheme" : "https",
            "nfServiceStatus" : "REGISTERED",
            "ipEndPoints" : [
                {
                    "ipv4Address" : "amf",
                    "transport" : "TCP",
                    "port" : 29518 
                } ],
            "apiPrefix" : "https://amf:29518",
            "serviceInstanceId" : "1",
            "serviceName" : "namf-evts" 
        },
        {
            "scheme" : "https",
            "nfServiceStatus" : "REGISTERED",
            "ipEndPoints" : [
                {
                    "ipv4Address" : "amf",
                    "transport" : "TCP",
                    "port" : 29518 
                } ],
            "apiPrefix" : "https://amf:29518",
            "serviceInstanceId" : "2",
            "serviceName" : "namf-mt",
            "versions" : [
                {
                    "apiVersionInUri" : "v1",
                    "apiFullVersion" : "1.0.0" 
                } ] 
        },
        {
            "ipEndPoints" : [
                {
                    "ipv4Address" : "amf",
                    "transport" : "TCP",
                    "port" : 29518 
                } ],
            "apiPrefix" : "https://amf:29518",
            "serviceInstanceId" : "3",
            "serviceName" : "namf-loc",
            "versions" : [
                {
                    "apiVersionInUri" : "v1",
                    "apiFullVersion" : "1.0.0" 
                } ],
            "scheme" : "https",
            "nfServiceStatus" : "REGISTERED" 
        } ] 
}
 

猜你喜欢

转载自blog.csdn.net/zhonglinzhang/article/details/107554349