cfg: support multiple radius auth/acct listeners

This commit is contained in:
ionutboangiu
2025-07-07 16:36:51 +03:00
committed by Dan Christian Bogos
parent 43e1630a55
commit 43930547b9
8 changed files with 337 additions and 217 deletions

View File

@@ -21,6 +21,7 @@ package agents
import (
"fmt"
"strings"
"sync"
"time"
"github.com/cgrates/birpc/context"
@@ -43,37 +44,56 @@ const (
MSCHAP2SuccessAVP = "MS-CHAP2-Success"
)
func NewRadiusAgent(cgrCfg *config.CGRConfig, filterS *engine.FilterS,
connMgr *engine.ConnManager) (ra *RadiusAgent, err error) {
dts := make(map[string]*radigo.Dictionary, len(cgrCfg.RadiusAgentCfg().ClientDictionaries))
for clntID, dictPath := range cgrCfg.RadiusAgentCfg().ClientDictionaries {
utils.Logger.Info(
fmt.Sprintf("<%s> loading dictionary for clientID: <%s> out of path <%s>",
utils.RadiusAgent, clntID, dictPath))
if dts[clntID], err = radigo.NewDictionaryFromFoldersWithRFC2865(dictPath); err != nil {
return
func NewRadiusAgent(cfg *config.CGRConfig, fltrs *engine.FilterS,
cm *engine.ConnManager) (*RadiusAgent, error) {
ra := &RadiusAgent{
cfg: cfg,
fltrs: fltrs,
cm: cm,
}
raCfg := cfg.RadiusAgentCfg()
dts := make(map[string]*radigo.Dictionary, len(raCfg.ClientDictionaries))
for clntID, dictPath := range raCfg.ClientDictionaries {
utils.Logger.Info(fmt.Sprintf(
"<%s> loading dictionary for clientID %q out of path %q",
utils.RadiusAgent, clntID, dictPath))
dt, err := radigo.NewDictionaryFromFoldersWithRFC2865(dictPath)
if err != nil {
return nil, err
}
dts[clntID] = dt
}
dicts := radigo.NewDictionaries(dts)
ra = &RadiusAgent{cgrCfg: cgrCfg, filterS: filterS, connMgr: connMgr}
secrets := radigo.NewSecrets(cgrCfg.RadiusAgentCfg().ClientSecrets)
ra.rsAuth = radigo.NewServer(cgrCfg.RadiusAgentCfg().ListenNet,
cgrCfg.RadiusAgentCfg().ListenAuth, secrets, dicts,
map[radigo.PacketCode]func(*radigo.Packet) (*radigo.Packet, error){
radigo.AccessRequest: ra.handleAuth}, nil, utils.Logger)
ra.rsAcct = radigo.NewServer(cgrCfg.RadiusAgentCfg().ListenNet,
cgrCfg.RadiusAgentCfg().ListenAcct, secrets, dicts,
map[radigo.PacketCode]func(*radigo.Packet) (*radigo.Packet, error){
radigo.AccountingRequest: ra.handleAcct}, nil, utils.Logger)
return
secrets := radigo.NewSecrets(raCfg.ClientSecrets)
ra.rsAuth = make(map[string]*radigo.Server, len(raCfg.Listeners))
ra.rsAcct = make(map[string]*radigo.Server, len(raCfg.Listeners))
for i := range raCfg.Listeners {
net := raCfg.Listeners[i].Network
authAddr := raCfg.Listeners[i].AuthAddr
ra.rsAuth[net+"://"+authAddr] = radigo.NewServer(net, authAddr, secrets, dicts,
map[radigo.PacketCode]func(*radigo.Packet) (*radigo.Packet, error){
radigo.AccessRequest: ra.handleAuth,
}, nil, utils.Logger)
acctAddr := raCfg.Listeners[i].AcctAddr
ra.rsAcct[net+"://"+acctAddr] = radigo.NewServer(net, acctAddr, secrets, dicts,
map[radigo.PacketCode]func(*radigo.Packet) (*radigo.Packet, error){
radigo.AccountingRequest: ra.handleAcct,
}, nil, utils.Logger)
}
return ra, nil
}
type RadiusAgent struct {
cgrCfg *config.CGRConfig // reference for future config reloads
connMgr *engine.ConnManager
filterS *engine.FilterS
rsAuth *radigo.Server
rsAcct *radigo.Server
mu sync.RWMutex
cfg *config.CGRConfig // reference for future config reloads
cm *engine.ConnManager
fltrs *engine.FilterS
rsAuth map[string]*radigo.Server
rsAcct map[string]*radigo.Server
wg sync.WaitGroup
}
// handleAuth handles RADIUS Authorization request
@@ -87,12 +107,12 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e
opts := utils.MapStorage{}
var processed bool
reqVars := &utils.DataNode{Type: utils.NMMapType, Map: map[string]*utils.DataNode{utils.RemoteHost: utils.NewLeafNode(req.RemoteAddr().String())}}
for _, reqProcessor := range ra.cgrCfg.RadiusAgentCfg().RequestProcessors {
for _, reqProcessor := range ra.cfg.RadiusAgentCfg().RequestProcessors {
agReq := NewAgentRequest(dcdr, reqVars, cgrRplyNM, rplyNM, opts,
reqProcessor.Tenant, ra.cgrCfg.GeneralCfg().DefaultTenant,
reqProcessor.Tenant, ra.cfg.GeneralCfg().DefaultTenant,
utils.FirstNonEmpty(reqProcessor.Timezone,
config.CgrConfig().GeneralCfg().DefaultTimezone),
ra.filterS, nil)
ra.fltrs, nil)
agReq.Vars.Map[MetaRadReqType] = utils.NewLeafNode(MetaRadAuth)
var lclProcessed bool
if lclProcessed, err = ra.processRequest(req, reqProcessor, agReq, rpl); lclProcessed {
@@ -132,12 +152,12 @@ func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err e
opts := utils.MapStorage{}
var processed bool
reqVars := &utils.DataNode{Type: utils.NMMapType, Map: map[string]*utils.DataNode{utils.RemoteHost: utils.NewLeafNode(req.RemoteAddr().String())}}
for _, reqProcessor := range ra.cgrCfg.RadiusAgentCfg().RequestProcessors {
for _, reqProcessor := range ra.cfg.RadiusAgentCfg().RequestProcessors {
agReq := NewAgentRequest(dcdr, reqVars, cgrRplyNM, rplyNM, opts,
reqProcessor.Tenant, ra.cgrCfg.GeneralCfg().DefaultTenant,
reqProcessor.Tenant, ra.cfg.GeneralCfg().DefaultTenant,
utils.FirstNonEmpty(reqProcessor.Timezone,
config.CgrConfig().GeneralCfg().DefaultTimezone),
ra.filterS, nil)
ra.fltrs, nil)
var lclProcessed bool
if lclProcessed, err = ra.processRequest(req, reqProcessor, agReq, rpl); lclProcessed {
processed = lclProcessed
@@ -167,7 +187,7 @@ func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err e
func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.RequestProcessor,
agReq *AgentRequest, rpl *radigo.Packet) (processed bool, err error) {
startTime := time.Now()
if pass, err := ra.filterS.Pass(context.TODO(), agReq.Tenant,
if pass, err := ra.fltrs.Pass(context.TODO(), agReq.Tenant,
reqProcessor.Filters, agReq); err != nil || !pass {
return pass, err
}
@@ -202,30 +222,30 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R
utils.RadiusAgent, reqProcessor.ID, utils.ToJSON(cgrEv)))
case utils.MetaAuthorize:
rply := new(sessions.V1AuthorizeReply)
err = ra.connMgr.Call(context.TODO(), ra.cgrCfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1AuthorizeEvent,
err = ra.cm.Call(context.TODO(), ra.cfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1AuthorizeEvent,
cgrEv, rply)
rply.SetMaxUsageNeeded(utils.OptAsBool(cgrEv.APIOpts, utils.MetaAccounts))
agReq.setCGRReply(rply, err)
case utils.MetaInitiate:
rply := new(sessions.V1InitSessionReply)
err = ra.connMgr.Call(context.TODO(), ra.cgrCfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1InitiateSession,
err = ra.cm.Call(context.TODO(), ra.cfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1InitiateSession,
cgrEv, rply)
rply.SetMaxUsageNeeded(utils.OptAsBool(cgrEv.APIOpts, utils.OptsSesInitiate))
agReq.setCGRReply(rply, err)
case utils.MetaUpdate:
rply := new(sessions.V1UpdateSessionReply)
err = ra.connMgr.Call(context.TODO(), ra.cgrCfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1UpdateSession,
err = ra.cm.Call(context.TODO(), ra.cfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1UpdateSession,
cgrEv, rply)
rply.SetMaxUsageNeeded(utils.OptAsBool(cgrEv.APIOpts, utils.OptsSesUpdate))
agReq.setCGRReply(rply, err)
case utils.MetaTerminate:
var rply string
err = ra.connMgr.Call(context.TODO(), ra.cgrCfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1TerminateSession,
err = ra.cm.Call(context.TODO(), ra.cfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1TerminateSession,
cgrEv, &rply)
agReq.setCGRReply(nil, err)
case utils.MetaMessage:
rply := new(sessions.V1ProcessMessageReply)
err = ra.connMgr.Call(context.TODO(), ra.cgrCfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1ProcessMessage, cgrEv, rply)
err = ra.cm.Call(context.TODO(), ra.cfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1ProcessMessage, cgrEv, rply)
// if utils.ErrHasPrefix(err, utils.RalsErrorPrfx) {
// cgrEv.Event[utils.Usage] = 0 // avoid further debits
// } else
@@ -237,7 +257,7 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R
agReq.setCGRReply(rply, err)
case utils.MetaEvent:
rply := new(sessions.V1ProcessEventReply)
err = ra.connMgr.Call(context.TODO(), ra.cgrCfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1ProcessEvent,
err = ra.cm.Call(context.TODO(), ra.cfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1ProcessEvent,
cgrEv, rply)
// if utils.ErrHasPrefix(err, utils.RalsErrorPrfx) {
// cgrEv.Event[utils.Usage] = 0 // avoid further debits
@@ -257,7 +277,7 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R
// separate request so we can capture the Terminate/Event also here
if reqProcessor.Flags.GetBool(utils.MetaCDRs) {
var rplyCDRs string
if err = ra.connMgr.Call(context.TODO(), ra.cgrCfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1ProcessCDR,
if err = ra.cm.Call(context.TODO(), ra.cfg.RadiusAgentCfg().SessionSConns, utils.SessionSv1ProcessCDR,
cgrEv, &rplyCDRs); err != nil {
agReq.CGRReply.Map[utils.Error] = utils.NewLeafNode(err.Error())
}
@@ -303,7 +323,7 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R
statIDs := strings.Split(rawStatIDs, utils.ANDSep)
ev.APIOpts[utils.OptsStatsProfileIDs] = statIDs
var reply []string
if err := ra.connMgr.Call(context.TODO(), ra.cgrCfg.RadiusAgentCfg().StatSConns,
if err := ra.cm.Call(context.TODO(), ra.cfg.RadiusAgentCfg().StatSConns,
utils.StatSv1ProcessEvent, ev, &reply); err != nil {
return false, fmt.Errorf("failed to process %s event in %s: %v",
utils.RadiusAgent, utils.StatS, err)
@@ -315,7 +335,7 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R
thIDs := strings.Split(rawThIDs, utils.ANDSep)
ev.APIOpts[utils.OptsThresholdsProfileIDs] = thIDs
var reply []string
if err := ra.connMgr.Call(context.TODO(), ra.cgrCfg.RadiusAgentCfg().ThresholdSConns,
if err := ra.cm.Call(context.TODO(), ra.cfg.RadiusAgentCfg().ThresholdSConns,
utils.ThresholdSv1ProcessEvent, ev, &reply); err != nil {
return false, fmt.Errorf("failed to process %s event in %s: %v",
utils.RadiusAgent, utils.ThresholdS, err)
@@ -326,18 +346,38 @@ func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.R
func (ra *RadiusAgent) ListenAndServe(stopChan <-chan struct{}) (err error) {
errListen := make(chan error, 2)
go func() {
utils.Logger.Info(fmt.Sprintf("<%s> Start listening for auth requests on <%s>", utils.RadiusAgent, ra.cgrCfg.RadiusAgentCfg().ListenAuth))
if err := ra.rsAuth.ListenAndServe(stopChan); err != nil {
errListen <- err
}
}()
go func() {
utils.Logger.Info(fmt.Sprintf("<%s> Start listening for acct req on <%s>", utils.RadiusAgent, ra.cgrCfg.RadiusAgentCfg().ListenAcct))
if err := ra.rsAcct.ListenAndServe(stopChan); err != nil {
errListen <- err
}
}()
for uri, server := range ra.rsAuth {
ra.wg.Add(1)
go func(srv *radigo.Server, uri string) {
defer ra.wg.Done()
utils.Logger.Info(fmt.Sprintf("<%s> Start listening for auth requests on <%s>", utils.RadiusAgent, uri))
if err := srv.ListenAndServe(stopChan); err != nil {
utils.Logger.Warning(fmt.Sprintf("<%s> error <%v>, on ListenAndServe <%s>",
utils.RadiusAgent, err, uri))
if strings.Contains(err.Error(), "address already in use") {
return
}
errListen <- err
}
}(server, uri)
}
for uri, server := range ra.rsAcct {
ra.wg.Add(1)
go func(srv *radigo.Server, uri string) {
defer ra.wg.Done()
utils.Logger.Info(fmt.Sprintf("<%s> Start listening for acct requests on <%s>", utils.RadiusAgent, uri))
if err := srv.ListenAndServe(stopChan); err != nil {
utils.Logger.Warning(fmt.Sprintf("<%s> error <%v>, on ListenAndServe <%s>",
utils.RadiusAgent, err, uri))
if strings.Contains(err.Error(), "address already in use") {
return
}
errListen <- err
}
}(server, uri)
}
err = <-errListen
return
}
func (ra *RadiusAgent) Wait() { ra.wg.Wait() }

View File

@@ -1040,9 +1040,13 @@ const CGRATES_CFG_JSON = `
"radius_agent": {
"enabled": false, // enables the radius agent: <true|false>
"listen_net": "udp", // network to listen on <udp|tcp>
"listen_auth": "127.0.0.1:1812", // address where to listen for radius authentication requests <x.y.z.y:1234>
"listen_acct": "127.0.0.1:1813", // address where to listen for radius accounting requests <x.y.z.y:1234>
"listeners":[
{
"network": "udp", // network to listen on <udp|tcp>
"auth_address": "127.0.0.1:1812", // address where to listen for radius authentication requests <x.y.z.y:1234>
"acct_address": "127.0.0.1:1813" // address where to listen for radius accounting requests <x.y.z.y:1234>
}
],
"client_secrets": { // hash containing secrets for clients connecting here <*default|$client_ip>
"*default": "CGRateS.org"
},

View File

@@ -761,10 +761,14 @@ func TestDiameterAgentJsonCfg(t *testing.T) {
func TestRadiusAgentJsonCfg(t *testing.T) {
eCfg := &RadiusAgentJsonCfg{
Enabled: utils.BoolPointer(false),
ListenNet: utils.StringPointer("udp"),
ListenAuth: utils.StringPointer("127.0.0.1:1812"),
ListenAcct: utils.StringPointer("127.0.0.1:1813"),
Enabled: utils.BoolPointer(false),
Listeners: &[]*RadiusListenerJsonCfg{
{
Network: utils.StringPointer("udp"),
AuthAddress: utils.StringPointer("127.0.0.1:1812"),
AcctAddress: utils.StringPointer("127.0.0.1:1813"),
},
},
ClientSecrets: map[string]string{
utils.MetaDefault: "CGRateS.org",
},

File diff suppressed because one or more lines are too long

View File

@@ -19,20 +19,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package config
import (
"slices"
"maps"
"slices"
"github.com/cgrates/birpc/context"
"github.com/cgrates/cgrates/utils"
)
type RadiusListener struct {
AuthAddr string
AcctAddr string
Network string // udp or tcp
}
// AsMapInterface returns the config as a map[string]any.
func (lstn *RadiusListener) AsMapInterface() map[string]any {
return map[string]any{
utils.AuthAddrCfg: lstn.AuthAddr,
utils.AcctAddrCfg: lstn.AcctAddr,
utils.NetworkCfg: lstn.Network,
}
}
// RadiusAgentCfg the config section that describes the Radius Agent
type RadiusAgentCfg struct {
Enabled bool
ListenNet string // udp or tcp
ListenAuth string
ListenAcct string
Listeners []RadiusListener
ClientSecrets map[string]string
ClientDictionaries map[string][]string
SessionSConns []string
@@ -50,34 +62,41 @@ func (ra *RadiusAgentCfg) Load(ctx *context.Context, jsnCfg ConfigDB, _ *CGRConf
return ra.loadFromJSONCfg(jsnRACfg)
}
func (ra *RadiusAgentCfg) loadFromJSONCfg(jsnCfg *RadiusAgentJsonCfg) (err error) {
if jsnCfg == nil {
func (ra *RadiusAgentCfg) loadFromJSONCfg(jc *RadiusAgentJsonCfg) (err error) {
if jc == nil {
return nil
}
if jsnCfg.Enabled != nil {
ra.Enabled = *jsnCfg.Enabled
if jc.Enabled != nil {
ra.Enabled = *jc.Enabled
}
if jsnCfg.ListenNet != nil {
ra.ListenNet = *jsnCfg.ListenNet
if jc.Listeners != nil {
ra.Listeners = make([]RadiusListener, 0, len(*jc.Listeners))
for _, jl := range *jc.Listeners {
var rl RadiusListener
if jl.AuthAddress != nil {
rl.AuthAddr = *jl.AuthAddress
}
if jl.AcctAddress != nil {
rl.AcctAddr = *jl.AcctAddress
}
if jl.Network != nil {
rl.Network = *jl.Network
}
ra.Listeners = append(ra.Listeners, rl)
}
}
if jsnCfg.ListenAuth != nil {
ra.ListenAuth = *jsnCfg.ListenAuth
maps.Copy(ra.ClientSecrets, jc.ClientSecrets)
maps.Copy(ra.ClientDictionaries, jc.ClientDictionaries)
if jc.SessionSConns != nil {
ra.SessionSConns = tagInternalConns(*jc.SessionSConns, utils.MetaSessionS)
}
if jsnCfg.ListenAcct != nil {
ra.ListenAcct = *jsnCfg.ListenAcct
if jc.StatSConns != nil {
ra.StatSConns = tagInternalConns(*jc.StatSConns, utils.MetaStats)
}
maps.Copy(ra.ClientSecrets, jsnCfg.ClientSecrets)
maps.Copy(ra.ClientDictionaries, jsnCfg.ClientDictionaries)
if jsnCfg.SessionSConns != nil {
ra.SessionSConns = tagInternalConns(*jsnCfg.SessionSConns, utils.MetaSessionS)
if jc.ThresholdSConns != nil {
ra.ThresholdSConns = tagInternalConns(*jc.ThresholdSConns, utils.MetaThresholds)
}
if jsnCfg.StatSConns != nil {
ra.StatSConns = tagInternalConns(*jsnCfg.StatSConns, utils.MetaStats)
}
if jsnCfg.ThresholdSConns != nil {
ra.ThresholdSConns = tagInternalConns(*jsnCfg.ThresholdSConns, utils.MetaThresholds)
}
ra.RequestProcessors, err = appendRequestProcessors(ra.RequestProcessors, jsnCfg.RequestProcessors)
ra.RequestProcessors, err = appendRequestProcessors(ra.RequestProcessors, jc.RequestProcessors)
return
}
@@ -87,11 +106,13 @@ func (ra RadiusAgentCfg) AsMapInterface() any {
for i, item := range ra.RequestProcessors {
requestProcessors[i] = item.AsMapInterface()
}
listeners := make([]map[string]any, len(ra.Listeners))
for i, item := range ra.Listeners {
listeners[i] = item.AsMapInterface()
}
mp := map[string]any{
utils.EnabledCfg: ra.Enabled,
utils.ListenNetCfg: ra.ListenNet,
utils.ListenAuthCfg: ra.ListenAuth,
utils.ListenAcctCfg: ra.ListenAcct,
utils.ListenersCfg: listeners,
utils.ClientSecretsCfg: maps.Clone(ra.ClientSecrets),
utils.ClientDictionariesCfg: maps.Clone(ra.ClientDictionaries),
utils.SessionSConnsCfg: stripInternalConns(ra.SessionSConns),
@@ -109,9 +130,7 @@ func (ra RadiusAgentCfg) CloneSection() Section { return ra.Clone() }
func (ra RadiusAgentCfg) Clone() *RadiusAgentCfg {
clone := &RadiusAgentCfg{
Enabled: ra.Enabled,
ListenNet: ra.ListenNet,
ListenAuth: ra.ListenAuth,
ListenAcct: ra.ListenAcct,
Listeners: slices.Clone(ra.Listeners),
ClientSecrets: maps.Clone(ra.ClientSecrets),
SessionSConns: slices.Clone(ra.SessionSConns),
StatSConns: slices.Clone(ra.StatSConns),
@@ -132,18 +151,22 @@ func (ra RadiusAgentCfg) Clone() *RadiusAgentCfg {
return clone
}
type RadiusListenerJsonCfg struct {
Network *string `json:"network"`
AuthAddress *string `json:"auth_address"`
AcctAddress *string `json:"acct_address"`
}
// Radius Agent configuration section
type RadiusAgentJsonCfg struct {
Enabled *bool `json:"enabled"`
ListenNet *string `json:"listen_net"`
ListenAuth *string `json:"listen_auth"`
ListenAcct *string `json:"listen_acct"`
ClientSecrets map[string]string `json:"client_secrets"`
ClientDictionaries map[string][]string `json:"client_dictionaries"`
SessionSConns *[]string `json:"sessions_conns"`
StatSConns *[]string `json:"stats_conns"`
ThresholdSConns *[]string `json:"thresholds_conns"`
RequestProcessors *[]*ReqProcessorJsnCfg `json:"request_processors"`
Enabled *bool `json:"enabled"`
Listeners *[]*RadiusListenerJsonCfg `json:"listeners"`
ClientSecrets map[string]string `json:"client_secrets"`
ClientDictionaries map[string][]string `json:"client_dictionaries"`
SessionSConns *[]string `json:"sessions_conns"`
StatSConns *[]string `json:"stats_conns"`
ThresholdSConns *[]string `json:"thresholds_conns"`
RequestProcessors *[]*ReqProcessorJsnCfg `json:"request_processors"`
}
func diffRadiusAgentJsonCfg(d *RadiusAgentJsonCfg, v1, v2 *RadiusAgentCfg) *RadiusAgentJsonCfg {
@@ -153,14 +176,16 @@ func diffRadiusAgentJsonCfg(d *RadiusAgentJsonCfg, v1, v2 *RadiusAgentCfg) *Radi
if v1.Enabled != v2.Enabled {
d.Enabled = utils.BoolPointer(v2.Enabled)
}
if v1.ListenNet != v2.ListenNet {
d.ListenNet = utils.StringPointer(v2.ListenNet)
}
if v1.ListenAuth != v2.ListenAuth {
d.ListenAuth = utils.StringPointer(v2.ListenAuth)
}
if v1.ListenAcct != v2.ListenAcct {
d.ListenAcct = utils.StringPointer(v2.ListenAcct)
if !slices.Equal(v1.Listeners, v2.Listeners) {
listeners := make([]*RadiusListenerJsonCfg, len(v2.Listeners))
for i, listener := range v2.Listeners {
listeners[i] = &RadiusListenerJsonCfg{
AuthAddress: utils.StringPointer(listener.AuthAddr),
AcctAddress: utils.StringPointer(listener.AcctAddr),
Network: utils.StringPointer(listener.Network),
}
}
d.Listeners = &listeners
}
d.ClientSecrets = diffMapString(d.ClientSecrets, v1.ClientSecrets, v2.ClientSecrets)
d.ClientDictionaries = diffMapStringSlice(d.ClientDictionaries, v1.ClientDictionaries, v2.ClientDictionaries)

View File

@@ -28,10 +28,14 @@ import (
func TestRadiusAgentCfgloadFromJsonCfgCase1(t *testing.T) {
cfgJSON := &RadiusAgentJsonCfg{
Enabled: utils.BoolPointer(true),
ListenNet: utils.StringPointer(utils.UDP),
ListenAuth: utils.StringPointer("127.0.0.1:1812"),
ListenAcct: utils.StringPointer("127.0.0.1:1813"),
Enabled: utils.BoolPointer(true),
Listeners: &[]*RadiusListenerJsonCfg{
{
Network: utils.StringPointer(utils.UDP),
AuthAddress: utils.StringPointer("127.0.0.1:1812"),
AcctAddress: utils.StringPointer("127.0.0.1:1813"),
},
},
ClientSecrets: map[string]string{utils.MetaDefault: "CGRateS.org"},
ClientDictionaries: map[string][]string{utils.MetaDefault: {"/usr/share/cgrates/radius/dict/"}},
SessionSConns: &[]string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaSessionS)},
@@ -59,10 +63,14 @@ func TestRadiusAgentCfgloadFromJsonCfgCase1(t *testing.T) {
},
}
expected := &RadiusAgentCfg{
Enabled: true,
ListenNet: "udp",
ListenAuth: "127.0.0.1:1812",
ListenAcct: "127.0.0.1:1813",
Enabled: true,
Listeners: []RadiusListener{
{
Network: utils.UDP,
AuthAddr: "127.0.0.1:1812",
AcctAddr: "127.0.0.1:1813",
},
},
ClientSecrets: map[string]string{utils.MetaDefault: "CGRateS.org"},
ClientDictionaries: map[string][]string{utils.MetaDefault: {"/usr/share/cgrates/radius/dict/"}},
SessionSConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaSessionS)},
@@ -155,38 +163,46 @@ func TestRadiusAgentCfgloadFromJsonCfgCase3(t *testing.T) {
func TestRadiusAgentCfgAsMapInterface(t *testing.T) {
cfgJSONStr := `{
"radius_agent": {
"enabled": true,
"listen_auth": "127.0.0.1:1816",
"listen_acct": "127.0.0.1:1892",
"client_dictionaries": {
"*default": [
"/usr/share/cgrates/"
]
},
"sessions_conns": ["*birpc_internal", "*conn1","*conn2"],
"stats_conns": ["*internal", "*conn1","*conn2"],
"thresholds_conns": ["*internal", "*conn1","*conn2"],
"request_processors": [
{
"id": "OutboundAUTHDryRun",
"filters": ["*string:~*req.request_type:OutboundAUTH","*string:~*req.Msisdn:497700056231"],
"tenant": "cgrates.org",
"flags": ["*dryRun"],
"request_fields":[],
"reply_fields":[
{"tag": "Allow", "path": "*rep.response.Allow", "type": "*constant",
"value": "1", "mandatory": true},
],
},],
},
"radius_agent": {
"enabled": true,
"listeners":[
{
"network": "udp",
"auth_address": "127.0.0.1:1816",
"acct_address": "127.0.0.1:1892"
}
],
"client_dictionaries": {
"*default": [
"/usr/share/cgrates/"
]
},
"sessions_conns": ["*birpc_internal", "*conn1","*conn2"],
"stats_conns": ["*internal", "*conn1","*conn2"],
"thresholds_conns": ["*internal", "*conn1","*conn2"],
"request_processors": [
{
"id": "OutboundAUTHDryRun",
"filters": ["*string:~*req.request_type:OutboundAUTH","*string:~*req.Msisdn:497700056231"],
"tenant": "cgrates.org",
"flags": ["*dryRun"],
"request_fields":[],
"reply_fields":[
{"tag": "Allow", "path": "*rep.response.Allow", "type": "*constant", "value": "1", "mandatory": true},
]
}
]
}
}`
eMap := map[string]any{
utils.EnabledCfg: true,
utils.ListenNetCfg: "udp",
utils.ListenAuthCfg: "127.0.0.1:1816",
utils.ListenAcctCfg: "127.0.0.1:1892",
utils.EnabledCfg: true,
utils.ListenersCfg: []map[string]any{
{
utils.NetworkCfg: utils.UDP,
utils.AuthAddrCfg: "127.0.0.1:1816",
utils.AcctAddrCfg: "127.0.0.1:1892",
},
},
utils.ClientSecretsCfg: map[string]string{
utils.MetaDefault: "CGRateS.org",
},
@@ -222,10 +238,14 @@ func TestRadiusAgentCfgAsMapInterface1(t *testing.T) {
"radius_agent": {},
}`
eMap := map[string]any{
utils.EnabledCfg: false,
utils.ListenNetCfg: "udp",
utils.ListenAuthCfg: "127.0.0.1:1812",
utils.ListenAcctCfg: "127.0.0.1:1813",
utils.EnabledCfg: false,
utils.ListenersCfg: []map[string]any{
{
utils.NetworkCfg: utils.UDP,
utils.AuthAddrCfg: "127.0.0.1:1812",
utils.AcctAddrCfg: "127.0.0.1:1813",
},
},
utils.ClientSecretsCfg: map[string]string{
utils.MetaDefault: "CGRateS.org",
},
@@ -246,10 +266,14 @@ func TestRadiusAgentCfgAsMapInterface1(t *testing.T) {
func TestRadiusAgentCfgClone(t *testing.T) {
ban := &RadiusAgentCfg{
Enabled: true,
ListenNet: "udp",
ListenAuth: "127.0.0.1:1812",
ListenAcct: "127.0.0.1:1813",
Enabled: true,
Listeners: []RadiusListener{
{
Network: utils.UDP,
AuthAddr: "127.0.0.1:1812",
AcctAddr: "127.0.0.1:1813",
},
},
ClientSecrets: map[string]string{utils.MetaDefault: "CGRateS.org"},
ClientDictionaries: map[string][]string{utils.MetaDefault: {"/usr/share/cgrates/radius/dict/"}},
SessionSConns: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaSessionS), "*conn1"},
@@ -296,10 +320,14 @@ func TestDiffRadiusAgentJsonCfg(t *testing.T) {
var d *RadiusAgentJsonCfg
v1 := &RadiusAgentCfg{
Enabled: false,
ListenNet: "tcp",
ListenAuth: "radius_auth",
ListenAcct: "radius_account",
Enabled: false,
Listeners: []RadiusListener{
{
Network: utils.TCP,
AuthAddr: "radius_auth",
AcctAddr: "radius_account",
},
},
ClientSecrets: map[string]string{},
ClientDictionaries: map[string][]string{},
SessionSConns: []string{"*localhost"},
@@ -309,10 +337,14 @@ func TestDiffRadiusAgentJsonCfg(t *testing.T) {
}
v2 := &RadiusAgentCfg{
Enabled: true,
ListenNet: "udp",
ListenAuth: "radius_auth2",
ListenAcct: "radius_account2",
Enabled: true,
Listeners: []RadiusListener{
{
Network: utils.UDP,
AuthAddr: "radius_auth2",
AcctAddr: "radius_account2",
},
},
ClientSecrets: map[string]string{
"radius_user": "radius_pass",
},
@@ -331,10 +363,14 @@ func TestDiffRadiusAgentJsonCfg(t *testing.T) {
}
expected := &RadiusAgentJsonCfg{
Enabled: utils.BoolPointer(true),
ListenNet: utils.StringPointer("udp"),
ListenAuth: utils.StringPointer("radius_auth2"),
ListenAcct: utils.StringPointer("radius_account2"),
Enabled: utils.BoolPointer(true),
Listeners: &[]*RadiusListenerJsonCfg{
{
Network: utils.StringPointer(utils.UDP),
AuthAddress: utils.StringPointer("radius_auth2"),
AcctAddress: utils.StringPointer("radius_account2"),
},
},
ClientSecrets: map[string]string{
"radius_user": "radius_pass",
},
@@ -373,10 +409,14 @@ func TestDiffRadiusAgentJsonCfg(t *testing.T) {
func TestRadiusAgentCloneSection(t *testing.T) {
rdagCfg := &RadiusAgentCfg{
Enabled: true,
ListenNet: "udp",
ListenAuth: "radius_auth2",
ListenAcct: "radius_account2",
Enabled: true,
Listeners: []RadiusListener{
{
Network: utils.UDP,
AuthAddr: "radius_auth2",
AcctAddr: "radius_account2",
},
},
ClientSecrets: map[string]string{
"radius_user": "radius_pass",
},
@@ -395,10 +435,14 @@ func TestRadiusAgentCloneSection(t *testing.T) {
}
exp := &RadiusAgentCfg{
Enabled: true,
ListenNet: "udp",
ListenAuth: "radius_auth2",
ListenAcct: "radius_account2",
Enabled: true,
Listeners: []RadiusListener{
{
Network: utils.UDP,
AuthAddr: "radius_auth2",
AcctAddr: "radius_account2",
},
},
ClientSecrets: map[string]string{
"radius_user": "radius_pass",
},

View File

@@ -42,9 +42,6 @@ type RadiusAgent struct {
cfg *config.CGRConfig
stopChan chan struct{}
rad *agents.RadiusAgent
lnet string
lauth string
lacct string
stateDeps *StateDependencies // channel subscriptions for state changes
}
@@ -65,10 +62,6 @@ func (rad *RadiusAgent) Start(shutdown *utils.SyncedChan, registry *servmanager.
rad.mu.Lock()
defer rad.mu.Unlock()
rad.lnet = rad.cfg.RadiusAgentCfg().ListenNet
rad.lauth = rad.cfg.RadiusAgentCfg().ListenAuth
rad.lacct = rad.cfg.RadiusAgentCfg().ListenAcct
if rad.rad, err = agents.NewRadiusAgent(rad.cfg, fs.FilterS(), cms.ConnManager()); err != nil {
return
}
@@ -88,27 +81,21 @@ func (rad *RadiusAgent) listenAndServe(r *agents.RadiusAgent, shutdown *utils.Sy
// Reload handles the change of config
func (rad *RadiusAgent) Reload(shutdown *utils.SyncedChan, registry *servmanager.ServiceRegistry) (err error) {
if rad.lnet == rad.cfg.RadiusAgentCfg().ListenNet &&
rad.lauth == rad.cfg.RadiusAgentCfg().ListenAuth &&
rad.lacct == rad.cfg.RadiusAgentCfg().ListenAcct {
return
}
rad.shutdown()
rad.Shutdown(registry)
return rad.Start(shutdown, registry)
}
// Shutdown stops the service
func (rad *RadiusAgent) Shutdown(_ *servmanager.ServiceRegistry) (err error) {
rad.shutdown()
return // no shutdown for the momment
}
func (rad *RadiusAgent) shutdown() {
func (rad *RadiusAgent) Shutdown(_ *servmanager.ServiceRegistry) error {
if rad.rad == nil {
return nil
}
close(rad.stopChan)
rad.rad.Wait()
rad.mu.Lock()
defer rad.mu.Unlock()
close(rad.stopChan)
rad.rad = nil
return nil
}
// ServiceName returns the service name

View File

@@ -2258,8 +2258,8 @@ const (
ReplyFieldsCfg = "reply_fields"
// RadiusAgentCfg
ListenAuthCfg = "listen_auth"
ListenAcctCfg = "listen_acct"
AuthAddrCfg = "auth_address"
AcctAddrCfg = "acct_address"
ClientSecretsCfg = "client_secrets"
ClientDictionariesCfg = "client_dictionaries"