From e4d31e8a09179414a31a18d3851f74c818f8f902 Mon Sep 17 00:00:00 2001 From: Sheng <51371214+YouShengLiu@users.noreply.github.com> Date: Tue, 12 Sep 2023 15:16:08 +0800 Subject: [PATCH] Feature/cfg update cmd (#105) * Add IE, universal time, in the configuration update command message: * Add TimeZone field in the AMFContext. * Fix bug: the format of time zone is wrong * Modify BuildConfigurationUpdateCommand * Update go module * Modify the BuildConfigurationUpdateCommand and SendConfigurationUpdateCommand functions: * Enhance the generality of the BuildConfigurationUpdateCommand function. * Decouple the BuildConfigurationUpdateCommand from the SendConfigurationUpdateCommand, enabling finer customization of the Configuration Update Command message. * Add T3555 timer for retransmission of CONFIGURATION UPDATE COMMAND message. * Leave two TODOs in AmPolicyControlUpdateNotifyUpdateProcedure function. * Modify coding style * Stop T3555 timer in HandleConfigurationUpdateComplete function * Modify BuildConfigurationUpdateCommand * Reduce the number of parameters. * AmPolicyControlUpdateNotifyUpdateProcedure keeps its original functionality with the configurationUpdateCommandFlags. * Modify BuildConfigurationUpdateCommand * Show some warning message while the required argument is nothing. * Check the Configuration Update Command message if is valid. * Modify SendConfigurationUpdateCommand * Add ConfigurationUpdateCommandFlags parameter to control how to build the Configuration Update Command message. * Determine whether to start the T3555 timer or not in the SendConfigurationUpdateCommand. * Modify SendConfigurationUpdateCommand * Add the missing return. * Modify BuildConfigurationUpdateCommand and SendConfigurationUpdateCommand * Fix bug: the original getTimeZone function was wrong * Aggregate the same function into nasConvert.GetTimeZone * Update go module * Modify BuildConfigurationUpdateCommand * Remove the parameter NetworkSlicingIndication * Modify AmfUe structure * Replace ConfigurationUpdateMessage by ConfigurationUpdateCommandFlags * Modify HandleServiceRequest --- go.mod | 2 +- go.sum | 4 +- internal/context/amf_ue.go | 79 +++++++--- internal/context/context.go | 22 +-- internal/gmm/common/timer.go | 4 + internal/gmm/handler.go | 17 ++- internal/gmm/message/build.go | 243 +++++++++++++++++++----------- internal/gmm/message/send.go | 27 +++- internal/sbi/producer/callback.go | 24 ++- pkg/factory/config.go | 5 + 10 files changed, 280 insertions(+), 147 deletions(-) diff --git a/go.mod b/go.mod index 78290431..374e7a6f 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/davecgh/go-spew v1.1.1 github.com/free5gc/aper v1.0.5-0.20230614030933-c73735898582 - github.com/free5gc/nas v1.1.2-0.20230731044306-324a5cc4c35e + github.com/free5gc/nas v1.1.2-0.20230828074825-175b09665828 github.com/free5gc/ngap v1.0.7-0.20230614061954-9c128114ab1f github.com/free5gc/openapi v1.0.7-0.20230802173229-2b3ded4db293 github.com/free5gc/util v1.0.5-0.20230823103219-e511c4fd20ef diff --git a/go.sum b/go.sum index 13c2b893..4de6abbf 100644 --- a/go.sum +++ b/go.sum @@ -68,8 +68,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7 github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/free5gc/aper v1.0.5-0.20230614030933-c73735898582 h1:IV9PXKo6MH62e7nngSqr+cwjuoffkouPMmyX3jHC9Ds= github.com/free5gc/aper v1.0.5-0.20230614030933-c73735898582/go.mod h1:ybHxhYnRqQ9wD4yB9r/3MZdbCYCjtqUyfLpSnJpwWd4= -github.com/free5gc/nas v1.1.2-0.20230731044306-324a5cc4c35e h1:M2vWqCRQKa8hgJzbJkPrswFEe8lJoRTD/CmMwbJoW+Q= -github.com/free5gc/nas v1.1.2-0.20230731044306-324a5cc4c35e/go.mod h1:fjWwpyp7/wOyL72HTkjvIe9YTCfGyZosjITsI5sXyuU= +github.com/free5gc/nas v1.1.2-0.20230828074825-175b09665828 h1:/gIlzF8hacxWa6hsx+HQlw9F8yEorTvZvS1VR5Iwpjc= +github.com/free5gc/nas v1.1.2-0.20230828074825-175b09665828/go.mod h1:fjWwpyp7/wOyL72HTkjvIe9YTCfGyZosjITsI5sXyuU= github.com/free5gc/ngap v1.0.7-0.20230614061954-9c128114ab1f h1:wgXjoknZ7JJoZ72J15g/f2/0DgdCpfcTg189lnhUPuY= github.com/free5gc/ngap v1.0.7-0.20230614061954-9c128114ab1f/go.mod h1:lKA1sLTYM3CGEBhZVxkGGJIkai5+Bvy2yHIMhb7Vx/k= github.com/free5gc/openapi v1.0.6/go.mod h1:iw/N0E+FlX44EEx24IBi2EdZW8v+bkj3ETWPGnlK9DI= diff --git a/internal/context/amf_ue.go b/internal/context/amf_ue.go index 132e1ba2..a387ffae 100644 --- a/internal/context/amf_ue.go +++ b/internal/context/amf_ue.go @@ -71,20 +71,19 @@ type AmfUe struct { /* Used for AMF relocation */ TargetAmfProfile *models.NfProfile TargetAmfUri string - /* Ue Identity*/ - PlmnId models.PlmnId - Suci string - Supi string - UnauthenticatedSupi bool - Gpsi string - Pei string - Tmsi int32 // 5G-Tmsi - Guti string - GroupID string - EBI int32 - /* Ue Identity*/ + /* Ue Identity */ + PlmnId models.PlmnId + Suci string + Supi string + UnauthenticatedSupi bool + Gpsi string + Pei string + Tmsi int32 // 5G-Tmsi + Guti string + GroupID string + EBI int32 EventSubscriptionsInfo map[string]*AmfUeEventSubscription - /* User Location*/ + /* User Location */ RatType models.RatType Location models.UserLocation Tai models.Tai @@ -122,8 +121,7 @@ type AmfUe struct { AmPolicyUri string AmPolicyAssociation *models.PolicyAssociation RequestTriggerLocationChange bool // true if AmPolicyAssociation.Trigger contains RequestTrigger_LOC_CH - ConfigurationUpdateMessage []byte - /* UeContextForHandover*/ + /* UeContextForHandover */ HandoverNotifyUri string /* N1N2Message */ N1N2MessageIDGenerator *idgenerator.IDGenerator @@ -133,13 +131,14 @@ type AmfUe struct { N1N2MessageSubscription sync.Map /* Pdu Sesseion context */ SmContextList sync.Map // map[int32]*SmContext, pdu session id as key - /* Related Context*/ + /* Related Context */ RanUe map[models.AccessType]*RanUe /* other */ - onGoing map[models.AccessType]*OnGoing - UeRadioCapability string // OCTET string - Capability5GMM nasType.Capability5GMM - ConfigurationUpdateIndication nasType.ConfigurationUpdateIndication + onGoing map[models.AccessType]*OnGoing + UeRadioCapability string // OCTET string + Capability5GMM nasType.Capability5GMM + ConfigurationUpdateIndication nasType.ConfigurationUpdateIndication + ConfigurationUpdateCommandFlags *ConfigurationUpdateCommandFlags /* context related to Paging */ UeRadioCapabilityForPaging *UERadioCapabilityForPaging InfoOnRecommendedCellsAndRanNodesForPaging *InfoOnRecommendedCellsAndRanNodesForPaging @@ -183,6 +182,8 @@ type AmfUe struct { T3522 *Timer /* T3570 (for identity request) */ T3570 *Timer + /* T3555 (for configuration update command) */ + T3555 *Timer /* Ue Context Release Cause */ ReleaseCause map[models.AccessType]*CauseAll /* T3502 (Assigned by AMF, and used by UE to initialize registration procedure) */ @@ -245,6 +246,22 @@ type NGRANCGI struct { EUTRACGI *models.Ecgi } +// TS 24.501 8.2.19 +type ConfigurationUpdateCommandFlags struct { + NeedGUTI bool + NeedNITZ bool + NeedTaiList bool + NeedRejectNSSAI bool + NeedAllowedNSSAI bool + NeedSmsIndication bool + NeedMicoIndication bool + NeedLadnInformation bool + NeedServiceAreaList bool + NeedConfiguredNSSAI bool + NeedNetworkSlicingIndication bool + NeedOperatordefinedAccessCategoryDefinitions bool +} + func (ue *AmfUe) init() { ue.servingAMF = GetSelf() ue.State = make(map[models.AccessType]*fsm.State) @@ -290,6 +307,8 @@ func (ue *AmfUe) Remove() { ue.StopT3560() ue.StopT3550() ue.StopT3522() + ue.StopT3570() + ue.StopT3555() for _, ranUe := range ue.RanUe { if err := ranUe.Remove(); err != nil { @@ -889,3 +908,23 @@ func (ue *AmfUe) StopT3522() { ue.T3522.Stop() ue.T3522 = nil // clear the timer } + +func (ue *AmfUe) StopT3570() { + if ue.T3570 == nil { + return + } + + ue.GmmLog.Infof("Stop T3570 timer") + ue.T3570.Stop() + ue.T3570 = nil // clear the timer +} + +func (ue *AmfUe) StopT3555() { + if ue.T3555 == nil { + return + } + + ue.GmmLog.Infof("Stop T3555 timer") + ue.T3555.Stop() + ue.T3555 = nil // clear the timer +} diff --git a/internal/context/context.go b/internal/context/context.go index c5fcf703..bc37af80 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -14,6 +14,7 @@ import ( "github.com/free5gc/amf/internal/logger" "github.com/free5gc/amf/pkg/factory" + "github.com/free5gc/nas/nasConvert" "github.com/free5gc/nas/security" "github.com/free5gc/openapi" "github.com/free5gc/openapi/models" @@ -80,6 +81,7 @@ type AMFContext struct { T3560Cfg factory.TimerValue T3565Cfg factory.TimerValue T3570Cfg factory.TimerValue + T3555Cfg factory.TimerValue Locality string } @@ -130,7 +132,7 @@ func InitAmfContext(context *AMFContext) { context.SecurityAlgorithm.CipheringOrder = getEncAlgOrder(security.CipheringOrder) } context.NetworkName = configuration.NetworkName - context.TimeZone = getTimeZone(time.Now()) + context.TimeZone = nasConvert.GetTimeZone(time.Now()) context.T3502Value = configuration.T3502Value context.T3512Value = configuration.T3512Value context.Non3gppDeregTimerValue = configuration.Non3gppDeregTimerValue @@ -140,26 +142,10 @@ func InitAmfContext(context *AMFContext) { context.T3560Cfg = configuration.T3560 context.T3565Cfg = configuration.T3565 context.T3570Cfg = configuration.T3570 + context.T3555Cfg = configuration.T3555 context.Locality = configuration.Locality } -func getTimeZone(now time.Time) string { - timezone := "" - _, offset := now.Zone() - if offset < 0 { - timezone += "-" - offset = 0 - offset - } else { - timezone += "+" - } - timezone += fmt.Sprintf("%02d:%02d", offset/3600, (offset%3600)/60) - if now.IsDST() { - timezone += "+1" - } - - return timezone -} - func getIntAlgOrder(integrityOrder []string) (intOrder []uint8) { for _, intAlg := range integrityOrder { switch intAlg { diff --git a/internal/gmm/common/timer.go b/internal/gmm/common/timer.go index 63200b74..c49ef64f 100644 --- a/internal/gmm/common/timer.go +++ b/internal/gmm/common/timer.go @@ -35,4 +35,8 @@ func StopAll5GSMMTimers(ue *context.AmfUe) { ue.T3570.Stop() ue.T3570 = nil // clear the timer } + if ue.T3555 != nil { + ue.T3555.Stop() + ue.T3555 = nil // clear the timer + } } diff --git a/internal/gmm/handler.go b/internal/gmm/handler.go index 2191ee0a..9cbf2489 100644 --- a/internal/gmm/handler.go +++ b/internal/gmm/handler.go @@ -1582,7 +1582,8 @@ func HandleConfigurationUpdateComplete(ue *context.AmfUe, return fmt.Errorf("NAS message integrity check failed") } - // TODO: Stop timer T3555 in TS 24.501 Figure 5.4.4.1.1 in handler + // Stop timer T3555 in TS 24.501 Figure 5.4.4.1.1 in handler + ue.StopT3555() // TODO: Send acknowledgment by Nudm_SMD_Info_Service to UDM in handler // import "github.com/free5gc/openapi/Nudm_SubscriberDataManagement" client.Info @@ -1849,16 +1850,14 @@ func HandleServiceRequest(ue *context.AmfUe, anType models.AccessType, } // downlink signaling - if ue.ConfigurationUpdateMessage != nil { + if ue.ConfigurationUpdateCommandFlags != nil { err := gmm_message.SendServiceAccept(ue, anType, cxtList, pduStatusResult, reactivationResult, errPduSessionId, errCause) if err != nil { return err } - mobilityRestrictionList := ngap_message.BuildIEMobilityRestrictionList(ue) - ngap_message.SendDownlinkNasTransport(ue.RanUe[models.AccessType__3_GPP_ACCESS], - ue.ConfigurationUpdateMessage, &mobilityRestrictionList) - ue.ConfigurationUpdateMessage = nil + gmm_message.SendConfigurationUpdateCommand(ue, anType, ue.ConfigurationUpdateCommandFlags) + ue.ConfigurationUpdateCommandFlags = nil } case nasMessage.ServiceTypeData: if anType == models.AccessType__3_GPP_ACCESS { @@ -2162,6 +2161,12 @@ func HandleRegistrationComplete(ue *context.AmfUe, accessType models.AccessType, }) } + // Send NITZ information to UE + configurationUpdateCommandFlags := &context.ConfigurationUpdateCommandFlags{ + NeedNITZ: true, + } + gmm_message.SendConfigurationUpdateCommand(ue, accessType, configurationUpdateCommandFlags) + // if registrationComplete.SORTransparentContainer != nil { // TODO: if at regsitration procedure 14b, udm provide amf Steering of Roaming info & request an ack, // AMF provides the UE's ack with Nudm_SDM_Info (SOR not supportted in this stage) diff --git a/internal/gmm/message/build.go b/internal/gmm/message/build.go index a7450f6e..f9f93a8e 100644 --- a/internal/gmm/message/build.go +++ b/internal/gmm/message/build.go @@ -726,9 +726,11 @@ func BuildStatus5GMM(ue *context.AmfUe, accessType models.AccessType, cause uint return nas_security.Encode(ue, m, accessType) } +// Fllowed by TS 24.501 - 5.4.4 Generic UE configuration update procedure - 5.4.4.1 General func BuildConfigurationUpdateCommand(ue *context.AmfUe, anType models.AccessType, - networkSlicingIndication *nasType.NetworkSlicingIndication, -) ([]byte, error) { + flags *context.ConfigurationUpdateCommandFlags, +) ([]byte, error, bool) { + needTimer := false m := nas.NewMessage() m.GmmMessage = nas.NewGmmMessage() m.GmmHeader.SetMessageType(nas.MsgTypeConfigurationUpdateCommand) @@ -739,119 +741,183 @@ func BuildConfigurationUpdateCommand(ue *context.AmfUe, anType models.AccessType configurationUpdateCommand.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) configurationUpdateCommand.SetMessageType(nas.MsgTypeConfigurationUpdateCommand) - if ue.ConfigurationUpdateIndication.Octet != 0 { - configurationUpdateCommand.ConfigurationUpdateIndication = nasType. - NewConfigurationUpdateIndication(nasMessage.ConfigurationUpdateCommandConfigurationUpdateIndicationType) - configurationUpdateCommand.ConfigurationUpdateIndication = &ue.ConfigurationUpdateIndication - } - - if networkSlicingIndication != nil { + if flags.NeedNetworkSlicingIndication { configurationUpdateCommand.NetworkSlicingIndication = nasType. NewNetworkSlicingIndication(nasMessage.ConfigurationUpdateCommandNetworkSlicingIndicationType) - configurationUpdateCommand.NetworkSlicingIndication = networkSlicingIndication + configurationUpdateCommand.NetworkSlicingIndication.SetNSSCI(0x01) } - if ue.Guti != "" { - gutiNas, err := nasConvert.GutiToNasWithError(ue.Guti) - if err != nil { - return nil, fmt.Errorf("encode GUTI failed: %w", err) + if flags.NeedGUTI { + if ue.Guti != "" { + gutiNas, err := nasConvert.GutiToNasWithError(ue.Guti) + if err != nil { + return nil, fmt.Errorf("encode GUTI failed: %w", err), needTimer + } + configurationUpdateCommand.GUTI5G = &gutiNas + configurationUpdateCommand.GUTI5G.SetIei(nasMessage.ConfigurationUpdateCommandGUTI5GType) + } else { + logger.GmmLog.Warnf("Require 5G-GUTI, but got nothing.") } - configurationUpdateCommand.GUTI5G = &gutiNas - configurationUpdateCommand.GUTI5G.SetIei(nasMessage.ConfigurationUpdateCommandGUTI5GType) } - if len(ue.RegistrationArea[anType]) > 0 { - configurationUpdateCommand.TAIList = nasType.NewTAIList(nasMessage.ConfigurationUpdateCommandTAIListType) - taiListNas := nasConvert.TaiListToNas(ue.RegistrationArea[anType]) - configurationUpdateCommand.TAIList.SetLen(uint8(len(taiListNas))) - configurationUpdateCommand.TAIList.SetPartialTrackingAreaIdentityList(taiListNas) - } + if flags.NeedAllowedNSSAI { + if len(ue.AllowedNssai[anType]) > 0 { + configurationUpdateCommand.AllowedNSSAI = nasType. + NewAllowedNSSAI(nasMessage.ConfigurationUpdateCommandAllowedNSSAIType) - if len(ue.AllowedNssai[anType]) > 0 { - configurationUpdateCommand.AllowedNSSAI = nasType. - NewAllowedNSSAI(nasMessage.ConfigurationUpdateCommandAllowedNSSAIType) - var buf []uint8 - for _, allowedSnssai := range ue.AllowedNssai[anType] { - buf = append(buf, nasConvert.SnssaiToNas(*allowedSnssai.AllowedSnssai)...) + var buf []uint8 + for _, allowedSnssai := range ue.AllowedNssai[anType] { + buf = append(buf, nasConvert.SnssaiToNas(*allowedSnssai.AllowedSnssai)...) + } + configurationUpdateCommand.AllowedNSSAI.SetLen(uint8(len(buf))) + configurationUpdateCommand.AllowedNSSAI.SetSNSSAIValue(buf) + } else { + logger.GmmLog.Warnf("Require Allowed NSSAI, but got nothing.") } - configurationUpdateCommand.AllowedNSSAI.SetLen(uint8(len(buf))) - configurationUpdateCommand.AllowedNSSAI.SetSNSSAIValue(buf) } - if len(ue.ConfiguredNssai) > 0 { - configurationUpdateCommand.ConfiguredNSSAI = nasType. - NewConfiguredNSSAI(nasMessage.ConfigurationUpdateCommandConfiguredNSSAIType) - var buf []uint8 - for _, snssai := range ue.ConfiguredNssai { - buf = append(buf, nasConvert.SnssaiToNas(*snssai.ConfiguredSnssai)...) + if flags.NeedConfiguredNSSAI { + if len(ue.ConfiguredNssai) > 0 { + configurationUpdateCommand.ConfiguredNSSAI = nasType. + NewConfiguredNSSAI(nasMessage.ConfigurationUpdateCommandConfiguredNSSAIType) + + var buf []uint8 + for _, snssai := range ue.ConfiguredNssai { + buf = append(buf, nasConvert.SnssaiToNas(*snssai.ConfiguredSnssai)...) + } + configurationUpdateCommand.ConfiguredNSSAI.SetLen(uint8(len(buf))) + configurationUpdateCommand.ConfiguredNSSAI.SetSNSSAIValue(buf) + } else { + logger.GmmLog.Warnf("Require Configured NSSAI, but got nothing.") } - configurationUpdateCommand.ConfiguredNSSAI.SetLen(uint8(len(buf))) - configurationUpdateCommand.ConfiguredNSSAI.SetSNSSAIValue(buf) } - if ue.NetworkSliceInfo != nil { - if len(ue.NetworkSliceInfo.RejectedNssaiInPlmn) != 0 || len(ue.NetworkSliceInfo.RejectedNssaiInTa) != 0 { + if flags.NeedRejectNSSAI { + if ue.NetworkSliceInfo != nil && + (len(ue.NetworkSliceInfo.RejectedNssaiInPlmn) != 0 || len(ue.NetworkSliceInfo.RejectedNssaiInTa) != 0) { rejectedNssaiNas := nasConvert.RejectedNssaiToNas( ue.NetworkSliceInfo.RejectedNssaiInPlmn, ue.NetworkSliceInfo.RejectedNssaiInTa) configurationUpdateCommand.RejectedNSSAI = &rejectedNssaiNas configurationUpdateCommand.RejectedNSSAI.SetIei(nasMessage.ConfigurationUpdateCommandRejectedNSSAIType) + } else { + logger.GmmLog.Warnf("Require Rejected NSSAI, but got nothing.") } } - if anType == models.AccessType__3_GPP_ACCESS && ue.AmPolicyAssociation != nil && - ue.AmPolicyAssociation.ServAreaRes != nil { - configurationUpdateCommand.ServiceAreaList = nasType. - NewServiceAreaList(nasMessage.ConfigurationUpdateCommandServiceAreaListType) - partialServiceAreaList := nasConvert. - PartialServiceAreaListToNas(ue.PlmnId, *ue.AmPolicyAssociation.ServAreaRes) - configurationUpdateCommand.ServiceAreaList.SetLen(uint8(len(partialServiceAreaList))) - configurationUpdateCommand.ServiceAreaList.SetPartialServiceAreaList(partialServiceAreaList) + if flags.NeedTaiList && anType == models.AccessType__3_GPP_ACCESS { + if len(ue.RegistrationArea[anType]) > 0 { + configurationUpdateCommand.TAIList = nasType.NewTAIList(nasMessage.ConfigurationUpdateCommandTAIListType) + taiListNas := nasConvert.TaiListToNas(ue.RegistrationArea[anType]) + configurationUpdateCommand.TAIList.SetLen(uint8(len(taiListNas))) + configurationUpdateCommand.TAIList.SetPartialTrackingAreaIdentityList(taiListNas) + } else { + logger.GmmLog.Warnf("Require TAI List, but got nothing.") + } } - amfSelf := context.GetSelf() - if amfSelf.NetworkName.Full != "" { - fullNetworkName := nasConvert.FullNetworkNameToNas(amfSelf.NetworkName.Full) - configurationUpdateCommand.FullNameForNetwork = &fullNetworkName - configurationUpdateCommand.FullNameForNetwork.SetIei(nasMessage.ConfigurationUpdateCommandFullNameForNetworkType) + if flags.NeedServiceAreaList && anType == models.AccessType__3_GPP_ACCESS { + if ue.AmPolicyAssociation != nil && ue.AmPolicyAssociation.ServAreaRes != nil { + configurationUpdateCommand.ServiceAreaList = nasType. + NewServiceAreaList(nasMessage.ConfigurationUpdateCommandServiceAreaListType) + partialServiceAreaList := nasConvert. + PartialServiceAreaListToNas(ue.PlmnId, *ue.AmPolicyAssociation.ServAreaRes) + configurationUpdateCommand.ServiceAreaList.SetLen(uint8(len(partialServiceAreaList))) + configurationUpdateCommand.ServiceAreaList.SetPartialServiceAreaList(partialServiceAreaList) + } else { + logger.GmmLog.Warnf("Require Service Area List, but got nothing.") + } } - if amfSelf.NetworkName.Short != "" { - shortNetworkName := nasConvert.ShortNetworkNameToNas(amfSelf.NetworkName.Short) - configurationUpdateCommand.ShortNameForNetwork = &shortNetworkName - configurationUpdateCommand.ShortNameForNetwork.SetIei(nasMessage.ConfigurationUpdateCommandShortNameForNetworkType) + if flags.NeedLadnInformation && anType == models.AccessType__3_GPP_ACCESS { + if len(ue.LadnInfo) > 0 { + configurationUpdateCommand.LADNInformation = nasType. + NewLADNInformation(nasMessage.ConfigurationUpdateCommandLADNInformationType) + var buf []uint8 + for _, ladn := range ue.LadnInfo { + ladnNas := nasConvert.LadnToNas(ladn.Dnn, ladn.TaiList) + buf = append(buf, ladnNas...) + } + configurationUpdateCommand.LADNInformation.SetLen(uint16(len(buf))) + configurationUpdateCommand.LADNInformation.SetLADND(buf) + } else { + logger.GmmLog.Warnf("Require LADN Information, but got nothing.") + } } - now := time.Now() - universalTimeAndLocalTimeZone := nasConvert.EncodeUniversalTimeAndLocalTimeZoneToNas(now) - universalTimeAndLocalTimeZone.SetIei(nasMessage.ConfigurationUpdateCommandUniversalTimeAndLocalTimeZoneType) - configurationUpdateCommand.UniversalTimeAndLocalTimeZone = &universalTimeAndLocalTimeZone - - if ue.TimeZone != amfSelf.TimeZone { - ue.TimeZone = amfSelf.TimeZone - - localTimeZone := nasConvert.EncodeLocalTimeZoneToNas(ue.TimeZone) - localTimeZone.SetIei(nasMessage.ConfigurationUpdateCommandLocalTimeZoneType) - configurationUpdateCommand.LocalTimeZone = nasType. - NewLocalTimeZone(nasMessage.ConfigurationUpdateCommandLocalTimeZoneType) - configurationUpdateCommand.LocalTimeZone = &localTimeZone + amfSelf := context.GetSelf() - daylightSavingTime := nasConvert.EncodeDaylightSavingTimeToNas(ue.TimeZone) - daylightSavingTime.SetIei(nasMessage.ConfigurationUpdateCommandNetworkDaylightSavingTimeType) - configurationUpdateCommand.NetworkDaylightSavingTime = nasType. - NewNetworkDaylightSavingTime(nasMessage.ConfigurationUpdateCommandNetworkDaylightSavingTimeType) - configurationUpdateCommand.NetworkDaylightSavingTime = &daylightSavingTime + if flags.NeedNITZ { + // Full network name + if amfSelf.NetworkName.Full != "" { + fullNetworkName := nasConvert.FullNetworkNameToNas(amfSelf.NetworkName.Full) + configurationUpdateCommand.FullNameForNetwork = &fullNetworkName + configurationUpdateCommand.FullNameForNetwork.SetIei(nasMessage.ConfigurationUpdateCommandFullNameForNetworkType) + } else { + logger.GmmLog.Warnf("Require Full Network Name, but got nothing.") + } + // Short network name + if amfSelf.NetworkName.Short != "" { + shortNetworkName := nasConvert.ShortNetworkNameToNas(amfSelf.NetworkName.Short) + configurationUpdateCommand.ShortNameForNetwork = &shortNetworkName + configurationUpdateCommand.ShortNameForNetwork.SetIei(nasMessage.ConfigurationUpdateCommandShortNameForNetworkType) + } else { + logger.GmmLog.Warnf("Require Short Network Name, but got nothing.") + } + // Universal Time and Local Time Zone + now := time.Now() + universalTimeAndLocalTimeZone := nasConvert.EncodeUniversalTimeAndLocalTimeZoneToNas(now) + universalTimeAndLocalTimeZone.SetIei(nasMessage.ConfigurationUpdateCommandUniversalTimeAndLocalTimeZoneType) + configurationUpdateCommand.UniversalTimeAndLocalTimeZone = &universalTimeAndLocalTimeZone + + if ue.TimeZone != amfSelf.TimeZone { + ue.TimeZone = amfSelf.TimeZone + // Local Time Zone + localTimeZone := nasConvert.EncodeLocalTimeZoneToNas(ue.TimeZone) + localTimeZone.SetIei(nasMessage.ConfigurationUpdateCommandLocalTimeZoneType) + configurationUpdateCommand.LocalTimeZone = nasType. + NewLocalTimeZone(nasMessage.ConfigurationUpdateCommandLocalTimeZoneType) + configurationUpdateCommand.LocalTimeZone = &localTimeZone + // Daylight Saving Time + daylightSavingTime := nasConvert.EncodeDaylightSavingTimeToNas(ue.TimeZone) + daylightSavingTime.SetIei(nasMessage.ConfigurationUpdateCommandNetworkDaylightSavingTimeType) + configurationUpdateCommand.NetworkDaylightSavingTime = nasType. + NewNetworkDaylightSavingTime(nasMessage.ConfigurationUpdateCommandNetworkDaylightSavingTimeType) + configurationUpdateCommand.NetworkDaylightSavingTime = &daylightSavingTime + } } - if len(ue.LadnInfo) > 0 { - configurationUpdateCommand.LADNInformation = nasType. - NewLADNInformation(nasMessage.ConfigurationUpdateCommandLADNInformationType) - var buf []uint8 - for _, ladn := range ue.LadnInfo { - ladnNas := nasConvert.LadnToNas(ladn.Dnn, ladn.TaiList) - buf = append(buf, ladnNas...) - } - configurationUpdateCommand.LADNInformation.SetLen(uint16(len(buf))) - configurationUpdateCommand.LADNInformation.SetLADND(buf) + configurationUpdateCommand.ConfigurationUpdateIndication = nasType. + NewConfigurationUpdateIndication(nasMessage.ConfigurationUpdateCommandConfigurationUpdateIndicationType) + if configurationUpdateCommand.GUTI5G != nil || + configurationUpdateCommand.TAIList != nil || + configurationUpdateCommand.AllowedNSSAI != nil || + configurationUpdateCommand.LADNInformation != nil || + configurationUpdateCommand.ServiceAreaList != nil || + configurationUpdateCommand.MICOIndication != nil || + configurationUpdateCommand.ConfiguredNSSAI != nil || + configurationUpdateCommand.RejectedNSSAI != nil || + configurationUpdateCommand.NetworkSlicingIndication != nil || + configurationUpdateCommand.OperatordefinedAccessCategoryDefinitions != nil || + configurationUpdateCommand.SMSIndication != nil { + // TS 24.501 - 5.4.4.2 Generic UE configuration update procedure initiated by the network + // Acknowledgement shall be requested for all parameters except when only NITZ is included + configurationUpdateCommand.ConfigurationUpdateIndication.SetACK(uint8(1)) + needTimer = true + } + if configurationUpdateCommand.MICOIndication != nil { + // Allowed NSSAI and Configured NSSAI are optional to request to perform the registration procedure + configurationUpdateCommand.ConfigurationUpdateIndication.SetRED(uint8(1)) + } + + // Check if the Configuration Update Command is vaild + if configurationUpdateCommand.ConfigurationUpdateIndication.GetACK() == uint8(0) && + configurationUpdateCommand.ConfigurationUpdateIndication.GetRED() == uint8(0) && + (configurationUpdateCommand.FullNameForNetwork == nil && + configurationUpdateCommand.ShortNameForNetwork == nil && + configurationUpdateCommand.UniversalTimeAndLocalTimeZone == nil && + configurationUpdateCommand.LocalTimeZone == nil && + configurationUpdateCommand.NetworkDaylightSavingTime == nil) { + return nil, fmt.Errorf("Configuration Update Command is invaild"), false } m.GmmMessage.ConfigurationUpdateCommand = configurationUpdateCommand @@ -860,5 +926,10 @@ func BuildConfigurationUpdateCommand(ue *context.AmfUe, anType models.AccessType ProtocolDiscriminator: nasMessage.Epd5GSMobilityManagementMessage, SecurityHeaderType: nas.SecurityHeaderTypeIntegrityProtectedAndCiphered, } - return nas_security.Encode(ue, m, anType) + + b, err := nas_security.Encode(ue, m, anType) + if err != nil { + return nil, fmt.Errorf("BuildConfigurationUpdateCommand() err: %v", err), false + } + return b, err, needTimer } diff --git a/internal/gmm/message/send.go b/internal/gmm/message/send.go index 4df330ad..9297716e 100644 --- a/internal/gmm/message/send.go +++ b/internal/gmm/message/send.go @@ -9,7 +9,6 @@ import ( ngap_message "github.com/free5gc/amf/internal/ngap/message" "github.com/free5gc/amf/internal/sbi/producer/callback" "github.com/free5gc/nas/nasMessage" - "github.com/free5gc/nas/nasType" "github.com/free5gc/ngap/ngapType" "github.com/free5gc/openapi/models" ) @@ -179,8 +178,9 @@ func SendServiceAccept(amfUe *context.AmfUe, anType models.AccessType, return nil } -func SendConfigurationUpdateCommand(amfUe *context.AmfUe, accessType models.AccessType, - networkSlicingIndication *nasType.NetworkSlicingIndication, +func SendConfigurationUpdateCommand(amfUe *context.AmfUe, + accessType models.AccessType, + flags *context.ConfigurationUpdateCommandFlags, ) { if amfUe == nil { logger.GmmLog.Error("SendConfigurationUpdateCommand: AmfUe is nil") @@ -190,15 +190,30 @@ func SendConfigurationUpdateCommand(amfUe *context.AmfUe, accessType models.Acce logger.GmmLog.Error("SendConfigurationUpdateCommand: RanUe is nil") return } - amfUe.GmmLog.Info("Configuration Update Command") - nasMsg, err := BuildConfigurationUpdateCommand(amfUe, accessType, networkSlicingIndication) + nasMsg, err, startT3555 := BuildConfigurationUpdateCommand(amfUe, accessType, flags) if err != nil { - amfUe.GmmLog.Error(err.Error()) + amfUe.GmmLog.Errorf("BuildConfigurationUpdateCommand Error: %+v", err) return } + amfUe.GmmLog.Info("Send Configuration Update Command") + mobilityRestrictionList := ngap_message.BuildIEMobilityRestrictionList(amfUe) ngap_message.SendDownlinkNasTransport(amfUe.RanUe[accessType], nasMsg, &mobilityRestrictionList) + + if startT3555 && context.GetSelf().T3555Cfg.Enable { + cfg := context.GetSelf().T3555Cfg + amfUe.GmmLog.Infof("Start T3555 timer") + amfUe.T3555 = context.NewTimer(cfg.ExpireTime, cfg.MaxRetryTimes, func(expireTimes int32) { + amfUe.GmmLog.Warnf("T3555 expires, retransmit Configuration Update Command (retry: %d)", + expireTimes) + ngap_message.SendDownlinkNasTransport(amfUe.RanUe[accessType], nasMsg, &mobilityRestrictionList) + }, func() { + amfUe.GmmLog.Warnf("T3555 Expires %d times, abort configuration update procedure", + cfg.MaxRetryTimes) + }, + ) + } } func SendAuthenticationReject(ue *context.RanUe, eapMsg string) { diff --git a/internal/sbi/producer/callback.go b/internal/sbi/producer/callback.go index 08d01f33..3c2f1e84 100644 --- a/internal/sbi/producer/callback.go +++ b/internal/sbi/producer/callback.go @@ -153,18 +153,26 @@ func AmPolicyControlUpdateNotifyUpdateProcedure(polAssoID string, } }() + configurationUpdateCommandFlags := &context.ConfigurationUpdateCommandFlags{ + NeedGUTI: true, + NeedAllowedNSSAI: true, + NeedConfiguredNSSAI: true, + NeedRejectNSSAI: true, + NeedTaiList: true, + NeedNITZ: true, + NeedLadnInformation: true, + } + // UE is CM-Connected State if ue.CmConnect(models.AccessType__3_GPP_ACCESS) { - gmm_message.SendConfigurationUpdateCommand(ue, models.AccessType__3_GPP_ACCESS, nil) - // UE is CM-IDLE => paging + gmm_message.SendConfigurationUpdateCommand(ue, + models.AccessType__3_GPP_ACCESS, + configurationUpdateCommandFlags, + ) } else { - message, err := gmm_message.BuildConfigurationUpdateCommand(ue, models.AccessType__3_GPP_ACCESS, nil) - if err != nil { - logger.GmmLog.Errorf("Build Configuration Update Command Failed : %s", err.Error()) - return - } + // UE is CM-IDLE => paging + ue.ConfigurationUpdateCommandFlags = configurationUpdateCommandFlags - ue.ConfigurationUpdateMessage = message ue.SetOnGoing(models.AccessType__3_GPP_ACCESS, &context.OnGoing{ Procedure: context.OnGoingProcedurePaging, }) diff --git a/pkg/factory/config.go b/pkg/factory/config.go index 62ad3f0a..51239e10 100644 --- a/pkg/factory/config.go +++ b/pkg/factory/config.go @@ -88,6 +88,7 @@ type Configuration struct { T3560 TimerValue `yaml:"t3560" valid:"required"` T3565 TimerValue `yaml:"t3565" valid:"required"` T3570 TimerValue `yaml:"t3570" valid:"required"` + T3555 TimerValue `yaml:"t3555" valid:"required"` Locality string `yaml:"locality,omitempty" valid:"type(string),optional"` SCTP *Sctp `yaml:"sctp,omitempty" valid:"optional"` DefaultUECtxReq bool `yaml:"defaultUECtxReq,omitempty" valid:"type(bool),optional"` @@ -267,6 +268,10 @@ func (c *Configuration) validate() (bool, error) { return false, err } + if _, err := c.T3555.validate(); err != nil { + return false, err + } + if c.SCTP != nil { if _, err := c.SCTP.validate(); err != nil { return false, err