Compare commits

...

48 Commits
master ... 4.2

Author SHA1 Message Date
  lazedo 8afbef27dd add customization points 7 years ago
  lazedo 377399a927 early handle of subscription timeout 7 years ago
  lazedo bc63cbc849 CVE-2018-14767 7 years ago
  Sergey Safarov efb26d6957 Added block rule for PortSIP VoIP SDK user agents 8 years ago
  Noah Mehl aaa3b61180 Add NAT_UAC_TEST as config option in local.cfg and defs.cfg 8 years ago
  lazedo 77a302fce4 some fixes & optimizations 8 years ago
  lazedo 78e6a85f74 accept token-reg from uri.params and tobody.params 8 years ago
  lazedo cfbeb214b3 add PUSHER_ATTEMPT_REGISTRATION to registrar 8 years ago
  bitbashing 83c224a65e
Merge pull request #84 from 2600hz/hamdle-expired-contacts-4.2 8 years ago
  bitbashing c4505a2ff8
Merge pull request #68 from 2600hz/HELP-36142-4.2 8 years ago
  lazedo 372b4be6f9
handle push failover in kamailio 8 years ago
  lazedo 14e7e34c22
do not send 100 twice 8 years ago
  lazedo 892b8333a4
100 "waking the dead guy" 8 years ago
  lazedo 34d3cf5404
determine ws/wss 8 years ago
  lazedo 787b920b55
set reply route 8 years ago
  lazedo 3708d9403b
test X-KAZOO-INVITE-FORMAT before lookup 8 years ago
  lazedo 9758ebd717 handle expired contacts 8 years ago
  lazedo b20f044d5f
handle variations of push notification 8 years ago
  lazedo 1ffd76677e
do not process pusher in default 8 years ago
  Edwin Fine 60f597e101 Add "LimitMEMLOCK=infinity" to enable locking of memory pages 8 years ago
  Edwin Fine 747d92ecd5 Add (int) cast to remove log warning 8 years ago
  lazedo d789548f26
freeswitch 1.6.20 has trouble finding params 8 years ago
  lazedo 80013a9def
fix cfg_get.kazoo.fast_pickup_cookies typo 8 years ago
  bitbashing faa6f5b5ed
Merge pull request #79 from 2600hz/fix-ds-group-4.2 8 years ago
  lazedo 9033d6605d fix ds_group assignment & reorder 8 years ago
  bitbashing 002a690a58
Merge pull request #77 from 2600hz/parking-4.2 8 years ago
  Luis Azedo 9cd8043f35 handle park redirects without replaces header from presence info 8 years ago
  bitbashing 23d0c8b521
Merge pull request #75 from 2600hz/registrar-port-4.2 8 years ago
  Luis Azedo ea11537e7c change default example & typo 8 years ago
  Luis Azedo 8fb537bc19 revert mhomed 8 years ago
  Luis Azedo 9dd7309db6 handle registration port redirect per protocol 8 years ago
  Luis Azedo 1cb714d1c3 registrar port without KZ_MULTI_HOMED 8 years ago
  Luis Azedo c99a6effc5 support single port for registrar success messages 8 years ago
  lazedo baeedd4526 allow configuration of registrar contact size 8 years ago
  James Aimonetti 72c93863bb fix hearbeat to heartbeat 8 years ago
  lazedo 18e14b1392
pusher can send kazoo invite format 8 years ago
  bitbashing fb4e942fa3
Merge pull request #65 from 2600hz/expires-4.2 8 years ago
  lazedo 56e1ac9e32
revert to 4.1 settings 8 years ago
  lazedo deeef6128c
@hf_value_exists not working as expected 8 years ago
  bitbashing c39a6e4efa
Merge pull request #59 from 2600hz/nodes-patch-42 8 years ago
  lazedo 3727a1c494
nodes timers, publish, log 8 years ago
  karl anderson 944dfd7eb5 Merge remote-tracking branch 'origin/master' into 4.2 8 years ago
  karl anderson 85921cec28 Merge remote-tracking branch 'origin/master' into 4.2 8 years ago
  karl anderson 826f447ad4 Merge remote-tracking branch 'origin/master' into 4.2 8 years ago
  karl anderson f41996823e Merge remote-tracking branch 'origin/master' into 4.2 8 years ago
  karl anderson 1b31e7d833 Merge remote-tracking branch 'origin/master' into 4.2 8 years ago
  karl anderson aeda9f51b8 Merge remote-tracking branch 'origin/master' into 4.2 8 years ago
  lazedo 00221154c7
fix syntax 8 years ago
11 changed files with 440 additions and 152 deletions
Split View
  1. +13
    -16
      kamailio/default.cfg
  2. +15
    -0
      kamailio/defs.cfg
  3. +9
    -11
      kamailio/dispatcher-role.cfg
  4. +55
    -57
      kamailio/fast-pickup-role.cfg
  5. +32
    -0
      kamailio/local.cfg
  6. +1
    -1
      kamailio/nat-traversal-role.cfg
  7. +18
    -12
      kamailio/nodes-role.cfg
  8. +4
    -3
      kamailio/presence_notify_sync-role.cfg
  9. +159
    -23
      kamailio/pusher-role.cfg
  10. +133
    -29
      kamailio/registrar-role.cfg
  11. +1
    -0
      system/systemd/kazoo-kamailio.service

+ 13
- 16
kamailio/default.cfg View File

@ -324,6 +324,8 @@ route
route(HANDLE_OPTIONS);
route_if_exists("CUSTOM_START_ROUTES");
route(HANDLE_NOTIFY);
#!ifdef AUTHORIZATION_ROLE
@ -378,6 +380,12 @@ route[CHECK_RETRANS]
route[SANITY_CHECK]
{
## CVE-2018-14767
if($(hdr(To)[1]) != $null) {
xlog("second To header not null - dropping message");
drop;
}
if (!sanity_check()) {
xlog("L_WARN", "$ci|end|message from $si:$sp is insane\n");
exit;
@ -393,6 +401,7 @@ route[SANITY_CHECK]
$ua == "sundayddr" ||
$ua == "pplsip" ||
$ua =~ "NiceGuy" ||
$ua =~ "PortSIP" ||
$ua =~ "sipcli" ) {
xlog("L_WARN", "$ci|end|dropping message with user-agent $ua from $si:$sp\n");
exit;
@ -647,22 +656,16 @@ route[INTERNAL_TO_EXTERNAL_RELAY]
xlog("L_INFO", "$ci|end|routing to contact $ru\n");
} else {
xlog("L_INFO", "$ci|end|lookup for AOR $hdr(X-KAZOO-AOR) failed\n");
sl_send_reply("404", "Not registered");
sl_send_reply("410", "Not registered");
exit;
}
} else if (reg_fetch_contacts("location", "$hdr(X-KAZOO-AOR)", "callee")) {
$du = $(ulc(callee=>received));
$fs = $(ulc(callee=>socket));
xlog("L_INFO", "$ci|log|routing $hdr(X-KAZOO-AOR) to $du via $fs\n");
} else if ($hdr(X-KAZOO-PUSHER-Token-ID) != $null) {
xlog("L_INFO", "$ci|log|ignoring missing registration while waiting for push notification response\n");
t_on_reply("EXTERNAL_REPLY");
t_set_fr(0, 10000);
t_release();
exit;
} else {
xlog("L_INFO", "$ci|end|user is not registered\n");
sl_send_reply("404", "Not registered");
sl_send_reply("410", "Not registered");
exit;
}
}
@ -692,10 +695,6 @@ route[EXTERNAL_TO_INTERNAL_RELAY]
}
#!endif
#!ifdef FAST_PICKUP_ROLE
route(FAST_PICKUP_REFER);
#!endif
remove_hf_re("^X-.*");
append_hf("X-AUTH-IP: $si\r\n");
append_hf("X-AUTH-PORT: $sp\r\n");
@ -761,10 +760,6 @@ onreply_route[INTERNAL_REPLY]
route(DOS_PREVENTION);
#!endif
#!ifdef FAST_PICKUP_ROLE
route(FAST_PICKUP_REPLY);
#!endif
if (is_method("INVITE") &&
!isflagset(FLAG_SESSION_PROGRESS) &&
t_check_status("(180)|(183)|(200)")
@ -926,4 +921,6 @@ event_route[tm:local-request]
}
#!endif
#!import_file "custom-routes.cfg"
# vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab

+ 15
- 0
kamailio/defs.cfg View File

@ -99,4 +99,19 @@
#!define KZ_PRESENCE_REQUEST_PROBE 1
#!endif
#!ifndef NAT_UAC_TEST_LEVEL
#!substdef "!NAT_UAC_TEST_LEVEL!3!g"
#!endif
#!ifndef KZ_DISABLE_WEBSOCKETS_REGISTRAR_PORT
#!trydef KZ_WEBSOCKETS_REGISTRAR_PORT 7000
#!endif
#!ifndef KZ_DISABLE_TLS_REGISTRAR_PORT
#!trydef KZ_TLS_REGISTRAR_PORT 7000
#!endif
#!trydef KZ_FAST_PICKUP_COOKIES 1
#!trydef KZ_FAST_PICKUP_REALTIME 1
# vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab

+ 9
- 11
kamailio/dispatcher-role.cfg View File

@ -85,18 +85,16 @@ route[DISPATCHER_FIND_ROUTES]
#!import_file "dispatcher-network-find.cfg"
$var(ds_group) = $var(ds_primary_group);
if (!ds_select_dst("$var(ds_primary_group)", "0") || $(avp(ds_dst)[0]) == $null) {
# if we selected from primary group, try again in backup group
if ($var(ds_primary_group) == 1) {
if (!ds_select_dst("$var(ds_backup_group)", "0")) {
xlog("L_WARN", "$ci|end|no servers available in primary or backup group\n");
sl_send_reply("480", "All servers busy");
exit;
}
# we selected from primary group, try again in backup group
if (!ds_select_dst("$var(ds_backup_group)", "0") || $(avp(ds_dst)[0]) == $null) {
xlog("L_WARN", "$ci|end|no servers available in primary or backup group\n");
sl_send_reply("480", "All servers busy");
exit;
} else {
xlog("L_INFO", "$ci|end|no servers available in group $var(ds_primary_group)\n");
sl_send_reply("480", "All servers busy");
exit;
$var(ds_group) = $var(ds_backup_group);
}
}
@ -132,7 +130,7 @@ route[DISPATCHER_REORDER_ROUTES]
$var(i) = $var(i) + 1;
}
if (!$var(found) && $var(ds_group) == 1 && ds_select_dst("2", "0")) {
if (!$var(found) && $var(ds_group) == $var(ds_primary_group) && ds_select_dst("$var(ds_backup_group)", "0")) {
$var(i) = 0;
while($(avp(ds_dst)[$var(i)]) != $null) {
if($(avp(ds_dst)[$var(i)]) == $var(prefered_route)) {


+ 55
- 57
kamailio/fast-pickup-role.cfg View File

@ -2,6 +2,9 @@
modparam("htable", "htable", "park=>size=16;autoexpire=600")
modparam("htable", "htable", "fp=>size=8");
kazoo.fast_pickup_cookies = KZ_FAST_PICKUP_COOKIES descr "maintains a hash table for correlating call-ids with media servers"
kazoo.fast_pickup_realtime = KZ_FAST_PICKUP_REALTIME descr "queries channels api for realtime status of call-id"
route[FAST_PICKUP_START]
{
$sht(fp=>count) = 0;
@ -66,66 +69,46 @@ route[FAST_PICKUP_ATTEMPT]
}
}
if($var(replaced_call_id) != "none") {
xlog("L_INFO", "$ci|log|request has replaces call-id $var(replaced_call_id)\n");
$var(amqp_payload_request) = '{"Event-Category" : "call_event" , "Event-Name" : "channel_status_req", "Call-ID" : "' + $var(replaced_call_id) + '", "Active-Only" : true }';
$var(amqp_routing_key) = "call.status_req." + $(var(replaced_call_id){kz.encode});
sl_send_reply("100", "locating your call");
xlog("L_INFO", "$ci|log|querying cluster for the location of call-id $var(replaced_call_id)\n");
if(kazoo_query("callevt", $var(amqp_routing_key), $var(amqp_payload_request))) {
$du = $(kzR{kz.json,Switch-URL});
if($du != $null) {
if($(ru{uri.user}) =~ "\*") {
remove_hf_re("^Replaces");
append_hf("Replaces: $var(replaced_call_id);a-leg=true\r\n");
}
xlog("L_INFO", "$ci|log|call-id $var(replaced_call_id) found redirecting call ($(ru{uri.user})) to $du\n");
route(EXTERNAL_TO_INTERNAL_RELAY);
exit();
} else {
xlog("L_WARN", "$ci|log|call-id $var(replaced_call_id) not found in cluster, proceeding with normal dispatch\n");
remove_hf_re("^Replaces");
}
} else {
remove_hf_re("^Replaces");
if(@cfg_get.kazoo.fast_pickup_realtime == 1) {
if($var(replaced_call_id) != "none") {
xlog("L_INFO", "$ci|log|request has replaces call-id $var(replaced_call_id)\n");
$var(amqp_payload_request) = '{"Event-Category" : "call_event" , "Event-Name" : "channel_status_req", "Call-ID" : "' + $var(replaced_call_id) + '", "Active-Only" : true }';
$var(amqp_routing_key) = "call.status_req." + $(var(replaced_call_id){kz.encode});
sl_send_reply("100", "locating your call");
xlog("L_INFO", "$ci|log|querying cluster for the location of call-id $var(replaced_call_id)\n");
if(kazoo_query("callevt", $var(amqp_routing_key), $var(amqp_payload_request))) {
$du = $(kzR{kz.json,Switch-URL});
if($du != $null) {
if($(kzR{kz.json,Other-Leg-Call-ID}) == "") {
## not bridged
$var(rep) = $_s($var(replaced_call_id);a-leg=true);
} else {
## ensure early-only=true
$var(rep) = $_s($var(replaced_call_id);early-only=true);
}
remove_hf_re("^Replaces");
append_hf("Replaces: $var(rep)\r\n");
xlog("L_INFO", "$ci|log|call-id $var(replaced_call_id) found, redirecting call ($(ru{uri.user})) to $du => $var(rep)\n");
route(EXTERNAL_TO_INTERNAL_RELAY);
exit();
} else {
xlog("L_WARN", "$ci|log|call-id $var(replaced_call_id) not found in cluster, proceeding with normal dispatch\n");
}
} else {
xlog("L_WARN", "$ci|log|error querying cluster for call-id $var(replaced_call_id), proceeding with normal dispatch\n");
}
}
}
##### CALL-PARK ####
if($(ru{uri.user}) =~ "\*" && $sht(park=>$(ru{uri.user})@$(ru{uri.domain})) != $null) {
if($sht(park=>$(ru{uri.user})@$(ru{uri.domain})) != $null) {
$du = $sht(park=>$(ru{uri.user})@$(ruri{uri.domain}));
xlog("L_INFO", "$ci|log|redirecting park request to $du\n");
if ($hdr(Proxy-Authorization) != $null) {
xlog("L_INFO", "$ci|log|removed park redirect\n");
$sht(park=>$(ru{uri.user})@$(ruri{uri.domain})) = $null;
}
route(EXTERNAL_TO_INTERNAL_RELAY);
exit();
}
}
route[FAST_PICKUP_REFER]
{
if(!is_method("REFER")) {
return;
}
$avp(refer_to) = $hdr(Refer-To);
$avp(referred_by) = $hdr(Referred-By);
$avp(refer_to_uri) = $rt;
}
route[FAST_PICKUP_REPLY] {
if (!is_method("REFER") || !t_check_status("(200)|(202)") ) {
return;
}
$var(contact) = "sip:" + $(ct{tobody.uri}{uri.host}) + ":" + $(ct{tobody.uri}{uri.port});
xlog("L_INFO", "$ci|log|caching park info $(avp(refer_to_uri){uri.user})@$(avp(refer_to_uri){uri.domain}) = $var(contact)\n");
$sht(park=>$(avp(refer_to_uri){uri.user})@$(avp(refer_to_uri){uri.domain})) = $var(contact);
}
route[FAST_PICKUP_OPTION]
{
$var(Pickup) = "";
@ -145,16 +128,31 @@ route[FAST_PICKUP_OPTION]
route[FAST_PICKUP_INIT]
{
$var(AppName) = $(kzE{kz.json,App-Name});
## park redirects without replaces header
if($var(AppName) == "park") {
$var(Pickup) = 1; #";a-leg=true";
} else {
$var(Pickup) = 2; #";early-only=true";
if($(kzE{kz.json,State}) == "terminated") {
$sht(park=>$(kzE{kz.json,Presence-ID})) = $null;
} else {
$sht(park=>$(kzE{kz.json,Presence-ID})) = $(kzE{kz.json,Switch-URI});
}
}
## fast pickup with cookies
if(@cfg_get.kazoo.fast_pickup_cookies == 1) {
if($var(AppName) == "park") {
$var(Pickup) = 1; #";a-leg=true";
} else {
$var(Pickup) = 2; #";early-only=true";
}
$var(Option) = $(var(Pickup){s.encode.hexa});
$var(Cookie) = $(kzE{kz.json,Switch-URI}{s.md5});
$var(call_id) = $(kzE{kz.json,Call-ID});
$var(JObj) = $(kzE{re.subst,/"Call-ID"\s*\:\s*"([^"]*)"/"Call-ID" : "kfp+$var(Option)$var(Cookie)@\1"/});
xlog("L_INFO", "$var(call_id)|fast|shortcut ($var(Pickup)) kfp+$var(Option)$var(Cookie)@$var(call_id)\n");
}
$var(Option) = $(var(Pickup){s.encode.hexa});
$var(Cookie) = $(kzE{kz.json,Switch-URI}{s.md5});
$var(call_id) = $(kzE{kz.json,Call-ID});
$var(JObj) = $(kzE{re.subst,/"Call-ID"\s*\:\s*"([^\s"]*)"/"Call-ID" : "kfp+$var(Option)$var(Cookie)@\1"/});
xlog("L_DEBUG", "$ci|init|fast|created shortcut kfp+$var(Option)$var(Cookie)@ for call-id $var(call_id)\n");
}
# vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab


+ 32
- 0
kamailio/local.cfg View File

@ -82,6 +82,38 @@
## destination. This reduces performance.
mhomed=0
################################################################################
## KZ_DISABLE_REGISTRAR_PORTS
################################################################################
## This parameter is OPTIONAL.
## It will disable publishing single proxy port on registrar success messages
## By default single port proxy is send on registrar success messages
## for websockets/tls protocols. the default port is 7000
## which enables config less in freeswitch for handling webrtc/tls
## ie, no certs in freeswitch, no webrtc/tls listeners on freeswitch
## by disabling single port proxy, ecallmgr needs to be started with
## 'use_transport_for_fs_path' set to true in its environment configuration
##
###!define KZ_DISABLE_WEBSOCKETS_REGISTRAR_PORT 1
###!define KZ_DISABLE_TLS_REGISTRAR_PORT 1
##
## you can also change the ports used for single port redirect
##!define KZ_WEBSOCKETS_REGISTRAR_PORT 5060
##!define KZ_TLS_REGISTRAR_PORT 5060
##
## also available for udp/tcp if it works for you
##!define KZ_UDP_REGISTRAR_PORT 7000
##!define KZ_TCP_REGISTRAR_PORT 7000
################################################################################
## NAT
################################################################################
## These parameters are OPTIONAL.
## It allows overriding the nat_uac_test with a different value than "3"
## (default) as it is proven that it fixes the issue reported here:
## https://lists.kamailio.org/pipermail/sr-users/2016-August/094211.html
# # #!substdef "!NAT_UAC_TEST_LEVEL!1!g"
## YOU SHOULD NOT HAVE TO CHANGE THESE!
## By setting MY_IP_ADDRESS above these will resolve
## to the proper bindings. These are here


+ 1
- 1
kamailio/nat-traversal-role.cfg View File

@ -32,7 +32,7 @@ route[NAT_TEST_AND_CORRECT]
return();
}
if (nat_uac_test("3")) {
if (nat_uac_test("NAT_UAC_TEST_LEVEL")) {
force_rport();
fix_nated_contact();
}


+ 18
- 12
kamailio/nodes-role.cfg View File

@ -8,18 +8,20 @@
#!define NODES_FUDGE_FACTOR 10
#!endif
modparam("htable", "htable", "nodes=>size=8;initval=0;autoexpire=180;");
modparam("htable", "htable", "media=>size=8;initval=0;autoexpire=180;");
modparam("htable", "htable", "nodes=>size=8;initval=0;autoexpire=60");
modparam("htable", "htable", "media=>size=8;initval=0;autoexpire=60");
####### RTIMER module ##########
#!ifndef RTIMER_LOADED
loadmodule "rtimer.so"
#!trydef RTIMER_LOADED
#!endif
modparam("rtimer", "timer", "name=ta;interval=5;mode=2;")
modparam("rtimer", "exec", "timer=ta;route=NODES_ADVERTISE_ROUTE")
modparam("rtimer", "exec", "timer=ta;route=NODE_TRACK_ROUTE")
modparam("rtimer", "timer", "name=ta;interval=2;mode=2;")
modparam("rtimer", "timer", "name=retry;interval=5;mode=2;")
modparam("rtimer", "timer", "name=pub;interval=10;mode=1;")
modparam("rtimer", "exec", "timer=ta;route=NODE_HEARTBEAT_ROUTE")
modparam("rtimer", "exec", "timer=retry;route=NODE_TRACK_ROUTE")
modparam("rtimer", "exec", "timer=pub;route=NODES_ADVERTISE_ROUTE")
modparam("mqueue","mqueue", "name=node_track")
@ -59,7 +61,7 @@ route(LISTENER_STATUS);
#!endif
$var(Roles) = $_s("Roles" : {"Proxy" : $var(listeners) $var(Dispatcher) $var(Presence) $var(Registrar)});
$var(Payload) = '{"Event-Category" : "nodes", "Event-Name" : "advertise", "Expires" : 5000, "Used-Memory" : $(stat(real_used_size){s.int}), "Startup" : $Tb, "WhApps" : {"kamailio" : {"Startup" : $Tb }}, $var(Roles)}';
$var(Payload) = '{"Event-Category" : "nodes", "Event-Name" : "advertise", "Expires" : 15000, "Used-Memory" : $(stat(real_used_size){s.int}), "Startup" : $Tb, "WhApps" : {"kamailio" : {"Startup" : $Tb }}, $var(Roles)}';
kazoo_publish("nodes", "", $var(Payload));
}
@ -68,11 +70,13 @@ event_route[kazoo:consumer-event-nodes-advertise]
{
$var(count) = $shtinc(nodes=>$(kzE{kz.json,Node})::count);
if($var(count) == 0) {
xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|nodes|hearbeat for reconnected node $(kzE{kz.json,Node})\n");
xlog("L_WARNING", "$(kzE{kz.json,Msg-ID})|nodes|heartbeat for reconnected node $(kzE{kz.json,Node})\n");
$var(count) = $shtinc(nodes=>$(kzE{kz.json,Node})::count);
} else {
if($var(count) == 1) {
xlog("L_INFO", "$(kzE{kz.json,Msg-ID})|nodes|hearbeat from new node $(kzE{kz.json,Node})\n");
xlog("L_WARNING", "$(kzE{kz.json,Msg-ID})|nodes|heartbeat from new node $(kzE{kz.json,Node})\n");
} else {
xlog("L_DEBUG", "$(kzE{kz.json,Msg-ID})|nodes|heartbeat from existing node $(kzE{kz.json,Node})\n");
}
}
mq_add("node_heartbeat", "$(kzE{kz.json,Node})", "$kzE");
@ -82,13 +86,13 @@ event_route[htable:expired:nodes]
{
if($shtrecord(key) =~ "::count$$") {
if($shtrecord(value) == -1) {
xlog("L_INFO", "htable|nodes|node $(shtrecord(key){s.rm,::count}) is still unreachable\n");
xlog("L_WARNING", "htable|nodes|node $(shtrecord(key){s.rm,::count}) is still unreachable\n");
}
mq_add("node_track", "$shtrecord(key)", "");
return;
}
xlog("L_INFO", "htable|nodes|hearbeat expired for node $shtrecord(key)\n");
xlog("L_WARNING", "htable|nodes|heartbeat expired for node $shtrecord(key)\n");
}
route[NODE_TRACK_ROUTE]
@ -108,6 +112,7 @@ route[NODE_HEARTBEAT_ROUTE]
while(mq_fetch("node_heartbeat") == 1 && $var(runloop) < MAX_WHILE_LOOPS) {
$var(Node) = $mqk(node_heartbeat);
$var(Payload) = $mqv(node_heartbeat);
xlog("L_DEBUG", "$(var(Payload){kz.json,Msg-ID})|nodes|processing heartbeat for node $var(Node)\n");
route(CHECK_MEDIA_SERVERS);
@ -149,6 +154,7 @@ route[CHECK_MEDIA_SERVERS]
route(MEDIA_SERVER_UP);
};
$var(MediaExpire) = ($(var(Payload){kz.json,Expires}{s.int}) / 1000) + NODES_FUDGE_FACTOR;
xlog("L_DEBUG", "nodes|media|$var(Node) media expiration $var(MediaExpire) for $var(MediaUrl)\n");
$shtex(media=>$var(MediaUrl)::count) = $var(MediaExpire);
$var(ProfileIdx) = $var(ProfileIdx) + 1;
}
@ -167,7 +173,7 @@ event_route[htable:expired:media]
route[MEDIA_SERVER_UP]
{
xlog("L_INFO", "nodes|media|$var(Node) reported new media server $var(MediaUrl) in zone $var(Zone)\n");
xlog("L_WARNING", "nodes|media|$var(Node) reported new media server $var(MediaUrl) in zone $var(Zone)\n");
#!ifdef DISPATCHER_ROLE
route(DISPATCHER_CHECK_MEDIA_SERVER);
@ -181,7 +187,7 @@ route[MEDIA_SERVER_UP]
route[MEDIA_SERVER_DOWN]
{
xlog("L_INFO", "htable|media|hearbeat expired for media server $var(MediaUrl) in zone $var(Zone)\n");
xlog("L_WARNING", "htable|media|heartbeat expired for media server $var(MediaUrl) in zone $var(Zone)\n");
#!ifdef PRESENCE_ROLE
route(RESET_PUBLISHER);


+ 4
- 3
kamailio/presence_notify_sync-role.cfg View File

@ -51,12 +51,13 @@ event_route[presence:notify-reply]
$xavp(pres=>delete_subscription) = 0;
if($notify_reply($rs) == 200) {
if($subs(reason) == "timeout") {
$xavp(pres=>delete_subscription) = 1;
xlog("L_INFO", "$ci|end|deleting subscribtion $subs(pres_uri) for $subs(watcher_username)@$subs(watcher_domain) due to timeout\n");
} else if($notify_reply($rs) == 200) {
$sht(notify=>$ci) = $null;
$sht(notify=>$ci::count) = 0;
xlog("L_INFO", "$ci|end|notified $subs(watcher_username)@$subs(watcher_domain) on behalf of $subs(pres_uri)\n");
} else if($notify_reply($rs) == 481 && $subs(reason) == "timeout") {
xlog("L_INFO","$ci|end|sent subscription $hdr(Subscription-State)\n");
} else {
xlog("L_ERROR", "$ci|error|received $notify_reply($rs) when notifying $subs(watcher_username)@$subs(watcher_domain) on behalf of $subs(pres_uri)\n");
if($rP != "UDP") {


+ 159
- 23
kamailio/pusher-role.cfg View File

@ -1,50 +1,186 @@
## PUSHER ROLE
####### SQL OPS module ##########
#!ifndef TSILO_LOADED
loadmodule "tsilo.so"
modparam("tsilo", "use_domain", 1)
#!trydef TSILO_LOADED
#!endif
######## Generic Hash Table container in shared memory ########
modparam("htable", "htable", "push_cache=>autoexpire=60;")
modparam("tm", "failure_exec_mode", 1)
route[PUSHER_ROUTE]
{
if ( (!is_method("INVITE")) || (!isflagset(FLAG_INTERNALLY_SOURCED)) || $hdr(X-KAZOO-PUSHER-Token-ID) == $null)
return;
if(registered("location") && $hdr(X-KAZOO-PUSHER-Invite-Format) != "push_and_invite") {
return;
xlog("L_INFO", "$ci|pusher|start deliver call to $hdr(X-KAZOO-PUSHER-Token-ID)\n");
if(route(PUSHER_PREPARE_PUSH)) {
if(reg_fetch_contacts("location", "$hdr(X-KAZOO-AOR)", "callee")) {
$du = $(ulc(callee=>received));
$fs = $(ulc(callee=>socket));
xlog("L_INFO", "$ci|pusher|routing $hdr(X-KAZOO-AOR) to contact $du\n");
send_reply(100, "calling a push device");
route(PUSHER_TO_EXTERNAL_RELAY);
} else {
send_reply(100, "waking the push device");
route(PUSHER_SEND_PUSH_NOTIFICATION);
}
}
exit();
}
xlog("L_INFO", "$ci| pusher received request to wakeup $tu\n");
sl_send_reply(180, "waking the dead guy");
route[PUSHER_TO_EXTERNAL_RELAY]
{
remove_hf_re("^X-.*");
t_set_fr(0, 2000);
t_relay();
}
failure_route[PUSHER_EXTERNAL_FAULT]
{
if (!t_check_status("486|487|603") && $avp(push_sent) != 1) {
send_reply(182, "waking the dead guy");
route(PUSHER_SEND_PUSH_NOTIFICATION);
} else if (t_check_status("487")) {
xlog("L_INFO", "$ci|pusher|push transaction canceled\n");
t_reply("$T_reply_code", "Pusher Canceled");
} else {
xlog("L_INFO", "$ci|pusher|push transaction result - $T_reply_code\n");
t_reply("$T_reply_code", "Pusher Failed");
}
}
route[PUSHER_PREPARE_PUSH]
{
if (t_newtran()) {
route(SEND_PUSH_NOTIFICATION);
t_set_fr(20000, 20000);
route(PUSHER_PREPARE_PUSH_PAYLOAD);
remove_hf_re("^X-.*");
t_on_reply("EXTERNAL_REPLY");
t_on_failure("PUSHER_EXTERNAL_FAULT");
t_save_lumps();
ts_store("$(tu{s.tolower})");
} else {
t_set_auto_inv_100(0);
return 1;
} else {
sl_send_reply(500, "error creating transaction for waking the dead guy");
return 0;
}
}
route[SEND_PUSH_NOTIFICATION]
route[PUSHER_PREPARE_PUSH_PAYLOAD]
{
$var(TokenID) = $hdr(X-KAZOO-PUSHER-Token-ID);
$var(TokenType) = $hdr(X-KAZOO-PUSHER-Token-Type);
$var(TokenApp) = $hdr(X-KAZOO-PUSHER-Token-App);
$var(rp) = $hdr(Remote-Party-ID);
$var(from) = $(var(rp){tobody.user}) + " - " + $(var(rp){tobody.display}{s.escape.common});
$var(Payload) = '{ "Event-Category" : "notification", "Event-Name" : "push_req", "Call-ID" : "$ci", "Token-ID" : "$var(TokenID)", "Token-Type" : "$var(TokenType)", "Token-App" : "$var(TokenApp)", "Alert-Body" : "$var(from)" }';
$var(RoutingKey) = "notification.push." + $var(TokenType) + "." + $var(TokenID);
$var(exchange) = "pushes";
kazoo_publish($var(exchange), $var(RoutingKey), $var(Payload));
$var(TokenProxy) = $hdr(X-KAZOO-PUSHER-Token-Proxy);
### token for fast reg ###
$var(TokenReg) = $uuid(g);
$sht(push_cache=>$var(TokenReg)) = 1;
### caller-id ###
if($hdr(Remote-Party-ID) != $null) {
$var(from_user) = $(hdr(Remote-Party-ID){tobody.user});
$var(from_name) = $(hdr(Remote-Party-ID){tobody.display}{re.subst,/"//g});
} else if($hdr(P-Asserted-Identity) != $null) {
$var(from_user) = $(hdr(P-Asserted-Identity){tobody.user});
$var(from_name) = $(hdr(P-Asserted-Identity){tobody.display}{re.subst,/"//g});
} else if($hdr(P-Preferred-Identity) != $null) {
$var(from_user) = $(hdr(P-Preferred-Identity){tobody.user});
$var(from_name) = $(hdr(P-Preferred-Identity){tobody.display}{re.subst,/"//g});
} else {
$var(from_user) = $(hdr(From){tobody.user});
$var(from_name) = $(hdr(From){tobody.display}{re.subst,/"//g});
}
$var(from) = $_s($var(from_user) - $var(from_name));
$var(PushPayload) = $_s({"call-id" : "$ci", "proxy" : "$var(TokenProxy)", "caller-id-number" : "$var(from_user)", "caller-id-name" : "$var(from_name)", "registration-token" : "$var(TokenReg)"});
$var(Payload) = $_s({ "Event-Category" : "notification", "Event-Name" : "push_req", "Call-ID" : "$ci", "Token-ID" : "$var(TokenID)", "Token-Type" : "$var(TokenType)", "Token-App" : "$var(TokenApp)", "Alert-Key" : "IC_SIL", "Alert-Params" : ["$var(from)"], "Sound" : "ring.caf", "Payload" : $var(PushPayload) });
$avp(push_routing_key) = "notification.push." + $var(TokenType) + "." + $var(TokenID);
$avp(push_payload) = $var(Payload);
}
route[PUSHER_SEND_PUSH_NOTIFICATION]
{
xlog("L_INFO", "$ci|pusher|sending push notification request\n");
xlog("L_DEBUG", "$ci|pusher|pushing to $avp(push_routing_key) : $avp(push_payload)\n");
t_set_fr(20000, 20000);
$avp(push_sent) = 1;
t_suspend();
$sht(push_cache=>$(tu{s.tolower})) = $_s(a=0;index=$T(id_index);label=$T(id_label));
kazoo_publish("pushes", $avp(push_routing_key), $avp(push_payload));
}
route[PUSHER_ATTEMPT_REGISTRATION]
{
if (!is_method("REGISTER")) {
return;
}
if($hdr(X-Token-Reg) != $null) {
if($sht(push_cache=>$hdr(X-Token-Reg)) != $null) {
$var(password) = $null;
$sht(push_cache=>$hdr(X-Token-Reg)) = $null;
xlog("L_INFO", "$ci|pusher|registration with x-token-reg $hdr(X-Token-Reg)\n");
$xavp(ulattrs=>custom_channel_vars) = "{}";
$xavp(ulattrs[0]=>x_token_reg) = $hdr(X-Token-Reg);
route(SAVE_LOCATION);
} else {
xlog("L_INFO", "$ci|pusher|registration x-token-reg '$hdr(X-Token-Reg)' from header was not found\n");
}
}
if($(sel(contact.uri){uri.param,x-token-reg}) != "") {
if($sht(push_cache=>$(sel(contact.uri){uri.param,x-token-reg})) != $null) {
$var(password) = $null;
$sht(push_cache=>$(sel(contact.uri){uri.param,x-token-reg})) = $null;
xlog("L_INFO", "$ci|pusher|registration with x-token-reg $(sel(contact.uri){uri.param,x-token-reg})\n");
$xavp(ulattrs=>custom_channel_vars) = "{}";
$xavp(ulattrs[0]=>x_token_reg) = $(sel(contact.uri){uri.param,x-token-reg});
route(SAVE_LOCATION);
} else {
xlog("L_INFO", "$ci|pusher|registration x-token-reg from contact uri param '$(sel(contact.uri){uri.param,x-token-reg})' was not found\n");
}
}
if($(sel(contact){tobody.params}{param.value,x-token-reg}) != "") {
if($sht(push_cache=>$(sel(contact){tobody.params}{param.value,x-token-reg})) != $null) {
$var(password) = $null;
$sht(push_cache=>$(sel(contact){tobody.params}{param.value,x-token-reg})) = $null;
xlog("L_INFO", "$ci|pusher|registration with x-token-reg $(sel(contact){tobody.params}{param.value,x-token-reg})\n");
$xavp(ulattrs=>custom_channel_vars) = "{}";
$xavp(ulattrs[0]=>x_token_reg) = $(sel(contact){tobody.params}{param.value,x-token-reg});
route(SAVE_LOCATION);
} else {
xlog("L_INFO", "$ci|pusher|registration x-token-reg from contact param '$(sel(contact){tobody.params}{param.value,x-token-reg})' was not found\n");
}
}
}
route[PUSHER_ON_REGISTRATION]
{
if($(xavp(ulattrs=>custom_channel_vars){kz.json,Pusher-Application}{s.len}) > 0 && $var(Status) == "Registered") {
xlog("L_INFO", "$ci| pusher ON_REGISTRATION - $(xavp(ulattrs=>custom_channel_vars){kz.json,Pusher-Application})\n");
ts_append("location", "$tu");
if( ( $(xavp(ulattrs=>x_token_reg){s.len}) > 0 ||
$(xavp(ulattrs=>custom_channel_vars){kz.json,Pusher-Application}{s.len}) > 0) &&
$var(Status) == "Registered") {
if($sht(push_cache=>$(tu{s.tolower})) != $null) {
xlog("L_INFO", "$ci|pusher|device registered, delivering the call\n");
$var(ref) = $sht(push_cache=>$(tu{s.tolower}));
$sht(push_cache=>$(tu{s.tolower})) = $null;
$var(t_index) = $(var(ref){param.value,index}{s.int});
$var(t_label) = $(var(ref){param.value,label}{s.int});
t_continue("$var(t_index)", "$var(t_label)", "PUSHER_DELIVER_CALL");
}
}
}
route[PUSHER_DELIVER_CALL]
{
if(reg_fetch_contacts("location", "$hdr(X-KAZOO-AOR)", "callee")) {
$du = $(ulc(callee=>received));
$fs = $(ulc(callee=>socket));
route(PUSHER_TO_EXTERNAL_RELAY);
} else {
t_reply(486, "Failed to lookup after resume");
}
}

+ 133
- 29
kamailio/registrar-role.cfg View File

@ -2,6 +2,15 @@
#!trydef REGISTRAR_NAT_PING_WORKERS 5
#!trydef REGISTRAR_MIN_EXPIRES 300
#!trydef REGISTRAR_MAX_EXPIRES 3600
#!trydef REGISTRAR_CONTACT_MAX_SIZE 2048
#!trydef REGISTRAR_HANDLE_LOST_TCP 1
#!trydef REGISTRAR_CLOSE_EXPIRED_TCP 1
#!trydef REGISTRAR_HANDLE_EXPIRED_TCP 1
#!trydef REGISTRAR_HANDLE_EXPIRED_UDP 0
#!trydef REGISTRAR_HANDLE_EXPIRED_TLS 1
#!trydef REGISTRAR_HANDLE_EXPIRED_WS 1
######## Generic Hash Table container in shared memory ########
modparam("htable", "htable", "auth_cache=>size=16;autoexpire=7200;")
@ -30,6 +39,8 @@ modparam("usrloc", "timer_interval", 30)
modparam("usrloc", "timer_procs", 1)
modparam("usrloc", "db_timer_clean", 1)
modparam("usrloc", "fetch_rows", 400)
modparam("usrloc", "handle_lost_tcp", REGISTRAR_HANDLE_LOST_TCP)
modparam("usrloc", "close_expired_tcp", REGISTRAR_CLOSE_EXPIRED_TCP)
######## NAT Traversal module - signaling functions ########
#!ifdef NAT_TRAVERSAL_ROLE
@ -54,6 +65,13 @@ modparam("registrar", "path_mode", 1)
modparam("registrar", "use_path", 1)
modparam("registrar", "received_param", "")
##modparam("registrar", "xavp_rcd", "ulrcd")
modparam("registrar", "contact_max_size", REGISTRAR_CONTACT_MAX_SIZE)
##### handle expired registrations realtime params #####
kazoo.registrar_handle_expired_tcp = REGISTRAR_HANDLE_EXPIRED_TCP descr "handles expired tcp registrations"
kazoo.registrar_handle_expired_udp = REGISTRAR_HANDLE_EXPIRED_UDP descr "handles expired udp registrations"
kazoo.registrar_handle_expired_tls = REGISTRAR_HANDLE_EXPIRED_TLS descr "handles expired tls registrations"
kazoo.registrar_handle_expired_ws = REGISTRAR_HANDLE_EXPIRED_WS descr "handles expired ws registrations"
####### Registrar Logic ########
route[HANDLE_REGISTER]
@ -69,8 +87,8 @@ route[HANDLE_REGISTER]
## KAZOO-1846: Cisco SPA8000 freaks out on options pings
if (!($ua =~ "Linksys/SPA8000"
|| $ua =~ "OpenBTS"
|| $ua =~ "SIPp"
|| $ua =~ "OpenBTS"
|| $ua =~ "SIPp"
|| (af==INET6)
)) {
setbflag(FLB_NATB);
@ -108,6 +126,9 @@ route[AUTHORIZATION_CHECK]
route[ATTEMPT_AUTHORIZATION]
{
#!ifdef PUSHER_ROLE
route(PUSHER_ATTEMPT_REGISTRATION);
#!endif
$var(nonce) = $(uuid(g){s.rm,-});
#!ifdef OPENBTS_AUTH_ROLE
@ -190,33 +211,27 @@ route[KAZOO_AUTHORIZATION_ERROR]
route[CHECK_AUTHORIZATION]
{
if($ua =~ "OpenBTS") {
xlog("L_INFO", "$ci|end|OPENBTS attempt for $Au $si:$sp\n");
} else {
if($var(password) == $null || $var(password) == "") {
auth_challenge("$fd", "0");
xlog("L_INFO", "$ci|end|issued auth challenge to registration attempt for $Au $si:$sp\n");
exit;
}
if (!pv_auth_check("$fd", "$var(password)", "0", "0")) {
#!ifdef ANTIFLOOD_ROLE
route(ANITFLOOD_FAILED_AUTH);
#!endif
if($var(password) == $null || $var(password) == "") {
auth_challenge("$fd", "0");
xlog("L_INFO", "$ci|end|issued auth challenge to registration attempt for $Au $si:$sp\n");
exit;
}
auth_challenge("$fd", "0");
xlog("L_WARNING", "$ci|end|issued auth challenge to failed registration attempt for $Au from IP $si:$sp\n");
exit;
}
if (!pv_auth_check("$fd", "$var(password)", "0", "0")) {
#!ifdef ANTIFLOOD_ROLE
route(ANITFLOOD_FAILED_AUTH);
#!endif
auth_challenge("$fd", "0");
xlog("L_WARNING", "$ci|end|issued auth challenge to failed registration attempt for $Au from IP $si:$sp\n");
exit;
}
}
#!ifdef ANTIFLOOD_ROLE
route(ANTIFLOOD_SUCCESSFUL_AUTH);
#!endif
@ -230,7 +245,6 @@ route[CHECK_AUTHORIZATION]
route[SAVE_LOCATION]
{
if ($sht(auth_cache=>$Au) == $null) {
xlog("L_INFO", "$ci|log|caching sip credentials for $Au\n");
};
@ -254,7 +268,7 @@ route[SAVE_LOCATION]
}
}
if(@hf_value_exists.contact.expires == "1") {
if((int)@contact.expires) {
$var(expires) = @contact.expires;
} else {
if(is_present_hf("Expires")) {
@ -264,6 +278,14 @@ route[SAVE_LOCATION]
}
}
## this is what we should be using
## but ulrcd seems to have a weird leak
## if($var(save_result) == 3) {
## $var(expires) = 0;
## } else {
## $var(expires) = $xavp(ulrcd=>expires);
## }
if($var(expires) == 0) {
xlog("L_INFO", "$ci|end|unregister request from $Au $si:$sp\n");
$var(Status) = "Unregistered";
@ -281,13 +303,43 @@ route[SAVE_LOCATION]
$var(ip) = "[" + $Ri + "]";
}
$var(port) = $Rp;
# allow port redirection on registration
switch($proto)
{
#!ifdef KZ_WEBSOCKETS_REGISTRAR_PORT
case "ws":
case "wss":
$var(port) = KZ_WEBSOCKETS_REGISTRAR_PORT;
break;
#!endif
#!ifdef KZ_TLS_REGISTRAR_PORT
case "tls":
$var(port) = KZ_TLS_REGISTRAR_PORT;
break;
#!endif
#!ifdef KZ_UDP_REGISTRAR_PORT
case "udp":
$var(port) = KZ_UDP_REGISTRAR_PORT;
break;
#!endif
#!ifdef KZ_TCP_REGISTRAR_PORT
case "tcp":
$var(port) = KZ_TCP_REGISTRAR_PORT;
break;
#!endif
default:
$var(port) = $Rp;
}
$var(AdvIP) = $RAi;
if(af==INET6) {
$var(AdvIP) = "[" + $RAi + "]";
}
$var(amqp_payload_request) = $_s({"Event-Category" : "directory", "Event-Name" : "reg_success", "Status" : "$var(Status)", "Event-Timestamp" : $TS, "Expires" : $(var(expires){s.int}), "First-Registration" : $var(new_reg), "Contact" : "$(ct{s.escape.common}{s.replace,\','}{s.replace,$$,})", "Call-ID" : "$ci", "Realm" : "$fd", "Username" : "$fU", "From-User" : "$fU", "From-Host" : "$fd", "To-User" : "$tU", "To-Host" : "$td", "User-Agent" : "$(ua{s.escape.common}{s.replace,\','}{s.replace,$$,})" , "Custom-Channel-Vars" : $xavp(ulattrs=>custom_channel_vars), "Proxy-Path" : "sip:$var(ip):$var(port)", "Proxy-Protocol" : "$proto", "Proxy-IP" : "$var(AdvIP)", "Proxy-Port" : "$RAp", "Source-IP": "$si", "Source-Port": "$sp" });
$var(amqp_routing_key) = "registration.success." + $(fd{kz.encode}) + "." + $(fU{kz.encode});
@ -320,22 +372,74 @@ event_route[kazoo:consumer-event-directory-reg-flush]
}
reg_free_contacts("caller");
}
#!ifdef ANTIFLOOD_ROLE
route(ANTIFLOOD_RESET_AUTH);
#!endif
}
event_route[usrloc:contact-expired]
{
$var(transport) = $(ulc(exp=>received){uri.transport});
$var(proto) = $(ulc(exp=>socket){re.subst,/^([^:]*):(.*)/\1/});
if($var(proto) == "tls" && $var(transport) == "ws") {
$var(proto) = "wss";
}
$var(handle) = 0;
switch($var(proto))
{
case "ws":
case "wss":
if(@cfg_get.kazoo.registrar_handle_expired_ws == 1) {
$var(handle) = 1;
}
break;
case "tls":
if(@cfg_get.kazoo.registrar_handle_expired_tls == 1) {
$var(handle) = 1;
}
break;
case "tcp":
if(@cfg_get.kazoo.registrar_handle_expired_tcp == 1) {
$var(handle) = 1;
}
break;
case "udp":
if(@cfg_get.kazoo.registrar_handle_expired_udp == 1) {
$var(handle) = 1;
}
break;
default:
break;
}
if($var(handle) == 1) {
$var(aor) = $_s(sip:$ulc(exp=>aor));
$var(username) = $(var(aor){uri.user});
$var(domain) = $(var(aor){uri.host});
$var(amqp_payload_request) = $_s({"Event-Category" : "directory", "Event-Name" : "reg_success", "Status" : "Unregistered", "Event-Timestamp" : $TS, "Expires" : 0, "First-Registration" : false, "Contact" : "$(ulc(exp=>addr){s.escape.common}{s.replace,\','}{s.replace,$$,})", "Call-ID" : "$ulc(exp=>callid)", "Realm" : "$var(domain)", "Username" : "$var(username)", "From-User" : "$var(username)", "From-Host" : "$var(domain)", "To-User" : "$var(username)", "To-Host" : "$var(domain)", "Proxy-Path" : "$ulc(exp=>socket)", "User-Agent" : "$(ulc(exp=>user_agent){s.escape.common}{s.replace,\','}{s.replace,$$,})"});
$var(amqp_routing_key) = "registration.success." + $(var(domain){kz.encode}) + "." + $(var(username){kz.encode});
kazoo_publish("registrar", $var(amqp_routing_key), $var(amqp_payload_request));
xlog("L_INFO", "$ulc(exp=>callid)|expired|notified kazoo about removed registration with contact : $ulc(exp=>addr)\n");
} else {
xlog("L_INFO", "$ulc(exp=>callid)|expired|removed registration with contact : $ulc(exp=>addr)\n");
}
}
route[REGISTRAR_BINDINGS]
{
#!import_file "registrar-custom-bindings.cfg"
#!ifndef REGISTRAR_CUSTOM_BINDINGS
#!ifndef REGISTRAR_CUSTOM_BINDINGS
$var(payload) = "{ 'exchange' : 'registrar' , 'type' : 'topic', 'queue' : 'registrar-flush-MY_HOSTNAME', 'routing' : 'registration.flush.*', 'federate' : 1 }";
kazoo_subscribe("$var(payload)");
#!endif
#!ifdef REGISTRAR_SYNC_ROLE


+ 1
- 0
system/systemd/kazoo-kamailio.service View File

@ -8,6 +8,7 @@ Group=daemon
PermissionsStartOnly=true
LimitNOFILE=65536
LimitCORE=infinity
LimitMEMLOCK=infinity
ExecStartPre=/usr/sbin/kazoo-kamailio prepare
ExecStart=/usr/sbin/kazoo-kamailio start
ExecStop=/usr/sbin/kazoo-kamailio stop


Loading…
Cancel
Save