Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for SDM Subscription and Unsubscription #95

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ type SMFContext struct {
UEDefaultPathPool map[string]*UEDefaultPaths
LocalSEIDCount uint64

Ues *Ues

// Each pdu session should have a unique charging id
ChargingIDGenerator *idgenerator.IDGenerator
}
Expand Down Expand Up @@ -261,6 +263,9 @@ func InitSmfContext(config *factory.Config) {

smfContext.Locality = configuration.Locality


smfContext.Ues = InitSmfUeData()

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

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
}
178 changes: 178 additions & 0 deletions internal/sbi/consumer/subscriber_data_management.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package consumer

import (
"github.com/antihax/optional"
"github.com/pkg/errors"

"github.com/free5gc/openapi"
"github.com/free5gc/openapi/Nudm_SubscriberDataManagement"
"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 SDMGetSmData(smCtx *smf_context.SMContext,
smPlmnID *models.PlmnId,
) (problemDetails *models.ProblemDetails, err error) {
// Query UDM
if problemDetails, err = SendNFDiscoveryUDM(); err != nil {
smCtx.Log.Warnf("Send NF Discovery Serving UDM Error[%v]", err)
} else if problemDetails != nil {
smCtx.Log.Warnf("Send NF Discovery Serving UDM Problem[%+v]", problemDetails)
} else {
smCtx.Log.Infoln("Send NF Discovery Serving UDM Successfully")
}

smDataParams := &Nudm_SubscriberDataManagement.GetSmDataParamOpts{
Dnn: optional.NewString(smCtx.Dnn),
PlmnId: optional.NewInterface(openapi.MarshToJsonString(smPlmnID)),
SingleNssai: optional.NewInterface(openapi.MarshToJsonString(smCtx.SNssai)),
}

SubscriberDataManagementClient := smf_context.GetSelf().SubscriberDataManagementClient

ctx, pd, oauthErr := smf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDM_SDM, models.NfType_UDM)
if oauthErr != nil {
return pd, oauthErr
}

sessSubData, rsp, localErr := SubscriberDataManagementClient.
SessionManagementSubscriptionDataRetrievalApi.
GetSmData(ctx, smCtx.Supi, smDataParams)
if localErr == nil {
defer func() {
if rspCloseErr := rsp.Body.Close(); rspCloseErr != nil {
logger.ConsumerLog.Errorf("GetSmData response body cannot close: %+v", rspCloseErr)
}
}()
if len(sessSubData) > 0 {
smCtx.DnnConfiguration = sessSubData[0].DnnConfigurations[smCtx.Dnn]
// UP Security info present in session management subscription data
if smCtx.DnnConfiguration.UpSecurity != nil {
smCtx.UpSecurity = smCtx.DnnConfiguration.UpSecurity
}
} else {
logger.ConsumerLog.Errorln("SessionManagementSubscriptionData from UDM is nil")
err = openapi.ReportError("SmData is nil")
}
} else if rsp != nil {
if rsp.Status != localErr.Error() {
err = localErr
return nil, err
}
problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails)
problemDetails = &problem
} else {
logger.ConsumerLog.Errorln("Get SessionManagementSubscriptionData error:", localErr)
err = localErr
}

return problemDetails, err
}

func SDMSubscribe(smCtx *smf_context.SMContext, smPlmnID *models.PlmnId) (
problemDetails *models.ProblemDetails, err error,
) {
if !smf_context.GetSelf().Ues.UeExists(smCtx.Supi) {
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")
}

configuration := Nudm_SubscriberDataManagement.NewConfiguration()
configuration.SetBasePath(sdmUri)
client := Nudm_SubscriberDataManagement.NewAPIClient(configuration)

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

ctx, pd, oauthErr := smf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDM_SDM, models.NfType_UDM)
if oauthErr != nil {
return pd, oauthErr
}

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.ConsumerLog.Infoln("SDM Subscription Successful UE:", smCtx.Supi, "SubscriptionId:",
resSubscription.SubscriptionId)
} else if httpResp != nil {
if httpResp.Status != localErr.Error() {
err = localErr
return problemDetails, err
}
problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails)
problemDetails = &problem
} else {
err = openapi.ReportError("server no response")
}
}

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

func SDMUnSubscribe(smCtx *smf_context.SMContext) (problemDetails *models.ProblemDetails,
err error,
) {
if smf_context.GetSelf().Ues.UeExists(smCtx.Supi) {
if smf_context.GetSelf().Ues.IsLastPduSession(smCtx.Supi) {
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")
}
configuration := Nudm_SubscriberDataManagement.NewConfiguration()
configuration.SetBasePath(sdmUri)

client := Nudm_SubscriberDataManagement.NewAPIClient(configuration)
subscriptionId := smf_context.GetSelf().Ues.GetSubscriptionId(smCtx.Supi)

ctx, pd, oauthErr := smf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDM_SDM, models.NfType_UDM)
if oauthErr != nil {
return pd, oauthErr
}

httpResp, localErr := client.SubscriptionDeletionApi.Unsubscribe(ctx, smCtx.Supi, subscriptionId)
defer func() {
if httpResp != nil {
if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil {
logger.ConsumerLog.Errorf("Unsubscribe response body cannot close: %+v",
rspCloseErr)
}
}
}()
if localErr == nil {
logger.ConsumerLog.Infoln("SDM UnSubscription Successful UE:", smCtx.Supi, "SubscriptionId:",
subscriptionId)
} else if httpResp != nil {
if httpResp.Status != localErr.Error() {
err = localErr
return problemDetails, err
}
problem := localErr.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails)
problemDetails = &problem
} else {
err = openapi.ReportError("server no response")
}
smf_context.GetSelf().Ues.DeleteUe(smCtx.Supi)
} else {
smf_context.GetSelf().Ues.DecrementPduSessionCount(smCtx.Supi)
}
}
return problemDetails, err
}
Loading
Loading