diff --git a/data/tutorials/kamevapi/cgrates/etc/cgrates/cgrates.json b/data/tutorials/kamevapi/cgrates/etc/cgrates/cgrates.json index d101aa818..66fa95e56 100644 --- a/data/tutorials/kamevapi/cgrates/etc/cgrates/cgrates.json +++ b/data/tutorials/kamevapi/cgrates/etc/cgrates/cgrates.json @@ -81,7 +81,7 @@ "kamailio_agent": { "enabled": true, "evapi_conns":[ // instantiate connections to multiple Kamailio servers - {"address": "192.168.56.102:8448", "reconnects": 5} + {"address": "127.0.0.1:8448", "reconnects": 5} ], "sessions_conns": [ {"address": "*internal"} // connection towards session service: <*internal> diff --git a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg index 65ac77a90..aec03e9cf 100644 --- a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg +++ b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio-cgrates.cfg @@ -1,10 +1,12 @@ # Kamailio-CGRateS related route blocks + # Called on new connection over evapi, should normally be the case of CGRateS engine event_route[evapi:connection-new] { $sht(cgrconn=>cgr) = $evapi(srcaddr) + ":" + $evapi(srcport); # Detect presence of at least one connection } + # Called when the connection with CGRateS closes event_route[evapi:connection-closed] { $var(connClosed) = $evapi(srcaddr) + ":" + $evapi(srcport); @@ -13,22 +15,37 @@ event_route[evapi:connection-closed] { } } + # Message received from CGRateS, dispatch it to own route event_route[evapi:message-received] { json_get_field("$evapi(msg)", "Event", "$var(Event)"); route($(var(Event){s.rm,"})); # String characters are kept by json_get_field, remove them here } + # Called by Kamailio on new dialog event_route[dialog:start] { route(CGR_CALL_START); } + # Called by Kamailio on dialog end event_route[dialog:end] { route(CGR_CALL_END); } +# Parse the CGRateS attributes from encoded variable into pseudovariables +route[PARSE_CGRATES_ATTRIBUTES] { + # convert encoded attributes into individual Kamailio pseudovariables + $var(idx) = 0; + while !strempty($(var(cgrAttributes){s.select,$var(idx),,})) { + $avp($(var(cgrAttributes){s.select,$var(idx),,}{s.select,0,:})) + = $(var(cgrAttributes){s.select,$var(idx),,}{s.select,1,:}); + $var(idx) = $var(idx) + 1; + } +} + + # CGRateS request for session disconnect route[CGR_SESSION_DISCONNECT] { json_get_field("$evapi(msg)", "HashEntry", "$var(HashEntry)"); @@ -39,8 +56,46 @@ route[CGR_SESSION_DISCONNECT] { } -# Send AUTH_REQUEST to CGRateS -route[CGRATES_AUTH_REQUEST] { +# Route to mainly query account password from CGRateS +route[CGRATES_SIMPLEAUTH_REQUEST] { + if $sht(cgrconn=>cgr) == $null { + sl_send_reply("503","Charging controller unreachable"); + exit; + } + evapi_async_relay("{\"event\":\"CGR_AUTH_REQUEST\", + \"tr_index\":\"$T(id_index)\", + \"tr_label\":\"$T(id_label)\", + \"cgr_subsystems\":\"*attributes\", + \"cgr_context\":\"simpleauth\", + \"reply_route\":\"CGR_SIMPLEAUTH_REPLY\", + \"Account\":\"$fU\"}"); +} + + +# Route receiving simpleauth replies, sanitizes them and dispatch back into Kamailio inside CGRATES_SIMPLEAUTH_REPLY +route[CGR_SIMPLEAUTH_REPLY] { + + json_get_field("$evapi(msg)", "TransactionIndex", "$var(TransactionIndex)"); + $var(TransactionIndex) = $(var(TransactionIndex){s.rm,"}); + $var(id_index) = $(var(TransactionIndex){s.int}); + + json_get_field("$evapi(msg)", "TransactionLabel", "$var(TransactionLabel)"); + $var(TransactionLabel) = $(var(TransactionLabel){s.rm,"}); + $var(id_label) = $(var(TransactionLabel){s.int}); + + json_get_field("$evapi(msg)", "Attributes", "$var(cgrAttributes)"); + $var(cgrAttributes) = $(var(cgrAttributes){s.rm,"}); + + json_get_field("$evapi(msg)", "Error", "$var(cgrError)"); + $var(cgrError) = $(var(cgrError){s.rm,"}); + + t_continue("$var(id_index)", "$var(id_label)", "CGRATES_SIMPLEAUTH_REPLY"); # Unpark the transaction + +} + + +# Request session auth information from CGRateS +route[CGRATES_SESSIONAUTH_REQUEST] { # Auth INVITEs with CGRateS if $sht(cgrconn=>cgr) == $null { sl_send_reply("503","Charging controller unreachable"); @@ -49,16 +104,16 @@ route[CGRATES_AUTH_REQUEST] { evapi_async_relay("{\"event\":\"CGR_AUTH_REQUEST\", \"tr_index\":\"$T(id_index)\", \"tr_label\":\"$T(id_label)\", - \"cgr_subsystems\":\"*attributes;*resources;*accounts;*suppliers;*stats;*thresholds\", - \"RequestType\":\"$dlg_var(cgrReqType)\", - \"Tenant\":\"$dlg_var(cgrTenant)\", - \"Account\":\"$dlg_var(cgrAccount)\", - \"Destination\":\"$dlg_var(cgrDestination)\", + \"cgr_subsystems\":\"*attributes;*accounts\", + \"reply_route\":\"CGR_SESSIONAUTH_REPLY\", + \"Account\":\"$fU\", + \"Destination\":\"$rU\", \"SetupTime\":\"$TS\"}"); } -# Process AUTH_REPLY from CGRateS -route[CGR_AUTH_REPLY] { + +# Process SESSIONAUTH_reply from CGRateS +route[CGR_SESSIONAUTH_REPLY] { json_get_field("$evapi(msg)", "TransactionIndex", "$var(TransactionIndex)"); $var(TransactionIndex) = $(var(TransactionIndex){s.rm,"}); $var(id_index) = $(var(TransactionIndex){s.int}); @@ -82,20 +137,20 @@ route[CGR_AUTH_REPLY] { json_get_field("$evapi(msg)", "Error", "$var(cgrError)"); $var(cgrError) = $(var(cgrError){s.rm,"}); - t_continue("$var(id_index)", "$var(id_label)", "CGRATES_AUTH_REPLY"); # Unpark the transaction + t_continue("$var(id_index)", "$var(id_label)", "CGRATES_SESSIONAUTH_REPLY"); # Unpark the transaction } + # Inform CGRateS about CALL_START (start prepaid sessions loops) route[CGR_CALL_START] { if $sht(cgrconn=>cgr) == $null { xlog("Charging controller unreachable"); exit; } - $var(cgrOriginID) = $dlg(callid)+";"+$dlg(from_tag); evapi_relay("{\"event\":\"CGR_CALL_START\", \"h_entry\":\"$dlg(h_entry)\", \"h_id\":\"$dlg(h_id)\", - \"cgr_subsystems\":\"*attributes;*resources;*accounts;*stats;*thresholds\", + \"cgr_subsystems\":\"*attributes;*accounts\", \"OriginID\":\"$dlg_var(cgrOriginID)\", \"RequestType\":\"$dlg_var(cgrReqType)\", \"Tenant\":\"$dlg_var(cgrTenant)\", @@ -105,7 +160,6 @@ route[CGR_CALL_START] { } - # Inform CGRateS about CALL_END (stop debit loops, perform accounting if desired in this way) route[CGR_CALL_END] { if $sht(cgrconn=>cgr) == $null { @@ -114,7 +168,7 @@ route[CGR_CALL_END] { } $var(callDur) = $TS - $dlg(start_ts); evapi_relay("{\"event\":\"CGR_CALL_END\", - \"cgr_subsystems\":\"*resources;*accounts;*stats;*thresholds\", + \"cgr_subsystems\":\"*accounts\", \"OriginID\":\"$dlg_var(cgrOriginID)\", \"RequestType\":\"$dlg_var(cgrReqType)\", \"Tenant\":\"$dlg_var(cgrTenant)\", @@ -122,4 +176,5 @@ route[CGR_CALL_END] { \"Destination\":\"$dlg_var(cgrDestination)\", \"AnswerTime\":\"$dlg(start_ts)\", \"Usage\":\"$var(callDur)\"}"); -} \ No newline at end of file +} + diff --git a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg index db75768bd..94987a1d3 100644 --- a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg +++ b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg @@ -1,7 +1,7 @@ #!KAMAILIO # Sample demo config for Kamailio-CGRateS communication -# tested against Kamailio 5.0 +# tested against Kamailio 5.1 ####### Defined Values ######### @@ -28,10 +28,6 @@ dns_retr_no=1 dns_servers_no=1 dns_use_search_list=no -listen=udp:eth0:5060 -listen=udp:127.0.0.1:5080 -listen=tcp:127.0.0.1:5060 - ####### Modules Section ######## mpath="/usr/lib/x86_64-linux-gnu/kamailio/modules/" @@ -93,23 +89,12 @@ modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)") modparam("usrloc", "nat_bflag", FLB_NATB) # ----- htable params ----- -modparam("htable", "htable", "users=>size=8;") modparam("htable", "htable", "cgrconn=>size=1;") ####### Routing Logic ######## include_file "kamailio-cgrates.cfg" -event_route[htable:mod-init] { - $sht(users=>1001) = "CGRateS.org"; - $sht(users=>1002) = "CGRateS.org"; - $sht(users=>1003) = "CGRateS.org"; - $sht(users=>1004) = "CGRateS.org"; - $sht(users=>1005) = "CGRateS.org"; - $sht(users=>1006) = "CGRateS.org"; - $sht(users=>1007) = "CGRateS.org"; -} - # Main SIP request routing logic request_route { @@ -140,9 +125,6 @@ request_route { } t_check_trans(); - # authentication - route(AUTH); - # record routing for dialog forming requests (in case they are routed) # - remove preloaded route headers remove_hf("Route"); @@ -157,8 +139,11 @@ request_route { ### requests for my local domains - # handle registrations - route(REGISTRAR); + # SIMPLE AUTH methods + if is_method("REGISTER|SUBSCRIBE|PUBLISH") { + route(CGRATES_SIMPLEAUTH_REQUEST); + exit; + } if ($rU==$null) { # request with no Username in RURI @@ -166,43 +151,65 @@ request_route { exit; } - # user location service - route(LOCATION); if !is_method("INVITE") { - route(RELAY); - } - dlg_manage(); - switch ($fU) { - case 1001: - case 1006: - case 1007: - $dlg_var(cgrReqType) = "*prepaid"; - break; - case 1002: - $dlg_var(cgrReqType) = "*postpaid"; - break; - case 1003: - $dlg_var(cgrReqType) = "*pseudoprepaid"; - break; - default: - $dlg_var(cgrReqType) = "*rated"; - } - $dlg_var(cgrOriginID) = $dlg(callid) + ";" + $dlg(from_tag); - $dlg_var(cgrTenant) = "cgrates.org"; - $dlg_var(cgrAccount) = $fU; - $dlg_var(cgrDestination) = $rU; - route(CGRATES_AUTH_REQUEST); # Will be answered in CGRATES_AUTH_REPLY - #route(RELAY); + sl_send_reply("503", "Unsupported method"); + exit; + } + + route(CGRATES_SESSIONAUTH_REQUEST); exit; } + +# Here will land requests after processing them with CGRateS. +route[CGRATES_SIMPLEAUTH_REPLY] { + if $var(cgrError) != "" { + xlog("CGR_PROFILE_ERROR: $var(cgrError)"); + sl_send_reply("503","CGR_ERROR"); + exit; + } + + # parse the CGRateS attributes + route(PARSE_CGRATES_ATTRIBUTES); + + # password based auth + route(AUTH); + + if is_method("REGISTER") { + route(REGISTRAR); + exit; + } + + # other methods going over location + route(LOCATION); + route(RELAY); +} + + # Here will land requests after processing them with CGRateS. Call RELAY or other routes following this route -route[CGRATES_AUTH_REPLY] { +route[CGRATES_SESSIONAUTH_REPLY] { + if $var(cgrError) != "" { xlog("CGR_AUTH_ERROR: $var(cgrError)"); sl_send_reply("503","CGR_ERROR"); exit; } + + # parse the CGRateS attributes + route(PARSE_CGRATES_ATTRIBUTES); + + # password based auth + route(AUTH); + + # track the dialog so we can set it's timeout and channel variables to store in CDRs + # cannot initialize the dialog sooner due to AUTH + dlg_manage(); + $dlg_var(cgrOriginID) = $dlg(callid) + ";" + $dlg(from_tag); + $dlg_var(cgrTenant) = "cgrates.org"; + $dlg_var(cgrReqType) = $avp(RequestType); + $dlg_var(cgrAccount) = $fU; + $dlg_var(cgrDestination) = $rU; + if $var(cgrMaxUsage) != -1 { if $var(cgrMaxUsage) == 0 { // Not enough balance, do not allow the call to go through sl_send_reply("403","Insufficient credit"); @@ -212,12 +219,20 @@ route[CGRATES_AUTH_REPLY] { exit; } } + if $var(cgrSuppliers) != "" { # Enforce the supplier variable to the first one received from CGRateS, here more for demo purposes $dlg_var(cgrSupplier) = $(var(cgrSuppliers){s.select,0,,}); } + + # user location service + route(LOCATION); + + # send out route(RELAY); + } + # Wrapper for relaying requests route[RELAY] { # enable additional event routes for forwarded requests @@ -293,8 +308,6 @@ route[WITHINDLG] { # Handle SIP registrations route[REGISTRAR] { - if (!is_method("REGISTER")) return; - if(isflagset(FLT_NATS)) { setbflag(FLB_NATB); } @@ -321,15 +334,16 @@ route[LOCATION] { } } + # user uthentication route[AUTH] { if (is_method("REGISTER")) { - if ( strempty($au) || !pv_www_authenticate("$td", "$sht(users=>$au)", "0") ) { + if ( strempty($au) || !pv_www_authenticate("$td", "$avp(Password)", "0") ) { www_challenge("$td", "0"); exit; } } else { # All other methods here - if ( strempty($au) || !pv_proxy_authenticate("$td", "$sht(users=>$au)", "0") ) { + if ( strempty($au) || !pv_proxy_authenticate("$td", "$avp(Password)", "0") ) { proxy_challenge("$td", "0"); exit; } @@ -338,6 +352,7 @@ route[AUTH] { return; } + # Caller NAT detection route[NATDETECT] { force_rport(); @@ -414,3 +429,4 @@ failure_route[MANAGE_FAILURE] { exit; } } +