diff --git a/cmd/main.go b/cmd/main.go index 593bfe58..f4137688 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,27 +1,26 @@ package main import ( - "fmt" "os" "path/filepath" "runtime/debug" - "github.com/asaskevich/govalidator" "github.com/urfave/cli" "github.com/free5gc/amf/internal/logger" - "github.com/free5gc/amf/internal/util" + "github.com/free5gc/amf/pkg/factory" "github.com/free5gc/amf/pkg/service" + logger_util "github.com/free5gc/util/logger" "github.com/free5gc/util/version" ) -var AMF = &service.AMF{} +var AMF *service.AmfApp func main() { defer func() { if p := recover(); p != nil { // Print stack for panic to log. Fatalf() will let program exit. - logger.AppLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + logger.MainLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) } }() @@ -29,59 +28,68 @@ func main() { app.Name = "amf" app.Usage = "5G Access and Mobility Management Function (AMF)" app.Action = action - app.Flags = AMF.GetCliCmd() + app.Flags = []cli.Flag{ + cli.StringFlag{ + Name: "config, c", + Usage: "Load configuration from `FILE`", + }, + cli.StringSliceFlag{ + Name: "log, l", + Usage: "Output NF log to `FILE`", + }, + } if err := app.Run(os.Args); err != nil { - logger.AppLog.Errorf("AMF Run error: %v\n", err) + logger.MainLog.Errorf("AMF Run error: %v\n", err) return } } -func action(c *cli.Context) error { - if err := initLogFile(c.String("log"), c.String("log5gc")); err != nil { - logger.AppLog.Errorf("%+v", err) +func action(cliCtx *cli.Context) error { + tlsKeyLogPath, err := initLogFile(cliCtx.StringSlice("log")) + if err != nil { return err } - if err := AMF.Initialize(c); err != nil { - switch err1 := err.(type) { - case govalidator.Errors: - errs := err1.Errors() - for _, e := range errs { - logger.CfgLog.Errorf("%+v", e) - } - default: - logger.CfgLog.Errorf("%+v", err) - } + logger.MainLog.Infoln("AMF version: ", version.GetVersion()) - logger.CfgLog.Errorf("[-- PLEASE REFER TO SAMPLE CONFIG FILE COMMENTS --]") - return fmt.Errorf("Failed to initialize !!") + cfg, err := factory.ReadConfig(cliCtx.String("config")) + if err != nil { + return err } + factory.AmfConfig = cfg - logger.AppLog.Infoln(c.App.Name) - logger.AppLog.Infoln("AMF version: ", version.GetVersion()) + amf, err := service.NewApp(cfg) + if err != nil { + return err + } + AMF = amf - AMF.Start() + amf.Start(tlsKeyLogPath) return nil } -func initLogFile(logNfPath, log5gcPath string) error { - AMF.KeyLogPath = util.AmfDefaultKeyLogPath +func initLogFile(logNfPath []string) (string, error) { + logTlsKeyPath := "" - if err := logger.LogFileHook(logNfPath, log5gcPath); err != nil { - return err - } + for _, path := range logNfPath { + if err := logger_util.LogFileHook(logger.Log, path); err != nil { + return "", err + } - if logNfPath != "" { - nfDir, _ := filepath.Split(logNfPath) + if logTlsKeyPath != "" { + continue + } + + nfDir, _ := filepath.Split(path) tmpDir := filepath.Join(nfDir, "key") if err := os.MkdirAll(tmpDir, 0o775); err != nil { logger.InitLog.Errorf("Make directory %s failed: %+v", tmpDir, err) - return err + return "", err } - _, name := filepath.Split(util.AmfDefaultKeyLogPath) - AMF.KeyLogPath = filepath.Join(tmpDir, name) + _, name := filepath.Split(factory.AmfDefaultTLSKeyLogPath) + logTlsKeyPath = filepath.Join(tmpDir, name) } - return nil + return logTlsKeyPath, nil } diff --git a/go.mod b/go.mod index 4348a46b..4e08c0b6 100644 --- a/go.mod +++ b/go.mod @@ -1,24 +1,68 @@ module github.com/free5gc/amf -go 1.14 +go 1.17 require ( git.cs.nctu.edu.tw/calee/sctp v1.1.0 github.com/antihax/optional v1.0.0 - github.com/antonfisher/nested-logrus-formatter v1.3.1 github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d + github.com/davecgh/go-spew v1.1.1 github.com/free5gc/aper v1.0.4 github.com/free5gc/nas v1.1.1 github.com/free5gc/ngap v1.0.6 github.com/free5gc/openapi v1.0.6 - github.com/free5gc/util v1.0.3 + github.com/free5gc/util v1.0.5-0.20230306071612-a52909216bd2 github.com/gin-contrib/cors v1.3.1 github.com/gin-gonic/gin v1.9.0 github.com/google/uuid v1.3.0 - github.com/mitchellh/mapstructure v1.4.1 + github.com/mitchellh/mapstructure v1.4.2 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 + github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.8.1 + github.com/smartystreets/goconvey v1.6.4 github.com/stretchr/testify v1.8.1 github.com/urfave/cli v1.22.5 gopkg.in/yaml.v2 v2.4.0 ) + +require ( + github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 // indirect + github.com/antonfisher/nested-logrus-formatter v1.3.1 // indirect + github.com/bytedance/sonic v1.8.0 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.11.2 // indirect + github.com/goccy/go-json v0.10.0 // indirect + github.com/golang-jwt/jwt v3.2.1+incompatible // indirect + github.com/golang/protobuf v1.5.0 // indirect + github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect + github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect + github.com/tim-ywliu/nested-logrus-formatter v1.3.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.9 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect + golang.org/x/crypto v0.5.0 // indirect + golang.org/x/net v0.7.0 // indirect + golang.org/x/oauth2 v0.0.0-20210810183815-faf39c7919d5 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + google.golang.org/appengine v1.6.6 // indirect + google.golang.org/protobuf v1.28.1 // indirect + gopkg.in/h2non/gock.v1 v1.1.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 15c88ec9..e714eba1 100644 --- a/go.sum +++ b/go.sum @@ -75,14 +75,14 @@ github.com/free5gc/ngap v1.0.6/go.mod h1:TG1kwwU/EyIlJ3bxY591rdxpD5ZeYnLZTzoWjcf github.com/free5gc/openapi v1.0.4/go.mod h1:KRCnnp0GeK0Bl4gnrX79cQAidKXNENf8VRdG0y9R0Fc= github.com/free5gc/openapi v1.0.6 h1:ytRjU/YZRI8UhKKyfajXSyGB6s1YDFkJ1weeAGJ8LXw= github.com/free5gc/openapi v1.0.6/go.mod h1:iw/N0E+FlX44EEx24IBi2EdZW8v+bkj3ETWPGnlK9DI= -github.com/free5gc/util v1.0.3 h1:or/gqHCAi3j2YKd+nzViRnc/tl1tuuJAYxCao6IbOAU= -github.com/free5gc/util v1.0.3/go.mod h1:DL1Dnryh//Ps5B+hfXbhU1R07fVfrmPs4uuTO4g9yTg= +github.com/free5gc/util v1.0.5-0.20230306071612-a52909216bd2 h1:FG8KlJ46Epscj3F9XBAKuDGJD9kSKJdstCL9fttjUjE= +github.com/free5gc/util v1.0.5-0.20230306071612-a52909216bd2/go.mod h1:fgV0hXf5arxAWs8D9LfrrfNByZ1IDCWYlgBzncy5GtE= github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA= github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= -github.com/gin-gonic/gin v1.7.3/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= +github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -197,8 +197,9 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo= +github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -244,6 +245,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tim-ywliu/nested-logrus-formatter v1.3.2 h1:jugNJ2/CNCI79SxOJCOhwUHeN3O7/7/bj+ZRGOFlCSw= +github.com/tim-ywliu/nested-logrus-formatter v1.3.2/go.mod h1:oGPmcxZB65j9Wo7mCnQKSrKEJtVDqyjD666SGmyStXI= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= @@ -276,6 +279,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -338,6 +342,7 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -391,12 +396,14 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -408,6 +415,7 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= diff --git a/internal/context/3gpp_types.go b/internal/context/3gpp_types.go index 732355c8..ef59f840 100644 --- a/internal/context/3gpp_types.go +++ b/internal/context/3gpp_types.go @@ -37,11 +37,6 @@ const ( TimeT3565 time.Duration = 6 * time.Second ) -type LADN struct { - Dnn string - TaiLists []models.Tai -} - type CauseAll struct { Cause *models.Cause NgapCause *models.NgApCause diff --git a/internal/context/amf_ran.go b/internal/context/amf_ran.go index 8d9cafab..49ee47a5 100644 --- a/internal/context/amf_ran.go +++ b/internal/context/amf_ran.go @@ -3,6 +3,7 @@ package context import ( "fmt" "net" + "sync" "github.com/sirupsen/logrus" @@ -29,7 +30,7 @@ type AmfRan struct { SupportedTAList []SupportedTAI /* RAN UE List */ - RanUeList []*RanUe // RanUeNgapId as key + RanUeList sync.Map // RanUeNgapId as key /* logger */ Log *logrus.Entry @@ -47,13 +48,13 @@ func NewSupportedTAI() (tai SupportedTAI) { func (ran *AmfRan) Remove() { ran.Log.Infof("Remove RAN Context[ID: %+v]", ran.RanID()) - ran.RemoveAllUeInRan() - AMF_Self().DeleteAmfRan(ran.Conn) + ran.RemoveAllRanUe(true) + GetSelf().DeleteAmfRan(ran.Conn) } func (ran *AmfRan) NewRanUe(ranUeNgapID int64) (*RanUe, error) { ranUe := RanUe{} - self := AMF_Self() + self := GetSelf() amfUeNgapID, err := self.AllocateAmfUeNgapID() if err != nil { return nil, fmt.Errorf("Allocate AMF UE NGAP ID error: %+v", err) @@ -61,32 +62,50 @@ func (ran *AmfRan) NewRanUe(ranUeNgapID int64) (*RanUe, error) { ranUe.AmfUeNgapId = amfUeNgapID ranUe.RanUeNgapId = ranUeNgapID ranUe.Ran = ran + ranUe.Log = ran.Log ranUe.UpdateLogFields() - ran.RanUeList = append(ran.RanUeList, &ranUe) + if ranUeNgapID != RanUeNgapIdUnspecified { + // store to RanUeList only when RANUENGAPID is specified + // (otherwise, will be stored only in amfContext.RanUePool) + ran.RanUeList.Store(ranUeNgapID, &ranUe) + } self.RanUePool.Store(ranUe.AmfUeNgapId, &ranUe) + ranUe.Log.Infof("New RanUe [RanUeNgapID:%d][AmfUeNgapID:%d]", ranUe.RanUeNgapId, ranUe.AmfUeNgapId) return &ranUe, nil } -func (ran *AmfRan) RemoveAllUeInRan() { - saveRanUeList := make([]*RanUe, len(ran.RanUeList)) - copy(saveRanUeList, ran.RanUeList) - for _, ranUe := range saveRanUeList { +func (ran *AmfRan) RemoveAllRanUe(removeAmfUe bool) { + // Using revered removal since ranUe.Remove() will also modify the slice r.RanUeList + ran.RanUeList.Range(func(k, v interface{}) bool { + ranUe := v.(*RanUe) if err := ranUe.Remove(); err != nil { - logger.ContextLog.Errorf("Remove RanUe error: %v", err) + logger.CtxLog.Errorf("Remove RanUe error: %v", err) } - } + return true + }) } func (ran *AmfRan) RanUeFindByRanUeNgapID(ranUeNgapID int64) *RanUe { - for _, ranUe := range ran.RanUeList { - if ranUe.RanUeNgapId == ranUeNgapID { - return ranUe - } + if value, ok := ran.RanUeList.Load(ranUeNgapID); ok { + return value.(*RanUe) } return nil } +func (ran *AmfRan) FindRanUeByAmfUeNgapID(amfUeNgapID int64) *RanUe { + var ru *RanUe + ran.RanUeList.Range(func(k, v interface{}) bool { + ranUe := v.(*RanUe) + if ranUe.AmfUeNgapId == amfUeNgapID { + ru = ranUe + return false + } + return true + }) + return ru +} + func (ran *AmfRan) SetRanId(ranNodeId *ngapType.GlobalRANNodeID) { ranId := ngapConvert.RanIdToModels(*ranNodeId) ran.RanPresent = ranNodeId.Present diff --git a/internal/context/amf_ran_test.go b/internal/context/amf_ran_test.go new file mode 100644 index 00000000..efe42962 --- /dev/null +++ b/internal/context/amf_ran_test.go @@ -0,0 +1,31 @@ +package context + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/free5gc/amf/internal/logger" +) + +func TestRemoveAndRemoveAllRanUeRaceCondition(t *testing.T) { + ran := &AmfRan{ + Log: logger.NgapLog.WithField("", ""), + } + + // create ranUe & store in RanUeList + for i := 1; i <= 10000; i++ { + ranUe, err := ran.NewRanUe(int64(i)) + require.NoError(t, err) + ran.RanUeList.Store(i, ranUe) + } + + require.NotPanics(t, func() { runRanUeRemove(ran) }) +} + +func runRanUeRemove(ran *AmfRan) { + for i := 1; i <= 10000; i++ { + go ran.RanUeList.Delete(i) + } + ran.RemoveAllRanUe(true) +} diff --git a/internal/context/amf_ue.go b/internal/context/amf_ue.go index 5b03dfe6..4191b60b 100644 --- a/internal/context/amf_ue.go +++ b/internal/context/amf_ue.go @@ -14,6 +14,7 @@ import ( "github.com/sirupsen/logrus" "github.com/free5gc/amf/internal/logger" + "github.com/free5gc/amf/pkg/factory" "github.com/free5gc/nas/nasMessage" "github.com/free5gc/nas/nasType" "github.com/free5gc/nas/security" @@ -160,7 +161,7 @@ type AmfUe struct { IntegrityAlg uint8 /* Registration Area */ RegistrationArea map[models.AccessType][]models.Tai - LadnInfo []LADN + LadnInfo []factory.Ladn /* Network Slicing related context and Nssf */ NssfId string NssfUri string @@ -169,7 +170,7 @@ type AmfUe struct { ConfiguredNssai []models.ConfiguredSnssai NetworkSlicingSubscriptionChanged bool SdmSubscriptionId string - UeCmRegistered bool + UeCmRegistered map[models.AccessType]bool /* T3513(Paging) */ T3513 *Timer // for paging /* T3565(Notification) */ @@ -185,10 +186,10 @@ type AmfUe struct { /* Ue Context Release Cause */ ReleaseCause map[models.AccessType]*CauseAll /* T3502 (Assigned by AMF, and used by UE to initialize registration procedure) */ - T3502Value int // Second - T3512Value int // default 54 min - Non3gppDeregistrationTimerValue int // default 54 min - Lock sync.Mutex + T3502Value int // Second + T3512Value int // default 54 min + Non3gppDeregTimerValue int // default 54 min + Lock sync.Mutex // Update context to prevent race condition // logger NASLog *logrus.Entry @@ -245,7 +246,7 @@ type NGRANCGI struct { } func (ue *AmfUe) init() { - ue.servingAMF = AMF_Self() + ue.servingAMF = GetSelf() ue.State = make(map[models.AccessType]*fsm.State) ue.State[models.AccessType__3_GPP_ACCESS] = fsm.NewState(Deregistered) ue.State[models.AccessType_NON_3_GPP_ACCESS] = fsm.NewState(Deregistered) @@ -262,6 +263,10 @@ func (ue *AmfUe) init() { ue.onGoing[models.AccessType__3_GPP_ACCESS] = new(OnGoing) ue.onGoing[models.AccessType__3_GPP_ACCESS].Procedure = OnGoingProcedureNothing ue.ReleaseCause = make(map[models.AccessType]*CauseAll) + ue.UeCmRegistered = make(map[models.AccessType]bool) + ue.GmmLog = logger.GmmLog + ue.NASLog = logger.GmmLog + ue.ProducerLog = logger.ProducerLog } func (ue *AmfUe) ServingAMF() *AMFContext { @@ -280,48 +285,60 @@ func (ue *AmfUe) CmIdle(anType models.AccessType) bool { } func (ue *AmfUe) Remove() { + ue.StopT3513() + ue.StopT3565() + ue.StopT3560() + ue.StopT3550() + ue.StopT3522() + for _, ranUe := range ue.RanUe { if err := ranUe.Remove(); err != nil { - logger.ContextLog.Errorf("Remove RanUe error: %v", err) + logger.CtxLog.Errorf("Remove RanUe error: %v", err) } } tmsiGenerator.FreeID(int64(ue.Tmsi)) if len(ue.Supi) > 0 { - AMF_Self().UePool.Delete(ue.Supi) + GetSelf().UePool.Delete(ue.Supi) } + logger.CtxLog.Infof("AmfUe[%s] is removed", ue.Supi) } func (ue *AmfUe) DetachRanUe(anType models.AccessType) { + if ue == nil { + return + } delete(ue.RanUe, anType) - ue.UpdateLogFields() + ue.UpdateLogFields(anType) } // Don't call this function directly. Use gmm_common.AttachRanUeToAmfUeAndReleaseOldIfAny(). func (ue *AmfUe) AttachRanUe(ranUe *RanUe) { ue.RanUe[ranUe.Ran.AnType] = ranUe ranUe.AmfUe = ue - ue.UpdateLogFields() - // TODO: stop Implicit Deregistration timer + ue.UpdateLogFields(ranUe.Ran.AnType) } -func (ue *AmfUe) UpdateLogFields() { - nasLog := logger.NasLog - gmmLog := logger.GmmLog - producerLog := logger.ProducerLog - - if ranUe, ok := ue.RanUe[models.AccessType__3_GPP_ACCESS]; ok { - nasLog = nasLog.WithField(logger.FieldAmfUeNgapID, fmt.Sprintf("AMF_UE_NGAP_ID:%d", ranUe.AmfUeNgapId)) - gmmLog = gmmLog.WithField(logger.FieldAmfUeNgapID, fmt.Sprintf("AMF_UE_NGAP_ID:%d", ranUe.AmfUeNgapId)) +func (ue *AmfUe) UpdateLogFields(accessType models.AccessType) { + anTypeStr := "" + if accessType == models.AccessType__3_GPP_ACCESS { + anTypeStr = "3GPP" + } else if accessType == models.AccessType_NON_3_GPP_ACCESS { + anTypeStr = "Non3GPP" } - if ue.Supi != "" { - nasLog = nasLog.WithField(logger.FieldSupi, fmt.Sprintf("SUPI:%s", ue.Supi)) - gmmLog = gmmLog.WithField(logger.FieldSupi, fmt.Sprintf("SUPI:%s", ue.Supi)) - producerLog = producerLog.WithField(logger.FieldSupi, fmt.Sprintf("SUPI:%s", ue.Supi)) + if ranUe, ok := ue.RanUe[accessType]; ok { + ue.NASLog = ue.NASLog.WithField(logger.FieldAmfUeNgapID, fmt.Sprintf("RU:%d,AU:%d(%s)", + ranUe.RanUeNgapId, ranUe.AmfUeNgapId, anTypeStr)) + ue.GmmLog = ue.GmmLog.WithField(logger.FieldAmfUeNgapID, fmt.Sprintf("RU:%d,AU:%d(%s)", + ranUe.RanUeNgapId, ranUe.AmfUeNgapId, anTypeStr)) + } else { + ue.NASLog = ue.NASLog.WithField(logger.FieldAmfUeNgapID, fmt.Sprintf("RU:,AU:(%s)", anTypeStr)) + ue.GmmLog = ue.GmmLog.WithField(logger.FieldAmfUeNgapID, fmt.Sprintf("RU:,AU:(%s)", anTypeStr)) } - ue.NASLog = nasLog - ue.GmmLog = gmmLog - ue.ProducerLog = producerLog + // will log "[SUPI:]" if ue.SUPI=="" + ue.NASLog = ue.NASLog.WithField(logger.FieldSupi, fmt.Sprintf("SUPI:%s", ue.Supi)) + ue.GmmLog = ue.GmmLog.WithField(logger.FieldSupi, fmt.Sprintf("SUPI:%s", ue.Supi)) + ue.ProducerLog = ue.ProducerLog.WithField(logger.FieldSupi, fmt.Sprintf("SUPI:%s", ue.Supi)) } func (ue *AmfUe) GetAnType() models.AccessType { @@ -410,7 +427,7 @@ func (ue *AmfUe) SecurityContextIsValid() bool { func (ue *AmfUe) DerivateKamf() { supiRegexp, err := regexp.Compile("(?:imsi|supi)-([0-9]{5,15})") if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } groups := supiRegexp.FindStringSubmatch(ue.Supi) @@ -426,12 +443,12 @@ func (ue *AmfUe) DerivateKamf() { KseafDecode, err := hex.DecodeString(ue.Kseaf) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } KamfBytes, err := ueauth.GetKDFValue(KseafDecode, ueauth.FC_FOR_KAMF_DERIVATION, P0, L0, P1, L1) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } ue.Kamf = hex.EncodeToString(KamfBytes) @@ -447,12 +464,12 @@ func (ue *AmfUe) DerivateAlgKey() { KamfBytes, err := hex.DecodeString(ue.Kamf) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } kenc, err := ueauth.GetKDFValue(KamfBytes, ueauth.FC_FOR_ALGORITHM_KEY_DERIVATION, P0, L0, P1, L1) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } copy(ue.KnasEnc[:], kenc[16:32]) @@ -465,7 +482,7 @@ func (ue *AmfUe) DerivateAlgKey() { kint, err := ueauth.GetKDFValue(KamfBytes, ueauth.FC_FOR_ALGORITHM_KEY_DERIVATION, P0, L0, P1, L1) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } copy(ue.KnasInt[:], kint[16:32]) @@ -485,12 +502,12 @@ func (ue *AmfUe) DerivateAnKey(anType models.AccessType) { KamfBytes, err := hex.DecodeString(ue.Kamf) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } key, err := ueauth.GetKDFValue(KamfBytes, ueauth.FC_FOR_KGNB_KN3IWF_DERIVATION, P0, L0, P1, L1) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } switch accessType { @@ -508,12 +525,12 @@ func (ue *AmfUe) DerivateNH(syncInput []byte) { KamfBytes, err := hex.DecodeString(ue.Kamf) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } ue.NH, err = ueauth.GetKDFValue(KamfBytes, ueauth.FC_FOR_NH_DERIVATION, P0, L0) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } } @@ -596,7 +613,7 @@ func (ue *AmfUe) ClearRegistrationRequestData(accessType models.AccessType) { ue.ServingAmfChanged = false ue.RegistrationAcceptForNon3GPPAccess = nil if ranUe := ue.RanUe[accessType]; ranUe != nil { - ranUe.UeContextRequest = false + ranUe.UeContextRequest = factory.AmfConfig.Configuration.DefaultUECtxReq } ue.RetransmissionOfInitialNASMsg = false if onGoing := ue.onGoing[accessType]; onGoing != nil { @@ -693,7 +710,7 @@ func (ue *AmfUe) CopyDataFromUeContextModel(ueContext models.UeContext) { } } if nh, err := hex.DecodeString(seafData.Nh); err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } else { ue.NH = nh @@ -786,7 +803,7 @@ func (ue *AmfUe) CopyDataFromUeContextModel(ueContext models.UeContext) { // ue.SecurityCapabilities buf, err := base64.StdEncoding.DecodeString(mmContext.UeSecurityCapability) if err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) return } ue.UESecurityCapability.Buffer = buf @@ -819,7 +836,56 @@ func (ue *AmfUe) StoreSmContext(pduSessionID int32, smContext *SmContext) { func (ue *AmfUe) SmContextFindByPDUSessionID(pduSessionID int32) (*SmContext, bool) { if value, ok := ue.SmContextList.Load(pduSessionID); ok { return value.(*SmContext), true - } else { - return nil, false } + return nil, false +} + +func (ue *AmfUe) StopT3513() { + if ue.T3513 == nil { + return + } + + ue.GmmLog.Infof("Stop T3513 timer") + ue.T3513.Stop() + ue.T3513 = nil // clear the timer +} + +func (ue *AmfUe) StopT3565() { + if ue.T3565 == nil { + return + } + + ue.GmmLog.Infof("Stop T3565 timer") + ue.T3565.Stop() + ue.T3565 = nil // clear the timer +} + +func (ue *AmfUe) StopT3560() { + if ue.T3560 == nil { + return + } + + ue.GmmLog.Infof("Stop T3560 timer") + ue.T3560.Stop() + ue.T3560 = nil // clear the timer +} + +func (ue *AmfUe) StopT3550() { + if ue.T3550 == nil { + return + } + + ue.GmmLog.Infof("Stop T3550 timer") + ue.T3550.Stop() + ue.T3550 = nil // clear the timer +} + +func (ue *AmfUe) StopT3522() { + if ue.T3522 == nil { + return + } + + ue.GmmLog.Infof("Stop T3522 timer") + ue.T3522.Stop() + ue.T3522 = nil // clear the timer } diff --git a/internal/context/common_function.go b/internal/context/common_function.go index 3ed12284..edcd9e29 100644 --- a/internal/context/common_function.go +++ b/internal/context/common_function.go @@ -53,16 +53,16 @@ func TacInAreas(targetTac string, areas []models.Area) bool { func AttachSourceUeTargetUe(sourceUe, targetUe *RanUe) { if sourceUe == nil { - logger.ContextLog.Error("Source Ue is Nil") + logger.CtxLog.Error("Source Ue is Nil") return } if targetUe == nil { - logger.ContextLog.Error("Target Ue is Nil") + logger.CtxLog.Error("Target Ue is Nil") return } amfUe := sourceUe.AmfUe if amfUe == nil { - logger.ContextLog.Error("AmfUe is Nil") + logger.CtxLog.Error("AmfUe is Nil") return } targetUe.AmfUe = amfUe @@ -72,7 +72,7 @@ func AttachSourceUeTargetUe(sourceUe, targetUe *RanUe) { func DetachSourceUeTargetUe(ranUe *RanUe) { if ranUe == nil { - logger.ContextLog.Error("ranUe is Nil") + logger.CtxLog.Error("ranUe is Nil") return } if ranUe.TargetUe != nil { diff --git a/internal/context/context.go b/internal/context/context.go index aad7f82e..1e251cc9 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -10,63 +10,67 @@ import ( "sync" "time" + "github.com/google/uuid" + "github.com/free5gc/amf/internal/logger" "github.com/free5gc/amf/pkg/factory" + "github.com/free5gc/nas/security" "github.com/free5gc/openapi/models" "github.com/free5gc/util/idgenerator" ) var ( - amfContext = AMFContext{} + amfContext AMFContext tmsiGenerator *idgenerator.IDGenerator = nil amfUeNGAPIDGenerator *idgenerator.IDGenerator = nil amfStatusSubscriptionIDGenerator *idgenerator.IDGenerator = nil ) func init() { - AMF_Self().LadnPool = make(map[string]*LADN) - AMF_Self().EventSubscriptionIDGenerator = idgenerator.NewGenerator(1, math.MaxInt32) - AMF_Self().Name = "amf" - AMF_Self().UriScheme = models.UriScheme_HTTPS - AMF_Self().RelativeCapacity = 0xff - AMF_Self().ServedGuamiList = make([]models.Guami, 0, MaxNumOfServedGuamiList) - AMF_Self().PlmnSupportList = make([]factory.PlmnSupportItem, 0, MaxNumOfPLMNs) - AMF_Self().NfService = make(map[models.ServiceName]models.NfService) - AMF_Self().NetworkName.Full = "free5GC" + GetSelf().LadnPool = make(map[string]factory.Ladn) + GetSelf().EventSubscriptionIDGenerator = idgenerator.NewGenerator(1, math.MaxInt32) + GetSelf().Name = "amf" + GetSelf().UriScheme = models.UriScheme_HTTPS + GetSelf().RelativeCapacity = 0xff + GetSelf().ServedGuamiList = make([]models.Guami, 0, MaxNumOfServedGuamiList) + GetSelf().PlmnSupportList = make([]factory.PlmnSupportItem, 0, MaxNumOfPLMNs) + GetSelf().NfService = make(map[models.ServiceName]models.NfService) + GetSelf().NetworkName.Full = "free5GC" tmsiGenerator = idgenerator.NewGenerator(1, math.MaxInt32) amfStatusSubscriptionIDGenerator = idgenerator.NewGenerator(1, math.MaxInt32) amfUeNGAPIDGenerator = idgenerator.NewGenerator(1, MaxValueOfAmfUeNgapId) } type AMFContext struct { - EventSubscriptionIDGenerator *idgenerator.IDGenerator - EventSubscriptions sync.Map - UePool sync.Map // map[supi]*AmfUe - RanUePool sync.Map // map[AmfUeNgapID]*RanUe - AmfRanPool sync.Map // map[net.Conn]*AmfRan - LadnPool map[string]*LADN // dnn as key - SupportTaiLists []models.Tai - ServedGuamiList []models.Guami - PlmnSupportList []factory.PlmnSupportItem - RelativeCapacity int64 - NfId string - Name string - NfService map[models.ServiceName]models.NfService // nfservice that amf support - UriScheme models.UriScheme - BindingIPv4 string - SBIPort int - RegisterIPv4 string - HttpIPv6Address string - TNLWeightFactor int64 - SupportDnnLists []string - AMFStatusSubscriptions sync.Map // map[subscriptionID]models.SubscriptionData - NrfUri string - SecurityAlgorithm SecurityAlgorithm - NetworkName factory.NetworkName - NgapIpList []string // NGAP Server IP - T3502Value int // unit is second - T3512Value int // unit is second - Non3gppDeregistrationTimerValue int // unit is second + EventSubscriptionIDGenerator *idgenerator.IDGenerator + EventSubscriptions sync.Map + UePool sync.Map // map[supi]*AmfUe + RanUePool sync.Map // map[AmfUeNgapID]*RanUe + AmfRanPool sync.Map // map[net.Conn]*AmfRan + LadnPool map[string]factory.Ladn // dnn as key + SupportTaiLists []models.Tai + ServedGuamiList []models.Guami + PlmnSupportList []factory.PlmnSupportItem + RelativeCapacity int64 + NfId string + Name string + NfService map[models.ServiceName]models.NfService // nfservice that amf support + UriScheme models.UriScheme + BindingIPv4 string + SBIPort int + RegisterIPv4 string + HttpIPv6Address string + TNLWeightFactor int64 + SupportDnnLists []string + AMFStatusSubscriptions sync.Map // map[subscriptionID]models.SubscriptionData + NrfUri string + SecurityAlgorithm SecurityAlgorithm + NetworkName factory.NetworkName + NgapIpList []string // NGAP Server IP + NgapPort int + T3502Value int // unit is second + T3512Value int // unit is second + Non3gppDeregTimerValue int // unit is second // read-only fields T3513Cfg factory.TimerValue T3522Cfg factory.TimerValue @@ -90,6 +94,88 @@ type SecurityAlgorithm struct { CipheringOrder []uint8 // slice of security.AlgCipheringXXX } +func InitAmfContext(context *AMFContext) { + config := factory.AmfConfig + logger.UtilLog.Infof("amfconfig Info: Version[%s]", config.GetVersion()) + configuration := config.Configuration + context.NfId = uuid.New().String() + if configuration.AmfName != "" { + context.Name = configuration.AmfName + } + if configuration.NgapIpList != nil { + context.NgapIpList = configuration.NgapIpList + } else { + context.NgapIpList = []string{"127.0.0.1"} // default localhost + } + context.NgapPort = config.GetNgapPort() + context.UriScheme = models.UriScheme(config.GetSbiScheme()) + context.RegisterIPv4 = config.GetSbiRegisterIP() + context.SBIPort = config.GetSbiPort() + context.BindingIPv4 = config.GetSbiBindingIP() + + context.InitNFService(config.GetServiceNameList(), config.GetVersion()) + context.ServedGuamiList = configuration.ServedGumaiList + context.SupportTaiLists = configuration.SupportTAIList + context.PlmnSupportList = configuration.PlmnSupportList + context.SupportDnnLists = configuration.SupportDnnList + for _, ladn := range configuration.SupportLadnList { + context.LadnPool[ladn.Dnn] = ladn + } + context.NrfUri = config.GetNrfUri() + security := configuration.Security + if security != nil { + context.SecurityAlgorithm.IntegrityOrder = getIntAlgOrder(security.IntegrityOrder) + context.SecurityAlgorithm.CipheringOrder = getEncAlgOrder(security.CipheringOrder) + } + context.NetworkName = configuration.NetworkName + context.T3502Value = configuration.T3502Value + context.T3512Value = configuration.T3512Value + context.Non3gppDeregTimerValue = configuration.Non3gppDeregTimerValue + context.T3513Cfg = configuration.T3513 + context.T3522Cfg = configuration.T3522 + context.T3550Cfg = configuration.T3550 + context.T3560Cfg = configuration.T3560 + context.T3565Cfg = configuration.T3565 + context.T3570Cfg = configuration.T3570 + context.Locality = configuration.Locality +} + +func getIntAlgOrder(integrityOrder []string) (intOrder []uint8) { + for _, intAlg := range integrityOrder { + switch intAlg { + case "NIA0": + intOrder = append(intOrder, security.AlgIntegrity128NIA0) + case "NIA1": + intOrder = append(intOrder, security.AlgIntegrity128NIA1) + case "NIA2": + intOrder = append(intOrder, security.AlgIntegrity128NIA2) + case "NIA3": + intOrder = append(intOrder, security.AlgIntegrity128NIA3) + default: + logger.UtilLog.Errorf("Unsupported algorithm: %s", intAlg) + } + } + return +} + +func getEncAlgOrder(cipheringOrder []string) (encOrder []uint8) { + for _, encAlg := range cipheringOrder { + switch encAlg { + case "NEA0": + encOrder = append(encOrder, security.AlgCiphering128NEA0) + case "NEA1": + encOrder = append(encOrder, security.AlgCiphering128NEA1) + case "NEA2": + encOrder = append(encOrder, security.AlgCiphering128NEA2) + case "NEA3": + encOrder = append(encOrder, security.AlgCiphering128NEA3) + default: + logger.UtilLog.Errorf("Unsupported algorithm: %s", encAlg) + } + } + return +} + func NewPlmnSupportItem() (item factory.PlmnSupportItem) { item.SNssaiList = make([]models.Snssai, 0, MaxNumOfSlice) return @@ -98,7 +184,7 @@ func NewPlmnSupportItem() (item factory.PlmnSupportItem) { func (context *AMFContext) TmsiAllocate() int32 { tmsi, err := tmsiGenerator.Allocate() if err != nil { - logger.ContextLog.Errorf("Allocate TMSI error: %+v", err) + logger.CtxLog.Errorf("Allocate TMSI error: %+v", err) return -1 } return int32(tmsi) @@ -140,7 +226,7 @@ func (context *AMFContext) AllocateRegistrationArea(ue *AmfUe, anType models.Acc func (context *AMFContext) NewAMFStatusSubscription(subscriptionData models.SubscriptionData) (subscriptionID string) { id, err := amfStatusSubscriptionIDGenerator.Allocate() if err != nil { - logger.ContextLog.Errorf("Allocate subscriptionID error: %+v", err) + logger.CtxLog.Errorf("Allocate subscriptionID error: %+v", err) return "" } @@ -162,7 +248,7 @@ func (context *AMFContext) FindAMFStatusSubscription(subscriptionID string) (*mo func (context *AMFContext) DeleteAMFStatusSubscription(subscriptionID string) { context.AMFStatusSubscriptions.Delete(subscriptionID) if id, err := strconv.ParseInt(subscriptionID, 10, 64); err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) } else { amfStatusSubscriptionIDGenerator.FreeID(id) } @@ -183,7 +269,7 @@ func (context *AMFContext) FindEventSubscription(subscriptionID string) (*AMFCon func (context *AMFContext) DeleteEventSubscription(subscriptionID string) { context.EventSubscriptions.Delete(subscriptionID) if id, err := strconv.ParseInt(subscriptionID, 10, 32); err != nil { - logger.ContextLog.Error(err) + logger.CtxLog.Error(err) } else { context.EventSubscriptionIDGenerator.FreeID(id) } @@ -191,7 +277,7 @@ func (context *AMFContext) DeleteEventSubscription(subscriptionID string) { func (context *AMFContext) AddAmfUeToUePool(ue *AmfUe, supi string) { if len(supi) == 0 { - logger.ContextLog.Errorf("Supi is nil") + logger.CtxLog.Errorf("Supi is nil") } ue.Supi = supi context.UePool.Store(ue.Supi, ue) @@ -207,6 +293,7 @@ func (context *AMFContext) NewAmfUe(supi string) *AmfUe { context.AllocateGutiToUe(&ue) + logger.CtxLog.Infof("New AmfUe [supi:%s][guti:%s]", supi, ue.Guti) return &ue } @@ -224,15 +311,28 @@ func (context *AMFContext) AmfUeFindByUeContextID(ueContextID string) (*AmfUe, b return nil, false } -func (context *AMFContext) AmfUeFindBySupi(supi string) (ue *AmfUe, ok bool) { - if value, loadOk := context.UePool.Load(supi); loadOk { - ue = value.(*AmfUe) - ok = loadOk +func (context *AMFContext) AmfUeFindBySupi(supi string) (*AmfUe, bool) { + if value, ok := context.UePool.Load(supi); ok { + return value.(*AmfUe), ok } + return nil, false +} + +func (context *AMFContext) AmfUeFindBySuci(suci string) (ue *AmfUe, ok bool) { + context.UePool.Range(func(key, value interface{}) bool { + candidate := value.(*AmfUe) + if ok = (candidate.Suci == suci); ok { + ue = candidate + return false + } + return true + }) return } -func (context *AMFContext) AmfUeFindByPei(pei string) (ue *AmfUe, ok bool) { +func (context *AMFContext) AmfUeFindByPei(pei string) (*AmfUe, bool) { + var ue *AmfUe + var ok bool context.UePool.Range(func(key, value interface{}) bool { candidate := value.(*AmfUe) if ok = (candidate.Pei == pei); ok { @@ -241,7 +341,7 @@ func (context *AMFContext) AmfUeFindByPei(pei string) (ue *AmfUe, ok bool) { } return true }) - return + return ue, ok } func (context *AMFContext) NewAmfRan(conn net.Conn) *AmfRan { @@ -267,9 +367,14 @@ func (context *AMFContext) AmfRanFindByRanID(ranNodeID models.GlobalRanNodeId) ( var ok bool context.AmfRanPool.Range(func(key, value interface{}) bool { amfRan := value.(*AmfRan) + if amfRan.RanId == nil { + return true + } + switch amfRan.RanPresent { case RanPresentGNbId: - if amfRan.RanId.GNbId.GNBValue == ranNodeID.GNbId.GNBValue { + if amfRan.RanId.GNbId != nil && ranNodeID.GNbId != nil && + amfRan.RanId.GNbId.GNBValue == ranNodeID.GNbId.GNBValue { ran = amfRan ok = true return false @@ -316,7 +421,9 @@ func (context *AMFContext) InPlmnSupportList(snssai models.Snssai) bool { return false } -func (context *AMFContext) AmfUeFindByGuti(guti string) (ue *AmfUe, ok bool) { +func (context *AMFContext) AmfUeFindByGuti(guti string) (*AmfUe, bool) { + var ue *AmfUe + var ok bool context.UePool.Range(func(key, value interface{}) bool { candidate := value.(*AmfUe) if ok = (candidate.Guti == guti); ok { @@ -325,10 +432,12 @@ func (context *AMFContext) AmfUeFindByGuti(guti string) (ue *AmfUe, ok bool) { } return true }) - return + return ue, ok } -func (context *AMFContext) AmfUeFindByPolicyAssociationID(polAssoId string) (ue *AmfUe, ok bool) { +func (context *AMFContext) AmfUeFindByPolicyAssociationID(polAssoId string) (*AmfUe, bool) { + var ue *AmfUe + var ok bool context.UePool.Range(func(key, value interface{}) bool { candidate := value.(*AmfUe) if ok = (candidate.PolicyAssociationId == polAssoId); ok { @@ -337,15 +446,14 @@ func (context *AMFContext) AmfUeFindByPolicyAssociationID(polAssoId string) (ue } return true }) - return + return ue, ok } func (context *AMFContext) RanUeFindByAmfUeNgapID(amfUeNgapID int64) *RanUe { if value, ok := context.RanUePool.Load(amfUeNgapID); ok { return value.(*RanUe) - } else { - return nil } + return nil } func (context *AMFContext) GetIPv4Uri() string { @@ -419,6 +527,6 @@ func (context *AMFContext) Reset() { } // Create new AMF context -func AMF_Self() *AMFContext { +func GetSelf() *AMFContext { return &amfContext } diff --git a/internal/context/ran_ue.go b/internal/context/ran_ue.go index 78d5a014..c4ff542a 100644 --- a/internal/context/ran_ue.go +++ b/internal/context/ran_ue.go @@ -60,10 +60,10 @@ type RanUe struct { OldAmfName string InitialUEMessage []byte RRCEstablishmentCause string // Received from initial ue message; pattern: ^[0-9a-fA-F]+$ - UeContextRequest bool + UeContextRequest bool // Receive UEContextRequest IE from RAN /* send initial context setup request or not*/ - SentInitialContextSetupRequest bool + InitialContextSetup bool /* logger */ Log *logrus.Entry @@ -82,13 +82,9 @@ func (ranUe *RanUe) Remove() error { ranUe.DetachAmfUe() } - for index, ranUe1 := range ran.RanUeList { - if ranUe1 == ranUe { - ran.RanUeList = append(ran.RanUeList[:index], ran.RanUeList[index+1:]...) - break - } - } - self := AMF_Self() + ran.RanUeList.Delete(ranUe.RanUeNgapId) + + self := GetSelf() self.RanUePool.Delete(ranUe.AmfUeNgapId) amfUeNGAPIDGenerator.FreeID(ranUe.AmfUeNgapId) return nil @@ -110,15 +106,10 @@ func (ranUe *RanUe) SwitchToRan(newRan *AmfRan, ranUeNgapId int64) error { oldRan := ranUe.Ran // remove ranUe from oldRan - for index, ranUe1 := range oldRan.RanUeList { - if ranUe1 == ranUe { - oldRan.RanUeList = append(oldRan.RanUeList[:index], oldRan.RanUeList[index+1:]...) - break - } - } + oldRan.RanUeList.Delete(ranUe.RanUeNgapId) // add ranUe to newRan - newRan.RanUeList = append(newRan.RanUeList, ranUe) + newRan.RanUeList.Store(ranUeNgapId, ranUe) // switch to newRan ranUe.Ran = newRan @@ -127,16 +118,34 @@ func (ranUe *RanUe) SwitchToRan(newRan *AmfRan, ranUeNgapId int64) error { // update log information ranUe.UpdateLogFields() - logger.ContextLog.Infof("RanUe[RanUeNgapID: %d] Switch to new Ran[Name: %s]", ranUe.RanUeNgapId, ranUe.Ran.Name) + logger.CtxLog.Infof("RanUe[RanUeNgapID: %d] Switch to new Ran[Name: %s]", ranUe.RanUeNgapId, ranUe.Ran.Name) return nil } +func (ranUe *RanUe) UpdateLogFields() { + if ranUe.Ran != nil && ranUe.Ran.Conn != nil { + ranUe.Log = ranUe.Log.WithField(logger.FieldRanAddr, ranUe.Ran.Conn.RemoteAddr().String()) + + anTypeStr := "" + if ranUe.Ran.AnType == models.AccessType__3_GPP_ACCESS { + anTypeStr = "3GPP" + } else if ranUe.Ran.AnType == models.AccessType_NON_3_GPP_ACCESS { + anTypeStr = "Non3GPP" + } + ranUe.Log = ranUe.Log.WithField(logger.FieldAmfUeNgapID, + fmt.Sprintf("RU:%d,AU:%d(%s)", ranUe.RanUeNgapId, ranUe.AmfUeNgapId, anTypeStr)) + } else { + ranUe.Log = ranUe.Log.WithField(logger.FieldRanAddr, "no ran conn") + ranUe.Log = ranUe.Log.WithField(logger.FieldAmfUeNgapID, "RU:,AU:") + } +} + func (ranUe *RanUe) UpdateLocation(userLocationInformation *ngapType.UserLocationInformation) { if userLocationInformation == nil { return } - amfSelf := AMF_Self() + amfSelf := GetSelf() curTime := time.Now().UTC() switch userLocationInformation.Present { case ngapType.UserLocationInformationPresentUserLocationInformationEUTRA: @@ -243,14 +252,3 @@ func (ranUe *RanUe) UpdateLocation(userLocationInformation *ngapType.UserLocatio case ngapType.UserLocationInformationPresentNothing: } } - -func (ranUe *RanUe) UpdateLogFields() { - ngapLog := logger.NgapLog - - if ranUe.Ran != nil && ranUe.Ran.Conn != nil { - ngapLog = ngapLog.WithField(logger.FieldRanAddr, ranUe.Ran.Conn.RemoteAddr().String()) - ngapLog = ngapLog.WithField(logger.FieldAmfUeNgapID, fmt.Sprintf("AMF_UE_NGAP_ID:%d", ranUe.AmfUeNgapId)) - } - - ranUe.Log = ngapLog -} diff --git a/internal/context/timer.go b/internal/context/timer.go index f9588719..72c7a5bc 100644 --- a/internal/context/timer.go +++ b/internal/context/timer.go @@ -1,8 +1,11 @@ package context import ( + "runtime/debug" "sync/atomic" "time" + + "github.com/free5gc/amf/internal/logger" ) // Timer can be used for retransmission, it will manage retry times automatically @@ -28,6 +31,13 @@ func NewTimer(d time.Duration, maxRetryTimes int, t.ticker = time.NewTicker(d) go func(ticker *time.Ticker) { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.CtxLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + } + }() + defer ticker.Stop() for { diff --git a/internal/context/timer_test.go b/internal/context/timer_test.go new file mode 100644 index 00000000..245cdb16 --- /dev/null +++ b/internal/context/timer_test.go @@ -0,0 +1,83 @@ +package context + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestTimerNewTimer(t *testing.T) { + timer := NewTimer(100*time.Millisecond, 3, func(expireTimes int32) { + t.Logf("expire %d times", expireTimes) + }, func() { + t.Log("exceed max retry times (3)") + }) + assert.NotNil(t, timer) +} + +func TestTimerStartAndStop(t *testing.T) { + timer := NewTimer(100*time.Millisecond, 3, + func(expireTimes int32) { + t.Logf("expire %d times", expireTimes) + }, + func() { + t.Log("exceed max retry times (3)") + }) + assert.NotNil(t, timer) + + time.Sleep(350 * time.Millisecond) + timer.Stop() + assert.EqualValues(t, 3, timer.ExpireTimes()) +} + +func TestTimerExceedMaxRetryTimes(t *testing.T) { + timer := NewTimer(100*time.Millisecond, 3, + func(expireTimes int32) { + t.Logf("expire %d times", expireTimes) + }, + func() { + t.Log("exceed max retry times (3)") + }) + assert.NotNil(t, timer) + + time.Sleep(450 * time.Millisecond) +} + +/* +func TestTimerRestartTimerWithoutExceedMaxRetry(t *testing.T) { + for i := 0; i < 100; i++ { + t.Run(fmt.Sprintf("timer %d", i), func(t *testing.T) { + t.Parallel() + for j := 0; j < 10; j++ { + t.Logf("timer start %d-th", j) + timer := NewTimer(100*time.Millisecond, 10, func(expireTimes int32) { + t.Logf("expire %d times", expireTimes) + }, func() { + t.Log("exceed max retry times") + }) + time.Sleep(50 * time.Millisecond) + timer.Stop() + } + }) + } +} + +func TestTimerRestartTimerWithExceedMaxRetry(t *testing.T) { + for i := 0; i < 50; i++ { + t.Run(fmt.Sprintf("timer %d", i), func(t *testing.T) { + t.Parallel() + for j := 0; j < 10; j++ { + t.Logf("timer start %d-th", j) + timer := NewTimer(50*time.Millisecond, 3, func(expireTimes int32) { + t.Logf("expire %d times", expireTimes) + }, func() { + t.Log("exceed max retry times") + }) + time.Sleep(200 * time.Millisecond) + timer.Stop() + } + }) + } +} +*/ diff --git a/internal/gmm/common/user_profile.go b/internal/gmm/common/user_profile.go index 0eae81c7..2556e41d 100644 --- a/internal/gmm/common/user_profile.go +++ b/internal/gmm/common/user_profile.go @@ -1,8 +1,6 @@ package common import ( - "fmt" - "github.com/free5gc/amf/internal/context" "github.com/free5gc/amf/internal/logger" ngap_message "github.com/free5gc/amf/internal/ngap/message" @@ -11,7 +9,37 @@ import ( "github.com/free5gc/openapi/models" ) -func RemoveAmfUe(ue *context.AmfUe) { +func RemoveAmfUe(ue *context.AmfUe, notifyNF bool) { + if notifyNF { + // notify SMF to release all sessions + ue.SmContextList.Range(func(key, value interface{}) bool { + smContext := value.(*context.SmContext) + + problemDetail, err := consumer.SendReleaseSmContextRequest(ue, smContext, nil, "", nil) + if problemDetail != nil { + ue.GmmLog.Errorf("Release SmContext Failed Problem[%+v]", problemDetail) + } else if err != nil { + ue.GmmLog.Errorf("Release SmContext Error[%v]", err.Error()) + } + return true + }) + + // notify PCF to terminate AmPolicy association + if ue.AmPolicyAssociation != nil { + problemDetails, err := consumer.AMPolicyControlDelete(ue) + if problemDetails != nil { + ue.GmmLog.Errorf("AM Policy Control Delete Failed Problem[%+v]", problemDetails) + } else if err != nil { + ue.GmmLog.Errorf("AM Policy Control Delete Error[%v]", err.Error()) + } + } + } + + PurgeAmfUeSubscriberData(ue) + ue.Remove() +} + +func PurgeAmfUeSubscriberData(ue *context.AmfUe) { if ue.RanUe[models.AccessType__3_GPP_ACCESS] != nil { err := purgeSubscriberData(ue, models.AccessType__3_GPP_ACCESS) if err != nil { @@ -24,7 +52,6 @@ func RemoveAmfUe(ue *context.AmfUe) { logger.GmmLog.Errorf("Purge subscriber data Error[%v]", err.Error()) } } - ue.Remove() } func AttachRanUeToAmfUeAndReleaseOldIfAny(ue *context.AmfUe, ranUe *context.RanUe) { @@ -48,24 +75,25 @@ func purgeSubscriberData(ue *context.AmfUe, accessType models.AccessType) error if !ue.ContextValid { return nil } - // Purge of subscriber data in AMF described in TS 29.503 4.5.3 + // Purge of subscriber data in AMF described in TS 23.502 4.5.3 if ue.SdmSubscriptionId != "" { problemDetails, err := consumer.SDMUnsubscribe(ue) if problemDetails != nil { logger.GmmLog.Errorf("SDM Unubscribe Failed Problem[%+v]", problemDetails) } else if err != nil { logger.GmmLog.Errorf("SDM Unubscribe Error[%+v]", err) - return fmt.Errorf("SDM Unubscribe Error[%+v]", err) } + ue.SdmSubscriptionId = "" } - if ue.UeCmRegistered { + if ue.UeCmRegistered[accessType] { problemDetails, err := consumer.UeCmDeregistration(ue, accessType) if problemDetails != nil { logger.GmmLog.Errorf("UECM_Registration Failed Problem[%+v]", problemDetails) } else if err != nil { logger.GmmLog.Errorf("UECM_Registration Error[%+v]", err) } + ue.UeCmRegistered[accessType] = false } return nil } diff --git a/internal/gmm/handler.go b/internal/gmm/handler.go index b387560b..e2131286 100644 --- a/internal/gmm/handler.go +++ b/internal/gmm/handler.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/sha256" "encoding/hex" - "errors" "fmt" "net/http" "reflect" @@ -15,13 +14,17 @@ import ( "github.com/antihax/optional" "github.com/mitchellh/mapstructure" "github.com/mohae/deepcopy" + "github.com/pkg/errors" "github.com/free5gc/amf/internal/context" + gmm_common "github.com/free5gc/amf/internal/gmm/common" gmm_message "github.com/free5gc/amf/internal/gmm/message" + "github.com/free5gc/amf/internal/logger" ngap_message "github.com/free5gc/amf/internal/ngap/message" "github.com/free5gc/amf/internal/sbi/consumer" "github.com/free5gc/amf/internal/sbi/producer/callback" "github.com/free5gc/amf/internal/util" + "github.com/free5gc/amf/pkg/factory" "github.com/free5gc/nas" "github.com/free5gc/nas/nasConvert" "github.com/free5gc/nas/nasMessage" @@ -35,6 +38,8 @@ import ( "github.com/free5gc/util/fsm" ) +const psiArraySize = 16 + func HandleULNASTransport(ue *context.AmfUe, anType models.AccessType, ulNasTransport *nasMessage.ULNASTransport, ) error { @@ -123,7 +128,7 @@ func transport5GSMMessage(ue *context.AmfUe, anType models.AccessType, updateData := models.SmContextUpdateData{ Release: true, Cause: models.Cause_REL_DUE_TO_DUPLICATE_SESSION_ID, - SmContextStatusUri: fmt.Sprintf("%s/namf-callback/v1/smContextStatus/%s/%d", + SmContextStatusUri: fmt.Sprintf("%s"+factory.AmfCallbackResUriPrefix+"/smContextStatus/%s/%d", ue.ServingAMF().GetIPv4Uri(), ue.Guti, pduSessionID), } ue.GmmLog.Warningf("Duplicated PDU session ID[%d]", pduSessionID) @@ -260,8 +265,6 @@ func CreatePDUSession(ulNasTransport *nasMessage.ULNASTransport, gmm_message.SendDLNASTransport(ue.RanUe[anType], nasMessage.PayloadContainerTypeN1SMInfo, smMessage, pduSessionID, cause, nil, 0) } else { - ue.Lock.Lock() - defer ue.Lock.Unlock() _, smContextRef, errResponse, problemDetail, err := consumer.SendCreateSmContextRequest( ue, newSmContext, nil, smMessage) if err != nil { @@ -373,7 +376,7 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc registrationRequest *nasMessage.RegistrationRequest, ) error { var guamiFromUeGuti models.Guami - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if ue == nil { return fmt.Errorf("AmfUe is nil") @@ -389,14 +392,8 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc Procedure: context.OnGoingProcedureRegistration, }) - if ue.T3513 != nil { - ue.T3513.Stop() - ue.T3513 = nil // clear the timer - } - if ue.T3565 != nil { - ue.T3565.Stop() - ue.T3565 = nil // clear the timer - } + ue.StopT3513() + ue.StopT3565() // TS 24.501 8.2.6.21: if the UE is sending a REGISTRATION REQUEST message as an initial NAS message, // the UE has a valid 5G NAS security context and the UE needs to send non-cleartext IEs @@ -434,15 +431,16 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc ue.RegistrationType5GS = registrationRequest.NgksiAndRegistrationType5GS.GetRegistrationType5GS() switch ue.RegistrationType5GS { case nasMessage.RegistrationType5GSInitialRegistration: - ue.GmmLog.Debugf("RegistrationType: Initial Registration") + ue.GmmLog.Infof("RegistrationType: Initial Registration") + ue.SecurityContextAvailable = false // need to start authentication procedure later case nasMessage.RegistrationType5GSMobilityRegistrationUpdating: - ue.GmmLog.Debugf("RegistrationType: Mobility Registration Updating") + ue.GmmLog.Infof("RegistrationType: Mobility Registration Updating") if ue.State[anType].Is(context.Deregistered) { gmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMImplicitlyDeregistered, "") return fmt.Errorf("Mobility Registration Updating was sent when the UE state was Deregistered") } case nasMessage.RegistrationType5GSPeriodicRegistrationUpdating: - ue.GmmLog.Debugf("RegistrationType: Periodic Registration Updating") + ue.GmmLog.Infof("RegistrationType: Periodic Registration Updating") if ue.State[anType].Is(context.Deregistered) { gmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMImplicitlyDeregistered, "") return fmt.Errorf("Periodic Registration Updating was sent when the UE state was Deregistered") @@ -451,9 +449,9 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc return fmt.Errorf("Not Supportted RegistrationType: Emergency Registration") case nasMessage.RegistrationType5GSReserved: ue.RegistrationType5GS = nasMessage.RegistrationType5GSInitialRegistration - ue.GmmLog.Debugf("RegistrationType: Reserved") + ue.GmmLog.Infof("RegistrationType: Reserved") default: - ue.GmmLog.Debugf("RegistrationType: %v, chage state to InitialRegistration", ue.RegistrationType5GS) + ue.GmmLog.Infof("RegistrationType: %v, chage state to InitialRegistration", ue.RegistrationType5GS) ue.RegistrationType5GS = nasMessage.RegistrationType5GSInitialRegistration } @@ -464,7 +462,7 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc ue.IdentityTypeUsedForRegistration = nasConvert.GetTypeOfIdentity(mobileIdentity5GSContents[0]) switch ue.IdentityTypeUsedForRegistration { // get type of identity case nasMessage.MobileIdentity5GSTypeNoIdentity: - ue.GmmLog.Debugf("No Identity") + ue.GmmLog.Infof("MobileIdentity5GS: No Identity") case nasMessage.MobileIdentity5GSTypeSuci: if suci, plmnId, err := nasConvert.SuciToStringWithError(mobileIdentity5GSContents); err != nil { return fmt.Errorf("decode SUCI failed: %w", err) @@ -474,27 +472,29 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc ue.Suci = suci ue.PlmnId = util.PlmnIdStringToModels(plmnId) } - ue.GmmLog.Debugf("SUCI: %s", ue.Suci) + ue.GmmLog.Infof("MobileIdentity5GS: SUCI[%s]", ue.Suci) case nasMessage.MobileIdentity5GSType5gGuti: guamiFromUeGutiTmp, guti, err := nasConvert.GutiToStringWithError(mobileIdentity5GSContents) if err != nil { return fmt.Errorf("decode GUTI failed: %w", err) } guamiFromUeGuti = guamiFromUeGutiTmp - ue.GmmLog.Debugf("GUTI: %s", guti) + ue.GmmLog.Infof("MobileIdentity5GS: GUTI[%s]", guti) + // TODO: support multiple ServedGuami servedGuami := amfSelf.ServedGuamiList[0] if reflect.DeepEqual(guamiFromUeGuti, servedGuami) { ue.ServingAmfChanged = false // refresh 5G-GUTI according to 6.12.3 Subscription temporary identifier, TS33.501 if ue.SecurityContextAvailable { - context.AMF_Self().FreeTmsi(int64(ue.Tmsi)) - context.AMF_Self().AllocateGutiToUe(ue) + context.GetSelf().FreeTmsi(int64(ue.Tmsi)) + context.GetSelf().AllocateGutiToUe(ue) } } else { - ue.GmmLog.Debugf("Serving AMF has changed") + ue.GmmLog.Infof("Serving AMF has changed: guamiFromUeGuti[%+v], servedGuami[%+v]", + guamiFromUeGuti, servedGuami) ue.ServingAmfChanged = true - context.AMF_Self().FreeTmsi(int64(ue.Tmsi)) + context.GetSelf().FreeTmsi(int64(ue.Tmsi)) ue.Guti = guti } case nasMessage.MobileIdentity5GSTypeImei: @@ -503,14 +503,14 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc return fmt.Errorf("decode PEI failed: %w", err) } ue.Pei = imei - ue.GmmLog.Debugf("PEI: %s", imei) + ue.GmmLog.Infof("MobileIdentity5GS: PEI[%s]", imei) case nasMessage.MobileIdentity5GSTypeImeisv: imeisv, err := nasConvert.PeiToStringWithError(mobileIdentity5GSContents) if err != nil { return fmt.Errorf("decode PEI failed: %w", err) } ue.Pei = imeisv - ue.GmmLog.Debugf("PEI: %s", imeisv) + ue.GmmLog.Infof("MobileIdentity5GS: PEI[%s]", imeisv) } // NgKsi: TS 24.501 9.11.3.32 @@ -528,8 +528,12 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc } // Copy UserLocation from ranUe - ue.Location = ue.RanUe[anType].Location - ue.Tai = ue.RanUe[anType].Tai + // TODO: This check due to RanUe may release during the process;it should be a better way to make this procedure + // as an atomic operation + if ue.RanUe[anType] != nil { + ue.Location = ue.RanUe[anType].Location + ue.Tai = ue.RanUe[anType].Tai + } // Check TAI if !context.InTaiList(ue.Tai, amfSelf.SupportTaiLists) { @@ -540,8 +544,12 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc if registrationRequest.UESecurityCapability != nil { ue.UESecurityCapability = *registrationRequest.UESecurityCapability } else { - gmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMProtocolErrorUnspecified, "") - return fmt.Errorf("UESecurityCapability is nil") + // TS 23.501 8.2.6.4 + // The UE shall include this IE, unless the UE performs a periodic registration updating procedure. + if registrationRequest.GetRegistrationType5GS() != nasMessage.RegistrationType5GSPeriodicRegistrationUpdating { + gmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMProtocolErrorUnspecified, "") + return fmt.Errorf("UESecurityCapability is nil") + } } // TODO (TS 23.502 4.2.2.2 step 4): if UE's 5g-GUTI is included & serving AMF has changed @@ -552,8 +560,8 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc ue.GmmLog.Warnf("[GMM] %+v", err) // if failed, give up to retrieve the old context and start a new authentication procedure. ue.ServingAmfChanged = false - context.AMF_Self().AllocateGutiToUe(ue) // refresh 5G-GUTI - ue.SecurityContextAvailable = false // need to start authentication procedure later + context.GetSelf().AllocateGutiToUe(ue) // refresh 5G-GUTI + ue.SecurityContextAvailable = false // need to start authentication procedure later } } return nil @@ -562,7 +570,7 @@ func HandleRegistrationRequest(ue *context.AmfUe, anType models.AccessType, proc func contextTransferFromOldAmf(ue *context.AmfUe, anType models.AccessType, oldAmfGuami models.Guami) error { ue.GmmLog.Infof("ContextTransfer from old AMF[%s %s]", oldAmfGuami.PlmnId, oldAmfGuami.AmfId) - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() searchOpt := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ Guami: optional.NewInterface(openapi.MarshToJsonString(oldAmfGuami)), } @@ -601,7 +609,7 @@ func IdentityVerification(ue *context.AmfUe) bool { func HandleInitialRegistration(ue *context.AmfUe, anType models.AccessType) error { ue.GmmLog.Infoln("Handle InitialRegistration") - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() // update Kgnb/Kn3iwf ue.UpdateSecurityContext(anType) @@ -662,8 +670,9 @@ func HandleInitialRegistration(ue *context.AmfUe, anType models.AccessType) erro if ue.ServingAmfChanged || ue.State[models.AccessType_NON_3_GPP_ACCESS].Is(context.Registered) || !ue.ContextValid { if err := communicateWithUDM(ue, anType); err != nil { + ue.GmmLog.Errorf("communicateWithUDM error: %v", err) gmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMPLMNNotAllowed, "") - return err + return errors.Wrap(err, "communicateWithUDM failed") } } @@ -739,30 +748,17 @@ func HandleInitialRegistration(ue *context.AmfUe, anType models.AccessType) erro if anType == models.AccessType__3_GPP_ACCESS { ue.T3512Value = amfSelf.T3512Value } else { - ue.Non3gppDeregistrationTimerValue = amfSelf.Non3gppDeregistrationTimerValue + ue.Non3gppDeregTimerValue = amfSelf.Non3gppDeregTimerValue } - if anType == models.AccessType__3_GPP_ACCESS { - gmm_message.SendRegistrationAccept(ue, anType, nil, nil, nil, nil, nil) - } else { - // TS 23.502 4.12.2.2 10a ~ 13: if non-3gpp, AMF should send initial context setup request to N3IWF first, - // and send registration accept after receiving initial context setup response - ngap_message.SendInitialContextSetupRequest(ue, anType, nil, nil, nil, nil, nil) - - registrationAccept, err := gmm_message.BuildRegistrationAccept(ue, anType, nil, nil, nil, nil) - if err != nil { - ue.GmmLog.Errorf("Build Registration Accept: %+v", err) - return nil - } - ue.RegistrationAcceptForNon3GPPAccess = registrationAccept - } + gmm_message.SendRegistrationAccept(ue, anType, nil, nil, nil, nil, nil) return nil } func HandleMobilityAndPeriodicRegistrationUpdating(ue *context.AmfUe, anType models.AccessType) error { ue.GmmLog.Infoln("Handle MobilityAndPeriodicRegistrationUpdating") - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if ue.RegistrationRequest.UpdateType5GS != nil { if ue.RegistrationRequest.UpdateType5GS.GetNGRanRcu() == nasMessage.NGRanRadioCapabilityUpdateNeeded { @@ -815,19 +811,19 @@ func HandleMobilityAndPeriodicRegistrationUpdating(ue *context.AmfUe, anType mod if ue.ServingAmfChanged || ue.State[models.AccessType_NON_3_GPP_ACCESS].Is(context.Registered) || !ue.ContextValid { if err := communicateWithUDM(ue, anType); err != nil { + ue.GmmLog.Errorf("communicateWithUDM error: %v", err) gmm_message.SendRegistrationReject(ue.RanUe[anType], nasMessage.Cause5GMMPLMNNotAllowed, "") - return err + return errors.Wrap(err, "communicateWithUDM failed") } } - var reactivationResult *[16]bool + var reactivationResult *[psiArraySize]bool var errPduSessionId, errCause []uint8 - ctxList := ngapType.PDUSessionResourceSetupListCxtReq{} - suList := ngapType.PDUSessionResourceSetupListSUReq{} + cxtList := ngapType.PDUSessionResourceSetupListCxtReq{} if ue.RegistrationRequest.UplinkDataStatus != nil { uplinkDataPsi := nasConvert.PSIToBooleanArray(ue.RegistrationRequest.UplinkDataStatus.Buffer) - reactivationResult = new([16]bool) + reactivationResult = new([psiArraySize]bool) allowReEstablishPduSession := true // determines that the UE is in non-allowed area or is not in allowed area @@ -848,178 +844,64 @@ func HandleMobilityAndPeriodicRegistrationUpdating(ue *context.AmfUe, anType mod } } } else { - for idx, hasUplinkData := range uplinkDataPsi { - pduSessionId := int32(idx) - if smContext, ok := ue.SmContextFindByPDUSessionID(pduSessionId); ok { - // uplink data are pending for the corresponding PDU session identity - if hasUplinkData && smContext.AccessType() == models.AccessType__3_GPP_ACCESS { - response, errResponse, problemDetail, err := consumer.SendUpdateSmContextActivateUpCnxState( - ue, smContext, anType) - if response == nil { - reactivationResult[pduSessionId] = true - errPduSessionId = append(errPduSessionId, uint8(pduSessionId)) - cause := nasMessage.Cause5GMMProtocolErrorUnspecified - if errResponse != nil { - switch errResponse.JsonData.Error.Cause { - case "OUT_OF_LADN_SERVICE_AREA": - cause = nasMessage.Cause5GMMLADNNotAvailable - case "PRIORITIZED_SERVICES_ONLY": - cause = nasMessage.Cause5GMMRestrictedServiceArea - case "DNN_CONGESTION", "S-NSSAI_CONGESTION": - cause = nasMessage.Cause5GMMInsufficientUserPlaneResourcesForThePDUSession - } - } - errCause = append(errCause, cause) - - if problemDetail != nil { - ue.GmmLog.Errorf("Update SmContext Failed Problem[%+v]", problemDetail) - } else if err != nil { - ue.GmmLog.Errorf("Update SmContext Error[%v]", err.Error()) - } - } else { - if ue.RanUe[anType].UeContextRequest { - ngap_message.AppendPDUSessionResourceSetupListCxtReq(&ctxList, pduSessionId, - smContext.Snssai(), response.BinaryDataN1SmMessage, response.BinaryDataN2SmInformation) - } else { - ngap_message.AppendPDUSessionResourceSetupListSUReq(&suList, pduSessionId, - smContext.Snssai(), response.BinaryDataN1SmMessage, response.BinaryDataN2SmInformation) - } - } - } - } - } + // There is no serviceType in MobilityAndPeriodicRegistrationUpdating + errPduSessionId, errCause = reactivatePendingULDataPDUSession(ue, anType, 0, &uplinkDataPsi, 0, &cxtList, + reactivationResult, errPduSessionId, errCause) } } - var pduSessionStatus *[16]bool + var pduSessionStatus *[psiArraySize]bool if ue.RegistrationRequest.PDUSessionStatus != nil { - pduSessionStatus = new([16]bool) - psiArray := nasConvert.PSIToBooleanArray(ue.RegistrationRequest.PDUSessionStatus.Buffer) - for psi := 1; psi <= 15; psi++ { - pduSessionId := int32(psi) - if smContext, ok := ue.SmContextFindByPDUSessionID(pduSessionId); ok { - if !psiArray[psi] && smContext.AccessType() == anType { - cause := models.Cause_PDU_SESSION_STATUS_MISMATCH - causeAll := &context.CauseAll{ - Cause: &cause, - } - problemDetail, err := consumer.SendReleaseSmContextRequest(ue, smContext, causeAll, "", nil) - if problemDetail != nil { - pduSessionStatus[psi] = true - ue.GmmLog.Errorf("Release SmContext Failed Problem[%+v]", problemDetail) - } else if err != nil { - pduSessionStatus[psi] = true - ue.GmmLog.Errorf("Release SmContext Error[%v]", err.Error()) - } else { - pduSessionStatus[psi] = false - } - } else { - pduSessionStatus[psi] = true - } - } - } + pduSessionStatus = new([psiArraySize]bool) + pduSessionPsi := nasConvert.PSIToBooleanArray(ue.RegistrationRequest.PDUSessionStatus.Buffer) + releaseInactivePDUSession(ue, anType, &pduSessionPsi, pduSessionStatus) } - if ue.RegistrationRequest.AllowedPDUSessionStatus != nil { - allowedPsis := nasConvert.PSIToBooleanArray(ue.RegistrationRequest.AllowedPDUSessionStatus.Buffer) - if ue.N1N2Message != nil { - requestData := ue.N1N2Message.Request.JsonData - n1Msg := ue.N1N2Message.Request.BinaryDataN1Message - n2Info := ue.N1N2Message.Request.BinaryDataN2Information - - // downlink signalling - if n2Info == nil { - if len(suList.List) != 0 { - nasPdu, err := gmm_message.BuildRegistrationAccept(ue, anType, pduSessionStatus, - reactivationResult, errPduSessionId, errCause) - if err != nil { - return err - } - ngap_message.SendPDUSessionResourceSetupRequest(ue.RanUe[anType], nasPdu, suList) - } else { - gmm_message.SendRegistrationAccept(ue, anType, pduSessionStatus, - reactivationResult, errPduSessionId, errCause, &ctxList) - } - switch requestData.N1MessageContainer.N1MessageClass { - case models.N1MessageClass_SM: - gmm_message.SendDLNASTransport( - ue.RanUe[anType], nasMessage.PayloadContainerTypeN1SMInfo, - n1Msg, requestData.PduSessionId, 0, nil, 0) - case models.N1MessageClass_LPP: - gmm_message.SendDLNASTransport( - ue.RanUe[anType], nasMessage.PayloadContainerTypeLPP, n1Msg, 0, 0, nil, 0) - case models.N1MessageClass_SMS: - gmm_message.SendDLNASTransport( - ue.RanUe[anType], nasMessage.PayloadContainerTypeSMS, n1Msg, 0, 0, nil, 0) - case models.N1MessageClass_UPDP: - gmm_message.SendDLNASTransport( - ue.RanUe[anType], nasMessage.PayloadContainerTypeUEPolicy, n1Msg, 0, 0, nil, 0) - } - ue.N1N2Message = nil - return nil - } - - smInfo := requestData.N2InfoContainer.SmInfo - smContext, exist := ue.SmContextFindByPDUSessionID(requestData.PduSessionId) - if !exist { - ue.N1N2Message = nil - return fmt.Errorf("Pdu Session Id not Exists") - } + // AllowedPDUSessionStatus indicate to the network PDU sessions associated with non-3GPP access that + // are allowed to be re-established over 3GPP access + if ue.RegistrationRequest.AllowedPDUSessionStatus != nil && + anType == models.AccessType__3_GPP_ACCESS && ue.N1N2Message != nil { + allowedPsi := nasConvert.PSIToBooleanArray(ue.RegistrationRequest.AllowedPDUSessionStatus.Buffer) + requestData := ue.N1N2Message.Request.JsonData + n1Msg := ue.N1N2Message.Request.BinaryDataN1Message + n2Info := ue.N1N2Message.Request.BinaryDataN2Information + + if n2Info == nil { + // SMF has indicated pending downlink signalling only, + // forward the received 5GSM message via 3GPP access to the UE + // after the REGISTRATION ACCEPT message is sent + gmm_message.SendRegistrationAccept(ue, anType, pduSessionStatus, + reactivationResult, errPduSessionId, errCause, &cxtList) + + switch requestData.N1MessageContainer.N1MessageClass { + case models.N1MessageClass_SM: + gmm_message.SendDLNASTransport(ue.RanUe[anType], nasMessage.PayloadContainerTypeN1SMInfo, + n1Msg, requestData.PduSessionId, 0, nil, 0) + case models.N1MessageClass_LPP: + gmm_message.SendDLNASTransport(ue.RanUe[anType], nasMessage.PayloadContainerTypeLPP, + n1Msg, 0, 0, nil, 0) + case models.N1MessageClass_SMS: + gmm_message.SendDLNASTransport(ue.RanUe[anType], nasMessage.PayloadContainerTypeSMS, + n1Msg, 0, 0, nil, 0) + case models.N1MessageClass_UPDP: + gmm_message.SendDLNASTransport(ue.RanUe[anType], nasMessage.PayloadContainerTypeUEPolicy, + n1Msg, 0, 0, nil, 0) + } + ue.N1N2Message = nil + return nil + } - if smContext.AccessType() == models.AccessType_NON_3_GPP_ACCESS { - if reactivationResult == nil { - reactivationResult = new([16]bool) - } - if allowedPsis[requestData.PduSessionId] { - // TODO: error handling - response, errRes, _, err := consumer.SendUpdateSmContextChangeAccessType(ue, smContext, true) - if err != nil { - return err - } else if response == nil { - reactivationResult[requestData.PduSessionId] = true - errPduSessionId = append(errPduSessionId, uint8(requestData.PduSessionId)) - cause := nasMessage.Cause5GMMProtocolErrorUnspecified - if errRes != nil { - switch errRes.JsonData.Error.Cause { - case "OUT_OF_LADN_SERVICE_AREA": - cause = nasMessage.Cause5GMMLADNNotAvailable - case "PRIORITIZED_SERVICES_ONLY": - cause = nasMessage.Cause5GMMRestrictedServiceArea - case "DNN_CONGESTION", "S-NSSAI_CONGESTION": - cause = nasMessage.Cause5GMMInsufficientUserPlaneResourcesForThePDUSession - } - } - errCause = append(errCause, cause) - } else { - smContext.SetUserLocation(deepcopy.Copy(ue.Location).(models.UserLocation)) - smContext.SetAccessType(models.AccessType__3_GPP_ACCESS) - if response.BinaryDataN2SmInformation != nil && - response.JsonData.N2SmInfoType == models.N2SmInfoType_PDU_RES_SETUP_REQ { - ngap_message.AppendPDUSessionResourceSetupListSUReq(&suList, requestData.PduSessionId, - smContext.Snssai(), nil, response.BinaryDataN2SmInformation) - } - } - } else { - ue.GmmLog.Warnf("UE was reachable but did not accept to re-activate the PDU Session[%d]", - requestData.PduSessionId) - callback.SendN1N2TransferFailureNotification(ue, - models.N1N2MessageTransferCause_UE_NOT_REACHABLE_FOR_SESSION) - } - } else if smInfo.N2InfoContent.NgapIeType == models.NgapIeType_PDU_RES_SETUP_REQ { - var nasPdu []byte - var err error - if n1Msg != nil { - pduSessionId := uint8(smInfo.PduSessionId) - nasPdu, err = gmm_message.BuildDLNASTransport(ue, anType, nasMessage.PayloadContainerTypeN1SMInfo, - n1Msg, pduSessionId, nil, nil, 0) - if err != nil { - return err - } - } - ngap_message.AppendPDUSessionResourceSetupListSUReq(&suList, smInfo.PduSessionId, - *smInfo.SNssai, nasPdu, n2Info) - } + // SMF has indicated pending downlink data + // notify the SMF that reactivation of the user-plane resources for the corresponding PDU session(s) + // associated with non-3GPP access + smContext, exist := ue.SmContextFindByPDUSessionID(requestData.PduSessionId) + if !exist { + ue.N1N2Message = nil + return fmt.Errorf("SmContext[PDU Session ID:%d] not found", requestData.PduSessionId) } + + errPduSessionId, errCause = reestablishAllowedPDUSessionOver3GPP(ue, anType, smContext, &allowedPsi, &cxtList, + reactivationResult, errPduSessionId, errCause) } if ue.LocationChanged && ue.RequestTriggerLocationChange { @@ -1048,39 +930,9 @@ func HandleMobilityAndPeriodicRegistrationUpdating(ue *context.AmfUe, anType mod // TODO: GUTI reassignment if need (based on operator poilcy) // TODO: T3512/Non3GPP de-registration timer reassignment if need (based on operator policy) - if ue.RanUe[anType].UeContextRequest { - // update Kgnb/Kn3iwf - ue.UpdateSecurityContext(anType) - - if anType == models.AccessType__3_GPP_ACCESS { - gmm_message.SendRegistrationAccept(ue, anType, pduSessionStatus, reactivationResult, - errPduSessionId, errCause, &ctxList) - } else { - ngap_message.SendInitialContextSetupRequest(ue, anType, nil, &ctxList, nil, nil, nil) - registrationAccept, err := gmm_message.BuildRegistrationAccept(ue, anType, - pduSessionStatus, reactivationResult, errPduSessionId, errCause) - if err != nil { - ue.GmmLog.Errorf("Build Registration Accept: %+v", err) - return nil - } - ue.RegistrationAcceptForNon3GPPAccess = registrationAccept - } - return nil - } else { - nasPdu, err := gmm_message.BuildRegistrationAccept(ue, anType, pduSessionStatus, reactivationResult, - errPduSessionId, errCause) - if err != nil { - ue.GmmLog.Error(err.Error()) - } - if len(suList.List) != 0 { - ngap_message.SendPDUSessionResourceSetupRequest(ue.RanUe[anType], nasPdu, suList) - } else { - ngap_message.SendDownlinkNasTransport(ue.RanUe[anType], nasPdu, nil) - } - // TODO: when state machaine, remove it - // ue.ClearRegistrationRequestData(anType) - return nil - } + gmm_message.SendRegistrationAccept(ue, anType, pduSessionStatus, reactivationResult, + errPduSessionId, errCause, &cxtList) + return nil } // TS 23.502 4.2.2.2.2 step 1 @@ -1130,7 +982,7 @@ func negotiateDRXParameters(ue *context.AmfUe, requestedDRXParameters *nasType.R func communicateWithUDM(ue *context.AmfUe, accessType models.AccessType) error { ue.GmmLog.Debugln("communicateWithUDM") - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() // UDM selection described in TS 23.501 6.3.8 // TODO: consider udm group id, Routing ID part of SUCI, GPSI or External Group ID (e.g., by the NEF) @@ -1139,7 +991,7 @@ func communicateWithUDM(ue *context.AmfUe, accessType models.AccessType) error { } resp, err := consumer.SendSearchNFInstances(amfSelf.NrfUri, models.NfType_UDM, models.NfType_AMF, ¶m) if err != nil { - return fmt.Errorf("AMF can not select an UDM by NRF") + return errors.Errorf("AMF can not select an UDM by NRF: SendSearchNFInstances failed") } var uecmUri, sdmUri string @@ -1154,51 +1006,52 @@ func communicateWithUDM(ue *context.AmfUe, accessType models.AccessType) error { ue.NudmUECMUri = uecmUri ue.NudmSDMUri = sdmUri if ue.NudmUECMUri == "" || ue.NudmSDMUri == "" { - return fmt.Errorf("AMF can not select an UDM by NRF") + return errors.Errorf("AMF can not select an UDM by NRF: SearchNFServiceUri failed") } problemDetails, err := consumer.UeCmRegistration(ue, accessType, true) if problemDetails != nil { - ue.GmmLog.Errorf("UECM_Registration Failed Problem[%+v]", problemDetails) + return errors.Errorf(problemDetails.Cause) } else if err != nil { - ue.GmmLog.Errorf("UECM_Registration Error[%+v]", err) + return errors.Wrap(err, "UECM_Registration Error") } + // TS 23.502 4.2.2.2.1 14a-c. + // "After a successful response is received, the AMF subscribes to be notified + // using Nudm_SDM_Subscribe when the data requested is modified" problemDetails, err = consumer.SDMGetAmData(ue) if problemDetails != nil { - ue.GmmLog.Errorf("SDM_Get AmData Failed Problem[%+v]", problemDetails) - return fmt.Errorf(problemDetails.Cause) + return errors.Errorf(problemDetails.Cause) } else if err != nil { - return fmt.Errorf("SDM_Get AmData Error[%+v]", err) + return errors.Wrap(err, "SDM_Get AmData Error") } problemDetails, err = consumer.SDMGetSmfSelectData(ue) if problemDetails != nil { - ue.GmmLog.Errorf("SDM_Get SmfSelectData Failed Problem[%+v]", problemDetails) + return errors.Errorf(problemDetails.Cause) } else if err != nil { - return fmt.Errorf("SDM_Get SmfSelectData Error[%+v]", err) + return errors.Wrap(err, "SDM_Get SmfSelectData Error") } problemDetails, err = consumer.SDMGetUeContextInSmfData(ue) if problemDetails != nil { - ue.GmmLog.Errorf("SDM_Get UeContextInSmfData Failed Problem[%+v]", problemDetails) + return errors.Errorf(problemDetails.Cause) } else if err != nil { - return fmt.Errorf("SDM_Get UeContextInSmfData Error[%+v]", err) + return errors.Wrap(err, "SDM_Get UeContextInSmfData Error") } problemDetails, err = consumer.SDMSubscribe(ue) if problemDetails != nil { - ue.GmmLog.Errorf("SDM Subscribe Failed Problem[%+v]", problemDetails) + return errors.Errorf(problemDetails.Cause) } else if err != nil { - ue.GmmLog.Errorf("SDM Subscribe Error[%+v]", err) - return fmt.Errorf("SDM Subscribe Error[%+v]", err) + return errors.Wrap(err, "SDM Subscribe Error") } ue.ContextValid = true return nil } func getSubscribedNssai(ue *context.AmfUe) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if ue.NudmSDMUri == "" { param := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ Supi: optional.NewString(ue.Supi), @@ -1223,7 +1076,7 @@ func getSubscribedNssai(ue *context.AmfUe) { // TS 23.502 4.2.2.2.3 Registration with AMF Re-allocation func handleRequestedNssai(ue *context.AmfUe, anType models.AccessType) error { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if ue.RegistrationRequest.RequestedNSSAI != nil { requestedNssai, err := nasConvert.RequestedNssaiToModels(ue.RegistrationRequest.RequestedNSSAI) @@ -1255,8 +1108,7 @@ func handleRequestedNssai(ue *context.AmfUe, anType models.AccessType) error { if needSliceSelection { if ue.NssfUri == "" { for { - err := consumer.SearchNssfNSSelectionInstance( - ue, amfSelf.NrfUri, models.NfType_NSSF, models.NfType_AMF, nil) + err := consumer.SearchNssfNSSelectionInstance(ue, amfSelf.NrfUri, models.NfType_NSSF, models.NfType_AMF, nil) if err != nil { ue.GmmLog.Errorf("AMF can not select an NSSF Instance by NRF[Error: %+v]", err) time.Sleep(2 * time.Second) @@ -1305,18 +1157,17 @@ func handleRequestedNssai(ue *context.AmfUe, anType models.AccessType) error { } if !reflect.DeepEqual(*guami.PlmnId, targetAmfPlmnId) { - searchTargetAmfQueryParam.TargetPlmnList = optional.NewInterface( - openapi.MarshToJsonString([]models.PlmnId{targetAmfPlmnId})) - searchTargetAmfQueryParam.RequesterPlmnList = optional.NewInterface( - openapi.MarshToJsonString([]models.PlmnId{*guami.PlmnId})) + searchTargetAmfQueryParam.TargetPlmnList = optional. + NewInterface(openapi.MarshToJsonString([]models.PlmnId{targetAmfPlmnId})) + searchTargetAmfQueryParam.RequesterPlmnList = optional. + NewInterface(openapi.MarshToJsonString([]models.PlmnId{*guami.PlmnId})) } searchTargetAmfQueryParam.AmfRegionId = optional.NewString(targetAmfSetToken[2]) searchTargetAmfQueryParam.AmfSetId = optional.NewString(targetAmfSetToken[3]) } else if len(netwotkSliceInfo.CandidateAmfList) > 0 { // TODO: select candidate Amf based on local poilcy - searchTargetAmfQueryParam.TargetNfInstanceId = optional.NewInterface( - netwotkSliceInfo.CandidateAmfList[0]) + searchTargetAmfQueryParam.TargetNfInstanceId = optional.NewInterface(netwotkSliceInfo.CandidateAmfList[0]) } } @@ -1357,8 +1208,7 @@ func handleRequestedNssai(ue *context.AmfUe, anType models.AccessType) error { } else { // Condition (B) Step 7: initial AMF can not find Target AMF via NRF -> Send Reroute NAS Request to RAN allowedNssaiNgap := ngapConvert.AllowedNssaiToNgap(ue.AllowedNssai[anType]) - ngap_message.SendRerouteNasRequest( - ue, anType, nil, ue.RanUe[anType].InitialUEMessage, &allowedNssaiNgap) + ngap_message.SendRerouteNasRequest(ue, anType, nil, ue.RanUe[anType].InitialUEMessage, &allowedNssaiNgap) } return nil } @@ -1382,25 +1232,25 @@ func handleRequestedNssai(ue *context.AmfUe, anType models.AccessType) error { } func assignLadnInfo(ue *context.AmfUe, accessType models.AccessType) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ue.LadnInfo = nil if ue.RegistrationRequest.LADNIndication != nil { - ue.LadnInfo = make([]context.LADN, 0) + ue.LadnInfo = make([]factory.Ladn, 0) // request for LADN information if ue.RegistrationRequest.LADNIndication.GetLen() == 0 { if ue.HasWildCardSubscribedDNN() { for _, ladn := range amfSelf.LadnPool { - if ue.TaiListInRegistrationArea(ladn.TaiLists, accessType) { - ue.LadnInfo = append(ue.LadnInfo, *ladn) + if ue.TaiListInRegistrationArea(ladn.TaiList, accessType) { + ue.LadnInfo = append(ue.LadnInfo, ladn) } } } else { for _, snssaiInfos := range ue.SmfSelectionData.SubscribedSnssaiInfos { for _, dnnInfo := range snssaiInfos.DnnInfos { if ladn, ok := amfSelf.LadnPool[dnnInfo.Dnn]; ok { // check if this dnn is a ladn - if ue.TaiListInRegistrationArea(ladn.TaiLists, accessType) { - ue.LadnInfo = append(ue.LadnInfo, *ladn) + if ue.TaiListInRegistrationArea(ladn.TaiList, accessType) { + ue.LadnInfo = append(ue.LadnInfo, ladn) } } } @@ -1410,8 +1260,8 @@ func assignLadnInfo(ue *context.AmfUe, accessType models.AccessType) { requestedLadnList := nasConvert.LadnToModels(ue.RegistrationRequest.LADNIndication.GetLADNDNNValue()) for _, requestedLadn := range requestedLadnList { if ladn, ok := amfSelf.LadnPool[requestedLadn]; ok { - if ue.TaiListInRegistrationArea(ladn.TaiLists, accessType) { - ue.LadnInfo = append(ue.LadnInfo, *ladn) + if ue.TaiListInRegistrationArea(ladn.TaiList, accessType) { + ue.LadnInfo = append(ue.LadnInfo, ladn) } } } @@ -1421,8 +1271,8 @@ func assignLadnInfo(ue *context.AmfUe, accessType models.AccessType) { for _, dnnInfo := range snssaiInfos.DnnInfos { if dnnInfo.Dnn != "*" { if ladn, ok := amfSelf.LadnPool[dnnInfo.Dnn]; ok { - if ue.TaiListInRegistrationArea(ladn.TaiLists, accessType) { - ue.LadnInfo = append(ue.LadnInfo, *ladn) + if ue.TaiListInRegistrationArea(ladn.TaiList, accessType) { + ue.LadnInfo = append(ue.LadnInfo, ladn) } } } @@ -1431,6 +1281,186 @@ func assignLadnInfo(ue *context.AmfUe, accessType models.AccessType) { } } +func reactivatePendingULDataPDUSession(ue *context.AmfUe, anType models.AccessType, serviceType uint8, + uplinkDataPsi *[psiArraySize]bool, dlPduSessionId int32, cxtList *ngapType.PDUSessionResourceSetupListCxtReq, + reactivationResult *[psiArraySize]bool, errPduSessionId, errCause []uint8, +) ([]uint8, []uint8) { + ue.SmContextList.Range(func(key, value interface{}) bool { + pduSessionID := key.(int32) + smContext := value.(*context.SmContext) + + // uplink data are pending for the corresponding PDU session identity + if !uplinkDataPsi[pduSessionID] || + (pduSessionID == dlPduSessionId && serviceType == nasMessage.ServiceTypeMobileTerminatedServices) { + // Skipping SendUpdateSmContextActivateUpCnxState for the following reason: + // In Step 4 of 4.2.3.2 UE Triggered Service Request in TS23.502 + // > This procedure is triggered by the SMF but the PDU Session(s) identified by the UE + // > correlates to other PDU Session ID(s) than the one triggering the procedure + // However, in the case of Mo-data etc., it cannot be skipped because AMF need to know + // latest N2SmInformation even if the UE has known the N2Information received at + // previous N1N2MessageTransfer. + return true + } + + // indicate the SMF to re-establish the user-plane resources for the corresponding PDU session + // TODO: determine the UE presence in LADN service area and forward the UE presence + // in LADN service area towards the SMF, if the corresponding PDU session is + // a PDU session for LADN + response, errRsp, problemDetail, err := consumer.SendUpdateSmContextActivateUpCnxState( + ue, smContext, anType) + if err != nil { + reactivationResult[pduSessionID] = true + ue.GmmLog.Errorf("SendUpdateSmContextActivateUpCnxState[pduSessionID:%d] Error: %+v", + pduSessionID, err) + } else if response == nil { + reactivationResult[pduSessionID] = true + errPduSessionId = append(errPduSessionId, uint8(pduSessionID)) + cause := nasMessage.Cause5GMMProtocolErrorUnspecified + if errRsp != nil { + switch errRsp.JsonData.Error.Cause { + case "OUT_OF_LADN_SERVICE_AREA": + cause = nasMessage.Cause5GMMLADNNotAvailable + case "PRIORITIZED_SERVICES_ONLY": + cause = nasMessage.Cause5GMMRestrictedServiceArea + case "DNN_CONGESTION", "S-NSSAI_CONGESTION": + cause = nasMessage.Cause5GMMInsufficientUserPlaneResourcesForThePDUSession + } + } + errCause = append(errCause, cause) + + if problemDetail != nil { + ue.GmmLog.Errorf("Update SmContext Failed Problem[%+v]", problemDetail) + } else if err != nil { + ue.GmmLog.Errorf("Update SmContext Error[%v]", err.Error()) + } + } else { + ue.GmmLog.Infof("Re-active the pending uplink PDU Session[%d] over %q successfully", + pduSessionID, smContext.AccessType()) + ngap_message.AppendPDUSessionResourceSetupListCxtReq(cxtList, pduSessionID, + smContext.Snssai(), response.BinaryDataN1SmMessage, response.BinaryDataN2SmInformation) + } + return true + }) + return errPduSessionId, errCause +} + +func releaseInactivePDUSession(ue *context.AmfUe, anType models.AccessType, uePduStatus *[psiArraySize]bool, + pduStatusResult *[psiArraySize]bool, +) { + ue.SmContextList.Range(func(key, value interface{}) bool { + pduSessionID := key.(int32) + smContext := value.(*context.SmContext) + + if uePduStatus[pduSessionID] { + pduStatusResult[pduSessionID] = true + return true + } + + // perform a local release of all those PDU session which are in 5GSM state PDU SESSION ACTIVE + // on the AMF side associated with the access type the REGISTRATION REQUEST message is sent over, + // but are indicated by the UE as being in 5GSM state PDU SESSION INACTIVE + cause := models.Cause_PDU_SESSION_STATUS_MISMATCH + causeAll := &context.CauseAll{ + Cause: &cause, + } + ue.GmmLog.Infof("Release Inactive PDU Session[%d] over %q", pduSessionID, smContext.AccessType()) + problemDetail, err := consumer.SendReleaseSmContextRequest(ue, smContext, causeAll, "", nil) + if problemDetail != nil { + ue.GmmLog.Errorf("Release SmContext Failed Problem[%+v]", problemDetail) + } else if err != nil { + ue.GmmLog.Errorf("Release SmContext Error[%v]", err.Error()) + } + return true + }) +} + +func reestablishAllowedPDUSessionOver3GPP(ue *context.AmfUe, anType models.AccessType, smContext *context.SmContext, + allowedPsi *[psiArraySize]bool, cxtList *ngapType.PDUSessionResourceSetupListCxtReq, + reactivationResult *[psiArraySize]bool, errPduSessionId, errCause []uint8, +) ([]uint8, []uint8) { + requestData := ue.N1N2Message.Request.JsonData + + if ue.N1N2Message == nil || ue.N1N2Message.Request.BinaryDataN2Information == nil { + // no pending downlink data + return errPduSessionId, errCause + } + + if smContext == nil || smContext.AccessType() != models.AccessType_NON_3_GPP_ACCESS { + return errPduSessionId, errCause + } + + // SMF has indicated pending downlink data + // notify the SMF that reactivation of the user-plane resources for the corresponding PDU session(s) + // associated with non-3GPP access + if reactivationResult == nil { + reactivationResult = new([psiArraySize]bool) + } + if allowedPsi[requestData.PduSessionId] { + // re-establish the PDU session associated with non-3GPP access over 3GPP access. + // notify the SMF if the corresponding PDU session ID(s) associated with non-3GPP access + // are indicated in the Allowed PDU session status IE + // TODO: error handling + response, errRes, _, err := consumer.SendUpdateSmContextChangeAccessType(ue, smContext, true) + if err != nil { + reactivationResult[requestData.PduSessionId] = true + ue.GmmLog.Errorf("SendUpdateSmContextActivateUpCnxState[pduSessionID:%d] Error: %+v", + requestData.PduSessionId, err) + } else if response == nil { + ue.GmmLog.Warnf("failed to re-establish allowed PDU Session[%d] over 3GPP access", + requestData.PduSessionId) + reactivationResult[requestData.PduSessionId] = true + errPduSessionId = append(errPduSessionId, uint8(requestData.PduSessionId)) + cause := nasMessage.Cause5GMMProtocolErrorUnspecified + if errRes != nil { + switch errRes.JsonData.Error.Cause { + case "OUT_OF_LADN_SERVICE_AREA": + cause = nasMessage.Cause5GMMLADNNotAvailable + case "PRIORITIZED_SERVICES_ONLY": + cause = nasMessage.Cause5GMMRestrictedServiceArea + case "DNN_CONGESTION", "S-NSSAI_CONGESTION": + cause = nasMessage.Cause5GMMInsufficientUserPlaneResourcesForThePDUSession + } + } + errCause = append(errCause, cause) + } else { + ue.GmmLog.Infof("re-establish allowed PDU Session[%d] over 3GPP access successfully", + requestData.PduSessionId) + // the AMF and SMF update the associated access type of the corresponding PDU session + smContext.SetUserLocation(deepcopy.Copy(ue.Location).(models.UserLocation)) + smContext.SetAccessType(models.AccessType__3_GPP_ACCESS) + if response.BinaryDataN2SmInformation != nil && + response.JsonData.N2SmInfoType == models.N2SmInfoType_PDU_RES_SETUP_REQ { + // discard the received 5GSM message for PDU session(s) associated with non-3GPP access + ngap_message.AppendPDUSessionResourceSetupListCxtReq(cxtList, requestData.PduSessionId, + smContext.Snssai(), nil, response.BinaryDataN2SmInformation) + } + } + } else { + // notify the SMF if the corresponding PDU session ID(s) associated with non-3GPP access + // are not indicated in the Allowed PDU session status IE + ue.GmmLog.Warnf("UE was reachable but did not accept to re-activate the PDU Session[%d]", + requestData.PduSessionId) + callback.SendN1N2TransferFailureNotification(ue, + models.N1N2MessageTransferCause_UE_NOT_REACHABLE_FOR_SESSION) + } + return errPduSessionId, errCause +} + +func getPDUSessionStatus(ue *context.AmfUe, anType models.AccessType) *[psiArraySize]bool { + var pduStatusResult [psiArraySize]bool + ue.SmContextList.Range(func(key, value interface{}) bool { + pduSessionID := key.(int32) + smContext := value.(*context.SmContext) + + if smContext.AccessType() != anType { + return true + } + pduStatusResult[pduSessionID] = true + return true + }) + return &pduStatusResult +} + func HandleIdentityResponse(ue *context.AmfUe, identityResponse *nasMessage.IdentityResponse) error { if ue == nil { return fmt.Errorf("AmfUe is nil") @@ -1515,10 +1545,7 @@ func HandleNotificationResponse(ue *context.AmfUe, notificationResponse *nasMess return fmt.Errorf("NAS message integrity check failed") } - if ue.T3565 != nil { - ue.T3565.Stop() - ue.T3565 = nil // clear the timer - } + ue.StopT3565() if notificationResponse != nil && notificationResponse.PDUSessionStatus != nil { psiArray := nasConvert.PSIToBooleanArray(notificationResponse.PDUSessionStatus.Buffer) @@ -1576,7 +1603,7 @@ func AuthenticationProcedure(ue *context.AmfUe, accessType models.AccessType) (b return false, nil } - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() // TODO: consider ausf group id, Routing ID part of SUCI param := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{} @@ -1642,14 +1669,8 @@ func HandleServiceRequest(ue *context.AmfUe, anType models.AccessType, ue.GmmLog.Info("Handle Service Request") - if ue.T3513 != nil { - ue.T3513.Stop() - ue.T3513 = nil // clear the timer - } - if ue.T3565 != nil { - ue.T3565.Stop() - ue.T3565 = nil // clear the timer - } + ue.StopT3513() + ue.StopT3565() // Set No ongoing if procedure := ue.OnGoing(anType).Procedure; procedure == context.OnGoingProcedurePaging { @@ -1660,10 +1681,16 @@ func HandleServiceRequest(ue *context.AmfUe, anType models.AccessType, ue.GmmLog.Warnf("UE should not in OnGoing[%s]", procedure) } + var pduStatusResult *[psiArraySize]bool + if serviceRequest.PDUSessionStatus != nil { + pduStatusResult = getPDUSessionStatus(ue, anType) + } + // Send Authtication / Security Procedure not support if !ue.SecurityContextIsValid() { ue.GmmLog.Warnf("No Security Context : SUPI[%s]", ue.Supi) - gmm_message.SendServiceReject(ue.RanUe[anType], nil, nasMessage.Cause5GMMUEIdentityCannotBeDerivedByTheNetwork) + gmm_message.SendServiceReject(ue.RanUe[anType], pduStatusResult, + nasMessage.Cause5GMMUEIdentityCannotBeDerivedByTheNetwork) ngap_message.SendUEContextReleaseCommand(ue.RanUe[anType], context.UeContextN2NormalRelease, ngapType.CausePresentNas, ngapType.CauseNasPresentNormalRelease) return nil @@ -1703,26 +1730,34 @@ func HandleServiceRequest(ue *context.AmfUe, anType models.AccessType, } serviceType := serviceRequest.GetServiceTypeValue() - var reactivationResult, acceptPduSessionPsi *[16]bool + var reactivationResult *[psiArraySize]bool var errPduSessionId, errCause []uint8 - var targetPduSessionId int32 - suList := ngapType.PDUSessionResourceSetupListSUReq{} - ctxList := ngapType.PDUSessionResourceSetupListCxtReq{} + var dlPduSessionId int32 + cxtList := ngapType.PDUSessionResourceSetupListCxtReq{} if serviceType == nasMessage.ServiceTypeEmergencyServices || serviceType == nasMessage.ServiceTypeEmergencyServicesFallback { ue.GmmLog.Warnf("emergency service is not supported") + gmm_message.SendServiceReject(ue.RanUe[anType], pduStatusResult, nasMessage.Cause5GMM5GSServicesNotAllowed) + ngap_message.SendUEContextReleaseCommand(ue.RanUe[anType], + context.UeContextN2NormalRelease, ngapType.CausePresentNas, ngapType.CauseNasPresentNormalRelease) + return nil } if serviceType == nasMessage.ServiceTypeSignalling { - err := sendServiceAccept(ue, anType, ctxList, suList, nil, nil, nil, nil) + err := gmm_message.SendServiceAccept(ue, anType, cxtList, pduStatusResult, nil, nil, nil) return err } + + var N1N2ReqData *models.N1N2MessageTransferReqData + var n1Msg, n2Info []byte if ue.N1N2Message != nil { - requestData := ue.N1N2Message.Request.JsonData - if ue.N1N2Message.Request.BinaryDataN2Information != nil { - if requestData.N2InfoContainer.N2InformationClass == models.N2InformationClass_SM { - targetPduSessionId = requestData.N2InfoContainer.SmInfo.PduSessionId + N1N2ReqData = ue.N1N2Message.Request.JsonData + n1Msg = ue.N1N2Message.Request.BinaryDataN1Message + n2Info = ue.N1N2Message.Request.BinaryDataN2Information + if n2Info != nil { + if N1N2ReqData.N2InfoContainer.N2InformationClass == models.N2InformationClass_SM { + dlPduSessionId = N1N2ReqData.N2InfoContainer.SmInfo.PduSessionId } else { ue.N1N2Message = nil return fmt.Errorf("Service Request triggered by Network has not implemented about non SM N2Info") @@ -1732,96 +1767,35 @@ func HandleServiceRequest(ue *context.AmfUe, anType models.AccessType, if serviceRequest.UplinkDataStatus != nil { uplinkDataPsi := nasConvert.PSIToBooleanArray(serviceRequest.UplinkDataStatus.Buffer) - reactivationResult = new([16]bool) - ue.SmContextList.Range(func(key, value interface{}) bool { - pduSessionID := key.(int32) - smContext := value.(*context.SmContext) - - if pduSessionID == targetPduSessionId && serviceType == nasMessage.ServiceTypeMobileTerminatedServices { - // Skipping SendUpdateSmContextActivateUpCnxState for the following reason: - // In Step 4 of 4.2.3.2 UE Triggered Service Request in TS23.502 - // > This procedure is triggered by the SMF but the PDU Session(s) identified by the UE - // > correlates to other PDU Session ID(s) than the one triggering the procedure - // However, in the case of Mo-data etc., it cannot be skipped because AMF need to know - // latest N2SmInformation even if the UE has known the N2Information received at - // previous N1N2MessageTransfer. - return true - } - if uplinkDataPsi[pduSessionID] && smContext.AccessType() == models.AccessType__3_GPP_ACCESS { - response, errRes, _, err := consumer.SendUpdateSmContextActivateUpCnxState( - ue, smContext, models.AccessType__3_GPP_ACCESS) - if err != nil { - ue.GmmLog.Errorf("SendUpdateSmContextActivateUpCnxState[pduSessionID:%d] Error: %+v", - pduSessionID, err) - } else if response == nil { - reactivationResult[pduSessionID] = true - errPduSessionId = append(errPduSessionId, uint8(pduSessionID)) - cause := nasMessage.Cause5GMMProtocolErrorUnspecified - if errRes != nil { - switch errRes.JsonData.Error.Cause { - case "OUT_OF_LADN_SERVICE_AREA": - cause = nasMessage.Cause5GMMLADNNotAvailable - case "PRIORITIZED_SERVICES_ONLY": - cause = nasMessage.Cause5GMMRestrictedServiceArea - case "DNN_CONGESTION", "S-NSSAI_CONGESTION": - cause = nasMessage.Cause5GMMInsufficientUserPlaneResourcesForThePDUSession - } - } - errCause = append(errCause, cause) - } else if ue.RanUe[anType].UeContextRequest { - ngap_message.AppendPDUSessionResourceSetupListCxtReq(&ctxList, - pduSessionID, smContext.Snssai(), nil, response.BinaryDataN2SmInformation) - } else { - ngap_message.AppendPDUSessionResourceSetupListSUReq(&suList, - pduSessionID, smContext.Snssai(), nil, response.BinaryDataN2SmInformation) - } - } - return true - }) + if reactivationResult == nil { + reactivationResult = new([psiArraySize]bool) + } + errPduSessionId, errCause = reactivatePendingULDataPDUSession(ue, anType, serviceType, &uplinkDataPsi, + dlPduSessionId, &cxtList, reactivationResult, errPduSessionId, errCause) } + if serviceRequest.PDUSessionStatus != nil { - acceptPduSessionPsi = new([16]bool) - psiArray := nasConvert.PSIToBooleanArray(serviceRequest.PDUSessionStatus.Buffer) - ue.SmContextList.Range(func(key, value interface{}) bool { - pduSessionID := key.(int32) - smContext := value.(*context.SmContext) - if smContext.AccessType() == anType { - if !psiArray[pduSessionID] { - cause := models.Cause_PDU_SESSION_STATUS_MISMATCH - causeAll := &context.CauseAll{ - Cause: &cause, - } - problemDetail, err := consumer.SendReleaseSmContextRequest(ue, smContext, causeAll, "", nil) - if problemDetail != nil { - ue.GmmLog.Errorf("Release SmContext Failed Problem[%+v]", problemDetail) - } else if err != nil { - ue.GmmLog.Errorf("Release SmContext Error[%v]", err.Error()) - } - } else { - acceptPduSessionPsi[pduSessionID] = true - } - } - return true - }) + uePduStatus := nasConvert.PSIToBooleanArray(serviceRequest.PDUSessionStatus.Buffer) + if pduStatusResult == nil { + pduStatusResult = new([psiArraySize]bool) + } + releaseInactivePDUSession(ue, anType, &uePduStatus, pduStatusResult) } + switch serviceType { case nasMessage.ServiceTypeMobileTerminatedServices: // Trigger by Network if ue.N1N2Message != nil { - requestData := ue.N1N2Message.Request.JsonData - n1Msg := ue.N1N2Message.Request.BinaryDataN1Message - n2Info := ue.N1N2Message.Request.BinaryDataN2Information - - // downlink signalling + // downlink signalling only if n2Info == nil { - err := sendServiceAccept(ue, anType, ctxList, suList, acceptPduSessionPsi, + err := gmm_message.SendServiceAccept(ue, anType, cxtList, pduStatusResult, reactivationResult, errPduSessionId, errCause) if err != nil { return err } - switch requestData.N1MessageContainer.N1MessageClass { + switch N1N2ReqData.N1MessageContainer.N1MessageClass { case models.N1MessageClass_SM: gmm_message.SendDLNASTransport(ue.RanUe[anType], - nasMessage.PayloadContainerTypeN1SMInfo, n1Msg, requestData.PduSessionId, 0, nil, 0) + nasMessage.PayloadContainerTypeN1SMInfo, n1Msg, N1N2ReqData.PduSessionId, 0, nil, 0) case models.N1MessageClass_LPP: gmm_message.SendDLNASTransport(ue.RanUe[anType], nasMessage.PayloadContainerTypeLPP, n1Msg, 0, 0, nil, 0) @@ -1835,93 +1809,46 @@ func HandleServiceRequest(ue *context.AmfUe, anType models.AccessType, ue.N1N2Message = nil return nil } + // TODO: Area of validity for the N2 SM information - smInfo := requestData.N2InfoContainer.SmInfo - smContext, exist := ue.SmContextFindByPDUSessionID(requestData.PduSessionId) - if !exist { - ue.N1N2Message = nil - return fmt.Errorf("Service Request triggered by Network error for pduSessionId does not exist") + smInfo := N1N2ReqData.N2InfoContainer.SmInfo + smContext, ok := ue.SmContextFindByPDUSessionID(N1N2ReqData.PduSessionId) + if !ok { + return fmt.Errorf("Service Request triggered by Network error for pduSession[%d] does not exist", + N1N2ReqData.PduSessionId) } if smContext.AccessType() == models.AccessType_NON_3_GPP_ACCESS { if serviceRequest.AllowedPDUSessionStatus != nil { allowPduSessionPsi := nasConvert.PSIToBooleanArray(serviceRequest.AllowedPDUSessionStatus.Buffer) - if reactivationResult == nil { - reactivationResult = new([16]bool) - } - if allowPduSessionPsi[requestData.PduSessionId] { - response, errRes, _, err := consumer.SendUpdateSmContextChangeAccessType( - ue, smContext, true) - if err != nil { - return err - } else if response == nil { - reactivationResult[requestData.PduSessionId] = true - errPduSessionId = append(errPduSessionId, uint8(requestData.PduSessionId)) - cause := nasMessage.Cause5GMMProtocolErrorUnspecified - if errRes != nil { - switch errRes.JsonData.Error.Cause { - case "OUT_OF_LADN_SERVICE_AREA": - cause = nasMessage.Cause5GMMLADNNotAvailable - case "PRIORITIZED_SERVICES_ONLY": - cause = nasMessage.Cause5GMMRestrictedServiceArea - case "DNN_CONGESTION", "S-NSSAI_CONGESTION": - cause = nasMessage.Cause5GMMInsufficientUserPlaneResourcesForThePDUSession - } - } - errCause = append(errCause, cause) - } else { - smContext.SetUserLocation(deepcopy.Copy(ue.Location).(models.UserLocation)) - smContext.SetAccessType(models.AccessType__3_GPP_ACCESS) - if response.BinaryDataN2SmInformation != nil && - response.JsonData.N2SmInfoType == models.N2SmInfoType_PDU_RES_SETUP_REQ { - if ue.RanUe[anType].UeContextRequest { - ngap_message.AppendPDUSessionResourceSetupListCxtReq(&ctxList, - requestData.PduSessionId, smContext.Snssai(), nil, - response.BinaryDataN2SmInformation) - } else { - ngap_message.AppendPDUSessionResourceSetupListSUReq(&suList, - requestData.PduSessionId, smContext.Snssai(), nil, - response.BinaryDataN2SmInformation) - } - } - } - } else { - ue.GmmLog.Warnf("UE was reachable but did not accept to re-activate the PDU Session[%d]", - requestData.PduSessionId) - callback.SendN1N2TransferFailureNotification( - ue, models.N1N2MessageTransferCause_UE_NOT_REACHABLE_FOR_SESSION) - } + errPduSessionId, errCause = reestablishAllowedPDUSessionOver3GPP(ue, anType, smContext, + &allowPduSessionPsi, &cxtList, reactivationResult, errPduSessionId, errCause) } } else if smInfo.N2InfoContent.NgapIeType == models.NgapIeType_PDU_RES_SETUP_REQ { var nasPdu []byte var err error if n1Msg != nil { pduSessionId := uint8(smInfo.PduSessionId) - nasPdu, err = gmm_message.BuildDLNASTransport( - ue, anType, nasMessage.PayloadContainerTypeN1SMInfo, + nasPdu, err = gmm_message.BuildDLNASTransport(ue, anType, nasMessage.PayloadContainerTypeN1SMInfo, n1Msg, pduSessionId, nil, nil, 0) if err != nil { return err } } - if ue.RanUe[anType].UeContextRequest { - ngap_message.AppendPDUSessionResourceSetupListCxtReq(&ctxList, - smInfo.PduSessionId, *smInfo.SNssai, nasPdu, n2Info) - } else { - ngap_message.AppendPDUSessionResourceSetupListSUReq(&suList, - smInfo.PduSessionId, *smInfo.SNssai, nasPdu, n2Info) - } + ngap_message.AppendPDUSessionResourceSetupListCxtReq(&cxtList, smInfo.PduSessionId, *smInfo.SNssai, + nasPdu, n2Info) } - err := sendServiceAccept(ue, anType, ctxList, suList, acceptPduSessionPsi, + err := gmm_message.SendServiceAccept(ue, anType, cxtList, pduStatusResult, reactivationResult, errPduSessionId, errCause) if err != nil { return err } } + // downlink signaling if ue.ConfigurationUpdateMessage != nil { - err := sendServiceAccept(ue, anType, ctxList, suList, - acceptPduSessionPsi, reactivationResult, errPduSessionId, errCause) + err := gmm_message.SendServiceAccept(ue, anType, cxtList, + pduStatusResult, reactivationResult, errPduSessionId, errCause) if err != nil { return err } @@ -1946,17 +1873,11 @@ func HandleServiceRequest(ue *context.AmfUe, anType models.AccessType, return nil } } - err := sendServiceAccept(ue, anType, ctxList, suList, acceptPduSessionPsi, - reactivationResult, errPduSessionId, errCause) - if err != nil { - return err - } - } else { - err := sendServiceAccept(ue, anType, ctxList, suList, acceptPduSessionPsi, - reactivationResult, errPduSessionId, errCause) - if err != nil { - return err - } + } + err := gmm_message.SendServiceAccept(ue, anType, cxtList, pduStatusResult, + reactivationResult, errPduSessionId, errCause) + if err != nil { + return err } default: return fmt.Errorf("Service Type[%d] is not supported", serviceType) @@ -1968,48 +1889,13 @@ func HandleServiceRequest(ue *context.AmfUe, anType models.AccessType, return nil } -func sendServiceAccept(ue *context.AmfUe, anType models.AccessType, ctxList ngapType.PDUSessionResourceSetupListCxtReq, - suList ngapType.PDUSessionResourceSetupListSUReq, pDUSessionStatus *[16]bool, - reactivationResult *[16]bool, errPduSessionId, errCause []uint8, -) error { - if ue.RanUe[anType].UeContextRequest { - // update Kgnb/Kn3iwf - ue.UpdateSecurityContext(anType) - - nasPdu, err := gmm_message.BuildServiceAccept(ue, anType, pDUSessionStatus, reactivationResult, - errPduSessionId, errCause) - if err != nil { - return err - } - if len(ctxList.List) != 0 { - ngap_message.SendInitialContextSetupRequest(ue, anType, nasPdu, &ctxList, nil, nil, nil) - } else { - ngap_message.SendInitialContextSetupRequest(ue, anType, nasPdu, nil, nil, nil, nil) - } - } else if len(suList.List) != 0 { - nasPdu, err := gmm_message.BuildServiceAccept(ue, anType, pDUSessionStatus, reactivationResult, - errPduSessionId, errCause) - if err != nil { - return err - } - ngap_message.SendPDUSessionResourceSetupRequest(ue.RanUe[anType], nasPdu, suList) - } else { - gmm_message.SendServiceAccept(ue.RanUe[anType], anType, pDUSessionStatus, reactivationResult, - errPduSessionId, errCause) - } - return nil -} - // TS 24.501 5.4.1 func HandleAuthenticationResponse(ue *context.AmfUe, accessType models.AccessType, authenticationResponse *nasMessage.AuthenticationResponse, ) error { ue.GmmLog.Info("Handle Authentication Response") - if ue.T3560 != nil { - ue.T3560.Stop() - ue.T3560 = nil // clear the timer - } + ue.StopT3560() if ue.AuthenticationCtx == nil { return fmt.Errorf("Ue Authentication Context is nil") @@ -2039,8 +1925,7 @@ func HandleAuthenticationResponse(ue *context.AmfUe, accessType models.AccessTyp if hResStar != av5gAka.HxresStar { ue.GmmLog.Errorf("HRES* Validation Failure (received: %s, expected: %s)", hResStar, av5gAka.HxresStar) - if ue.IdentityTypeUsedForRegistration == nasMessage.MobileIdentity5GSType5gGuti && - ue.IdentityRequestSendTimes == 0 { + if ue.IdentityTypeUsedForRegistration == nasMessage.MobileIdentity5GSType5gGuti && ue.IdentityRequestSendTimes == 0 { ue.IdentityRequestSendTimes++ gmm_message.SendIdentityRequest(ue.RanUe[accessType], accessType, nasMessage.MobileIdentity5GSTypeSuci) return nil @@ -2049,7 +1934,7 @@ func HandleAuthenticationResponse(ue *context.AmfUe, accessType models.AccessTyp return GmmFSM.SendEvent(ue.State[accessType], AuthFailEvent, fsm.ArgsType{ ArgAmfUe: ue, ArgAccessType: accessType, - }) + }, logger.GmmLog) } } @@ -2072,10 +1957,9 @@ func HandleAuthenticationResponse(ue *context.AmfUe, accessType models.AccessTyp ArgAccessType: accessType, ArgEAPSuccess: false, ArgEAPMessage: "", - }) + }, logger.GmmLog) case models.AuthResult_FAILURE: - if ue.IdentityTypeUsedForRegistration == nasMessage.MobileIdentity5GSType5gGuti && - ue.IdentityRequestSendTimes == 0 { + if ue.IdentityTypeUsedForRegistration == nasMessage.MobileIdentity5GSType5gGuti && ue.IdentityRequestSendTimes == 0 { ue.IdentityRequestSendTimes++ gmm_message.SendIdentityRequest(ue.RanUe[accessType], accessType, nasMessage.MobileIdentity5GSTypeSuci) return nil @@ -2084,7 +1968,7 @@ func HandleAuthenticationResponse(ue *context.AmfUe, accessType models.AccessTyp return GmmFSM.SendEvent(ue.State[accessType], AuthFailEvent, fsm.ArgsType{ ArgAmfUe: ue, ArgAccessType: accessType, - }) + }, logger.GmmLog) } } case models.AuthType_EAP_AKA_PRIME: @@ -2109,10 +1993,9 @@ func HandleAuthenticationResponse(ue *context.AmfUe, accessType models.AccessTyp ArgAccessType: accessType, ArgEAPSuccess: true, ArgEAPMessage: response.EapPayload, - }) + }, logger.GmmLog) case models.AuthResult_FAILURE: - if ue.IdentityTypeUsedForRegistration == nasMessage.MobileIdentity5GSType5gGuti && - ue.IdentityRequestSendTimes == 0 { + if ue.IdentityTypeUsedForRegistration == nasMessage.MobileIdentity5GSType5gGuti && ue.IdentityRequestSendTimes == 0 { ue.IdentityRequestSendTimes++ gmm_message.SendAuthenticationResult(ue.RanUe[accessType], false, response.EapPayload) gmm_message.SendIdentityRequest(ue.RanUe[accessType], accessType, nasMessage.MobileIdentity5GSTypeSuci) @@ -2122,7 +2005,7 @@ func HandleAuthenticationResponse(ue *context.AmfUe, accessType models.AccessTyp return GmmFSM.SendEvent(ue.State[accessType], AuthFailEvent, fsm.ArgsType{ ArgAmfUe: ue, ArgAccessType: accessType, - }) + }, logger.GmmLog) } case models.AuthResult_ONGOING: ue.AuthenticationCtx.Var5gAuthData = response.EapPayload @@ -2150,10 +2033,7 @@ func HandleAuthenticationFailure(ue *context.AmfUe, anType models.AccessType, ) error { ue.GmmLog.Info("Handle Authentication Failure") - if ue.T3560 != nil { - ue.T3560.Stop() - ue.T3560 = nil // clear the timer - } + ue.StopT3560() cause5GMM := authenticationFailure.Cause5GMM.GetCauseValue() @@ -2162,11 +2042,27 @@ func HandleAuthenticationFailure(ue *context.AmfUe, anType models.AccessType, case nasMessage.Cause5GMMMACFailure: ue.GmmLog.Warnln("Authentication Failure Cause: Mac Failure") gmm_message.SendAuthenticationReject(ue.RanUe[anType], "") - return GmmFSM.SendEvent(ue.State[anType], AuthFailEvent, fsm.ArgsType{ArgAmfUe: ue, ArgAccessType: anType}) + return GmmFSM.SendEvent( + ue.State[anType], + AuthFailEvent, + fsm.ArgsType{ + ArgAmfUe: ue, + ArgAccessType: anType, + }, + logger.GmmLog, + ) case nasMessage.Cause5GMMNon5GAuthenticationUnacceptable: ue.GmmLog.Warnln("Authentication Failure Cause: Non-5G Authentication Unacceptable") gmm_message.SendAuthenticationReject(ue.RanUe[anType], "") - return GmmFSM.SendEvent(ue.State[anType], AuthFailEvent, fsm.ArgsType{ArgAmfUe: ue, ArgAccessType: anType}) + return GmmFSM.SendEvent( + ue.State[anType], + AuthFailEvent, + fsm.ArgsType{ + ArgAmfUe: ue, + ArgAccessType: anType, + }, + logger.GmmLog, + ) case nasMessage.Cause5GMMngKSIAlreadyInUse: ue.GmmLog.Warnln("Authentication Failure Cause: NgKSI Already In Use") ue.AuthFailureCauseSynchFailureTimes = 0 @@ -2185,8 +2081,21 @@ func HandleAuthenticationFailure(ue *context.AmfUe, anType models.AccessType, if ue.AuthFailureCauseSynchFailureTimes >= 2 { ue.GmmLog.Warnf("2 consecutive Synch Failure, terminate authentication procedure") gmm_message.SendAuthenticationReject(ue.RanUe[anType], "") - return GmmFSM.SendEvent(ue.State[anType], AuthFailEvent, - fsm.ArgsType{ArgAmfUe: ue, ArgAccessType: anType}) + return GmmFSM.SendEvent( + ue.State[anType], + AuthFailEvent, + fsm.ArgsType{ + ArgAmfUe: ue, + ArgAccessType: anType, + }, + logger.GmmLog, + ) + } + + var av5gAka models.Av5gAka + if err := mapstructure.Decode(ue.AuthenticationCtx.Var5gAuthData, &av5gAka); err != nil { + ue.GmmLog.Error("Var5gAuthData Convert Type Error") + return err } if authenticationFailure.AuthenticationFailureParameter == nil { @@ -2195,17 +2104,10 @@ func HandleAuthenticationFailure(ue *context.AmfUe, anType models.AccessType, auts := authenticationFailure.AuthenticationFailureParameter.GetAuthenticationFailureParameter() resynchronizationInfo := &models.ResynchronizationInfo{ Auts: hex.EncodeToString(auts[:]), + Rand: av5gAka.Rand, } - var av5gAka models.Av5gAka - if err := mapstructure.Decode(ue.AuthenticationCtx.Var5gAuthData, &av5gAka); err != nil { - ue.GmmLog.Error("Var5gAuthData Convert Type Error") - return err - } - resynchronizationInfo.Rand = av5gAka.Rand - - response, problemDetails, err := consumer.SendUEAuthenticationAuthenticateRequest( - ue, resynchronizationInfo) + response, problemDetails, err := consumer.SendUEAuthenticationAuthenticateRequest(ue, resynchronizationInfo) if err != nil { return err } else if problemDetails != nil { @@ -2238,9 +2140,23 @@ func HandleRegistrationComplete(ue *context.AmfUe, accessType models.AccessType, ) error { ue.GmmLog.Info("Handle Registration Complete") - if ue.T3550 != nil { - ue.T3550.Stop() - ue.T3550 = nil // clear the timer + ue.StopT3550() + + // Release existed old SmContext when Initial Registration completed + if ue.RegistrationType5GS == nasMessage.RegistrationType5GSInitialRegistration { + ue.SmContextList.Range(func(key, value interface{}) bool { + smContext := value.(*context.SmContext) + + if smContext.AccessType() == accessType { + problemDetail, err := consumer.SendReleaseSmContextRequest(ue, smContext, nil, "", nil) + if problemDetail != nil { + ue.GmmLog.Errorf("Release SmContext Failed Problem[%+v]", problemDetail) + } else if err != nil { + ue.GmmLog.Errorf("Release SmContext Error[%v]", err.Error()) + } + } + return true + }) } // if registrationComplete.SORTransparentContainer != nil { @@ -2261,7 +2177,7 @@ func HandleRegistrationComplete(ue *context.AmfUe, accessType models.AccessType, return GmmFSM.SendEvent(ue.State[accessType], ContextSetupSuccessEvent, fsm.ArgsType{ ArgAmfUe: ue, ArgAccessType: accessType, - }) + }, logger.GmmLog) } // TS 33.501 6.7.2 @@ -2274,10 +2190,7 @@ func HandleSecurityModeComplete(ue *context.AmfUe, anType models.AccessType, pro return fmt.Errorf("NAS message integrity check failed") } - if ue.T3560 != nil { - ue.T3560.Stop() - ue.T3560 = nil // clear the timer - } + ue.StopT3560() if ue.SecurityContextIsValid() { // update Kgnb/Kn3iwf @@ -2310,8 +2223,7 @@ func HandleSecurityModeComplete(ue *context.AmfUe, anType models.AccessType, pro case nas.MsgTypeServiceRequest: argsType[ArgNASMessage] = m.GmmMessage.ServiceRequest if !ue.State[anType].Is(context.Registered) { - gmm_message.SendServiceReject(ue.RanUe[anType], nil, - nasMessage.Cause5GMMUEIdentityCannotBeDerivedByTheNetwork) + gmm_message.SendServiceReject(ue.RanUe[anType], nil, nasMessage.Cause5GMMUEIdentityCannotBeDerivedByTheNetwork) ue.GmmLog.Warnf("Service Request was sent when UE state was not Registered") ngap_message.SendUEContextReleaseCommand(ue.RanUe[anType], context.UeContextN2NormalRelease, ngapType.CausePresentNas, ngapType.CauseNasPresentNormalRelease) @@ -2321,14 +2233,14 @@ func HandleSecurityModeComplete(ue *context.AmfUe, anType models.AccessType, pro ue.GmmLog.Errorln("nas message container Iei type error") return errors.New("nas message container Iei type error") } - return GmmFSM.SendEvent(ue.State[anType], event, argsType) + return GmmFSM.SendEvent(ue.State[anType], event, argsType, logger.GmmLog) } return GmmFSM.SendEvent(ue.State[anType], SecurityModeSuccessEvent, fsm.ArgsType{ ArgAmfUe: ue, ArgAccessType: anType, ArgProcedureCode: procedureCode, ArgNASMessage: ue.RegistrationRequest, - }) + }, logger.GmmLog) } func HandleSecurityModeReject(ue *context.AmfUe, anType models.AccessType, @@ -2336,10 +2248,7 @@ func HandleSecurityModeReject(ue *context.AmfUe, anType models.AccessType, ) error { ue.GmmLog.Info("Handle Security Mode Reject") - if ue.T3560 != nil { - ue.T3560.Stop() - ue.T3560 = nil // clear the timer - } + ue.StopT3560() cause := securityModeReject.Cause5GMM.GetCauseValue() ue.GmmLog.Warnf("Reject Cause: %s", nasMessage.Cause5GMMToString(cause)) @@ -2388,6 +2297,8 @@ func HandleDeregistrationRequest(ue *context.AmfUe, anType models.AccessType, } } + gmm_common.PurgeAmfUeSubscriberData(ue) + // if Deregistration type is not switch-off, send Deregistration Accept if deregistrationRequest.GetSwitchOff() == 0 { gmm_message.SendDeregistrationAccept(ue.RanUe[anType]) @@ -2403,7 +2314,7 @@ func HandleDeregistrationRequest(ue *context.AmfUe, anType models.AccessType, return GmmFSM.SendEvent(ue.State[models.AccessType__3_GPP_ACCESS], DeregistrationAcceptEvent, fsm.ArgsType{ ArgAmfUe: ue, ArgAccessType: anType, - }) + }, logger.GmmLog) case nasMessage.AccessTypeNon3GPP: if ue.RanUe[models.AccessType_NON_3_GPP_ACCESS] != nil { ngap_message.SendUEContextReleaseCommand(ue.RanUe[models.AccessType_NON_3_GPP_ACCESS], @@ -2412,7 +2323,7 @@ func HandleDeregistrationRequest(ue *context.AmfUe, anType models.AccessType, return GmmFSM.SendEvent(ue.State[models.AccessType_NON_3_GPP_ACCESS], DeregistrationAcceptEvent, fsm.ArgsType{ ArgAmfUe: ue, ArgAccessType: anType, - }) + }, logger.GmmLog) case nasMessage.AccessTypeBoth: if ue.RanUe[models.AccessType__3_GPP_ACCESS] != nil { ngap_message.SendUEContextReleaseCommand(ue.RanUe[models.AccessType__3_GPP_ACCESS], @@ -2426,14 +2337,14 @@ func HandleDeregistrationRequest(ue *context.AmfUe, anType models.AccessType, err := GmmFSM.SendEvent(ue.State[models.AccessType__3_GPP_ACCESS], DeregistrationAcceptEvent, fsm.ArgsType{ ArgAmfUe: ue, ArgAccessType: anType, - }) + }, logger.GmmLog) if err != nil { ue.GmmLog.Errorln(err) } return GmmFSM.SendEvent(ue.State[models.AccessType_NON_3_GPP_ACCESS], DeregistrationAcceptEvent, fsm.ArgsType{ ArgAmfUe: ue, ArgAccessType: anType, - }) + }, logger.GmmLog) } return nil @@ -2445,10 +2356,7 @@ func HandleDeregistrationAccept(ue *context.AmfUe, anType models.AccessType, ) error { ue.GmmLog.Info("Handle Deregistration Accept(UE Terminated)") - if ue.T3522 != nil { - ue.T3522.Stop() - ue.T3522 = nil // clear the timer - } + ue.StopT3522() switch ue.DeregistrationTargetAccessType { case nasMessage.AccessType3GPP: diff --git a/internal/gmm/init_test.go b/internal/gmm/init_test.go index cfafc292..7d8f835a 100644 --- a/internal/gmm/init_test.go +++ b/internal/gmm/init_test.go @@ -1,15 +1,14 @@ -package gmm_test +package gmm import ( "fmt" "testing" - "github.com/free5gc/amf/internal/gmm" "github.com/free5gc/util/fsm" ) func TestGmmFSM(t *testing.T) { - if err := fsm.ExportDot(gmm.GmmFSM, "gmm"); err != nil { + if err := fsm.ExportDot(GmmFSM, "gmm"); err != nil { fmt.Printf("fsm export data return error: %+v", err) } } diff --git a/internal/gmm/message/build.go b/internal/gmm/message/build.go index cff1db6c..6fef850c 100644 --- a/internal/gmm/message/build.go +++ b/internal/gmm/message/build.go @@ -73,8 +73,7 @@ func BuildNotification(ue *context.AmfUe, accessType models.AccessType) ([]byte, notification := nasMessage.NewNotification(0) notification.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) - notification.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( - nasMessage.Epd5GSMobilityManagementMessage) + notification.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator(nasMessage.Epd5GSMobilityManagementMessage) notification.SetMessageType(nas.MsgTypeNotification) if accessType == models.AccessType__3_GPP_ACCESS { notification.SetAccessType(nasMessage.AccessType3GPP) @@ -139,8 +138,8 @@ func BuildAuthenticationRequest(ue *context.AmfUe) ([]byte, error) { if err != nil { return nil, err } - authenticationRequest.AuthenticationParameterRAND = nasType.NewAuthenticationParameterRAND( - nasMessage.AuthenticationRequestAuthenticationParameterRANDType) + authenticationRequest.AuthenticationParameterRAND = nasType. + NewAuthenticationParameterRAND(nasMessage.AuthenticationRequestAuthenticationParameterRANDType) copy(tmpArray[:], rand[0:16]) authenticationRequest.AuthenticationParameterRAND.SetRANDValue(tmpArray) @@ -148,8 +147,8 @@ func BuildAuthenticationRequest(ue *context.AmfUe) ([]byte, error) { if err != nil { return nil, err } - authenticationRequest.AuthenticationParameterAUTN = nasType.NewAuthenticationParameterAUTN( - nasMessage.AuthenticationRequestAuthenticationParameterAUTNType) + authenticationRequest.AuthenticationParameterAUTN = nasType. + NewAuthenticationParameterAUTN(nasMessage.AuthenticationRequestAuthenticationParameterAUTNType) authenticationRequest.AuthenticationParameterAUTN.SetLen(uint8(len(autn))) copy(tmpArray[:], autn[0:16]) authenticationRequest.AuthenticationParameterAUTN.SetAUTN(tmpArray) @@ -301,10 +300,14 @@ func BuildRegistrationReject(ue *context.AmfUe, cause5GMM uint8, eapMessage stri registrationReject.RegistrationRejectMessageIdentity.SetMessageType(nas.MsgTypeRegistrationReject) registrationReject.Cause5GMM.SetCauseValue(cause5GMM) - if ue.T3502Value != 0 { + t3502Val := context.GetSelf().T3502Value + if ue != nil { + t3502Val = ue.T3502Value + } + if t3502Val != 0 { registrationReject.T3502Value = nasType.NewT3502Value(nasMessage.RegistrationRejectT3502ValueType) registrationReject.T3502Value.SetLen(1) - t3502 := nasConvert.GPRSTimer2ToNas(ue.T3502Value) + t3502 := nasConvert.GPRSTimer2ToNas(t3502Val) registrationReject.T3502Value.SetGPRSTimer2Value(t3502) } @@ -358,8 +361,8 @@ func BuildSecurityModeCommand(ue *context.AmfUe, accessType models.AccessType, e securityModeCommand.IMEISVRequest.SetIMEISVRequestValue(nasMessage.IMEISVRequested) } - securityModeCommand.Additional5GSecurityInformation = nasType.NewAdditional5GSecurityInformation( - nasMessage.SecurityModeCommandAdditional5GSecurityInformationType) + securityModeCommand.Additional5GSecurityInformation = nasType. + NewAdditional5GSecurityInformation(nasMessage.SecurityModeCommandAdditional5GSecurityInformationType) securityModeCommand.Additional5GSecurityInformation.SetLen(1) if ue.RetransmissionOfInitialNASMsg { securityModeCommand.Additional5GSecurityInformation.SetRINMR(1) @@ -509,10 +512,9 @@ func BuildRegistrationAccept( registrationAccept.GUTI5G.SetIei(nasMessage.RegistrationAcceptGUTI5GType) } - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if len(amfSelf.PlmnSupportList) > 1 { - registrationAccept.EquivalentPlmns = nasType.NewEquivalentPlmns( - nasMessage.RegistrationAcceptEquivalentPlmnsType) + registrationAccept.EquivalentPlmns = nasType.NewEquivalentPlmns(nasMessage.RegistrationAcceptEquivalentPlmnsType) var buf []uint8 for _, plmnSupportItem := range amfSelf.PlmnSupportList { buf = append(buf, nasConvert.PlmnIDToNas(*plmnSupportItem.PlmnId)...) @@ -548,8 +550,7 @@ func BuildRegistrationAccept( } if includeConfiguredNssaiCheck(ue) { - registrationAccept.ConfiguredNSSAI = nasType.NewConfiguredNSSAI( - nasMessage.RegistrationAcceptConfiguredNSSAIType) + registrationAccept.ConfiguredNSSAI = nasType.NewConfiguredNSSAI(nasMessage.RegistrationAcceptConfiguredNSSAIType) var buf []uint8 for _, snssai := range ue.ConfiguredNssai { buf = append(buf, nasConvert.SnssaiToNas(*snssai.ConfiguredSnssai)...) @@ -559,33 +560,32 @@ func BuildRegistrationAccept( } // 5gs network feature support - if factory.AmfConfig.Configuration.Get5gsNwFeatSuppEnable() { - registrationAccept.NetworkFeatureSupport5GS = nasType.NewNetworkFeatureSupport5GS( - nasMessage.RegistrationAcceptNetworkFeatureSupport5GSType) - registrationAccept.NetworkFeatureSupport5GS.SetLen(2) + if c := factory.AmfConfig.GetNasIENetworkFeatureSupport5GS(); c != nil && c.Enable { + registrationAccept.NetworkFeatureSupport5GS = nasType. + NewNetworkFeatureSupport5GS(nasMessage.RegistrationAcceptNetworkFeatureSupport5GSType) + registrationAccept.NetworkFeatureSupport5GS.SetLen(c.Length) if anType == models.AccessType__3_GPP_ACCESS { - registrationAccept.SetIMSVoPS3GPP(factory.AmfConfig.Configuration.Get5gsNwFeatSuppImsVoPS()) + registrationAccept.SetIMSVoPS3GPP(c.ImsVoPS) } else { - registrationAccept.SetIMSVoPSN3GPP(factory.AmfConfig.Configuration.Get5gsNwFeatSuppImsVoPS()) + registrationAccept.SetIMSVoPSN3GPP(c.ImsVoPS) } - registrationAccept.SetEMC(factory.AmfConfig.Configuration.Get5gsNwFeatSuppEmc()) - registrationAccept.SetEMF(factory.AmfConfig.Configuration.Get5gsNwFeatSuppEmf()) - registrationAccept.SetIWKN26(factory.AmfConfig.Configuration.Get5gsNwFeatSuppIwkN26()) - registrationAccept.SetMPSI(factory.AmfConfig.Configuration.Get5gsNwFeatSuppMpsi()) - registrationAccept.SetEMCN(factory.AmfConfig.Configuration.Get5gsNwFeatSuppEmcN3()) - registrationAccept.SetMCSI(factory.AmfConfig.Configuration.Get5gsNwFeatSuppMcsi()) + registrationAccept.SetEMC(c.Emc) + registrationAccept.SetEMF(c.Emf) + registrationAccept.SetIWKN26(c.IwkN26) + registrationAccept.SetMPSI(c.Mpsi) + registrationAccept.SetEMCN(c.EmcN3) + registrationAccept.SetMCSI(c.Mcsi) } if pDUSessionStatus != nil { - registrationAccept.PDUSessionStatus = nasType.NewPDUSessionStatus( - nasMessage.RegistrationAcceptPDUSessionStatusType) + registrationAccept.PDUSessionStatus = nasType.NewPDUSessionStatus(nasMessage.RegistrationAcceptPDUSessionStatusType) registrationAccept.PDUSessionStatus.SetLen(2) registrationAccept.PDUSessionStatus.Buffer = nasConvert.PSIToBuf(*pDUSessionStatus) } if reactivationResult != nil { - registrationAccept.PDUSessionReactivationResult = nasType.NewPDUSessionReactivationResult( - nasMessage.RegistrationAcceptPDUSessionReactivationResultType) + registrationAccept.PDUSessionReactivationResult = nasType. + NewPDUSessionReactivationResult(nasMessage.RegistrationAcceptPDUSessionReactivationResultType) registrationAccept.PDUSessionReactivationResult.SetLen(2) registrationAccept.PDUSessionReactivationResult.Buffer = nasConvert.PSIToBuf(*reactivationResult) } @@ -599,11 +599,10 @@ func BuildRegistrationAccept( } if ue.LadnInfo != nil { - registrationAccept.LADNInformation = nasType.NewLADNInformation( - nasMessage.RegistrationAcceptLADNInformationType) + registrationAccept.LADNInformation = nasType.NewLADNInformation(nasMessage.RegistrationAcceptLADNInformationType) buf := make([]uint8, 0) for _, ladn := range ue.LadnInfo { - ladnNas := nasConvert.LadnToNas(ladn.Dnn, ladn.TaiLists) + ladnNas := nasConvert.LadnToNas(ladn.Dnn, ladn.TaiList) buf = append(buf, ladnNas...) } registrationAccept.LADNInformation.SetLen(uint16(len(buf))) @@ -611,8 +610,8 @@ func BuildRegistrationAccept( } if ue.NetworkSlicingSubscriptionChanged { - registrationAccept.NetworkSlicingIndication = nasType.NewNetworkSlicingIndication( - nasMessage.RegistrationAcceptNetworkSlicingIndicationType) + registrationAccept.NetworkSlicingIndication = nasType. + NewNetworkSlicingIndication(nasMessage.RegistrationAcceptNetworkSlicingIndicationType) registrationAccept.NetworkSlicingIndication.SetNSSCI(1) registrationAccept.NetworkSlicingIndication.SetDCNI(0) ue.NetworkSlicingSubscriptionChanged = false // reset the value @@ -620,10 +619,8 @@ func BuildRegistrationAccept( if anType == models.AccessType__3_GPP_ACCESS && ue.AmPolicyAssociation != nil && ue.AmPolicyAssociation.ServAreaRes != nil { - registrationAccept.ServiceAreaList = nasType.NewServiceAreaList( - nasMessage.RegistrationAcceptServiceAreaListType) - partialServiceAreaList := nasConvert.PartialServiceAreaListToNas( - ue.PlmnId, *ue.AmPolicyAssociation.ServAreaRes) + registrationAccept.ServiceAreaList = nasType.NewServiceAreaList(nasMessage.RegistrationAcceptServiceAreaListType) + partialServiceAreaList := nasConvert.PartialServiceAreaListToNas(ue.PlmnId, *ue.AmPolicyAssociation.ServAreaRes) registrationAccept.ServiceAreaList.SetLen(uint8(len(partialServiceAreaList))) registrationAccept.ServiceAreaList.SetPartialServiceAreaList(partialServiceAreaList) } @@ -636,10 +633,10 @@ func BuildRegistrationAccept( } if anType == models.AccessType_NON_3_GPP_ACCESS { - registrationAccept.Non3GppDeregistrationTimerValue = nasType.NewNon3GppDeregistrationTimerValue( - nasMessage.RegistrationAcceptNon3GppDeregistrationTimerValueType) + registrationAccept.Non3GppDeregistrationTimerValue = nasType. + NewNon3GppDeregistrationTimerValue(nasMessage.RegistrationAcceptNon3GppDeregistrationTimerValueType) registrationAccept.Non3GppDeregistrationTimerValue.SetLen(1) - timerValue := nasConvert.GPRSTimer2ToNas(ue.Non3gppDeregistrationTimerValue) + timerValue := nasConvert.GPRSTimer2ToNas(ue.Non3gppDeregTimerValue) registrationAccept.Non3GppDeregistrationTimerValue.SetGPRSTimer2Value(timerValue) } @@ -651,8 +648,8 @@ func BuildRegistrationAccept( } if ue.UESpecificDRX != nasMessage.DRXValueNotSpecified { - registrationAccept.NegotiatedDRXParameters = nasType.NewNegotiatedDRXParameters( - nasMessage.RegistrationAcceptNegotiatedDRXParametersType) + registrationAccept.NegotiatedDRXParameters = nasType. + NewNegotiatedDRXParameters(nasMessage.RegistrationAcceptNegotiatedDRXParametersType) registrationAccept.NegotiatedDRXParameters.SetLen(1) registrationAccept.NegotiatedDRXParameters.SetDRXValue(ue.UESpecificDRX) } @@ -674,8 +671,7 @@ func includeConfiguredNssaiCheck(ue *context.AmfUe) bool { if ue.NetworkSliceInfo != nil && len(ue.NetworkSliceInfo.RejectedNssaiInPlmn) != 0 { return true } - if registrationRequest.NetworkSlicingIndication != nil && - registrationRequest.NetworkSlicingIndication.GetDCNI() == 1 { + if registrationRequest.NetworkSlicingIndication != nil && registrationRequest.NetworkSlicingIndication.GetDCNI() == 1 { return true } return false @@ -706,20 +702,19 @@ func BuildConfigurationUpdateCommand(ue *context.AmfUe, anType models.AccessType configurationUpdateCommand := nasMessage.NewConfigurationUpdateCommand(0) configurationUpdateCommand.SetExtendedProtocolDiscriminator(nasMessage.Epd5GSMobilityManagementMessage) - configurationUpdateCommand.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType( - nas.SecurityHeaderTypePlainNas) + configurationUpdateCommand.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) configurationUpdateCommand.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) configurationUpdateCommand.SetMessageType(nas.MsgTypeConfigurationUpdateCommand) if ue.ConfigurationUpdateIndication.Octet != 0 { - configurationUpdateCommand.ConfigurationUpdateIndication = nasType.NewConfigurationUpdateIndication( - nasMessage.ConfigurationUpdateCommandConfigurationUpdateIndicationType) + configurationUpdateCommand.ConfigurationUpdateIndication = nasType. + NewConfigurationUpdateIndication(nasMessage.ConfigurationUpdateCommandConfigurationUpdateIndicationType) configurationUpdateCommand.ConfigurationUpdateIndication = &ue.ConfigurationUpdateIndication } if networkSlicingIndication != nil { - configurationUpdateCommand.NetworkSlicingIndication = nasType.NewNetworkSlicingIndication( - nasMessage.ConfigurationUpdateCommandNetworkSlicingIndicationType) + configurationUpdateCommand.NetworkSlicingIndication = nasType. + NewNetworkSlicingIndication(nasMessage.ConfigurationUpdateCommandNetworkSlicingIndicationType) configurationUpdateCommand.NetworkSlicingIndication = networkSlicingIndication } @@ -740,8 +735,8 @@ func BuildConfigurationUpdateCommand(ue *context.AmfUe, anType models.AccessType } if len(ue.AllowedNssai[anType]) > 0 { - configurationUpdateCommand.AllowedNSSAI = nasType.NewAllowedNSSAI( - nasMessage.ConfigurationUpdateCommandAllowedNSSAIType) + configurationUpdateCommand.AllowedNSSAI = nasType. + NewAllowedNSSAI(nasMessage.ConfigurationUpdateCommandAllowedNSSAIType) var buf []uint8 for _, allowedSnssai := range ue.AllowedNssai[anType] { buf = append(buf, nasConvert.SnssaiToNas(*allowedSnssai.AllowedSnssai)...) @@ -751,8 +746,8 @@ func BuildConfigurationUpdateCommand(ue *context.AmfUe, anType models.AccessType } if len(ue.ConfiguredNssai) > 0 { - configurationUpdateCommand.ConfiguredNSSAI = nasType.NewConfiguredNSSAI( - nasMessage.ConfigurationUpdateCommandConfiguredNSSAIType) + configurationUpdateCommand.ConfiguredNSSAI = nasType. + NewConfiguredNSSAI(nasMessage.ConfigurationUpdateCommandConfiguredNSSAIType) var buf []uint8 for _, snssai := range ue.ConfiguredNssai { buf = append(buf, nasConvert.SnssaiToNas(*snssai.ConfiguredSnssai)...) @@ -773,51 +768,49 @@ func BuildConfigurationUpdateCommand(ue *context.AmfUe, anType models.AccessType // TODO: UniversalTimeAndLocalTimeZone 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 = nasType. + NewServiceAreaList(nasMessage.ConfigurationUpdateCommandServiceAreaListType) + partialServiceAreaList := nasConvert. + PartialServiceAreaListToNas(ue.PlmnId, *ue.AmPolicyAssociation.ServAreaRes) configurationUpdateCommand.ServiceAreaList.SetLen(uint8(len(partialServiceAreaList))) configurationUpdateCommand.ServiceAreaList.SetPartialServiceAreaList(partialServiceAreaList) } - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if amfSelf.NetworkName.Full != "" { fullNetworkName := nasConvert.FullNetworkNameToNas(amfSelf.NetworkName.Full) configurationUpdateCommand.FullNameForNetwork = &fullNetworkName - configurationUpdateCommand.FullNameForNetwork.SetIei( - nasMessage.ConfigurationUpdateCommandFullNameForNetworkType) + configurationUpdateCommand.FullNameForNetwork.SetIei(nasMessage.ConfigurationUpdateCommandFullNameForNetworkType) } if amfSelf.NetworkName.Short != "" { shortNetworkName := nasConvert.ShortNetworkNameToNas(amfSelf.NetworkName.Short) configurationUpdateCommand.ShortNameForNetwork = &shortNetworkName - configurationUpdateCommand.ShortNameForNetwork.SetIei( - nasMessage.ConfigurationUpdateCommandShortNameForNetworkType) + configurationUpdateCommand.ShortNameForNetwork.SetIei(nasMessage.ConfigurationUpdateCommandShortNameForNetworkType) } if ue.TimeZone != "" { localTimeZone := nasConvert.LocalTimeZoneToNas(ue.TimeZone) localTimeZone.SetIei(nasMessage.ConfigurationUpdateCommandLocalTimeZoneType) - configurationUpdateCommand.LocalTimeZone = nasType.NewLocalTimeZone( - nasMessage.ConfigurationUpdateCommandLocalTimeZoneType) + configurationUpdateCommand.LocalTimeZone = nasType. + NewLocalTimeZone(nasMessage.ConfigurationUpdateCommandLocalTimeZoneType) configurationUpdateCommand.LocalTimeZone = &localTimeZone } if ue.TimeZone != "" { daylightSavingTime := nasConvert.DaylightSavingTimeToNas(ue.TimeZone) daylightSavingTime.SetIei(nasMessage.ConfigurationUpdateCommandNetworkDaylightSavingTimeType) - configurationUpdateCommand.NetworkDaylightSavingTime = nasType.NewNetworkDaylightSavingTime( - nasMessage.ConfigurationUpdateCommandNetworkDaylightSavingTimeType) + configurationUpdateCommand.NetworkDaylightSavingTime = nasType. + NewNetworkDaylightSavingTime(nasMessage.ConfigurationUpdateCommandNetworkDaylightSavingTimeType) configurationUpdateCommand.NetworkDaylightSavingTime = &daylightSavingTime } if len(ue.LadnInfo) > 0 { - configurationUpdateCommand.LADNInformation = nasType.NewLADNInformation( - nasMessage.ConfigurationUpdateCommandLADNInformationType) + configurationUpdateCommand.LADNInformation = nasType. + NewLADNInformation(nasMessage.ConfigurationUpdateCommandLADNInformationType) var buf []uint8 for _, ladn := range ue.LadnInfo { - ladnNas := nasConvert.LadnToNas(ladn.Dnn, ladn.TaiLists) + ladnNas := nasConvert.LadnToNas(ladn.Dnn, ladn.TaiList) buf = append(buf, ladnNas...) } configurationUpdateCommand.LADNInformation.SetLen(uint16(len(buf))) diff --git a/internal/gmm/message/send.go b/internal/gmm/message/send.go index 30670380..a42e44d1 100644 --- a/internal/gmm/message/send.go +++ b/internal/gmm/message/send.go @@ -1,6 +1,8 @@ package message import ( + "fmt" + "github.com/free5gc/amf/internal/context" gmm_common "github.com/free5gc/amf/internal/gmm/common" "github.com/free5gc/amf/internal/logger" @@ -52,17 +54,18 @@ func SendNotification(ue *context.RanUe, nasMsg []byte) { amfUe := ue.AmfUe amfUe.GmmLog.Info("Send Notification") - if context.AMF_Self().T3565Cfg.Enable { - cfg := context.AMF_Self().T3565Cfg + if context.GetSelf().T3565Cfg.Enable { + cfg := context.GetSelf().T3565Cfg + amfUe.GmmLog.Infof("Start T3565 timer") amfUe.T3565 = context.NewTimer(cfg.ExpireTime, cfg.MaxRetryTimes, func(expireTimes int32) { amfUe.GmmLog.Warnf("T3565 expires, retransmit Notification (retry: %d)", expireTimes) ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) }, func() { amfUe.GmmLog.Warnf("T3565 Expires %d times, abort notification procedure", cfg.MaxRetryTimes) + amfUe.T3565 = nil // clear the timer if amfUe.OnGoing(models.AccessType__3_GPP_ACCESS).Procedure != context.OnGoingProcedureN2Handover { callback.SendN1N2TransferFailureNotification(amfUe, models.N1N2MessageTransferCause_UE_NOT_RESPONDING) } - amfUe.T3565 = nil // clear the timer }) } } @@ -88,15 +91,15 @@ func SendIdentityRequest(ue *context.RanUe, accessType models.AccessType, typeOf amfUe.RequestIdentityType = typeOfIdentity - if context.AMF_Self().T3570Cfg.Enable { - cfg := context.AMF_Self().T3570Cfg + if context.GetSelf().T3570Cfg.Enable { + cfg := context.GetSelf().T3570Cfg amfUe.T3570 = context.NewTimer(cfg.ExpireTime, cfg.MaxRetryTimes, func(expireTimes int32) { amfUe.GmmLog.Warnf("T3570 expires, retransmit Identity Request (retry: %d)", expireTimes) ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) }, func() { amfUe.GmmLog.Warnf("T3570 Expires %d times, abort identification procedure & ongoing 5GMM procedure", cfg.MaxRetryTimes) - gmm_common.RemoveAmfUe(amfUe) + gmm_common.RemoveAmfUe(amfUe, false) }) } } @@ -125,40 +128,50 @@ func SendAuthenticationRequest(ue *context.RanUe) { } ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) - if context.AMF_Self().T3560Cfg.Enable { - cfg := context.AMF_Self().T3560Cfg + if context.GetSelf().T3560Cfg.Enable { + cfg := context.GetSelf().T3560Cfg + amfUe.GmmLog.Infof("Start T3560 timer") amfUe.T3560 = context.NewTimer(cfg.ExpireTime, cfg.MaxRetryTimes, func(expireTimes int32) { amfUe.GmmLog.Warnf("T3560 expires, retransmit Authentication Request (retry: %d)", expireTimes) ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) }, func() { + amfUe.Lock.Lock() + defer amfUe.Lock.Unlock() + amfUe.GmmLog.Warnf("T3560 Expires %d times, abort authentication procedure & ongoing 5GMM procedure", cfg.MaxRetryTimes) - gmm_common.RemoveAmfUe(amfUe) + amfUe.T3560 = nil + gmm_common.RemoveAmfUe(amfUe, false) }) } } -func SendServiceAccept(ue *context.RanUe, anType models.AccessType, pDUSessionStatus *[16]bool, +func SendServiceAccept(amfUe *context.AmfUe, anType models.AccessType, + cxtList ngapType.PDUSessionResourceSetupListCxtReq, pDUSessionStatus *[16]bool, reactivationResult *[16]bool, errPduSessionId, errCause []uint8, -) { - if ue == nil { - logger.GmmLog.Error("SendServiceAccept: RanUe is nil") - return +) error { + if amfUe == nil { + return fmt.Errorf("SendServiceAccept: AmfUe is nil") } - if ue.AmfUe == nil { - logger.GmmLog.Error("SendServiceAccept: AmfUe is nil") - return + if amfUe.RanUe[anType] == nil { + return fmt.Errorf("SendServiceAccept: RanUe is nil") } - amfUe := ue.AmfUe amfUe.GmmLog.Info("Send Service Accept") nasMsg, err := BuildServiceAccept(amfUe, anType, pDUSessionStatus, reactivationResult, errPduSessionId, errCause) if err != nil { amfUe.GmmLog.Error(err.Error()) - return + return err } - ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) + + if amfUe.RanUe[anType].UeContextRequest || + (!amfUe.RanUe[anType].InitialContextSetup && len(cxtList.List) > 0) { + // update Kgnb/Kn3iwf + amfUe.UpdateSecurityContext(anType) + } + ngap_message.SendN2Message(amfUe, anType, nasMsg, &cxtList, nil, nil, nil, nil) + return nil } func SendConfigurationUpdateCommand(amfUe *context.AmfUe, accessType models.AccessType, @@ -229,15 +242,18 @@ func SendServiceReject(ue *context.RanUe, pDUSessionStatus *[16]bool, cause uint return } if ue.AmfUe == nil { - logger.GmmLog.Error("SendServiceReject: AmfUe is nil") - return + ue.Log.Info("Send Service Reject") + } else { + ue.AmfUe.GmmLog.Info("Send Service Reject") } - amfUe := ue.AmfUe - amfUe.GmmLog.Info("Send Service Reject") nasMsg, err := BuildServiceReject(pDUSessionStatus, cause) if err != nil { - amfUe.GmmLog.Error(err.Error()) + if ue.AmfUe == nil { + ue.Log.Error(err.Error()) + } else { + ue.AmfUe.GmmLog.Error(err.Error()) + } return } ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) @@ -251,15 +267,18 @@ func SendRegistrationReject(ue *context.RanUe, cause5GMM uint8, eapMessage strin return } if ue.AmfUe == nil { - logger.GmmLog.Error("SendRegistrationReject: AmfUe is nil") - return + ue.Log.Info("Send Registration Reject") + } else { + ue.AmfUe.GmmLog.Info("Send Registration Reject") } - amfUe := ue.AmfUe - amfUe.GmmLog.Info("Send Registration Reject") - nasMsg, err := BuildRegistrationReject(amfUe, cause5GMM, eapMessage) + nasMsg, err := BuildRegistrationReject(ue.AmfUe, cause5GMM, eapMessage) if err != nil { - amfUe.GmmLog.Error(err.Error()) + if ue.AmfUe == nil { + ue.Log.Error(err.Error()) + } else { + ue.AmfUe.GmmLog.Error(err.Error()) + } return } ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) @@ -286,14 +305,19 @@ func SendSecurityModeCommand(ue *context.RanUe, accessType models.AccessType, ea } ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) - if context.AMF_Self().T3560Cfg.Enable { - cfg := context.AMF_Self().T3560Cfg + if context.GetSelf().T3560Cfg.Enable { + cfg := context.GetSelf().T3560Cfg + amfUe.GmmLog.Infof("Start T3560 timer") amfUe.T3560 = context.NewTimer(cfg.ExpireTime, cfg.MaxRetryTimes, func(expireTimes int32) { amfUe.GmmLog.Warnf("T3560 expires, retransmit Security Mode Command (retry: %d)", expireTimes) ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) }, func() { + amfUe.Lock.Lock() + defer amfUe.Lock.Unlock() + amfUe.GmmLog.Warnf("T3560 Expires %d times, abort security mode control procedure", cfg.MaxRetryTimes) - gmm_common.RemoveAmfUe(amfUe) + amfUe.T3560 = nil + gmm_common.RemoveAmfUe(amfUe, false) }) } } @@ -317,8 +341,9 @@ func SendDeregistrationRequest(ue *context.RanUe, accessType uint8, reRegistrati } ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) - if context.AMF_Self().T3522Cfg.Enable { - cfg := context.AMF_Self().T3522Cfg + if context.GetSelf().T3522Cfg.Enable { + cfg := context.GetSelf().T3522Cfg + amfUe.GmmLog.Infof("Start T3522 timer") amfUe.T3522 = context.NewTimer(cfg.ExpireTime, cfg.MaxRetryTimes, func(expireTimes int32) { amfUe.GmmLog.Warnf("T3522 expires, retransmit Deregistration Request (retry: %d)", expireTimes) ngap_message.SendDownlinkNasTransport(ue, nasMsg, nil) @@ -362,12 +387,10 @@ func SendDeregistrationAccept(ue *context.RanUe) { } func SendRegistrationAccept( - amfUe *context.AmfUe, - anType models.AccessType, - pDUSessionStatus *[16]bool, - reactivationResult *[16]bool, + amfUe *context.AmfUe, anType models.AccessType, + pDUSessionStatus *[16]bool, reactivationResult *[16]bool, errPduSessionId, errCause []uint8, - pduSessionResourceSetupList *ngapType.PDUSessionResourceSetupListCxtReq, + cxtList *ngapType.PDUSessionResourceSetupListCxtReq, ) { if amfUe == nil { logger.GmmLog.Error("SendRegistrationAccept: AmfUe is nil") @@ -385,21 +408,26 @@ func SendRegistrationAccept( return } - if amfUe.RanUe[anType].UeContextRequest { - ngap_message.SendInitialContextSetupRequest(amfUe, anType, nasMsg, pduSessionResourceSetupList, nil, nil, nil) + if anType == models.AccessType_NON_3_GPP_ACCESS { + // TS 23.502 4.12.2.2 10a ~ 13: if non-3gpp, AMF should send initial context setup request to N3IWF first, + // and send registration accept after receiving initial context setup response + amfUe.RegistrationAcceptForNon3GPPAccess = nasMsg + ngap_message.SendInitialContextSetupRequest(amfUe, anType, nil, cxtList, nil, nil, nil) } else { - ngap_message.SendDownlinkNasTransport(amfUe.RanUe[models.AccessType__3_GPP_ACCESS], nasMsg, nil) + // anType is 3GPP_ACCESS + ngap_message.SendN2Message(amfUe, anType, nasMsg, cxtList, nil, nil, nil, nil) } - if context.AMF_Self().T3550Cfg.Enable { - cfg := context.AMF_Self().T3550Cfg + if context.GetSelf().T3550Cfg.Enable { + cfg := context.GetSelf().T3550Cfg + amfUe.GmmLog.Infof("Start T3550 timer") amfUe.T3550 = context.NewTimer(cfg.ExpireTime, cfg.MaxRetryTimes, func(expireTimes int32) { if amfUe.RanUe[anType] == nil { amfUe.GmmLog.Warnf("[NAS] UE Context released, abort retransmission of Registration Accept") amfUe.T3550 = nil } else { amfUe.GmmLog.Warnf("T3550 expires, retransmit Registration Accept (retry: %d)", expireTimes) - ngap_message.SendDownlinkNasTransport(amfUe.RanUe[anType], nasMsg, nil) + ngap_message.SendN2Message(amfUe, anType, nasMsg, cxtList, nil, nil, nil, nil) } }, func() { amfUe.GmmLog.Warnf("T3550 Expires %d times, abort retransmission of Registration Accept", cfg.MaxRetryTimes) diff --git a/internal/gmm/sm.go b/internal/gmm/sm.go index cf76ee27..f8685922 100644 --- a/internal/gmm/sm.go +++ b/internal/gmm/sm.go @@ -37,7 +37,7 @@ func DeRegistered(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { ArgAmfUe: amfUe, ArgAccessType: accessType, ArgProcedureCode: procedureCode, - }); err != nil { + }, logger.GmmLog); err != nil { logger.GmmLog.Errorln(err) } } @@ -83,7 +83,7 @@ func Registered(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { ArgAmfUe: amfUe, ArgAccessType: accessType, ArgProcedureCode: procedureCode, - }); err != nil { + }, logger.GmmLog); err != nil { logger.GmmLog.Errorln(err) } } @@ -108,7 +108,7 @@ func Registered(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { ArgAmfUe: amfUe, ArgAccessType: accessType, ArgNASMessage: gmmMessage, - }); err != nil { + }, logger.GmmLog); err != nil { logger.GmmLog.Errorln(err) } case nas.MsgTypeStatus5GMM: @@ -147,7 +147,7 @@ func Authentication(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { if err := GmmFSM.SendEvent(state, AuthErrorEvent, fsm.ArgsType{ ArgAmfUe: amfUe, ArgAccessType: accessType, - }); err != nil { + }, logger.GmmLog); err != nil { logger.GmmLog.Errorln(err) } } @@ -155,7 +155,7 @@ func Authentication(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { if err := GmmFSM.SendEvent(state, AuthSuccessEvent, fsm.ArgsType{ ArgAmfUe: amfUe, ArgAccessType: accessType, - }); err != nil { + }, logger.GmmLog); err != nil { logger.GmmLog.Errorln(err) } } @@ -174,7 +174,14 @@ func Authentication(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { mobileIdentityContents := gmmMessage.IdentityResponse.MobileIdentity.GetMobileIdentityContents() amfUe.IdentityTypeUsedForRegistration = nasConvert.GetTypeOfIdentity(mobileIdentityContents[0]) - err := GmmFSM.SendEvent(state, AuthRestartEvent, fsm.ArgsType{ArgAmfUe: amfUe, ArgAccessType: accessType}) + err := GmmFSM.SendEvent( + state, + AuthRestartEvent, + fsm.ArgsType{ + ArgAmfUe: amfUe, + ArgAccessType: accessType, + }, logger.GmmLog, + ) if err != nil { logger.GmmLog.Errorln(err) } @@ -217,7 +224,7 @@ func Authentication(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { logger.GmmLog.Errorln(err) } } - gmm_common.RemoveAmfUe(amfUe) + gmm_common.RemoveAmfUe(amfUe, true) case fsm.ExitEvent: // clear authentication related data at exit amfUe := args[ArgAmfUe].(*context.AmfUe) @@ -236,7 +243,7 @@ func SecurityMode(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { amfUe := args[ArgAmfUe].(*context.AmfUe) accessType := args[ArgAccessType].(models.AccessType) // set log information - amfUe.UpdateLogFields() + amfUe.UpdateLogFields(accessType) amfUe.GmmLog.Debugln("EntryEvent at GMM State[SecurityMode]") if amfUe.SecurityContextIsValid() { @@ -245,14 +252,14 @@ func SecurityMode(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { ArgAmfUe: amfUe, ArgAccessType: accessType, ArgNASMessage: amfUe.RegistrationRequest, - }); err != nil { + }, logger.GmmLog); err != nil { logger.GmmLog.Errorln(err) } } else { eapSuccess := args[ArgEAPSuccess].(bool) eapMessage := args[ArgEAPMessage].(string) // Select enc/int algorithm based on ue security capability & amf's policy, - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if err := amfUe.SelectSecurityAlg(amfSelf.SecurityAlgorithm.IntegrityOrder, amfSelf.SecurityAlgorithm.CipheringOrder); err != nil { amfUe.GmmLog.Errorf("Select security algorithm failed: %s", err) @@ -260,7 +267,7 @@ func SecurityMode(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { err = GmmFSM.SendEvent(state, SecurityModeFailEvent, fsm.ArgsType{ ArgAmfUe: amfUe, ArgAccessType: accessType, - }) + }, logger.GmmLog) if err != nil { logger.GmmLog.Errorln(err) } @@ -288,7 +295,7 @@ func SecurityMode(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { err := GmmFSM.SendEvent(state, SecurityModeFailEvent, fsm.ArgsType{ ArgAmfUe: amfUe, ArgAccessType: accessType, - }) + }, logger.GmmLog) if err != nil { logger.GmmLog.Errorln(err) } @@ -330,7 +337,7 @@ func ContextSetup(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { err = GmmFSM.SendEvent(state, ContextSetupFailEvent, fsm.ArgsType{ ArgAmfUe: amfUe, ArgAccessType: accessType, - }) + }, logger.GmmLog) if err != nil { logger.GmmLog.Errorln(err) } @@ -343,7 +350,7 @@ func ContextSetup(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { err = GmmFSM.SendEvent(state, ContextSetupFailEvent, fsm.ArgsType{ ArgAmfUe: amfUe, ArgAccessType: accessType, - }) + }, logger.GmmLog) if err != nil { logger.GmmLog.Errorln(err) } @@ -373,7 +380,7 @@ func ContextSetup(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { err = GmmFSM.SendEvent(state, ContextSetupFailEvent, fsm.ArgsType{ ArgAmfUe: amfUe, ArgAccessType: accessType, - }) + }, logger.GmmLog) if err != nil { logger.GmmLog.Errorln(err) } @@ -386,7 +393,7 @@ func ContextSetup(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { err = GmmFSM.SendEvent(state, ContextSetupFailEvent, fsm.ArgsType{ ArgAmfUe: amfUe, ArgAccessType: accessType, - }) + }, logger.GmmLog) if err != nil { logger.GmmLog.Errorln(err) } @@ -411,7 +418,7 @@ func ContextSetup(state *fsm.State, event fsm.EventType, args fsm.ArgsType) { logger.GmmLog.Debugln(event) amfUe := args[ArgAmfUe].(*context.AmfUe) accessType := args[ArgAccessType].(models.AccessType) - if amfUe.UeCmRegistered { + if amfUe.UeCmRegistered[accessType] { problemDetails, err := consumer.UeCmDeregistration(amfUe, accessType) if problemDetails != nil { if problemDetails.Cause != "CONTEXT_NOT_FOUND" { diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 41ad28d3..6800e814 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -1,25 +1,19 @@ package logger import ( - "os" - "time" - - formatter "github.com/antonfisher/nested-logrus-formatter" "github.com/sirupsen/logrus" - aperLogger "github.com/free5gc/aper/logger" - nasLogger "github.com/free5gc/nas/logger" - ngapLogger "github.com/free5gc/ngap/logger" - fsmLogger "github.com/free5gc/util/fsm/logger" logger_util "github.com/free5gc/util/logger" ) var ( - log *logrus.Logger - AppLog *logrus.Entry + Log *logrus.Logger + NfLog *logrus.Entry + MainLog *logrus.Entry InitLog *logrus.Entry CfgLog *logrus.Entry - ContextLog *logrus.Entry + CtxLog *logrus.Entry + GinLog *logrus.Entry NgapLog *logrus.Entry HandlerLog *logrus.Entry HttpLog *logrus.Entry @@ -33,7 +27,6 @@ var ( NasLog *logrus.Entry ConsumerLog *logrus.Entry EeLog *logrus.Entry - GinLog *logrus.Entry ) const ( @@ -43,75 +36,29 @@ const ( ) func init() { - log = logrus.New() - log.SetReportCaller(false) - - log.Formatter = &formatter.Formatter{ - TimestampFormat: time.RFC3339Nano, - TrimMessages: true, - NoFieldsSpace: true, - HideKeys: true, - FieldsOrder: []string{"component", "category", FieldRanAddr, FieldAmfUeNgapID, FieldSupi}, - } - - AppLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "App"}) - InitLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "Init"}) - CfgLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "CFG"}) - ContextLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "Context"}) - NgapLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "NGAP"}) - HandlerLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "Handler"}) - HttpLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "HTTP"}) - GmmLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "GMM"}) - MtLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "MT"}) - ProducerLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "Producer"}) - LocationLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "LocInfo"}) - CommLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "Comm"}) - CallbackLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "Callback"}) - UtilLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "Util"}) - NasLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "NAS"}) - ConsumerLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "Consumer"}) - EeLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "EventExposure"}) - GinLog = log.WithFields(logrus.Fields{"component": "AMF", "category": "GIN"}) -} - -func LogFileHook(logNfPath string, log5gcPath string) error { - if fullPath, err := logger_util.CreateFree5gcLogFile(log5gcPath); err == nil { - if fullPath != "" { - free5gcLogHook, hookErr := logger_util.NewFileHook(fullPath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o666) - if err != nil { - return hookErr - } - log.Hooks.Add(free5gcLogHook) - aperLogger.GetLogger().Hooks.Add(free5gcLogHook) - ngapLogger.GetLogger().Hooks.Add(free5gcLogHook) - nasLogger.GetLogger().Hooks.Add(free5gcLogHook) - fsmLogger.GetLogger().Hooks.Add(free5gcLogHook) - } - } else { - return err - } - - if fullPath, err := logger_util.CreateNfLogFile(logNfPath, "amf.log"); err == nil { - selfLogHook, hookErr := logger_util.NewFileHook(fullPath, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0o666) - if err != nil { - return hookErr - } - log.Hooks.Add(selfLogHook) - aperLogger.GetLogger().Hooks.Add(selfLogHook) - ngapLogger.GetLogger().Hooks.Add(selfLogHook) - nasLogger.GetLogger().Hooks.Add(selfLogHook) - fsmLogger.GetLogger().Hooks.Add(selfLogHook) - } else { - return err + fieldsOrder := []string{ + logger_util.FieldNF, + logger_util.FieldCategory, } - return nil -} - -func SetLogLevel(level logrus.Level) { - log.SetLevel(level) -} - -func SetReportCaller(set bool) { - log.SetReportCaller(set) + Log = logger_util.New(fieldsOrder) + NfLog = Log.WithField(logger_util.FieldNF, "AMF") + MainLog = NfLog.WithField(logger_util.FieldCategory, "Main") + InitLog = NfLog.WithField(logger_util.FieldCategory, "Init") + CfgLog = NfLog.WithField(logger_util.FieldCategory, "CFG") + CtxLog = NfLog.WithField(logger_util.FieldCategory, "CTX") + GinLog = NfLog.WithField(logger_util.FieldCategory, "GIN") + NgapLog = NfLog.WithField(logger_util.FieldCategory, "Ngap") + HandlerLog = NfLog.WithField(logger_util.FieldCategory, "Handler") + HttpLog = NfLog.WithField(logger_util.FieldCategory, "Http") + GmmLog = NfLog.WithField(logger_util.FieldCategory, "Gmm") + MtLog = NfLog.WithField(logger_util.FieldCategory, "Mt") + ProducerLog = NfLog.WithField(logger_util.FieldCategory, "Producer") + LocationLog = NfLog.WithField(logger_util.FieldCategory, "Location") + CommLog = NfLog.WithField(logger_util.FieldCategory, "Comm") + CallbackLog = NfLog.WithField(logger_util.FieldCategory, "Callback") + UtilLog = NfLog.WithField(logger_util.FieldCategory, "Util") + NasLog = NfLog.WithField(logger_util.FieldCategory, "Nas") + ConsumerLog = NfLog.WithField(logger_util.FieldCategory, "Consumer") + EeLog = NfLog.WithField(logger_util.FieldCategory, "Ee") } diff --git a/internal/nas/dispatch.go b/internal/nas/dispatch.go index 39424b9a..198e846c 100644 --- a/internal/nas/dispatch.go +++ b/internal/nas/dispatch.go @@ -6,6 +6,7 @@ import ( "github.com/free5gc/amf/internal/context" "github.com/free5gc/amf/internal/gmm" + "github.com/free5gc/amf/internal/logger" "github.com/free5gc/nas" "github.com/free5gc/openapi/models" "github.com/free5gc/util/fsm" @@ -29,5 +30,5 @@ func Dispatch(ue *context.AmfUe, accessType models.AccessType, procedureCode int gmm.ArgAccessType: accessType, gmm.ArgNASMessage: msg.GmmMessage, gmm.ArgProcedureCode: procedureCode, - }) + }, logger.GmmLog) } diff --git a/internal/nas/fuzz_test.go b/internal/nas/fuzz_test.go index 4aca6b44..8c533d68 100644 --- a/internal/nas/fuzz_test.go +++ b/internal/nas/fuzz_test.go @@ -19,7 +19,7 @@ import ( ) func FuzzHandleNAS(f *testing.F) { - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() amfSelf.ServedGuamiList = []models.Guami{{ PlmnId: &models.PlmnId{ Mcc: "208", @@ -107,12 +107,13 @@ func FuzzHandleNAS(f *testing.F) { ue.Ran.Log = logger.NgapLog ue.Log = logger.NgapLog ue.Tai = tai + ue.AmfUe = amfSelf.NewAmfUe("") amf_nas.HandleNAS(ue, ngapType.ProcedureCodeInitialUEMessage, d, true) }) } func FuzzHandleNAS2(f *testing.F) { - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() amfSelf.ServedGuamiList = []models.Guami{{ PlmnId: &models.PlmnId{ Mcc: "208", @@ -216,6 +217,7 @@ func FuzzHandleNAS2(f *testing.F) { ue.Ran.Log = logger.NgapLog ue.Log = logger.NgapLog ue.Tai = tai + ue.AmfUe = amfSelf.NewAmfUe("") amf_nas.HandleNAS(ue, ngapType.ProcedureCodeInitialUEMessage, regPkt, true) amfUe := ue.AmfUe amfUe.State[models.AccessType__3_GPP_ACCESS].Set(amf_context.Authentication) diff --git a/internal/nas/handler.go b/internal/nas/handler.go index 1a881604..69e82f48 100644 --- a/internal/nas/handler.go +++ b/internal/nas/handler.go @@ -1,14 +1,17 @@ package nas import ( - "github.com/free5gc/amf/internal/context" + "fmt" + + amf_context "github.com/free5gc/amf/internal/context" gmm_common "github.com/free5gc/amf/internal/gmm/common" "github.com/free5gc/amf/internal/logger" "github.com/free5gc/amf/internal/nas/nas_security" + "github.com/free5gc/nas" ) -func HandleNAS(ue *context.RanUe, procedureCode int64, nasPdu []byte, initialMessage bool) { - amfSelf := context.AMF_Self() +func HandleNAS(ue *amf_context.RanUe, procedureCode int64, nasPdu []byte, initialMessage bool) { + amfSelf := amf_context.GetSelf() if ue == nil { logger.NasLog.Error("RanUe is nil") @@ -36,3 +39,20 @@ func HandleNAS(ue *context.RanUe, procedureCode int64, nasPdu []byte, initialMes ue.AmfUe.NASLog.Errorf("Handle NAS Error: %v", err) } } + +// Get5GSMobileIdentityFromNASPDU is used to find MobileIdentity from plain nas +// return value is: mobileId, mobileIdType, err +func GetNas5GSMobileIdentity(gmmMessage *nas.GmmMessage) (string, string, error) { + var err error + var mobileId, mobileIdType string + + if gmmMessage.GmmHeader.GetMessageType() == nas.MsgTypeRegistrationRequest { + mobileId, mobileIdType, err = gmmMessage.RegistrationRequest.MobileIdentity5GS.GetMobileIdentity() + } else if gmmMessage.GmmHeader.GetMessageType() == nas.MsgTypeServiceRequest { + mobileId, mobileIdType, err = gmmMessage.ServiceRequest.TMSI5GS.Get5GSTMSI() + } else { + err = fmt.Errorf("gmmMessageType: [%d] is not RegistrationRequest or ServiceRequest", + gmmMessage.GmmHeader.GetMessageType()) + } + return mobileId, mobileIdType, err +} diff --git a/internal/nas/nas_security/security.go b/internal/nas/nas_security/security.go index 3236a342..22d89634 100644 --- a/internal/nas/nas_security/security.go +++ b/internal/nas/nas_security/security.go @@ -340,6 +340,32 @@ func Decode(ue *context.AmfUe, accessType models.AccessType, payload []byte, return msg, integrityProtected, nil } +// DecodePlainNas is used to decode plain nas. +// If nas pdu is ciphered, this function will return error message. +// return value is: *nas.Message +func DecodePlainNasNoIntegrityCheck(payload []byte) (*nas.Message, error) { + const SecurityHeaderTypeMask uint8 = 0x0f + + if payload == nil { + return nil, fmt.Errorf("nas payload is empty") + } + + msg := new(nas.Message) + msg.SecurityHeaderType = nas.GetSecurityHeaderType(payload) & SecurityHeaderTypeMask + if msg.SecurityHeaderType == nas.SecurityHeaderTypeIntegrityProtectedAndCiphered || + msg.SecurityHeaderType == nas.SecurityHeaderTypeIntegrityProtectedAndCipheredWithNew5gNasSecurityContext { + return nil, fmt.Errorf("nas payload is ciphered") + } + + if msg.SecurityHeaderType != nas.SecurityHeaderTypePlainNas { + // remove security Header + payload = payload[7:] + } + + err := msg.PlainNasDecode(&payload) + return msg, err +} + func GetBearerType(accessType models.AccessType) uint8 { if accessType == models.AccessType__3_GPP_ACCESS { return security.Bearer3GPP diff --git a/internal/nas/testing/fake_nas_pdu.go b/internal/nas/testing/fake_nas_pdu.go new file mode 100644 index 00000000..a6dbf32c --- /dev/null +++ b/internal/nas/testing/fake_nas_pdu.go @@ -0,0 +1,930 @@ +package testing + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "fmt" + + "github.com/free5gc/nas" + "github.com/free5gc/nas/logger" + "github.com/free5gc/nas/nasConvert" + "github.com/free5gc/nas/nasMessage" + "github.com/free5gc/nas/nasType" + "github.com/free5gc/openapi/models" +) + +const ( + PDUSesModiReq string = "PDU Session Modification Request" + PDUSesModiCmp string = "PDU Session Modification Complete" + PDUSesModiCmdRej string = "PDU Session Modification Command Reject" + PDUSesRelReq string = "PDU Session Release Request" + PDUSesRelCmp string = "PDU Session Release Complete" + PDUSesRelRej string = "PDU Session Release Reject" + PDUSesAuthCmp string = "PDU Session Authentication Complete" +) + +func GetRegistrationRequest( + registrationType uint8, + mobileIdentity nasType.MobileIdentity5GS, + requestedNSSAI *nasType.RequestedNSSAI, + ueSecurityCapability *nasType.UESecurityCapability, + capability5GMM *nasType.Capability5GMM, + nasMessageContainer []uint8, + uplinkDataStatus *nasType.UplinkDataStatus, +) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeRegistrationRequest) + + registrationRequest := nasMessage.NewRegistrationRequest(0) + registrationRequest.SetExtendedProtocolDiscriminator(nasMessage.Epd5GSMobilityManagementMessage) + registrationRequest.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + registrationRequest.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0x00) + registrationRequest.RegistrationRequestMessageIdentity.SetMessageType(nas.MsgTypeRegistrationRequest) + registrationRequest.NgksiAndRegistrationType5GS.SetTSC(nasMessage.TypeOfSecurityContextFlagNative) + registrationRequest.NgksiAndRegistrationType5GS.SetNasKeySetIdentifiler(0x7) + registrationRequest.NgksiAndRegistrationType5GS.SetFOR(1) + registrationRequest.NgksiAndRegistrationType5GS.SetRegistrationType5GS(registrationType) + registrationRequest.MobileIdentity5GS = mobileIdentity + + registrationRequest.UESecurityCapability = ueSecurityCapability + registrationRequest.Capability5GMM = capability5GMM + registrationRequest.RequestedNSSAI = requestedNSSAI + registrationRequest.UplinkDataStatus = uplinkDataStatus + + if nasMessageContainer != nil { + registrationRequest.NASMessageContainer = nasType.NewNASMessageContainer( + nasMessage.RegistrationRequestNASMessageContainerType) + registrationRequest.NASMessageContainer.SetLen(uint16(len(nasMessageContainer))) + registrationRequest.NASMessageContainer.SetNASMessageContainerContents(nasMessageContainer) + } + + m.GmmMessage.RegistrationRequest = registrationRequest + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetPduSessionEstablishmentRequest(pduSessionId uint8) []byte { + m := nas.NewMessage() + m.GsmMessage = nas.NewGsmMessage() + m.GsmHeader.SetMessageType(nas.MsgTypePDUSessionEstablishmentRequest) + + pduSessionEstablishmentRequest := nasMessage.NewPDUSessionEstablishmentRequest(0) + pduSessionEstablishmentRequest.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSSessionManagementMessage) + pduSessionEstablishmentRequest.SetMessageType(nas.MsgTypePDUSessionEstablishmentRequest) + pduSessionEstablishmentRequest.PDUSessionID.SetPDUSessionID(pduSessionId) + pduSessionEstablishmentRequest.PTI.SetPTI(0x00) + pduSessionEstablishmentRequest.IntegrityProtectionMaximumDataRate. + SetMaximumDataRatePerUEForUserPlaneIntegrityProtectionForDownLink(0xff) + pduSessionEstablishmentRequest.IntegrityProtectionMaximumDataRate. + SetMaximumDataRatePerUEForUserPlaneIntegrityProtectionForUpLink(0xff) + + pduSessionEstablishmentRequest.PDUSessionType = nasType. + NewPDUSessionType(nasMessage.PDUSessionEstablishmentRequestPDUSessionTypeType) + pduSessionEstablishmentRequest.PDUSessionType.SetPDUSessionTypeValue(uint8(0x01)) // IPv4 type + + pduSessionEstablishmentRequest.ExtendedProtocolConfigurationOptions = nasType.NewExtendedProtocolConfigurationOptions( + nasMessage.PDUSessionEstablishmentRequestExtendedProtocolConfigurationOptionsType) + protocolConfigurationOptions := nasConvert.NewProtocolConfigurationOptions() + protocolConfigurationOptions.AddIPAddressAllocationViaNASSignallingUL() + protocolConfigurationOptions.AddDNSServerIPv4AddressRequest() + protocolConfigurationOptions.AddDNSServerIPv6AddressRequest() + pcoContents := protocolConfigurationOptions.Marshal() + pcoContentsLength := len(pcoContents) + pduSessionEstablishmentRequest.ExtendedProtocolConfigurationOptions.SetLen(uint16(pcoContentsLength)) + pduSessionEstablishmentRequest.ExtendedProtocolConfigurationOptions. + SetExtendedProtocolConfigurationOptionsContents(pcoContents) + + m.GsmMessage.PDUSessionEstablishmentRequest = pduSessionEstablishmentRequest + + data := new(bytes.Buffer) + err := m.GsmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetUlNasTransport_PduSessionEstablishmentRequest(pduSessionId uint8, requestType uint8, dnn string, + sNssai *models.Snssai, +) []byte { + pduSessionEstablishmentRequest := GetPduSessionEstablishmentRequest(pduSessionId) + + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeULNASTransport) + + ulNasTransport := nasMessage.NewULNASTransport(0) + ulNasTransport.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + ulNasTransport.SetMessageType(nas.MsgTypeULNASTransport) + ulNasTransport.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + ulNasTransport.PduSessionID2Value = new(nasType.PduSessionID2Value) + ulNasTransport.PduSessionID2Value.SetIei(nasMessage.ULNASTransportPduSessionID2ValueType) + ulNasTransport.PduSessionID2Value.SetPduSessionID2Value(pduSessionId) + ulNasTransport.RequestType = new(nasType.RequestType) + ulNasTransport.RequestType.SetIei(nasMessage.ULNASTransportRequestTypeType) + ulNasTransport.RequestType.SetRequestTypeValue(requestType) + if dnn != "" { + ulNasTransport.DNN = new(nasType.DNN) + ulNasTransport.DNN.SetIei(nasMessage.ULNASTransportDNNType) + ulNasTransport.DNN.SetDNN(dnn) + } + if sNssai != nil { + var sdTemp [3]uint8 + sd, err := hex.DecodeString(sNssai.Sd) + if err != nil { + logger.NasMsgLog.Errorf("sNssai decode error: %+v", err) + } + copy(sdTemp[:], sd) + ulNasTransport.SNSSAI = nasType.NewSNSSAI(nasMessage.ULNASTransportSNSSAIType) + ulNasTransport.SNSSAI.SetLen(4) + ulNasTransport.SNSSAI.SetSST(uint8(sNssai.Sst)) + ulNasTransport.SNSSAI.SetSD(sdTemp) + } + + ulNasTransport.SpareHalfOctetAndPayloadContainerType.SetPayloadContainerType(nasMessage.PayloadContainerTypeN1SMInfo) + ulNasTransport.PayloadContainer.SetLen(uint16(len(pduSessionEstablishmentRequest))) + ulNasTransport.PayloadContainer.SetPayloadContainerContents(pduSessionEstablishmentRequest) + + m.GmmMessage.ULNASTransport = ulNasTransport + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetUlNasTransport_PduSessionModificationRequest(pduSessionId uint8, requestType uint8, dnn string, + sNssai *models.Snssai, +) []byte { + pduSessionModificationRequest := GetPduSessionModificationRequest(pduSessionId) + + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeULNASTransport) + + ulNasTransport := nasMessage.NewULNASTransport(0) + ulNasTransport.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + ulNasTransport.SetMessageType(nas.MsgTypeULNASTransport) + ulNasTransport.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + ulNasTransport.PduSessionID2Value = new(nasType.PduSessionID2Value) + ulNasTransport.PduSessionID2Value.SetIei(nasMessage.ULNASTransportPduSessionID2ValueType) + ulNasTransport.PduSessionID2Value.SetPduSessionID2Value(pduSessionId) + ulNasTransport.RequestType = new(nasType.RequestType) + ulNasTransport.RequestType.SetIei(nasMessage.ULNASTransportRequestTypeType) + ulNasTransport.RequestType.SetRequestTypeValue(requestType) + if dnn != "" { + ulNasTransport.DNN = new(nasType.DNN) + ulNasTransport.DNN.SetIei(nasMessage.ULNASTransportDNNType) + ulNasTransport.DNN.SetDNN(dnn) + } + if sNssai != nil { + var sdTemp [3]uint8 + sd, err := hex.DecodeString(sNssai.Sd) + if err != nil { + logger.NasMsgLog.Errorf("sNssai SD decode error: %+v", err) + } + copy(sdTemp[:], sd) + ulNasTransport.SNSSAI = nasType.NewSNSSAI(nasMessage.ULNASTransportSNSSAIType) + ulNasTransport.SNSSAI.SetLen(4) + ulNasTransport.SNSSAI.SetSST(uint8(sNssai.Sst)) + ulNasTransport.SNSSAI.SetSD(sdTemp) + } + + ulNasTransport.SpareHalfOctetAndPayloadContainerType.SetPayloadContainerType(nasMessage.PayloadContainerTypeN1SMInfo) + ulNasTransport.PayloadContainer.SetLen(uint16(len(pduSessionModificationRequest))) + ulNasTransport.PayloadContainer.SetPayloadContainerContents(pduSessionModificationRequest) + + m.GmmMessage.ULNASTransport = ulNasTransport + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetPduSessionModificationRequest(pduSessionId uint8) []byte { + m := nas.NewMessage() + m.GsmMessage = nas.NewGsmMessage() + m.GsmHeader.SetMessageType(nas.MsgTypePDUSessionModificationRequest) + + pduSessionModificationRequest := nasMessage.NewPDUSessionModificationRequest(0) + pduSessionModificationRequest.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSSessionManagementMessage) + pduSessionModificationRequest.SetMessageType(nas.MsgTypePDUSessionModificationRequest) + pduSessionModificationRequest.PDUSessionID.SetPDUSessionID(pduSessionId) + pduSessionModificationRequest.PTI.SetPTI(0x00) + // pduSessionModificationRequest.RequestedQosFlowDescriptions = nasType.NewRequestedQosFlowDescriptions(nasMessage. + // PDUSessionModificationRequestRequestedQosFlowDescriptionsType) + // pduSessionModificationRequest.RequestedQosFlowDescriptions.SetLen(6) + // pduSessionModificationRequest.RequestedQosFlowDescriptions.SetQoSFlowDescriptions([]uint8{0x09, 0x20, 0x41, 0x01, + // 0x01, 0x09}) + + m.GsmMessage.PDUSessionModificationRequest = pduSessionModificationRequest + + data := new(bytes.Buffer) + err := m.GsmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetPduSessionModificationComplete(pduSessionId uint8) []byte { + m := nas.NewMessage() + m.GsmMessage = nas.NewGsmMessage() + m.GsmHeader.SetMessageType(nas.MsgTypePDUSessionModificationComplete) + + pduSessionModificationComplete := nasMessage.NewPDUSessionModificationComplete(0) + pduSessionModificationComplete.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSSessionManagementMessage) + pduSessionModificationComplete.SetMessageType(nas.MsgTypePDUSessionModificationComplete) + pduSessionModificationComplete.PDUSessionID.SetPDUSessionID(pduSessionId) + pduSessionModificationComplete.PTI.SetPTI(0x00) + + m.GsmMessage.PDUSessionModificationComplete = pduSessionModificationComplete + + data := new(bytes.Buffer) + err := m.GsmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetPduSessionModificationCommandReject(pduSessionId uint8) []byte { + m := nas.NewMessage() + m.GsmMessage = nas.NewGsmMessage() + m.GsmHeader.SetMessageType(nas.MsgTypePDUSessionModificationCommandReject) + + pduSessionModificationCommandReject := nasMessage.NewPDUSessionModificationCommandReject(0) + pduSessionModificationCommandReject.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSSessionManagementMessage) + pduSessionModificationCommandReject.SetMessageType(nas.MsgTypePDUSessionModificationCommandReject) + pduSessionModificationCommandReject.PDUSessionID.SetPDUSessionID(pduSessionId) + pduSessionModificationCommandReject.PTI.SetPTI(0x00) + + m.GsmMessage.PDUSessionModificationCommandReject = pduSessionModificationCommandReject + + data := new(bytes.Buffer) + err := m.GsmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetPduSessionReleaseRequest(pduSessionId uint8) []byte { + m := nas.NewMessage() + m.GsmMessage = nas.NewGsmMessage() + m.GsmHeader.SetMessageType(nas.MsgTypePDUSessionReleaseRequest) + + pduSessionReleaseRequest := nasMessage.NewPDUSessionReleaseRequest(0) + pduSessionReleaseRequest.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSSessionManagementMessage) + pduSessionReleaseRequest.SetMessageType(nas.MsgTypePDUSessionReleaseRequest) + pduSessionReleaseRequest.PDUSessionID.SetPDUSessionID(pduSessionId) + pduSessionReleaseRequest.PTI.SetPTI(0x00) + + m.GsmMessage.PDUSessionReleaseRequest = pduSessionReleaseRequest + + data := new(bytes.Buffer) + err := m.GsmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetPduSessionReleaseComplete(pduSessionId uint8) []byte { + m := nas.NewMessage() + m.GsmMessage = nas.NewGsmMessage() + m.GsmHeader.SetMessageType(nas.MsgTypePDUSessionReleaseComplete) + + pduSessionReleaseComplete := nasMessage.NewPDUSessionReleaseComplete(0) + pduSessionReleaseComplete.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSSessionManagementMessage) + pduSessionReleaseComplete.SetMessageType(nas.MsgTypePDUSessionReleaseComplete) + pduSessionReleaseComplete.PDUSessionID.SetPDUSessionID(pduSessionId) + pduSessionReleaseComplete.PTI.SetPTI(0x00) + + m.GsmMessage.PDUSessionReleaseComplete = pduSessionReleaseComplete + + data := new(bytes.Buffer) + err := m.GsmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetPduSessionReleaseReject(pduSessionId uint8) []byte { + m := nas.NewMessage() + m.GsmMessage = nas.NewGsmMessage() + m.GsmHeader.SetMessageType(nas.MsgTypePDUSessionReleaseReject) + + pduSessionReleaseReject := nasMessage.NewPDUSessionReleaseReject(0) + pduSessionReleaseReject.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSSessionManagementMessage) + pduSessionReleaseReject.SetMessageType(nas.MsgTypePDUSessionReleaseReject) + pduSessionReleaseReject.PDUSessionID.SetPDUSessionID(pduSessionId) + pduSessionReleaseReject.PTI.SetPTI(0x00) + + m.GsmMessage.PDUSessionReleaseReject = pduSessionReleaseReject + + data := new(bytes.Buffer) + err := m.GsmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetPduSessionAuthenticationComplete(pduSessionId uint8) []byte { + m := nas.NewMessage() + m.GsmMessage = nas.NewGsmMessage() + m.GsmHeader.SetMessageType(nas.MsgTypePDUSessionAuthenticationComplete) + + pduSessionAuthenticaitonComplete := nasMessage.NewPDUSessionAuthenticationComplete(0) + pduSessionAuthenticaitonComplete.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSSessionManagementMessage) + pduSessionAuthenticaitonComplete.SetMessageType(nas.MsgTypePDUSessionAuthenticationComplete) + pduSessionAuthenticaitonComplete.PDUSessionID.SetPDUSessionID(pduSessionId) + pduSessionAuthenticaitonComplete.PTI.SetPTI(0x00) + pduSessionAuthenticaitonComplete.EAPMessage.SetLen(6) + pduSessionAuthenticaitonComplete.EAPMessage.SetEAPMessage([]byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}) + + m.GsmMessage.PDUSessionAuthenticationComplete = pduSessionAuthenticaitonComplete + + data := new(bytes.Buffer) + err := m.GsmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetUlNasTransport_PduSessionCommonData(pduSessionId uint8, types string) []byte { + var payload []byte + switch types { + case PDUSesModiReq: + payload = GetPduSessionModificationRequest(pduSessionId) + case PDUSesModiCmp: + payload = GetPduSessionModificationComplete(pduSessionId) + case PDUSesModiCmdRej: + payload = GetPduSessionModificationCommandReject(pduSessionId) + case PDUSesRelReq: + payload = GetPduSessionReleaseRequest(pduSessionId) + case PDUSesRelCmp: + payload = GetPduSessionReleaseComplete(pduSessionId) + case PDUSesRelRej: + payload = GetPduSessionReleaseReject(pduSessionId) + case PDUSesAuthCmp: + payload = GetPduSessionAuthenticationComplete(pduSessionId) + } + + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeULNASTransport) + + ulNasTransport := nasMessage.NewULNASTransport(0) + ulNasTransport.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + ulNasTransport.SetMessageType(nas.MsgTypeULNASTransport) + ulNasTransport.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + ulNasTransport.PduSessionID2Value = new(nasType.PduSessionID2Value) + ulNasTransport.PduSessionID2Value.SetIei(nasMessage.ULNASTransportPduSessionID2ValueType) + ulNasTransport.PduSessionID2Value.SetPduSessionID2Value(pduSessionId) + + ulNasTransport.SpareHalfOctetAndPayloadContainerType.SetPayloadContainerType(nasMessage.PayloadContainerTypeN1SMInfo) + ulNasTransport.PayloadContainer.SetLen(uint16(len(payload))) + ulNasTransport.PayloadContainer.SetPayloadContainerContents(payload) + + m.GmmMessage.ULNASTransport = ulNasTransport + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetIdentityResponse(mobileIdentity nasType.MobileIdentity) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeIdentityResponse) + + identityResponse := nasMessage.NewIdentityResponse(0) + identityResponse.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + identityResponse.IdentityResponseMessageIdentity.SetMessageType(nas.MsgTypeIdentityResponse) + identityResponse.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + identityResponse.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) + identityResponse.MobileIdentity = mobileIdentity + + m.GmmMessage.IdentityResponse = identityResponse + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetNotificationResponse(pDUSessionStatus []uint8) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeNotificationResponse) + + notificationResponse := nasMessage.NewNotificationResponse(0) + notificationResponse.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + notificationResponse.SetMessageType(nas.MsgTypeNotificationResponse) + notificationResponse.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + notificationResponse.PDUSessionStatus = new(nasType.PDUSessionStatus) + notificationResponse.PDUSessionStatus.SetIei(nasMessage.NotificationResponsePDUSessionStatusType) + notificationResponse.PDUSessionStatus.Buffer = pDUSessionStatus + + m.GmmMessage.NotificationResponse = notificationResponse + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetConfigurationUpdateComplete() []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeConfigurationUpdateComplete) + + configurationUpdateComplete := nasMessage.NewConfigurationUpdateComplete(0) + configurationUpdateComplete.SetExtendedProtocolDiscriminator(nasMessage.Epd5GSMobilityManagementMessage) + configurationUpdateComplete.SetSecurityHeaderType(0x00) + configurationUpdateComplete.SetSpareHalfOctet(0x00) + configurationUpdateComplete.SetMessageType(nas.MsgTypeConfigurationUpdateComplete) + + m.GmmMessage.ConfigurationUpdateComplete = configurationUpdateComplete + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetServiceRequest(serviceType uint8) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeServiceRequest) + + serviceRequest := nasMessage.NewServiceRequest(0) + serviceRequest.SetExtendedProtocolDiscriminator(nasMessage.Epd5GSMobilityManagementMessage) + serviceRequest.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + serviceRequest.SetMessageType(nas.MsgTypeServiceRequest) + serviceRequest.SetServiceTypeValue(serviceType) + serviceRequest.SetNasKeySetIdentifiler(0x01) + serviceRequest.SetAMFSetID(uint16(0xFE) << 2) + serviceRequest.SetAMFPointer(0) + serviceRequest.SetTMSI5G([4]uint8{0, 0, 0, 1}) + serviceRequest.TMSI5GS.SetLen(7) + switch serviceType { + case nasMessage.ServiceTypeMobileTerminatedServices: + serviceRequest.AllowedPDUSessionStatus = new(nasType.AllowedPDUSessionStatus) + serviceRequest.AllowedPDUSessionStatus.SetIei(nasMessage.ServiceRequestAllowedPDUSessionStatusType) + serviceRequest.AllowedPDUSessionStatus.SetLen(2) + serviceRequest.AllowedPDUSessionStatus.Buffer = []uint8{0x00, 0x08} + case nasMessage.ServiceTypeData: + serviceRequest.UplinkDataStatus = new(nasType.UplinkDataStatus) + serviceRequest.UplinkDataStatus.SetIei(nasMessage.ServiceRequestUplinkDataStatusType) + serviceRequest.UplinkDataStatus.SetLen(2) + serviceRequest.UplinkDataStatus.Buffer = []uint8{0x00, 0x04} + case nasMessage.ServiceTypeSignalling: + } + + m.GmmMessage.ServiceRequest = serviceRequest + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetAuthenticationResponse(authenticationResponseParam []uint8, eapMsg string) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeAuthenticationResponse) + + authenticationResponse := nasMessage.NewAuthenticationResponse(0) + authenticationResponse.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + authenticationResponse.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + authenticationResponse.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) + authenticationResponse.AuthenticationResponseMessageIdentity.SetMessageType(nas.MsgTypeAuthenticationResponse) + + if len(authenticationResponseParam) > 0 { + authenticationResponse.AuthenticationResponseParameter = nasType.NewAuthenticationResponseParameter( + nasMessage.AuthenticationResponseAuthenticationResponseParameterType) + authenticationResponse.AuthenticationResponseParameter.SetLen(uint8(len(authenticationResponseParam))) + copy(authenticationResponse.AuthenticationResponseParameter.Octet[:], authenticationResponseParam[0:16]) + } else if eapMsg != "" { + rawEapMsg, err := base64.StdEncoding.DecodeString(eapMsg) + if err != nil { + logger.NasMsgLog.Warnf("EAP decode error: %+v", err) + } + authenticationResponse.EAPMessage = nasType.NewEAPMessage(nasMessage.AuthenticationResponseEAPMessageType) + authenticationResponse.EAPMessage.SetLen(uint16(len(rawEapMsg))) + authenticationResponse.EAPMessage.SetEAPMessage(rawEapMsg) + } + + m.GmmMessage.AuthenticationResponse = authenticationResponse + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetAuthenticationFailure(cause5GMM uint8, authenticationFailureParam []uint8) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeAuthenticationFailure) + + authenticationFailure := nasMessage.NewAuthenticationFailure(0) + authenticationFailure.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + authenticationFailure.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + authenticationFailure.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) + authenticationFailure.AuthenticationFailureMessageIdentity.SetMessageType(nas.MsgTypeAuthenticationFailure) + authenticationFailure.Cause5GMM.SetCauseValue(cause5GMM) + + if cause5GMM == nasMessage.Cause5GMMSynchFailure { + authenticationFailure.AuthenticationFailureParameter = nasType.NewAuthenticationFailureParameter( + nasMessage.AuthenticationFailureAuthenticationFailureParameterType) + authenticationFailure.AuthenticationFailureParameter.SetLen(uint8(len(authenticationFailureParam))) + copy(authenticationFailure.AuthenticationFailureParameter.Octet[:], authenticationFailureParam) + } + + m.GmmMessage.AuthenticationFailure = authenticationFailure + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetRegistrationComplete(sorTransparentContainer []uint8) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeRegistrationComplete) + + registrationComplete := nasMessage.NewRegistrationComplete(0) + registrationComplete.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + registrationComplete.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + registrationComplete.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) + registrationComplete.RegistrationCompleteMessageIdentity.SetMessageType(nas.MsgTypeRegistrationComplete) + + if sorTransparentContainer != nil { + registrationComplete.SORTransparentContainer = nasType.NewSORTransparentContainer( + nasMessage.RegistrationCompleteSORTransparentContainerType) + registrationComplete.SORTransparentContainer.SetLen(uint16(len(sorTransparentContainer))) + registrationComplete.SORTransparentContainer.SetSORContent(sorTransparentContainer) + } + + m.GmmMessage.RegistrationComplete = registrationComplete + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +// TS 24.501 8.2.26. +func GetSecurityModeComplete(nasMessageContainer []uint8) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeSecurityModeComplete) + + securityModeComplete := nasMessage.NewSecurityModeComplete(0) + securityModeComplete.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + // TODO: modify security header type if need security protected + securityModeComplete.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + securityModeComplete.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) + securityModeComplete.SecurityModeCompleteMessageIdentity.SetMessageType(nas.MsgTypeSecurityModeComplete) + + securityModeComplete.IMEISV = nasType.NewIMEISV(nasMessage.SecurityModeCompleteIMEISVType) + securityModeComplete.IMEISV.SetLen(9) + securityModeComplete.SetOddEvenIdic(0) + securityModeComplete.SetTypeOfIdentity(nasMessage.MobileIdentity5GSTypeImeisv) + securityModeComplete.SetIdentityDigit1(1) + securityModeComplete.SetIdentityDigitP_1(1) + securityModeComplete.SetIdentityDigitP(1) + + if nasMessageContainer != nil { + securityModeComplete.NASMessageContainer = nasType.NewNASMessageContainer( + nasMessage.SecurityModeCompleteNASMessageContainerType) + securityModeComplete.NASMessageContainer.SetLen(uint16(len(nasMessageContainer))) + securityModeComplete.NASMessageContainer.SetNASMessageContainerContents(nasMessageContainer) + } + + m.GmmMessage.SecurityModeComplete = securityModeComplete + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetSecurityModeReject(cause5GMM uint8) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeSecurityModeReject) + + securityModeReject := nasMessage.NewSecurityModeReject(0) + securityModeReject.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + securityModeReject.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + securityModeReject.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) + securityModeReject.SecurityModeRejectMessageIdentity.SetMessageType(nas.MsgTypeSecurityModeReject) + + securityModeReject.Cause5GMM.SetCauseValue(cause5GMM) + + m.GmmMessage.SecurityModeReject = securityModeReject + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetDeregistrationRequest(accessType uint8, switchOff uint8, ngKsi uint8, + mobileIdentity5GS nasType.MobileIdentity5GS, +) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeDeregistrationRequestUEOriginatingDeregistration) + + deregistrationRequest := nasMessage.NewDeregistrationRequestUEOriginatingDeregistration(0) + deregistrationRequest.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + deregistrationRequest.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + deregistrationRequest.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) + deregistrationRequest.DeregistrationRequestMessageIdentity.SetMessageType( + nas.MsgTypeDeregistrationRequestUEOriginatingDeregistration) + + deregistrationRequest.NgksiAndDeregistrationType.SetAccessType(accessType) + deregistrationRequest.NgksiAndDeregistrationType.SetSwitchOff(switchOff) + deregistrationRequest.NgksiAndDeregistrationType.SetReRegistrationRequired(0) + deregistrationRequest.NgksiAndDeregistrationType.SetTSC(ngKsi) + deregistrationRequest.NgksiAndDeregistrationType.SetNasKeySetIdentifiler(ngKsi) + deregistrationRequest.MobileIdentity5GS.SetLen(mobileIdentity5GS.GetLen()) + deregistrationRequest.MobileIdentity5GS.SetMobileIdentity5GSContents(mobileIdentity5GS.GetMobileIdentity5GSContents()) + + m.GmmMessage.DeregistrationRequestUEOriginatingDeregistration = deregistrationRequest + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetDeregistrationAccept() []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeDeregistrationAcceptUETerminatedDeregistration) + + deregistrationAccept := nasMessage.NewDeregistrationAcceptUETerminatedDeregistration(0) + deregistrationAccept.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + deregistrationAccept.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + deregistrationAccept.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) + deregistrationAccept.DeregistrationAcceptMessageIdentity.SetMessageType( + nas.MsgTypeDeregistrationAcceptUETerminatedDeregistration) + + m.GmmMessage.DeregistrationAcceptUETerminatedDeregistration = deregistrationAccept + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetStatus5GMM(cause uint8) []byte { + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeStatus5GMM) + + status5GMM := nasMessage.NewStatus5GMM(0) + status5GMM.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator(nasMessage.Epd5GSMobilityManagementMessage) + status5GMM.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + status5GMM.SpareHalfOctetAndSecurityHeaderType.SetSpareHalfOctet(0) + status5GMM.STATUSMessageIdentity5GMM.SetMessageType(nas.MsgTypeStatus5GMM) + status5GMM.Cause5GMM.SetCauseValue(cause) + + m.GmmMessage.Status5GMM = status5GMM + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetStatus5GSM(pduSessionId uint8, cause uint8) []byte { + m := nas.NewMessage() + m.GsmMessage = nas.NewGsmMessage() + m.GsmHeader.SetMessageType(nas.MsgTypeStatus5GSM) + + status5GSM := nasMessage.NewStatus5GSM(0) + status5GSM.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator(nasMessage.Epd5GSSessionManagementMessage) + status5GSM.STATUSMessageIdentity5GSM.SetMessageType(nas.MsgTypeStatus5GSM) + status5GSM.PDUSessionID.SetPDUSessionID(pduSessionId) + status5GSM.PTI.SetPTI(0x00) + status5GSM.Cause5GSM.SetCauseValue(cause) + + m.GsmMessage.Status5GSM = status5GSM + + data := new(bytes.Buffer) + err := m.GsmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetUlNasTransport_Status5GSM(pduSessionId uint8, cause uint8) []byte { + payload := GetStatus5GSM(pduSessionId, cause) + + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeULNASTransport) + + ulNasTransport := nasMessage.NewULNASTransport(0) + ulNasTransport.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + ulNasTransport.SetMessageType(nas.MsgTypeULNASTransport) + ulNasTransport.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + ulNasTransport.PduSessionID2Value = new(nasType.PduSessionID2Value) + ulNasTransport.PduSessionID2Value.SetIei(nasMessage.ULNASTransportPduSessionID2ValueType) + ulNasTransport.PduSessionID2Value.SetPduSessionID2Value(pduSessionId) + + ulNasTransport.SpareHalfOctetAndPayloadContainerType.SetPayloadContainerType(nasMessage.PayloadContainerTypeN1SMInfo) + ulNasTransport.PayloadContainer.SetLen(uint16(len(payload))) + ulNasTransport.PayloadContainer.SetPayloadContainerContents(payload) + + m.GmmMessage.ULNASTransport = ulNasTransport + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetUlNasTransport_PduSessionReleaseRequest(pduSessionId uint8) []byte { + pduSessionReleaseRequest := GetPduSessionReleaseRequest(pduSessionId) + + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeULNASTransport) + + ulNasTransport := nasMessage.NewULNASTransport(0) + ulNasTransport.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + ulNasTransport.SetMessageType(nas.MsgTypeULNASTransport) + ulNasTransport.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + ulNasTransport.PduSessionID2Value = new(nasType.PduSessionID2Value) + ulNasTransport.PduSessionID2Value.SetIei(nasMessage.ULNASTransportPduSessionID2ValueType) + ulNasTransport.PduSessionID2Value.SetPduSessionID2Value(pduSessionId) + + ulNasTransport.SpareHalfOctetAndPayloadContainerType.SetPayloadContainerType(nasMessage.PayloadContainerTypeN1SMInfo) + ulNasTransport.PayloadContainer.SetLen(uint16(len(pduSessionReleaseRequest))) + ulNasTransport.PayloadContainer.SetPayloadContainerContents(pduSessionReleaseRequest) + + m.GmmMessage.ULNASTransport = ulNasTransport + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} + +func GetUlNasTransport_PduSessionReleaseComplete(pduSessionId uint8, requestType uint8, dnn string, + sNssai *models.Snssai, +) []byte { + pduSessionReleaseRequest := GetPduSessionReleaseComplete(pduSessionId) + + m := nas.NewMessage() + m.GmmMessage = nas.NewGmmMessage() + m.GmmHeader.SetMessageType(nas.MsgTypeULNASTransport) + + ulNasTransport := nasMessage.NewULNASTransport(0) + ulNasTransport.SpareHalfOctetAndSecurityHeaderType.SetSecurityHeaderType(nas.SecurityHeaderTypePlainNas) + ulNasTransport.SetMessageType(nas.MsgTypeULNASTransport) + ulNasTransport.ExtendedProtocolDiscriminator.SetExtendedProtocolDiscriminator( + nasMessage.Epd5GSMobilityManagementMessage) + ulNasTransport.PduSessionID2Value = new(nasType.PduSessionID2Value) + ulNasTransport.PduSessionID2Value.SetIei(nasMessage.ULNASTransportPduSessionID2ValueType) + ulNasTransport.PduSessionID2Value.SetPduSessionID2Value(pduSessionId) + ulNasTransport.RequestType = new(nasType.RequestType) + ulNasTransport.RequestType.SetIei(nasMessage.ULNASTransportRequestTypeType) + ulNasTransport.RequestType.SetRequestTypeValue(requestType) + if dnn != "" { + ulNasTransport.DNN = new(nasType.DNN) + ulNasTransport.DNN.SetIei(nasMessage.ULNASTransportDNNType) + ulNasTransport.DNN.SetDNN(dnn) + } + if sNssai != nil { + var sdTemp [3]uint8 + sd, err := hex.DecodeString(sNssai.Sd) + if err != nil { + logger.NasMsgLog.Warnf("sNssai SD decode error: %+v", err) + } + copy(sdTemp[:], sd) + ulNasTransport.SNSSAI = nasType.NewSNSSAI(nasMessage.ULNASTransportSNSSAIType) + ulNasTransport.SNSSAI.SetLen(4) + ulNasTransport.SNSSAI.SetSST(uint8(sNssai.Sst)) + ulNasTransport.SNSSAI.SetSD(sdTemp) + } + + ulNasTransport.SpareHalfOctetAndPayloadContainerType.SetPayloadContainerType(nasMessage.PayloadContainerTypeN1SMInfo) + ulNasTransport.PayloadContainer.SetLen(uint16(len(pduSessionReleaseRequest))) + ulNasTransport.PayloadContainer.SetPayloadContainerContents(pduSessionReleaseRequest) + + m.GmmMessage.ULNASTransport = ulNasTransport + + data := new(bytes.Buffer) + err := m.GmmMessageEncode(data) + if err != nil { + fmt.Println(err.Error()) + } + + return data.Bytes() +} diff --git a/internal/ngap/dispatcher.go b/internal/ngap/dispatcher.go index 6dfbb878..8cd415d1 100644 --- a/internal/ngap/dispatcher.go +++ b/internal/ngap/dispatcher.go @@ -12,7 +12,7 @@ import ( func Dispatch(conn net.Conn, msg []byte) { var ran *context.AmfRan - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ran, ok := amfSelf.AmfRanFindByConn(conn) if !ok { @@ -46,7 +46,7 @@ func Dispatch(conn net.Conn, msg []byte) { } func HandleSCTPNotification(conn net.Conn, notification sctp.Notification) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() logger.NgapLog.Infof("Handle SCTP Notification[addr: %+v]", conn.RemoteAddr()) @@ -77,3 +77,16 @@ func HandleSCTPNotification(conn net.Conn, notification sctp.Notification) { ran.Log.Warnf("Non handled notification type: 0x%x", notification.Type()) } } + +func HandleSCTPConnError(conn net.Conn) { + amfSelf := context.GetSelf() + + logger.NgapLog.Infof("Handle SCTP Connection Error[addr: %+v] - remove RAN", conn.RemoteAddr()) + + ran, ok := amfSelf.AmfRanFindByConn(conn) + if !ok { + logger.NgapLog.Warnf("RAN context has been removed[addr: %+v]", conn.RemoteAddr()) + return + } + ran.Remove() +} diff --git a/internal/ngap/handler.go b/internal/ngap/handler.go index 12063b9c..f8ccd9fb 100644 --- a/internal/ngap/handler.go +++ b/internal/ngap/handler.go @@ -8,10 +8,13 @@ import ( "github.com/free5gc/amf/internal/context" gmm_common "github.com/free5gc/amf/internal/gmm/common" gmm_message "github.com/free5gc/amf/internal/gmm/message" - "github.com/free5gc/amf/internal/nas" + amf_nas "github.com/free5gc/amf/internal/nas" + "github.com/free5gc/amf/internal/nas/nas_security" ngap_message "github.com/free5gc/amf/internal/ngap/message" "github.com/free5gc/amf/internal/sbi/consumer" + "github.com/free5gc/amf/pkg/factory" "github.com/free5gc/aper" + "github.com/free5gc/nas" "github.com/free5gc/nas/nasMessage" libngap "github.com/free5gc/ngap" "github.com/free5gc/ngap/ngapConvert" @@ -72,7 +75,7 @@ func handleNGSetupRequestMain(ran *context.AmfRan, } else { var found bool for i, tai := range ran.SupportedTAList { - if context.InTaiList(tai.Tai, context.AMF_Self().SupportTaiLists) { + if context.InTaiList(tai.Tai, context.GetSelf().SupportTaiLists) { ran.Log.Tracef("SERVED_TAI_INDEX[%d]", i) found = true break @@ -114,7 +117,7 @@ func handleUplinkNASTransportMain(ran *context.AmfRan, ranUe.UpdateLocation(userLocationInformation) } - nas.HandleNAS(ranUe, ngapType.ProcedureCodeUplinkNASTransport, nASPDU.Value, false) + amf_nas.HandleNAS(ranUe, ngapType.ProcedureCodeUplinkNASTransport, nASPDU.Value, false) } func handleNGResetMain(ran *context.AmfRan, @@ -128,7 +131,7 @@ func handleNGResetMain(ran *context.AmfRan, switch resetType.Present { case ngapType.ResetTypePresentNGInterface: ran.Log.Trace("ResetType Present: NG Interface") - ran.RemoveAllUeInRan() + ran.RemoveAllRanUe(false) ngap_message.SendNGResetAcknowledge(ran, nil, nil) case ngapType.ResetTypePresentPartOfNGInterface: ran.Log.Trace("ResetType Present: Part of NG Interface") @@ -144,12 +147,7 @@ func handleNGResetMain(ran *context.AmfRan, for _, ueAssociatedLogicalNGConnectionItem := range partOfNGInterface.List { if ueAssociatedLogicalNGConnectionItem.AMFUENGAPID != nil { ran.Log.Tracef("AmfUeNgapID[%d]", ueAssociatedLogicalNGConnectionItem.AMFUENGAPID.Value) - for _, ue := range ran.RanUeList { - if ue.AmfUeNgapId == ueAssociatedLogicalNGConnectionItem.AMFUENGAPID.Value { - ranUe = ue - break - } - } + ranUe = ran.FindRanUeByAmfUeNgapID(ueAssociatedLogicalNGConnectionItem.AMFUENGAPID.Value) } else if ueAssociatedLogicalNGConnectionItem.RANUENGAPID != nil { ran.Log.Tracef("RanUeNgapID[%d]", ueAssociatedLogicalNGConnectionItem.RANUENGAPID.Value) ranUe = ran.RanUeFindByRanUeNgapID(ueAssociatedLogicalNGConnectionItem.RANUENGAPID.Value) @@ -283,13 +281,14 @@ func handleUEContextReleaseCompleteMain(ran *context.AmfRan, cause = *tmp } if amfUe.State[ran.AnType].Is(context.Registered) { - ranUe.Log.Info("Rel Ue Context in GMM-Registered") + ranUe.Log.Info("Release Ue Context in GMM-Registered") + // If this release cause by handover, no needs deactivate CN tunnel if cause.NgapCause != nil && pDUSessionResourceList != nil { for _, pduSessionReourceItem := range pDUSessionResourceList.List { pduSessionID := int32(pduSessionReourceItem.PDUSessionID.Value) smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) + ranUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) // TODO: Check if doing error handling here continue } @@ -303,23 +302,26 @@ func handleUEContextReleaseCompleteMain(ran *context.AmfRan, } } + // TODO: stop timer and release RanUe context // Remove UE N2 Connection delete(amfUe.ReleaseCause, ran.AnType) switch ranUe.ReleaseAction { case context.UeContextN2NormalRelease: - ranUe.Log.Infof("Release UE[%s] Context : N2 Connection Release", amfUe.Supi) + ran.Log.Infof("Release UE[%s] Context : N2 Connection Release", amfUe.Supi) // amfUe.DetachRanUe(ran.AnType) err := ranUe.Remove() if err != nil { ran.Log.Errorln(err.Error()) } case context.UeContextReleaseUeContext: - ranUe.Log.Infof("Release UE[%s] Context : Release Ue Context", amfUe.Supi) - gmm_common.RemoveAmfUe(amfUe) + ran.Log.Infof("Release UE[%s] Context : Release Ue Context", amfUe.Supi) + amfUe.Lock.Lock() + gmm_common.RemoveAmfUe(amfUe, false) + amfUe.Lock.Unlock() case context.UeContextReleaseHandover: - ranUe.Log.Infof("Release UE[%s] Context : Release for Handover", amfUe.Supi) + ran.Log.Infof("Release UE[%s] Context : Release for Handover", amfUe.Supi) // TODO: it's a workaround, need to fix it. - targetRanUe := context.AMF_Self().RanUeFindByAmfUeNgapID(ranUe.TargetUe.AmfUeNgapId) + targetRanUe := context.GetSelf().RanUeFindByAmfUeNgapID(ranUe.TargetUe.AmfUeNgapId) context.DetachSourceUeTargetUe(ranUe) err := ranUe.Remove() @@ -358,16 +360,19 @@ func handlePDUSessionResourceReleaseResponseMain(ran *context.AmfRan, return } if pDUSessionResourceReleasedList != nil { - ranUe.Log.Trace("Send PDUSessionResourceReleaseResponseTransfer to SMF") + ranUe.Log.Infof("Send PDUSessionResourceReleaseResponseTransfer to SMF") for _, item := range pDUSessionResourceReleasedList.List { pduSessionID := int32(item.PDUSessionID.Value) transfer := item.PDUSessionResourceReleaseResponseTransfer smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if NAS (PDU Session Release Complete) comes before PDUSesstionResourceRelease + ranUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here continue } + _, responseErr, problemDetail, err := consumer.SendUpdateSmContextN2Info(amfUe, smContext, models.N2SmInfoType_PDU_RES_REL_RSP, transfer) // TODO: error handling @@ -414,8 +419,6 @@ func handleInitialUEMessageMain(ran *context.AmfRan, uEContextRequest *ngapType.UEContextRequest, allowedNSSAI *ngapType.AllowedNSSAI, ) { - amfSelf := context.AMF_Self() - ranUe := ran.RanUeFindByRanUeNgapID(rANUENGAPID.Value) if ranUe != nil { amfUe := ranUe.AmfUe @@ -440,30 +443,79 @@ func handleInitialUEMessageMain(ran *context.AmfRan, } ran.Log.Debugf("New RanUe [RanUeNgapID: %d]", ranUe.RanUeNgapId) - if fiveGSTMSI != nil { - ranUe.Log.Debug("Receive 5G-S-TMSI") - - servedGuami := amfSelf.ServedGuamiList[0] + // Try to get identity from 5G-S-TMSI IE first; if not available, try to get identity from the plain NAS. + var id, idType string + var gmmMessage *nas.GmmMessage + var nasMsgType, regReqType uint8 + // Get nasMsgType to send corresponding NAS reject to UE when amfUe is not found. + nasMsg, err := nas_security.DecodePlainNasNoIntegrityCheck(nASPDU.Value) + if err == nil && nasMsg.GmmMessage != nil { + gmmMessage = nasMsg.GmmMessage + nasMsgType = gmmMessage.GmmHeader.GetMessageType() + if gmmMessage.RegistrationRequest != nil { + regReqType = gmmMessage.RegistrationRequest.NgksiAndRegistrationType5GS.GetRegistrationType5GS() + } + } + if fiveGSTMSI != nil { // <5G-S-TMSI> := <5G-TMSI> // GUAMI := // 5G-GUTI := <5G-TMSI> - tmpReginID, _, _ := ngapConvert.AmfIdToNgap(servedGuami.AmfId) - amfID := ngapConvert.AmfIdToModels(tmpReginID, fiveGSTMSI.AMFSetID.Value, fiveGSTMSI.AMFPointer.Value) + amfSetPtrID := hex.EncodeToString([]byte{ + fiveGSTMSI.AMFSetID.Value.Bytes[0], + (fiveGSTMSI.AMFSetID.Value.Bytes[1] & 0xc0) | (fiveGSTMSI.AMFPointer.Value.Bytes[0] >> 2), + }) tmsi := hex.EncodeToString(fiveGSTMSI.FiveGTMSI.Value) - guti := servedGuami.PlmnId.Mcc + servedGuami.PlmnId.Mnc + amfID + tmsi + id = amfSetPtrID + tmsi + idType = "5G-S-TMSI" + ranUe.Log.Infof("Find 5G-S-TMSI [%q] in InitialUEMessage", id) + } else if regReqType == nasMessage.RegistrationType5GSInitialRegistration { + // NGAP 5G-S-TMSI IE might not be present in InitialUEMessage carrying Initial Registration. + // Need to get 5GSMobileIdentity from Initial Registration. + + id, idType, err = amf_nas.GetNas5GSMobileIdentity(gmmMessage) + ran.Log.Infof("5GSMobileIdentity [%q:%q, err: %v]", idType, id, err) + } else { + // Missing NGAP 5G-S-TMSI IE + var iesCriticalityDiagnostics ngapType.CriticalityDiagnosticsIEList + ranUe.Log.Warnf("Missing 5G-S-TMSI IE in InitialUEMessage; send ErrorIndication") + item := buildCriticalityDiagnosticsIEItem(ngapType.CriticalityPresentReject, + ngapType.ProtocolIEIDFiveGSTMSI, ngapType.TypeOfErrorPresentMissing) + iesCriticalityDiagnostics.List = append(iesCriticalityDiagnostics.List, item) + sendErrorMessage(ran, nil, rANUENGAPID, iesCriticalityDiagnostics) + + ngap_message.SendUEContextReleaseCommand(ranUe, context.UeContextN2NormalRelease, + ngapType.CausePresentProtocol, ngapType.CauseProtocolPresentUnspecified) + return + } + + // If id type is GUTI, since MAC can't be checked here (no amfUe context), the GUTI may not direct to the right amfUe. + // In this case, create a new amfUe to handle the following registration procedure. + var isInvalidGUTI bool = (idType == "5G-GUTI") + amfUe, ok := findAmfUe(ran, id, idType) + if ok && !isInvalidGUTI { // TODO: invoke Namf_Communication_UEContextTransfer if serving AMF has changed since // last Registration Request procedure // Described in TS 23.502 4.2.2.2.2 step 4 (without UDSF deployment) - - if amfUe, ok := amfSelf.AmfUeFindByGuti(guti); !ok { - ranUe.Log.Warnf("Unknown UE [GUTI: %s]", guti) - } else { - ranUe.Log.Tracef("find AmfUe [GUTI: %s]", guti) - ranUe.Log.Debugf("AmfUe Attach RanUe [RanUeNgapID: %d]", ranUe.RanUeNgapId) - gmm_common.AttachRanUeToAmfUeAndReleaseOldIfAny(amfUe, ranUe) - } + ranUe.Log.Infof("find AmfUe [%q:%q]", idType, id) + ranUe.Log.Debugf("AmfUe Attach RanUe [RanUeNgapID: %d]", ranUe.RanUeNgapId) + gmm_common.AttachRanUeToAmfUeAndReleaseOldIfAny(amfUe, ranUe) + } else if regReqType != nasMessage.RegistrationType5GSInitialRegistration { + if regReqType == nasMessage.RegistrationType5GSPeriodicRegistrationUpdating || + regReqType == nasMessage.RegistrationType5GSMobilityRegistrationUpdating { + gmm_message.SendRegistrationReject( + ranUe, nasMessage.Cause5GMMImplicitlyDeregistered, "") + ranUe.Log.Warn("Send RegistrationReject [Cause5GMMImplicitlyDeregistered]") + } else if nasMsgType == nas.MsgTypeServiceRequest { + gmm_message.SendServiceReject( + ranUe, nil, nasMessage.Cause5GMMImplicitlyDeregistered) + ranUe.Log.Warn("Send ServiceReject [Cause5GMMImplicitlyDeregistered]") + } + + ngap_message.SendUEContextReleaseCommand(ranUe, context.UeContextN2NormalRelease, + ngapType.CausePresentNas, ngapType.CauseNasPresentNormalRelease) + return } if userLocationInformation != nil { @@ -480,7 +532,7 @@ func handleInitialUEMessageMain(ran *context.AmfRan, ranUe.UeContextRequest = true // TODO: Trigger Initial Context Setup procedure } else { - ranUe.UeContextRequest = false + ranUe.UeContextRequest = factory.AmfConfig.Configuration.DefaultUECtxReq } // TS 23.502 4.2.2.2.3 step 6a Nnrf_NFDiscovery_Request (NF type, AMF Set) @@ -500,7 +552,46 @@ func handleInitialUEMessageMain(ran *context.AmfRan, ran.Log.Errorf("libngap Encoder Error: %+v", err) } ranUe.InitialUEMessage = pdu - nas.HandleNAS(ranUe, ngapType.ProcedureCodeInitialUEMessage, nASPDU.Value, true) + amf_nas.HandleNAS(ranUe, ngapType.ProcedureCodeInitialUEMessage, nASPDU.Value, true) +} + +func findAmfUe(ran *context.AmfRan, id, idType string) (*context.AmfUe, bool) { + var amfUe *context.AmfUe + var ok bool + + amfSelf := context.GetSelf() + servedGuami := amfSelf.ServedGuamiList[0] + tmpRegionID, _, _ := ngapConvert.AmfIdToNgap(servedGuami.AmfId) + + switch idType { + case "SUPI": + ran.Log.Debugf("SUPI %s", id) + amfUe, ok = amfSelf.AmfUeFindBySupi(id) + case "SUCI": + ran.Log.Debugf("SUCI %s", id) + amfUe, ok = amfSelf.AmfUeFindBySuci(id) + case "5G-GUTI": + ran.Log.Debugf("5G-GUTI %s", id) + amfUe, ok = amfSelf.AmfUeFindByGuti(id) + case "5G-S-TMSI": + id = servedGuami.PlmnId.Mcc + servedGuami.PlmnId.Mnc + ngapConvert.BitStringToHex(&tmpRegionID) + id + ran.Log.Debugf("5G-S-TMSI %s", id) + amfUe, ok = amfSelf.AmfUeFindByGuti(id) + } + return amfUe, ok +} + +func sendErrorMessage(ran *context.AmfRan, amfUeNgapId *ngapType.AMFUENGAPID, ranUeNgapId *ngapType.RANUENGAPID, + iesCriticalityDiagnostics ngapType.CriticalityDiagnosticsIEList, +) { + ran.Log.Trace("Has missing reject IE(s)") + + procedureCode := ngapType.ProcedureCodeInitialUEMessage + triggeringMessage := ngapType.TriggeringMessagePresentInitiatingMessage + procedureCriticality := ngapType.CriticalityPresentIgnore + criticalityDiagnostics := buildCriticalityDiagnostics(&procedureCode, &triggeringMessage, &procedureCriticality, + &iesCriticalityDiagnostics) + ngap_message.SendErrorIndication(ran, amfUeNgapId, ranUeNgapId, nil, &criticalityDiagnostics) } func handlePDUSessionResourceSetupResponseMain(ran *context.AmfRan, @@ -667,117 +758,120 @@ func handlePDUSessionResourceNotifyMain(ran *context.AmfRan, ranUe.UpdateLocation(userLocationInformation) } - ranUe.Log.Trace("Send PDUSessionResourceNotifyTransfer to SMF") - if pDUSessionResourceNotifyList != nil { - for _, item := range pDUSessionResourceNotifyList.List { - pduSessionID := int32(item.PDUSessionID.Value) - transfer := item.PDUSessionResourceNotifyTransfer - smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) - if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) - continue - } - response, errResponse, problemDetail, err := consumer.SendUpdateSmContextN2Info(amfUe, smContext, - models.N2SmInfoType_PDU_RES_NTY, transfer) - if err != nil { - ranUe.Log.Errorf("SendUpdateSmContextN2Info[PDUSessionResourceNotifyTransfer] Error: %+v", err) - } - - if response != nil { - responseData := response.JsonData - n2Info := response.BinaryDataN1SmMessage - n1Msg := response.BinaryDataN2SmInformation - if n2Info != nil { - switch responseData.N2SmInfoType { - case models.N2SmInfoType_PDU_RES_MOD_REQ: - ranUe.Log.Debugln("AMF Transfer NGAP PDU Resource Modify Req from SMF") - var nasPdu []byte - if n1Msg != nil { - pduSessionId := uint8(pduSessionID) - nasPdu, err = gmm_message.BuildDLNASTransport(amfUe, ran.AnType, nasMessage.PayloadContainerTypeN1SMInfo, - n1Msg, pduSessionId, nil, nil, 0) - if err != nil { - ranUe.Log.Warnf("GMM Message build DL NAS Transport filaed: %v", err) + ranUe.Log.Infof("Send PDUSessionResourceNotifyTransfer to SMF") + + if pDUSessionResourceNotifyList != nil { + for _, item := range pDUSessionResourceNotifyList.List { + pduSessionID := int32(item.PDUSessionID.Value) + transfer := item.PDUSessionResourceNotifyTransfer + smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) + if !ok { + ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) + continue + } + response, errResponse, problemDetail, err := consumer.SendUpdateSmContextN2Info(amfUe, smContext, + models.N2SmInfoType_PDU_RES_NTY, transfer) + if err != nil { + ranUe.Log.Errorf("SendUpdateSmContextN2Info[PDUSessionResourceNotifyTransfer] Error: %+v", err) + } + + if response != nil { + responseData := response.JsonData + n2Info := response.BinaryDataN1SmMessage + n1Msg := response.BinaryDataN2SmInformation + if n2Info != nil { + switch responseData.N2SmInfoType { + case models.N2SmInfoType_PDU_RES_MOD_REQ: + ranUe.Log.Debugln("AMF Transfer NGAP PDU Resource Modify Req from SMF") + var nasPdu []byte + if n1Msg != nil { + pduSessionId := uint8(pduSessionID) + nasPdu, err = gmm_message.BuildDLNASTransport(amfUe, ran.AnType, nasMessage.PayloadContainerTypeN1SMInfo, + n1Msg, pduSessionId, nil, nil, 0) + if err != nil { + ranUe.Log.Warnf("GMM Message build DL NAS Transport filaed: %v", err) + } } + list := ngapType.PDUSessionResourceModifyListModReq{} + ngap_message.AppendPDUSessionResourceModifyListModReq(&list, pduSessionID, nasPdu, n2Info) + ngap_message.SendPDUSessionResourceModifyRequest(ranUe, list) } - list := ngapType.PDUSessionResourceModifyListModReq{} - ngap_message.AppendPDUSessionResourceModifyListModReq(&list, pduSessionID, nasPdu, n2Info) - ngap_message.SendPDUSessionResourceModifyRequest(ranUe, list) } + } else if errResponse != nil { + errJSON := errResponse.JsonData + n1Msg := errResponse.BinaryDataN2SmInformation + ranUe.Log.Warnf("PDU Session Modification is rejected by SMF[pduSessionId:%d], Error[%s]\n", + pduSessionID, errJSON.Error.Cause) + if n1Msg != nil { + gmm_message.SendDLNASTransport( + ranUe, nasMessage.PayloadContainerTypeN1SMInfo, errResponse.BinaryDataN1SmMessage, pduSessionID, 0, nil, 0) + } + // TODO: handle n2 info transfer + } else if err != nil { + return + } else { + // TODO: error handling + ranUe.Log.Errorf("Failed to Update smContext[pduSessionID: %d], Error[%v]", pduSessionID, problemDetail) + return } - } else if errResponse != nil { - errJSON := errResponse.JsonData - n1Msg := errResponse.BinaryDataN2SmInformation - ranUe.Log.Warnf("PDU Session Modification is rejected by SMF[pduSessionId:%d], Error[%s]\n", - pduSessionID, errJSON.Error.Cause) - if n1Msg != nil { - gmm_message.SendDLNASTransport( - ranUe, nasMessage.PayloadContainerTypeN1SMInfo, errResponse.BinaryDataN1SmMessage, pduSessionID, 0, nil, 0) - } - // TODO: handle n2 info transfer - } else if err != nil { - return - } else { - // TODO: error handling - ranUe.Log.Errorf("Failed to Update smContext[pduSessionID: %d], Error[%v]", pduSessionID, problemDetail) - return } } - } - if pDUSessionResourceReleasedListNot != nil { - ranUe.Log.Trace("Send PDUSessionResourceNotifyReleasedTransfer to SMF") - for _, item := range pDUSessionResourceReleasedListNot.List { - pduSessionID := int32(item.PDUSessionID.Value) - transfer := item.PDUSessionResourceNotifyReleasedTransfer - smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) - if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) - continue - } - response, errResponse, problemDetail, err := consumer.SendUpdateSmContextN2Info(amfUe, smContext, - models.N2SmInfoType_PDU_RES_NTY_REL, transfer) - if err != nil { - ranUe.Log.Errorf("SendUpdateSmContextN2Info[PDUSessionResourceNotifyReleasedTransfer] Error: %+v", err) - } - if response != nil { - responseData := response.JsonData - n2Info := response.BinaryDataN1SmMessage - n1Msg := response.BinaryDataN2SmInformation - if n2Info != nil { - switch responseData.N2SmInfoType { - case models.N2SmInfoType_PDU_RES_REL_CMD: - ranUe.Log.Debugln("AMF Transfer NGAP PDU Session Resource Rel Co from SMF") - var nasPdu []byte - if n1Msg != nil { - pduSessionId := uint8(pduSessionID) - nasPdu, err = gmm_message.BuildDLNASTransport(amfUe, ran.AnType, - nasMessage.PayloadContainerTypeN1SMInfo, n1Msg, pduSessionId, nil, nil, 0) - if err != nil { - ranUe.Log.Warnf("GMM Message build DL NAS Transport filaed: %v", err) + if pDUSessionResourceReleasedListNot != nil { + ranUe.Log.Infof("Send PDUSessionResourceNotifyReleasedTransfer to SMF") + for _, item := range pDUSessionResourceReleasedListNot.List { + pduSessionID := int32(item.PDUSessionID.Value) + transfer := item.PDUSessionResourceNotifyReleasedTransfer + smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) + if !ok { + ranUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here + continue + } + response, errResponse, problemDetail, err := consumer.SendUpdateSmContextN2Info(amfUe, smContext, + models.N2SmInfoType_PDU_RES_NTY_REL, transfer) + if err != nil { + ranUe.Log.Errorf("SendUpdateSmContextN2Info[PDUSessionResourceNotifyReleasedTransfer] Error: %+v", err) + } + if response != nil { + responseData := response.JsonData + n2Info := response.BinaryDataN1SmMessage + n1Msg := response.BinaryDataN2SmInformation + if n2Info != nil { + switch responseData.N2SmInfoType { + case models.N2SmInfoType_PDU_RES_REL_CMD: + ranUe.Log.Debugln("AMF Transfer NGAP PDU Session Resource Rel Co from SMF") + var nasPdu []byte + if n1Msg != nil { + nasPdu, err = gmm_message.BuildDLNASTransport( + amfUe, ran.AnType, nasMessage.PayloadContainerTypeN1SMInfo, n1Msg, + uint8(pduSessionID), nil, nil, 0) + if err != nil { + ranUe.Log.Warnf("GMM Message build DL NAS Transport filaed: %v", err) + } } + list := ngapType.PDUSessionResourceToReleaseListRelCmd{} + ngap_message.AppendPDUSessionResourceToReleaseListRelCmd(&list, pduSessionID, n2Info) + ngap_message.SendPDUSessionResourceReleaseCommand(ranUe, nasPdu, list) } - list := ngapType.PDUSessionResourceToReleaseListRelCmd{} - ngap_message.AppendPDUSessionResourceToReleaseListRelCmd(&list, pduSessionID, n2Info) - ngap_message.SendPDUSessionResourceReleaseCommand(ranUe, nasPdu, list) } + } else if errResponse != nil { + errJSON := errResponse.JsonData + n1Msg := errResponse.BinaryDataN2SmInformation + ranUe.Log.Warnf("PDU Session Release is rejected by SMF[pduSessionID:%d], Error[%s]\n", + pduSessionID, errJSON.Error.Cause) + if n1Msg != nil { + gmm_message.SendDLNASTransport( + ranUe, nasMessage.PayloadContainerTypeN1SMInfo, errResponse.BinaryDataN1SmMessage, pduSessionID, 0, nil, 0) + } + } else if err != nil { + return + } else { + // TODO: error handling + ranUe.Log.Errorf("Failed to Update smContext[pduSessionID: %d], Error[%v]", pduSessionID, problemDetail) + return } - } else if errResponse != nil { - errJSON := errResponse.JsonData - n1Msg := errResponse.BinaryDataN2SmInformation - ranUe.Log.Warnf("PDU Session Release is rejected by SMF[pduSessionId:%d], Error[%s]\n", - pduSessionID, errJSON.Error.Cause) - if n1Msg != nil { - gmm_message.SendDLNASTransport( - ranUe, nasMessage.PayloadContainerTypeN1SMInfo, errResponse.BinaryDataN1SmMessage, pduSessionID, 0, nil, 0) - } - } else if err != nil { - return - } else { - // TODO: error handling - ranUe.Log.Errorf("Failed to Update smContext[pduSessionID: %d], Error[%v]", pduSessionID, problemDetail) - return } } } @@ -796,28 +890,33 @@ func handlePDUSessionResourceModifyIndicationMain(ran *context.AmfRan, pduSessionResourceModifyListModCfm := ngapType.PDUSessionResourceModifyListModCfm{} pduSessionResourceFailedToModifyListModCfm := ngapType.PDUSessionResourceFailedToModifyListModCfm{} - ran.Log.Trace("Send PDUSessionResourceModifyIndicationTransfer to SMF") - for _, item := range pduSessionResourceModifyIndicationList.List { - pduSessionID := int32(item.PDUSessionID.Value) - transfer := item.PDUSessionResourceModifyIndicationTransfer - smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) - if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) - continue - } - response, errResponse, _, err := consumer.SendUpdateSmContextN2Info(amfUe, smContext, - models.N2SmInfoType_PDU_RES_MOD_IND, transfer) - if err != nil { - ran.Log.Errorf("SendUpdateSmContextN2Info Error:\n%s", err.Error()) - } + if pduSessionResourceModifyIndicationList != nil { + ran.Log.Infof("Send PDUSessionResourceModifyIndicationTransfer to SMF") + for _, item := range pduSessionResourceModifyIndicationList.List { + pduSessionID := int32(item.PDUSessionID.Value) + transfer := item.PDUSessionResourceModifyIndicationTransfer + smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) + if !ok { + ranUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here + continue + } + response, errResponse, _, err := consumer.SendUpdateSmContextN2Info(amfUe, smContext, + models.N2SmInfoType_PDU_RES_MOD_IND, transfer) + if err != nil { + ran.Log.Errorf("SendUpdateSmContextN2Info Error:\n%s", err.Error()) + } - if response != nil && response.BinaryDataN2SmInformation != nil { - ngap_message.AppendPDUSessionResourceModifyListModCfm(&pduSessionResourceModifyListModCfm, int64(pduSessionID), - response.BinaryDataN2SmInformation) - } - if errResponse != nil && errResponse.BinaryDataN2SmInformation != nil { - ngap_message.AppendPDUSessionResourceFailedToModifyListModCfm(&pduSessionResourceFailedToModifyListModCfm, - int64(pduSessionID), errResponse.BinaryDataN2SmInformation) + if response != nil && response.BinaryDataN2SmInformation != nil { + ngap_message.AppendPDUSessionResourceModifyListModCfm( + &pduSessionResourceModifyListModCfm, + int64(pduSessionID), response.BinaryDataN2SmInformation) + } + if errResponse != nil && errResponse.BinaryDataN2SmInformation != nil { + ngap_message.AppendPDUSessionResourceFailedToModifyListModCfm( + &pduSessionResourceFailedToModifyListModCfm, + int64(pduSessionID), errResponse.BinaryDataN2SmInformation) + } } } @@ -842,15 +941,19 @@ func handleInitialContextSetupResponseMain(ran *context.AmfRan, return } + ran.Log.Tracef("RanUeNgapID[%d] AmfUeNgapID[%d]", ranUe.RanUeNgapId, ranUe.AmfUeNgapId) + ranUe.InitialContextSetup = true + if pDUSessionResourceSetupResponseList != nil { - ranUe.Log.Trace("Send PDUSessionResourceSetupResponseTransfer to SMF") + ranUe.Log.Infof("Send PDUSessionResourceSetupResponseTransfer to SMF") for _, item := range pDUSessionResourceSetupResponseList.List { pduSessionID := int32(item.PDUSessionID.Value) transfer := item.PDUSessionResourceSetupResponseTransfer smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) + ranUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here continue } // response, _, _, err := consumer.SendUpdateSmContextN2Info(amfUe, pduSessionID, @@ -869,14 +972,15 @@ func handleInitialContextSetupResponseMain(ran *context.AmfRan, } if pDUSessionResourceFailedToSetupList != nil { - ranUe.Log.Trace("Send PDUSessionResourceSetupUnsuccessfulTransfer to SMF") + ranUe.Log.Infof("Send PDUSessionResourceSetupUnsuccessfulTransfer to SMF") for _, item := range pDUSessionResourceFailedToSetupList.List { pduSessionID := int32(item.PDUSessionID.Value) transfer := item.PDUSessionResourceSetupUnsuccessfulTransfer smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) + ranUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here continue } // response, _, _, err := consumer.SendUpdateSmContextN2Info(amfUe, pduSessionID, @@ -924,19 +1028,20 @@ func handleInitialContextSetupFailureMain(ran *context.AmfRan, amfUe := ranUe.AmfUe if amfUe == nil { - ranUe.Log.Error("amfUe is nil") + ran.Log.Error("amfUe is nil") return } if pDUSessionResourceFailedToSetupList != nil { - ranUe.Log.Trace("Send PDUSessionResourceSetupUnsuccessfulTransfer to SMF") + ranUe.Log.Infof("Send PDUSessionResourceSetupUnsuccessfulTransfer to SMF") for _, item := range pDUSessionResourceFailedToSetupList.List { pduSessionID := int32(item.PDUSessionID.Value) transfer := item.PDUSessionResourceSetupUnsuccessfulTransfer smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) + ranUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here continue } _, _, _, err := consumer.SendUpdateSmContextN2Info(amfUe, smContext, @@ -989,7 +1094,7 @@ func handleUEContextReleaseRequestMain(ran *context.AmfRan, pduSessionID := int32(pduSessionReourceItem.PDUSessionID.Value) smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) + ranUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) // TODO: Check if doing error handling here continue } @@ -1014,10 +1119,12 @@ func handleUEContextReleaseRequestMain(ran *context.AmfRan, return true }) ngap_message.SendUEContextReleaseCommand(ranUe, context.UeContextReleaseUeContext, causeGroup, causeValue) + // TODO: start timer to release RanUe context return } } ngap_message.SendUEContextReleaseCommand(ranUe, context.UeContextN2NormalRelease, causeGroup, causeValue) + // TODO: start timer to release RanUe context } func handleUEContextModificationResponseMain(ran *context.AmfRan, @@ -1099,11 +1206,12 @@ func handleHandoverNotifyMain(ran *context.AmfRan, // Desciibed in (23.502 4.9.1.3.3) [conditional] 6a.Namf_Communication_N2InfoNotify. ran.Log.Error("N2 Handover between AMF has not been implemented yet") } else { - targetUe.Log.Info("Handle Handover notification Finshed ") - for _, pduSessionid := range targetUe.SuccessPduSessionId { - smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionid) + ran.Log.Info("Handle Handover notification Finshed") + for _, pduSessionID := range targetUe.SuccessPduSessionId { + smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) if !ok { - sourceUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionid) + sourceUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here continue } _, _, _, err := consumer.SendUpdateSmContextN2HandoverComplete(amfUe, smContext, "", nil) @@ -1129,7 +1237,7 @@ func handlePathSwitchRequestMain(ran *context.AmfRan, pduSessionResourceToBeSwitchedInDLList *ngapType.PDUSessionResourceToBeSwitchedDLList, pduSessionResourceFailedToSetupList *ngapType.PDUSessionResourceFailedToSetupListPSReq, ) { - ranUe := context.AMF_Self().RanUeFindByAmfUeNgapID(sourceAMFUENGAPID.Value) + ranUe := context.GetSelf().RanUeFindByAmfUeNgapID(sourceAMFUENGAPID.Value) if ranUe == nil { ran.Log.Errorf("Cannot find UE from sourceAMfUeNgapID[%d]", sourceAMFUENGAPID.Value) ngap_message.SendPathSwitchRequestFailure(ran, sourceAMFUENGAPID.Value, rANUENGAPID.Value, nil, nil) @@ -1137,7 +1245,6 @@ func handlePathSwitchRequestMain(ran *context.AmfRan, } ran.Log.Tracef("AmfUeNgapID[%d] RanUeNgapID[%d]", ranUe.AmfUeNgapId, ranUe.RanUeNgapId) - ranUe.Log.Info("Handle Path Switch Request") amfUe := ranUe.AmfUe if amfUe == nil { @@ -1176,12 +1283,14 @@ func handlePathSwitchRequestMain(ran *context.AmfRan, var pduSessionResourceReleasedListPSFail ngapType.PDUSessionResourceReleasedListPSFail if pduSessionResourceToBeSwitchedInDLList != nil { + ranUe.Log.Infof("Send PathSwitchRequestTransfer to SMF") for _, item := range pduSessionResourceToBeSwitchedInDLList.List { pduSessionID := int32(item.PDUSessionID.Value) transfer := item.PathSwitchRequestTransfer smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) + ranUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here continue } response, errResponse, _, err := consumer.SendUpdateSmContextXnHandover(amfUe, smContext, @@ -1206,12 +1315,14 @@ func handlePathSwitchRequestMain(ran *context.AmfRan, } if pduSessionResourceFailedToSetupList != nil { + ranUe.Log.Infof("Send PathSwitchRequestSetupFailedTransfer to SMF") for _, item := range pduSessionResourceFailedToSetupList.List { pduSessionID := int32(item.PDUSessionID.Value) transfer := item.PathSwitchRequestSetupFailedTransfer smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) if !ok { - ranUe.Log.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) + ranUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here continue } response, errResponse, _, err := consumer.SendUpdateSmContextXnHandoverFailed(amfUe, smContext, @@ -1274,7 +1385,7 @@ func handleHandoverRequestAcknowledgeMain(ran *context.AmfRan, if rANUENGAPID != nil { targetUe.RanUeNgapId = rANUENGAPID.Value - targetUe.UpdateLogFields() + ran.RanUeList.Store(targetUe.RanUeNgapId, targetUe) } ran.Log.Debugf("Target Ue RanUeNgapID[%d] AmfUeNgapID[%d]", targetUe.RanUeNgapId, targetUe.AmfUeNgapId) @@ -1289,50 +1400,58 @@ func handleHandoverRequestAcknowledgeMain(ran *context.AmfRan, // describe in 23.502 4.9.1.3.2 step11 if pDUSessionResourceAdmittedList != nil { + targetUe.Log.Infof("Send HandoverRequestAcknowledgeTransfer to SMF") for _, item := range pDUSessionResourceAdmittedList.List { - pduSessionID := item.PDUSessionID.Value + pduSessionID := int32(item.PDUSessionID.Value) transfer := item.HandoverRequestAcknowledgeTransfer - pduSessionId := int32(pduSessionID) - if smContext, exist := amfUe.SmContextFindByPDUSessionID(pduSessionId); exist { - response, errResponse, problemDetails, err := consumer.SendUpdateSmContextN2HandoverPrepared(amfUe, - smContext, models.N2SmInfoType_HANDOVER_REQ_ACK, transfer) - if err != nil { - targetUe.Log.Errorf("Send HandoverRequestAcknowledgeTransfer error: %v", err) - } - if problemDetails != nil { - targetUe.Log.Warnf("ProblemDetails[status: %d, Cause: %s]", problemDetails.Status, problemDetails.Cause) - } - if response != nil && response.BinaryDataN2SmInformation != nil { - handoverItem := ngapType.PDUSessionResourceHandoverItem{} - handoverItem.PDUSessionID = item.PDUSessionID - handoverItem.HandoverCommandTransfer = response.BinaryDataN2SmInformation - pduSessionResourceHandoverList.List = append(pduSessionResourceHandoverList.List, handoverItem) - targetUe.SuccessPduSessionId = append(targetUe.SuccessPduSessionId, pduSessionId) - } - if errResponse != nil && errResponse.BinaryDataN2SmInformation != nil { - releaseItem := ngapType.PDUSessionResourceToReleaseItemHOCmd{} - releaseItem.PDUSessionID = item.PDUSessionID - releaseItem.HandoverPreparationUnsuccessfulTransfer = errResponse.BinaryDataN2SmInformation - pduSessionResourceToReleaseList.List = append(pduSessionResourceToReleaseList.List, releaseItem) - } + smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) + if !ok { + targetUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here + continue + } + response, errResponse, problemDetails, err := consumer.SendUpdateSmContextN2HandoverPrepared(amfUe, + smContext, models.N2SmInfoType_HANDOVER_REQ_ACK, transfer) + if err != nil { + targetUe.Log.Errorf("Send HandoverRequestAcknowledgeTransfer error: %v", err) + } + if problemDetails != nil { + targetUe.Log.Warnf("ProblemDetails[status: %d, Cause: %s]", problemDetails.Status, problemDetails.Cause) + } + if response != nil && response.BinaryDataN2SmInformation != nil { + handoverItem := ngapType.PDUSessionResourceHandoverItem{} + handoverItem.PDUSessionID = item.PDUSessionID + handoverItem.HandoverCommandTransfer = response.BinaryDataN2SmInformation + pduSessionResourceHandoverList.List = append(pduSessionResourceHandoverList.List, handoverItem) + targetUe.SuccessPduSessionId = append(targetUe.SuccessPduSessionId, pduSessionID) + } + if errResponse != nil && errResponse.BinaryDataN2SmInformation != nil { + releaseItem := ngapType.PDUSessionResourceToReleaseItemHOCmd{} + releaseItem.PDUSessionID = item.PDUSessionID + releaseItem.HandoverPreparationUnsuccessfulTransfer = errResponse.BinaryDataN2SmInformation + pduSessionResourceToReleaseList.List = append(pduSessionResourceToReleaseList.List, releaseItem) } } } if pDUSessionResourceFailedToSetupListHOAck != nil { + targetUe.Log.Infof("Send HandoverResourceAllocationUnsuccessfulTransfer to SMF") for _, item := range pDUSessionResourceFailedToSetupListHOAck.List { - pduSessionID := item.PDUSessionID.Value + pduSessionID := int32(item.PDUSessionID.Value) transfer := item.HandoverResourceAllocationUnsuccessfulTransfer - pduSessionId := int32(pduSessionID) - if smContext, exist := amfUe.SmContextFindByPDUSessionID(pduSessionId); exist { - _, _, problemDetails, err := consumer.SendUpdateSmContextN2HandoverPrepared(amfUe, smContext, - models.N2SmInfoType_HANDOVER_RES_ALLOC_FAIL, transfer) - if err != nil { - targetUe.Log.Errorf("Send HandoverResourceAllocationUnsuccessfulTransfer error: %v", err) - } - if problemDetails != nil { - targetUe.Log.Warnf("ProblemDetails[status: %d, Cause: %s]", problemDetails.Status, problemDetails.Cause) - } + smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) + if !ok { + targetUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here + continue + } + _, _, problemDetails, err := consumer.SendUpdateSmContextN2HandoverPrepared(amfUe, smContext, + models.N2SmInfoType_HANDOVER_RES_ALLOC_FAIL, transfer) + if err != nil { + targetUe.Log.Errorf("Send HandoverResourceAllocationUnsuccessfulTransfer error: %v", err) + } + if problemDetails != nil { + targetUe.Log.Warnf("ProblemDetails[status: %d, Cause: %s]", problemDetails.Status, problemDetails.Cause) } } } @@ -1400,7 +1519,7 @@ func handleHandoverFailureMain(ran *context.AmfRan, } _, _, _, err := consumer.SendUpdateSmContextN2HandoverCanceled(amfUe, smContext, causeAll) if err != nil { - amfUe.ProducerLog.Errorf("Send UpdateSmContextN2HandoverCanceled Error for PduSessionId[%d]", pduSessionID) + ran.Log.Errorf("Send UpdateSmContextN2HandoverCanceled Error for pduSessionID[%d]", pduSessionID) } return true }) @@ -1452,7 +1571,7 @@ func handleHandoverRequiredMain(ran *context.AmfRan, ngap_message.SendHandoverPreparationFailure(sourceUe, *cause, nil) return } - aMFSelf := context.AMF_Self() + aMFSelf := context.GetSelf() targetRanNodeId := ngapConvert.RanIdToModels(targetID.TargetRANNodeID.GlobalRANNodeID) targetRan, ok := aMFSelf.AmfRanFindByRanID(targetRanNodeId) if !ok { @@ -1470,20 +1589,30 @@ func handleHandoverRequiredMain(ran *context.AmfRan, RanNodeId: &targetRanNodeId, Tai: &tai, } + var pduSessionReqList ngapType.PDUSessionResourceSetupListHOReq - for _, pDUSessionResourceHoItem := range pDUSessionResourceListHORqd.List { - pduSessionId := int32(pDUSessionResourceHoItem.PDUSessionID.Value) - if smContext, exist := amfUe.SmContextFindByPDUSessionID(pduSessionId); exist { + + if pDUSessionResourceListHORqd != nil { + sourceUe.Log.Infof("Send HandoverRequiredTransfer to SMF") + for _, pDUSessionResourceHoItem := range pDUSessionResourceListHORqd.List { + pduSessionID := int32(pDUSessionResourceHoItem.PDUSessionID.Value) + smContext, ok := amfUe.SmContextFindByPDUSessionID(pduSessionID) + if !ok { + sourceUe.Log.Warnf("SmContext[PDU Session ID:%d] not found", pduSessionID) + // TODO: Check if doing error handling here + continue + } + response, _, _, err := consumer.SendUpdateSmContextN2HandoverPreparing(amfUe, smContext, models.N2SmInfoType_HANDOVER_REQUIRED, pDUSessionResourceHoItem.HandoverRequiredTransfer, "", &targetId) if err != nil { sourceUe.Log.Errorf("consumer.SendUpdateSmContextN2HandoverPreparing Error: %+v", err) } if response == nil { - sourceUe.Log.Errorf("SendUpdateSmContextN2HandoverPreparing Error for PduSessionId[%d]", pduSessionId) + sourceUe.Log.Errorf("SendUpdateSmContextN2HandoverPreparing Error for pduSessionID[%d]", pduSessionID) continue } else if response.BinaryDataN2SmInformation != nil { - ngap_message.AppendPDUSessionResourceSetupListHOReq(&pduSessionReqList, pduSessionId, + ngap_message.AppendPDUSessionResourceSetupListHOReq(&pduSessionReqList, pduSessionID, smContext.Snssai(), response.BinaryDataN2SmInformation) } } @@ -1544,7 +1673,7 @@ func handleHandoverCancelMain(ran *context.AmfRan, } _, _, _, err := consumer.SendUpdateSmContextN2HandoverCanceled(amfUe, smContext, causeAll) if err != nil { - sourceUe.Log.Errorf("Send UpdateSmContextN2HandoverCanceled Error for PduSessionId[%d]", pduSessionID) + sourceUe.Log.Errorf("Send UpdateSmContextN2HandoverCanceled Error for pduSessionID[%d]", pduSessionID) } return true }) @@ -1576,7 +1705,7 @@ func handleNASNonDeliveryIndicationMain(ran *context.AmfRan, } if nASPDU != nil { - nas.HandleNAS(ranUe, ngapType.ProcedureCodeNASNonDeliveryIndication, nASPDU.Value, false) + amf_nas.HandleNAS(ranUe, ngapType.ProcedureCodeNASNonDeliveryIndication, nASPDU.Value, false) } } @@ -1624,7 +1753,7 @@ func handleRANConfigurationUpdateMain(ran *context.AmfRan, } else { var found bool for i, tai := range ran.SupportedTAList { - if context.InTaiList(tai.Tai, context.AMF_Self().SupportTaiLists) { + if context.InTaiList(tai.Tai, context.GetSelf().SupportTaiLists) { ran.Log.Tracef("SERVED_TAI_INDEX[%d]", i) found = true break @@ -1658,7 +1787,7 @@ func handleUplinkRANConfigurationTransferMain(ran *context.AmfRan, ran.Log.Tracef("targerRanID [%s]", targetRanNodeID.GNbId.GNBValue) } - aMFSelf := context.AMF_Self() + aMFSelf := context.GetSelf() targetRan, ok := aMFSelf.AmfRanFindByRanID(targetRanNodeID) if !ok { @@ -1746,15 +1875,14 @@ func handleUERadioCapabilityInfoIndicationMain(ran *context.AmfRan, uERadioCapabilityForPaging *ngapType.UERadioCapabilityForPaging, ) { amfUe := ranUe.AmfUe + if amfUe == nil { ranUe.Log.Errorln("amfUe is nil") return } - if uERadioCapability != nil { amfUe.UeRadioCapability = hex.EncodeToString(uERadioCapability.Value) } - if uERadioCapabilityForPaging != nil { amfUe.UeRadioCapabilityForPaging = &context.UERadioCapabilityForPaging{} if uERadioCapabilityForPaging.UERadioCapabilityForPagingOfNR != nil { @@ -1830,7 +1958,7 @@ func handleErrorIndicationMain(ran *context.AmfRan, // > AP ID as either the local or remote identifier. // So we think that these Cause codes that represent incorrect AP ID(s) need to trigger local release. if aMFUENGAPID != nil { - ranUe := context.AMF_Self().RanUeFindByAmfUeNgapID(aMFUENGAPID.Value) + ranUe := context.GetSelf().RanUeFindByAmfUeNgapID(aMFUENGAPID.Value) if ranUe != nil && ranUe.Ran == ran { removeRanUeByInvalidId(ran, ranUe, fmt.Sprintf("ErrorIndication (AmfUeNgapID: %d)", aMFUENGAPID.Value)) } @@ -1991,7 +2119,7 @@ func buildCriticalityDiagnosticsIEItem(ieCriticality aper.Enumerated, ieID int64 } func isLatestAmfUe(amfUe *context.AmfUe) bool { - if latestAmfUe, ok := context.AMF_Self().AmfUeFindByUeContextID(amfUe.Supi); ok { + if latestAmfUe, ok := context.GetSelf().AmfUeFindByUeContextID(amfUe.Supi); ok { if amfUe == latestAmfUe { return true } @@ -2026,7 +2154,7 @@ func removeRanUeByInvalidId(ran *context.AmfRan, ranUe *context.RanUe, reason st // > having the erroneous AP ID as either the local or remote identifier. func removeRanUeByInvalidUE(ran *context.AmfRan, aMFUENGAPID *ngapType.AMFUENGAPID, rANUENGAPID *ngapType.RANUENGAPID) { if aMFUENGAPID != nil { - ranUe := context.AMF_Self().RanUeFindByAmfUeNgapID(aMFUENGAPID.Value) + ranUe := context.GetSelf().RanUeFindByAmfUeNgapID(aMFUENGAPID.Value) if ranUe != nil && ranUe.Ran == ran { removeRanUeByInvalidId(ran, ranUe, fmt.Sprintf("Invalid UE ID (AmfUeNgapID: %d)", aMFUENGAPID.Value)) } @@ -2059,7 +2187,7 @@ func ranUeFind(ran *context.AmfRan, rANUENGAPID_string = fmt.Sprintf("%d", rANUENGAPID.Value) } - ranUe = context.AMF_Self().RanUeFindByAmfUeNgapID(aMFUENGAPID.Value) + ranUe = context.GetSelf().RanUeFindByAmfUeNgapID(aMFUENGAPID.Value) if ranUe == nil { cause := &ngapType.Cause{ Present: ngapType.CausePresentRadioNetwork, diff --git a/internal/ngap/handler_test.go b/internal/ngap/handler_test.go new file mode 100644 index 00000000..8ed5d41d --- /dev/null +++ b/internal/ngap/handler_test.go @@ -0,0 +1,433 @@ +package ngap + +import ( + "encoding/hex" + "fmt" + "net" + "testing" + + "github.com/google/uuid" + . "github.com/smartystreets/goconvey/convey" + "github.com/stretchr/testify/require" + + amf_context "github.com/free5gc/amf/internal/context" + "github.com/free5gc/amf/internal/logger" + nastesting "github.com/free5gc/amf/internal/nas/testing" + ngaptesting "github.com/free5gc/amf/internal/ngap/testing" + "github.com/free5gc/amf/pkg/factory" + "github.com/free5gc/aper" + "github.com/free5gc/nas/nasMessage" + "github.com/free5gc/nas/nasType" + "github.com/free5gc/ngap" + "github.com/free5gc/ngap/ngapType" + "github.com/free5gc/openapi/models" +) + +func NewAmfRan(conn net.Conn) *amf_context.AmfRan { + ran := amf_context.AmfRan{ + RanPresent: 1, + RanId: &models.GlobalRanNodeId{ + PlmnId: &models.PlmnId{ + Mcc: "208", + Mnc: "93", + }, + GNbId: &models.GNbId{ + BitLength: 24, + GNBValue: "000102", + }, + }, + Name: "free5gc", + AnType: "3GPP_ACCESS", + + /* socket Connect*/ + Conn: conn, + + /* Supported TA List */ + SupportedTAList: []amf_context.SupportedTAI{ + { + Tai: models.Tai{ + PlmnId: &models.PlmnId{ + Mcc: "208", + Mnc: "93", + }, + Tac: "000001", + }, + SNssaiList: []models.Snssai{ + { + Sst: 1, + Sd: "010203", + }, + }, + }, + }, + + /* logger */ + Log: logger.NgapLog.WithField(logger.FieldRanAddr, "127.0.0.1"), + } + return &ran +} + +func NewAmfContext(amfCtx *amf_context.AMFContext) { + *amfCtx = amf_context.AMFContext{ + NfId: uuid.New().String(), + + NgapIpList: []string{"127.0.0.1"}, + NgapPort: 38412, + UriScheme: "http", + RegisterIPv4: "127.0.0.18", + BindingIPv4: "127.0.0.18", + SBIPort: 8000, + ServedGuamiList: []models.Guami{ + { + PlmnId: &models.PlmnId{ + Mcc: "208", + Mnc: "93", + }, + AmfId: "cafe00", + }, + }, + SupportTaiLists: []models.Tai{ + { + PlmnId: &models.PlmnId{ + Mcc: "208", + Mnc: "93", + }, + Tac: "000001", + }, + }, + PlmnSupportList: []factory.PlmnSupportItem{ + { + PlmnId: &models.PlmnId{ + Mcc: "208", + Mnc: "93", + }, + SNssaiList: []models.Snssai{ + { + Sst: 1, + Sd: "010203", + }, + { + Sst: 1, + Sd: "112233", + }, + }, + }, + }, + SupportDnnLists: []string{ + "internet", + }, + NrfUri: "http://127.0.0.10:8000", + SecurityAlgorithm: amf_context.SecurityAlgorithm{ + IntegrityOrder: []uint8{0x02}, + CipheringOrder: []uint8{0x00}, + }, + NetworkName: factory.NetworkName{ + Full: "free5GC", + Short: "free", + }, + T3502Value: 720, + T3512Value: 3600, + Non3gppDeregTimerValue: 3240, + T3513Cfg: factory.TimerValue{ + Enable: true, + ExpireTime: 6000000000, + MaxRetryTimes: 4, + }, + T3522Cfg: factory.TimerValue{ + Enable: true, + ExpireTime: 6000000000, + MaxRetryTimes: 4, + }, + T3550Cfg: factory.TimerValue{ + Enable: true, + ExpireTime: 6000000000, + MaxRetryTimes: 4, + }, + T3560Cfg: factory.TimerValue{ + Enable: true, + ExpireTime: 6000000000, + MaxRetryTimes: 4, + }, + T3565Cfg: factory.TimerValue{ + Enable: true, + ExpireTime: 6000000000, + MaxRetryTimes: 4, + }, + } +} + +func BuildInitialUEMessage(ranUeNgapID int64, nasPdu []byte, fiveGSTmsi string) ngapType.NGAPPDU { + var TestPlmn ngapType.PLMNIdentity + var tmsi aper.OctetString + var amfSetID, amfPointer []byte + var err error + + pdu := ngapType.NGAPPDU{ + Present: ngapType.NGAPPDUPresentInitiatingMessage, + InitiatingMessage: &ngapType.InitiatingMessage{ + ProcedureCode: ngapType.ProcedureCode{ + Value: ngapType.ProcedureCodeInitialUEMessage, + }, + Criticality: ngapType.Criticality{ + Value: ngapType.CriticalityPresentIgnore, + }, + Value: ngapType.InitiatingMessageValue{ + Present: ngapType.InitiatingMessagePresentInitialUEMessage, + InitialUEMessage: &ngapType.InitialUEMessage{ + ProtocolIEs: ngapType.ProtocolIEContainerInitialUEMessageIEs{ + List: []ngapType.InitialUEMessageIEs{ + { + Id: ngapType.ProtocolIEID{ + Value: ngapType.ProtocolIEIDRANUENGAPID, + }, + Criticality: ngapType.Criticality{ + Value: ngapType.CriticalityPresentReject, + }, + Value: ngapType.InitialUEMessageIEsValue{ + Present: ngapType.InitialUEMessageIEsPresentRANUENGAPID, + RANUENGAPID: &ngapType.RANUENGAPID{ + Value: ranUeNgapID, + }, + }, + }, + { + Id: ngapType.ProtocolIEID{ + Value: ngapType.ProtocolIEIDNASPDU, + }, + Criticality: ngapType.Criticality{ + Value: ngapType.CriticalityPresentReject, + }, + Value: ngapType.InitialUEMessageIEsValue{ + Present: ngapType.InitialUEMessageIEsPresentNASPDU, + NASPDU: &ngapType.NASPDU{ + Value: nasPdu, + }, + }, + }, + { + Id: ngapType.ProtocolIEID{ + Value: ngapType.ProtocolIEIDUserLocationInformation, + }, + Criticality: ngapType.Criticality{ + Value: ngapType.CriticalityPresentReject, + }, + Value: ngapType.InitialUEMessageIEsValue{ + Present: ngapType.InitialUEMessageIEsPresentUserLocationInformation, + UserLocationInformation: &ngapType.UserLocationInformation{ + UserLocationInformationNR: &ngapType.UserLocationInformationNR{ + NRCGI: ngapType.NRCGI{ + PLMNIdentity: ngapType.PLMNIdentity{ + Value: TestPlmn.Value, + }, + NRCellIdentity: ngapType.NRCellIdentity{ + Value: aper.BitString{ + Bytes: []byte{0x00, 0x00, 0x00, 0x00, 0x10}, + BitLength: 36, + }, + }, + }, + TAI: ngapType.TAI{ + PLMNIdentity: ngapType.PLMNIdentity{ + Value: TestPlmn.Value, + }, + TAC: ngapType.TAC{ + Value: aper.OctetString("\x00\x00\x01"), + }, + }, + }, + }, + }, + }, + { + Id: ngapType.ProtocolIEID{ + Value: ngapType.ProtocolIEIDRRCEstablishmentCause, + }, + Criticality: ngapType.Criticality{ + Value: ngapType.CriticalityPresentIgnore, + }, + Value: ngapType.InitialUEMessageIEsValue{ + Present: ngapType.InitialUEMessageIEsPresentRRCEstablishmentCause, + RRCEstablishmentCause: &ngapType.RRCEstablishmentCause{ + Value: ngapType.RRCEstablishmentCausePresentMtAccess, + }, + }, + }, + { + Id: ngapType.ProtocolIEID{ + Value: ngapType.ProtocolIEIDFiveGSTMSI, + }, + Criticality: ngapType.Criticality{ + Value: ngapType.CriticalityPresentReject, + }, + }, + }, + }, + }, + }, + }, + } + + if fiveGSTmsi != "" { + ie := ngapType.InitialUEMessageIEs{} + ie.Id.Value = ngapType.ProtocolIEIDFiveGSTMSI + ie.Criticality.Value = ngapType.CriticalityPresentReject + ie.Value.Present = ngapType.InitialUEMessageIEsPresentFiveGSTMSI + ie.Value.FiveGSTMSI = new(ngapType.FiveGSTMSI) + + fiveGSTMSI := ie.Value.FiveGSTMSI + + amfSetID, err = hex.DecodeString(fiveGSTmsi[:4]) + if err != nil { + fmt.Println("DecodeString AMFSetID error in BuildInitialUEMessage") + } + + fiveGSTMSI.AMFSetID.Value = aper.BitString{ + Bytes: amfSetID, + BitLength: 10, + } + + amfPointer, err = hex.DecodeString(fiveGSTmsi[2:4]) + if err != nil { + fmt.Println("DecodeString AMFPointer error in BuildInitialUEMessage") + } + + fiveGSTMSI.AMFPointer.Value = aper.BitString{ + Bytes: amfPointer, + BitLength: 6, + } + + tmsi, err = hex.DecodeString(fiveGSTmsi[4:]) + if err != nil { + fmt.Println("DecodeString 5G-S-TMSI error in BuildInitialUEMessage") + } + + fiveGSTMSI.FiveGTMSI.Value = tmsi + + pdu.InitiatingMessage.Value.InitialUEMessage.ProtocolIEs.List = append( + pdu.InitiatingMessage.Value.InitialUEMessage.ProtocolIEs.List, ie) + } + + return pdu +} + +func TestHandleInitialUEMessage(t *testing.T) { + var message *ngapType.NGAPPDU + var ranUeNgapID int64 = 1 + var fiveGSTmsi string = "fe0000000001" + var msg ngapType.NGAPPDU + + testCases := []struct { + amfUENGAPID int + nasPdu []byte + paramStr string + resultStr string + expectedResponse aper.OctetString + }{ + { + amfUENGAPID: 1, + nasPdu: nastesting.GetServiceRequest(nasMessage.ServiceTypeData), + paramStr: "Service Request after NGSetup. AMF can not recognize UE.", + resultStr: "DownlinkNASTransport with Service Reject", + expectedResponse: aper.OctetString{0x7e, 0x00, 0x4d, 0x0a}, + }, + { + amfUENGAPID: 2, + nasPdu: nastesting.GetRegistrationRequest( + nasMessage.RegistrationType5GSPeriodicRegistrationUpdating, + nasType.MobileIdentity5GS{ + Len: 12, // suci + Buffer: []uint8{0x01, 0x02, 0xf8, 0x39, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x47, 0x78}, + }, + nil, nil, nil, nil, nil), + paramStr: "Periodic Registration. AMF can not recognize UE.", + resultStr: "DownlinkNASTransport with Registration Reject", + expectedResponse: aper.OctetString{0x7e, 0x00, 0x44, 0x0a, 0x16, 0x01, 0x2C}, + }, + { + amfUENGAPID: 3, + nasPdu: nastesting.GetRegistrationRequest( + nasMessage.RegistrationType5GSMobilityRegistrationUpdating, + nasType.MobileIdentity5GS{ + Len: 12, // suci + Buffer: []uint8{0x01, 0x02, 0xf8, 0x39, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x47, 0x78}, + }, + nil, nil, nil, nil, nil), + paramStr: "Mobility Registration. AMF can not recognize UE.", + resultStr: "DownlinkNASTransport with Registration Reject", + expectedResponse: aper.OctetString{0x7e, 0x00, 0x44, 0x0a, 0x16, 0x01, 0x2C}, + }, + } + + for i, testcase := range testCases { + infoStr := fmt.Sprintf("testcase[%d]: ", i) + + // Set up fake connection + connStub := new(ngaptesting.SctpConnStub) + + // Init AMF context + amf_self := amf_context.GetSelf() + NewAmfContext(amf_self) + + // Set up AmfRan + ran := NewAmfRan(connStub) + + // Set up message + msg = BuildInitialUEMessage(ranUeNgapID, testcase.nasPdu, fiveGSTmsi) + message = &msg + initiatingMessage := message.InitiatingMessage + require.NotNil(t, initiatingMessage) + + handlerInitialUEMessage(ran, &msg, initiatingMessage) + Convey(infoStr, t, func() { + Convey(testcase.paramStr, func() { + Convey(testcase.resultStr, func() { + rcv, err := ngap.Decoder(connStub.MsgList[0]) + if err != nil { + fmt.Println("decode ngap message error") + } + ieListDownlinkNASTransport := rcv.InitiatingMessage.Value.DownlinkNASTransport.ProtocolIEs.List + for _, ie := range ieListDownlinkNASTransport { + switch ie.Value.Present { + case ngapType.DownlinkNASTransportIEsPresentAMFUENGAPID: + Convey("AMFUENGAPID", func() { + So(ie.Value.AMFUENGAPID.Value, ShouldEqual, testcase.amfUENGAPID) + }) + case ngapType.DownlinkNASTransportIEsPresentRANUENGAPID: + Convey("RANUENGAPID", func() { + So(ie.Value.RANUENGAPID.Value, ShouldEqual, 1) + }) + case ngapType.DownlinkNASTransportIEsPresentNASPDU: + Convey("Cause5GMMImplicitlyDeregistered", func() { + So(ie.Value.NASPDU.Value, ShouldResemble, testcase.expectedResponse) + }) + } + } + }) + + Convey("UEContextReleaseCommand ", func() { + rcv, err := ngap.Decoder(connStub.MsgList[1]) + if err != nil { + fmt.Println("decode ngap message error") + } + + ieListUEContextReleaseCommand := rcv.InitiatingMessage.Value.UEContextReleaseCommand.ProtocolIEs.List + for _, ie := range ieListUEContextReleaseCommand { + switch ie.Value.Present { + case ngapType.UEContextReleaseCommandIEsPresentUENGAPIDs: + Convey("AMFUENGAPID", func() { + So(ie.Value.UENGAPIDs.UENGAPIDPair.AMFUENGAPID.Value, ShouldEqual, testcase.amfUENGAPID) + }) + Convey("RANUENGAPID", func() { + So(ie.Value.UENGAPIDs.UENGAPIDPair.RANUENGAPID.Value, ShouldEqual, 1) + }) + case ngapType.UEContextReleaseCommandIEsPresentCause: + Convey("UEContextReleaseCommandIEsPresentCause", func() { + So(ie.Value.Cause.Nas.Value, ShouldEqual, ngapType.CauseNasPresentNormalRelease) + }) + } + } + }) + }) + }) + } +} diff --git a/internal/ngap/message/build.go b/internal/ngap/message/build.go index 91f039a4..3770497e 100644 --- a/internal/ngap/message/build.go +++ b/internal/ngap/message/build.go @@ -7,6 +7,7 @@ import ( "github.com/free5gc/amf/internal/context" "github.com/free5gc/amf/internal/logger" + "github.com/free5gc/amf/pkg/factory" "github.com/free5gc/aper" "github.com/free5gc/ngap" "github.com/free5gc/ngap/ngapConvert" @@ -79,7 +80,7 @@ func BuildPDUSessionResourceReleaseCommand(ue *context.RanUe, nasPdu []byte, } func BuildNGSetupResponse() ([]byte, error) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() var pdu ngapType.NGAPPDU pdu.Present = ngapType.NGAPPDUPresentSuccessfulOutcome pdu.SuccessfulOutcome = new(ngapType.SuccessfulOutcome) @@ -279,19 +280,17 @@ func BuildNGResetAcknowledge(partOfNGInterface *ngapType.UEAssociatedLogicalNGCo uEAssociatedLogicalNGConnectionItem.AMFUENGAPID = new(ngapType.AMFUENGAPID) uEAssociatedLogicalNGConnectionItem.AMFUENGAPID = item.AMFUENGAPID logger.NgapLog.Tracef( - "[Build NG Reset Ack] (pair %d) AmfUeNgapID[%d]", i, - uEAssociatedLogicalNGConnectionItem.AMFUENGAPID) + "[Build NG Reset Ack] (pair %d) AmfUeNgapID[%d]", i, uEAssociatedLogicalNGConnectionItem.AMFUENGAPID) } if item.RANUENGAPID != nil { uEAssociatedLogicalNGConnectionItem.RANUENGAPID = new(ngapType.RANUENGAPID) uEAssociatedLogicalNGConnectionItem.RANUENGAPID = item.RANUENGAPID logger.NgapLog.Tracef( - "[Build NG Reset Ack] (pair %d) RanUeNgapID[%d]", i, - uEAssociatedLogicalNGConnectionItem.RANUENGAPID) + "[Build NG Reset Ack] (pair %d) RanUeNgapID[%d]", i, uEAssociatedLogicalNGConnectionItem.RANUENGAPID) } - uEAssociatedLogicalNGConnectionList.List = append( - uEAssociatedLogicalNGConnectionList.List, uEAssociatedLogicalNGConnectionItem) + uEAssociatedLogicalNGConnectionList.List = append(uEAssociatedLogicalNGConnectionList.List, + uEAssociatedLogicalNGConnectionItem) } nGResetAcknowledgeIEs.List = append(nGResetAcknowledgeIEs.List, ie) @@ -382,7 +381,8 @@ func BuildDownlinkNasTransport(ue *context.RanUe, nasPdu []byte, // RAN Paging Priority (optional) // Mobility Restriction List (optional) - if ue.Ran.AnType == models.AccessType__3_GPP_ACCESS && mobilityRestrictionList != nil { + if c := factory.AmfConfig.GetNgapIEMobilityRestrictionList(); c != nil && c.Enable && + ue.Ran.AnType == models.AccessType__3_GPP_ACCESS && mobilityRestrictionList != nil { amfUe := ue.AmfUe if amfUe == nil { return nil, fmt.Errorf("amfUe is nil") @@ -659,7 +659,7 @@ func BuildHandoverCancelAcknowledge( // nasPDU: from nas layer // pduSessionResourceSetupRequestList: provided by AMF, and transfer data is from SMF func BuildPDUSessionResourceSetupRequest(ue *context.RanUe, nasPdu []byte, - pduSessionResourceSetupRequestList ngapType.PDUSessionResourceSetupListSUReq, + pduSessionResourceSetupRequestList *ngapType.PDUSessionResourceSetupListSUReq, ) ([]byte, error) { // TODO: Ran Paging Priority (optional) @@ -721,7 +721,7 @@ func BuildPDUSessionResourceSetupRequest(ue *context.RanUe, nasPdu []byte, ie.Id.Value = ngapType.ProtocolIEIDPDUSessionResourceSetupListSUReq ie.Criticality.Value = ngapType.CriticalityPresentReject ie.Value.Present = ngapType.PDUSessionResourceSetupRequestIEsPresentPDUSessionResourceSetupListSUReq - ie.Value.PDUSessionResourceSetupListSUReq = &pduSessionResourceSetupRequestList + ie.Value.PDUSessionResourceSetupListSUReq = pduSessionResourceSetupRequestList pDUSessionResourceSetupRequestIEs.List = append(pDUSessionResourceSetupRequestIEs.List, ie) // UE AggreateMaximum Bit Rate @@ -910,7 +910,7 @@ func BuildInitialContextSetupRequest( if !ok { return nil, fmt.Errorf("ranUe for %s is nil", anType) } - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() pdu.Present = ngapType.NGAPPDUPresentInitiatingMessage pdu.InitiatingMessage = new(ngapType.InitiatingMessage) @@ -964,7 +964,7 @@ func BuildInitialContextSetupRequest( // UE Aggregate Maximum Bit Rate (conditional: if pdu session resource setup) // The subscribed UE-AMBR is a subscription parameter which is // retrieved from UDM and provided to the (R)AN by the AMF - if pduSessionResourceSetupRequestList != nil { + if pduSessionResourceSetupRequestList != nil && len(pduSessionResourceSetupRequestList.List) > 0 { ie = ngapType.InitialContextSetupRequestIEs{} ie.Id.Value = ngapType.ProtocolIEIDUEAggregateMaximumBitRate ie.Criticality.Value = ngapType.CriticalityPresentReject @@ -1058,17 +1058,15 @@ func BuildInitialContextSetupRequest( nrIntegrityAlgorithm[0] |= amfUe.UESecurityCapability.GetIA2_128_5G() << 6 nrIntegrityAlgorithm[0] |= amfUe.UESecurityCapability.GetIA3_128_5G() << 5 - ueSecurityCapabilities.NRintegrityProtectionAlgorithms.Value = ngapConvert.ByteToBitString( - nrIntegrityAlgorithm, 16) + ueSecurityCapabilities.NRintegrityProtectionAlgorithms.Value = ngapConvert.ByteToBitString(nrIntegrityAlgorithm, 16) // only support NR algorithms eutraEncryptionAlgorithm := []byte{0x00, 0x00} - ueSecurityCapabilities.EUTRAencryptionAlgorithms.Value = ngapConvert.ByteToBitString( - eutraEncryptionAlgorithm, 16) + ueSecurityCapabilities.EUTRAencryptionAlgorithms.Value = ngapConvert.ByteToBitString(eutraEncryptionAlgorithm, 16) eutraIntegrityAlgorithm := []byte{0x00, 0x00} - ueSecurityCapabilities.EUTRAintegrityProtectionAlgorithms.Value = ngapConvert.ByteToBitString( - eutraIntegrityAlgorithm, 16) + ueSecurityCapabilities.EUTRAintegrityProtectionAlgorithms.Value = ngapConvert. + ByteToBitString(eutraIntegrityAlgorithm, 16) initialContextSetupRequestIEs.List = append(initialContextSetupRequestIEs.List, ie) @@ -1104,7 +1102,8 @@ func BuildInitialContextSetupRequest( } // Mobility Restriction List (optional) - if anType == models.AccessType__3_GPP_ACCESS { + if c := factory.AmfConfig.GetNgapIEMobilityRestrictionList(); c != nil && c.Enable && + anType == models.AccessType__3_GPP_ACCESS { ie = ngapType.InitialContextSetupRequestIEs{} ie.Id.Value = ngapType.ProtocolIEIDMobilityRestrictionList ie.Criticality.Value = ngapType.CriticalityPresentIgnore @@ -1113,7 +1112,6 @@ func BuildInitialContextSetupRequest( mobilityRestrictionList := BuildIEMobilityRestrictionList(amfUe) ie.Value.MobilityRestrictionList = &mobilityRestrictionList - initialContextSetupRequestIEs.List = append(initialContextSetupRequestIEs.List, ie) } @@ -1150,7 +1148,8 @@ func BuildInitialContextSetupRequest( // last 4 digits of the SNR masked by setting the corresponding bits to 1. // The first to fourth bits correspond to the first digit of the IMEISV, // the fifth to eighth bits correspond to the second digit of the IMEISV, and so on - if amfUe.Pei != "" && strings.HasPrefix(amfUe.Pei, "imeisv") { + if c := factory.AmfConfig.GetNgapIEMaskedIMEISV(); c != nil && c.Enable && + amfUe.Pei != "" && strings.HasPrefix(amfUe.Pei, "imeisv") { ie = ngapType.InitialContextSetupRequestIEs{} ie.Id.Value = ngapType.ProtocolIEIDMaskedIMEISV ie.Criticality.Value = ngapType.CriticalityPresentIgnore @@ -1217,15 +1216,15 @@ func BuildInitialContextSetupRequest( uERadioCapabilityForPaging := ie.Value.UERadioCapabilityForPaging var err error if amfUe.UeRadioCapabilityForPaging.NR != "" { - uERadioCapabilityForPaging.UERadioCapabilityForPagingOfNR.Value, err = hex.DecodeString( - amfUe.UeRadioCapabilityForPaging.NR) + uERadioCapabilityForPaging.UERadioCapabilityForPagingOfNR.Value, err = hex. + DecodeString(amfUe.UeRadioCapabilityForPaging.NR) if err != nil { logger.NgapLog.Errorf("[Build Error] DecodeString amfUe.UeRadioCapabilityForPaging.NR error: %+v", err) } } if amfUe.UeRadioCapabilityForPaging.EUTRA != "" { - uERadioCapabilityForPaging.UERadioCapabilityForPagingOfEUTRA.Value, err = hex.DecodeString( - amfUe.UeRadioCapabilityForPaging.EUTRA) + uERadioCapabilityForPaging.UERadioCapabilityForPagingOfEUTRA.Value, err = hex. + DecodeString(amfUe.UeRadioCapabilityForPaging.EUTRA) if err != nil { logger.NgapLog.Errorf("[Build Error] DecodeString amfUe.UeRadioCapabilityForPaging.NR error: %+v", err) } @@ -1233,6 +1232,17 @@ func BuildInitialContextSetupRequest( initialContextSetupRequestIEs.List = append(initialContextSetupRequestIEs.List, ie) } + // Redirection for Voice EPS Fallback (optional) + if c := factory.AmfConfig.GetNgapIERedirectionVoiceFallback(); c != nil && c.Enable { + ie = ngapType.InitialContextSetupRequestIEs{} + ie.Id.Value = ngapType.ProtocolIEIDRedirectionVoiceFallback + ie.Criticality.Value = ngapType.CriticalityPresentIgnore + ie.Value.Present = ngapType.InitialContextSetupRequestIEsPresentRedirectionVoiceFallback + ie.Value.RedirectionVoiceFallback = new(ngapType.RedirectionVoiceFallback) + ie.Value.RedirectionVoiceFallback.Value = ngapType.RedirectionVoiceFallbackPresentNotPossible + initialContextSetupRequestIEs.List = append(initialContextSetupRequestIEs.List, ie) + } + return ngap.Encoder(pdu) } @@ -1584,7 +1594,7 @@ func BuildHandoverRequest(ue *context.RanUe, cause ngapType.Cause, pduSessionResourceSetupListHOReq ngapType.PDUSessionResourceSetupListHOReq, sourceToTargetTransparentContainer ngapType.SourceToTargetTransparentContainer, nsci bool, ) ([]byte, error) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() amfUe := ue.AmfUe if amfUe == nil { return nil, fmt.Errorf("AmfUe is nil") @@ -1671,17 +1681,15 @@ func BuildHandoverRequest(ue *context.RanUe, cause ngapType.Cause, nrIntegrityAlgorithm[0] |= amfUe.UESecurityCapability.GetIA1_128_5G() << 7 nrIntegrityAlgorithm[0] |= amfUe.UESecurityCapability.GetIA2_128_5G() << 6 nrIntegrityAlgorithm[0] |= amfUe.UESecurityCapability.GetIA3_128_5G() << 5 - ueSecurityCapabilities.NRintegrityProtectionAlgorithms.Value = ngapConvert.ByteToBitString( - nrIntegrityAlgorithm, 16) + ueSecurityCapabilities.NRintegrityProtectionAlgorithms.Value = ngapConvert.ByteToBitString(nrIntegrityAlgorithm, 16) // only support NR algorithms eutraEncryptionAlgorithm := []byte{0x00, 0x00} - ueSecurityCapabilities.EUTRAencryptionAlgorithms.Value = ngapConvert.ByteToBitString( - eutraEncryptionAlgorithm, 16) + ueSecurityCapabilities.EUTRAencryptionAlgorithms.Value = ngapConvert.ByteToBitString(eutraEncryptionAlgorithm, 16) eutraIntegrityAlgorithm := []byte{0x00, 0x00} - ueSecurityCapabilities.EUTRAintegrityProtectionAlgorithms.Value = ngapConvert.ByteToBitString( - eutraIntegrityAlgorithm, 16) + ueSecurityCapabilities.EUTRAintegrityProtectionAlgorithms.Value = ngapConvert. + ByteToBitString(eutraIntegrityAlgorithm, 16) handoverRequestIEs.List = append(handoverRequestIEs.List, ie) @@ -1721,9 +1729,38 @@ func BuildHandoverRequest(ue *context.RanUe, cause ngapType.Cause, allowedNSSAIItem.SNSSAI = ngapSnssai allowedNSSAI.List = append(allowedNSSAI.List, allowedNSSAIItem) } - handoverRequestIEs.List = append(handoverRequestIEs.List, ie) + // Masked IMEISV (optional) + // TS 38.413 9.3.1.54; TS 23.003 6.2; TS 23.501 5.9.3 + // last 4 digits of the SNR masked by setting the corresponding bits to 1. + // The first to fourth bits correspond to the first digit of the IMEISV, + // the fifth to eighth bits correspond to the second digit of the IMEISV, and so on + if c := factory.AmfConfig.GetNgapIEMaskedIMEISV(); c != nil && c.Enable && + amfUe.Pei != "" && strings.HasPrefix(amfUe.Pei, "imeisv") { + ie = ngapType.HandoverRequestIEs{} + ie.Id.Value = ngapType.ProtocolIEIDMaskedIMEISV + ie.Criticality.Value = ngapType.CriticalityPresentIgnore + ie.Value.Present = ngapType.HandoverRequestIEsPresentMaskedIMEISV + ie.Value.MaskedIMEISV = new(ngapType.MaskedIMEISV) + + imeisv := strings.TrimPrefix(amfUe.Pei, "imeisv-") + imeisvBytes, err := hex.DecodeString(imeisv) + if err != nil { + logger.NgapLog.Errorf("[Build Error] DecodeString imeisv error: %+v", err) + } + + var maskedImeisv []byte + maskedImeisv = append(maskedImeisv, imeisvBytes[:5]...) + maskedImeisv = append(maskedImeisv, []byte{0xff, 0xff}...) + maskedImeisv = append(maskedImeisv, imeisvBytes[7]) + ie.Value.MaskedIMEISV.Value = aper.BitString{ + BitLength: 64, + Bytes: maskedImeisv, + } + handoverRequestIEs.List = append(handoverRequestIEs.List, ie) + } + // Source To Target Transparent Container ie = ngapType.HandoverRequestIEs{} ie.Id.Value = ngapType.ProtocolIEIDSourceToTargetTransparentContainer @@ -1735,6 +1772,20 @@ func BuildHandoverRequest(ue *context.RanUe, cause ngapType.Cause, sourceToTargetTransparentContaine.Value = sourceToTargetTransparentContainer.Value handoverRequestIEs.List = append(handoverRequestIEs.List, ie) + + // Mobility Restriction List (optional) + if c := factory.AmfConfig.GetNgapIEMobilityRestrictionList(); c != nil && c.Enable { + ie = ngapType.HandoverRequestIEs{} + ie.Id.Value = ngapType.ProtocolIEIDMobilityRestrictionList + ie.Criticality.Value = ngapType.CriticalityPresentIgnore + ie.Value.Present = ngapType.HandoverRequestIEsPresentMobilityRestrictionList + ie.Value.MobilityRestrictionList = new(ngapType.MobilityRestrictionList) + + mobilityRestrictionList := BuildIEMobilityRestrictionList(amfUe) + ie.Value.MobilityRestrictionList = &mobilityRestrictionList + handoverRequestIEs.List = append(handoverRequestIEs.List, ie) + } + // GUAMI ie = ngapType.HandoverRequestIEs{} ie.Id.Value = ngapType.ProtocolIEIDGUAMI @@ -1788,6 +1839,18 @@ func BuildHandoverRequest(ue *context.RanUe, cause ngapType.Cause, // Mobility Restriction List(optional) // Location Reporting Request Type(optional) // RRC Inactive Transition Report Reques(optional) + + // Redirection for Voice EPS Fallback (optional) + if c := factory.AmfConfig.GetNgapIERedirectionVoiceFallback(); c != nil && c.Enable { + ie = ngapType.HandoverRequestIEs{} + ie.Id.Value = ngapType.ProtocolIEIDRedirectionVoiceFallback + ie.Criticality.Value = ngapType.CriticalityPresentIgnore + ie.Value.Present = ngapType.HandoverRequestIEsPresentRedirectionVoiceFallback + ie.Value.RedirectionVoiceFallback = new(ngapType.RedirectionVoiceFallback) + ie.Value.RedirectionVoiceFallback.Value = ngapType.RedirectionVoiceFallbackPresentNotPossible + handoverRequestIEs.List = append(handoverRequestIEs.List, ie) + } + return ngap.Encoder(pdu) } @@ -1809,7 +1872,7 @@ func BuildPathSwitchRequestAcknowledge( rrcInactiveTransitionReportRequest *ngapType.RRCInactiveTransitionReportRequest, criticalityDiagnostics *ngapType.CriticalityDiagnostics, ) ([]byte, error) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() var pdu ngapType.NGAPPDU pdu.Present = ngapType.NGAPPDUPresentSuccessfulOutcome @@ -1861,24 +1924,22 @@ func BuildPathSwitchRequestAcknowledge( nrEncryptionAlgorighm[0] |= ue.AmfUe.UESecurityCapability.GetEA1_128_5G() << 7 nrEncryptionAlgorighm[0] |= ue.AmfUe.UESecurityCapability.GetEA2_128_5G() << 6 nrEncryptionAlgorighm[0] |= ue.AmfUe.UESecurityCapability.GetEA3_128_5G() << 5 - ueSecurityCapabilities.NRencryptionAlgorithms.Value = ngapConvert.ByteToBitString( - nrEncryptionAlgorighm, 16) + ueSecurityCapabilities.NRencryptionAlgorithms.Value = ngapConvert.ByteToBitString(nrEncryptionAlgorighm, 16) nrIntegrityAlgorithm := []byte{0x00, 0x00} nrIntegrityAlgorithm[0] |= ue.AmfUe.UESecurityCapability.GetIA1_128_5G() << 7 nrIntegrityAlgorithm[0] |= ue.AmfUe.UESecurityCapability.GetIA2_128_5G() << 6 nrIntegrityAlgorithm[0] |= ue.AmfUe.UESecurityCapability.GetIA3_128_5G() << 5 - ueSecurityCapabilities.NRintegrityProtectionAlgorithms.Value = ngapConvert.ByteToBitString( - nrIntegrityAlgorithm, 16) + ueSecurityCapabilities.NRintegrityProtectionAlgorithms.Value = ngapConvert.ByteToBitString(nrIntegrityAlgorithm, 16) // only support NR algorithms eutraEncryptionAlgorithm := []byte{0x00, 0x00} - ueSecurityCapabilities.EUTRAencryptionAlgorithms.Value = ngapConvert.ByteToBitString( - eutraEncryptionAlgorithm, 16) + ueSecurityCapabilities.EUTRAencryptionAlgorithms.Value = ngapConvert. + ByteToBitString(eutraEncryptionAlgorithm, 16) eutraIntegrityAlgorithm := []byte{0x00, 0x00} - ueSecurityCapabilities.EUTRAintegrityProtectionAlgorithms.Value = ngapConvert.ByteToBitString( - eutraIntegrityAlgorithm, 16) + ueSecurityCapabilities.EUTRAintegrityProtectionAlgorithms.Value = ngapConvert. + ByteToBitString(eutraIntegrityAlgorithm, 16) pathSwitchRequestAckIEs.List = append(pathSwitchRequestAckIEs.List, ie) @@ -1972,6 +2033,17 @@ func BuildPathSwitchRequestAcknowledge( pathSwitchRequestAckIEs.List = append(pathSwitchRequestAckIEs.List, ie) } + // Redirection for Voice EPS Fallback (optional) + if c := factory.AmfConfig.GetNgapIERedirectionVoiceFallback(); c != nil && c.Enable { + ie = ngapType.PathSwitchRequestAcknowledgeIEs{} + ie.Id.Value = ngapType.ProtocolIEIDRedirectionVoiceFallback + ie.Criticality.Value = ngapType.CriticalityPresentIgnore + ie.Value.Present = ngapType.PathSwitchRequestAcknowledgeIEsPresentRedirectionVoiceFallback + ie.Value.RedirectionVoiceFallback = new(ngapType.RedirectionVoiceFallback) + ie.Value.RedirectionVoiceFallback.Value = ngapType.RedirectionVoiceFallbackPresentNotPossible + pathSwitchRequestAckIEs.List = append(pathSwitchRequestAckIEs.List, ie) + } + return ngap.Encoder(pdu) } @@ -2207,16 +2279,16 @@ func BuildPaging( ie.Value.UERadioCapabilityForPaging = new(ngapType.UERadioCapabilityForPaging) uERadioCapabilityForPaging := ie.Value.UERadioCapabilityForPaging if ue.UeRadioCapabilityForPaging.NR != "" { - uERadioCapabilityForPaging.UERadioCapabilityForPagingOfNR.Value, err = hex.DecodeString( - ue.UeRadioCapabilityForPaging.NR) + uERadioCapabilityForPaging.UERadioCapabilityForPagingOfNR.Value, err = hex. + DecodeString(ue.UeRadioCapabilityForPaging.NR) if err != nil { logger.NgapLog.Errorf( "[Build Error] DecodeString ue.UeRadioCapabilityForPaging.NR error: %+v", err) } } if ue.UeRadioCapabilityForPaging.EUTRA != "" { - uERadioCapabilityForPaging.UERadioCapabilityForPagingOfEUTRA.Value, err = hex.DecodeString( - ue.UeRadioCapabilityForPaging.EUTRA) + uERadioCapabilityForPaging.UERadioCapabilityForPagingOfEUTRA.Value, err = hex. + DecodeString(ue.UeRadioCapabilityForPaging.EUTRA) if err != nil { logger.NgapLog.Errorf("[Build Error] DecodeString ue.UeRadioCapabilityForPaging.EUTRA error: %+v", err) } @@ -2251,8 +2323,7 @@ func BuildPaging( recommendedCellItem.NGRANCGI.EUTRACGI = new(ngapType.EUTRACGI) eutraCGI := recommendedCellItem.NGRANCGI.EUTRACGI eutraCGI.PLMNIdentity = ngapConvert.PlmnIdToNgap(*recommendedCell.NgRanCGI.EUTRACGI.PlmnId) - eutraCGI.EUTRACellIdentity.Value = ngapConvert.HexToBitString( - recommendedCell.NgRanCGI.EUTRACGI.EutraCellId, 28) + eutraCGI.EUTRACellIdentity.Value = ngapConvert.HexToBitString(recommendedCell.NgRanCGI.EUTRACGI.EutraCellId, 28) } if recommendedCell.TimeStayedInCell != nil { @@ -2828,10 +2899,9 @@ func BuildLocationReportingControl( // location reference ID to be Cancelled [Conditional] if locationReportingRequestType.EventType.Value == ngapType.EventTypePresentStopUePresenceInAreaOfInterest { - locationReportingRequestType.LocationReportingReferenceIDToBeCancelled = new( - ngapType.LocationReportingReferenceID) - locationReportingRequestType.LocationReportingReferenceIDToBeCancelled. - Value = LocationReportingReferenceIDToBeCancelled + locationReportingRequestType.LocationReportingReferenceIDToBeCancelled = new(ngapType.LocationReportingReferenceID) + locationReportingRequestType. + LocationReportingReferenceIDToBeCancelled.Value = LocationReportingReferenceIDToBeCancelled } locationReportingControlIEs.List = append(locationReportingControlIEs.List, ie) @@ -2886,7 +2956,7 @@ func BuildUETNLABindingReleaseRequest(ue *context.RanUe) ([]byte, error) { func BuildAMFConfigurationUpdate(tNLassociationUsage ngapType.TNLAssociationUsage, tNLAddressWeightFactor ngapType.TNLAddressWeightFactor, ) ([]byte, error) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() var pdu ngapType.NGAPPDU pdu.Present = ngapType.NGAPPDUPresentInitiatingMessage @@ -2979,8 +3049,8 @@ func BuildAMFConfigurationUpdate(tNLassociationUsage ngapType.TNLAssociationUsag aMFTNLAssociationToAddItem.AMFTNLAssociationAddress.Present = ngapType. CPTransportLayerInformationPresentEndpointIPAddress aMFTNLAssociationToAddItem.AMFTNLAssociationAddress.EndpointIPAddress = new(ngapType.TransportLayerAddress) - *aMFTNLAssociationToAddItem.AMFTNLAssociationAddress.EndpointIPAddress = ngapConvert.IPAddressToNgap( - amfSelf.RegisterIPv4, amfSelf.HttpIPv6Address) + *aMFTNLAssociationToAddItem.AMFTNLAssociationAddress.EndpointIPAddress = ngapConvert. + IPAddressToNgap(amfSelf.RegisterIPv4, amfSelf.HttpIPv6Address) // AMF TNL Association Usage[optional] if aMFTNLAssociationToAddItem.TNLAssociationUsage != nil { @@ -3008,8 +3078,8 @@ func BuildAMFConfigurationUpdate(tNLassociationUsage ngapType.TNLAssociationUsag aMFTNLAssociationToRemoveItem.AMFTNLAssociationAddress.Present = ngapType. CPTransportLayerInformationPresentEndpointIPAddress aMFTNLAssociationToRemoveItem.AMFTNLAssociationAddress.EndpointIPAddress = new(ngapType.TransportLayerAddress) - *aMFTNLAssociationToRemoveItem.AMFTNLAssociationAddress.EndpointIPAddress = ngapConvert.IPAddressToNgap( - amfSelf.RegisterIPv4, amfSelf.HttpIPv6Address) + *aMFTNLAssociationToRemoveItem.AMFTNLAssociationAddress.EndpointIPAddress = ngapConvert. + IPAddressToNgap(amfSelf.RegisterIPv4, amfSelf.HttpIPv6Address) aMFTNLAssociationToRemoveList.List = append(aMFTNLAssociationToRemoveList.List, aMFTNLAssociationToRemoveItem) aMFConfigurationUpdateIEs.List = append(aMFConfigurationUpdateIEs.List, ie) @@ -3028,8 +3098,8 @@ func BuildAMFConfigurationUpdate(tNLassociationUsage ngapType.TNLAssociationUsag aMFTNLAssociationToUpdateItem.AMFTNLAssociationAddress.Present = ngapType. CPTransportLayerInformationPresentEndpointIPAddress aMFTNLAssociationToUpdateItem.AMFTNLAssociationAddress.EndpointIPAddress = new(ngapType.TransportLayerAddress) - *aMFTNLAssociationToUpdateItem.AMFTNLAssociationAddress.EndpointIPAddress = ngapConvert.IPAddressToNgap( - amfSelf.RegisterIPv4, amfSelf.HttpIPv6Address) + *aMFTNLAssociationToUpdateItem.AMFTNLAssociationAddress.EndpointIPAddress = ngapConvert. + IPAddressToNgap(amfSelf.RegisterIPv4, amfSelf.HttpIPv6Address) // TNLAssociationUsage in AMFTNLAssociationtoUpdateItem [optional] if aMFTNLAssociationToUpdateItem.TNLAssociationUsage != nil { diff --git a/internal/ngap/message/forward_ie.go b/internal/ngap/message/forward_ie.go index a5fe1f1d..f62543cb 100644 --- a/internal/ngap/message/forward_ie.go +++ b/internal/ngap/message/forward_ie.go @@ -48,6 +48,24 @@ func AppendPDUSessionResourceSetupListCxtReq(list *ngapType.PDUSessionResourceSe list.List = append(list.List, item) } +func ConvertPDUSessionResourceSetupListCxtReqToSUReq( + listCxtReq *ngapType.PDUSessionResourceSetupListCxtReq, +) *ngapType.PDUSessionResourceSetupListSUReq { + if listCxtReq == nil { + return nil + } + listSUReq := ngapType.PDUSessionResourceSetupListSUReq{} + for _, itemCxt := range listCxtReq.List { + var itemSU ngapType.PDUSessionResourceSetupItemSUReq + itemSU.PDUSessionID = itemCxt.PDUSessionID + itemSU.PDUSessionNASPDU = itemCxt.NASPDU + itemSU.SNSSAI = itemCxt.SNSSAI + itemSU.PDUSessionResourceSetupRequestTransfer = itemCxt.PDUSessionResourceSetupRequestTransfer + listSUReq.List = append(listSUReq.List, itemSU) + } + return &listSUReq +} + func AppendPDUSessionResourceModifyListModReq(list *ngapType.PDUSessionResourceModifyListModReq, pduSessionId int32, nasPDU []byte, transfer []byte, ) { diff --git a/internal/ngap/message/send.go b/internal/ngap/message/send.go index a76e0488..84a1b3c9 100644 --- a/internal/ngap/message/send.go +++ b/internal/ngap/message/send.go @@ -11,6 +11,8 @@ import ( func SendToRan(ran *context.AmfRan, packet []byte) { defer func() { + // This is workaround. + // TODO: Handle ran.Conn close event correctly err := recover() if err != nil { logger.NgapLog.Warnf("Send error, gNB may have been lost: %+v", err) @@ -203,6 +205,7 @@ func SendUEContextReleaseCommand(ue *context.RanUe, action context.RelAction, ca }, } } + ue.InitialContextSetup = false SendToRanUe(ue, pkt) } @@ -268,7 +271,7 @@ func SendHandoverCancelAcknowledge(ue *context.RanUe, criticalityDiagnostics *ng // nasPDU: from nas layer // pduSessionResourceSetupRequestList: provided by AMF, and transfer data is from SMF func SendPDUSessionResourceSetupRequest(ue *context.RanUe, nasPdu []byte, - pduSessionResourceSetupRequestList ngapType.PDUSessionResourceSetupListSUReq, + pduSessionResourceSetupRequestList *ngapType.PDUSessionResourceSetupListSUReq, ) { if ue == nil { logger.NgapLog.Error("RanUe is nil") @@ -377,8 +380,7 @@ func SendInitialContextSetupRequest( amfUe.RanUe[anType].Log.Errorf("Build InitialContextSetupRequest failed : %s", err.Error()) return } - amfUe.RanUe[anType].UeContextRequest = false - amfUe.RanUe[anType].SentInitialContextSetupRequest = true + NasSendToRan(amfUe, anType, pkt) } @@ -654,7 +656,7 @@ func SendPaging(ue *context.AmfUe, ngapBuf []byte) { // ngaplog.Errorf("Build Paging failed : %s", err.Error()) // } taiList := ue.RegistrationArea[models.AccessType__3_GPP_ACCESS] - context.AMF_Self().AmfRanPool.Range(func(key, value interface{}) bool { + context.GetSelf().AmfRanPool.Range(func(key, value interface{}) bool { ran := value.(*context.AmfRan) for _, item := range ran.SupportedTAList { if context.InTaiList(item.Tai, taiList) { @@ -667,11 +669,12 @@ func SendPaging(ue *context.AmfUe, ngapBuf []byte) { return true }) - if context.AMF_Self().T3513Cfg.Enable { - cfg := context.AMF_Self().T3513Cfg + if context.GetSelf().T3513Cfg.Enable { + cfg := context.GetSelf().T3513Cfg + ue.GmmLog.Infof("Start T3513 timer") ue.T3513 = context.NewTimer(cfg.ExpireTime, cfg.MaxRetryTimes, func(expireTimes int32) { ue.GmmLog.Warnf("T3513 expires, retransmit Paging (retry: %d)", expireTimes) - context.AMF_Self().AmfRanPool.Range(func(key, value interface{}) bool { + context.GetSelf().AmfRanPool.Range(func(key, value interface{}) bool { ran := value.(*context.AmfRan) for _, item := range ran.SupportedTAList { if context.InTaiList(item.Tai, taiList) { @@ -1001,3 +1004,37 @@ func SendDownlinkUEAssociatedNRPPaTransport(ue *context.RanUe, nRPPaPDU ngapType } SendToRanUe(ue, pkt) } + +func SendN2Message( + amfUe *context.AmfUe, + anType models.AccessType, + nasPdu []byte, + pduSessionResourceSetupRequestList *ngapType.PDUSessionResourceSetupListCxtReq, + rrcInactiveTransitionReportRequest *ngapType.RRCInactiveTransitionReportRequest, + coreNetworkAssistanceInfo *ngapType.CoreNetworkAssistanceInformation, + emergencyFallbackIndicator *ngapType.EmergencyFallbackIndicator, + mobilityRestrictionList *ngapType.MobilityRestrictionList, +) { + if amfUe == nil { + logger.NgapLog.Error("AmfUe is nil") + return + } + + ranUe := amfUe.RanUe[anType] + if ranUe == nil { + logger.NgapLog.Error("RanUe is nil") + return + } + + if !ranUe.InitialContextSetup && (ranUe.UeContextRequest || + (pduSessionResourceSetupRequestList != nil && len(pduSessionResourceSetupRequestList.List) > 0)) { + SendInitialContextSetupRequest(amfUe, anType, nasPdu, pduSessionResourceSetupRequestList, + rrcInactiveTransitionReportRequest, coreNetworkAssistanceInfo, emergencyFallbackIndicator) + } else if ranUe.InitialContextSetup && + (pduSessionResourceSetupRequestList != nil && len(pduSessionResourceSetupRequestList.List) > 0) { + suList := ConvertPDUSessionResourceSetupListCxtReqToSUReq(pduSessionResourceSetupRequestList) + SendPDUSessionResourceSetupRequest(ranUe, nasPdu, suList) + } else { + SendDownlinkNasTransport(ranUe, nasPdu, mobilityRestrictionList) + } +} diff --git a/internal/ngap/message/send_test.go.test b/internal/ngap/message/send_test.go.test index 1288ff60..99ebf59a 100644 --- a/internal/ngap/message/send_test.go.test +++ b/internal/ngap/message/send_test.go.test @@ -1,11 +1,15 @@ package message_test import ( - "encoding/hex" "github.com/free5gc/CommonConsumerTestData/AMF/TestAmf" "github.com/free5gc/CommonConsumerTestData/AMF/TestComm" + "github.com/free5gc/amf/handler" + "github.com/free5gc/amf/internal/context" + gmm_message "github.com/free5gc/amf/internal/gmm/message" + "github.com/free5gc/amf/internal/logger" + ngap_message "github.com/free5gc/amf/internal/ngap/message" + "github.com/free5gc/amf/internal/util" "github.com/free5gc/aper" - "github.com/free5gc/http2_util" "github.com/free5gc/nas/nasMessage" "github.com/free5gc/nas/nasTestpacket" "github.com/free5gc/nas/nasType" @@ -13,12 +17,8 @@ import ( "github.com/free5gc/ngap/ngapConvert" "github.com/free5gc/ngap/ngapType" "github.com/free5gc/openapi/models" - "github.com/free5gc/amf/internal/context" - gmm_message "github.com/free5gc/amf/internal/gmm/message" - "github.com/free5gc/amf/handler" - "github.com/free5gc/amf/internal/logger" - ngap_message "github.com/free5gc/amf/internal/ngap/message" - "github.com/free5gc/amf/internal/util" + "github.com/free5gc/util/httpwrapper" + "encoding/hex" smf_handler "free5gc/src/smf/handler" "free5gc/src/smf/pdusession" "free5gc/src/test/ngapTestpacket" @@ -35,9 +35,9 @@ func init() { go smf_handler.Handle() go func() { router := pdusession.NewRouter() - server, err := http2_util.NewServer(":29502", TestAmf.AmfLogPath, router) + server, err := httpwrapper.NewHttp2Server(":29502", TestAmf.AmfLogPath, router) if err == nil && server != nil { - err = server.ListenAndServeTLS(TestAmf.AmfPemPath, TestAmf.AmfKeyPath) + err = server.ListenAndServeTLS(TestAmf.AmfDefaultPemPath, TestAmf.AmfDefaultKeyPath) } if err != nil { logger.NgapLog.Error(err.Error()) @@ -777,7 +777,7 @@ func TestSendAMFConfigurationUpdate(t *testing.T) { time.Sleep(200 * time.Millisecond) - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() amfSelf.RegisterIPv4 = "127.0.0.1" amfSelf.HttpIPv6Address = "2001:0db8:85a3:08d3:1319:8a2e:0370:7344" amfSelf.TNLWeightFactor = 123 diff --git a/internal/ngap/service/service.go b/internal/ngap/service/service.go index 22475e97..c01d61b0 100644 --- a/internal/ngap/service/service.go +++ b/internal/ngap/service/service.go @@ -4,21 +4,24 @@ import ( "encoding/hex" "io" "net" + "runtime/debug" "sync" "syscall" "git.cs.nctu.edu.tw/calee/sctp" "github.com/free5gc/amf/internal/logger" + "github.com/free5gc/amf/pkg/factory" "github.com/free5gc/ngap" ) type NGAPHandler struct { - HandleMessage func(conn net.Conn, msg []byte) - HandleNotification func(conn net.Conn, notification sctp.Notification) + HandleMessage func(conn net.Conn, msg []byte) + HandleNotification func(conn net.Conn, notification sctp.Notification) + HandleConnectionError func(conn net.Conn) } -const readBufSize uint32 = 8192 +const readBufSize uint32 = 262144 // set default read timeout to 2 seconds var readTimeout syscall.Timeval = syscall.Timeval{Sec: 2, Usec: 0} @@ -28,13 +31,21 @@ var ( connections sync.Map ) -var sctpConfig sctp.SocketConfig = sctp.SocketConfig{ - InitMsg: sctp.InitMsg{NumOstreams: 3, MaxInstreams: 5, MaxAttempts: 2, MaxInitTimeout: 2}, - RtoInfo: &sctp.RtoInfo{SrtoAssocID: 0, SrtoInitial: 500, SrtoMax: 1500, StroMin: 100}, - AssocInfo: &sctp.AssocInfo{AsocMaxRxt: 4}, +func NewSctpConfig(cfg *factory.Sctp) *sctp.SocketConfig { + sctpConfig := &sctp.SocketConfig{ + InitMsg: sctp.InitMsg{ + NumOstreams: uint16(cfg.NumOstreams), + MaxInstreams: uint16(cfg.MaxInstreams), + MaxAttempts: uint16(cfg.MaxAttempts), + MaxInitTimeout: uint16(cfg.MaxInitTimeout), + }, + RtoInfo: &sctp.RtoInfo{SrtoAssocID: 0, SrtoInitial: 500, SrtoMax: 1500, StroMin: 100}, + AssocInfo: &sctp.AssocInfo{AsocMaxRxt: 4}, + } + return sctpConfig } -func Run(addresses []string, port int, handler NGAPHandler) { +func Run(addresses []string, port int, handler NGAPHandler, sctpConfig *sctp.SocketConfig) { ips := []net.IPAddr{} for _, addr := range addresses { @@ -51,10 +62,22 @@ func Run(addresses []string, port int, handler NGAPHandler) { Port: port, } - go listenAndServe(addr, handler) + go listenAndServe(addr, handler, sctpConfig) } -func listenAndServe(addr *sctp.SCTPAddr, handler NGAPHandler) { +func listenAndServe(addr *sctp.SCTPAddr, handler NGAPHandler, sctpConfig *sctp.SocketConfig) { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.NgapLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + } + }() + + if sctpConfig == nil { + logger.NgapLog.Errorf("Error sctp SocketConfig is nil") + return + } + if listener, err := sctpConfig.Listen("sctp", addr); err != nil { logger.NgapLog.Errorf("Failed to listen: %+v", err) return @@ -129,8 +152,7 @@ func listenAndServe(addr *sctp.SCTPAddr, handler NGAPHandler) { } else { logger.NgapLog.Debugf("Set read timeout: %+v", readTimeout) } - - logger.NgapLog.Infof("[AMF] SCTP Accept from: %s", newConn.RemoteAddr().String()) + logger.NgapLog.Infof("[AMF] SCTP Accept from: %+v", newConn.RemoteAddr()) connections.Store(newConn, newConn) go handleConnection(newConn, readBufSize, handler) @@ -157,6 +179,11 @@ func Stop() { func handleConnection(conn *sctp.SCTPConn, bufsize uint32, handler NGAPHandler) { defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.NgapLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + } + // if AMF call Stop(), then conn.Close() will return EBADF because conn has been closed inside Stop() if err := conn.Close(); err != nil && err != syscall.EBADF { logger.NgapLog.Errorf("close connection error: %+v", err) @@ -172,6 +199,7 @@ func handleConnection(conn *sctp.SCTPConn, bufsize uint32, handler NGAPHandler) switch err { case io.EOF, io.ErrUnexpectedEOF: logger.NgapLog.Debugln("Read EOF from client") + handler.HandleConnectionError(conn) return case syscall.EAGAIN: logger.NgapLog.Debugln("SCTP read timeout") @@ -180,7 +208,12 @@ func handleConnection(conn *sctp.SCTPConn, bufsize uint32, handler NGAPHandler) logger.NgapLog.Debugf("SCTPRead: %+v", err) continue default: - logger.NgapLog.Errorf("Handle connection[addr: %+v] error: %+v", conn.RemoteAddr(), err) + logger.NgapLog.Errorf( + "Handle connection[addr: %+v] error: %+v", + conn.RemoteAddr(), + err, + ) + handler.HandleConnectionError(conn) return } } diff --git a/internal/ngap/testing/conn_stub.go b/internal/ngap/testing/conn_stub.go new file mode 100644 index 00000000..e97c26a1 --- /dev/null +++ b/internal/ngap/testing/conn_stub.go @@ -0,0 +1,56 @@ +package testing + +import ( + "fmt" + "net" + "time" +) + +type SctpConnStub struct { + MsgList [][]byte +} + +func (c *SctpConnStub) Read(b []byte) (n int, err error) { + return 0, nil +} + +func (c *SctpConnStub) Write(b []byte) (n int, err error) { + c.MsgList = append(c.MsgList, b) + return 0, nil +} + +func (c *SctpConnStub) Close() error { + return fmt.Errorf("close error") +} + +func (c *SctpConnStub) SetDeadline(time.Time) error { + return fmt.Errorf("SetDeadline error") +} + +func (c *SctpConnStub) SetReadDeadline(time.Time) error { + return fmt.Errorf("SetReadDeadline error") +} + +func (c *SctpConnStub) SetWriteDeadline(time.Time) error { + return fmt.Errorf("SetWriteDeadline error") +} + +func (c *SctpConnStub) LocalAddr() net.Addr { + addr := new(testAddr) + return addr +} + +func (c *SctpConnStub) RemoteAddr() net.Addr { + addr := new(testAddr) + return addr +} + +type testAddr struct{} + +func (fa *testAddr) Network() string { + return "sctp" +} + +func (fa *testAddr) String() string { + return "127.0.0.1" +} diff --git a/internal/sbi/communication/routers.go b/internal/sbi/communication/routers.go index e5b35727..df9cdbb7 100644 --- a/internal/sbi/communication/routers.go +++ b/internal/sbi/communication/routers.go @@ -17,6 +17,7 @@ import ( "github.com/sirupsen/logrus" "github.com/free5gc/amf/internal/logger" + "github.com/free5gc/amf/pkg/factory" logger_util "github.com/free5gc/util/logger" ) @@ -49,7 +50,7 @@ func NewRouter() *gin.Engine { } func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group("/namf-comm/v1") + group := engine.Group(factory.AmfCommResUriPrefix) for _, route := range routes { switch route.Method { diff --git a/internal/sbi/consumer/am_policy.go b/internal/sbi/consumer/am_policy.go index f7bf251e..4660a2f7 100644 --- a/internal/sbi/consumer/am_policy.go +++ b/internal/sbi/consumer/am_policy.go @@ -6,6 +6,7 @@ import ( amf_context "github.com/free5gc/amf/internal/context" "github.com/free5gc/amf/internal/logger" + "github.com/free5gc/amf/pkg/factory" "github.com/free5gc/openapi" "github.com/free5gc/openapi/Npcf_AMPolicy" "github.com/free5gc/openapi/models" @@ -16,10 +17,10 @@ func AMPolicyControlCreate(ue *amf_context.AmfUe, anType models.AccessType) (*mo configuration.SetBasePath(ue.PcfUri) client := Npcf_AMPolicy.NewAPIClient(configuration) - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() policyAssociationRequest := models.PolicyAssociationRequest{ - NotificationUri: amfSelf.GetIPv4Uri() + "/namf-callback/v1/am-policy/", + NotificationUri: amfSelf.GetIPv4Uri() + factory.AmfCallbackResUriPrefix + "/am-policy/", Supi: ue.Supi, Pei: ue.Pei, Gpsi: ue.Gpsi, @@ -36,6 +37,14 @@ func AMPolicyControlCreate(ue *amf_context.AmfUe, anType models.AccessType) (*mo } res, httpResp, localErr := client.DefaultApi.PoliciesPost(context.Background(), policyAssociationRequest) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("PoliciesPost response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { locationHeader := httpResp.Header.Get("Location") logger.ConsumerLog.Debugf("location header: %+v", locationHeader) @@ -61,11 +70,6 @@ func AMPolicyControlCreate(ue *amf_context.AmfUe, anType models.AccessType) (*mo logger.ConsumerLog.Debugf("UE AM Policy Association ID: %s", ue.PolicyAssociationId) logger.ConsumerLog.Debugf("AmPolicyAssociation: %+v", ue.AmPolicyAssociation) } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("PoliciesPost' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { return nil, localErr } @@ -86,6 +90,14 @@ func AMPolicyControlUpdate(ue *amf_context.AmfUe, updateRequest models.PolicyAss res, httpResp, localErr := client.DefaultApi.PoliciesPolAssoIdUpdatePost( context.Background(), ue.PolicyAssociationId, updateRequest) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("PoliciesPolAssoIdUpdatePost response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { if res.ServAreaRes != nil { ue.AmPolicyAssociation.ServAreaRes = res.ServAreaRes @@ -105,11 +117,6 @@ func AMPolicyControlUpdate(ue *amf_context.AmfUe, updateRequest models.PolicyAss } return } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("PoliciesPolAssoIdUpdatePost' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return @@ -128,14 +135,17 @@ func AMPolicyControlDelete(ue *amf_context.AmfUe) (problemDetails *models.Proble client := Npcf_AMPolicy.NewAPIClient(configuration) httpResp, localErr := client.DefaultApi.PoliciesPolAssoIdDelete(context.Background(), ue.PolicyAssociationId) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("PoliciesPolAssoIdDelete response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { ue.RemoveAmPolicyAssociation() } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("PoliciesPolAssoIdDelete' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return diff --git a/internal/sbi/consumer/communication.go b/internal/sbi/consumer/communication.go index e278741e..59a6a6f8 100644 --- a/internal/sbi/consumer/communication.go +++ b/internal/sbi/consumer/communication.go @@ -126,15 +126,18 @@ func CreateUEContextRequest(ue *amf_context.AmfUe, ueContextCreateData models.Ue JsonData: &ueContextCreateData, } res, httpResp, localErr := client.IndividualUeContextDocumentApi.CreateUEContext(context.TODO(), ue.Guti, req) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("CreateUEContext response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { ueContextCreatedData = res.JsonData logger.ConsumerLog.Debugf("UeContextCreatedData: %+v", *ueContextCreatedData) } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("CreateUEContext' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return @@ -144,7 +147,7 @@ func CreateUEContextRequest(ue *amf_context.AmfUe, ueContextCreateData models.Ue } else { err = openapi.ReportError("%s: server no response", ue.TargetAmfUri) } - return + return ueContextCreatedData, problemDetails, err } func ReleaseUEContextRequest(ue *amf_context.AmfUe, ngapCause models.NgApCause) ( @@ -171,14 +174,17 @@ func ReleaseUEContextRequest(ue *amf_context.AmfUe, ngapCause models.NgApCause) httpResp, localErr := client.IndividualUeContextDocumentApi.ReleaseUEContext( context.TODO(), ueContextId, ueContextRelease) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("ReleaseUEContext response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { return } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("ReleaseUEContext' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return @@ -225,17 +231,19 @@ func UEContextTransferRequest( // guti format is defined at TS 29.518 Table 6.1.3.2.2-1 5g-guti-[0-9]{5,6}[0-9a-fA-F]{14} ueContextId := fmt.Sprintf("5g-guti-%s", ue.Guti) - res, httpResp, localErr := client.IndividualUeContextDocumentApi.UEContextTransfer( - context.TODO(), ueContextId, req) + res, httpResp, localErr := client.IndividualUeContextDocumentApi.UEContextTransfer(context.TODO(), ueContextId, req) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("UEContextTransfer response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { ueContextTransferRspData = res.JsonData logger.ConsumerLog.Debugf("UeContextTransferRspData: %+v", *ueContextTransferRspData) } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("UEContextTransfer' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return @@ -257,16 +265,19 @@ func RegistrationStatusUpdate(ue *amf_context.AmfUe, request models.UeRegStatusU client := Namf_Communication.NewAPIClient(configuration) ueContextId := fmt.Sprintf("5g-guti-%s", ue.Guti) - res, httpResp, localErr := client.IndividualUeContextDocumentApi.RegistrationStatusUpdate( - context.TODO(), ueContextId, request) + res, httpResp, localErr := client.IndividualUeContextDocumentApi. + RegistrationStatusUpdate(context.TODO(), ueContextId, request) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("RegistrationStatusUpdate response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { regStatusTransferComplete = res.RegStatusTransferComplete } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("RegistrationStatusUpdate' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return @@ -276,5 +287,5 @@ func RegistrationStatusUpdate(ue *amf_context.AmfUe, request models.UeRegStatusU } else { err = openapi.ReportError("%s: server no response", ue.TargetAmfUri) } - return + return regStatusTransferComplete, problemDetails, err } diff --git a/internal/sbi/consumer/nf_discovery.go b/internal/sbi/consumer/nf_discovery.go index 7f817f2f..421d4a0c 100644 --- a/internal/sbi/consumer/nf_discovery.go +++ b/internal/sbi/consumer/nf_discovery.go @@ -28,8 +28,10 @@ func SendSearchNFInstances(nrfUri string, targetNfType, requestNfType models.NfT return result, err } defer func() { - if bodyCloseErr := res.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("SearchNFInstances' response body cannot close: %v", bodyCloseErr) + if res != nil { + if bodyCloseErr := res.Body.Close(); bodyCloseErr != nil { + err = fmt.Errorf("SearchNFInstances' response body cannot close: %+w", bodyCloseErr) + } } }() return result, err diff --git a/internal/sbi/consumer/nf_mangement.go b/internal/sbi/consumer/nf_mangement.go index e819d509..1c506d06 100644 --- a/internal/sbi/consumer/nf_mangement.go +++ b/internal/sbi/consumer/nf_mangement.go @@ -10,6 +10,7 @@ import ( amf_context "github.com/free5gc/amf/internal/context" "github.com/free5gc/amf/internal/logger" "github.com/free5gc/amf/internal/util" + "github.com/free5gc/amf/pkg/factory" "github.com/free5gc/openapi" "github.com/free5gc/openapi/Nnrf_NFManagement" "github.com/free5gc/openapi/models" @@ -61,12 +62,12 @@ func BuildNFInstance(context *amf_context.AMFContext) (profile models.NfProfile, } defaultNotificationSubscription := models.DefaultNotificationSubscription{ - CallbackUri: fmt.Sprintf("%s/namf-callback/v1/n1-message-notify", context.GetIPv4Uri()), + CallbackUri: fmt.Sprintf("%s"+factory.AmfCallbackResUriPrefix+"/n1-message-notify", context.GetIPv4Uri()), NotificationType: models.NotificationType_N1_MESSAGES, N1MessageClass: models.N1MessageClass__5_GMM, } - profile.DefaultNotificationSubscriptions = append( - profile.DefaultNotificationSubscriptions, defaultNotificationSubscription) + profile.DefaultNotificationSubscriptions = append(profile.DefaultNotificationSubscriptions, + defaultNotificationSubscription) return profile, err } @@ -88,8 +89,10 @@ func SendRegisterNFInstance(nrfUri, nfInstanceId string, profile models.NfProfil continue } defer func() { - if bodyCloseErr := res.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("SearchNFInstances' response body cannot close: %v", bodyCloseErr) + if res != nil { + if bodyCloseErr := res.Body.Close(); bodyCloseErr != nil { + err = fmt.Errorf("SearchNFInstances' response body cannot close: %+w", bodyCloseErr) + } } }() status := res.StatusCode @@ -113,7 +116,7 @@ func SendRegisterNFInstance(nrfUri, nfInstanceId string, profile models.NfProfil func SendDeregisterNFInstance() (problemDetails *models.ProblemDetails, err error) { logger.ConsumerLog.Infof("[AMF] Send Deregister NFInstance") - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() // Set client and set url configuration := Nnrf_NFManagement.NewConfiguration() configuration.SetBasePath(amfSelf.NrfUri) @@ -127,7 +130,7 @@ func SendDeregisterNFInstance() (problemDetails *models.ProblemDetails, err erro } else if res != nil { defer func() { if bodyCloseErr := res.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("SearchNFInstances' response body cannot close: %v", bodyCloseErr) + err = fmt.Errorf("SearchNFInstances' response body cannot close: %+w", bodyCloseErr) } }() if res.Status != err.Error() { diff --git a/internal/sbi/consumer/nsselection.go b/internal/sbi/consumer/nsselection.go index af572843..7f44e1fe 100644 --- a/internal/sbi/consumer/nsselection.go +++ b/internal/sbi/consumer/nsselection.go @@ -20,7 +20,7 @@ func NSSelectionGetForRegistration(ue *amf_context.AmfUe, requestedNssai []model configuration.SetBasePath(ue.NssfUri) client := Nnssf_NSSelection.NewAPIClient(configuration) - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() sliceInfo := models.SliceInfoForRegistration{ SubscribedNssai: ue.SubscribedNssai, } @@ -42,6 +42,14 @@ func NSSelectionGetForRegistration(ue *amf_context.AmfUe, requestedNssai []model } res, httpResp, localErr := client.NetworkSliceInformationDocumentApi.NSSelectionGet(context.Background(), models.NfType_AMF, amfSelf.NfId, ¶mOpt) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("NSSelectionGet response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { ue.NetworkSliceInfo = &res for _, allowedNssai := range res.AllowedNssaiList { @@ -49,11 +57,6 @@ func NSSelectionGetForRegistration(ue *amf_context.AmfUe, requestedNssai []model } ue.ConfiguredNssai = res.ConfiguredNssai } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("NSSelectionGet' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err := localErr return nil, err @@ -74,7 +77,7 @@ func NSSelectionGetForPduSession(ue *amf_context.AmfUe, snssai models.Snssai) ( configuration.SetBasePath(ue.NssfUri) client := Nnssf_NSSelection.NewAPIClient(configuration) - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() sliceInfoForPduSession := models.SliceInfoForPduSession{ SNssai: &snssai, RoamingIndication: models.RoamingIndication_NON_ROAMING, // not support roaming @@ -89,14 +92,17 @@ func NSSelectionGetForPduSession(ue *amf_context.AmfUe, snssai models.Snssai) ( } res, httpResp, localErr := client.NetworkSliceInformationDocumentApi.NSSelectionGet(context.Background(), models.NfType_AMF, amfSelf.NfId, ¶mOpt) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("NSSelectionGet response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { return &res, nil, nil } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("NSSelectionGet' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { return nil, nil, localErr } diff --git a/internal/sbi/consumer/sm_context.go b/internal/sbi/consumer/sm_context.go index 23868681..3cfa459b 100644 --- a/internal/sbi/consumer/sm_context.go +++ b/internal/sbi/consumer/sm_context.go @@ -12,6 +12,7 @@ import ( amf_context "github.com/free5gc/amf/internal/context" "github.com/free5gc/amf/internal/logger" "github.com/free5gc/amf/internal/util" + "github.com/free5gc/amf/pkg/factory" "github.com/free5gc/nas/nasMessage" "github.com/free5gc/openapi" "github.com/free5gc/openapi/Nnrf_NFDiscovery" @@ -86,8 +87,8 @@ func SelectSmf( if ue.PlmnId.Mcc != "" { param.TargetPlmnList = optional.NewInterface(openapi.MarshToJsonString(ue.PlmnId)) } - if amf_context.AMF_Self().Locality != "" { - param.PreferredLocality = optional.NewString(amf_context.AMF_Self().Locality) + if amf_context.GetSelf().Locality != "" { + param.PreferredLocality = optional.NewString(amf_context.GetSelf().Locality) } ue.GmmLog.Debugf("Search SMF from NRF[%s]", nrfUri) @@ -130,18 +131,20 @@ func SendCreateSmContextRequest(ue *amf_context.AmfUe, smContext *amf_context.Sm configuration.SetBasePath(smContext.SmfUri()) client := Nsmf_PDUSession.NewAPIClient(configuration) - postSmContextReponse, httpResponse, err := client.SMContextsCollectionApi.PostSmContexts( - context.Background(), postSmContextsRequest) - + postSmContextReponse, httpResponse, err := client.SMContextsCollectionApi. + PostSmContexts(context.Background(), postSmContextsRequest) + defer func() { + if httpResponse != nil { + if rspCloseErr := httpResponse.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("PostSmContexts response body cannot close: %+v", + rspCloseErr) + } + } + }() if err == nil { response = &postSmContextReponse smContextRef = httpResponse.Header.Get("Location") } else if httpResponse != nil { - defer func() { - if bodyCloseErr := httpResponse.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("PostSmContexts' response body cannot close: %v", bodyCloseErr) - } - }() if httpResponse.Status != err.Error() { err1 = err return @@ -163,7 +166,7 @@ func SendCreateSmContextRequest(ue *amf_context.AmfUe, smContext *amf_context.Sm func buildCreateSmContextRequest(ue *amf_context.AmfUe, smContext *amf_context.SmContext, requestType *models.RequestType, ) (smContextCreateData models.SmContextCreateData) { - context := amf_context.AMF_Self() + context := amf_context.GetSelf() smContextCreateData.Supi = ue.Supi smContextCreateData.UnauthenticatedSupi = ue.UnauthenticatedSupi smContextCreateData.Pei = ue.Pei @@ -189,7 +192,7 @@ func buildCreateSmContextRequest(ue *amf_context.AmfUe, smContext *amf_context.S // smContextCreateData.UeLocation = ue.Location // } smContextCreateData.UeTimeZone = ue.TimeZone - smContextCreateData.SmContextStatusUri = context.GetIPv4Uri() + "/namf-callback/v1/smContextStatus/" + + smContextCreateData.SmContextStatusUri = context.GetIPv4Uri() + factory.AmfCallbackResUriPrefix + "/smContextStatus/" + ue.Supi + "/" + strconv.Itoa(int(smContext.PduSessionID())) return smContextCreateData @@ -225,7 +228,7 @@ func SendUpdateSmContextActivateUpCnxState( updateData.AnType = smContext.AccessType() } if ladn, ok := ue.ServingAMF().LadnPool[smContext.Dnn()]; ok { - if amf_context.InTaiList(ue.Tai, ladn.TaiLists) { + if amf_context.InTaiList(ue.Tai, ladn.TaiList) { updateData.PresenceInLadn = models.PresenceState_IN_AREA } } @@ -285,7 +288,7 @@ func SendUpdateSmContextXnHandover( updateData.ToBeSwitched = true updateData.UeLocation = &ue.Location if ladn, ok := ue.ServingAMF().LadnPool[smContext.Dnn()]; ok { - if amf_context.InTaiList(ue.Tai, ladn.TaiLists) { + if amf_context.InTaiList(ue.Tai, ladn.TaiList) { updateData.PresenceInLadn = models.PresenceState_IN_AREA } else { updateData.PresenceInLadn = models.PresenceState_OUT_OF_AREA @@ -356,7 +359,7 @@ func SendUpdateSmContextN2HandoverComplete( updateData.Guami = guami } if ladn, ok := ue.ServingAMF().LadnPool[smContext.Dnn()]; ok { - if amf_context.InTaiList(ue.Tai, ladn.TaiLists) { + if amf_context.InTaiList(ue.Tai, ladn.TaiList) { updateData.PresenceInLadn = models.PresenceState_IN_AREA } else { updateData.PresenceInLadn = models.PresenceState_OUT_OF_AREA @@ -410,7 +413,7 @@ func SendUpdateSmContextHandoverBetweenAMF( updateData.UeLocation = &ue.Location } if ladn, ok := ue.ServingAMF().LadnPool[smContext.Dnn()]; ok { - if amf_context.InTaiList(ue.Tai, ladn.TaiLists) { + if amf_context.InTaiList(ue.Tai, ladn.TaiList) { updateData.PresenceInLadn = models.PresenceState_IN_AREA } } @@ -432,18 +435,20 @@ func SendUpdateSmContextRequest(smContext *amf_context.SmContext, updateSmContextRequest.BinaryDataN1SmMessage = n1Msg updateSmContextRequest.BinaryDataN2SmInformation = n2Info - updateSmContextReponse, httpResponse, err := client.IndividualSMContextApi.UpdateSmContext( - context.Background(), smContext.SmContextRef(), - updateSmContextRequest) - + updateSmContextReponse, httpResponse, err := client.IndividualSMContextApi. + UpdateSmContext(context.Background(), smContext.SmContextRef(), + updateSmContextRequest) + defer func() { + if httpResponse != nil { + if rspCloseErr := httpResponse.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("UpdateSmContext response body cannot close: %+v", + rspCloseErr) + } + } + }() if err == nil { response = &updateSmContextReponse } else if httpResponse != nil { - defer func() { - if bodyCloseErr := httpResponse.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("UpdateSmContext' response body cannot close: %v", bodyCloseErr) - } - }() if httpResponse.Status != err.Error() { err1 = err return @@ -479,15 +484,17 @@ func SendReleaseSmContextRequest(ue *amf_context.AmfUe, smContext *amf_context.S response, err1 := client.IndividualSMContextApi.ReleaseSmContext( context.Background(), smContext.SmContextRef(), releaseSmContextRequest) - + defer func() { + if response != nil { + if rspCloseErr := response.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("ReleaseSmContext response body cannot close: %+v", + rspCloseErr) + } + } + }() if err1 == nil { ue.SmContextList.Delete(smContext.PduSessionID()) } else if response != nil && response.Status == err1.Error() { - defer func() { - if bodyCloseErr := response.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("ReleaseSmContext' response body cannot close: %v", bodyCloseErr) - } - }() if response.StatusCode == 404 { // assume succeeded to release SmContext ue.SmContextList.Delete(smContext.PduSessionID()) diff --git a/internal/sbi/consumer/subscriber_data_management.go b/internal/sbi/consumer/subscriber_data_management.go index 080b1038..5f961923 100644 --- a/internal/sbi/consumer/subscriber_data_management.go +++ b/internal/sbi/consumer/subscriber_data_management.go @@ -23,12 +23,13 @@ func PutUpuAck(ue *amf_context.AmfUe, upuMacIue string) error { upuOpt := Nudm_SubscriberDataManagement.PutUpuAckParamOpts{ AcknowledgeInfo: optional.NewInterface(ackInfo), } - rsp, err := client.ProvidingAcknowledgementOfUEParametersUpdateApi.PutUpuAck( - context.Background(), ue.Supi, &upuOpt) + httpResp, err := client.ProvidingAcknowledgementOfUEParametersUpdateApi. + PutUpuAck(context.Background(), ue.Supi, &upuOpt) defer func() { - if rsp != nil { - if bodyCloseErr := rsp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("PutUpuAck' response body cannot close: %v", bodyCloseErr) + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("PutUpuAck response body cannot close: %+v", + rspCloseErr) } } }() @@ -46,15 +47,18 @@ func SDMGetAmData(ue *amf_context.AmfUe) (problemDetails *models.ProblemDetails, data, httpResp, localErr := client.AccessAndMobilitySubscriptionDataRetrievalApi.GetAmData( context.Background(), ue.Supi, &getAmDataParamOpt) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("GetAmData response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { ue.AccessAndMobilitySubscriptionData = &data ue.Gpsi = data.Gpsis[0] // TODO: select GPSI } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("GetAmData' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return @@ -64,7 +68,7 @@ func SDMGetAmData(ue *amf_context.AmfUe) (problemDetails *models.ProblemDetails, } else { err = openapi.ReportError("server no response") } - return + return problemDetails, err } func SDMGetSmfSelectData(ue *amf_context.AmfUe) (problemDetails *models.ProblemDetails, err error) { @@ -75,16 +79,19 @@ func SDMGetSmfSelectData(ue *amf_context.AmfUe) (problemDetails *models.ProblemD paramOpt := Nudm_SubscriberDataManagement.GetSmfSelectDataParamOpts{ PlmnId: optional.NewInterface(openapi.MarshToJsonString(ue.PlmnId)), } - data, httpResp, localErr := client.SMFSelectionSubscriptionDataRetrievalApi.GetSmfSelectData( - context.Background(), ue.Supi, ¶mOpt) + data, httpResp, localErr := client.SMFSelectionSubscriptionDataRetrievalApi. + GetSmfSelectData(context.Background(), ue.Supi, ¶mOpt) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("GetSmfSelectData response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { ue.SmfSelectionData = &data } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("GetSmfSelectData' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return @@ -95,7 +102,7 @@ func SDMGetSmfSelectData(ue *amf_context.AmfUe) (problemDetails *models.ProblemD err = openapi.ReportError("server no response") } - return + return problemDetails, err } func SDMGetUeContextInSmfData(ue *amf_context.AmfUe) (problemDetails *models.ProblemDetails, err error) { @@ -103,16 +110,19 @@ func SDMGetUeContextInSmfData(ue *amf_context.AmfUe) (problemDetails *models.Pro configuration.SetBasePath(ue.NudmSDMUri) client := Nudm_SubscriberDataManagement.NewAPIClient(configuration) - data, httpResp, localErr := client.UEContextInSMFDataRetrievalApi.GetUeContextInSmfData( - context.Background(), ue.Supi, nil) + data, httpResp, localErr := client.UEContextInSMFDataRetrievalApi. + GetUeContextInSmfData(context.Background(), ue.Supi, nil) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("GetUeContextInSmfData response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { ue.UeContextInSmfData = &data } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("GetUeContextInSmfData' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return @@ -131,7 +141,7 @@ func SDMSubscribe(ue *amf_context.AmfUe) (problemDetails *models.ProblemDetails, configuration.SetBasePath(ue.NudmSDMUri) client := Nudm_SubscriberDataManagement.NewAPIClient(configuration) - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() sdmSubscription := models.SdmSubscription{ NfInstanceId: amfSelf.NfId, PlmnId: &ue.PlmnId, @@ -139,15 +149,18 @@ func SDMSubscribe(ue *amf_context.AmfUe) (problemDetails *models.ProblemDetails, resSubscription, httpResp, localErr := client.SubscriptionCreationApi.Subscribe( context.Background(), ue.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 { ue.SdmSubscriptionId = resSubscription.SubscriptionId return } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("Subscribe' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return @@ -168,8 +181,16 @@ func SDMGetSliceSelectionSubscriptionData(ue *amf_context.AmfUe) (problemDetails paramOpt := Nudm_SubscriberDataManagement.GetNssaiParamOpts{ PlmnId: optional.NewInterface(openapi.MarshToJsonString(ue.PlmnId)), } - nssai, httpResp, localErr := client.SliceSelectionSubscriptionDataRetrievalApi.GetNssai( - context.Background(), ue.Supi, ¶mOpt) + nssai, httpResp, localErr := client.SliceSelectionSubscriptionDataRetrievalApi. + GetNssai(context.Background(), ue.Supi, ¶mOpt) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("GetNssai response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { for _, defaultSnssai := range nssai.DefaultSingleNssais { subscribedSnssai := models.SubscribedSnssai{ @@ -192,11 +213,6 @@ func SDMGetSliceSelectionSubscriptionData(ue *amf_context.AmfUe) (problemDetails ue.SubscribedNssai = append(ue.SubscribedNssai, subscribedSnssai) } } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("GetNssai' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return @@ -215,14 +231,17 @@ func SDMUnsubscribe(ue *amf_context.AmfUe) (problemDetails *models.ProblemDetail client := Nudm_SubscriberDataManagement.NewAPIClient(configuration) httpResp, localErr := client.SubscriptionDeletionApi.Unsubscribe(context.Background(), ue.Supi, ue.SdmSubscriptionId) + 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 { return } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("Unsubscribe' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { err = localErr return diff --git a/internal/sbi/consumer/ue_authentication.go b/internal/sbi/consumer/ue_authentication.go index e77338fd..5d08fb7f 100644 --- a/internal/sbi/consumer/ue_authentication.go +++ b/internal/sbi/consumer/ue_authentication.go @@ -25,7 +25,7 @@ func SendUEAuthenticationAuthenticateRequest(ue *amf_context.AmfUe, client := Nausf_UEAuthentication.NewAPIClient(configuration) - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() servedGuami := amfSelf.ServedGuamiList[0] var authInfo models.AuthenticationInfo @@ -40,14 +40,17 @@ func SendUEAuthenticationAuthenticateRequest(ue *amf_context.AmfUe, } ueAuthenticationCtx, httpResponse, err := client.DefaultApi.UeAuthenticationsPost(context.Background(), authInfo) + defer func() { + if httpResponse != nil { + if rspCloseErr := httpResponse.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("UeAuthenticationsPost response body cannot close: %+v", + rspCloseErr) + } + } + }() if err == nil { return &ueAuthenticationCtx, nil, nil } else if httpResponse != nil { - defer func() { - if bodyCloseErr := httpResponse.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("UeAuthenticationsPost' response body cannot close: %v", bodyCloseErr) - } - }() if httpResponse.Status != err.Error() { return nil, nil, err } @@ -80,15 +83,17 @@ func SendAuth5gAkaConfirmRequest(ue *amf_context.AmfUe, resStar string) ( confirmResult, httpResponse, err := client.DefaultApi.UeAuthenticationsAuthCtxId5gAkaConfirmationPut( context.Background(), ue.Suci, confirmData) + defer func() { + if httpResponse != nil { + if rspCloseErr := httpResponse.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("UeAuthenticationsAuthCtxId5gAkaConfirmationPut response body cannot close: %+v", + rspCloseErr) + } + } + }() if err == nil { return &confirmResult, nil, nil } else if httpResponse != nil { - defer func() { - if bodyCloseErr := httpResponse.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf( - "UeAuthenticationsAuthCtxId5gAkaConfirmationPut' response body cannot close: %v", bodyCloseErr) - } - }() if httpResponse.Status != err.Error() { return nil, nil, err } @@ -123,14 +128,17 @@ func SendEapAuthConfirmRequest(ue *amf_context.AmfUe, eapMsg nasType.EAPMessage) } eapSession, httpResponse, err := client.DefaultApi.EapAuthMethod(context.Background(), ue.Suci, eapSessionReq) + defer func() { + if httpResponse != nil { + if rspCloseErr := httpResponse.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("EapAuthMethod response body cannot close: %+v", + rspCloseErr) + } + } + }() if err == nil { response = &eapSession } else if httpResponse != nil { - defer func() { - if bodyCloseErr := httpResponse.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("EapAuthMethod' response body cannot close: %v", bodyCloseErr) - } - }() if httpResponse.Status != err.Error() { err1 = err return diff --git a/internal/sbi/consumer/ue_context_management.go b/internal/sbi/consumer/ue_context_management.go index 9ca7ca2b..de7475ad 100644 --- a/internal/sbi/consumer/ue_context_management.go +++ b/internal/sbi/consumer/ue_context_management.go @@ -17,7 +17,7 @@ func UeCmRegistration(ue *amf_context.AmfUe, accessType models.AccessType, initi configuration.SetBasePath(ue.NudmUECMUri) client := Nudm_UEContextManagement.NewAPIClient(configuration) - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() switch accessType { case models.AccessType__3_GPP_ACCESS: @@ -32,15 +32,18 @@ func UeCmRegistration(ue *amf_context.AmfUe, accessType models.AccessType, initi _, httpResp, localErr := client.AMFRegistrationFor3GPPAccessApi.Registration(context.Background(), ue.Supi, registrationData) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("Registration response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { - ue.UeCmRegistered = true + ue.UeCmRegistered[accessType] = true return nil, nil } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("Registration' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { return nil, localErr } @@ -56,17 +59,20 @@ func UeCmRegistration(ue *amf_context.AmfUe, accessType models.AccessType, initi RatType: ue.RatType, } - _, httpResp, localErr := client.AMFRegistrationForNon3GPPAccessApi.Register( - context.Background(), ue.Supi, registrationData) + _, httpResp, localErr := client.AMFRegistrationForNon3GPPAccessApi. + Register(context.Background(), ue.Supi, registrationData) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("Register response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { - ue.UeCmRegistered = true + ue.UeCmRegistered[accessType] = true return nil, nil } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("Register' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { return nil, localErr } @@ -87,7 +93,7 @@ func UeCmDeregistration(ue *amf_context.AmfUe, accessType models.AccessType) ( configuration.SetBasePath(ue.NudmUECMUri) client := Nudm_UEContextManagement.NewAPIClient(configuration) - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() switch accessType { case models.AccessType__3_GPP_ACCESS: @@ -98,14 +104,17 @@ func UeCmDeregistration(ue *amf_context.AmfUe, accessType models.AccessType) ( httpResp, localErr := client.ParameterUpdateInTheAMFRegistrationFor3GPPAccessApi.Update(context.Background(), ue.Supi, modificationData) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("Update response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { return nil, nil } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("Update' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { return nil, localErr } @@ -122,14 +131,17 @@ func UeCmDeregistration(ue *amf_context.AmfUe, accessType models.AccessType) ( httpResp, localErr := client.ParameterUpdateInTheAMFRegistrationForNon3GPPAccessApi.UpdateAmfNon3gppAccess( context.Background(), ue.Supi, modificationData) + defer func() { + if httpResp != nil { + if rspCloseErr := httpResp.Body.Close(); rspCloseErr != nil { + logger.ConsumerLog.Errorf("UpdateAmfNon3gppAccess response body cannot close: %+v", + rspCloseErr) + } + } + }() if localErr == nil { return nil, nil } else if httpResp != nil { - defer func() { - if bodyCloseErr := httpResp.Body.Close(); bodyCloseErr != nil { - logger.ConsumerLog.Errorf("UpdateAmfNon3gppAccess' response body cannot close: %v", bodyCloseErr) - } - }() if httpResp.Status != localErr.Error() { return nil, localErr } diff --git a/internal/sbi/eventexposure/routers.go b/internal/sbi/eventexposure/routers.go index e158a6f6..729f5682 100644 --- a/internal/sbi/eventexposure/routers.go +++ b/internal/sbi/eventexposure/routers.go @@ -16,6 +16,7 @@ import ( "github.com/gin-gonic/gin" "github.com/free5gc/amf/internal/logger" + "github.com/free5gc/amf/pkg/factory" logger_util "github.com/free5gc/util/logger" ) @@ -42,7 +43,7 @@ func NewRouter() *gin.Engine { } func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group("/namf-evts/v1") + group := engine.Group(factory.AmfEvtsResUriPrefix) for _, route := range routes { switch route.Method { diff --git a/internal/sbi/httpcallback/router.go b/internal/sbi/httpcallback/router.go index 48d66a14..a8a2dea1 100644 --- a/internal/sbi/httpcallback/router.go +++ b/internal/sbi/httpcallback/router.go @@ -8,6 +8,7 @@ import ( "github.com/sirupsen/logrus" "github.com/free5gc/amf/internal/logger" + "github.com/free5gc/amf/pkg/factory" logger_util "github.com/free5gc/util/logger" ) @@ -40,7 +41,7 @@ func NewRouter() *gin.Engine { } func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group("/namf-callback/v1") + group := engine.Group(factory.AmfCallbackResUriPrefix) for _, route := range routes { switch route.Method { diff --git a/internal/sbi/location/routers.go b/internal/sbi/location/routers.go index e3e99d60..e47bef1d 100644 --- a/internal/sbi/location/routers.go +++ b/internal/sbi/location/routers.go @@ -16,6 +16,7 @@ import ( "github.com/gin-gonic/gin" "github.com/free5gc/amf/internal/logger" + "github.com/free5gc/amf/pkg/factory" logger_util "github.com/free5gc/util/logger" ) @@ -42,7 +43,7 @@ func NewRouter() *gin.Engine { } func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group("/namf-loc/v1") + group := engine.Group(factory.AmfLocResUriPrefix) for _, route := range routes { switch route.Method { diff --git a/internal/sbi/mt/routers.go b/internal/sbi/mt/routers.go index babd8117..26abc7d6 100644 --- a/internal/sbi/mt/routers.go +++ b/internal/sbi/mt/routers.go @@ -16,6 +16,7 @@ import ( "github.com/gin-gonic/gin" "github.com/free5gc/amf/internal/logger" + "github.com/free5gc/amf/pkg/factory" logger_util "github.com/free5gc/util/logger" ) @@ -42,7 +43,7 @@ func NewRouter() *gin.Engine { } func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group("/namf-mt/v1") + group := engine.Group(factory.AmfMtResUriPrefix) for _, route := range routes { switch route.Method { diff --git a/internal/sbi/oam/routers.go b/internal/sbi/oam/routers.go index 34797870..f65e86c0 100644 --- a/internal/sbi/oam/routers.go +++ b/internal/sbi/oam/routers.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" "github.com/free5gc/amf/internal/logger" + "github.com/free5gc/amf/pkg/factory" logger_util "github.com/free5gc/util/logger" ) @@ -46,7 +47,7 @@ func NewRouter() *gin.Engine { } func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group("/namf-oam/v1") + group := engine.Group(factory.AmfOamResUriPrefix) for _, route := range routes { switch route.Method { diff --git a/internal/sbi/producer/callback.go b/internal/sbi/producer/callback.go index a7dbc3d6..08d01f33 100644 --- a/internal/sbi/producer/callback.go +++ b/internal/sbi/producer/callback.go @@ -3,14 +3,14 @@ package producer import ( "fmt" "net/http" + "runtime/debug" "strconv" "github.com/free5gc/amf/internal/context" - "github.com/free5gc/amf/internal/gmm" gmm_common "github.com/free5gc/amf/internal/gmm/common" gmm_message "github.com/free5gc/amf/internal/gmm/message" "github.com/free5gc/amf/internal/logger" - "github.com/free5gc/amf/internal/nas" + amf_nas "github.com/free5gc/amf/internal/nas" ngap_message "github.com/free5gc/amf/internal/ngap/message" "github.com/free5gc/amf/internal/sbi/consumer" "github.com/free5gc/ngap/ngapType" @@ -42,7 +42,7 @@ func HandleSmContextStatusNotify(request *httpwrapper.Request) *httpwrapper.Resp func SmContextStatusNotifyProcedure(supi string, pduSessionID int32, smContextStatusNotification models.SmContextStatusNotification, ) *models.ProblemDetails { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ue, ok := amfSelf.AmfUeFindBySupi(supi) if !ok { @@ -54,8 +54,12 @@ func SmContextStatusNotifyProcedure(supi string, pduSessionID int32, return problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + smContext, ok := ue.SmContextFindByPDUSessionID(pduSessionID) if !ok { + ue.ProducerLog.Errorf("SmContext[PDU Session ID:%d] not found", pduSessionID) problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -65,28 +69,14 @@ func SmContextStatusNotifyProcedure(supi string, pduSessionID int32, } if smContextStatusNotification.StatusInfo.ResourceStatus == models.ResourceStatus_RELEASED { - ue.ProducerLog.Debugf("Release PDU Session[%d] (Cause: %s)", pduSessionID, - smContextStatusNotification.StatusInfo.Cause) - if smContext.PduSessionIDDuplicated() { - ue.ProducerLog.Debugf("Resume establishing PDU Session[%d]", pduSessionID) + ue.ProducerLog.Infof("Local release duplicated SmContext[%d]", pduSessionID) smContext.SetDuplicatedPduSessionID(false) - go func() { - ulNasTransport := smContext.ULNASTransport() - smMessage := ulNasTransport.PayloadContainer.GetPayloadContainerContents() - anType := smContext.AccessType() - setNewSmContext, err := gmm.CreatePDUSession(ulNasTransport, ue, anType, pduSessionID, smMessage) - if !setNewSmContext { - ue.SmContextList.Delete(pduSessionID) - } - smContext.DeleteULNASTransport() - if err != nil { - logger.CallbackLog.Errorln(err) - } - }() } else { - ue.SmContextList.Delete(pduSessionID) + ue.ProducerLog.Infof("Release SmContext[%d] (Cause: %s)", pduSessionID, + smContextStatusNotification.StatusInfo.Cause) } + ue.SmContextList.Delete(pduSessionID) } else { problemDetails := &models.ProblemDetails{ Status: http.StatusBadRequest, @@ -118,7 +108,7 @@ func HandleAmPolicyControlUpdateNotifyUpdate(request *httpwrapper.Request) *http func AmPolicyControlUpdateNotifyUpdateProcedure(polAssoID string, policyUpdate models.PolicyUpdate, ) *models.ProblemDetails { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ue, ok := amfSelf.AmfUeFindByPolicyAssociationID(polAssoID) if !ok { @@ -130,6 +120,9 @@ func AmPolicyControlUpdateNotifyUpdateProcedure(polAssoID string, return problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + ue.AmPolicyAssociation.Triggers = policyUpdate.Triggers ue.RequestTriggerLocationChange = false @@ -153,6 +146,13 @@ func AmPolicyControlUpdateNotifyUpdateProcedure(polAssoID string, if ue != nil { // use go routine to write response first to ensure the order of the procedure go func() { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.CallbackLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + } + }() + // UE is CM-Connected State if ue.CmConnect(models.AccessType__3_GPP_ACCESS) { gmm_message.SendConfigurationUpdateCommand(ue, models.AccessType__3_GPP_ACCESS, nil) @@ -199,7 +199,7 @@ func HandleAmPolicyControlUpdateNotifyTerminate(request *httpwrapper.Request) *h func AmPolicyControlUpdateNotifyTerminateProcedure(polAssoID string, terminationNotification models.TerminationNotification, ) *models.ProblemDetails { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ue, ok := amfSelf.AmfUeFindByPolicyAssociationID(polAssoID) if !ok { @@ -211,10 +211,20 @@ func AmPolicyControlUpdateNotifyTerminateProcedure(polAssoID string, return problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + logger.CallbackLog.Infof("Cause of AM Policy termination[%+v]", terminationNotification.Cause) // use go routine to write response first to ensure the order of the procedure go func() { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.CallbackLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + } + }() + problem, err := consumer.AMPolicyControlDelete(ue) if problem != nil { logger.ProducerLog.Errorf("AM Policy Control Delete Failed Problem[%+v]", problem) @@ -242,7 +252,7 @@ func HandleN1MessageNotify(request *httpwrapper.Request) *httpwrapper.Response { func N1MessageNotifyProcedure(n1MessageNotify models.N1MessageNotify) *models.ProblemDetails { logger.ProducerLog.Debugf("n1MessageNotify: %+v", n1MessageNotify) - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() registrationCtxtContainer := n1MessageNotify.JsonData.RegistrationCtxtContainer if registrationCtxtContainer.UeContext == nil { @@ -265,6 +275,13 @@ func N1MessageNotifyProcedure(n1MessageNotify models.N1MessageNotify) *models.Pr } go func() { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.CallbackLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + } + }() + var amfUe *context.AmfUe ueContext := registrationCtxtContainer.UeContext if ueContext.Supi != "" { @@ -272,6 +289,10 @@ func N1MessageNotifyProcedure(n1MessageNotify models.N1MessageNotify) *models.Pr } else { amfUe = amfSelf.NewAmfUe("") } + + amfUe.Lock.Lock() + defer amfUe.Lock.Unlock() + amfUe.CopyDataFromUeContextModel(*ueContext) ranUe := ran.RanUeFindByRanUeNgapID(int64(registrationCtxtContainer.AnN2ApId)) @@ -292,7 +313,7 @@ func N1MessageNotifyProcedure(n1MessageNotify models.N1MessageNotify) *models.Pr gmm_common.AttachRanUeToAmfUeAndReleaseOldIfAny(amfUe, ranUe) - nas.HandleNAS(ranUe, ngapType.ProcedureCodeInitialUEMessage, n1MessageNotify.BinaryDataN1Message, true) + amf_nas.HandleNAS(ranUe, ngapType.ProcedureCodeInitialUEMessage, n1MessageNotify.BinaryDataN1Message, true) }() return nil } diff --git a/internal/sbi/producer/callback/subscription.go b/internal/sbi/producer/callback/subscription.go index 45f6544e..67c1a311 100644 --- a/internal/sbi/producer/callback/subscription.go +++ b/internal/sbi/producer/callback/subscription.go @@ -11,7 +11,7 @@ import ( ) func SendAmfStatusChangeNotify(amfStatus string, guamiList []models.Guami) { - amfSelf := amf_context.AMF_Self() + amfSelf := amf_context.GetSelf() amfSelf.AMFStatusSubscriptions.Range(func(key, value interface{}) bool { subscriptionData := value.(models.SubscriptionData) diff --git a/internal/sbi/producer/event_exposure.go b/internal/sbi/producer/event_exposure.go index bec0aa2e..cfc4f22a 100644 --- a/internal/sbi/producer/event_exposure.go +++ b/internal/sbi/producer/event_exposure.go @@ -32,7 +32,7 @@ func HandleCreateAMFEventSubscription(request *httpwrapper.Request) *httpwrapper func CreateAMFEventSubscriptionProcedure(createEventSubscription models.AmfCreateEventSubscription) ( *models.AmfCreatedEventSubscription, *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() createdEventSubscription := &models.AmfCreatedEventSubscription{} subscription := createEventSubscription.Subscription @@ -73,9 +73,11 @@ func CreateAMFEventSubscriptionProcedure(createEventSubscription models.AmfCreat ueEventSubscription.AnyUe = true amfSelf.UePool.Range(func(key, value interface{}) bool { ue := value.(*context.AmfUe) + ue.Lock.Lock() ue.EventSubscriptionsInfo[newSubscriptionID] = new(context.AmfUeEventSubscription) *ue.EventSubscriptionsInfo[newSubscriptionID] = ueEventSubscription contextEventSubscription.UeSupiList = append(contextEventSubscription.UeSupiList, ue.Supi) + ue.Lock.Unlock() return true }) } else if subscription.GroupId != "" { @@ -83,11 +85,13 @@ func CreateAMFEventSubscriptionProcedure(createEventSubscription models.AmfCreat ueEventSubscription.AnyUe = true amfSelf.UePool.Range(func(key, value interface{}) bool { ue := value.(*context.AmfUe) + ue.Lock.Lock() if ue.GroupID == subscription.GroupId { ue.EventSubscriptionsInfo[newSubscriptionID] = new(context.AmfUeEventSubscription) *ue.EventSubscriptionsInfo[newSubscriptionID] = ueEventSubscription contextEventSubscription.UeSupiList = append(contextEventSubscription.UeSupiList, ue.Supi) } + ue.Lock.Unlock() return true }) } else { @@ -98,9 +102,11 @@ func CreateAMFEventSubscriptionProcedure(createEventSubscription models.AmfCreat } return nil, problemDetails } else { + ue.Lock.Lock() ue.EventSubscriptionsInfo[newSubscriptionID] = new(context.AmfUeEventSubscription) *ue.EventSubscriptionsInfo[newSubscriptionID] = ueEventSubscription contextEventSubscription.UeSupiList = append(contextEventSubscription.UeSupiList, ue.Supi) + ue.Lock.Unlock() } } @@ -119,12 +125,15 @@ func CreateAMFEventSubscriptionProcedure(createEventSubscription models.AmfCreat if subscription.AnyUE { amfSelf.UePool.Range(func(key, value interface{}) bool { ue := value.(*context.AmfUe) + ue.Lock.Lock() + defer ue.Lock.Unlock() + if isImmediate { subReports(ue, newSubscriptionID) } for i, flag := range immediateFlags { if flag { - report, ok := NewAmfEventReport(ue, (*subscription.EventList)[i].Type, newSubscriptionID) + report, ok := newAmfEventReport(ue, (*subscription.EventList)[i].Type, newSubscriptionID) if ok { reportlist = append(reportlist, report) } @@ -139,13 +148,16 @@ func CreateAMFEventSubscriptionProcedure(createEventSubscription models.AmfCreat } else if subscription.GroupId != "" { amfSelf.UePool.Range(func(key, value interface{}) bool { ue := value.(*context.AmfUe) + ue.Lock.Lock() + defer ue.Lock.Unlock() + if isImmediate { subReports(ue, newSubscriptionID) } if ue.GroupID == subscription.GroupId { for i, flag := range immediateFlags { if flag { - report, ok := NewAmfEventReport(ue, (*subscription.EventList)[i].Type, newSubscriptionID) + report, ok := newAmfEventReport(ue, (*subscription.EventList)[i].Type, newSubscriptionID) if ok { reportlist = append(reportlist, report) } @@ -160,12 +172,15 @@ func CreateAMFEventSubscriptionProcedure(createEventSubscription models.AmfCreat }) } else { ue, _ := amfSelf.AmfUeFindBySupi(subscription.Supi) + ue.Lock.Lock() + defer ue.Lock.Unlock() + if isImmediate { subReports(ue, newSubscriptionID) } for i, flag := range immediateFlags { if flag { - report, ok := NewAmfEventReport(ue, (*subscription.EventList)[i].Type, newSubscriptionID) + report, ok := newAmfEventReport(ue, (*subscription.EventList)[i].Type, newSubscriptionID) if ok { reportlist = append(reportlist, report) } @@ -201,7 +216,7 @@ func HandleDeleteAMFEventSubscription(request *httpwrapper.Request) *httpwrapper } func DeleteAMFEventSubscriptionProcedure(subscriptionID string) *models.ProblemDetails { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() subscription, ok := amfSelf.FindEventSubscription(subscriptionID) if !ok { @@ -214,7 +229,9 @@ func DeleteAMFEventSubscriptionProcedure(subscriptionID string) *models.ProblemD for _, supi := range subscription.UeSupiList { if ue, ok := amfSelf.AmfUeFindBySupi(supi); ok { + ue.Lock.Lock() delete(ue.EventSubscriptionsInfo, subscriptionID) + ue.Lock.Unlock() } } amfSelf.DeleteEventSubscription(subscriptionID) @@ -247,7 +264,7 @@ func ModifyAMFEventSubscriptionProcedure( modifySubscriptionRequest models.ModifySubscriptionRequest) ( *models.AmfUpdatedEventSubscription, *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() contextSubscription, ok := amfSelf.FindEventSubscription(subscriptionID) if !ok { @@ -313,7 +330,7 @@ func subReports(ue *context.AmfUe, subscriptionId string) { } // DO NOT handle AmfEventType_PRESENCE_IN_AOI_REPORT and AmfEventType_UES_IN_AREA_REPORT(about area) -func NewAmfEventReport(ue *context.AmfUe, Type models.AmfEventType, subscriptionId string) ( +func newAmfEventReport(ue *context.AmfUe, Type models.AmfEventType, subscriptionId string) ( report models.AmfEventReport, ok bool, ) { ueSubscription, ok := ue.EventSubscriptionsInfo[subscriptionId] diff --git a/internal/sbi/producer/location_info.go b/internal/sbi/producer/location_info.go index 9551ccf1..f91a5205 100644 --- a/internal/sbi/producer/location_info.go +++ b/internal/sbi/producer/location_info.go @@ -26,10 +26,11 @@ func HandleProvideLocationInfoRequest(request *httpwrapper.Request) *httpwrapper func ProvideLocationInfoProcedure(requestLocInfo models.RequestLocInfo, ueContextID string) ( *models.ProvideLocInfo, *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) if !ok { + logger.CtxLog.Warnf("AmfUe Context[%s] not found", ueContextID) problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -37,6 +38,9 @@ func ProvideLocationInfoProcedure(requestLocInfo models.RequestLocInfo, ueContex return nil, problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + anType := ue.GetAnType() if anType == "" { problemDetails := &models.ProblemDetails{ diff --git a/internal/sbi/producer/mt.go b/internal/sbi/producer/mt.go index ae91ca7f..b7c33b8e 100644 --- a/internal/sbi/producer/mt.go +++ b/internal/sbi/producer/mt.go @@ -28,10 +28,11 @@ func HandleProvideDomainSelectionInfoRequest(request *httpwrapper.Request) *http func ProvideDomainSelectionInfoProcedure(ueContextID string, infoClassQuery string, supportedFeaturesQuery string) ( *models.UeContextInfo, *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) if !ok { + logger.CtxLog.Warnf("AmfUe Context[%s] not found", ueContextID) problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -39,6 +40,9 @@ func ProvideDomainSelectionInfoProcedure(ueContextID string, infoClassQuery stri return nil, problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + ueContextInfo := new(models.UeContextInfo) // TODO: Error Status 307, 403 in TS29.518 Table 6.3.3.3.3.1-3 diff --git a/internal/sbi/producer/n1n2message.go b/internal/sbi/producer/n1n2message.go index 0f9b2e31..fa31690a 100644 --- a/internal/sbi/producer/n1n2message.go +++ b/internal/sbi/producer/n1n2message.go @@ -58,6 +58,7 @@ func HandleN1N2MessageTransferRequest(request *httpwrapper.Request) *httpwrapper // response // - problemDetails: if AMF reject the request due to application error, e.g. UE context not found. // - TransferErr: if AMF reject the request due to procedure error, e.g. UE has an ongoing procedure. +// // see TS 29.518 6.1.3.5.3.1 for more details. func N1N2MessageTransferProcedure(ueContextID string, reqUri string, n1n2MessageTransferRequest models.N1N2MessageTransferRequest) ( @@ -77,9 +78,10 @@ func N1N2MessageTransferProcedure(ueContextID string, reqUri string, anType models.AccessType = models.AccessType__3_GPP_ACCESS ) - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if ue, ok = amfSelf.AmfUeFindByUeContextID(ueContextID); !ok { + logger.CtxLog.Warnf("AmfUe Context[%s] not found", ueContextID) problemDetails = &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -96,6 +98,7 @@ func N1N2MessageTransferProcedure(ueContextID string, reqUri string, ue.ProducerLog.Debugf("Receive N1 SM Message (PDU Session ID: %d)", requestData.PduSessionId) n1MsgType = nasMessage.PayloadContainerTypeN1SMInfo if smContext, ok = ue.SmContextFindByPDUSessionID(requestData.PduSessionId); !ok { + ue.ProducerLog.Errorf("SmContext[PDU Session ID:%d] not found", requestData.PduSessionId) problemDetails = &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -120,6 +123,7 @@ func N1N2MessageTransferProcedure(ueContextID string, reqUri string, ue.ProducerLog.Debugf("Receive N2 SM Message (PDU Session ID: %d)", requestData.PduSessionId) if smContext == nil { if smContext, ok = ue.SmContextFindByPDUSessionID(requestData.PduSessionId); !ok { + ue.ProducerLog.Errorf("SmContext[PDU Session ID:%d] not found", requestData.PduSessionId) problemDetails = &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -130,8 +134,7 @@ func N1N2MessageTransferProcedure(ueContextID string, reqUri string, } } default: - ue.ProducerLog.Warnf("N2 Information type [%s] is not supported", - requestData.N2InfoContainer.N2InformationClass) + ue.ProducerLog.Warnf("N2 Information type [%s] is not supported", requestData.N2InfoContainer.N2InformationClass) problemDetails = &models.ProblemDetails{ Status: http.StatusNotImplemented, Cause: "NOT_IMPLEMENTED", @@ -153,7 +156,7 @@ func N1N2MessageTransferProcedure(ueContextID string, reqUri string, } return nil, "", nil, transferErr } - ue.T3513.Stop() + ue.StopT3513() callback.SendN1N2TransferFailureNotification(ue, models.N1N2MessageTransferCause_UE_NOT_RESPONDING) case context.OnGoingProcedureRegistration: transferErr = new(models.N1N2MessageTransferError) @@ -178,8 +181,8 @@ func N1N2MessageTransferProcedure(ueContextID string, reqUri string, err error ) if n1Msg != nil { - nasPdu, err = gmm_message.BuildDLNASTransport( - ue, anType, n1MsgType, n1Msg, uint8(requestData.PduSessionId), nil, nil, 0) + nasPdu, err = gmm_message. + BuildDLNASTransport(ue, anType, n1MsgType, n1Msg, uint8(requestData.PduSessionId), nil, nil, 0) if err != nil { ue.ProducerLog.Errorf("Build DL NAS Transport error: %+v", err) problemDetails = &models.ProblemDetails{ @@ -205,17 +208,15 @@ func N1N2MessageTransferProcedure(ueContextID string, reqUri string, switch smInfo.N2InfoContent.NgapIeType { case models.NgapIeType_PDU_RES_SETUP_REQ: ue.ProducerLog.Debugln("AMF Transfer NGAP PDU Session Resource Setup Request from SMF") - if ue.RanUe[anType].SentInitialContextSetupRequest { + if ue.RanUe[anType].InitialContextSetup { list := ngapType.PDUSessionResourceSetupListSUReq{} - ngap_message.AppendPDUSessionResourceSetupListSUReq( - &list, smInfo.PduSessionId, *smInfo.SNssai, nasPdu, n2Info) - ngap_message.SendPDUSessionResourceSetupRequest(ue.RanUe[anType], nil, list) + ngap_message.AppendPDUSessionResourceSetupListSUReq(&list, smInfo.PduSessionId, *smInfo.SNssai, nasPdu, n2Info) + ngap_message.SendPDUSessionResourceSetupRequest(ue.RanUe[anType], nil, &list) } else { list := ngapType.PDUSessionResourceSetupListCxtReq{} - ngap_message.AppendPDUSessionResourceSetupListCxtReq( - &list, smInfo.PduSessionId, *smInfo.SNssai, nasPdu, n2Info) + ngap_message.AppendPDUSessionResourceSetupListCxtReq(&list, smInfo.PduSessionId, *smInfo.SNssai, nasPdu, n2Info) ngap_message.SendInitialContextSetupRequest(ue, anType, nil, &list, nil, nil, nil) - ue.RanUe[anType].SentInitialContextSetupRequest = true + ue.RanUe[anType].InitialContextSetup = true } n1n2MessageTransferRspData = new(models.N1N2MessageTransferRspData) n1n2MessageTransferRspData.Cause = models.N1N2MessageTransferCause_N1_N2_TRANSFER_INITIATED @@ -250,8 +251,7 @@ func N1N2MessageTransferProcedure(ueContextID string, reqUri string, // UE is CM-IDLE // 409: transfer a N2 PDU Session Resource Release Command to a 5G-AN and if the UE is in CM-IDLE - if n2Info != nil && - requestData.N2InfoContainer.SmInfo.N2InfoContent.NgapIeType == models.NgapIeType_PDU_RES_REL_CMD { + if n2Info != nil && requestData.N2InfoContainer.SmInfo.N2InfoContent.NgapIeType == models.NgapIeType_PDU_RES_REL_CMD { transferErr = new(models.N1N2MessageTransferError) transferErr.Error = &models.ProblemDetails{ Status: http.StatusConflict, @@ -285,7 +285,7 @@ func N1N2MessageTransferProcedure(ueContextID string, reqUri string, } else { n1n2MessageID = n1n2MessageIDTmp } - locationHeader = context.AMF_Self().GetIPv4Uri() + reqUri + "/" + strconv.Itoa(int(n1n2MessageID)) + locationHeader = context.GetSelf().GetIPv4Uri() + reqUri + "/" + strconv.Itoa(int(n1n2MessageID)) // Case A (UE is CM-IDLE in 3GPP access and the associated access type is 3GPP access) // in subclause 5.2.2.3.1.2 of TS29518 @@ -388,10 +388,11 @@ func HandleN1N2MessageTransferStatusRequest(request *httpwrapper.Request) *httpw func N1N2MessageTransferStatusProcedure(ueContextID string, reqUri string) (models.N1N2MessageTransferCause, *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) if !ok { + logger.CtxLog.Warnf("AmfUe Context[%s] not found", ueContextID) problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -399,6 +400,9 @@ func N1N2MessageTransferStatusProcedure(ueContextID string, reqUri string) (mode return "", problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + resourceUri := amfSelf.GetIPv4Uri() + reqUri n1n2Message := ue.N1N2Message if n1n2Message == nil || n1n2Message.ResourceUri != resourceUri { @@ -430,10 +434,11 @@ func N1N2MessageSubscribeProcedure(ueContextID string, ueN1N2InfoSubscriptionCreateData models.UeN1N2InfoSubscriptionCreateData) ( *models.UeN1N2InfoSubscriptionCreatedData, *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) if !ok { + logger.CtxLog.Warnf("AmfUe Context[%s] not found", ueContextID) problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -441,6 +446,9 @@ func N1N2MessageSubscribeProcedure(ueContextID string, return nil, problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + ueN1N2InfoSubscriptionCreatedData := new(models.UeN1N2InfoSubscriptionCreatedData) if newSubscriptionID, err := ue.N1N2MessageSubscribeIDGenerator.Allocate(); err != nil { @@ -472,10 +480,11 @@ func HandleN1N2MessageUnSubscribeRequest(request *httpwrapper.Request) *httpwrap } func N1N2MessageUnSubscribeProcedure(ueContextID string, subscriptionID string) *models.ProblemDetails { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) if !ok { + logger.CtxLog.Warnf("AmfUe Context[%s] not found", ueContextID) problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -483,6 +492,9 @@ func N1N2MessageUnSubscribeProcedure(ueContextID string, subscriptionID string) return problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + ue.N1N2MessageSubscription.Delete(subscriptionID) return nil } diff --git a/internal/sbi/producer/oam.go b/internal/sbi/producer/oam.go index 8ab9bff9..59a63832 100644 --- a/internal/sbi/producer/oam.go +++ b/internal/sbi/producer/oam.go @@ -49,10 +49,11 @@ func HandleOAMRegisteredUEContext(request *httpwrapper.Request) *httpwrapper.Res func OAMRegisteredUEContextProcedure(supi string) (UEContexts, *models.ProblemDetails) { var ueContexts UEContexts - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if supi != "" { if ue, ok := amfSelf.AmfUeFindBySupi(supi); ok { + ue.Lock.Lock() ueContext := buildUEContext(ue, models.AccessType__3_GPP_ACCESS) if ueContext != nil { ueContexts = append(ueContexts, *ueContext) @@ -61,6 +62,7 @@ func OAMRegisteredUEContextProcedure(supi string) (UEContexts, *models.ProblemDe if ueContext != nil { ueContexts = append(ueContexts, *ueContext) } + ue.Lock.Unlock() } else { problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, @@ -71,6 +73,7 @@ func OAMRegisteredUEContextProcedure(supi string) (UEContexts, *models.ProblemDe } else { amfSelf.UePool.Range(func(key, value interface{}) bool { ue := value.(*context.AmfUe) + ue.Lock.Lock() ueContext := buildUEContext(ue, models.AccessType__3_GPP_ACCESS) if ueContext != nil { ueContexts = append(ueContexts, *ueContext) @@ -79,6 +82,7 @@ func OAMRegisteredUEContextProcedure(supi string) (UEContexts, *models.ProblemDe if ueContext != nil { ueContexts = append(ueContexts, *ueContext) } + ue.Lock.Unlock() return true }) } diff --git a/internal/sbi/producer/subscription.go b/internal/sbi/producer/subscription.go index 4f586f52..2c6b0c65 100644 --- a/internal/sbi/producer/subscription.go +++ b/internal/sbi/producer/subscription.go @@ -30,7 +30,7 @@ func HandleAMFStatusChangeSubscribeRequest(request *httpwrapper.Request) *httpwr func AMFStatusChangeSubscribeProcedure(subscriptionDataReq models.SubscriptionData) ( subscriptionDataRsp models.SubscriptionData, locationHeader string, problemDetails *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() for _, guami := range subscriptionDataReq.GuamiList { for _, servedGumi := range amfSelf.ServedGuamiList { @@ -70,7 +70,7 @@ func HandleAMFStatusChangeUnSubscribeRequest(request *httpwrapper.Request) *http } func AMFStatusChangeUnSubscribeProcedure(subscriptionID string) (problemDetails *models.ProblemDetails) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if _, ok := amfSelf.FindAMFStatusSubscription(subscriptionID); !ok { problemDetails = &models.ProblemDetails{ @@ -103,7 +103,7 @@ func HandleAMFStatusChangeSubscribeModify(request *httpwrapper.Request) *httpwra func AMFStatusChangeSubscribeModifyProcedure(subscriptionID string, subscriptionData models.SubscriptionData) ( *models.SubscriptionData, *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if currentSubscriptionData, ok := amfSelf.FindAMFStatusSubscription(subscriptionID); !ok { problemDetails := &models.ProblemDetails{ diff --git a/internal/sbi/producer/ue_context.go b/internal/sbi/producer/ue_context.go index 97cd4ccc..bf648a85 100644 --- a/internal/sbi/producer/ue_context.go +++ b/internal/sbi/producer/ue_context.go @@ -30,7 +30,7 @@ func HandleCreateUEContextRequest(request *httpwrapper.Request) *httpwrapper.Res func CreateUEContextProcedure(ueContextID string, createUeContextRequest models.CreateUeContextRequest) ( *models.CreateUeContextResponse, *models.UeContextCreateError, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ueContextCreateData := createUeContextRequest.JsonData if ueContextCreateData.UeContext == nil || ueContextCreateData.TargetId == nil || @@ -46,6 +46,9 @@ func CreateUEContextProcedure(ueContextID string, createUeContextRequest models. } // create the UE context in target amf ue := amfSelf.NewAmfUe(ueContextID) + ue.Lock.Lock() + defer ue.Lock.Unlock() + // amfSelf.AmfRanSetByRanId(*ueContextCreateData.TargetId.RanNodeId) // ue.N1N2Message[ueContextId] = &context.N1N2Message{} // ue.N1N2Message[ueContextId].Request.JsonData = &models.N1N2MessageTransferReqData{ @@ -142,7 +145,7 @@ func HandleReleaseUEContextRequest(request *httpwrapper.Request) *httpwrapper.Re } func ReleaseUEContextProcedure(ueContextID string, ueContextRelease models.UeContextRelease) *models.ProblemDetails { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() // TODO: UE is emergency registered and the SUPI is not authenticated if ueContextRelease.Supi != "" { @@ -164,9 +167,9 @@ func ReleaseUEContextProcedure(ueContextID string, ueContextRelease models.UeCon logger.CommLog.Debugf("Release UE Context NGAP cause: %+v", ueContextRelease.NgapCause) - if ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID); ok { - gmm_common.RemoveAmfUe(ue) - } else { + ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) + if !ok { + logger.CtxLog.Warnf("AmfUe Context[%s] not found", ueContextID) problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -174,6 +177,17 @@ func ReleaseUEContextProcedure(ueContextID string, ueContextRelease models.UeCon return problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + + // TODO: TS 23.502 4.11.1.2.3.4 + // If the target CN node is AMF, the AMF invokes the + // "Nsmf_PDUSession_UpdateSMContext request (SUPI, Relocation Cancel Indication) toward the SMF. Based + // on the Relocation Cancel Indication, the target CN node deletes the session resources established during + // handover preparation phase in SMF and UPF. + + gmm_common.RemoveAmfUe(ue, false) + return nil } @@ -195,7 +209,7 @@ func HandleUEContextTransferRequest(request *httpwrapper.Request) *httpwrapper.R func UEContextTransferProcedure(ueContextID string, ueContextTransferRequest models.UeContextTransferRequest) ( *models.UeContextTransferResponse, *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() if ueContextTransferRequest.JsonData == nil { problemDetails := &models.ProblemDetails{ @@ -217,6 +231,7 @@ func UEContextTransferProcedure(ueContextID string, ueContextTransferRequest mod ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) if !ok { + logger.CtxLog.Warnf("AmfUe Context[%s] not found", ueContextID) problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -224,6 +239,9 @@ func UEContextTransferProcedure(ueContextID string, ueContextTransferRequest mod return nil, problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + var ueContextTransferResponse *models.UeContextTransferResponse ueContextTransferResponse.JsonData = new(models.UeContextTransferRspData) ueContextTransferRspData := ueContextTransferResponse.JsonData @@ -417,10 +435,11 @@ func HandleAssignEbiDataRequest(request *httpwrapper.Request) *httpwrapper.Respo func AssignEbiDataProcedure(ueContextID string, assignEbiData models.AssignEbiData) ( *models.AssignedEbiData, *models.AssignEbiError, *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) if !ok { + logger.CtxLog.Warnf("AmfUe Context[%s] not found", ueContextID) problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -428,15 +447,17 @@ func AssignEbiDataProcedure(ueContextID string, assignEbiData models.AssignEbiDa return nil, nil, problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + // TODO: AssignEbiError not used, check it! if _, ok := ue.SmContextFindByPDUSessionID(assignEbiData.PduSessionId); ok { var assignedEbiData *models.AssignedEbiData assignedEbiData.PduSessionId = assignEbiData.PduSessionId return assignedEbiData, nil, nil - } else { - logger.ProducerLog.Errorln("ue.SmContextList is nil") - return nil, nil, nil } + logger.ProducerLog.Errorf("SmContext[PDU Session ID:%d] not found", assignEbiData.PduSessionId) + return nil, nil, nil } // TS 29.518 5.2.2.2.2 @@ -457,7 +478,7 @@ func HandleRegistrationStatusUpdateRequest(request *httpwrapper.Request) *httpwr func RegistrationStatusUpdateProcedure(ueContextID string, ueRegStatusUpdateReqData models.UeRegStatusUpdateReqData) ( *models.UeRegStatusUpdateRspData, *models.ProblemDetails, ) { - amfSelf := context.AMF_Self() + amfSelf := context.GetSelf() // ueContextID must be a 5g GUTI (TS 29.518 6.1.3.2.4.5.1) if !strings.HasPrefix(ueContextID, "5g-guti") { @@ -470,6 +491,7 @@ func RegistrationStatusUpdateProcedure(ueContextID string, ueRegStatusUpdateReqD ue, ok := amfSelf.AmfUeFindByUeContextID(ueContextID) if !ok { + logger.CtxLog.Warnf("AmfUe Context[%s] not found", ueContextID) problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", @@ -477,6 +499,9 @@ func RegistrationStatusUpdateProcedure(ueContextID string, ueRegStatusUpdateReqD return nil, problemDetails } + ue.Lock.Lock() + defer ue.Lock.Unlock() + ueRegStatusUpdateRspData := new(models.UeRegStatusUpdateRspData) if ueRegStatusUpdateReqData.TransferStatus == models.UeContextTransferStatus_TRANSFERRED { @@ -508,7 +533,7 @@ func RegistrationStatusUpdateProcedure(ueContextID string, ueRegStatusUpdateReqD } } - gmm_common.RemoveAmfUe(ue) + gmm_common.RemoveAmfUe(ue, false) } else { // NOT_TRANSFERRED logger.CommLog.Debug("[AMF] RegistrationStatusUpdate: NOT_TRANSFERRED") diff --git a/internal/util/init_context.go b/internal/util/init_context.go deleted file mode 100644 index 2a420d89..00000000 --- a/internal/util/init_context.go +++ /dev/null @@ -1,122 +0,0 @@ -package util - -import ( - "os" - - "github.com/google/uuid" - - "github.com/free5gc/amf/internal/context" - "github.com/free5gc/amf/internal/logger" - "github.com/free5gc/amf/pkg/factory" - "github.com/free5gc/nas/security" - "github.com/free5gc/openapi/models" -) - -func InitAmfContext(context *context.AMFContext) { - config := factory.AmfConfig - logger.UtilLog.Infof("amfconfig Info: Version[%s] Description[%s]", config.Info.Version, config.Info.Description) - configuration := config.Configuration - context.NfId = uuid.New().String() - if configuration.AmfName != "" { - context.Name = configuration.AmfName - } - if configuration.NgapIpList != nil { - context.NgapIpList = configuration.NgapIpList - } else { - context.NgapIpList = []string{"127.0.0.1"} // default localhost - } - sbi := configuration.Sbi - if sbi.Scheme != "" { - context.UriScheme = models.UriScheme(sbi.Scheme) - } else { - logger.UtilLog.Warnln("SBI Scheme has not been set. Using http as default") - context.UriScheme = "http" - } - context.RegisterIPv4 = factory.AMF_DEFAULT_IPV4 // default localhost - context.SBIPort = factory.AMF_DEFAULT_PORT_INT // default port - if sbi != nil { - if sbi.RegisterIPv4 != "" { - context.RegisterIPv4 = sbi.RegisterIPv4 - } - if sbi.Port != 0 { - context.SBIPort = sbi.Port - } - context.BindingIPv4 = os.Getenv(sbi.BindingIPv4) - if context.BindingIPv4 != "" { - logger.UtilLog.Info("Parsing ServerIPv4 address from ENV Variable.") - } else { - context.BindingIPv4 = sbi.BindingIPv4 - if context.BindingIPv4 == "" { - logger.UtilLog.Warn("Error parsing ServerIPv4 address from string. Using the 0.0.0.0 as default.") - context.BindingIPv4 = "0.0.0.0" - } - } - } - serviceNameList := configuration.ServiceNameList - context.InitNFService(serviceNameList, config.Info.Version) - context.ServedGuamiList = configuration.ServedGumaiList - context.SupportTaiLists = configuration.SupportTAIList - for i := range context.SupportTaiLists { - context.SupportTaiLists[i].Tac = TACConfigToModels(context.SupportTaiLists[i].Tac) - } - context.PlmnSupportList = configuration.PlmnSupportList - context.SupportDnnLists = configuration.SupportDnnList - if configuration.NrfUri != "" { - context.NrfUri = configuration.NrfUri - } else { - logger.UtilLog.Warn("NRF Uri is empty! Using localhost as NRF IPv4 address.") - context.NrfUri = factory.AMF_DEFAULT_NRFURI - } - security := configuration.Security - if security != nil { - context.SecurityAlgorithm.IntegrityOrder = getIntAlgOrder(security.IntegrityOrder) - context.SecurityAlgorithm.CipheringOrder = getEncAlgOrder(security.CipheringOrder) - } - context.NetworkName = configuration.NetworkName - context.T3502Value = configuration.T3502Value - context.T3512Value = configuration.T3512Value - context.Non3gppDeregistrationTimerValue = configuration.Non3gppDeregistrationTimerValue - context.T3513Cfg = configuration.T3513 - context.T3522Cfg = configuration.T3522 - context.T3550Cfg = configuration.T3550 - context.T3560Cfg = configuration.T3560 - context.T3565Cfg = configuration.T3565 - context.T3570Cfg = configuration.T3570 - context.Locality = configuration.Locality -} - -func getIntAlgOrder(integrityOrder []string) (intOrder []uint8) { - for _, intAlg := range integrityOrder { - switch intAlg { - case "NIA0": - intOrder = append(intOrder, security.AlgIntegrity128NIA0) - case "NIA1": - intOrder = append(intOrder, security.AlgIntegrity128NIA1) - case "NIA2": - intOrder = append(intOrder, security.AlgIntegrity128NIA2) - case "NIA3": - intOrder = append(intOrder, security.AlgIntegrity128NIA3) - default: - logger.UtilLog.Errorf("Unsupported algorithm: %s", intAlg) - } - } - return -} - -func getEncAlgOrder(cipheringOrder []string) (encOrder []uint8) { - for _, encAlg := range cipheringOrder { - switch encAlg { - case "NEA0": - encOrder = append(encOrder, security.AlgCiphering128NEA0) - case "NEA1": - encOrder = append(encOrder, security.AlgCiphering128NEA1) - case "NEA2": - encOrder = append(encOrder, security.AlgCiphering128NEA2) - case "NEA3": - encOrder = append(encOrder, security.AlgCiphering128NEA3) - default: - logger.UtilLog.Errorf("Unsupported algorithm: %s", encAlg) - } - } - return -} diff --git a/internal/util/path.go b/internal/util/path.go deleted file mode 100644 index 7538198a..00000000 --- a/internal/util/path.go +++ /dev/null @@ -1,11 +0,0 @@ -//go:build !debug -// +build !debug - -package util - -var ( - AmfDefaultKeyLogPath = "./log/amfsslkey.log" - AmfDefaultPemPath = "./config/TLS/amf.pem" - AmfDefaultKeyPath = "./config/TLS/amf.key" - AmfDefaultConfigPath = "./config/amfcfg.yaml" -) diff --git a/internal/util/path_debug.go b/internal/util/path_debug.go deleted file mode 100644 index a891e3e6..00000000 --- a/internal/util/path_debug.go +++ /dev/null @@ -1,11 +0,0 @@ -//go:build debug -// +build debug - -package util - -var ( - AmfDefaultKeyLogPath = "./log/amfsslkey.log" - AmfDefaultPemPath = "./config/TLS/_debug.pem" - AmfDefaultKeyPath = "./config/TLS/_debug.key" - AmfDefaultConfigPath = "./config/amfcfg.yaml" -) diff --git a/internal/util/test/testAmfcfg.yaml b/internal/util/test/testAmfcfg.yaml deleted file mode 100644 index de456612..00000000 --- a/internal/util/test/testAmfcfg.yaml +++ /dev/null @@ -1,45 +0,0 @@ -info: - version: 1.0.0 - description: AMF initial local configuration - -configuration: - amfName: AMF - ngapIpList: - - 127.0.0.1 - sbi: - scheme: https - ipv4Addr: 127.0.0.1 - port: 29518 - serviceNameList: - - namf-comm - - namf-evts - - namf-mt - - namf-loc - servedGuamiList: - - plmnId: - mcc: 208 - mnc: 93 - amfId: cafe00 - supportTaiList: - - plmnId: - mcc: 208 - mnc: 93 - tac: 000001 - plmnSupportList: - - plmnId: - mcc: 208 - mnc: 93 - snssaiList: - - sst: 1 - sd: 010203 - supportDnnList: - - internet - nrfUri: https://localhost:29510 - security: - integrityOrder: - - NIA2 - cipheringOrder: - - NEA2 - networkName: - full: free5GC - short: free diff --git a/internal/util/test/testAmfcfg2.yaml b/internal/util/test/testAmfcfg2.yaml deleted file mode 100644 index f42019ec..00000000 --- a/internal/util/test/testAmfcfg2.yaml +++ /dev/null @@ -1,67 +0,0 @@ -info: - version: 2.0.0 - description: AMF initial local configuration - -configuration: - amfName: Wirelab - ngapIpList: - - 127.0.0.1 - - 192.188.2.2 - sbi: - scheme: http - ipv4Addr: 192.168.0.1 - port: 8888 - serviceNameList: - - namf-comm - - namf-evts - servedGuamiList: - - plmnId: - mcc: 208 - mnc: 93 - amfId: cafe00 - - plmnId: - mcc: 466 - mnc: 46 - amfId: 123456 - supportTaiList: - - plmnId: - mcc: 208 - mnc: 93 - tac: 1 - - plmnId: - mcc: 208 - mnc: 93 - tac: 258 - - plmnId: - mcc: 466 - mnc: 46 - tac: 513 - plmnSupportList: - - plmnId: - mcc: 208 - mnc: 93 - snssaiList: - - sst: 1 - sd: 010203 - - sst: 2 - sd: 112233 - - plmnId: - mcc: 466 - mnc: 46 - snssaiList: - - sst: 2 - sd: 445566 - supportDnnList: - - internet - - wire.cs.nctu.edu.tw - nrfUri: https://192.168.0.2:29510 - security: - integrityOrder: - - NIA2 - - NIA1 - cipheringOrder: - - NEA2 - - NEA3 - - EEA2 - networkName: - full: HAHAHAHA diff --git a/pkg/factory/config.go b/pkg/factory/config.go index 27f181f6..62ad3f0a 100644 --- a/pkg/factory/config.go +++ b/pkg/factory/config.go @@ -6,87 +6,97 @@ package factory import ( "fmt" + "os" + "strconv" + "sync" "time" "github.com/asaskevich/govalidator" + "github.com/davecgh/go-spew/spew" + "github.com/free5gc/amf/internal/logger" "github.com/free5gc/openapi/models" - logger_util "github.com/free5gc/util/logger" ) const ( - AMF_EXPECTED_CONFIG_VERSION = "1.0.3" - AMF_DEFAULT_IPV4 = "127.0.0.18" - AMF_DEFAULT_PORT = "8000" - AMF_DEFAULT_PORT_INT = 8000 - AMF_DEFAULT_NRFURI = "https://127.0.0.10:8000" + AmfDefaultTLSKeyLogPath = "./log/amfsslkey.log" + AmfDefaultCertPemPath = "./cert/amf.pem" + AmfDefaultPrivateKeyPath = "./cert/amf.key" + AmfDefaultConfigPath = "./config/amfcfg.yaml" + AmfSbiDefaultIPv4 = "127.0.0.18" + AmfSbiDefaultPort = 8000 + AmfSbiDefaultScheme = "https" + AmfDefaultNrfUri = "https://127.0.0.10:8000" + sctpDefaultNumOstreams = 3 + sctpDefaultMaxInstreams = 5 + sctpDefaultMaxAttempts = 2 + sctpDefaultMaxInitTimeout = 2 + ngapDefaultPort = 38412 + AmfCallbackResUriPrefix = "/namf-callback/v1" + AmfCommResUriPrefix = "/namf-comm/v1" + AmfEvtsResUriPrefix = "/namf-evts/v1" + AmfLocResUriPrefix = "/namf-loc/v1" + AmfMtResUriPrefix = "/namf-mt/v1" + AmfOamResUriPrefix = "/namf-oam/v1" ) type Config struct { - Info *Info `yaml:"info" valid:"required"` - Configuration *Configuration `yaml:"configuration" valid:"required"` - Logger *logger_util.Logger `yaml:"logger" valid:"required"` + Info *Info `yaml:"info" valid:"required"` + Configuration *Configuration `yaml:"configuration" valid:"required"` + Logger *Logger `yaml:"logger" valid:"required"` + sync.RWMutex } func (c *Config) Validate() (bool, error) { - info := c.Info - if _, err := info.validate(); err != nil { - return false, err - } - - Configuration := c.Configuration - if _, err := Configuration.validate(); err != nil { - return false, err - } - - Logger := c.Logger - if _, err := Logger.Validate(); err != nil { - return false, err - } - - if _, err := govalidator.ValidateStruct(c); err != nil { - return false, appendInvalid(err) + if configuration := c.Configuration; configuration != nil { + if result, err := configuration.validate(); err != nil { + return result, err + } } - return true, nil + result, err := govalidator.ValidateStruct(c) + return result, appendInvalid(err) } type Info struct { - Version string `yaml:"version,omitempty" valid:"required"` - Description string `yaml:"description,omitempty" valid:"-"` + Version string `yaml:"version,omitempty" valid:"required,in(1.0.9)"` + Description string `yaml:"description,omitempty" valid:"type(string)"` } -func (i *Info) validate() (bool, error) { - if _, err := govalidator.ValidateStruct(i); err != nil { - return false, appendInvalid(err) - } - - return true, nil +type Configuration struct { + AmfName string `yaml:"amfName,omitempty" valid:"required, type(string)"` + NgapIpList []string `yaml:"ngapIpList,omitempty" valid:"required"` + NgapPort int `yaml:"ngapPort,omitempty" valid:"optional,port"` + Sbi *Sbi `yaml:"sbi,omitempty" valid:"required"` + ServiceNameList []string `yaml:"serviceNameList,omitempty" valid:"required"` + ServedGumaiList []models.Guami `yaml:"servedGuamiList,omitempty" valid:"required"` + SupportTAIList []models.Tai `yaml:"supportTaiList,omitempty" valid:"required"` + PlmnSupportList []PlmnSupportItem `yaml:"plmnSupportList,omitempty" valid:"required"` + SupportDnnList []string `yaml:"supportDnnList,omitempty" valid:"required"` + SupportLadnList []Ladn `yaml:"supportLadnList,omitempty" valid:"optional"` + NrfUri string `yaml:"nrfUri,omitempty" valid:"required, url"` + Security *Security `yaml:"security,omitempty" valid:"required"` + NetworkName NetworkName `yaml:"networkName,omitempty" valid:"required"` + NgapIE *NgapIE `yaml:"ngapIE,omitempty" valid:"optional"` + NasIE *NasIE `yaml:"nasIE,omitempty" valid:"optional"` + T3502Value int `yaml:"t3502Value,omitempty" valid:"required, type(int)"` + T3512Value int `yaml:"t3512Value,omitempty" valid:"required, type(int)"` + Non3gppDeregTimerValue int `yaml:"non3gppDeregTimerValue,omitempty" valid:"-"` + T3513 TimerValue `yaml:"t3513" valid:"required"` + T3522 TimerValue `yaml:"t3522" valid:"required"` + T3550 TimerValue `yaml:"t3550" valid:"required"` + T3560 TimerValue `yaml:"t3560" valid:"required"` + T3565 TimerValue `yaml:"t3565" valid:"required"` + T3570 TimerValue `yaml:"t3570" 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"` } -type Configuration struct { - AmfName string `yaml:"amfName,omitempty" valid:"required, type(string)"` - NgapIpList []string `yaml:"ngapIpList,omitempty" valid:"required"` - Sbi *Sbi `yaml:"sbi,omitempty" valid:"required"` - NetworkFeatureSupport5GS *NetworkFeatureSupport5GS `yaml:"networkFeatureSupport5GS,omitempty" valid:"optional"` - ServiceNameList []string `yaml:"serviceNameList,omitempty" valid:"required"` - ServedGumaiList []models.Guami `yaml:"servedGuamiList,omitempty" valid:"required"` - SupportTAIList []models.Tai `yaml:"supportTaiList,omitempty" valid:"required"` - PlmnSupportList []PlmnSupportItem `yaml:"plmnSupportList,omitempty" valid:"required"` - SupportDnnList []string `yaml:"supportDnnList,omitempty" valid:"required"` - NrfUri string `yaml:"nrfUri,omitempty" valid:"required, url"` - Security *Security `yaml:"security,omitempty" valid:"required"` - NetworkName NetworkName `yaml:"networkName,omitempty" valid:"required"` - T3502Value int `yaml:"t3502Value,omitempty" valid:"required, type(int)"` - T3512Value int `yaml:"t3512Value,omitempty" valid:"required, type(int)"` - Non3gppDeregistrationTimerValue int `yaml:"non3gppDeregistrationTimerValue,omitempty" valid:"-"` - T3513 TimerValue `yaml:"t3513" valid:"required"` - T3522 TimerValue `yaml:"t3522" valid:"required"` - T3550 TimerValue `yaml:"t3550" valid:"required"` - T3560 TimerValue `yaml:"t3560" valid:"required"` - T3565 TimerValue `yaml:"t3565" valid:"required"` - T3570 TimerValue `yaml:"t3570" valid:"required"` - Locality string `yaml:"locality,omitempty" valid:"type(string),optional"` +type Logger struct { + Enable bool `yaml:"enable" valid:"type(bool)"` + Level string `yaml:"level" valid:"required,in(trace|debug|info|warn|error|fatal|panic)"` + ReportCaller bool `yaml:"reportCaller" valid:"type(bool)"` } func (c *Configuration) validate() (bool, error) { @@ -109,12 +119,6 @@ func (c *Configuration) validate() (bool, error) { } } - if c.NetworkFeatureSupport5GS != nil { - if _, err := c.NetworkFeatureSupport5GS.validate(); err != nil { - return false, err - } - } - if c.ServiceNameList != nil { var errs govalidator.Errors for _, v := range c.ServiceNameList { @@ -178,8 +182,8 @@ func (c *Configuration) validate() (bool, error) { } tac := v.Tac - if result := govalidator.InRangeInt(tac, 0, 16777215); !result { - err := fmt.Errorf("Invalid tac: %s, should be in the range of 0~16777215", tac) + if result := govalidator.StringMatches(tac, "^[A-Fa-f0-9]{6}$"); !result { + err := fmt.Errorf("Invalid tac: %s, should be 3 bytes hex string, range: 000000~FFFFFF", tac) errs = append(errs, err) } } @@ -200,14 +204,26 @@ func (c *Configuration) validate() (bool, error) { } } + if c.SupportLadnList != nil { + var errs govalidator.Errors + for _, v := range c.SupportLadnList { + if _, err := v.validate(); err != nil { + errs = append(errs, err) + } + } + if len(errs) > 0 { + return false, error(errs) + } + } + if c.Security != nil { if _, err := c.Security.validate(); err != nil { return false, err } } - if n3gppVal := &(c.Non3gppDeregistrationTimerValue); n3gppVal == nil { - err := fmt.Errorf("Invalid Non3gppDeregistrationTimerValue: value is required") + if n3gppVal := &(c.Non3gppDeregTimerValue); n3gppVal == nil { + err := fmt.Errorf("Invalid Non3gppDeregTimerValue: value is required") return false, err } @@ -215,6 +231,18 @@ func (c *Configuration) validate() (bool, error) { return false, err } + if c.NgapIE != nil { + if _, err := c.NgapIE.validate(); err != nil { + return false, err + } + } + + if c.NasIE != nil { + if _, err := c.NasIE.validate(); err != nil { + return false, err + } + } + if _, err := c.T3513.validate(); err != nil { return false, err } @@ -239,124 +267,16 @@ func (c *Configuration) validate() (bool, error) { return false, err } - if _, err := govalidator.ValidateStruct(c); err != nil { - return false, appendInvalid(err) - } - - return true, nil -} - -func (c *Configuration) Get5gsNwFeatSuppEnable() bool { - if c.NetworkFeatureSupport5GS != nil { - return c.NetworkFeatureSupport5GS.Enable - } - return true -} - -func (c *Configuration) Get5gsNwFeatSuppImsVoPS() uint8 { - if c.NetworkFeatureSupport5GS != nil { - return c.NetworkFeatureSupport5GS.ImsVoPS - } - return 0 -} - -func (c *Configuration) Get5gsNwFeatSuppEmc() uint8 { - if c.NetworkFeatureSupport5GS != nil { - return c.NetworkFeatureSupport5GS.Emc - } - return 0 -} - -func (c *Configuration) Get5gsNwFeatSuppEmf() uint8 { - if c.NetworkFeatureSupport5GS != nil { - return c.NetworkFeatureSupport5GS.Emf - } - return 0 -} - -func (c *Configuration) Get5gsNwFeatSuppIwkN26() uint8 { - if c.NetworkFeatureSupport5GS != nil { - return c.NetworkFeatureSupport5GS.IwkN26 - } - return 0 -} - -func (c *Configuration) Get5gsNwFeatSuppMpsi() uint8 { - if c.NetworkFeatureSupport5GS != nil { - return c.NetworkFeatureSupport5GS.Mpsi - } - return 0 -} - -func (c *Configuration) Get5gsNwFeatSuppEmcN3() uint8 { - if c.NetworkFeatureSupport5GS != nil { - return c.NetworkFeatureSupport5GS.EmcN3 - } - return 0 -} - -func (c *Configuration) Get5gsNwFeatSuppMcsi() uint8 { - if c.NetworkFeatureSupport5GS != nil { - return c.NetworkFeatureSupport5GS.Mcsi + if c.SCTP != nil { + if _, err := c.SCTP.validate(); err != nil { + return false, err + } } - return 0 -} - -type NetworkFeatureSupport5GS struct { - Enable bool `yaml:"enable" valid:"type(bool)"` - Length uint8 `yaml:"length" valid:"type(uint8)"` - ImsVoPS uint8 `yaml:"imsVoPS" valid:"type(uint8)"` - Emc uint8 `yaml:"emc" valid:"type(uint8)"` - Emf uint8 `yaml:"emf" valid:"type(uint8)"` - IwkN26 uint8 `yaml:"iwkN26" valid:"type(uint8)"` - Mpsi uint8 `yaml:"mpsi" valid:"type(uint8)"` - EmcN3 uint8 `yaml:"emcN3" valid:"type(uint8)"` - Mcsi uint8 `yaml:"mcsi" valid:"type(uint8)"` -} -func (f *NetworkFeatureSupport5GS) validate() (bool, error) { - var errs govalidator.Errors - - if result := govalidator.InRangeInt(f.Length, 1, 3); !result { - err := fmt.Errorf("Invalid length: %d, should be in the range of 1~3", f.Length) - errs = append(errs, err) - } - if result := govalidator.InRangeInt(f.ImsVoPS, 0, 1); !result { - err := fmt.Errorf("Invalid imsVoPS: %d, should be in the range of 0~1", f.ImsVoPS) - errs = append(errs, err) - } - if result := govalidator.InRangeInt(f.Emc, 0, 3); !result { - err := fmt.Errorf("Invalid emc: %d, should be in the range of 0~3", f.Emc) - errs = append(errs, err) - } - if result := govalidator.InRangeInt(f.Emf, 0, 3); !result { - err := fmt.Errorf("Invalid emf: %d, should be in the range of 0~3", f.Emf) - errs = append(errs, err) - } - if result := govalidator.InRangeInt(f.IwkN26, 0, 1); !result { - err := fmt.Errorf("Invalid iwkN26: %d, should be in the range of 0~1", f.IwkN26) - errs = append(errs, err) - } - if result := govalidator.InRangeInt(f.Mpsi, 0, 1); !result { - err := fmt.Errorf("Invalid mpsi: %d, should be in the range of 0~1", f.Mpsi) - errs = append(errs, err) - } - if result := govalidator.InRangeInt(f.EmcN3, 0, 1); !result { - err := fmt.Errorf("Invalid emcN3: %d, should be in the range of 0~1", f.EmcN3) - errs = append(errs, err) - } - if result := govalidator.InRangeInt(f.Mcsi, 0, 1); !result { - err := fmt.Errorf("Invalid mcsi: %d, should be in the range of 0~1", f.Mcsi) - errs = append(errs, err) - } - if _, err := govalidator.ValidateStruct(f); err != nil { + if _, err := govalidator.ValidateStruct(c); err != nil { return false, appendInvalid(err) } - if len(errs) > 0 { - return false, error(errs) - } - return true, nil } @@ -472,6 +392,46 @@ func (p *PlmnSupportItem) validate() (bool, error) { return true, nil } +type Ladn struct { + Dnn string `yaml:"dnn" valid:"type(string),minstringlength(1),required"` + TaiList []models.Tai `yaml:"taiList" valid:"required"` +} + +func (l *Ladn) validate() (bool, error) { + if _, err := govalidator.ValidateStruct(l); err != nil { + return false, appendInvalid(err) + } + + var errs govalidator.Errors + for _, v := range l.TaiList { + if v.PlmnId == nil { + return false, fmt.Errorf("PlmnId is nil") + } + mcc := v.PlmnId.Mcc + if result := govalidator.StringMatches(mcc, "^[0-9]{3}$"); !result { + err := fmt.Errorf("Invalid mcc: %s, should be a 3-digit number", mcc) + errs = append(errs, err) + } + + mnc := v.PlmnId.Mnc + if result := govalidator.StringMatches(mnc, "^[0-9]{2,3}$"); !result { + err := fmt.Errorf("Invalid mnc: %s, should be a 2 or 3-digit number", mnc) + errs = append(errs, err) + } + + tac := v.Tac + if result := govalidator.StringMatches(tac, "^[A-Fa-f0-9]{6}$"); !result { + err := fmt.Errorf("Invalid tac: %s, should be 3 bytes hex string, range: 000000~FFFFFF", tac) + errs = append(errs, err) + } + } + if len(errs) > 0 { + return false, error(errs) + } + + return true, nil +} + type NetworkName struct { Full string `yaml:"full" valid:"type(string)"` Short string `yaml:"short,omitempty" valid:"type(string)"` @@ -485,6 +445,180 @@ func (n *NetworkName) validate() (bool, error) { return true, nil } +type NgapIE struct { + MobilityRestrictionList *MobilityRestrictionList `yaml:"mobilityRestrictionList,omitempty" valid:"optional"` + MaskedIMEISV *MaskedIMEISV `yaml:"maskedIMEISV,omitempty" valid:"optional"` + RedirectionVoiceFallback *RedirectionVoiceFallback `yaml:"redirectionVoiceFallback,omitempty" valid:"optional"` +} + +func (n *NgapIE) validate() (bool, error) { + if n.MobilityRestrictionList != nil { + if _, err := n.MobilityRestrictionList.validate(); err != nil { + return false, err + } + } + + if n.MaskedIMEISV != nil { + if _, err := n.MaskedIMEISV.validate(); err != nil { + return false, err + } + } + + if n.RedirectionVoiceFallback != nil { + if _, err := n.RedirectionVoiceFallback.validate(); err != nil { + return false, err + } + } + + if _, err := govalidator.ValidateStruct(n); err != nil { + return false, appendInvalid(err) + } + + return true, nil +} + +type MobilityRestrictionList struct { + Enable bool `yaml:"enable" valid:"type(bool)"` +} + +func (m *MobilityRestrictionList) validate() (bool, error) { + if _, err := govalidator.ValidateStruct(m); err != nil { + return false, appendInvalid(err) + } + + return true, nil +} + +type MaskedIMEISV struct { + Enable bool `yaml:"enable" valid:"type(bool)"` +} + +func (m *MaskedIMEISV) validate() (bool, error) { + if _, err := govalidator.ValidateStruct(m); err != nil { + return false, appendInvalid(err) + } + + return true, nil +} + +type RedirectionVoiceFallback struct { + Enable bool `yaml:"enable" valid:"type(bool)"` +} + +func (r *RedirectionVoiceFallback) validate() (bool, error) { + if _, err := govalidator.ValidateStruct(r); err != nil { + return false, appendInvalid(err) + } + + return true, nil +} + +type Sctp struct { + NumOstreams uint `yaml:"numOstreams,omitempty" valid:"int"` + MaxInstreams uint `yaml:"maxInstreams,omitempty" valid:"int"` + MaxAttempts uint `yaml:"maxAttempts,omitempty" valid:"int"` + MaxInitTimeout uint `yaml:"maxInitTimeout,omitempty" valid:"int"` +} + +func (n *Sctp) validate() (bool, error) { + var errs govalidator.Errors + if _, err := govalidator.ValidateStruct(n); err != nil { + return false, appendInvalid(err) + } + if n.NumOstreams > 10 || n.NumOstreams <= 0 { + errs = append(errs, fmt.Errorf("0 < configuration.sctp.maxOsStream <=20")) + } + if n.MaxInstreams > 10 || n.MaxInstreams <= 0 { + errs = append(errs, fmt.Errorf("0 < configuration.sctp.maxInputStream <=20")) + } + if n.MaxInitTimeout > 5 || n.MaxInitTimeout <= 0 { + errs = append(errs, fmt.Errorf(" 0 < configuration.sctp.maxInitTimeOut<=5 ")) + } + if n.MaxAttempts > 5 || n.MaxAttempts <= 0 { + errs = append(errs, fmt.Errorf(" 0 < configuration.sctp.maxAttempts <=5 ")) + } + if len(errs) > 0 { + return false, errs + } + return true, nil +} + +type NasIE struct { + NetworkFeatureSupport5GS *NetworkFeatureSupport5GS `yaml:"networkFeatureSupport5GS,omitempty" valid:"optional"` +} + +func (n *NasIE) validate() (bool, error) { + if n.NetworkFeatureSupport5GS != nil { + if _, err := n.NetworkFeatureSupport5GS.validate(); err != nil { + return false, err + } + } + + if _, err := govalidator.ValidateStruct(n); err != nil { + return false, appendInvalid(err) + } + + return true, nil +} + +type NetworkFeatureSupport5GS struct { + Enable bool `yaml:"enable" valid:"type(bool)"` + Length uint8 `yaml:"length" valid:"type(uint8)"` + ImsVoPS uint8 `yaml:"imsVoPS" valid:"type(uint8)"` + Emc uint8 `yaml:"emc" valid:"type(uint8)"` + Emf uint8 `yaml:"emf" valid:"type(uint8)"` + IwkN26 uint8 `yaml:"iwkN26" valid:"type(uint8)"` + Mpsi uint8 `yaml:"mpsi" valid:"type(uint8)"` + EmcN3 uint8 `yaml:"emcN3" valid:"type(uint8)"` + Mcsi uint8 `yaml:"mcsi" valid:"type(uint8)"` +} + +func (f *NetworkFeatureSupport5GS) validate() (bool, error) { + var errs govalidator.Errors + + if result := govalidator.InRangeInt(f.Length, 1, 3); !result { + err := fmt.Errorf("Invalid length: %d, should be in the range of 1~3", f.Length) + errs = append(errs, err) + } + if result := govalidator.InRangeInt(f.ImsVoPS, 0, 1); !result { + err := fmt.Errorf("Invalid imsVoPS: %d, should be in the range of 0~1", f.ImsVoPS) + errs = append(errs, err) + } + if result := govalidator.InRangeInt(f.Emc, 0, 3); !result { + err := fmt.Errorf("Invalid emc: %d, should be in the range of 0~3", f.Emc) + errs = append(errs, err) + } + if result := govalidator.InRangeInt(f.Emf, 0, 3); !result { + err := fmt.Errorf("Invalid emf: %d, should be in the range of 0~3", f.Emf) + errs = append(errs, err) + } + if result := govalidator.InRangeInt(f.IwkN26, 0, 1); !result { + err := fmt.Errorf("Invalid iwkN26: %d, should be in the range of 0~1", f.IwkN26) + errs = append(errs, err) + } + if result := govalidator.InRangeInt(f.Mpsi, 0, 1); !result { + err := fmt.Errorf("Invalid mpsi: %d, should be in the range of 0~1", f.Mpsi) + errs = append(errs, err) + } + if result := govalidator.InRangeInt(f.EmcN3, 0, 1); !result { + err := fmt.Errorf("Invalid emcN3: %d, should be in the range of 0~1", f.EmcN3) + errs = append(errs, err) + } + if result := govalidator.InRangeInt(f.Mcsi, 0, 1); !result { + err := fmt.Errorf("Invalid mcsi: %d, should be in the range of 0~1", f.Mcsi) + errs = append(errs, err) + } + if _, err := govalidator.ValidateStruct(f); err != nil { + return false, appendInvalid(err) + } + + if len(errs) > 0 { + return false, error(errs) + } + + return true, nil +} + type TimerValue struct { Enable bool `yaml:"enable" valid:"type(bool)"` ExpireTime time.Duration `yaml:"expireTime" valid:"type(time.Duration)"` @@ -502,6 +636,10 @@ func (t *TimerValue) validate() (bool, error) { func appendInvalid(err error) error { var errs govalidator.Errors + if err == nil { + return nil + } + es := err.(govalidator.Errors).Errors() for _, e := range es { errs = append(errs, fmt.Errorf("Invalid %w", e)) @@ -510,9 +648,203 @@ func appendInvalid(err error) error { return error(errs) } +func (c *Config) Print() { + spew.Config.Indent = "\t" + str := spew.Sdump(c.Configuration) + logger.CfgLog.Infof("==================================================") + logger.CfgLog.Infof("%s", str) + logger.CfgLog.Infof("==================================================") +} + func (c *Config) GetVersion() string { - if c.Info != nil && c.Info.Version != "" { + c.RLock() + defer c.RUnlock() + + if c.Info.Version != "" { return c.Info.Version } return "" } + +func (c *Config) SetLogEnable(enable bool) { + c.Lock() + defer c.Unlock() + + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + c.Logger = &Logger{ + Enable: enable, + Level: "info", + } + } else { + c.Logger.Enable = enable + } +} + +func (c *Config) SetLogLevel(level string) { + c.Lock() + defer c.Unlock() + + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + c.Logger = &Logger{ + Level: level, + } + } else { + c.Logger.Level = level + } +} + +func (c *Config) SetLogReportCaller(reportCaller bool) { + c.Lock() + defer c.Unlock() + + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + c.Logger = &Logger{ + Level: "info", + ReportCaller: reportCaller, + } + } else { + c.Logger.ReportCaller = reportCaller + } +} + +func (c *Config) GetLogEnable() bool { + c.RLock() + defer c.RUnlock() + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + return false + } + return c.Logger.Enable +} + +func (c *Config) GetLogLevel() string { + c.RLock() + defer c.RUnlock() + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + return "info" + } + return c.Logger.Level +} + +func (c *Config) GetLogReportCaller() bool { + c.RLock() + defer c.RUnlock() + if c.Logger == nil { + logger.CfgLog.Warnf("Logger should not be nil") + return false + } + return c.Logger.ReportCaller +} + +func (c *Config) GetSbiScheme() string { + if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.Scheme != "" { + return c.Configuration.Sbi.Scheme + } + return AmfSbiDefaultScheme +} + +func (c *Config) GetSbiPort() int { + if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.Port != 0 { + return c.Configuration.Sbi.Port + } + return AmfSbiDefaultPort +} + +func (c *Config) GetSbiBindingIP() string { + bindIP := "0.0.0.0" + if c.Configuration == nil || c.Configuration.Sbi == nil { + return bindIP + } + if c.Configuration.Sbi.BindingIPv4 != "" { + if bindIP = os.Getenv(c.Configuration.Sbi.BindingIPv4); bindIP != "" { + logger.CfgLog.Infof("Parsing ServerIPv4 [%s] from ENV Variable", bindIP) + } else { + bindIP = c.Configuration.Sbi.BindingIPv4 + } + } + return bindIP +} + +func (c *Config) GetSbiBindingAddr() string { + return c.GetSbiBindingIP() + ":" + strconv.Itoa(c.GetSbiPort()) +} + +func (c *Config) GetSbiRegisterIP() string { + if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.RegisterIPv4 != "" { + return c.Configuration.Sbi.RegisterIPv4 + } + return AmfSbiDefaultIPv4 +} + +func (c *Config) GetSbiRegisterAddr() string { + return c.GetSbiRegisterIP() + ":" + strconv.Itoa(c.GetSbiPort()) +} + +func (c *Config) GetSbiUri() string { + return c.GetSbiScheme() + "://" + c.GetSbiRegisterAddr() +} + +func (c *Config) GetNrfUri() string { + if c.Configuration != nil && c.Configuration.NrfUri != "" { + return c.Configuration.NrfUri + } + return AmfDefaultNrfUri +} + +func (c *Config) GetServiceNameList() []string { + if c.Configuration != nil && len(c.Configuration.ServiceNameList) > 0 { + return c.Configuration.ServiceNameList + } + return nil +} + +func (c *Config) GetNgapIEMobilityRestrictionList() *MobilityRestrictionList { + if c != nil && c.Configuration != nil && c.Configuration.NgapIE != nil { + return c.Configuration.NgapIE.MobilityRestrictionList + } + return nil +} + +func (c *Config) GetNgapIEMaskedIMEISV() *MaskedIMEISV { + if c.Configuration != nil && c.Configuration.NgapIE != nil { + return c.Configuration.NgapIE.MaskedIMEISV + } + return nil +} + +func (c *Config) GetNgapIERedirectionVoiceFallback() *RedirectionVoiceFallback { + if c.Configuration != nil && c.Configuration.NgapIE != nil { + return c.Configuration.NgapIE.RedirectionVoiceFallback + } + return nil +} + +func (c *Config) GetNasIENetworkFeatureSupport5GS() *NetworkFeatureSupport5GS { + if c.Configuration != nil && c.Configuration.NasIE != nil { + return c.Configuration.NasIE.NetworkFeatureSupport5GS + } + return nil +} + +func (c *Config) GetNgapPort() int { + if c.Configuration.NgapPort != 0 { + return c.Configuration.NgapPort + } + return ngapDefaultPort +} + +func (c *Config) GetSctpConfig() *Sctp { + if c.Configuration != nil && c.Configuration.SCTP != nil { + return c.Configuration.SCTP + } + return &Sctp{ + NumOstreams: sctpDefaultNumOstreams, + MaxInstreams: sctpDefaultMaxInstreams, + MaxAttempts: sctpDefaultMaxAttempts, + MaxInitTimeout: sctpDefaultMaxInitTimeout, + } +} diff --git a/pkg/factory/config_test.go b/pkg/factory/config_test.go new file mode 100644 index 00000000..a0f460d6 --- /dev/null +++ b/pkg/factory/config_test.go @@ -0,0 +1,112 @@ +/* + * AMF Configuration Factory + */ + +package factory + +import ( + "testing" + + "github.com/asaskevich/govalidator" +) + +func TestSctp_validate(t *testing.T) { + type fields struct { + NumOstreams uint + MaxInstreams uint + MaxAttempts uint + MaxInitTimeout uint + } + tests := []struct { + name string + fields fields + want bool + wantErr bool + numErr int + }{ + // TODO: Add test cases. + { + name: "test OK -- Max", + fields: fields{ + NumOstreams: 10, + MaxInstreams: 10, + MaxAttempts: 5, + MaxInitTimeout: 5, + }, + want: true, + wantErr: false, + numErr: 0, + }, + { + name: "test OK -- Min", + fields: fields{ + NumOstreams: 1, + MaxInstreams: 1, + MaxAttempts: 1, + MaxInitTimeout: 1, + }, + want: true, + wantErr: false, + numErr: 0, + }, + { + name: "test Error -- zeros", + fields: fields{ + NumOstreams: 0, + MaxInstreams: 0, + MaxAttempts: 0, + MaxInitTimeout: 0, + }, + want: false, + wantErr: true, + numErr: 4, + }, + { + name: "test Error -- upperbound", + fields: fields{ + NumOstreams: 11, + MaxInstreams: 11, + MaxAttempts: 6, + MaxInitTimeout: 6, + }, + want: false, + wantErr: true, + numErr: 4, + }, + { + name: "test Error -- not set", + fields: fields{ + MaxInstreams: 10, + }, + want: false, + wantErr: true, + numErr: 3, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + n := &Sctp{ + NumOstreams: tt.fields.NumOstreams, + MaxInstreams: tt.fields.MaxInstreams, + MaxAttempts: tt.fields.MaxAttempts, + MaxInitTimeout: tt.fields.MaxInitTimeout, + } + got, err := n.validate() + + if (err != nil) != tt.wantErr { + t.Errorf("Sctp.validate() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.wantErr { + var errs govalidator.Errors = err.(govalidator.Errors) + if len(errs) != tt.numErr { + t.Errorf("Sctp.validate() error = %v, wantErr %v", err, tt.wantErr) + return + } + } + if got != tt.want { + t.Errorf("Sctp.validate() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/factory/factory.go b/pkg/factory/factory.go index 14c578bd..52f3c0ae 100644 --- a/pkg/factory/factory.go +++ b/pkg/factory/factory.go @@ -8,37 +8,46 @@ import ( "fmt" "io/ioutil" + "github.com/asaskevich/govalidator" "gopkg.in/yaml.v2" "github.com/free5gc/amf/internal/logger" ) -var AmfConfig Config +var AmfConfig *Config // TODO: Support configuration update from REST api -func InitConfigFactory(f string) error { +func InitConfigFactory(f string, cfg *Config) error { + if f == "" { + // Use default config path + f = AmfDefaultConfigPath + } + if content, err := ioutil.ReadFile(f); err != nil { - return err + return fmt.Errorf("[Factory] %+v", err) } else { - AmfConfig = Config{} - - if yamlErr := yaml.Unmarshal(content, &AmfConfig); yamlErr != nil { - return yamlErr + logger.CfgLog.Infof("Read config from [%s]", f) + if yamlErr := yaml.Unmarshal(content, cfg); yamlErr != nil { + return fmt.Errorf("[Factory] %+v", yamlErr) } } return nil } -func CheckConfigVersion() error { - currentVersion := AmfConfig.GetVersion() - - if currentVersion != AMF_EXPECTED_CONFIG_VERSION { - return fmt.Errorf("config version is [%s], but expected is [%s].", - currentVersion, AMF_EXPECTED_CONFIG_VERSION) +func ReadConfig(cfgPath string) (*Config, error) { + cfg := &Config{} + if err := InitConfigFactory(cfgPath, cfg); err != nil { + return nil, fmt.Errorf("ReadConfig [%s] Error: %+v", cfgPath, err) + } + if _, err := cfg.Validate(); err != nil { + validErrs := err.(govalidator.Errors).Errors() + for _, validErr := range validErrs { + logger.CfgLog.Errorf("%+v", validErr) + } + logger.CfgLog.Errorf("[-- PLEASE REFER TO SAMPLE CONFIG FILE COMMENTS --]") + return nil, fmt.Errorf("Config validate Error") } - logger.CfgLog.Infof("config version [%s]", currentVersion) - - return nil + return cfg, nil } diff --git a/pkg/service/init.go b/pkg/service/init.go index ffe06b8e..82e79342 100644 --- a/pkg/service/init.go +++ b/pkg/service/init.go @@ -1,20 +1,17 @@ package service import ( - "bufio" "fmt" + "io/ioutil" "os" - "os/exec" "os/signal" "runtime/debug" - "sync" "syscall" "github.com/gin-contrib/cors" "github.com/sirupsen/logrus" - "github.com/urfave/cli" - "github.com/free5gc/amf/internal/context" + amf_context "github.com/free5gc/amf/internal/context" "github.com/free5gc/amf/internal/logger" "github.com/free5gc/amf/internal/ngap" ngap_message "github.com/free5gc/amf/internal/ngap/message" @@ -27,179 +24,71 @@ import ( "github.com/free5gc/amf/internal/sbi/mt" "github.com/free5gc/amf/internal/sbi/oam" "github.com/free5gc/amf/internal/sbi/producer/callback" - "github.com/free5gc/amf/internal/util" "github.com/free5gc/amf/pkg/factory" - aperLogger "github.com/free5gc/aper/logger" - nasLogger "github.com/free5gc/nas/logger" - ngapLogger "github.com/free5gc/ngap/logger" "github.com/free5gc/openapi/models" - fsmLogger "github.com/free5gc/util/fsm/logger" "github.com/free5gc/util/httpwrapper" logger_util "github.com/free5gc/util/logger" ) -type AMF struct { - KeyLogPath string +type AmfApp struct { + cfg *factory.Config + amfCtx *amf_context.AMFContext } -type ( - // Commands information. - Commands struct { - config string - } -) +func NewApp(cfg *factory.Config) (*AmfApp, error) { + amf := &AmfApp{cfg: cfg} + amf.SetLogEnable(cfg.GetLogEnable()) + amf.SetLogLevel(cfg.GetLogLevel()) + amf.SetReportCaller(cfg.GetLogReportCaller()) -var commands Commands - -var cliCmd = []cli.Flag{ - cli.StringFlag{ - Name: "config, c", - Usage: "Load configuration from `FILE`", - }, - cli.StringFlag{ - Name: "log, l", - Usage: "Output NF log to `FILE`", - }, - cli.StringFlag{ - Name: "log5gc, lc", - Usage: "Output free5gc log to `FILE`", - }, + amf.amfCtx = amf_context.GetSelf() + amf_context.InitAmfContext(amf.amfCtx) + return amf, nil } -func (*AMF) GetCliCmd() (flags []cli.Flag) { - return cliCmd -} - -func (amf *AMF) Initialize(c *cli.Context) error { - commands = Commands{ - config: c.String("config"), +func (a *AmfApp) SetLogEnable(enable bool) { + logger.MainLog.Infof("Log enable is set to [%v]", enable) + if enable && logger.Log.Out == os.Stderr { + return + } else if !enable && logger.Log.Out == ioutil.Discard { + return } - if commands.config != "" { - if err := factory.InitConfigFactory(commands.config); err != nil { - return err - } + a.cfg.SetLogEnable(enable) + if enable { + logger.Log.SetOutput(os.Stderr) } else { - if err := factory.InitConfigFactory(util.AmfDefaultConfigPath); err != nil { - return err - } - } - - if err := factory.CheckConfigVersion(); err != nil { - return err + logger.Log.SetOutput(ioutil.Discard) } - - if _, err := factory.AmfConfig.Validate(); err != nil { - return err - } - - amf.SetLogLevel() - - return nil } -func (amf *AMF) SetLogLevel() { - if factory.AmfConfig.Logger == nil { - logger.InitLog.Warnln("AMF config without log level setting!!!") +func (a *AmfApp) SetLogLevel(level string) { + lvl, err := logrus.ParseLevel(level) + if err != nil { + logger.MainLog.Warnf("Log level [%s] is invalid", level) return } - if factory.AmfConfig.Logger.AMF != nil { - if factory.AmfConfig.Logger.AMF.DebugLevel != "" { - if level, err := logrus.ParseLevel(factory.AmfConfig.Logger.AMF.DebugLevel); err != nil { - logger.InitLog.Warnf("AMF Log level [%s] is invalid, set to [info] level", - factory.AmfConfig.Logger.AMF.DebugLevel) - logger.SetLogLevel(logrus.InfoLevel) - } else { - logger.InitLog.Infof("AMF Log level is set to [%s] level", level) - logger.SetLogLevel(level) - } - } else { - logger.InitLog.Warnln("AMF Log level not set. Default set to [info] level") - logger.SetLogLevel(logrus.InfoLevel) - } - logger.SetReportCaller(factory.AmfConfig.Logger.AMF.ReportCaller) - } - - if factory.AmfConfig.Logger.NAS != nil { - if factory.AmfConfig.Logger.NAS.DebugLevel != "" { - if level, err := logrus.ParseLevel(factory.AmfConfig.Logger.NAS.DebugLevel); err != nil { - nasLogger.NasLog.Warnf("NAS Log level [%s] is invalid, set to [info] level", - factory.AmfConfig.Logger.NAS.DebugLevel) - logger.SetLogLevel(logrus.InfoLevel) - } else { - nasLogger.SetLogLevel(level) - } - } else { - nasLogger.NasLog.Warnln("NAS Log level not set. Default set to [info] level") - nasLogger.SetLogLevel(logrus.InfoLevel) - } - nasLogger.SetReportCaller(factory.AmfConfig.Logger.NAS.ReportCaller) - } - - if factory.AmfConfig.Logger.NGAP != nil { - if factory.AmfConfig.Logger.NGAP.DebugLevel != "" { - if level, err := logrus.ParseLevel(factory.AmfConfig.Logger.NGAP.DebugLevel); err != nil { - ngapLogger.NgapLog.Warnf("NGAP Log level [%s] is invalid, set to [info] level", - factory.AmfConfig.Logger.NGAP.DebugLevel) - ngapLogger.SetLogLevel(logrus.InfoLevel) - } else { - ngapLogger.SetLogLevel(level) - } - } else { - ngapLogger.NgapLog.Warnln("NGAP Log level not set. Default set to [info] level") - ngapLogger.SetLogLevel(logrus.InfoLevel) - } - ngapLogger.SetReportCaller(factory.AmfConfig.Logger.NGAP.ReportCaller) - } - - if factory.AmfConfig.Logger.FSM != nil { - if factory.AmfConfig.Logger.FSM.DebugLevel != "" { - if level, err := logrus.ParseLevel(factory.AmfConfig.Logger.FSM.DebugLevel); err != nil { - fsmLogger.FsmLog.Warnf("FSM Log level [%s] is invalid, set to [info] level", - factory.AmfConfig.Logger.FSM.DebugLevel) - fsmLogger.SetLogLevel(logrus.InfoLevel) - } else { - fsmLogger.SetLogLevel(level) - } - } else { - fsmLogger.FsmLog.Warnln("FSM Log level not set. Default set to [info] level") - fsmLogger.SetLogLevel(logrus.InfoLevel) - } - fsmLogger.SetReportCaller(factory.AmfConfig.Logger.FSM.ReportCaller) + logger.MainLog.Infof("Log level is set to [%s]", level) + if lvl == logger.Log.GetLevel() { + return } - if factory.AmfConfig.Logger.Aper != nil { - if factory.AmfConfig.Logger.Aper.DebugLevel != "" { - if level, err := logrus.ParseLevel(factory.AmfConfig.Logger.Aper.DebugLevel); err != nil { - aperLogger.AperLog.Warnf("Aper Log level [%s] is invalid, set to [info] level", - factory.AmfConfig.Logger.Aper.DebugLevel) - aperLogger.SetLogLevel(logrus.InfoLevel) - } else { - aperLogger.SetLogLevel(level) - } - } else { - aperLogger.AperLog.Warnln("Aper Log level not set. Default set to [info] level") - aperLogger.SetLogLevel(logrus.InfoLevel) - } - aperLogger.SetReportCaller(factory.AmfConfig.Logger.Aper.ReportCaller) - } + a.cfg.SetLogLevel(level) + logger.Log.SetLevel(lvl) } -func (amf *AMF) FilterCli(c *cli.Context) (args []string) { - for _, flag := range amf.GetCliCmd() { - name := flag.GetName() - value := fmt.Sprint(c.Generic(name)) - if value == "" { - continue - } - - args = append(args, "--"+name, value) +func (a *AmfApp) SetReportCaller(reportCaller bool) { + logger.MainLog.Infof("Report Caller is set to [%v]", reportCaller) + if reportCaller == logger.Log.ReportCaller { + return } - return args + + a.cfg.SetLogReportCaller(reportCaller) + logger.Log.SetReportCaller(reportCaller) } -func (amf *AMF) Start() { +func (a *AmfApp) Start(tlsKeyLogPath string) { logger.InitLog.Infoln("Server started") router := logger_util.NewGinWithLogrus(logger.GinLog) @@ -230,25 +119,27 @@ func (amf *AMF) Start() { } } - pemPath := util.AmfDefaultPemPath - keyPath := util.AmfDefaultKeyPath + pemPath := factory.AmfDefaultCertPemPath + keyPath := factory.AmfDefaultPrivateKeyPath sbi := factory.AmfConfig.Configuration.Sbi if sbi.Tls != nil { pemPath = sbi.Tls.Pem keyPath = sbi.Tls.Key } - self := context.AMF_Self() - util.InitAmfContext(self) + self := a.amfCtx + amf_context.InitAmfContext(self) addr := fmt.Sprintf("%s:%d", self.BindingIPv4, self.SBIPort) ngapHandler := ngap_service.NGAPHandler{ - HandleMessage: ngap.Dispatch, - HandleNotification: ngap.HandleSCTPNotification, + HandleMessage: ngap.Dispatch, + HandleNotification: ngap.HandleSCTPNotification, + HandleConnectionError: ngap.HandleSCTPConnError, } - ngap_service.Run(self.NgapIpList, 38412, ngapHandler) + sctpConfig := ngap_service.NewSctpConfig(factory.AmfConfig.GetSctpConfig()) + ngap_service.Run(self.NgapIpList, self.NgapPort, ngapHandler, sctpConfig) // Register to NRF var profile models.NfProfile @@ -275,11 +166,11 @@ func (amf *AMF) Start() { }() <-signalChannel - amf.Terminate() + a.Terminate() os.Exit(0) }() - server, err := httpwrapper.NewHttp2Server(addr, amf.KeyLogPath, router) + server, err := httpwrapper.NewHttp2Server(addr, tlsKeyLogPath, router) if server == nil { logger.InitLog.Errorf("Initialize HTTP server failed: %+v", err) @@ -290,7 +181,7 @@ func (amf *AMF) Start() { logger.InitLog.Warnf("Initialize HTTP server: %+v", err) } - serverScheme := factory.AmfConfig.Configuration.Sbi.Scheme + serverScheme := factory.AmfConfig.GetSbiScheme() if serverScheme == "http" { err = server.ListenAndServe() } else if serverScheme == "https" { @@ -302,77 +193,10 @@ func (amf *AMF) Start() { } } -func (amf *AMF) Exec(c *cli.Context) error { - // AMF.Initialize(cfgPath, c) - - logger.InitLog.Traceln("args:", c.String("amfcfg")) - args := amf.FilterCli(c) - logger.InitLog.Traceln("filter: ", args) - command := exec.Command("./amf", args...) - - stdout, err := command.StdoutPipe() - if err != nil { - logger.InitLog.Fatalln(err) - } - wg := sync.WaitGroup{} - wg.Add(3) - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - in := bufio.NewScanner(stdout) - for in.Scan() { - fmt.Println(in.Text()) - } - wg.Done() - }() - - stderr, err := command.StderrPipe() - if err != nil { - logger.InitLog.Fatalln(err) - } - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - in := bufio.NewScanner(stderr) - for in.Scan() { - fmt.Println(in.Text()) - } - wg.Done() - }() - - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - if err = command.Start(); err != nil { - logger.InitLog.Errorf("AMF Start error: %+v", err) - } - wg.Done() - }() - - wg.Wait() - - return err -} - // Used in AMF planned removal procedure -func (amf *AMF) Terminate() { +func (a *AmfApp) Terminate() { logger.InitLog.Infof("Terminating AMF...") - amfSelf := context.AMF_Self() + amfSelf := amf_context.GetSelf() // TODO: forward registered UE contexts to target AMF in the same AMF set if there is one @@ -390,7 +214,7 @@ func (amf *AMF) Terminate() { logger.InitLog.Infof("Send AMF Status Indication to Notify RANs due to AMF terminating") unavailableGuamiList := ngap_message.BuildUnavailableGUAMIList(amfSelf.ServedGuamiList) amfSelf.AmfRanPool.Range(func(key, value interface{}) bool { - ran := value.(*context.AmfRan) + ran := value.(*amf_context.AmfRan) ngap_message.SendAMFStatusIndication(ran, unavailableGuamiList) return true })