diff --git a/agents/librad_test.go b/agents/librad_test.go index 6f5eb8daa..5e97ed5f1 100644 --- a/agents/librad_test.go +++ b/agents/librad_test.go @@ -45,8 +45,14 @@ ATTRIBUTE Password 2 string # Alias values VALUE Framed-Protocol PPP 1 +VALUE Service-Type Sip-Session 15 # Schulzrinne, acc, auth_radius VALUE Service-Type SIP-Caller-AVPs 30 # Proprietary, avp_radius +VALUE Sip-Method Invite 1 +VALUE Sip-Method Bye 8 +VALUE Acct-Status-Type Start 1 +VALUE Acct-Status-Type Stop 2 + # Vendors VENDOR Cisco 9 VENDOR Microsoft 311 diff --git a/agents/radagent.go b/agents/radagent.go index 98cedb69f..6e6130e18 100644 --- a/agents/radagent.go +++ b/agents/radagent.go @@ -89,10 +89,12 @@ func (ra *RadiusAgent) handleAuth(req *radigo.Packet) (rpl *radigo.Packet, err e } } if err != nil { - utils.Logger.Err(fmt.Sprintf(" request: %s, error: %s", utils.ToJSON(req), err.Error())) + utils.Logger.Err(fmt.Sprintf(" error: <%s> ignoring request: %s, process vars: %+v", + err.Error(), utils.ToJSON(req), procVars)) return nil, nil } else if !processed { - utils.Logger.Err(fmt.Sprintf(" No request processor enabled for request: %s, ignoring request", utils.ToJSON(req))) + utils.Logger.Err(fmt.Sprintf(" No request processor enabled, ignoring request %s, process vars: %+v", + utils.ToJSON(req), procVars)) return nil, nil } return @@ -126,10 +128,12 @@ func (ra *RadiusAgent) handleAcct(req *radigo.Packet) (rpl *radigo.Packet, err e } } if err != nil { - utils.Logger.Err(fmt.Sprintf(" request: %s, error: %s", utils.ToJSON(req), err.Error())) + utils.Logger.Err(fmt.Sprintf(" error: <%s> ignoring request: %s, process vars: %+v", + err.Error(), utils.ToJSON(req), procVars)) return nil, nil } else if !processed { - utils.Logger.Err(fmt.Sprintf(" No request processor enabled for request: %s, ignoring request", utils.ToJSON(req))) + utils.Logger.Err(fmt.Sprintf(" No request processor enabled, ignoring request %s, process vars: %+v", + utils.ToJSON(req), procVars)) return nil, nil } return diff --git a/agents/radagent_it_test.go b/agents/radagent_it_test.go index 67225235a..ed3fcf260 100644 --- a/agents/radagent_it_test.go +++ b/agents/radagent_it_test.go @@ -29,14 +29,15 @@ import ( "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/sessionmanager" "github.com/cgrates/cgrates/utils" "github.com/cgrates/radigo" ) var raCfgPath string var raCfg *config.CGRConfig -var raSMGrpc *rpc.Client var raAuthClnt, raAcctClnt *radigo.Client +var raRPC *rpc.Client func TestRAitInitCfg(t *testing.T) { raCfgPath = path.Join(*dataDir, "conf", "samples", "radagent") @@ -74,7 +75,7 @@ func TestRAitStartEngine(t *testing.T) { // Connect rpc client to rater func TestRAitApierRpcConn(t *testing.T) { var err error - raSMGrpc, err = jsonrpc.Dial("tcp", raCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + raRPC, err = jsonrpc.Dial("tcp", raCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed if err != nil { t.Fatal(err) } @@ -84,7 +85,7 @@ func TestRAitApierRpcConn(t *testing.T) { func TestRAitTPFromFolder(t *testing.T) { attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")} var loadInst utils.LoadInstance - if err := raSMGrpc.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil { + if err := raRPC.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil { t.Error(err) } time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups @@ -104,10 +105,10 @@ func TestRAitAuth(t *testing.T) { if err := authReq.AddAVPWithName("Called-Station-Id", "1002", ""); err != nil { t.Error(err) } - if err := authReq.AddAVPWithName("Acct-Session-Id", "2ca13afce9b2d76e15de7e1ec6568fc8@0:0:0:0:0:0:0:0", ""); err != nil { + if err := authReq.AddAVPWithName("Acct-Session-Id", "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0", ""); err != nil { t.Error(err) } - if err := authReq.AddAVPWithName("Sip-From-Tag", "7f30055f", ""); err != nil { + if err := authReq.AddAVPWithName("Sip-From-Tag", "51585361", ""); err != nil { t.Error(err) } if err := authReq.AddAVPWithName("NAS-IP-Address", "127.0.0.1", ""); err != nil { @@ -131,6 +132,157 @@ func TestRAitAuth(t *testing.T) { } } +func TestRAitAcctStart(t *testing.T) { + if raAcctClnt, err = radigo.NewClient("udp", "127.0.0.1:1813", "CGRateS.org", dictRad, 1, nil); err != nil { + t.Fatal(err) + } + req := raAcctClnt.NewRequest(radigo.AccountingRequest, 2) // emulates Kamailio packet for accounting start + if err := req.AddAVPWithName("Acct-Status-Type", "Start", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Service-Type", "Sip-Session", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Sip-Response-Code", "200", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Sip-Method", "Invite", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Event-Timestamp", "1497106115", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Sip-From-Tag", "51585361", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Sip-To-Tag", "75c2f57b", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Acct-Session-Id", "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("User-Name", "1001", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Called-Station-Id", "1002", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Ascend-User-Acct-Time", "1497106115", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("NAS-Port-Id", "5060", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Acct-Delay-Time", "0", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("NAS-IP-Address", "127.0.0.1", ""); err != nil { + t.Error(err) + } + reply, err := raAcctClnt.SendRequest(req) + if err != nil { + t.Error(err) + } + if reply.Code != radigo.AccountingResponse { + t.Errorf("Received reply: %+v", reply) + } + if len(reply.AVPs) != 0 { // we don't expect AVPs to be populated + t.Errorf("Received AVPs: %+v", reply.AVPs) + } + // Make sure the sessin is managed by SMG + var aSessions []*sessionmanager.ActiveSession + if err := raRPC.Call("SMGenericV1.GetActiveSessions", + map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, utils.ACCID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-51585361-75c2f57b"}, + &aSessions); err != nil { + t.Error(err) + } else if len(aSessions) != 1 { + t.Errorf("Unexpected number of sessions received: %+v", aSessions) + } else if aSessions[0].Usage != time.Duration(10)*time.Second { + t.Errorf("Expecting 10s, received usage: %v", aSessions[0].Usage) + } +} + +func TestRAitAcctStop(t *testing.T) { + req := raAcctClnt.NewRequest(radigo.AccountingRequest, 3) // emulates Kamailio packet for accounting start + if err := req.AddAVPWithName("Acct-Status-Type", "Stop", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Service-Type", "Sip-Session", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Sip-Response-Code", "200", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Sip-Method", "Bye", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Event-Timestamp", "1497106119", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Sip-From-Tag", "51585361", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Sip-To-Tag", "75c2f57b", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Acct-Session-Id", "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("User-Name", "1001", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Called-Station-Id", "1002", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Ascend-User-Acct-Time", "1497106115", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("NAS-Port-Id", "5060", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("Acct-Delay-Time", "0", ""); err != nil { + t.Error(err) + } + if err := req.AddAVPWithName("NAS-IP-Address", "127.0.0.1", ""); err != nil { + t.Error(err) + } + reply, err := raAcctClnt.SendRequest(req) + if err != nil { + t.Error(err) + } + if reply.Code != radigo.AccountingResponse { + t.Errorf("Received reply: %+v", reply) + } + if len(reply.AVPs) != 0 { // we don't expect AVPs to be populated + t.Errorf("Received AVPs: %+v", reply.AVPs) + } + // Make sure the sessin was disconnected from SMG + var aSessions []*sessionmanager.ActiveSession + if err := raRPC.Call("SMGenericV1.GetActiveSessions", + map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, utils.ACCID: "e4921177ab0e3586c37f6a185864b71a@0:0:0:0:0:0:0:0-51585361-75c2f57b"}, + &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } + time.Sleep(time.Duration(10 * time.Millisecond)) + var cdrs []*engine.ExternalCDR + args := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, DestinationPrefixes: []string{"1002"}} + if err := raRPC.Call("ApierV2.GetCdrs", args, &cdrs); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(cdrs) != 1 { + t.Error("Unexpected number of CDRs returned: ", len(cdrs)) + } else { + if cdrs[0].Usage != "4" { + t.Errorf("Unexpected CDR Usage received, cdr: %v %+v ", cdrs[0].Usage, cdrs[0]) + } + if cdrs[0].CostSource != utils.SESSION_MANAGER_SOURCE { + t.Errorf("Unexpected CDR CostSource received for CDR: %v", cdrs[0]) + } + if cdrs[0].Cost != 0.01 { + t.Errorf("Unexpected CDR Cost received for CDR: %v", cdrs[0]) + } + } +} + func TestRAitStopCgrEngine(t *testing.T) { if err := engine.KillEngine(100); err != nil { t.Error(err) diff --git a/data/conf/samples/radagent/cgrates.json b/data/conf/samples/radagent/cgrates.json index 73839a7cd..424d83a05 100644 --- a/data/conf/samples/radagent/cgrates.json +++ b/data/conf/samples/radagent/cgrates.json @@ -49,6 +49,9 @@ "cdrs": { "enabled": true, + "rals_conns": [ + {"address": "*internal"} + ], "cdrstats_conns": [ {"address": "*internal"} ], @@ -112,14 +115,16 @@ "value": "*prepaid", "mandatory": true}, {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Acct-Session-Id;^-;Sip-From-Tag;^-;Sip-To-Tag", "mandatory": true}, + {"tag": "OriginHost", "field_id": "OriginHost", "type": "*composed", + "value": "NAS-IP-Address", "mandatory": true}, {"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}, {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", - "value": "Event-Timestamp", "mandatory": true}, + "value": "Ascend-User-Acct-Time", "mandatory": true}, {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", - "value": "Event-Timestamp", "mandatory": true}, + "value": "Ascend-User-Acct-Time", "mandatory": true}, ], "reply_fields":[], }, @@ -138,10 +143,10 @@ {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Called-Station-Id", "mandatory": true}, {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", - "value": "Event-Timestamp", "mandatory": true}, + "value": "Ascend-User-Acct-Time", "mandatory": true}, {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", - "value": "Event-Timestamp", "mandatory": true}, - {"tag": "Usage", "field_id": "AnswerTime", "type": "*handler", "handler_id": "*usage_difference", + "value": "Ascend-User-Acct-Time", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*usage_difference", "value": "Event-Timestamp;^|;Ascend-User-Acct-Time", "mandatory": true}, ], "reply_fields":[], diff --git a/data/radius/dict/dictionary.kamailio b/data/radius/dict/dictionary.kamailio index a526298c0..131bff30b 100644 --- a/data/radius/dict/dictionary.kamailio +++ b/data/radius/dict/dictionary.kamailio @@ -58,6 +58,11 @@ ATTRIBUTE Sip-Rpid 213 string # Proprietary, auth_radius ATTRIBUTE SIP-AVP 225 string # Proprietary, avp_radius ### Acct-Status-Type Values ### +VALUE Acct-Status-Type Start 1 +VALUE Acct-Status-Type Stop 2 +VALUE Acct-Status-Type Alive 3 +VALUE Acct-Status-Type Accounting-On 7 +VALUE Acct-Status-Type Accounting-Off 8 VALUE Acct-Status-Type Failed 15 # RFC2866, acc ### Service-Type Values ### @@ -67,6 +72,8 @@ 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