Skip to content

Commit

Permalink
Add support for UECM Registration and Deregistration for a PDU Session (
Browse files Browse the repository at this point in the history
#92)

* Add support for UECM Registration and Deregistration for a PDU Session

* Addressed the suggested comments to change the state in UeCmDeregistration()

* fix: ci linter error

---------

Co-authored-by: Ian Chen <iancodinghtml@gmail.com>
  • Loading branch information
saileshvvr and ianchen0119 authored Jan 17, 2024
1 parent 4a4b6f4 commit 05c55bb
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 4 deletions.
2 changes: 2 additions & 0 deletions internal/context/sm_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ type SMContext struct {
// State
state SMContextState

UeCmRegistered bool

// Loggers
Log *logrus.Entry

Expand Down
103 changes: 103 additions & 0 deletions internal/sbi/consumer/ue_context_management.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
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"
)

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 {
smCtx.UeCmRegistered = false
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")
}
}
56 changes: 52 additions & 4 deletions internal/sbi/producer/pdu_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,25 +184,35 @@ 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,
nasMessage.Cause5GSMInsufficientResourcesForSpecificSliceAndDNN,
&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)
}

// 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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -314,6 +323,19 @@ 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")
}
}

cause := nasMessage.Cause5GSMRegularDeactivation
if m.PDUSessionReleaseRequest.Cause5GSM != nil {
cause = m.PDUSessionReleaseRequest.Cause5GSM.GetCauseValue()
Expand Down Expand Up @@ -859,6 +881,19 @@ 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")
}
}

if !smContext.CheckState(smf_context.InActive) {
smContext.SetState(smf_context.PFCPModification)
}
Expand Down Expand Up @@ -945,6 +980,19 @@ 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.SetState(smf_context.PFCPModification)

pfcpResponseStatus := releaseSession(smContext)
Expand Down
50 changes: 50 additions & 0 deletions internal/util/search_nf_service.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 05c55bb

Please sign in to comment.