Skip to content

Commit

Permalink
feat: SMF to support SDM Subscription and Unsubscription for UE Session
Browse files Browse the repository at this point in the history
  • Loading branch information
saileshvvr committed Sep 14, 2024
1 parent ed09202 commit 479c658
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 0 deletions.
4 changes: 4 additions & 0 deletions internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ type SMFContext struct {

// Each pdu session should have a unique charging id
ChargingIDGenerator *idgenerator.IDGenerator

Ues *Ues
}

func GenerateChargingID() int32 {
Expand Down Expand Up @@ -248,6 +250,8 @@ func InitSmfContext(config *factory.Config) {
smfContext.Locality = configuration.Locality

TeidGenerator = idgenerator.NewGenerator(1, math.MaxUint32)

smfContext.Ues = InitSmfUeData()
}

func InitSMFUERouting(routingConfig *factory.RoutingConfig) {
Expand Down
100 changes: 100 additions & 0 deletions internal/context/sm_ue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package context

import "sync"

type UeData struct {
PduSessionCount int // store number of PDU Sessions for each UE
SdmSubscriptionId string // store SDM Subscription ID per UE
}

type Ues struct {
ues map[string]UeData // map to store UE data with SUPI as key
mu sync.Mutex // mutex for concurrent access
}

func InitSmfUeData() *Ues {
return &Ues{
ues: make(map[string]UeData),
}
}

// IncrementPduSessionCount increments the PDU session count for a given UE.
func (u *Ues) IncrementPduSessionCount(ueId string) {
u.mu.Lock()
defer u.mu.Unlock()

ueData := u.ues[ueId]
ueData.PduSessionCount++
u.ues[ueId] = ueData
}

// DecrementPduSessionCount decrements the PDU session count for a given UE.
func (u *Ues) DecrementPduSessionCount(ueId string) {
u.mu.Lock()
defer u.mu.Unlock()

ueData := u.ues[ueId]
if ueData.PduSessionCount > 0 {
ueData.PduSessionCount--
u.ues[ueId] = ueData
}
}

// SetSubscriptionId sets the SDM subscription ID for a given UE.
func (u *Ues) SetSubscriptionId(ueId, subscriptionId string) {
u.mu.Lock()
defer u.mu.Unlock()

ueData := u.ues[ueId]
ueData.SdmSubscriptionId = subscriptionId
u.ues[ueId] = ueData
}

// GetSubscriptionId returns the SDM subscription ID for a given UE.
func (u *Ues) GetSubscriptionId(ueId string) string {
u.mu.Lock()
defer u.mu.Unlock()

return u.ues[ueId].SdmSubscriptionId
}

// GetUeData returns the data for a given UE.
func (u *Ues) GetUeData(ueId string) UeData {
u.mu.Lock()
defer u.mu.Unlock()

return u.ues[ueId]
}

// DeleteUe deletes a UE.
func (u *Ues) DeleteUe(ueId string) {
u.mu.Lock()
defer u.mu.Unlock()

delete(u.ues, ueId)
}

// UeExists checks if a UE already exists.
func (u *Ues) UeExists(ueId string) bool {
u.mu.Lock()
defer u.mu.Unlock()

_, exists := u.ues[ueId]
return exists
}

// IsLastPduSession checks if it is the last PDU session for a given UE.
func (u *Ues) IsLastPduSession(ueID string) bool {
u.mu.Lock()
defer u.mu.Unlock()

return u.ues[ueID].PduSessionCount == 1
}

// GetPduSessionCount returns the number of sessions for a given UE.
func (u *Ues) GetPduSessionCount(ueId string) int {
u.mu.Lock()
defer u.mu.Unlock()

return u.ues[ueId].PduSessionCount
}
121 changes: 121 additions & 0 deletions internal/sbi/consumer/udm_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,124 @@ func (s *nudmService) GetSmData(ctx context.Context, supi string,

return sessSubData, rsp, err
}

func (s *nudmService) Subscribe(ctx context.Context, smCtx *smf_context.SMContext, smPlmnID *models.PlmnId) (
*models.ProblemDetails, error,
) {
var client *Nudm_SubscriberDataManagement.APIClient
for _, service := range *s.consumer.Context().UDMProfile.NfServices {
if service.ServiceName == models.ServiceName_NUDM_SDM {
sdmUri := util.SearchNFServiceUri(smf_context.GetSelf().UDMProfile, models.ServiceName_NUDM_SDM,
models.NfServiceStatus_REGISTERED)
if sdmUri == "" {
return nil, errors.Errorf("SMF can not select an UDM by NRF: SearchNFServiceUri failed")
}

SDMConf := Nudm_SubscriberDataManagement.NewConfiguration()
SDMConf.SetBasePath(sdmUri)

client = Nudm_SubscriberDataManagement.NewAPIClient(SDMConf)
}
}

if client == nil {
return nil, fmt.Errorf("sdm client failed")
}

sdmSubscription := models.SdmSubscription{
NfInstanceId: smf_context.GetSelf().NfInstanceID,
PlmnId: smPlmnID,
}

resSubscription, httpResp, localErr := client.SubscriptionCreationApi.Subscribe(
ctx, smCtx.Supi, sdmSubscription)
defer func() {
if httpResp != nil {
if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil {
logger.ConsumerLog.Errorf("Subscribe response body cannot close: %+v",
rspCloseErr)
}
}
}()

if localErr == nil {
smf_context.GetSelf().Ues.SetSubscriptionId(smCtx.Supi, resSubscription.SubscriptionId)
logger.PduSessLog.Infoln("SDM Subscription Successful UE:", smCtx.Supi, "SubscriptionId:",
resSubscription.SubscriptionId)
} 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")
}

smf_context.GetSelf().Ues.IncrementPduSessionCount(smCtx.Supi)
return nil, nil
}

func (s *nudmService) UnSubscribe(smCtx *smf_context.SMContext) (
*models.ProblemDetails, error,
) {
ctx, _, oauthErr := smf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDM_SDM, models.NfType_UDM)
if oauthErr != nil {
return nil, fmt.Errorf("Get Token Context Error[%v]", oauthErr)
}

if smf_context.GetSelf().Ues.IsLastPduSession(smCtx.Supi) {
var client *Nudm_SubscriberDataManagement.APIClient
for _, service := range *s.consumer.Context().UDMProfile.NfServices {
if service.ServiceName == models.ServiceName_NUDM_SDM {
sdmUri := util.SearchNFServiceUri(smf_context.GetSelf().UDMProfile, models.ServiceName_NUDM_SDM,
models.NfServiceStatus_REGISTERED)
if sdmUri == "" {
return nil, errors.Errorf("SMF can not select an UDM by NRF: SearchNFServiceUri failed")
}

SDMConf := Nudm_SubscriberDataManagement.NewConfiguration()
SDMConf.SetBasePath(sdmUri)

client = Nudm_SubscriberDataManagement.NewAPIClient(SDMConf)
}
}

if client == nil {
return nil, fmt.Errorf("sdm client failed")
}

subscriptionId := smf_context.GetSelf().Ues.GetSubscriptionId(smCtx.Supi)

httpResp, localErr := client.SubscriptionDeletionApi.Unsubscribe(ctx, smCtx.Supi, subscriptionId)
defer func() {
if httpResp != nil {
if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil {
logger.PduSessLog.Errorf("Unsubscribe response body cannot close: %+v",
rspCloseErr)
}
}
}()
if localErr == nil {
logger.PduSessLog.Infoln("SDM UnSubscription Successful UE:", smCtx.Supi, "SubscriptionId:",
subscriptionId)
} else if httpResp != nil {

Check failure on line 303 in internal/sbi/consumer/udm_service.go

View workflow job for this annotation

GitHub Actions / lint (1.21)

unnecessary leading newline (whitespace)

if httpResp.Status != localErr.Error() {
return nil, localErr
}
problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails)

return &problem, nil

} else {

Check failure on line 312 in internal/sbi/consumer/udm_service.go

View workflow job for this annotation

GitHub Actions / lint (1.21)

unnecessary trailing newline (whitespace)
return nil, openapi.ReportError("server no response")
}
smf_context.GetSelf().Ues.DeleteUe(smCtx.Supi)

} else {

Check failure on line 317 in internal/sbi/consumer/udm_service.go

View workflow job for this annotation

GitHub Actions / lint (1.21)

unnecessary trailing newline (whitespace)
smf_context.GetSelf().Ues.DecrementPduSessionCount(smCtx.Supi)
}

return nil, nil
}
38 changes: 38 additions & 0 deletions internal/sbi/processor/pdu_session.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,17 @@ func (p *Processor) HandlePDUSessionSMContextCreate(
}
}

if !smf_context.GetSelf().Ues.UeExists(smContext.Supi) {
if problemDetails, err := p.Consumer().
Subscribe(ctx, smContext, smPlmnID); problemDetails != nil {
smContext.Log.Errorln("SDM Subscription Failed Problem:", problemDetails)
} else if err != nil {
smContext.Log.Errorln("SDM Subscription Error:", err)
}
} else {
smf_context.GetSelf().Ues.IncrementPduSessionCount(smContext.Supi)
}

establishmentRequest := m.PDUSessionEstablishmentRequest
if err := HandlePDUSessionEstablishmentRequest(smContext, establishmentRequest); err != nil {
smContext.Log.Errorf("PDU Session Establishment fail by %s", err)
Expand Down Expand Up @@ -325,6 +336,15 @@ func (p *Processor) HandlePDUSessionSMContextUpdate(
}
}

if smf_context.GetSelf().Ues.UeExists(smContext.Supi) {
problemDetails, err := p.Consumer().UnSubscribe(smContext)

Check failure on line 340 in internal/sbi/processor/pdu_session.go

View workflow job for this annotation

GitHub Actions / lint (1.21)

shadow: declaration of "err" shadows declaration at line 266 (govet)
if problemDetails != nil {
logger.PduSessLog.Errorf("SDM UnSubscription Failed Problem[%+v]", problemDetails)
} else if err != nil {
logger.PduSessLog.Errorf("SDM UnSubscription Error[%+v]", err)
}
}

if smContext.UeCmRegistered {
problemDetails, errUeCmDeregistration := p.Consumer().UeCmDeregistration(smContext)
if problemDetails != nil {
Expand Down Expand Up @@ -878,6 +898,15 @@ func (p *Processor) HandlePDUSessionSMContextRelease(
}
}

if smf_context.GetSelf().Ues.UeExists(smContext.Supi) {
problemDetails, err := p.Consumer().UnSubscribe(smContext)
if problemDetails != nil {
logger.PduSessLog.Errorf("SDM UnSubscription Failed Problem[%+v]", problemDetails)
} else if err != nil {
logger.PduSessLog.Errorf("SDM UnSubscription Error[%+v]", err)
}
}

if smContext.UeCmRegistered {
problemDetails, err := p.Consumer().UeCmDeregistration(smContext)
if problemDetails != nil {
Expand Down Expand Up @@ -969,6 +998,15 @@ func (p *Processor) HandlePDUSessionSMContextLocalRelease(
}
}

if smf_context.GetSelf().Ues.UeExists(smContext.Supi) {
problemDetails, err := p.Consumer().UnSubscribe(smContext)
if problemDetails != nil {
logger.PduSessLog.Errorf("SDM UnSubscription Failed Problem[%+v]", problemDetails)
} else if err != nil {
logger.PduSessLog.Errorf("SDM UnSubscription Error[%+v]", err)
}
}

if smContext.UeCmRegistered {
problemDetails, err := p.Consumer().UeCmDeregistration(smContext)
if problemDetails != nil {
Expand Down
9 changes: 9 additions & 0 deletions internal/sbi/processor/sm_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ func (p *Processor) RemoveSMContextFromAllNF(smContext *smf_context.SMContext, s
}
}

if smf_context.GetSelf().Ues.UeExists(smContext.Supi) {
problemDetails, err := p.Consumer().UnSubscribe(smContext)
if problemDetails != nil {
smContext.Log.Errorf("SDM UnSubscription Failed Problem[%+v]", problemDetails)
} else if err != nil {
smContext.Log.Errorf("SDM UnSubscription Error[%+v]", err)
}
}

// Because the amfUE who called this SMF API is being locked until the API Handler returns,
// sending SMContext Status Notification should run asynchronously
// so that this function returns immediately.
Expand Down

0 comments on commit 479c658

Please sign in to comment.