Update client_da_addresses structure

Supports configuring transport (tcp/udp), host, port and
flags (only *log for now).
This commit is contained in:
ionutboangiu
2024-03-05 11:09:38 -05:00
committed by Dan Christian Bogos
parent 8dc3a9e28f
commit ca6a9440f7
8 changed files with 114 additions and 30 deletions

View File

@@ -22,6 +22,7 @@ import (
"errors"
"fmt"
"net"
"strconv"
"strings"
"sync"
@@ -575,12 +576,13 @@ func (ra *RadiusAgent) sendRadDaReq(requestType radigo.PacketCode, requestTempla
return 0, fmt.Errorf("could not set attributes: %w", err)
}
remoteAddr, remoteHost, err := dmRemoteAddr(packet.RemoteAddr().String(),
remoteAddr, remoteHost, err := daRequestAddress(packet.RemoteAddr().String(),
ra.cgrCfg.RadiusAgentCfg().ClientDaAddresses)
if err != nil {
return 0, fmt.Errorf("retrieving remote address failed: %w", err)
}
dynAuthClient, err := radigo.NewClient(utils.UDP, remoteAddr,
clientOpts := ra.cgrCfg.RadiusAgentCfg().ClientDaAddresses[remoteHost]
dynAuthClient, err := radigo.NewClient(clientOpts.Transport, remoteAddr,
ra.dacCfg.secrets.GetSecret(remoteHost),
ra.dacCfg.dicts.GetInstance(remoteHost),
ra.cgrCfg.GeneralCfg().ConnectAttempts, nil, utils.Logger)
@@ -591,6 +593,11 @@ func (ra *RadiusAgent) sendRadDaReq(requestType radigo.PacketCode, requestTempla
if err = radAppendAttributes(dynAuthReq, agReq.radDAReq); err != nil {
return 0, fmt.Errorf("could not append attributes to the request packet: %w", err)
}
if clientOpts.Flags.Has(utils.MetaLog) {
utils.Logger.Info(
fmt.Sprintf("<%s> LOG, sending %s for session with ID '%s' to '%s': %s",
utils.RadiusAgent, requestType, sessionID, remoteAddr, utils.ToJSON(dynAuthReq)))
}
dynAuthReply, err := dynAuthClient.SendRequest(dynAuthReq)
if err != nil {
return 0, fmt.Errorf("failed to send request: %w", err)
@@ -598,9 +605,9 @@ func (ra *RadiusAgent) sendRadDaReq(requestType radigo.PacketCode, requestTempla
return dynAuthReply.Code, nil
}
// dmRemoteAddr ranges over the client_da_addresses map and returns the address configured for a
// daRequestAddress ranges over the client_da_addresses map and returns the address configured for a
// specific client alongside the host.
func dmRemoteAddr(remoteAddr string, dynAuthAddresses map[string]string) (string, string, error) {
func daRequestAddress(remoteAddr string, dynAuthAddresses map[string]config.DAClientOpts) (string, string, error) {
if len(dynAuthAddresses) == 0 {
return "", "", utils.ErrNotFound
}
@@ -608,9 +615,10 @@ func dmRemoteAddr(remoteAddr string, dynAuthAddresses map[string]string) (string
if err != nil {
return "", "", err
}
for host, addr := range dynAuthAddresses {
for host, opts := range dynAuthAddresses {
if host == remoteHost {
return addr, host, nil
address := opts.Host + ":" + strconv.Itoa(opts.Port)
return address, host, nil
}
}
return "", "", utils.ErrNotFound

View File

@@ -186,7 +186,7 @@ func TestRadiusCoADisconnect(t *testing.T) {
var testRadClient testNAS
secrets := radigo.NewSecrets(map[string]string{utils.MetaDefault: "CGRateS.org"})
dicts := radigo.NewDictionaries(map[string]*radigo.Dictionary{utils.MetaDefault: dictRad})
testRadClient.server = radigo.NewServer("udp", "127.0.0.1:3799", secrets, dicts,
testRadClient.server = radigo.NewServer(utils.UDP, "127.0.0.1:3799", secrets, dicts,
map[radigo.PacketCode]func(*radigo.Packet) (*radigo.Packet, error){
radigo.DisconnectRequest: handleDisconnect,
radigo.CoARequest: handleCoA,

View File

@@ -695,27 +695,35 @@ const CGRATES_CFG_JSON = `
"radius_agent": {
"enabled": false, // enables the radius agent: <true|false>
"enabled": false, // enables the radius agent: <true|false>
"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>
"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>
"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": [ // key represents the client IP or catch-all <*default|$client_ip>
"client_dictionaries": { // per client path towards directory holding additional dictionaries to load (extra to RFC)
"*default": [ // key represents the client IP or catch-all <*default|$client_ip>
"/usr/share/cgrates/radius/dict/",
]
},
"client_da_addresses": {}, // list of clients supporting dynamic authorization
"client_da_addresses": { // configuration for clients capable of handling Dynamic Authorization (CoA/DM) requests.
// "nasIdentifier": { // identifier for the NAS, typically the host from the initial RADIUS packet.
// "transport": "udp", // transport protocol for Dynamic Authorization requests, defaults to UDP.
// "host": "", // optionally specify an alternative host for DA requests. Defaults to the NAS identifier if empty.
// "port": 3799, // port for Dynamic Authorization requests, default is 3799.
// "flags": [] // additional options, currently supports *log for logging DA requests before sending.
// }
},
"requests_cache_key": "",
"sessions_conns": ["*internal"],
"dmr_template": "", // template used to build the Disconnect-Request packet
"coa_template": "", // template used to build the CoA-Request packet
"request_processors": [ // request processors to be applied to Radius messages
"dmr_template": "", // template used to build the Disconnect-Request packet
"coa_template": "", // template used to build the CoA-Request packet
"request_processors": [ // request processors to be applied to Radius messages
]
},

View File

@@ -1000,7 +1000,7 @@ func TestRadiusAgentJsonCfg(t *testing.T) {
Request_processors: &[]*ReqProcessorJsnCfg{},
Dmr_template: utils.StringPointer(""),
Coa_template: utils.StringPointer(""),
Client_da_addresses: map[string]string{},
Client_da_addresses: map[string]DAClientOptsJson{},
}
dfCgrJSONCfg, err := NewCgrJsonCfgFromBytes([]byte(CGRATES_CFG_JSON))
if err != nil {

View File

@@ -532,13 +532,20 @@ type RadiListenerJsnCfg struct {
Acct_Address *string
}
type DAClientOptsJson struct {
Transport *string
Host *string
Port *int
Flags []string
}
// Radius Agent configuration section
type RadiusAgentJsonCfg struct {
Enabled *bool
Listeners *[]*RadiListenerJsnCfg
Client_secrets *map[string]string
Client_dictionaries *map[string][]string
Client_da_addresses map[string]string
Client_da_addresses map[string]DAClientOptsJson
Sessions_conns *[]string
Dmr_template *string
Coa_template *string

View File

@@ -34,7 +34,7 @@ type RadiusAgentCfg struct {
Listeners []RadiusListener
ClientSecrets map[string]string
ClientDictionaries map[string][]string
ClientDaAddresses map[string]string
ClientDaAddresses map[string]DAClientOpts
SessionSConns []string
DMRTemplate string
CoATemplate string
@@ -82,10 +82,13 @@ func (ra *RadiusAgentCfg) loadFromJSONCfg(jsnCfg *RadiusAgentJsonCfg, separator
}
if len(jsnCfg.Client_da_addresses) != 0 {
if ra.ClientDaAddresses == nil {
ra.ClientDaAddresses = make(map[string]string)
ra.ClientDaAddresses = make(map[string]DAClientOpts)
}
for k, v := range jsnCfg.Client_da_addresses {
ra.ClientDaAddresses[k] = v
ra.ClientDaAddresses = make(map[string]DAClientOpts, len(jsnCfg.Client_da_addresses))
for hostKey, clientOpts := range jsnCfg.Client_da_addresses {
cfg := DAClientOpts{}
cfg.loadFromJSONCfg(clientOpts, hostKey)
ra.ClientDaAddresses[hostKey] = cfg
}
}
if jsnCfg.Sessions_conns != nil {
@@ -177,9 +180,9 @@ func (ra *RadiusAgentCfg) AsMapInterface(separator string) (initialMP map[string
}
initialMP[utils.ClientDictionariesCfg] = clientDictionaries
if len(ra.ClientDaAddresses) != 0 {
clientDaAddresses := make(map[string]string)
clientDaAddresses := make(map[string]any)
for k, v := range ra.ClientDaAddresses {
clientDaAddresses[k] = v
clientDaAddresses[k] = v.AsMapInterface()
}
initialMP[utils.ClientDaAddressesCfg] = clientDaAddresses
}
@@ -213,9 +216,9 @@ func (ra RadiusAgentCfg) Clone() (cln *RadiusAgentCfg) {
cln.ClientDictionaries[k] = v
}
if len(ra.ClientDaAddresses) != 0 {
cln.ClientDaAddresses = make(map[string]string)
cln.ClientDaAddresses = make(map[string]DAClientOpts, len(ra.ClientDaAddresses))
for k, v := range ra.ClientDaAddresses {
cln.ClientDaAddresses[k] = v
cln.ClientDaAddresses[k] = *v.Clone()
}
}
if ra.RequestProcessors != nil {
@@ -226,3 +229,53 @@ func (ra RadiusAgentCfg) Clone() (cln *RadiusAgentCfg) {
}
return
}
type DAClientOpts struct {
Transport string // transport protocol for Dynamic Authorization requests <UDP|TCP>.
Host string // alternative host for DA requests
Port int // port for Dynamic Authorization requests
Flags utils.FlagsWithParams // flags (only *log for now)
}
func (cda *DAClientOpts) loadFromJSONCfg(jsnCfg DAClientOptsJson, defaultHost string) error {
cda.Transport = utils.UDP
if jsnCfg.Transport != nil {
cda.Transport = *jsnCfg.Transport
}
cda.Host = defaultHost
if jsnCfg.Host != nil {
cda.Host = *jsnCfg.Host
}
cda.Port = 3799
if jsnCfg.Port != nil {
cda.Port = *jsnCfg.Port
}
if jsnCfg.Flags != nil {
cda.Flags = utils.FlagsWithParamsFromSlice(jsnCfg.Flags)
}
return nil
}
func (cda *DAClientOpts) Clone() *DAClientOpts {
cln := DAClientOpts{
Transport: cda.Transport,
Host: cda.Host,
Port: cda.Port,
}
if cda.Flags != nil {
cln.Flags = cda.Flags.Clone()
}
return &cln
}
func (cda *DAClientOpts) AsMapInterface() map[string]any {
mp := map[string]any{
utils.TransportCfg: cda.Transport,
utils.HostCfg: cda.Host,
utils.PortCfg: cda.Port,
}
if len(cda.Flags) != 0 {
mp[utils.FlagsCfg] = cda.Flags.SliceFlags()
}
return mp
}

View File

@@ -58,8 +58,13 @@
"radius_agent": {
"enabled": true,
"sessions_conns": ["*bijson_localhost"],
"client_da_addresses": {
"127.0.0.1": ":3799"
"client_da_addresses": {
"127.0.0.1": {
"transport": "udp",
"host": "",
"port": 3799,
"flags": ["*log"]
}
},
"listeners":[
{
@@ -78,3 +83,4 @@
}
}

View File

@@ -2261,6 +2261,8 @@ const (
ClientDaAddressesCfg = "client_da_addresses"
DMRTemplateCfg = "dmr_template"
CoATemplateCfg = "coa_template"
HostCfg = "host"
PortCfg = "port"
// AttributeSCfg
IndexedSelectsCfg = "indexed_selects"