From c0381470daea3c5e25ac96cf9f7ea5821e1925c9 Mon Sep 17 00:00:00 2001 From: saileshvvr Date: Sat, 6 Jan 2024 11:06:09 +0530 Subject: [PATCH 1/3] Add support for UECM Registration and Deregistration for a PDU Session --- internal/context/sm_context.go | 2 + .../sbi/consumer/ue_context_management.go | 98 +++++++++++++++++++ internal/sbi/producer/pdu_session.go | 53 +++++++++- internal/util/search_nf_service.go | 50 ++++++++++ 4 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 internal/sbi/consumer/ue_context_management.go create mode 100644 internal/util/search_nf_service.go diff --git a/internal/context/sm_context.go b/internal/context/sm_context.go index 308337d0..714ab250 100644 --- a/internal/context/sm_context.go +++ b/internal/context/sm_context.go @@ -208,6 +208,8 @@ type SMContext struct { // State state SMContextState + UeCmRegistered bool + // Loggers Log *logrus.Entry diff --git a/internal/sbi/consumer/ue_context_management.go b/internal/sbi/consumer/ue_context_management.go new file mode 100644 index 00000000..66fe6b40 --- /dev/null +++ b/internal/sbi/consumer/ue_context_management.go @@ -0,0 +1,98 @@ +package consumer + +import ( + "context" + + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/Nudm_UEContextManagement" + "github.com/free5gc/openapi/models" + smf_context "github.com/free5gc/smf/internal/context" + "github.com/free5gc/smf/internal/logger" + "github.com/free5gc/smf/internal/util" + "github.com/pkg/errors" +) + +func UeCmRegistration(smCtx *smf_context.SMContext) ( + *models.ProblemDetails, error, +) { + uecmUri := util.SearchNFServiceUri(smf_context.GetSelf().UDMProfile, models.ServiceName_NUDM_UECM, models.NfServiceStatus_REGISTERED) + if uecmUri == "" { + return nil, errors.Errorf("SMF can not select an UDM by NRF: SearchNFServiceUri failed") + } + + configuration := Nudm_UEContextManagement.NewConfiguration() + configuration.SetBasePath(uecmUri) + client := Nudm_UEContextManagement.NewAPIClient(configuration) + + registrationData := models.SmfRegistration{ + SmfInstanceId: smf_context.GetSelf().NfInstanceID, + SupportedFeatures: "", + PduSessionId: smCtx.PduSessionId, + SingleNssai: smCtx.SNssai, + Dnn: smCtx.Dnn, + EmergencyServices: false, + PcscfRestorationCallbackUri: "", + PlmnId: smCtx.Guami.PlmnId, + PgwFqdn: "", + } + + logger.PduSessLog.Infoln("UECM Registration SmfInstanceId:", registrationData.SmfInstanceId, " PduSessionId:", registrationData.PduSessionId, + " SNssai:", registrationData.SingleNssai, " Dnn:", registrationData.Dnn, " PlmnId:", registrationData.PlmnId) + + _, httpResp, localErr := client.SMFRegistrationApi.SmfRegistrationsPduSessionId(context.Background(), + smCtx.Supi, smCtx.PduSessionId, registrationData) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.PduSessLog.Errorf("UeCmRegistration response body cannot close: %+v", + rspCloseErr) + } + } + }() + + if localErr == nil { + smCtx.UeCmRegistered = true + return nil, nil + } else if httpResp != nil { + if httpResp.Status != localErr.Error() { + return nil, localErr + } + problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) + return &problem, nil + } else { + return nil, openapi.ReportError("server no response") + } +} + +func UeCmDeregistration(smCtx *smf_context.SMContext) (*models.ProblemDetails, error) { + uecmUri := util.SearchNFServiceUri(smf_context.GetSelf().UDMProfile, models.ServiceName_NUDM_UECM, models.NfServiceStatus_REGISTERED) + if uecmUri == "" { + return nil, errors.Errorf("SMF can not select an UDM by NRF: SearchNFServiceUri failed") + } + + configuration := Nudm_UEContextManagement.NewConfiguration() + configuration.SetBasePath(uecmUri) + client := Nudm_UEContextManagement.NewAPIClient(configuration) + + httpResp, localErr := client.SMFDeregistrationApi.Deregistration(context.Background(), + smCtx.Supi, smCtx.PduSessionId) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("UeCmDeregistration response body cannot close: %+v", + rspCloseErr) + } + } + }() + if localErr == nil { + return nil, nil + } else if httpResp != nil { + if httpResp.Status != localErr.Error() { + return nil, localErr + } + problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) + return &problem, nil + } else { + return nil, openapi.ReportError("server no response") + } +} diff --git a/internal/sbi/producer/pdu_session.go b/internal/sbi/producer/pdu_session.go index 086b097c..4182687a 100644 --- a/internal/sbi/producer/pdu_session.go +++ b/internal/sbi/producer/pdu_session.go @@ -203,6 +203,16 @@ func HandlePDUSessionSMContextCreate(isDone <-chan struct{}, smContext.Log.Errorf("apply sm policy decision error: %+v", err) } + // UECM registration + problemDetails, err := consumer.UeCmRegistration(smContext) + if problemDetails != nil { + smContext.Log.Errorf("UECM_Registration Error: %+v", problemDetails) + } else if err != nil { + smContext.Log.Errorf("UECM_Registration Error: %+v", err) + } else { + smContext.Log.Traceln("UECM Registration Successful") + } + // generate goroutine to handle PFCP and // reply PDUSessionSMContextCreate rsp immediately needUnlock = false @@ -230,7 +240,6 @@ func HandlePDUSessionSMContextCreate(isDone <-chan struct{}, Status: http.StatusCreated, Body: response, } - // TODO: UECM registration } func HandlePDUSessionSMContextUpdate(smContextRef string, body models.UpdateSmContextRequest) *httpwrapper.Response { @@ -314,6 +323,20 @@ func HandlePDUSessionSMContextUpdate(smContextRef string, body models.UpdateSmCo } } + if smContext.UeCmRegistered { + problemDetails, err := consumer.UeCmDeregistration(smContext) + if problemDetails != nil { + if problemDetails.Cause != "CONTEXT_NOT_FOUND" { + logger.PduSessLog.Errorf("UECM_DeRegistration Failed Problem[%+v]", problemDetails) + } + } else if err != nil { + logger.PduSessLog.Errorf("UECM_DeRegistration Error[%+v]", err) + } else { + logger.PduSessLog.Traceln("UECM_DeRegistration successful") + } + smContext.UeCmRegistered = false + } + cause := nasMessage.Cause5GSMRegularDeactivation if m.PDUSessionReleaseRequest.Cause5GSM != nil { cause = m.PDUSessionReleaseRequest.Cause5GSM.GetCauseValue() @@ -859,6 +882,20 @@ func HandlePDUSessionSMContextRelease(smContextRef string, body models.ReleaseSm } } + if smContext.UeCmRegistered { + problemDetails, err := consumer.UeCmDeregistration(smContext) + if problemDetails != nil { + if problemDetails.Cause != "CONTEXT_NOT_FOUND" { + logger.PduSessLog.Errorf("UECM_DeRegistration Failed Problem[%+v]", problemDetails) + } + } else if err != nil { + logger.PduSessLog.Errorf("UECM_DeRegistration Error[%+v]", err) + } else { + logger.PduSessLog.Traceln("UECM_DeRegistration successful") + } + smContext.UeCmRegistered = false + } + if !smContext.CheckState(smf_context.InActive) { smContext.SetState(smf_context.PFCPModification) } @@ -945,6 +982,20 @@ func HandlePDUSessionSMContextLocalRelease(smContext *smf_context.SMContext, cre } } + if smContext.UeCmRegistered { + problemDetails, err := consumer.UeCmDeregistration(smContext) + if problemDetails != nil { + if problemDetails.Cause != "CONTEXT_NOT_FOUND" { + logger.PduSessLog.Errorf("UECM_DeRegistration Failed Problem[%+v]", problemDetails) + } + } else if err != nil { + logger.PduSessLog.Errorf("UECM_DeRegistration Error[%+v]", err) + } else { + logger.PduSessLog.Traceln("UECM_DeRegistration successful") + } + smContext.UeCmRegistered = false + } + smContext.SetState(smf_context.PFCPModification) pfcpResponseStatus := releaseSession(smContext) diff --git a/internal/util/search_nf_service.go b/internal/util/search_nf_service.go new file mode 100644 index 00000000..32d5e79c --- /dev/null +++ b/internal/util/search_nf_service.go @@ -0,0 +1,50 @@ +package util + +import ( + "fmt" + + "github.com/free5gc/openapi/models" +) + +func SearchNFServiceUri(nfProfile models.NfProfile, serviceName models.ServiceName, + nfServiceStatus models.NfServiceStatus, +) (nfUri string) { + if nfProfile.NfServices != nil { + for _, service := range *nfProfile.NfServices { + if service.ServiceName == serviceName && service.NfServiceStatus == nfServiceStatus { + if nfProfile.Fqdn != "" { + nfUri = nfProfile.Fqdn + } else if service.Fqdn != "" { + nfUri = service.Fqdn + } else if service.ApiPrefix != "" { + nfUri = service.ApiPrefix + } else if service.IpEndPoints != nil { + point := (*service.IpEndPoints)[0] + if point.Ipv4Address != "" { + nfUri = getSbiUri(service.Scheme, point.Ipv4Address, point.Port) + } else if len(nfProfile.Ipv4Addresses) != 0 { + nfUri = getSbiUri(service.Scheme, nfProfile.Ipv4Addresses[0], point.Port) + } + } + } + if nfUri != "" { + break + } + } + } + return +} + +func getSbiUri(scheme models.UriScheme, ipv4Address string, port int32) (uri string) { + if port != 0 { + uri = fmt.Sprintf("%s://%s:%d", scheme, ipv4Address, port) + } else { + switch scheme { + case models.UriScheme_HTTP: + uri = fmt.Sprintf("%s://%s:80", scheme, ipv4Address) + case models.UriScheme_HTTPS: + uri = fmt.Sprintf("%s://%s:443", scheme, ipv4Address) + } + } + return +} From 5fd45fbf12865af28bd8350928a7b43e185a586d Mon Sep 17 00:00:00 2001 From: saileshvvr Date: Sat, 6 Jan 2024 21:43:48 +0530 Subject: [PATCH 2/3] Addressed the suggested comments to change the state in UeCmDeregistration() --- internal/sbi/consumer/ue_context_management.go | 12 ++++++++---- internal/sbi/producer/pdu_session.go | 3 --- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/internal/sbi/consumer/ue_context_management.go b/internal/sbi/consumer/ue_context_management.go index 66fe6b40..f9486879 100644 --- a/internal/sbi/consumer/ue_context_management.go +++ b/internal/sbi/consumer/ue_context_management.go @@ -15,7 +15,8 @@ import ( func UeCmRegistration(smCtx *smf_context.SMContext) ( *models.ProblemDetails, error, ) { - uecmUri := util.SearchNFServiceUri(smf_context.GetSelf().UDMProfile, models.ServiceName_NUDM_UECM, models.NfServiceStatus_REGISTERED) + uecmUri := util.SearchNFServiceUri(smf_context.GetSelf().UDMProfile, models.ServiceName_NUDM_UECM, + models.NfServiceStatus_REGISTERED) if uecmUri == "" { return nil, errors.Errorf("SMF can not select an UDM by NRF: SearchNFServiceUri failed") } @@ -36,8 +37,9 @@ func UeCmRegistration(smCtx *smf_context.SMContext) ( PgwFqdn: "", } - logger.PduSessLog.Infoln("UECM Registration SmfInstanceId:", registrationData.SmfInstanceId, " PduSessionId:", registrationData.PduSessionId, - " SNssai:", registrationData.SingleNssai, " Dnn:", registrationData.Dnn, " PlmnId:", registrationData.PlmnId) + logger.PduSessLog.Infoln("UECM Registration SmfInstanceId:", registrationData.SmfInstanceId, + " PduSessionId:", registrationData.PduSessionId, " SNssai:", registrationData.SingleNssai, + " Dnn:", registrationData.Dnn, " PlmnId:", registrationData.PlmnId) _, httpResp, localErr := client.SMFRegistrationApi.SmfRegistrationsPduSessionId(context.Background(), smCtx.Supi, smCtx.PduSessionId, registrationData) @@ -65,7 +67,8 @@ func UeCmRegistration(smCtx *smf_context.SMContext) ( } func UeCmDeregistration(smCtx *smf_context.SMContext) (*models.ProblemDetails, error) { - uecmUri := util.SearchNFServiceUri(smf_context.GetSelf().UDMProfile, models.ServiceName_NUDM_UECM, models.NfServiceStatus_REGISTERED) + uecmUri := util.SearchNFServiceUri(smf_context.GetSelf().UDMProfile, models.ServiceName_NUDM_UECM, + models.NfServiceStatus_REGISTERED) if uecmUri == "" { return nil, errors.Errorf("SMF can not select an UDM by NRF: SearchNFServiceUri failed") } @@ -85,6 +88,7 @@ func UeCmDeregistration(smCtx *smf_context.SMContext) (*models.ProblemDetails, e } }() if localErr == nil { + smCtx.UeCmRegistered = false return nil, nil } else if httpResp != nil { if httpResp.Status != localErr.Error() { diff --git a/internal/sbi/producer/pdu_session.go b/internal/sbi/producer/pdu_session.go index 4182687a..bb926087 100644 --- a/internal/sbi/producer/pdu_session.go +++ b/internal/sbi/producer/pdu_session.go @@ -334,7 +334,6 @@ func HandlePDUSessionSMContextUpdate(smContextRef string, body models.UpdateSmCo } else { logger.PduSessLog.Traceln("UECM_DeRegistration successful") } - smContext.UeCmRegistered = false } cause := nasMessage.Cause5GSMRegularDeactivation @@ -893,7 +892,6 @@ func HandlePDUSessionSMContextRelease(smContextRef string, body models.ReleaseSm } else { logger.PduSessLog.Traceln("UECM_DeRegistration successful") } - smContext.UeCmRegistered = false } if !smContext.CheckState(smf_context.InActive) { @@ -993,7 +991,6 @@ func HandlePDUSessionSMContextLocalRelease(smContext *smf_context.SMContext, cre } else { logger.PduSessLog.Traceln("UECM_DeRegistration successful") } - smContext.UeCmRegistered = false } smContext.SetState(smf_context.PFCPModification) From 682b5f2af85054ee5784947e0d23ea76a650d98e Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Tue, 16 Jan 2024 10:33:44 +0800 Subject: [PATCH 3/3] fix: ci linter error --- internal/sbi/consumer/ue_context_management.go | 3 ++- internal/sbi/producer/pdu_session.go | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/sbi/consumer/ue_context_management.go b/internal/sbi/consumer/ue_context_management.go index f9486879..a9263fa6 100644 --- a/internal/sbi/consumer/ue_context_management.go +++ b/internal/sbi/consumer/ue_context_management.go @@ -3,13 +3,14 @@ package consumer import ( "context" + "github.com/pkg/errors" + "github.com/free5gc/openapi" "github.com/free5gc/openapi/Nudm_UEContextManagement" "github.com/free5gc/openapi/models" smf_context "github.com/free5gc/smf/internal/context" "github.com/free5gc/smf/internal/logger" "github.com/free5gc/smf/internal/util" - "github.com/pkg/errors" ) func UeCmRegistration(smCtx *smf_context.SMContext) ( diff --git a/internal/sbi/producer/pdu_session.go b/internal/sbi/producer/pdu_session.go index bb926087..05610f60 100644 --- a/internal/sbi/producer/pdu_session.go +++ b/internal/sbi/producer/pdu_session.go @@ -184,14 +184,14 @@ func HandlePDUSessionSMContextCreate(isDone <-chan struct{}, smContext.SMPolicyID = smPolicyID // Update SessionRule from decision - if err := smContext.ApplySessionRules(smPolicyDecision); err != nil { + if err = smContext.ApplySessionRules(smPolicyDecision); err != nil { smContext.Log.Errorf("PDUSessionSMContextCreate err: %v", err) return makeEstRejectResAndReleaseSMContext(smContext, nasMessage.Cause5GSMRequestRejectedUnspecified, &Nsmf_PDUSession.SubscriptionDenied) } - if err := smContext.SelectDefaultDataPath(); err != nil { + if err = smContext.SelectDefaultDataPath(); err != nil { smContext.SetState(smf_context.InActive) smContext.Log.Errorf("PDUSessionSMContextCreate err: %v", err) return makeEstRejectResAndReleaseSMContext(smContext, @@ -199,7 +199,7 @@ func HandlePDUSessionSMContextCreate(isDone <-chan struct{}, &Nsmf_PDUSession.InsufficientResourceSliceDnn) } - if err := smContext.ApplyPccRules(smPolicyDecision); err != nil { + if err = smContext.ApplyPccRules(smPolicyDecision); err != nil { smContext.Log.Errorf("apply sm policy decision error: %+v", err) }