From 5d90bc80974c19999a51f9d33a89e357ee2b430a Mon Sep 17 00:00:00 2001 From: adjivas Date: Fri, 28 Feb 2025 16:26:15 +0100 Subject: [PATCH 1/8] feat: IPv6 support --- go.mod | 3 + go.sum | 2 + internal/context/ausf_context_init.go | 67 ++--- internal/context/context.go | 5 +- internal/context/context_test.go | 346 ++++++++++++++++++++++++++ internal/sbi/consumer/nrf_service.go | 9 +- internal/sbi/server.go | 12 +- pkg/factory/config.go | 84 ++++--- pkg/factory/factory.go | 4 +- 9 files changed, 450 insertions(+), 82 deletions(-) create mode 100644 internal/context/context_test.go diff --git a/go.mod b/go.mod index dcfca1c..003e6f7 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/google/uuid v1.3.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.9.0 github.com/urfave/cli v1.22.5 go.uber.org/mock v0.4.0 gopkg.in/yaml.v2 v2.4.0 @@ -21,6 +22,7 @@ require ( github.com/bytedance/sonic v1.9.1 // 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/davecgh/go-spew v1.1.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect @@ -41,6 +43,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // 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/tim-ywliu/nested-logrus-formatter v1.3.2 // indirect diff --git a/go.sum b/go.sum index ee4af3c..175bb88 100644 --- a/go.sum +++ b/go.sum @@ -94,6 +94,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tim-ywliu/nested-logrus-formatter v1.3.2 h1:jugNJ2/CNCI79SxOJCOhwUHeN3O7/7/bj+ZRGOFlCSw= diff --git a/internal/context/ausf_context_init.go b/internal/context/ausf_context_init.go index 80d7591..60fc995 100644 --- a/internal/context/ausf_context_init.go +++ b/internal/context/ausf_context_init.go @@ -1,15 +1,16 @@ package context import ( + "context" "fmt" + "net" + "net/netip" "os" - "strconv" - - "github.com/google/uuid" "github.com/free5gc/ausf/internal/logger" "github.com/free5gc/ausf/pkg/factory" "github.com/free5gc/openapi/models" + "github.com/google/uuid" ) func InitAusfContext(context *AUSFContext) { @@ -23,36 +24,22 @@ func InitAusfContext(context *AUSFContext) { context.GroupID = configuration.GroupId context.NrfUri = configuration.NrfUri context.NrfCertPem = configuration.NrfCertPem - context.UriScheme = models.UriScheme(configuration.Sbi.Scheme) // default uri scheme - context.RegisterIPv4 = factory.AusfSbiDefaultIPv4 // default localhost - context.SBIPort = factory.AusfSbiDefaultPort // default port - if sbi != nil { - if sbi.RegisterIPv4 != "" { - context.RegisterIPv4 = sbi.RegisterIPv4 - } - if sbi.Port != 0 { - context.SBIPort = sbi.Port - } - - if sbi.Scheme == "https" { - context.UriScheme = models.UriScheme_HTTPS - } else { - context.UriScheme = models.UriScheme_HTTP - } - - context.BindingIPv4 = os.Getenv(sbi.BindingIPv4) - if context.BindingIPv4 != "" { - logger.InitLog.Info("Parsing ServerIPv4 address from ENV Variable.") - } else { - context.BindingIPv4 = sbi.BindingIPv4 - if context.BindingIPv4 == "" { - logger.InitLog.Warn("Error parsing ServerIPv4 address as string. Using the 0.0.0.0 address as default.") - context.BindingIPv4 = "0.0.0.0" - } - } + + context.SBIPort = sbi.Port + context.UriScheme = models.UriScheme(sbi.Scheme) + + if bindingIP := os.Getenv(sbi.BindingIP); bindingIP != "" { + logger.UtilLog.Info("Parsing BindingIP address from ENV Variable.") + sbi.BindingIP = bindingIP + } + if registerIP := os.Getenv(sbi.RegisterIP); registerIP != "" { + logger.UtilLog.Info("Parsing RegisterIP address from ENV Variable.") + sbi.RegisterIP = registerIP } + context.BindingIP = resolveIP(sbi.BindingIP) + context.RegisterIP = resolveIP(sbi.RegisterIP) - context.Url = string(context.UriScheme) + "://" + context.RegisterIPv4 + ":" + strconv.Itoa(context.SBIPort) + context.Url = string(context.UriScheme) + "://" + netip.AddrPortFrom(context.RegisterIP, uint16(context.SBIPort)).String() context.PlmnList = append(context.PlmnList, configuration.PlmnSupportList...) // context.NfService @@ -63,6 +50,18 @@ func InitAusfContext(context *AUSFContext) { context.EapAkaSupiImsiPrefix = configuration.EapAkaSupiImsiPrefix } +func resolveIP(ip string) netip.Addr { + resolvedIPs, err := net.DefaultResolver.LookupNetIP(context.Background(), "ip", ip) + if err != nil { + logger.InitLog.Errorf("Lookup failed with %s: %+v", ip, err) + } + resolvedIP := resolvedIPs[0] + if resolvedIP := resolvedIP.String(); resolvedIP != ip { + logger.UtilLog.Infof("Lookup revolved %s into %s", ip, resolvedIP) + } + return resolvedIP +} + func AddNfServices( serviceMap *map[models.ServiceName]models.NrfNfManagementNfService, config *factory.Config, context *AUSFContext, ) { @@ -76,7 +75,11 @@ func AddNfServices( nfService.ServiceName = models.ServiceName_NAUSF_AUTH var ipEndPoint models.IpEndPoint - ipEndPoint.Ipv4Address = context.RegisterIPv4 + if context.RegisterIP.Is6() { + ipEndPoint.Ipv4Address = context.RegisterIP.String() + } else if context.RegisterIP.Is4() { + ipEndPoint.Ipv6Address = context.RegisterIP.String() + } ipEndPoint.Port = int32(context.SBIPort) ipEndPoints = append(ipEndPoints, ipEndPoint) diff --git a/internal/context/context.go b/internal/context/context.go index ad9a08d..e82a458 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -2,6 +2,7 @@ package context import ( "context" + "net/netip" "regexp" "sync" @@ -16,8 +17,8 @@ type AUSFContext struct { NfId string GroupID string SBIPort int - RegisterIPv4 string - BindingIPv4 string + RegisterIP netip.Addr // IP register to NRF + BindingIP netip.Addr Url string UriScheme models.UriScheme NrfUri string diff --git a/internal/context/context_test.go b/internal/context/context_test.go new file mode 100644 index 0000000..b3d1479 --- /dev/null +++ b/internal/context/context_test.go @@ -0,0 +1,346 @@ +package context + +import ( + "net/netip" + "os" + "testing" + + "github.com/free5gc/ausf/pkg/factory" + "github.com/free5gc/openapi/models" + "github.com/stretchr/testify/assert" +) + +func createConfigFile(t *testing.T, postContent []byte) *os.File { + content := []byte(`info: + version: "1.0.4" + +logger: + level: info +`) + + configFile, err := os.CreateTemp("", "") + if err != nil { + t.Errorf("can't create temp file: %+v", err) + } + + if _, err := configFile.Write(content); err != nil { + t.Errorf("can't write content of temp file: %+v", err) + } + if _, err := configFile.Write(postContent); err != nil { + t.Errorf("can't write content of temp file: %+v", err) + } + if err := configFile.Close(); err != nil { + t.Fatal(err) + } + return configFile +} + +func TestInitAusfContextWithConfigIPv6(t *testing.T) { + postContent := []byte(`configuration: + serviceNameList: + - nausf-auth + plmnSupportList: + - mcc: 208 + mnc: 93 + sbi: + scheme: http + registerIP: "2001:db8::1:0:0:13" + bindingIP: "2001:db8::1:0:0:13" + port: 8313 + nrfUri: http://127.0.0.10:8000`) + + configFile := createConfigFile(t, postContent) + + // Test the initialization with the config file + cfg, err := factory.ReadConfig(configFile.Name()) + if err != nil { + t.Errorf("invalid read config: %+v %+v", err, cfg) + } + factory.AusfConfig = cfg + + GetSelf().NfService = make(map[models.ServiceName]models.NrfNfManagementNfService) + InitAusfContext(GetSelf()) + + assert.Equal(t, ausfContext.SBIPort, 8313) + assert.Equal(t, ausfContext.RegisterIP.String(), "2001:db8::1:0:0:13") + assert.Equal(t, ausfContext.BindingIP.String(), "2001:db8::1:0:0:13") + assert.Equal(t, ausfContext.UriScheme, models.UriScheme("http")) + + // Close the config file + t.Cleanup(func() { + if err := os.RemoveAll(configFile.Name()); err != nil { + t.Fatal(err) + } + }) +} + +func TestInitAusfContextWithConfigIPv4(t *testing.T) { + postContent := []byte(`configuration: + serviceNameList: + - nausf-auth + plmnSupportList: + - mcc: 208 + mnc: 93 + sbi: + scheme: http + registerIP: "127.0.0.13" + bindingIP: "127.0.0.13" + port: 8131 + nrfUri: http://127.0.0.10:8000`) + + configFile := createConfigFile(t, postContent) + + // Test the initialization with the config file + cfg, err := factory.ReadConfig(configFile.Name()) + if err != nil { + t.Errorf("invalid read config: %+v %+v", err, cfg) + } + factory.AusfConfig = cfg + + GetSelf().NfService = make(map[models.ServiceName]models.NrfNfManagementNfService) + InitAusfContext(GetSelf()) + + assert.Equal(t, ausfContext.SBIPort, 8131) + assert.Equal(t, ausfContext.RegisterIP.String(), "::ffff:127.0.0.13") + assert.Equal(t, ausfContext.BindingIP.String(), "::ffff:127.0.0.13") + assert.Equal(t, ausfContext.UriScheme, models.UriScheme("http")) + + // Close the config file + t.Cleanup(func() { + if err := os.RemoveAll(configFile.Name()); err != nil { + t.Fatal(err) + } + }) +} + +func TestInitAusfContextWithConfigDeprecated(t *testing.T) { + postContent := []byte(`configuration: + serviceNameList: + - nausf-auth + plmnSupportList: + - mcc: 208 + mnc: 93 + sbi: + scheme: http + registerIPv4: "127.0.0.30" + bindingIPv4: "127.0.0.30" + port: 8003 + nrfUri: http://127.0.0.10:8000`) + + configFile := createConfigFile(t, postContent) + + // Test the initialization with the config file + cfg, err := factory.ReadConfig(configFile.Name()) + if err != nil { + t.Errorf("invalid read config: %+v %+v", err, cfg) + } + factory.AusfConfig = cfg + + GetSelf().NfService = make(map[models.ServiceName]models.NrfNfManagementNfService) + InitAusfContext(GetSelf()) + + assert.Equal(t, ausfContext.SBIPort, 8003) + assert.Equal(t, ausfContext.RegisterIP.String(), "::ffff:127.0.0.30") + assert.Equal(t, ausfContext.BindingIP.String(), "::ffff:127.0.0.30") + assert.Equal(t, ausfContext.UriScheme, models.UriScheme("http")) + + // Close the config file + t.Cleanup(func() { + if err := os.RemoveAll(configFile.Name()); err != nil { + t.Fatal(err) + } + }) +} + +func TestInitAusfContextWithConfigEmptySBI(t *testing.T) { + postContent := []byte(`configuration: + serviceNameList: + - nausf-auth + plmnSupportList: + - mcc: 208 + mnc: 93 + nrfUri: http://127.0.0.10:8000`) + + configFile := createConfigFile(t, postContent) + + // Test the initialization with the config file + cfg, err := factory.ReadConfig(configFile.Name()) + if err != nil { + t.Errorf("invalid read config: %+v %+v", err, cfg) + } + factory.AusfConfig = cfg + + GetSelf().NfService = make(map[models.ServiceName]models.NrfNfManagementNfService) + InitAusfContext(GetSelf()) + + assert.Equal(t, ausfContext.SBIPort, 8000) + assert.Equal(t, ausfContext.RegisterIP.String(), "::ffff:127.0.0.9") + assert.Equal(t, ausfContext.BindingIP.String(), "::ffff:127.0.0.9") + assert.Equal(t, ausfContext.UriScheme, models.UriScheme("https")) + + // Close the config file + t.Cleanup(func() { + if err := os.RemoveAll(configFile.Name()); err != nil { + t.Fatal(err) + } + }) +} + +func TestInitAusfContextWithConfigMissingRegisterIP(t *testing.T) { + postContent := []byte(`configuration: + serviceNameList: + - nausf-auth + plmnSupportList: + - mcc: 208 + mnc: 93 + sbi: + bindingIP: "2001:db8::1:0:0:130" + serviceNameList: + - nudm-sdm + nrfUri: http://127.0.0.10:8000`) + + configFile := createConfigFile(t, postContent) + + // Test the initialization with the config file + cfg, err := factory.ReadConfig(configFile.Name()) + if err != nil { + t.Errorf("invalid read config: %+v %+v", err, cfg) + } + factory.AusfConfig = cfg + + GetSelf().NfService = make(map[models.ServiceName]models.NrfNfManagementNfService) + InitAusfContext(GetSelf()) + + assert.Equal(t, ausfContext.SBIPort, 8000) + assert.Equal(t, ausfContext.BindingIP.String(), "2001:db8::1:0:0:130") + assert.Equal(t, ausfContext.RegisterIP.String(), "2001:db8::1:0:0:130") + assert.Equal(t, ausfContext.UriScheme, models.UriScheme("https")) + + // Close the config file + t.Cleanup(func() { + if err := os.RemoveAll(configFile.Name()); err != nil { + t.Fatal(err) + } + }) +} + +func TestInitAusfContextWithConfigMissingBindingIP(t *testing.T) { + postContent := []byte(`configuration: + serviceNameList: + - nausf-auth + plmnSupportList: + - mcc: 208 + mnc: 93 + sbi: + registerIP: "2001:db8::1:0:0:131" + serviceNameList: + - nudm-sdm + nrfUri: http://127.0.0.10:8000`) + + configFile := createConfigFile(t, postContent) + + // Test the initialization with the config file + cfg, err := factory.ReadConfig(configFile.Name()) + if err != nil { + t.Errorf("invalid read config: %+v %+v", err, cfg) + } + factory.AusfConfig = cfg + + GetSelf().NfService = make(map[models.ServiceName]models.NrfNfManagementNfService) + InitAusfContext(GetSelf()) + + assert.Equal(t, ausfContext.SBIPort, 8000) + assert.Equal(t, ausfContext.BindingIP.String(), "2001:db8::1:0:0:131") + assert.Equal(t, ausfContext.RegisterIP.String(), "2001:db8::1:0:0:131") + assert.Equal(t, ausfContext.UriScheme, models.UriScheme("https")) + + // Close the config file + t.Cleanup(func() { + if err := os.RemoveAll(configFile.Name()); err != nil { + t.Fatal(err) + } + }) +} + +func TestInitAusfContextWithConfigIPv6FromEnv(t *testing.T) { + postContent := []byte(`configuration: + serviceNameList: + - nudm-sdm + plmnSupportList: + - mcc: 208 + mnc: 93 + sbi: + scheme: http + registerIP: "MY_REGISTER_IP" + bindingIP: "MY_BINDING_IP" + port: 8313 + nrfUri: http://127.0.0.10:8000`) + + configFile := createConfigFile(t, postContent) + + if err := os.Setenv("MY_REGISTER_IP", "2001:db8::1:0:0:130"); err != nil { + t.Errorf("Can't set MY_BINDING_IP variable environnement: %+v", err) + } + if err := os.Setenv("MY_BINDING_IP", "2001:db8::1:0:0:130"); err != nil { + t.Errorf("Can't set MY_BINDING_IP variable environnement: %+v", err) + } + + // Test the initialization with the config file + cfg, err := factory.ReadConfig(configFile.Name()) + if err != nil { + t.Errorf("invalid read config: %+v %+v", err, cfg) + } + factory.AusfConfig = cfg + + GetSelf().NfService = make(map[models.ServiceName]models.NrfNfManagementNfService) + InitAusfContext(GetSelf()) + + assert.Equal(t, ausfContext.SBIPort, 8313) + assert.Equal(t, ausfContext.RegisterIP.String(), "2001:db8::1:0:0:130") + assert.Equal(t, ausfContext.BindingIP.String(), "2001:db8::1:0:0:130") + assert.Equal(t, ausfContext.UriScheme, models.UriScheme("http")) + + // Close the config file + t.Cleanup(func() { + if err := os.RemoveAll(configFile.Name()); err != nil { + t.Fatal(err) + } + }) +} + +func TestResolveIPLocalhost(t *testing.T) { + expectedAddr, err := netip.ParseAddr("::1") + if err != nil { + t.Errorf("invalid expected IP: %+v", expectedAddr) + } + + addr := resolveIP("localhost") + if addr != expectedAddr { + t.Errorf("invalid IP: %+v", addr) + } + assert.Equal(t, addr, expectedAddr) +} + +func TestResolveIPv4(t *testing.T) { + expectedAddr, err := netip.ParseAddr("::ffff:127.0.0.1") + if err != nil { + t.Errorf("invalid expected IP: %+v", expectedAddr) + } + + addr := resolveIP("127.0.0.1") + if addr != expectedAddr { + t.Errorf("invalid IP: %+v", addr) + } +} + +func TestResolveIPv6(t *testing.T) { + expectedAddr, err := netip.ParseAddr("2001:db8::1:0:0:1") + if err != nil { + t.Errorf("invalid expected IP: %+v", expectedAddr) + } + + addr := resolveIP("2001:db8::1:0:0:1") + if addr != expectedAddr { + t.Errorf("invalid IP: %+v", addr) + } +} diff --git a/internal/sbi/consumer/nrf_service.go b/internal/sbi/consumer/nrf_service.go index 8382703..585fca6 100644 --- a/internal/sbi/consumer/nrf_service.go +++ b/internal/sbi/consumer/nrf_service.go @@ -7,14 +7,13 @@ import ( "sync" "time" - "github.com/pkg/errors" - ausf_context "github.com/free5gc/ausf/internal/context" "github.com/free5gc/ausf/internal/logger" "github.com/free5gc/openapi" "github.com/free5gc/openapi/models" Nnrf_NFDiscovery "github.com/free5gc/openapi/nrf/NFDiscovery" Nnrf_NFManagement "github.com/free5gc/openapi/nrf/NFManagement" + "github.com/pkg/errors" ) type nnrfService struct { @@ -190,7 +189,11 @@ func (s *nnrfService) buildNfProfile(ausfContext *ausf_context.AUSFContext) ( profile.NfInstanceId = ausfContext.NfId profile.NfType = models.NrfNfManagementNfType_AUSF profile.NfStatus = models.NrfNfManagementNfStatus_REGISTERED - profile.Ipv4Addresses = append(profile.Ipv4Addresses, ausfContext.RegisterIPv4) + if ausfContext.RegisterIP.Is6() { + profile.Ipv6Addresses = append(profile.Ipv4Addresses, ausfContext.RegisterIP.String()) + } else if ausfContext.RegisterIP.Is4() { + profile.Ipv4Addresses = append(profile.Ipv4Addresses, ausfContext.RegisterIP.String()) + } services := []models.NrfNfManagementNfService{} for _, nfService := range ausfContext.NfService { services = append(services, nfService) diff --git a/internal/sbi/server.go b/internal/sbi/server.go index 46a5d73..d794fa7 100644 --- a/internal/sbi/server.go +++ b/internal/sbi/server.go @@ -5,13 +5,11 @@ import ( "fmt" "log" "net/http" + "net/netip" "runtime/debug" "sync" "time" - "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" - ausf_context "github.com/free5gc/ausf/internal/context" "github.com/free5gc/ausf/internal/logger" "github.com/free5gc/ausf/internal/sbi/consumer" @@ -22,6 +20,8 @@ import ( "github.com/free5gc/openapi/models" "github.com/free5gc/util/httpwrapper" logger_util "github.com/free5gc/util/logger" + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" ) type ServerAusf interface { @@ -45,8 +45,10 @@ func NewServer(ausf ServerAusf, tlsKeyLogPath string) (*Server, error) { s.router = newRouter(s) - cfg := s.Config() - bindAddr := cfg.GetSbiBindingAddr() + port := ausf.Context().SBIPort + addr := ausf.Context().BindingIP + bindAddr := netip.AddrPortFrom(addr, uint16(port)).String() + logger.SBILog.Infof("Binding addr: [%s]", bindAddr) var err error if s.httpServer, err = httpwrapper.NewHttp2Server(bindAddr, tlsKeyLogPath, s.router); err != nil { diff --git a/pkg/factory/config.go b/pkg/factory/config.go index e61b852..9de9681 100644 --- a/pkg/factory/config.go +++ b/pkg/factory/config.go @@ -7,12 +7,10 @@ package factory import ( "errors" "fmt" - "os" "strconv" "sync" "github.com/asaskevich/govalidator" - "github.com/free5gc/ausf/internal/logger" "github.com/free5gc/openapi/models" ) @@ -22,7 +20,7 @@ const ( AusfDefaultCertPemPath = "./cert/ausf.pem" AusfDefaultPrivateKeyPath = "./cert/ausf.key" AusfDefaultConfigPath = "./config/ausfcfg.yaml" - AusfSbiDefaultIPv4 = "127.0.0.9" + AusfSbiDefaultIP = "127.0.0.9" AusfSbiDefaultPort = 8000 AusfSbiDefaultScheme = "https" AusfDefaultNrfUri = "https://127.0.0.10:8000" @@ -50,12 +48,12 @@ func (c *Config) Validate() (bool, error) { } type Info struct { - Version string `yaml:"version,omitempty" valid:"required,in(1.0.3)"` + Version string `yaml:"version,omitempty" valid:"required,in(1.0.4)"` Description string `yaml:"description,omitempty" valid:"type(string)"` } type Configuration struct { - Sbi *Sbi `yaml:"sbi,omitempty" valid:"required"` + Sbi *Sbi `yaml:"sbi" valid:"optional"` ServiceNameList []string `yaml:"serviceNameList,omitempty" valid:"required"` NrfUri string `yaml:"nrfUri,omitempty" valid:"url,required"` NrfCertPem string `yaml:"nrfCertPem,omitempty" valid:"optional"` @@ -72,8 +70,14 @@ type Logger struct { func (c *Configuration) validate() (bool, error) { if sbi := c.Sbi; sbi != nil { - if result, err := sbi.validate(); err != nil { + return sbi.validate() + } else { + sbi := Sbi{} + result, err := sbi.validate() + if err != nil { return result, err + } else { + c.Sbi = &sbi } } @@ -106,17 +110,46 @@ func (c *Configuration) validate() (bool, error) { } type Sbi struct { - Scheme string `yaml:"scheme" valid:"scheme"` - RegisterIPv4 string `yaml:"registerIPv4,omitempty" valid:"host,required"` // IP that is registered at NRF. - BindingIPv4 string `yaml:"bindingIPv4,omitempty" valid:"host,required"` // IP used to run the server in the node. - Port int `yaml:"port,omitempty" valid:"port,required"` + Scheme string `yaml:"scheme" valid:"in(http|https),optional"` + RegisterIPv4 string `yaml:"registerIPv4,omitempty" valid:"host,optional"` // IP that is registered at NRF. + RegisterIP string `yaml:"registerIP,omitempty" valid:"host,optional"` // IP that is registered at NRF. + BindingIPv4 string `yaml:"bindingIPv4,omitempty" valid:"host,optional"` // IP used to run the server in the node. + BindingIP string `yaml:"bindingIP,omitempty" valid:"host,optional"` // IP used to run the server in the node. + Port int `yaml:"port,omitempty" valid:"port,optional"` Tls *Tls `yaml:"tls,omitempty" valid:"optional"` } func (s *Sbi) validate() (bool, error) { - govalidator.TagMap["scheme"] = govalidator.Validator(func(str string) bool { - return str == "https" || str == "http" - }) + // Set a default Schme if the Configuration does not provides one + if s.Scheme == "" { + s.Scheme = AusfSbiDefaultScheme + } + + // Set BindingIP/RegisterIP from deprecated BindingIPv4/RegisterIPv4 + if s.BindingIP == "" && s.BindingIPv4 != "" { + s.BindingIP = s.BindingIPv4 + } + if s.RegisterIP == "" && s.RegisterIPv4 != "" { + s.RegisterIP = s.RegisterIPv4 + } + + // Set a default BindingIP/RegisterIP if the Configuration does not provides them + if s.BindingIP == "" && s.RegisterIP == "" { + s.BindingIP = AusfSbiDefaultIP + s.RegisterIP = AusfSbiDefaultIP + } else { + // Complete any missing BindingIP/RegisterIP from RegisterIP/BindingIP + if s.BindingIP == "" { + s.BindingIP = s.RegisterIP + } else if s.RegisterIP == "" { + s.RegisterIP = s.BindingIP + } + } + + // Set a default Port if the Configuration does not provides one + if s.Port == 0 { + s.Port = AusfSbiDefaultPort + } if tls := s.Tls; tls != nil { if result, err := tls.validate(); err != nil { @@ -125,7 +158,7 @@ func (s *Sbi) validate() (bool, error) { } result, err := govalidator.ValidateStruct(s) - return result, appendInvalid(err) + return result, err } type Tls struct { @@ -237,29 +270,6 @@ func (c *Config) GetLogReportCaller() bool { return c.Logger.ReportCaller } -func (c *Config) GetSbiBindingAddr() string { - c.RLock() - defer c.RUnlock() - return c.GetSbiBindingIP() + ":" + strconv.Itoa(c.GetSbiPort()) -} - -func (c *Config) GetSbiBindingIP() string { - c.RLock() - defer c.RUnlock() - 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) GetSbiPort() int { c.RLock() defer c.RUnlock() diff --git a/pkg/factory/factory.go b/pkg/factory/factory.go index fcbd12d..0ca007e 100644 --- a/pkg/factory/factory.go +++ b/pkg/factory/factory.go @@ -9,9 +9,8 @@ import ( "os" "github.com/asaskevich/govalidator" - "gopkg.in/yaml.v2" - "github.com/free5gc/ausf/internal/logger" + "gopkg.in/yaml.v2" ) var AusfConfig *Config @@ -22,7 +21,6 @@ func InitConfigFactory(f string, cfg *Config) error { // Use default config path f = AusfDefaultConfigPath } - if content, err := os.ReadFile(f); err != nil { return fmt.Errorf("[Factory] %+v", err) } else { From 47814352a92fb8b3ce006420a035d32842ab8ec3 Mon Sep 17 00:00:00 2001 From: adjivas Date: Fri, 28 Feb 2025 16:58:51 +0100 Subject: [PATCH 2/8] fix: buildNfProfile should returns the profile --- internal/sbi/consumer/nrf_service.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/sbi/consumer/nrf_service.go b/internal/sbi/consumer/nrf_service.go index 585fca6..7c51e2d 100644 --- a/internal/sbi/consumer/nrf_service.go +++ b/internal/sbi/consumer/nrf_service.go @@ -183,9 +183,7 @@ func (s *nnrfService) RegisterNFInstance(ctx context.Context) ( return resouceNrfUri, retrieveNfInstanceID, err } -func (s *nnrfService) buildNfProfile(ausfContext *ausf_context.AUSFContext) ( - profile models.NrfNfManagementNfProfile, err error, -) { +func (s *nnrfService) buildNfProfile(ausfContext *ausf_context.AUSFContext) (profile models.NrfNfManagementNfProfile, err error) { profile.NfInstanceId = ausfContext.NfId profile.NfType = models.NrfNfManagementNfType_AUSF profile.NfStatus = models.NrfNfManagementNfStatus_REGISTERED @@ -213,7 +211,7 @@ func (s *nnrfService) buildNfProfile(ausfContext *ausf_context.AUSFContext) ( // }, // }, } - return + return profile, nil } func (s *nnrfService) GetUdmUrl(nrfUri string) string { From fd4fdda2516c0889c41127bb201aecba09d1ac89 Mon Sep 17 00:00:00 2001 From: adjivas Date: Fri, 28 Feb 2025 17:52:47 +0100 Subject: [PATCH 3/8] fix: golangci-lint --- internal/context/ausf_context_init.go | 7 +++++-- internal/context/context_test.go | 3 ++- internal/sbi/consumer/nrf_service.go | 7 +++++-- internal/sbi/server.go | 5 +++-- pkg/factory/config.go | 1 + pkg/factory/factory.go | 3 ++- 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/internal/context/ausf_context_init.go b/internal/context/ausf_context_init.go index 60fc995..562006e 100644 --- a/internal/context/ausf_context_init.go +++ b/internal/context/ausf_context_init.go @@ -7,10 +7,11 @@ import ( "net/netip" "os" + "github.com/google/uuid" + "github.com/free5gc/ausf/internal/logger" "github.com/free5gc/ausf/pkg/factory" "github.com/free5gc/openapi/models" - "github.com/google/uuid" ) func InitAusfContext(context *AUSFContext) { @@ -39,7 +40,9 @@ func InitAusfContext(context *AUSFContext) { context.BindingIP = resolveIP(sbi.BindingIP) context.RegisterIP = resolveIP(sbi.RegisterIP) - context.Url = string(context.UriScheme) + "://" + netip.AddrPortFrom(context.RegisterIP, uint16(context.SBIPort)).String() + addr := context.RegisterIP + port := uint16(context.SBIPort) + context.Url = string(context.UriScheme) + "://" + netip.AddrPortFrom(addr, port).String() context.PlmnList = append(context.PlmnList, configuration.PlmnSupportList...) // context.NfService diff --git a/internal/context/context_test.go b/internal/context/context_test.go index b3d1479..9bc5ed3 100644 --- a/internal/context/context_test.go +++ b/internal/context/context_test.go @@ -5,9 +5,10 @@ import ( "os" "testing" + "github.com/stretchr/testify/assert" + "github.com/free5gc/ausf/pkg/factory" "github.com/free5gc/openapi/models" - "github.com/stretchr/testify/assert" ) func createConfigFile(t *testing.T, postContent []byte) *os.File { diff --git a/internal/sbi/consumer/nrf_service.go b/internal/sbi/consumer/nrf_service.go index 7c51e2d..7248380 100644 --- a/internal/sbi/consumer/nrf_service.go +++ b/internal/sbi/consumer/nrf_service.go @@ -7,13 +7,14 @@ import ( "sync" "time" + "github.com/pkg/errors" + ausf_context "github.com/free5gc/ausf/internal/context" "github.com/free5gc/ausf/internal/logger" "github.com/free5gc/openapi" "github.com/free5gc/openapi/models" Nnrf_NFDiscovery "github.com/free5gc/openapi/nrf/NFDiscovery" Nnrf_NFManagement "github.com/free5gc/openapi/nrf/NFManagement" - "github.com/pkg/errors" ) type nnrfService struct { @@ -183,7 +184,9 @@ func (s *nnrfService) RegisterNFInstance(ctx context.Context) ( return resouceNrfUri, retrieveNfInstanceID, err } -func (s *nnrfService) buildNfProfile(ausfContext *ausf_context.AUSFContext) (profile models.NrfNfManagementNfProfile, err error) { +func (s *nnrfService) buildNfProfile(ausfContext *ausf_context.AUSFContext) ( + profile models.NrfNfManagementNfProfile, err error, +) { profile.NfInstanceId = ausfContext.NfId profile.NfType = models.NrfNfManagementNfType_AUSF profile.NfStatus = models.NrfNfManagementNfStatus_REGISTERED diff --git a/internal/sbi/server.go b/internal/sbi/server.go index d794fa7..600d8b7 100644 --- a/internal/sbi/server.go +++ b/internal/sbi/server.go @@ -10,6 +10,9 @@ import ( "sync" "time" + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + ausf_context "github.com/free5gc/ausf/internal/context" "github.com/free5gc/ausf/internal/logger" "github.com/free5gc/ausf/internal/sbi/consumer" @@ -20,8 +23,6 @@ import ( "github.com/free5gc/openapi/models" "github.com/free5gc/util/httpwrapper" logger_util "github.com/free5gc/util/logger" - "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" ) type ServerAusf interface { diff --git a/pkg/factory/config.go b/pkg/factory/config.go index 9de9681..6215b12 100644 --- a/pkg/factory/config.go +++ b/pkg/factory/config.go @@ -11,6 +11,7 @@ import ( "sync" "github.com/asaskevich/govalidator" + "github.com/free5gc/ausf/internal/logger" "github.com/free5gc/openapi/models" ) diff --git a/pkg/factory/factory.go b/pkg/factory/factory.go index 0ca007e..7b60532 100644 --- a/pkg/factory/factory.go +++ b/pkg/factory/factory.go @@ -9,8 +9,9 @@ import ( "os" "github.com/asaskevich/govalidator" - "github.com/free5gc/ausf/internal/logger" "gopkg.in/yaml.v2" + + "github.com/free5gc/ausf/internal/logger" ) var AusfConfig *Config From 06005f591e2832b86643aa3e76f4d05ec2e485f2 Mon Sep 17 00:00:00 2001 From: adjivas Date: Mon, 3 Mar 2025 17:43:24 +0100 Subject: [PATCH 4/8] fix: IP resolver with Unmap --- go.sum | 2 -- internal/context/ausf_context_init.go | 2 +- internal/context/context_test.go | 14 +++++++------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/go.sum b/go.sum index 175bb88..ee4af3c 100644 --- a/go.sum +++ b/go.sum @@ -94,8 +94,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tim-ywliu/nested-logrus-formatter v1.3.2 h1:jugNJ2/CNCI79SxOJCOhwUHeN3O7/7/bj+ZRGOFlCSw= diff --git a/internal/context/ausf_context_init.go b/internal/context/ausf_context_init.go index 562006e..eaaa84c 100644 --- a/internal/context/ausf_context_init.go +++ b/internal/context/ausf_context_init.go @@ -58,7 +58,7 @@ func resolveIP(ip string) netip.Addr { if err != nil { logger.InitLog.Errorf("Lookup failed with %s: %+v", ip, err) } - resolvedIP := resolvedIPs[0] + resolvedIP := resolvedIPs[0].Unmap() if resolvedIP := resolvedIP.String(); resolvedIP != ip { logger.UtilLog.Infof("Lookup revolved %s into %s", ip, resolvedIP) } diff --git a/internal/context/context_test.go b/internal/context/context_test.go index 9bc5ed3..70cdecd 100644 --- a/internal/context/context_test.go +++ b/internal/context/context_test.go @@ -102,8 +102,8 @@ func TestInitAusfContextWithConfigIPv4(t *testing.T) { InitAusfContext(GetSelf()) assert.Equal(t, ausfContext.SBIPort, 8131) - assert.Equal(t, ausfContext.RegisterIP.String(), "::ffff:127.0.0.13") - assert.Equal(t, ausfContext.BindingIP.String(), "::ffff:127.0.0.13") + assert.Equal(t, ausfContext.RegisterIP.String(), "127.0.0.13") + assert.Equal(t, ausfContext.BindingIP.String(), "127.0.0.13") assert.Equal(t, ausfContext.UriScheme, models.UriScheme("http")) // Close the config file @@ -141,8 +141,8 @@ func TestInitAusfContextWithConfigDeprecated(t *testing.T) { InitAusfContext(GetSelf()) assert.Equal(t, ausfContext.SBIPort, 8003) - assert.Equal(t, ausfContext.RegisterIP.String(), "::ffff:127.0.0.30") - assert.Equal(t, ausfContext.BindingIP.String(), "::ffff:127.0.0.30") + assert.Equal(t, ausfContext.RegisterIP.String(), "127.0.0.30") + assert.Equal(t, ausfContext.BindingIP.String(), "127.0.0.30") assert.Equal(t, ausfContext.UriScheme, models.UriScheme("http")) // Close the config file @@ -175,8 +175,8 @@ func TestInitAusfContextWithConfigEmptySBI(t *testing.T) { InitAusfContext(GetSelf()) assert.Equal(t, ausfContext.SBIPort, 8000) - assert.Equal(t, ausfContext.RegisterIP.String(), "::ffff:127.0.0.9") - assert.Equal(t, ausfContext.BindingIP.String(), "::ffff:127.0.0.9") + assert.Equal(t, ausfContext.RegisterIP.String(), "127.0.0.9") + assert.Equal(t, ausfContext.BindingIP.String(), "127.0.0.9") assert.Equal(t, ausfContext.UriScheme, models.UriScheme("https")) // Close the config file @@ -323,7 +323,7 @@ func TestResolveIPLocalhost(t *testing.T) { } func TestResolveIPv4(t *testing.T) { - expectedAddr, err := netip.ParseAddr("::ffff:127.0.0.1") + expectedAddr, err := netip.ParseAddr("127.0.0.1") if err != nil { t.Errorf("invalid expected IP: %+v", expectedAddr) } From bc99c46f7862781d9c96fd29ed381c2706b27269 Mon Sep 17 00:00:00 2001 From: adjivas Date: Mon, 3 Mar 2025 18:50:10 +0100 Subject: [PATCH 5/8] fix: GetUdmUrl from IPv4 or v6 --- internal/sbi/consumer/nrf_service.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/internal/sbi/consumer/nrf_service.go b/internal/sbi/consumer/nrf_service.go index 7248380..700b856 100644 --- a/internal/sbi/consumer/nrf_service.go +++ b/internal/sbi/consumer/nrf_service.go @@ -2,10 +2,10 @@ package consumer import ( "context" - "strconv" "strings" "sync" "time" + "net/netip" "github.com/pkg/errors" @@ -236,10 +236,16 @@ func (s *nnrfService) GetUdmUrl(nrfUri string) string { logger.ConsumerLog.Errorln("[Search UDM UEAU] ", err.Error(), "use defalt udmUrl", udmUrl) } else if len(res.NfInstances) > 0 { udmInstance := res.NfInstances[0] - if len(udmInstance.Ipv4Addresses) > 0 && udmInstance.NfServices != nil { - ueauService := udmInstance.NfServices[0] - ueauEndPoint := ueauService.IpEndPoints[0] - udmUrl = string(ueauService.Scheme) + "://" + ueauEndPoint.Ipv4Address + ":" + strconv.Itoa(int(ueauEndPoint.Port)) + ueauService := udmInstance.NfServices[0] + ueauEndPoint := ueauService.IpEndPoints[0] + if len(udmInstance.Ipv6Addresses) > 0 && udmInstance.NfServices != nil { + addr := netip.MustParseAddr(ueauEndPoint.Ipv6Address) + bindAddr := netip.AddrPortFrom(addr, uint16(ueauEndPoint.Port)) + udmUrl = string(ueauService.Scheme) + "://" + bindAddr.String() + } else if len(udmInstance.Ipv4Addresses) > 0 && udmInstance.NfServices != nil { + addr := netip.MustParseAddr(ueauEndPoint.Ipv4Address) + bindAddr := netip.AddrPortFrom(addr, uint16(ueauEndPoint.Port)) + udmUrl = string(ueauService.Scheme) + "://" + bindAddr.String() } } else { logger.ConsumerLog.Errorln("[Search UDM UEAU] len(NfInstances) = 0") From 8fb8a9c5ea5c0edb13c0a045d1940ffac275a339 Mon Sep 17 00:00:00 2001 From: adjivas Date: Tue, 4 Mar 2025 10:18:09 +0100 Subject: [PATCH 6/8] fix: golanci-lint context and net/netip --- internal/sbi/consumer/nrf_service.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/sbi/consumer/nrf_service.go b/internal/sbi/consumer/nrf_service.go index 700b856..e7f8bd8 100644 --- a/internal/sbi/consumer/nrf_service.go +++ b/internal/sbi/consumer/nrf_service.go @@ -2,12 +2,10 @@ package consumer import ( "context" + "net/netip" "strings" "sync" "time" - "net/netip" - - "github.com/pkg/errors" ausf_context "github.com/free5gc/ausf/internal/context" "github.com/free5gc/ausf/internal/logger" @@ -15,6 +13,7 @@ import ( "github.com/free5gc/openapi/models" Nnrf_NFDiscovery "github.com/free5gc/openapi/nrf/NFDiscovery" Nnrf_NFManagement "github.com/free5gc/openapi/nrf/NFManagement" + "github.com/pkg/errors" ) type nnrfService struct { From b2e53307ab6f5f0bfc9fc8bb977bb4dfdaf04f09 Mon Sep 17 00:00:00 2001 From: adjivas Date: Tue, 4 Mar 2025 11:45:02 +0100 Subject: [PATCH 7/8] fix: golangci-lint --- internal/sbi/consumer/nrf_service.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/sbi/consumer/nrf_service.go b/internal/sbi/consumer/nrf_service.go index e7f8bd8..01f7334 100644 --- a/internal/sbi/consumer/nrf_service.go +++ b/internal/sbi/consumer/nrf_service.go @@ -7,13 +7,14 @@ import ( "sync" "time" + "github.com/pkg/errors" + ausf_context "github.com/free5gc/ausf/internal/context" "github.com/free5gc/ausf/internal/logger" "github.com/free5gc/openapi" "github.com/free5gc/openapi/models" Nnrf_NFDiscovery "github.com/free5gc/openapi/nrf/NFDiscovery" Nnrf_NFManagement "github.com/free5gc/openapi/nrf/NFManagement" - "github.com/pkg/errors" ) type nnrfService struct { From df15ef882e3776d6c1866815774e90e56f6f8a43 Mon Sep 17 00:00:00 2001 From: adjivas Date: Wed, 5 Mar 2025 17:26:05 +0100 Subject: [PATCH 8/8] fix: Sbi is required --- internal/context/context_test.go | 20 +++++--------------- pkg/factory/config.go | 10 +--------- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/internal/context/context_test.go b/internal/context/context_test.go index 70cdecd..e3d9658 100644 --- a/internal/context/context_test.go +++ b/internal/context/context_test.go @@ -1,6 +1,7 @@ package context import ( + "errors" "net/netip" "os" "testing" @@ -16,7 +17,7 @@ func createConfigFile(t *testing.T, postContent []byte) *os.File { version: "1.0.4" logger: - level: info + level: debug `) configFile, err := os.CreateTemp("", "") @@ -164,20 +165,9 @@ func TestInitAusfContextWithConfigEmptySBI(t *testing.T) { configFile := createConfigFile(t, postContent) - // Test the initialization with the config file - cfg, err := factory.ReadConfig(configFile.Name()) - if err != nil { - t.Errorf("invalid read config: %+v %+v", err, cfg) - } - factory.AusfConfig = cfg - - GetSelf().NfService = make(map[models.ServiceName]models.NrfNfManagementNfService) - InitAusfContext(GetSelf()) - - assert.Equal(t, ausfContext.SBIPort, 8000) - assert.Equal(t, ausfContext.RegisterIP.String(), "127.0.0.9") - assert.Equal(t, ausfContext.BindingIP.String(), "127.0.0.9") - assert.Equal(t, ausfContext.UriScheme, models.UriScheme("https")) + // Test the initialization with the config file fails + _, err := factory.ReadConfig(configFile.Name()) + assert.Equal(t, err, errors.New("Config validate Error")) // Close the config file t.Cleanup(func() { diff --git a/pkg/factory/config.go b/pkg/factory/config.go index 6215b12..18c7b97 100644 --- a/pkg/factory/config.go +++ b/pkg/factory/config.go @@ -54,7 +54,7 @@ type Info struct { } type Configuration struct { - Sbi *Sbi `yaml:"sbi" valid:"optional"` + Sbi *Sbi `yaml:"sbi,omitempty" valid:"required"` ServiceNameList []string `yaml:"serviceNameList,omitempty" valid:"required"` NrfUri string `yaml:"nrfUri,omitempty" valid:"url,required"` NrfCertPem string `yaml:"nrfCertPem,omitempty" valid:"optional"` @@ -72,14 +72,6 @@ type Logger struct { func (c *Configuration) validate() (bool, error) { if sbi := c.Sbi; sbi != nil { return sbi.validate() - } else { - sbi := Sbi{} - result, err := sbi.validate() - if err != nil { - return result, err - } else { - c.Sbi = &sbi - } } for index, serviceName := range c.ServiceNameList {