diff --git a/go.sum b/go.sum index b76aa91d..4b7b72a8 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,7 @@ github.com/free5gc/nas v1.0.7 h1:c+UXWENvJgTr/QZl50SyX+ZZzUdazGHHZ9ZGtnnwG5A= github.com/free5gc/nas v1.0.7/go.mod h1:qPj0gxFk81cH9zIkg4hm3ID0hkYofBlzZzcciBnJxwY= github.com/free5gc/ngap v1.0.6 h1:f9sKqHMNrFZVo9Kp8hAyrCXSoI8l746N5O+DFn7vKHA= github.com/free5gc/ngap v1.0.6/go.mod h1:TG1kwwU/EyIlJ3bxY591rdxpD5ZeYnLZTzoWjcfvrBM= +github.com/free5gc/openapi v1.0.4/go.mod h1:KRCnnp0GeK0Bl4gnrX79cQAidKXNENf8VRdG0y9R0Fc= github.com/free5gc/openapi v1.0.5 h1:S25JqyrTgLwcH6pqZE6U448vv0RKg1CoH48AQ4Cj/d4= github.com/free5gc/openapi v1.0.5/go.mod h1:KRCnnp0GeK0Bl4gnrX79cQAidKXNENf8VRdG0y9R0Fc= github.com/free5gc/pfcp v1.0.4 h1:11ous/chOya/bG0bHAHHEUc7JUB2g6svABock8Ta2Zs= diff --git a/internal/context/context.go b/internal/context/context.go index d4c5e5c7..2540b762 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -1,10 +1,12 @@ package context import ( + "context" "fmt" "net" "os" "sync/atomic" + "time" "github.com/google/uuid" @@ -43,13 +45,15 @@ type SMFContext struct { SnssaiInfos []SnssaiSmfInfo - NrfUri string - NFManagementClient *Nnrf_NFManagement.APIClient - NFDiscoveryClient *Nnrf_NFDiscovery.APIClient - SubscriberDataManagementClient *Nudm_SubscriberDataManagement.APIClient - Locality string + NrfUri string + NFManagementClient *Nnrf_NFManagement.APIClient + NFDiscoveryClient *Nnrf_NFDiscovery.APIClient + SubscriberDataManagementClient *Nudm_SubscriberDataManagement.APIClient + Locality string + AssociationSetupFailedAlertInterval time.Duration UserPlaneInformation *UserPlaneInformation + PFCPCancelFunc context.CancelFunc // Now only "IPv4" supported // TODO: support "IPv6", "IPv4v6", "Ethernet" @@ -154,6 +158,12 @@ func InitSmfContext(config *factory.Config) { smfContext.CPNodeID.NodeIdType = pfcpType.NodeIdTypeIpv6Address smfContext.CPNodeID.IP = addr.IP } + + if pfcp.AlertInterval == 0 { + smfContext.AssociationSetupFailedAlertInterval = 5 * time.Minute + } else { + smfContext.AssociationSetupFailedAlertInterval = pfcp.AlertInterval + } } smfContext.SnssaiInfos = make([]SnssaiSmfInfo, 0, len(configuration.SNssaiInfo)) diff --git a/pkg/factory/config.go b/pkg/factory/config.go index 4f4c6a42..288c55a3 100644 --- a/pkg/factory/config.go +++ b/pkg/factory/config.go @@ -212,6 +212,8 @@ func (t *Tls) validate() (bool, error) { type PFCP struct { Addr string `yaml:"addr,omitempty" valid:"host,required"` Port uint16 `yaml:"port,omitempty" valid:"port,optional"` + // interval at which PFCP Association Setup error messages are output. + AlertInterval time.Duration `yaml:"associationSetupFailedAlertInterval,omitempty" valid:"type(time.Duration),optional"` } func (p *PFCP) validate() (bool, error) { diff --git a/pkg/service/association.go b/pkg/service/association.go index e279c916..b4bac9f6 100644 --- a/pkg/service/association.go +++ b/pkg/service/association.go @@ -1,7 +1,9 @@ package service import ( + "context" "fmt" + "time" "github.com/free5gc/pfcp" "github.com/free5gc/pfcp/pfcpType" @@ -10,6 +12,46 @@ import ( "github.com/free5gc/smf/internal/pfcp/message" ) +func toBeAssociatedWithUPF(ctx context.Context, upf *smf_context.UPF) { + var upfStr string + if upf.NodeID.NodeIdType == pfcpType.NodeIdTypeFqdn { + upfStr = fmt.Sprintf("[%s](%s)", upf.NodeID.FQDN, upf.NodeID.ResolveNodeIdToIp().String()) + } else { + upfStr = fmt.Sprintf("[%s]", upf.NodeID.ResolveNodeIdToIp().String()) + } + ensureSetupPfcpAssociation(ctx, upf, upfStr) +} + +func isDone(ctx context.Context) bool { + select { + case <-ctx.Done(): + return true + default: + return false + } +} + +func ensureSetupPfcpAssociation(ctx context.Context, upf *smf_context.UPF, upfStr string) { + var alertTime time.Time + alertInterval := smf_context.SMF_Self().AssociationSetupFailedAlertInterval + for { + err := setupPfcpAssociation(upf, upfStr) + if err == nil { + return + } + now := time.Now() + if alertTime.IsZero() || now.After(alertTime.Add(alertInterval)) { + logger.AppLog.Errorf("Failed to setup an association with UPF%s, error:%+v", upfStr, err) + alertTime = now + } + + if isDone(ctx) { + logger.AppLog.Infof("Canceled association request to UPF%s", upfStr) + return + } + } +} + func setupPfcpAssociation(upf *smf_context.UPF, upfStr string) error { logger.AppLog.Infof("Sending PFCP Association Request to UPF%s", upfStr) diff --git a/pkg/service/init.go b/pkg/service/init.go index 02ef09db..a1c68b1f 100644 --- a/pkg/service/init.go +++ b/pkg/service/init.go @@ -1,6 +1,7 @@ package service import ( + "context" "fmt" "os" "os/signal" @@ -16,8 +17,7 @@ import ( ngapLogger "github.com/free5gc/ngap/logger" "github.com/free5gc/openapi/models" pfcpLogger "github.com/free5gc/pfcp/logger" - "github.com/free5gc/pfcp/pfcpType" - "github.com/free5gc/smf/internal/context" + smf_context "github.com/free5gc/smf/internal/context" "github.com/free5gc/smf/internal/logger" "github.com/free5gc/smf/internal/pfcp" "github.com/free5gc/smf/internal/pfcp/udp" @@ -222,10 +222,10 @@ func (smf *SMF) Start() { keyPath = sbi.Tls.Key } - context.InitSmfContext(&factory.SmfConfig) + smf_context.InitSmfContext(&factory.SmfConfig) // allocate id for each upf - context.AllocateUPFID() - context.InitSMFUERouting(&factory.UERoutingConfig) + smf_context.AllocateUPFID() + smf_context.InitSMFUERouting(&factory.UERoutingConfig) logger.InitLog.Infoln("Server started") router := logger_util.NewGinWithLogrus(logger.GinLog) @@ -266,21 +266,15 @@ func (smf *SMF) Start() { } udp.Run(pfcp.Dispatch) - for _, upf := range context.SMF_Self().UserPlaneInformation.UPFs { - var upfStr string - if upf.NodeID.NodeIdType == pfcpType.NodeIdTypeFqdn { - upfStr = fmt.Sprintf("[%s](%s)", upf.NodeID.FQDN, upf.NodeID.ResolveNodeIdToIp().String()) - } else { - upfStr = fmt.Sprintf("[%s]", upf.NodeID.IP.String()) - } - if err = setupPfcpAssociation(upf.UPF, upfStr); err != nil { - logger.AppLog.Errorf("Failed to setup an association with UPF%s, error:%+v", upfStr, err) - } + ctx, cancel := context.WithCancel(context.Background()) + smf_context.SMF_Self().PFCPCancelFunc = cancel + for _, upNode := range smf_context.SMF_Self().UserPlaneInformation.UPFs { + go toBeAssociatedWithUPF(ctx, upNode.UPF) } time.Sleep(1000 * time.Millisecond) - HTTPAddr := fmt.Sprintf("%s:%d", context.SMF_Self().BindingIPv4, context.SMF_Self().SBIPort) + HTTPAddr := fmt.Sprintf("%s:%d", smf_context.SMF_Self().BindingIPv4, smf_context.SMF_Self().SBIPort) server, err := httpwrapper.NewHttp2Server(HTTPAddr, smf.KeyLogPath, router) if server == nil {