diff --git a/agents/librad.go b/agents/librad.go new file mode 100644 index 000000000..6eb7007ba --- /dev/null +++ b/agents/librad.go @@ -0,0 +1,41 @@ +/* +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 ( + "github.com/cgrates/cgrates/utils" + "github.com/cgrates/radigo" +) + +/* +Various RADIUS helpers here +*/ + +func radPassesFieldFilter(pkt *radigo.Packet, fieldFilter *utils.RSRField, processorVars map[string]string) (pass bool) { + if fieldFilter == nil { + return true + } + if val, hasIt := processorVars[fieldFilter.Id]; hasIt { // ProcessorVars have priority + if fieldFilter.FilterPasses(val) { + return true, 0 + } + return false + } + return +} diff --git a/agents/librad_test.go b/agents/librad_test.go new file mode 100644 index 000000000..8fee7742c --- /dev/null +++ b/agents/librad_test.go @@ -0,0 +1,36 @@ +/* +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 ( + "bytes" + "encoding/binary" + "fmt" + "reflect" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/sessionmanager" + "github.com/cgrates/cgrates/utils" + "github.com/fiorix/go-diameter/diam" + "github.com/fiorix/go-diameter/diam/avp" + "github.com/fiorix/go-diameter/diam/datatype" + "github.com/fiorix/go-diameter/diam/dict" +) diff --git a/agents/radagent.go b/agents/radagent.go index 374ded580..94d32cc23 100644 --- a/agents/radagent.go +++ b/agents/radagent.go @@ -35,9 +35,9 @@ func NewRadiusAgent(cgrCfg *config.CGRConfig, smg rpcclient.RpcClientConnection) } 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}) + map[radigo.PacketCode]func(*radigo.Packet) (*radigo.Packet, error){radigo.AccessRequest: ra.handleAuth}, nil) 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}) + map[radigo.PacketCode]func(*radigo.Packet) (*radigo.Packet, error){radigo.AccountingRequest: ra.handleAcct}, nil) return } @@ -59,12 +59,26 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e return } +// RadiusAgent handleAcct, received req: &{RWMutex:{w:{state:0 sema:0} writerSem:0 readerSem:0 readerCount:0 readerWait:0} dict:0xc4202e5840 secret:CGRateS.org Code:AccountingRequest Identifier:143 Authenticator:[67 77 204 122 189 209 219 22 9 176 15 228 24 246 183 7] AVPs:[0xc42023c230 0xc42023c2a0 0xc42023c310 0xc42023c460 0xc42023c4d0 0xc42023c540 0xc42023c850 0xc42023ce00 0xc42023d180 0xc42023d1f0 0xc42023d260]} +// Identifier:144 Authenticator:[192 197 33 53 203 181 16 117 204 143 172 174 231 245 81 116] AVPs:[0xc42023d5e0 0xc42023d650 0xc42023d880 0xc42023d8f0 0xc42023da40 0xc42023db20 0xc42023dc70 0xc42023dd50 0xc42023ddc0 0xc42023de30 0xc42023dea0]} func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err error) { - utils.Logger.Debug(fmt.Sprintf("RadiusAgent handleAcct, received req: %+v", req)) + req.SetAVPValues() + utils.Logger.Debug(fmt.Sprintf("Received request: %s", utils.ToJSON(req))) rpl = req.Reply() rpl.Code = radigo.AccountingResponse - for _, avp := range req.AVPs { - rpl.AVPs = append(rpl.AVPs, avp) + rpl.SetAVPValues() + return +} + +func (ra *RadiusAgent) processRequest(req *radigo.Packet, reqProcessor *config.RARequestProcessor) (processed bool, err error) { + passesAllFilters := true + for _, fldFilter := range reqProcessor.RequestFilter { + if passes, _ := radPassesFieldFilter(req, fldFilter, nil); !passes { + passesAllFilters = false + } + } + if !passesAllFilters { // Not going with this processor further + return false, nil } return } diff --git a/data/conf/samples/radagent/cgrates.json b/data/conf/samples/radagent/cgrates.json index ab571a016..cb7f38a01 100644 --- a/data/conf/samples/radagent/cgrates.json +++ b/data/conf/samples/radagent/cgrates.json @@ -1,8 +1,11 @@ { // CGRateS Configuration file // -// Used for cgradmin -// Starts rater, scheduler + +"general": { + "log_level": 7, +}, + "listen": { "rpc_json": ":2012", // RPC JSON listening address @@ -74,6 +77,36 @@ "radius_agent": { "enabled": true, + "request_processors": [ + { + "id": "KamailioAccountingStart", + "dry_run": false, + "request_filter": "Service-Type(Sip-Session);Sip-Method(Invite)", + "flags": [], + "continue_on_success": false, + "append_reply": true, + "request_fields":[ + {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "User-Name", "mandatory": true}, + {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Called-Station-Id", "mandatory": true}, + ], + "reply_fields":[], + }, + { + "id": "KamailioAccountingStop", + "dry_run": false, + "request_filter": "Service-Type(Sip-Session);Sip-Method(Bye)", + "flags": [], + "continue_on_success": false, + "append_reply": true, + "request_fields":[ + {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "User-Name", "mandatory": true}, + {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Called-Station-Id", "mandatory": true}, + ], + "reply_fields":[], + }, + + ], + }, } diff --git a/data/radius/dict/dictionary.kamailio b/data/radius/dict/dictionary.kamailio new file mode 100644 index 000000000..2188030ca --- /dev/null +++ b/data/radius/dict/dictionary.kamailio @@ -0,0 +1,86 @@ +# +# $Id$ +# +# SIP RADIUS attributes +# +# Proprietary indicates an attribute that hasn't +# been standardized +# +# +# NOTE: All standard (IANA registered) attributes are +# defined in the default dictionary of the +# radiusclient-ng library. +# + + +#### Attributes ### +ATTRIBUTE Sip-Response-Code 102 integer +ATTRIBUTE Sip-Method 101 integer +ATTRIBUTE Sip-Response-Code 102 integer +ATTRIBUTE Sip-CSeq 103 string +ATTRIBUTE Sip-To-Tag 104 string +ATTRIBUTE Sip-From-Tag 105 string +ATTRIBUTE Sip-Branch-ID 106 string +ATTRIBUTE Sip-Translated-Request-URI 107 string +ATTRIBUTE Sip-Source-IP-Address 108 ipaddr +ATTRIBUTE Sip-Source-Port 109 integer +ATTRIBUTE Sip-User-ID 110 string +ATTRIBUTE Sip-User-Realm 111 string +ATTRIBUTE Sip-User-Nonce 112 string +ATTRIBUTE Sip-User-Method 113 string +ATTRIBUTE Sip-User-Digest-URI 114 string +ATTRIBUTE Sip-User-Nonce-Count 115 string +ATTRIBUTE Sip-User-QOP 116 string +ATTRIBUTE Sip-User-Opaque 117 string +ATTRIBUTE Sip-User-Response 118 string +ATTRIBUTE Sip-User-CNonce 119 string +ATTRIBUTE Sip-URI-User 208 string +ATTRIBUTE Sip-Req-URI 210 string +ATTRIBUTE Sip-CC 212 string +ATTRIBUTE Sip-RPId 213 string +ATTRIBUTE Digest-Response 206 string +ATTRIBUTE Digest-Attributes 207 string +ATTRIBUTE Digest-Realm 1063 string +ATTRIBUTE Digest-Nonce 1064 string +ATTRIBUTE Digest-Method 1065 string +ATTRIBUTE Digest-URI 1066 string +ATTRIBUTE Digest-QOP 1067 string +ATTRIBUTE Digest-Algorithm 1068 string +ATTRIBUTE Digest-Body-Digest 1069 string +ATTRIBUTE Digest-CNonce 1070 string +ATTRIBUTE Digest-Nonce-Count 1071 string +ATTRIBUTE Digest-User-Name 1072 string + +ATTRIBUTE Sip-Uri-User 208 string # Proprietary, auth_radius +ATTRIBUTE Sip-Group 211 string # Proprietary, group_radius +ATTRIBUTE Sip-Rpid 213 string # Proprietary, auth_radius +ATTRIBUTE SIP-AVP 225 string # Proprietary, avp_radius + +### Acct-Status-Type Values ### +VALUE Acct-Status-Type Failed 15 # RFC2866, acc + +### Service-Type Values ### +#VALUE Service-Type Call-Check 10 # RFC2865, uri_radius +VALUE Service-Type Group-Check 12 # Proprietary, group_radius +VALUE Service-Type Sip-Session 15 # Schulzrinne, acc, auth_radius +VALUE Service-Type SIP-Caller-AVPs 30 # Proprietary, avp_radius +VALUE Service-Type SIP-Callee-AVPs 31 # Proprietary, avp_radius + +### Sip-Method Values ### +VALUE Sip-Method Undefined 0 +VALUE Sip-Method Invite 1 +VALUE Sip-Method Cancel 2 +VALUE Sip-Method Ack 4 +VALUE Sip-Method Bye 8 +VALUE Sip-Method Info 16 +VALUE Sip-Method Options 32 +VALUE Sip-Method Update 64 +VALUE Sip-Method Register 128 +VALUE Sip-Method Message 256 +VALUE Sip-Method Subscribe 512 +VALUE Sip-Method Notify 1024 +VALUE Sip-Method Prack 2048 +VALUE Sip-Method Refer 4096 +VALUE Sip-Method Other 8192 + + diff --git a/engine/storage_cdrs_it_test.go b/engine/storage_cdrs_it_test.go index 72e05abd4..44363bd9d 100644 --- a/engine/storage_cdrs_it_test.go +++ b/engine/storage_cdrs_it_test.go @@ -32,7 +32,6 @@ import ( ) func TestITCDRsMySQL(t *testing.T) { - cfg, err := config.NewCGRConfigFromFolder(path.Join(*dataDir, "conf", "samples", "storage", "mysql")) if err != nil { t.Error(err) @@ -49,7 +48,6 @@ func TestITCDRsMySQL(t *testing.T) { } func TestITCDRsPSQL(t *testing.T) { - cfg, err := config.NewCGRConfigFromFolder(path.Join(*dataDir, "conf", "samples", "storage", "postgres")) if err != nil { t.Error(err)