mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 10:06:24 +05:00
added multiple listeners for diameter agent
This commit is contained in:
committed by
Dan Christian Bogos
parent
897d6f0da1
commit
e859be8806
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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
@@ -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),
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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"`
|
||||
|
||||
166
general_tests/diam_listeners_it_test.go
Normal file
166
general_tests/diam_listeners_it_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user