diff --git a/agents/radagent.go b/agents/radagent.go new file mode 100644 index 000000000..aff97b8c4 --- /dev/null +++ b/agents/radagent.go @@ -0,0 +1,76 @@ +/* +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 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ +package agents + +import ( + "fmt" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/utils" + "github.com/cgrates/radigo" + "github.com/cgrates/rpcclient" +) + +func NewRadiusAgent(cgrCfg *config.CGRConfig, smg rpcclient.RpcClientConnection) (ra *RadiusAgent, err error) { + dicts := make(map[string]*radigo.Dictionary, len(cgrCfg.RadiusAgentCfg().ClientDictionaries)) + for clntID, dictPath := range cgrCfg.RadiusAgentCfg().ClientDictionaries { + if dicts[clntID], err = radigo.NewDictionaryFromFolderWithRFC2865(dictPath); err != nil { + return + } + } + ra = &RadiusAgent{cgrCfg: cgrCfg, smg: smg} + ra.rsAuth = radigo.NewServer(cgrCfg.RadiusAgentCfg().ListenNet, cgrCfg.RadiusAgentCfg().ListenAuth, cgrCfg.RadiusAgentCfg().ClientSecrets, dicts, + map[radigo.PacketCode]func(*radigo.Packet) (*radigo.Packet, error){radigo.AccessRequest: ra.handleAuth}) + ra.rsAcct = radigo.NewServer(cgrCfg.RadiusAgentCfg().ListenNet, cgrCfg.RadiusAgentCfg().ListenAcct, cgrCfg.RadiusAgentCfg().ClientSecrets, dicts, + map[radigo.PacketCode]func(*radigo.Packet) (*radigo.Packet, error){radigo.AccountingRequest: ra.handleAcct}) + return + +} + +type RadiusAgent struct { + cgrCfg *config.CGRConfig // reference for future config reloads + smg rpcclient.RpcClientConnection // Connection towards CGR-SMG component + rsAuth *radigo.Server + rsAcct *radigo.Server +} + +func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err error) { + return +} + +func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err error) { + return +} + +func (ra *RadiusAgent) ListenAndServe() (err error) { + var errListen chan error + go func() { + utils.Logger.Info(fmt.Sprintf(" Start listening for auth requests on <%s>", ra.cgrCfg.RadiusAgentCfg().ListenAuth)) + if err := ra.rsAuth.ListenAndServe(); err != nil { + errListen <- err + } + }() + go func() { + utils.Logger.Info(fmt.Sprintf(" Start listening for acct requests on <%s>", ra.cgrCfg.RadiusAgentCfg().ListenAcct)) + if err := ra.rsAcct.ListenAndServe(); err != nil { + errListen <- err + } + }() + err = <-errListen + return +} diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index a971acec1..5a0e77e42 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -210,7 +210,7 @@ func startSMAsterisk(internalSMGChan chan *sessionmanager.SMGeneric, exitChan ch } func startDiameterAgent(internalSMGChan chan *sessionmanager.SMGeneric, internalPubSubSChan chan rpcclient.RpcClientConnection, exitChan chan bool) { - utils.Logger.Info("Starting CGRateS DiameterAgent service.") + utils.Logger.Info("Starting CGRateS DiameterAgent service") smgChan := make(chan rpcclient.RpcClientConnection, 1) // Use it to pass smg go func(internalSMGChan chan *sessionmanager.SMGeneric, smgChan chan rpcclient.RpcClientConnection) { // Need this to pass from *sessionmanager.SMGeneric to rpcclient.RpcClientConnection @@ -250,8 +250,39 @@ func startDiameterAgent(internalSMGChan chan *sessionmanager.SMGeneric, internal exitChan <- true } +func startRadiusAgent(internalSMGChan chan *sessionmanager.SMGeneric, exitChan chan bool) { + utils.Logger.Info("Starting CGRateS RadiusAgent service") + smgChan := make(chan rpcclient.RpcClientConnection, 1) // Use it to pass smg + go func(internalSMGChan chan *sessionmanager.SMGeneric, smgChan chan rpcclient.RpcClientConnection) { + // Need this to pass from *sessionmanager.SMGeneric to rpcclient.RpcClientConnection + smg := <-internalSMGChan + internalSMGChan <- smg + smgChan <- smg + }(internalSMGChan, smgChan) + var smgConn *rpcclient.RpcClientPool + if len(cfg.RadiusAgentCfg().SMGenericConns) != 0 { + smgConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, cfg.ConnectTimeout, cfg.ReplyTimeout, + cfg.RadiusAgentCfg().SMGenericConns, smgChan, cfg.InternalTtl) + if err != nil { + utils.Logger.Crit(fmt.Sprintf(" Could not connect to SMG: %s", err.Error())) + exitChan <- true + return + } + } + ra, err := agents.NewRadiusAgent(cfg, smgConn) + if err != nil { + utils.Logger.Err(fmt.Sprintf(" error: <%s>", err.Error())) + exitChan <- true + return + } + if err = ra.ListenAndServe(); err != nil { + utils.Logger.Err(fmt.Sprintf(" error: <%s>", err.Error())) + } + exitChan <- true +} + func startSmFreeSWITCH(internalRaterChan, internalCDRSChan, rlsChan chan rpcclient.RpcClientConnection, cdrDb engine.CdrStorage, exitChan chan bool) { - utils.Logger.Info("Starting CGRateS SMFreeSWITCH service.") + utils.Logger.Info("Starting CGRateS SMFreeSWITCH service") var ralsConn, cdrsConn, rlsConn *rpcclient.RpcClientPool if len(cfg.SmFsConfig.RALsConns) != 0 { ralsConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST, cfg.ConnectAttempts, cfg.Reconnects, cfg.ConnectTimeout, cfg.ReplyTimeout, @@ -730,6 +761,10 @@ func main() { go startDiameterAgent(internalSMGChan, internalPubSubSChan, exitChan) } + if cfg.RadiusAgentCfg().Enabled { + go startRadiusAgent(internalSMGChan, exitChan) + } + // Start HistoryS service if cfg.HistoryServerEnabled { go startHistoryServer(internalHistorySChan, server, exitChan) diff --git a/config/config_defaults.go b/config/config_defaults.go index 4efc80674..c7a326679 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -359,7 +359,9 @@ const CGRATES_CFG_JSON = ` "listen_net": "udp", // network to listen on "listen_auth": "127.0.0.1:1812", // address where to listen for radius authentication requests "listen_acct": "127.0.0.1:1813", // address where to listen for radius accounting requests - "client_secrets": {"*default": "CGRateS.org"}, // hash containing secrets for clients connecting here <*default|$client_ip> + "client_secrets": { // hash containing secrets for clients connecting here <*default|$client_ip> + "*default": "CGRateS.org" + }, "client_dictionaries": { // per client path towards directory holding additional dictionaries to load (extra to RFC) "*default": "/usr/share/cgrates/radius/dict/", // key represents the client IP or catch-all <*default|$client_ip> }, diff --git a/data/conf/samples/radagent/cgrates.json b/data/conf/samples/radagent/cgrates.json new file mode 100644 index 000000000..ab571a016 --- /dev/null +++ b/data/conf/samples/radagent/cgrates.json @@ -0,0 +1,79 @@ +{ +// CGRateS Configuration file +// +// Used for cgradmin +// Starts rater, scheduler + +"listen": { + "rpc_json": ":2012", // RPC JSON listening address + "rpc_gob": ":2013", // RPC GOB listening address + "http": ":2080", // HTTP listening address +}, + +"data_db": { // database used to store runtime data (eg: accounts, cdr stats) + "db_type": "mongo", // stor database type to use: + "db_port": 27017, // the port to reach the stordb + "db_name": "datadb", + "db_password": "", +}, + +"stor_db": { + "db_type": "mongo", // stor database type to use: + "db_port": 27017, // the port to reach the stordb + "db_name": "stordb", + "db_password": "", +}, + +"rals": { + "enabled": true, + "cdrstats_conns": [ + {"address": "*internal"} + ], + "pubsubs_conns": [ + {"address": "*internal"} + ], + "users_conns": [ + {"address": "*internal"} + ], + "aliases_conns": [ + {"address": "*internal"} + ], +}, + +"scheduler": { + "enabled": true, +}, + +"cdrs": { + "enabled": true, + "cdrstats_conns": [ + {"address": "*internal"} + ], +}, + +"cdrstats": { + "enabled": true, +}, + +"pubsubs": { + "enabled": true, // starts PubSub service: . +}, + +"aliases": { + "enabled": true, // starts Aliases service: . +}, + +"users": { + "enabled": true, + "indexes": ["SubscriberId"], +}, + +"sm_generic": { + "enabled": true, +}, + +"radius_agent": { + "enabled": true, +}, + +} diff --git a/glide.lock b/glide.lock index 6dec8fdf7..0f91516f3 100644 --- a/glide.lock +++ b/glide.lock @@ -18,7 +18,7 @@ imports: - name: github.com/cgrates/osipsdagram version: 3d6beed663452471dec3ca194137a30d379d9e8f - name: github.com/cgrates/radigo - version: 8ee4c8409a0f8b8084d427dfb81010fd2add788c + version: b0690a5c829349f3c1bb0973f422788b870a9acc - name: github.com/cgrates/rpcclient version: dddae42e9344e877627cd4b7aba075d63b452c0b - name: github.com/ChrisTrenkamp/goxpath