added multiple listeners for diameter agent

This commit is contained in:
gezimbll
2025-12-02 14:17:03 +01:00
committed by Dan Christian Bogos
parent 897d6f0da1
commit e859be8806
17 changed files with 382 additions and 118 deletions

View File

@@ -85,10 +85,10 @@ func TestAgentCapsIT(t *testing.T) {
var i int
t.Run("DiameterAgent", func(t *testing.T) {
diamClient, err := NewDiameterClient(cfg.DiameterAgentCfg().Listen, "localhost",
diamClient, err := NewDiameterClient(cfg.DiameterAgentCfg().Listeners[0].Address, "localhost",
cfg.DiameterAgentCfg().OriginRealm, cfg.DiameterAgentCfg().VendorID,
cfg.DiameterAgentCfg().ProductName, utils.DiameterFirmwareRevision,
cfg.DiameterAgentCfg().DictionariesPath, cfg.DiameterAgentCfg().ListenNet)
cfg.DiameterAgentCfg().DictionariesPath, cfg.DiameterAgentCfg().Listeners[0].Network)
if err != nil {
t.Fatal(err)
}

View File

@@ -139,11 +139,11 @@ func TestDiamConnStats(t *testing.T) {
initDiamConn := func(originHost, originRealm string) io.Closer {
t.Helper()
client, err := NewDiameterClient(cfg.DiameterAgentCfg().Listen,
client, err := NewDiameterClient(cfg.DiameterAgentCfg().Listeners[0].Address,
originHost, originRealm, cfg.DiameterAgentCfg().VendorID,
cfg.DiameterAgentCfg().ProductName, utils.DiameterFirmwareRevision,
cfg.DiameterAgentCfg().DictionariesPath,
cfg.DiameterAgentCfg().ListenNet)
cfg.DiameterAgentCfg().Listeners[0].Network)
if err != nil {
t.Fatal(err)
}

View File

@@ -97,11 +97,11 @@ func testDiamEmptyCEItStartEngine(t *testing.T) {
}
func testDiamEmptyCEItConnectDiameterClient(t *testing.T) {
diamClntND, err = NewDiameterClient(daCfgND.DiameterAgentCfg().Listen,
diamClntND, err = NewDiameterClient(daCfgND.DiameterAgentCfg().Listeners[0].Address,
"INTEGRATION_TESTS",
daCfgND.DiameterAgentCfg().OriginRealm, daCfgND.DiameterAgentCfg().VendorID,
daCfgND.DiameterAgentCfg().ProductName, utils.DiameterFirmwareRevision,
daCfgND.DiameterAgentCfg().DictionariesPath, daCfgND.DiameterAgentCfg().ListenNet)
daCfgND.DiameterAgentCfg().DictionariesPath, daCfgND.DiameterAgentCfg().Listeners[0].Network)
if err.Error() != "missing application" {
t.Fatal(err)
}

View File

@@ -278,10 +278,10 @@ func testDiamItStartEngine(t *testing.T) {
}
func testDiamItConnectDiameterClient(t *testing.T) {
diamClnt, err = NewDiameterClient(daCfg.DiameterAgentCfg().Listen, "INTEGRATION_TESTS",
diamClnt, err = NewDiameterClient(daCfg.DiameterAgentCfg().Listeners[0].Address, "INTEGRATION_TESTS",
daCfg.DiameterAgentCfg().OriginRealm, daCfg.DiameterAgentCfg().VendorID,
daCfg.DiameterAgentCfg().ProductName, utils.DiameterFirmwareRevision,
daCfg.DiameterAgentCfg().DictionariesPath, daCfg.DiameterAgentCfg().ListenNet)
daCfg.DiameterAgentCfg().DictionariesPath, daCfg.DiameterAgentCfg().Listeners[0].Network)
if err != nil {
t.Fatal(err)
}

View File

@@ -328,10 +328,10 @@ cgrates.org,DEFAULT,*string:~*req.Account:1001,,*default,*none,10`,
}
time.Sleep(500 * time.Millisecond)
diamClient, err := NewDiameterClient(cfg.DiameterAgentCfg().Listen, "localhost",
diamClient, err := NewDiameterClient(cfg.DiameterAgentCfg().Listeners[0].Address, "localhost",
cfg.DiameterAgentCfg().OriginRealm, cfg.DiameterAgentCfg().VendorID,
cfg.DiameterAgentCfg().ProductName, utils.DiameterFirmwareRevision,
cfg.DiameterAgentCfg().DictionariesPath, cfg.DiameterAgentCfg().ListenNet)
cfg.DiameterAgentCfg().DictionariesPath, cfg.DiameterAgentCfg().Listeners[0].Network)
if err != nil {
t.Fatal(err)
}

View File

@@ -46,6 +46,10 @@ const (
dpa = "DPA"
)
var (
diamDictOnce sync.Once
)
// NewDiameterAgent initializes a new DiameterAgent
func NewDiameterAgent(cgrCfg *config.CGRConfig, filterS *engine.FilterS,
connMgr *engine.ConnManager, caps *engine.Caps) (*DiameterAgent, error) {
@@ -67,7 +71,10 @@ func NewDiameterAgent(cgrCfg *config.CGRConfig, filterS *engine.FilterS,
da.ctx = context.WithClient(context.TODO(), srv)
dictsPath := cgrCfg.DiameterAgentCfg().DictionariesPath
if len(dictsPath) != 0 {
if err := loadDictionaries(dictsPath, utils.DiameterAgent); err != nil {
diamDictOnce.Do(func() {
err = loadDictionaries(dictsPath, utils.DiameterAgent)
})
if err != nil {
return nil, err
}
}
@@ -107,33 +114,68 @@ type DiameterAgent struct {
// ListenAndServe is called when DiameterAgent is started, usually from within cmd/cgr-engine
func (da *DiameterAgent) ListenAndServe(stopChan <-chan struct{}) (err error) {
utils.Logger.Info(fmt.Sprintf("<%s> Start listening on <%s>", utils.DiameterAgent, da.cgrCfg.DiameterAgentCfg().Listen))
srv := &diam.Server{
Network: da.cgrCfg.DiameterAgentCfg().ListenNet,
Addr: da.cgrCfg.DiameterAgentCfg().Listen,
Handler: da.handlers(),
Dict: nil,
dSM := da.handlers()
errChan := make(chan error, len(da.cgrCfg.DiameterAgentCfg().Listeners))
var activeListeners []net.Listener
for _, lstnrCfg := range da.cgrCfg.DiameterAgentCfg().Listeners {
utils.Logger.Info(fmt.Sprintf("<%s> Start listening on <%s>", utils.DiameterAgent, lstnrCfg.Address))
srv := &diam.Server{
Network: lstnrCfg.Network,
Addr: lstnrCfg.Address,
Handler: dSM,
Dict: nil,
}
lsn, err := diam.MultistreamListen(
utils.FirstNonEmpty(srv.Network, utils.TCP),
utils.FirstNonEmpty(srv.Addr, ":3868"),
)
if err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> failed to bind listener %s: %v", utils.DiameterAgent, lstnrCfg.Address, err))
for _, l := range activeListeners {
l.Close()
}
return err
}
activeListeners = append(activeListeners, lsn)
go func(s *diam.Server, l net.Listener) {
errChan <- s.Serve(l)
}(srv, lsn)
}
// used to control the server state
var lsn net.Listener
if lsn, err = diam.MultistreamListen(utils.FirstNonEmpty(srv.Network, utils.TCP),
utils.FirstNonEmpty(srv.Addr, ":3868")); err != nil {
return
}
errChan := make(chan error)
go da.handleConns(dSM.HandshakeNotify())
go func() {
errChan <- srv.Serve(lsn)
errCh := dSM.ErrorReports()
for {
select {
case err, ok := <-errCh:
if !ok {
return
}
utils.Logger.Err(fmt.Sprintf("<%s> sm error: %v", utils.DiameterAgent, err))
case <-stopChan:
return
}
}
}()
select {
case err = <-errChan:
return
utils.Logger.Err(fmt.Sprintf("<%s> listener error: %v", utils.DiameterAgent, err))
case <-stopChan:
return lsn.Close()
utils.Logger.Info(fmt.Sprintf("<%s> received stop signal", utils.DiameterAgent))
}
for _, lsn := range activeListeners {
lsn.Close()
}
return err
}
// Creates the message handlers
func (da *DiameterAgent) handlers() diam.Handler {
func (da *DiameterAgent) handlers() *sm.StateMachine {
settings := &sm.Settings{
SupportedApps: da.cgrCfg.DiameterAgentCfg().CeApplications,
OriginHost: datatype.DiameterIdentity(da.cgrCfg.DiameterAgentCfg().OriginHost),
@@ -142,7 +184,21 @@ func (da *DiameterAgent) handlers() diam.Handler {
ProductName: datatype.UTF8String(da.cgrCfg.DiameterAgentCfg().ProductName),
FirmwareRevision: datatype.Unsigned32(utils.DiameterFirmwareRevision),
}
hosts := disectDiamListen(da.cgrCfg.DiameterAgentCfg().Listen)
var hosts []net.IP
for _, l := range da.cgrCfg.DiameterAgentCfg().Listeners {
host, _, err := net.SplitHostPort(l.Address)
if err != nil {
host = l.Address
}
if host == "" {
continue
}
if ip := net.ParseIP(host); ip != nil {
hosts = append(hosts, ip)
}
}
if len(hosts) == 0 {
interfaces, err := net.Interfaces()
if err != nil {
@@ -175,12 +231,6 @@ func (da *DiameterAgent) handlers() diam.Handler {
dSM.HandleFunc(raa, func(c diam.Conn, m *diam.Message) { go da.handleRAA(c, m) })
dSM.HandleFunc(dpa, func(c diam.Conn, m *diam.Message) { go da.handleDPA(c, m) })
}
go da.handleConns(dSM.HandshakeNotify())
go func() {
for err := range dSM.ErrorReports() {
utils.Logger.Err(fmt.Sprintf("<%s> sm error: %v", utils.DiameterAgent, err))
}
}()
return dSM
}

View File

@@ -70,10 +70,10 @@ cgrates.org,DEFAULT,,,*default,*none,0`,
client, cfg := ng.Run(b)
time.Sleep(10 * time.Millisecond) // wait for DiameterAgent service to start
diamClient, err := NewDiameterClient(cfg.DiameterAgentCfg().Listen, "localhost",
diamClient, err := NewDiameterClient(cfg.DiameterAgentCfg().Listeners[0].Address, "localhost",
cfg.DiameterAgentCfg().OriginRealm, cfg.DiameterAgentCfg().VendorID,
cfg.DiameterAgentCfg().ProductName, utils.DiameterFirmwareRevision,
cfg.DiameterAgentCfg().DictionariesPath, cfg.DiameterAgentCfg().ListenNet)
cfg.DiameterAgentCfg().DictionariesPath, cfg.DiameterAgentCfg().Listeners[0].Network)
if err != nil {
b.Fatal(err)
}
@@ -210,10 +210,10 @@ cgrates.org,Raw,,,*raw,*constant:*req.RequestType:*none,0`,
client, cfg := ng.Run(b)
time.Sleep(50 * time.Millisecond) // wait for DiameterAgent service to start
diamClient, err := NewDiameterClient(cfg.DiameterAgentCfg().Listen, "localhost",
diamClient, err := NewDiameterClient(cfg.DiameterAgentCfg().Listeners[0].Address, "localhost",
cfg.DiameterAgentCfg().OriginRealm, cfg.DiameterAgentCfg().VendorID,
cfg.DiameterAgentCfg().ProductName, utils.DiameterFirmwareRevision,
cfg.DiameterAgentCfg().DictionariesPath, cfg.DiameterAgentCfg().ListenNet)
cfg.DiameterAgentCfg().DictionariesPath, cfg.DiameterAgentCfg().Listeners[0].Network)
if err != nil {
b.Fatal(err)
}

View File

@@ -772,8 +772,12 @@ const CGRATES_CFG_JSON = `
"diameter_agent": {
"enabled": false, // enables the diameter agent: <true|false>
"listen": "127.0.0.1:3868", // address where to listen for diameter requests <x.y.z.y/x1.y1.z1.y1:1234>
"listen_net": "tcp", // transport type for diameter <tcp|sctp>
"listeners": [
{
"address": "127.0.0.1:3868", // address where to listen for diameter requests <x.y.z.y/x1.y1.z1.y1:1234>
"network": "tcp", // transport type for diameter <tcp|sctp>
}
],
"dictionaries_path": "/usr/share/cgrates/diameter/dict/", // path towards directory holding additional dictionaries to load
// "ce_applications": [], // list of applications in dictionaries wanted to be included in Capability-Exchange. Needed either "app name", "app ID", or "vendor name.app name/ID"
"sessions_conns": ["*birpc_internal"],

View File

@@ -1079,9 +1079,12 @@ func TestAsteriskAgentJsonCfg(t *testing.T) {
func TestDiameterAgentJsonCfg(t *testing.T) {
eCfg := &DiameterAgentJsonCfg{
Enabled: utils.BoolPointer(false),
Listen: utils.StringPointer("127.0.0.1:3868"),
ListenNet: utils.StringPointer(utils.TCP),
Enabled: utils.BoolPointer(false),
Listeners: &[]*DiamListenerJsnCfg{
{
Address: utils.StringPointer("127.0.0.1:3868"),
Network: utils.StringPointer(utils.TCP),
}},
DictionariesPath: utils.StringPointer("/usr/share/cgrates/diameter/dict/"),
SessionSConns: &[]string{rpcclient.BiRPCInternal},
StatSConns: &[]string{},

File diff suppressed because one or more lines are too long

View File

@@ -26,11 +26,15 @@ import (
"github.com/cgrates/rpcclient"
)
type DiameterListener struct {
Network string // sctp or tcp
Address string // address where to listen for diameter requests <x.y.z.y:1234>
}
// DiameterAgentCfg the config section that describes the Diameter Agent
type DiameterAgentCfg struct {
Enabled bool // enables the diameter agent: <true|false>
ListenNet string // sctp or tcp
Listen string // address where to listen for diameter requests <x.y.z.y:1234>
Enabled bool // enables the diameter agent: <true|false>
Listeners []DiameterListener
DictionariesPath string
CeApplications []string
SessionSConns []string
@@ -57,11 +61,18 @@ func (da *DiameterAgentCfg) loadFromJSONCfg(jc *DiameterAgentJsonCfg, separator
if jc.Enabled != nil {
da.Enabled = *jc.Enabled
}
if jc.Listen != nil {
da.Listen = *jc.Listen
}
if jc.ListenNet != nil {
da.ListenNet = *jc.ListenNet
if jc.Listeners != nil {
da.Listeners = make([]DiameterListener, 0, len(*jc.Listeners))
for _, listnr := range *jc.Listeners {
var ls DiameterListener
if listnr.Address != nil {
ls.Address = *listnr.Address
}
if listnr.Network != nil {
ls.Network = *listnr.Network
}
da.Listeners = append(da.Listeners, ls)
}
}
if jc.DictionariesPath != nil {
da.DictionariesPath = *jc.DictionariesPath
@@ -145,12 +156,24 @@ func (da *DiameterAgentCfg) loadFromJSONCfg(jc *DiameterAgentJsonCfg, separator
return
}
// AsMapInterface returns the config as a map[string]any
func (lstn *DiameterListener) AsMapInterface() map[string]any {
return map[string]any{
utils.NetworkCfg: lstn.Network,
utils.AddressCfg: lstn.Address,
}
}
// AsMapInterface returns the config as a map[string]any
func (da *DiameterAgentCfg) AsMapInterface(separator string) map[string]any {
listeners := make([]map[string]any, len(da.Listeners))
for i, item := range da.Listeners {
listeners[i] = item.AsMapInterface()
}
m := map[string]any{
utils.EnabledCfg: da.Enabled,
utils.ListenNetCfg: da.ListenNet,
utils.ListenCfg: da.Listen,
utils.ListenersCfg: listeners,
utils.DictionariesPathCfg: da.DictionariesPath,
utils.OriginHostCfg: da.OriginHost,
utils.OriginRealmCfg: da.OriginRealm,
@@ -198,8 +221,7 @@ func (da *DiameterAgentCfg) AsMapInterface(separator string) map[string]any {
func (da DiameterAgentCfg) Clone() *DiameterAgentCfg {
clone := &DiameterAgentCfg{
Enabled: da.Enabled,
ListenNet: da.ListenNet,
Listen: da.Listen,
Listeners: slices.Clone(da.Listeners),
DictionariesPath: da.DictionariesPath,
CeApplications: slices.Clone(da.CeApplications),
SessionSConns: slices.Clone(da.SessionSConns),

View File

@@ -27,9 +27,12 @@ import (
func TestDiameterAgentCfgloadFromJsonCfg(t *testing.T) {
jsonCFG := &DiameterAgentJsonCfg{
Enabled: utils.BoolPointer(true),
ListenNet: utils.StringPointer("tcp"),
Listen: utils.StringPointer("127.0.0.1:3868"),
Enabled: utils.BoolPointer(true),
Listeners: &[]*DiamListenerJsnCfg{
{Network: utils.StringPointer("tcp"),
Address: utils.StringPointer("127.0.0.1:3868")},
},
CeApplications: utils.SliceStringPointer([]string{"Base"}),
DictionariesPath: utils.StringPointer("/usr/share/cgrates/diameter/dict/"),
SessionSConns: &[]string{utils.MetaInternal, "*conn1"},
@@ -51,9 +54,14 @@ func TestDiameterAgentCfgloadFromJsonCfg(t *testing.T) {
},
}
expected := &DiameterAgentCfg{
Enabled: true,
ListenNet: "tcp",
Listen: "127.0.0.1:3868",
Enabled: true,
Listeners: []DiameterListener{
{
Network: "tcp",
Address: "127.0.0.1:3868",
},
},
CeApplications: []string{"Base"},
DictionariesPath: "/usr/share/cgrates/diameter/dict/",
SessionSConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaSessionS), "*conn1"},
@@ -157,13 +165,17 @@ func TestDiameterAgentCfgAsMapInterface(t *testing.T) {
},
}`
eMap := map[string]any{
utils.ASRTemplateCfg: "",
utils.CeApplicationsCfg: []string{"Base"},
utils.DictionariesPathCfg: "/usr/share/cgrates/diameter/dict/",
utils.EnabledCfg: false,
utils.ForcedDisconnectCfg: "*none",
utils.ListenCfg: "127.0.0.1:3868",
utils.ListenNetCfg: "tcp",
utils.ASRTemplateCfg: "",
utils.CeApplicationsCfg: []string{"Base"},
utils.DictionariesPathCfg: "/usr/share/cgrates/diameter/dict/",
utils.EnabledCfg: false,
utils.ForcedDisconnectCfg: "*none",
utils.ListenersCfg: []map[string]any{
{
utils.AddressCfg: "127.0.0.1:3868",
utils.NetworkCfg: "tcp",
},
},
utils.OriginHostCfg: "CGR-DA",
utils.OriginRealmCfg: "cgrates.org",
utils.ProductNameCfg: "CGRateS",
@@ -228,13 +240,17 @@ func TestDiameterAgentCfgAsMapInterface1(t *testing.T) {
},
}`
eMap := map[string]any{
utils.ASRTemplateCfg: "",
utils.CeApplicationsCfg: []string{"Nokia.4"},
utils.DictionariesPathCfg: "/usr/share/cgrates/diameter",
utils.EnabledCfg: true,
utils.ForcedDisconnectCfg: "*none",
utils.ListenCfg: "127.0.0.1:3868",
utils.ListenNetCfg: "tcp",
utils.ASRTemplateCfg: "",
utils.CeApplicationsCfg: []string{"Nokia.4"},
utils.DictionariesPathCfg: "/usr/share/cgrates/diameter",
utils.EnabledCfg: true,
utils.ForcedDisconnectCfg: "*none",
utils.ListenersCfg: []map[string]any{
{
utils.AddressCfg: "127.0.0.1:3868",
utils.NetworkCfg: "tcp",
},
},
utils.OriginHostCfg: "CGR-DA",
utils.OriginRealmCfg: "cgrates.org",
utils.ProductNameCfg: "CGRateS",
@@ -258,9 +274,11 @@ func TestDiameterAgentCfgAsMapInterface1(t *testing.T) {
func TestDiameterAgentCfgClone(t *testing.T) {
ban := &DiameterAgentCfg{
Enabled: true,
ListenNet: "tcp",
Listen: "127.0.0.1:3868",
Enabled: true,
Listeners: []DiameterListener{
{Network: "tcp",
Address: "127.0.0.1:3868"},
},
CeApplications: []string{"Base"},
DictionariesPath: "/usr/share/cgrates/diameter/dict/",
SessionSConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaSessionS), "*conn1"},

View File

@@ -557,11 +557,15 @@ type OsipsConnJsonCfg struct {
Reconnects *int
}
type DiamListenerJsnCfg struct {
Address *string `json:"address"`
Network *string `json:"network"`
}
// DiameterAgent configuration
type DiameterAgentJsonCfg struct {
Enabled *bool `json:"enabled"`
Listen *string `json:"listen"`
ListenNet *string `json:"listen_net"`
Listeners *[]*DiamListenerJsnCfg `json:"listeners"`
DictionariesPath *string `json:"dictionaries_path"`
CeApplications *[]string `json:"ce_applications"`
SessionSConns *[]string `json:"sessions_conns"`

View File

@@ -0,0 +1,166 @@
//go:build integration
// +build integration
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
*/
package general_tests
import (
"fmt"
"testing"
"time"
"github.com/cgrates/cgrates/agents"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/go-diameter/diam"
"github.com/cgrates/go-diameter/diam/avp"
"github.com/cgrates/go-diameter/diam/datatype"
"github.com/cgrates/go-diameter/diam/dict"
)
func TestDiamListeners(t *testing.T) {
content := `
{
"general": {
"log_level": 7
},
"apiers": {
"enabled": true
},
"cdrs": {
"enabled": true,
"rals_conns": ["*localhost"]
},
"sessions": {
"enabled": true,
"rals_conns": ["*localhost"],
"cdrs_conns": ["*localhost"]
},
"diameter_agent": {
"enabled": true,
"listeners": [
{"address": "127.0.0.1:3868", "network": "tcp"},
{"address": "127.0.0.2:3869", "network": "tcp"}
],
"sessions_conns": ["*bijson_localhost"],
"request_processors": [{
"id": "DiamRplcOID",
"flags": ["*cdrs"],
"request_fields": [
{
"tag": "OriginID",
"path": "*cgreq.OriginID",
"type": "*variable",
"value": "~*req.Session-Id",
"mandatory": true
}
],
"reply_fields": [
{
"tag": "Result-Code",
"path": "Result-Code",
"type": "*constant",
"value": "2001",
"mandatory": true
},
{
"tag": "Origin-Host",
"path": "Origin-Host",
"type": "*variable",
"value": "~*req.OriginHost",
"mandatory": true
},
{
"tag": "Origin-Realm",
"path": "Origin-Realm",
"type": "*variable",
"value": "~*req.OriginRealm",
"mandatory": true
}
]
}]
}
}
`
ng := engine.TestEngine{
ConfigJSON: content,
}
_, cfg := ng.Run(t)
t.Run("TestMultipleListeners", func(t *testing.T) {
listeners := cfg.DiameterAgentCfg().Listeners
if len(listeners) == 0 {
t.Fatal("No listeners configured in diameter_agent!")
}
for i, lCfg := range listeners {
cli, err := agents.NewDiameterClient(lCfg.Address, "INTEGRATION_TEST_CLIENT",
cfg.DiameterAgentCfg().OriginRealm, cfg.DiameterAgentCfg().VendorID,
cfg.DiameterAgentCfg().ProductName, utils.DiameterFirmwareRevision,
cfg.DiameterAgentCfg().DictionariesPath, lCfg.Network)
if err != nil {
t.Fatalf("Failed to create client for %s: %v", lCfg.Address, err)
}
m := diam.NewRequest(diam.CreditControl, 4, nil)
m.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String(fmt.Sprintf("test-conn-%d", i)))
m.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("INTEGRATION_TEST_CLIENT"))
m.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org"))
m.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4))
m.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(1))
m.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(0))
m.NewAVP(avp.DestinationHost, avp.Mbit, 0, datatype.DiameterIdentity(cfg.DiameterAgentCfg().OriginHost))
m.NewAVP(avp.DestinationRealm, avp.Mbit, 0, datatype.DiameterIdentity(cfg.DiameterAgentCfg().OriginRealm))
if err := cli.SendMessage(m); err != nil {
t.Errorf("Failed to send message to listener %s: %v", lCfg.Address, err)
continue
}
reply := cli.ReceivedMessage(time.Second)
if reply == nil {
t.Errorf("No reply received from listener %s", lCfg.Address)
continue
}
avps, err := reply.FindAVPsWithPath([]any{avp.OriginHost}, dict.UndefinedVendorID)
if err != nil {
t.Errorf("Error finding Origin-Host AVP: %v", err)
continue
}
if len(avps) == 0 {
t.Errorf("Origin-Host AVP missing in reply from %s", lCfg.Address)
continue
}
var replyOriginHost string
if val, ok := avps[0].Data.(datatype.DiameterIdentity); ok {
replyOriginHost = string(val)
} else {
t.Errorf("AVP data is not DiameterIdentity! Got type: %T, Value: %v", avps[0].Data, avps[0].Data)
continue
}
if replyOriginHost != "CGR-DA" {
t.Errorf("Identity Mismatch! Expected 'CGR-DA', got '%s'", replyOriginHost)
}
}
})
}

View File

@@ -97,10 +97,10 @@ cgrates.org,DEFAULT,,,*default,*none,0`,
}
client, cfg := ng.Run(t)
diamClient, err := agents.NewDiameterClient(cfg.DiameterAgentCfg().Listen, "localhost",
diamClient, err := agents.NewDiameterClient(cfg.DiameterAgentCfg().Listeners[0].Address, "localhost",
cfg.DiameterAgentCfg().OriginRealm, cfg.DiameterAgentCfg().VendorID,
cfg.DiameterAgentCfg().ProductName, utils.DiameterFirmwareRevision,
cfg.DiameterAgentCfg().DictionariesPath, cfg.DiameterAgentCfg().ListenNet)
cfg.DiameterAgentCfg().DictionariesPath, cfg.DiameterAgentCfg().Listeners[0].Network)
if err != nil {
t.Fatal(err)
}

View File

@@ -55,9 +55,6 @@ type DiameterAgent struct {
connMgr *engine.ConnManager
caps *engine.Caps
lnet string
laddr string
srvDep map[string]*sync.WaitGroup
}
@@ -83,8 +80,6 @@ func (da *DiameterAgent) start(filterS *engine.FilterS, caps *engine.Caps) error
utils.DiameterAgent, err))
return err
}
da.lnet = da.cfg.DiameterAgentCfg().ListenNet
da.laddr = da.cfg.DiameterAgentCfg().Listen
da.stopChan = make(chan struct{})
go func(d *agents.DiameterAgent) {
lnsErr := d.ListenAndServe(da.stopChan)
@@ -99,16 +94,8 @@ func (da *DiameterAgent) start(filterS *engine.FilterS, caps *engine.Caps) error
// Reload handles the change of config
func (da *DiameterAgent) Reload() (err error) {
da.Lock()
defer da.Unlock()
if da.lnet == da.cfg.DiameterAgentCfg().ListenNet &&
da.laddr == da.cfg.DiameterAgentCfg().Listen {
return
}
close(da.stopChan)
filterS := <-da.filterSChan
da.filterSChan <- filterS
return da.start(filterS, da.caps)
da.Shutdown()
return da.Start()
}
// Shutdown stops the service

View File

@@ -94,7 +94,6 @@ func TestDiameterAgentReload1(t *testing.T) {
cfg.DiameterAgentCfg().Enabled = false
cfg.GetReloadChan(config.DA_JSN) <- struct{}{}
diamSrv.(*DiameterAgent).lnet = "bad_lnet_test"
err2 := diamSrv.Reload()
if err != nil {
t.Errorf("\nExpected <%+v>, \nReceived <%+v>", nil, err2)
@@ -156,7 +155,7 @@ func TestDiameterAgentReload3(t *testing.T) {
srvDep := map[string]*sync.WaitGroup{utils.DataDB: new(sync.WaitGroup)}
srv := NewDiameterAgent(cfg, filterSChan, shdChan, nil, caps, srvDep)
cfg.DiameterAgentCfg().ListenNet = "bad"
cfg.DiameterAgentCfg().Listeners[0].Network = "bad"
cfg.DiameterAgentCfg().DictionariesPath = ""
da := srv.(*DiameterAgent)