The following KEMI scripting languages can be used to write SIP routing logic for Kamailio:
A configuration file for Kamailio that uses a KEMI scripting language has the glabal parameters, loading modules and
module parameters in the native scripting and sets the value of cfgengine
to the KEMI scripting language identifier.
The KEMI scripting language identifiers are:
jsdt
- for JavaScriptlua
- for Luapython
- for Pythonpython3
- for Python3ruby
- for Rubysqlang
- for SquirrelIt is implemented by app_jsdt
module. The JavaScript interpreter is imported inside the module from
DukTape project, therefore it doesn't require to install any external libraries.
To use it, set inside kamailio.cfg
:
loadmodule "app_jsdt.so"
modparam("app_jsdt", "load", "/path/to/script.js")
cfgengine "jsdt"
Inside the JavaScript script, following functions have a predefined role:
ksr_request_route()
- is executed by Kamailio core every time a SIP request is received. If this function is
not defined, then Kamailio will write error messages. This is equivalent of request_route {}
from kamailio.cfg
.ksr_reply_route()
- is executed by Kamailio core every time a SIP Response (reply) is received. If this function
is not defined, then Kamailio will not write error messages. This is equivalent of reply_route {}
from kamailio.cfg
.ksr_onsend_route()
- is executed by Kamailio core every time a SIP request (and optionally for a response) is
sent out. If this function is not defined, then Kamailio will not write error messages. This is equivalent of
onsend_route {}
from kamailio.cfg
.branch route callback
- the name of the JavaScript function to be executed instead of a branch route has to be
provided as parameter to KSR.tm.t_on_branch(…)
onreply route callback
- the name of the JavaScript function to be executed instead of an onreply route has to be
provided as parameter to KSR.tm.t_on_reply(…)
failure route callback
- the name of the JavaScript function to be executed instead of a failure route has to be
provided as parameter to KSR.tm.t_on_failure(…)
branch failure route callback
- the name of the JavaScript function to be executed instead of an event route
for
branch failure has to be provided as parameter to KSR.tm.t_on_branch_failure(…)
event route callback
- the name of the JavaScript function to be exectued instead of module specific
event_route
blocks is provided via event_callback
parameter of that moduleA complete example of using JavaScript as KEMI languages is offered by the next two files:
The file kamailio-basic-kemi.cfg
has to be saved as kamailio.cfg
and inside it add after the first line:
#!define WITH_CFGJSDT
The file kamailio-basic-kemi-jsdt.js
has to be saved to local disk and the load
parameter for app_jsdt
module
inside kamailio.cfg
has to be updated to point to it. Then run kamailio
with this kamailio.cfg
.
The documentation for app_jsdk
is available at:
It is implemented by app_lua
module. The Lua interpreter is linked from liblua
library, supported Lua versions:
5.1 and 5.2.
loadmodule "app_lua.so"
modparam("app_lua", "load", "/path/to/script.lua")
cfgengine "lua"
Inside the Lua script, following functions have a predefined role:
ksr_request_route()
- is executed by Kamailio core every time a SIP request is received. If this function is
not defined, then Kamailio will write error messages. This is equivalent of request_route {}
from kamailio.cfg
.ksr_reply_route()
- is executed by Kamailio core every time a SIP Response (reply) is received. If this function
is not defined, then Kamailio will not write error messages. This is equivalent of reply_route {}
from kamailio.cfg
.ksr_onsend_route()
- is executed by Kamailio core every time a SIP request (and optionally for a response) is
sent out. If this function is not defined, then Kamailio will not write error messages. This is equivalent of
onsend_route {}
from kamailio.cfg
.branch route callback
- the name of the Lua function to be executed instead of a branch route has to be
provided as parameter to KSR.tm.t_on_branch(…)
onreply route callback
- the name of the Lua function to be executed instead of an onreply route has to be
provided as parameter to KSR.tm.t_on_reply(…)
failure route callback
- the name of the Lua function to be executed instead of a failure route has to be
provided as parameter to KSR.tm.t_on_failure(…)
branch failure route callback
- the name of the Lua function to be executed instead of an event route
for
branch failure has to be provided as parameter to KSR.tm.t_on_branch_failure(…)
event route callback
- the name of the Lua function to be exectued instead of module specific
event_route
blocks is provided via event_callback
parameter of that moduleNote: besides the new KEMI Lua KSR module, the old sr
Lua module exported by app_lua
is still available.
A complete example of using Lua as KEMI languages is offered by the next two files:
The file kamailio-basic-kemi.cfg
has to be saved as kamailio.cfg
and inside it add after the first line:
#!define WITH_CFGLUA
The file kamailio-basic-kemi-lua.lua
has to be saved to local disk and the load
parameter for app_lua
module
inside kamailio.cfg
has to be updated to point to it. Then run kamailio
with this kamailio.cfg
.
The documentation for app_lua
is available at:
The file kamailio.cfg
with global parameters and module settings:
#!KAMAILIO
####### Global Parameters #########
debug=3
log_stderror=yes
fork=yes
children=2
memdbg=5
memlog=5
auto_aliases=no
listen=udp:127.0.0.1:5060
loadmodule "jsonrpcs.so"
loadmodule "kex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "xlog.so"
loadmodule "ctl.so"
loadmodule "debugger.so"
loadmodule "app_lua.so"
# ----------------- setting module-specific parameters ---------------
# ----- jsonrpcs params -----
modparam("jsonrpcs", "pretty_format", 1)
# ----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 30sec
modparam("tm", "fr_timer", 30000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)
# ----- debugger params -----
modparam("debugger", "cfgtrace", 1)
####### Routing Logic ########
modparam("app_lua", "load", "/etc/kamailio/kamailio.lua")
cfgengine "lua"
The file /etc/kamailio/kamailio.lua
with the routing logic for runtime:
-- Kamailio - equivalent of routing blocks in Lua
-- KSR - the new dynamic object exporting Kamailio functions
-- sr - the old static object exporting Kamailio functions
--
-- SIP request routing
-- equivalent of request_route{}
function ksr_request_route()
KSR.info("===== request - from kamailio lua script\n");
if KSR.maxfwd.process_maxfwd(10) < 0 then
KSR.sl.send_reply(483, "Too Many Hops");
return;
end
-- KSR.sl.sreply(200, "OK Lua");
KSR.pv.sets("$du", "sip:127.0.0.1:5080")
KSR.tm.t_on_branch("ksr_branch_route_one");
KSR.tm.t_on_reply("ksr_onreply_route_one");
KSR.tm.t_on_failure("ksr_failure_route_one");
if KSR.tm.t_relay() < 0 then
KSR.sl.send_reply(500, "Server error")
end
end
-- SIP response routing
-- equivalent of reply_route{}
function ksr_reply_route()
KSR.info("===== response - from kamailio lua script\n");
end
-- branch route callback
-- equivalent of a branch_route{}
function ksr_branch_route_one()
KSR.info("===== branch route - from kamailio lua script\n");
end
-- onreply route callback
-- equivalent of an onreply_route{}
function ksr_onreply_route_one()
KSR.info("===== onreply route - from kamailio lua script\n");
end
-- failure route callback
-- equivalent of a failure_route{}
function ksr_failure_route_one()
KSR.info("===== failure route - from kamailio lua script\n");
end
It is implemented by app_python
module. The Python interpreter is linked from libpython
, supported Python versions:
2.5, 2.6 and 3.x (via app_python3).
loadmodule "app_python.so"
modparam("app_python", "script_name", "/path/to/script.py")
cfgengine "python"
In the Python script you have to declare the global mod_init()
method where to instantiate an object of a class that
implements the other callback methods (functions) to be executed by Kamailio.
Inside the new class, the following methods are relevant:
ksr_request_route(self, msg)
- is executed by Kamailio core every time a SIP request is received. If this
function is not defined, then Kamailio will write error messages. This is equivalent of request_route {}
from
kamailio.cfg
.ksr_reply_route(self, msg)
- is executed by Kamailio core every time a SIP Response (reply) is received. If this
function is not defined, then Kamailio will not write error messages. This is equivalent of reply_route {}
from
kamailio.cfg
.ksr_onsend_route(self, msg)
- is executed by Kamailio core every time a SIP request (and optionally for a
response) is sent out. If this function is not defined, then Kamailio will not write error messages. This is
equivalent of onsend_route {}
from kamailio.cfg
.branch route callback
- the name of the Python function to be executed instead of a branch route has to be
provided as parameter to KSR.tm.t_on_branch(…)
onreply route callback
- the name of the Python function to be executed instead of an onreply route has to be
provided as parameter to KSR.tm.t_on_reply(…)
failure route callback
- the name of the Python function to be executed instead of a failure route has to be
provided as parameter to KSR.tm.t_on_failure(…)
branch failure route callback
- the name of the Python function to be executed instead of an event route for
branch failure has to be provided as parameter to KSR.tm.t_on_branch_failure(…)
event route callback
- the name of the Python function to be exectued instead of module specific
event_route
blocks is provided via event_callback
parameter of that moduleNote: besides the new KEMI Python KSR module, the old Router
Python module exported by app_python
is still
available.
A complete example of using Python as KEMI languages is offered by the next two files:
The file kamailio-basic-kemi.cfg
has to be saved as kamailio.cfg
and inside it add after the first line:
#!define WITH_CFGPYTHON
The file kamailio-basic-kemi-python.py
has to be saved to local disk and the load
parameter for
app_python
module inside kamailio.cfg
has to be updated to point to it. Then run kamailio
with this kamailio.cfg
.
The documentation for app_python
is available at:
The file kamailio.cfg
with the global parameters and modules settings:
#!KAMAILIO
####### Global Parameters #########
debug=4
log_stderror=yes
fork=yes
children=2
memdbg=5
memlog=5
auto_aliases=no
listen=udp:127.0.0.1:5060
loadmodule "jsonrpcs.so"
loadmodule "kex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "textops.so"
loadmodule "xlog.so"
loadmodule "ctl.so"
loadmodule "mi_rpc.so"
loadmodule "debugger.so"
loadmodule "app_python.so"
# ----------------- setting module-specific parameters ---------------
# ----- tm params -----
# auto-discard branches from previous serial forking leg
modparam("tm", "failure_reply_mode", 3)
# default retransmission timeout: 30sec
modparam("tm", "fr_timer", 30000)
# default invite retransmission timeout after 1xx: 120sec
modparam("tm", "fr_inv_timer", 120000)
# ----- debugger params -----
modparam("debugger", "cfgtrace", 1)
####### Routing Logic ########
modparam("app_python", "load", "/etc/kamailio/kamailio.py")
cfgengine "python"
The file /etc/kamailio/kamailio.py
with the routing logic for runtime:
import sys
import KSR as KSR
def dumpObj(obj):
for attr in dir(obj):
KSR.info("obj.%s = %s\n" % (attr, getattr(obj, attr)))
def mod_init():
KSR.info("===== from Python mod init\n")
# dumpObj(KSR)
return kamailio()
class kamailio:
def __init__(self):
KSR.info('===== kamailio.__init__\n')
def child_init(self, rank):
KSR.info('===== kamailio.child_init(%d)\n' % rank)
return 0
def ksr_request_route(self, msg):
KSR.info("===== request - from kamailio python script\n")
KSR.setdsturi("sip:[email protected]:5080")
KSR.tm.t_on_branch("ksr_branch_route_one")
KSR.tm.t_on_reply("ksr_onreply_route_one")
KSR.tm.t_on_failure("ksr_failure_route_one")
KSR.sl.send_reply(100, "Trying")
if KSR.tm.t_relay() < 0 :
KSR.sl.send_reply(500, "Server error")
return 1
def ksr_reply_route(self, msg):
KSR.info("===== response - from kamailio python script\n")
return 1
def ksr_onsend_route(self, msg):
KSR.info("===== onsend route - from kamailio python script\n")
return 1
def ksr_branch_route_one(self, msg):
KSR.info("===== branch route - from kamailio python script\n")
return 1
def ksr_onreply_route_one(self, msg):
KSR.info("===== onreply route - from kamailio python script\n")
return 1
def ksr_failure_route_one(self, msg):
KSR.info("===== failure route - from kamailio python script\n")
return 1
It is implemented by app_ruby
module. The module requires libruby-dev in order
to be compiled. The module was initially tested with libruby-2.3 and libruby-2.5.
loadmodule "app_ruby.so"
modparam("app_ruby", "load", "/path/to/script.rb")
cfgengine "ruby"
The documentation for app_ruby
is available at:
It is implemented by app_sqlang
module. The Squirrel language interpreter is imported inside the module from
Squirrel project, therefore it doesn't require to install any external libraries.
loadmodule "app_sqlang.so"
modparam("app_sqlang", "load", "/path/to/script.sq")
cfgengine "sqlang"
A complete example of using Squirrel as KEMI languages is offered by the next two files:
The file kamailio-basic-kemi.cfg
has to be saved as kamailio.cfg
and inside it add after the first line:
#!define WITH_CFGSQLANG
The file kamailio-basic-kemi-sqlang.sq
has to be saved to local disk and the load
parameter for app_sqlang
module inside kamailio.cfg
has to be updated to point to it. Then run kamailio
with this kamailio.cfg
.
The documentation for app_sqlang
is available at:
Inside the routing script, the functions exported by Kamailio through KEMI are available via KSR
module. With very
few exceptions, the KEMI functions return either an integer or a bool value and can take up to six parameters of type
string or integer.
The integer return code from a Kemi functions has to be evaluated with the following rules:
the value is greater than 0
, then the function was successfully executed and a logical evaluation should be
considered truethe value is less than 0
, then the function was not successfully executed or the function was successfully
executed and the a logical evaluation should be considered falsethe value is equal to 0
, then the execution of the KEMI script should be terminated (be careful with execution of
exit()
function from the embedded interpreter language, it may kill the interpreter completely, which in this case
is Kamailio, resulting in shutting down Kamailio - hint: check KSR.x.exit()
)The bool return code is expected to be evaluated as true
or false
inside the KEMI script.
If a function has void
as return type in the signature, the it doesn't return any value.
Several functions may return a string or xval
value, for example in the KSR.pv
submodule to get the value of pseudo-variables. If a function returns xval
, then the result value can be string
, integer
or null
.
The convention for the parameters in the signature of the functions is to enclose in double quotes if the parameter has a string type and no quotes if the parameter has integer type.
Most of the functions exported through KEMI have an equivalent in the functions available in the native scripting language. Generic mapping rules:
The available KEMI functions in a running instance of Kamailio can be listed via an RPC command. A matter of the interpreter used, one of the following commands needs to be run:
kamctl rpc app_jsdt.api_list
kamctl rpc app_lua.api_list
kamctl rpc app_python.api_list
kamctl rpc app_python3.api_list
kamctl rpc app_ruby.api_list
kamctl rpc app_sqlang.api_list
The functions exported by Kamailio via KSR submodules that return 0 were designed to stop the execution of the routing script. That is done automatically in the native scripting language, but in KEMI scripting languages requires additional steps to terminate the script execution for that SIP message -- typically means evaluation of the return code and if it is 0, then execute KSR.x.exit() or the equivalent in that scripting language.
There are only a few functions returning 0, this section tries to collect them for convenience:
tm
module
t_check_trans()
t_newtran()
ws_handshake()
Example handling t_check_trans()
for 0 return code in Lua script:
...
-- SIP request routing
-- equivalent of request_route{}
function ksr_request_route()
...
if KSR.tm.t_check_trans()==0 then
KSR.x.exit();
end
...
end
...
Note: these functions can also return negative or positive values, those cases have to be handled as well.
Because Kamailio needs to load modules in order to export useful functions to KEMI, statical wrappers to C functions implemented in other modules cannot be used, because they will introduce dependencies on each embedded interpreter for all modules.
The implementation relies on defining a set of generic functions that are exported to each embedded interpreter, which are associated at startup with a Kamailio C functions. The lookup at runtime is by an integer index, therefore very fast.
Currently the association table size is 1024 (it means that there can be maximum 1024 Kamailio C functions exported
to the interpreter by a configuration file). The number can be increased, but it should be fairly enough as all
kamailio.cfg
functions are around 1000 and it is no real use case to load all the modules at the same time for use
in production. Also, many functions may not be exported to an embedded language, as they have native alternative in
the embedded language.
Each existing component of Kamailio (e.g., module), can export new functions to KEMI in the following way:
The structure sr_kemi_t
is declared in Kamailio core, the file kemi.h
:
#define SR_KEMI_PARAMS_MAX 6
typedef struct sr_kemi {
str mname; /* sub-module name */
str fname; /* function name */
int rtype; /* return type (supported SR_KEMIP_INT, SR_KEMIP_BOOL, SR_KEMIP_XVAL) */
void *func; /* pointer to the C function to be executed */
int ptypes[SR_KEMI_PARAMS_MAX]; /* array with the type of parameters */
} sr_kemi_t;
Next C code snippet shows how sl module exports two functions:
sl_send_reply_str(…)
is exported as sl.sreply(…)
C function send_reply(…)
is exported as sl.freply(…)
static sr_kemi_t sl_kemi_exports[] = {
{ str_init("sl"), str_init("sreply"),
SR_KEMIP_INT, sl_send_reply_str,
{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
},
{ str_init("sl"), str_init("freply"),
SR_KEMIP_INT, send_reply,
{ SR_KEMIP_INT, SR_KEMIP_STR, SR_KEMIP_NONE,
SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
},
{ {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
};
int mod_register(char *path, int *dlflags, void *p1, void *p2)
{
sr_kemi_modules_add(sl_kemi_exports);
return 0;
}
Note that the exported array is ended by a sentinel of 0
/NULL
values for all fields.
Exported functions must take first parameter as sip_msg_t*
type (which is the structure with the SIP message being
processed), then followed by up to 6 int
or str*
parameters. When SR_KEMIP_NONE
is given in the array with the types
of parameters, it means there is no parameter from there on (some compilers may rise warning, so it is recommended
to fill all 6 items in array).
The functions exported by Kamailio core are listed inside the array _sr_kemi_core
from the file kemi.c
.
Not all combinations of extra (after sip_msg_t*
) parameters types are supported right now - currently the are:
1 param
- can be int
of str*
2 params
- any combination of int
or str*
3 params
- any combination of int
or str*
4 params
- any combination of int
or str*
5 params
- any combination of int
or str*
6 params
- all have to be str*
(other combinations to be added as needed)The return type can be:
SR_KEMIP_INT
- returned value is integer and has to be evaluated with the
following rules:
KSR.tm.t_check_trans(...)
)KSR.kx.get_status()
, KSR.kx.get_timestamp()
)SR_KEMIP_BOOL
- returned value can be evaluated in the routing script as
TRUE or FALSE. In the C code it has to return 1 for TRUE or 0 for FALSE.SR_KEMIP_XVAL
- returned value depends on the context, can be either
integer or string value. For example it is used for the functions that return
the value of pseudo-variables KSR.pv.get(...). This return type is supported
only for exported functions that have up to 2 parameters.