Kamailio uses a native scripting laguage for its configuration file kamailio.cfg . This components of this file are :
- global parameters
- loading modules
- module parameters
- routing blocks like request_route {…}, reply_route {…}, branch_route {…} etc
These parameters including initialization event routes , are interpeted and loaded at kamailio startup.
We know that restart of sip server hinder all ongoing traffic and slows development of routing logic.
Due to limitation of customizatiosn with native language and mostly due to the fact that interpreter precompiles kamailio.cfg at startup preventing routing script to reload without restart , other programming languages were used such as Kamailio Embedded Interface (KEMI) framework.
Post v5 of kamailio , the interpreters of these languages were integrated with kamailio and feature rich SIP routing logic could be written with them for runtime execution. The routing blocks are in form of functions written in a KEMI ( Kamaialio EMbedded Interface) supported scripting language such as Lua as discussed in this article. Other components are still initialised by kamailio.cfg at starup. Apart from above mentioned advantages of programming languages , another plus point is that it enables test-cases , debug functionality without having to restart the server for code update.
KEMI supported programming languages:
- JavaScript (app_jsdt)
- Lua (app_lua)
- Python (app_python)
- Python3 (app_python3)
- Squirrel (app_sqlang)
Kamailio has other modules that allow inline execution of scripts written in other programming languages, but don’t implement the KEMI yet. Such as
Perl (app_perl)
.Net (C#, etc.)
(app_mono),
Java (app_java)
As Kamailio becomes the interpreter for these languages , it executes logic faster at runtime . KEMI also extends the modules with extension to kamailio c functions
Lua KEMI Interpreter
Lua interpreter is linked from liblua library in app_lua module.
//loading loadmodule "app_lua.so" //script path modparam("app_lua", "load", "/path/to/script.lua") //specifying engine cfgengine "lua"
Predefined functions in app_lua module
ksr_request_route() : executed by Kamailio core every time a SIP request is received, equivalent of request_route {} from kamailio.cfg
ksr_reply_route() : executed by Kamailio core every time a SIP Response (reply) is received, equivalent of reply_route {} from kamailio.cfg
ksr_onsend_route() : executed when a SIP request (and optionally for a response) is sent out, equivalent of onsend_route {}
branch route callback : 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 : 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 : 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 : 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 : name of the Lua function to be exectued instead of module specific event_route blocks is provided via event_callback parameter of that module
KSR is the new dynamic object exporting Kamailio functions. sr is the old static object exporting Kamailio functions
route.lua for routing logic
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 function ksr_reply_route() KSR.info("===== response - from kamailio lua script\n"); end function ksr_branch_route_one() KSR.info("===== branch route - from kamailio lua script\n"); end function ksr_onreply_route_one() KSR.info("===== onreply route - from kamailio lua script\n"); end function ksr_failure_route_one() KSR.info("===== failure route - from kamailio lua script\n"); end
Core Kemi functions
KSR.add_local_rport() : Set the internal flag to add rport parameter to local generated Via header.
KSR.add_tcp_alias() : Adds a tcp port alias for the current connection (if tcp). Can help in firewall or nat traversal
KSR.add_tcp_alias_via() : Adds the port from the message via as an alias to TCP connection.
void KSR.dbg(…) : debug log
KSR.dbg("debug log message from embedded interpreter\n");
void KSR.err(…) : error logging
KSR.err("error log message from embedded interpreter\n");
void KSR.info(…) : info log
KSR.info("info log message from embedded interpreter\n");
KSR.force_rport() : Add rport parameter to the top Via of the incoming request and sent the SIP response to source port.
KSR.is_method() : Return true if the value of the parameter matches the method type of the SIP message.
if(KSR.is_method("INVITE")) {
...
}
KSR.is_method_in() ; Return true if SIP method of the currently processed message is matching one of the corresponding characters given as parameter. Matching the method is done based on corresponding characters:
I – INVITE
A – ACK
B – BYE
C – CANCEL
R – REGISTER
M – MESSAGE
O – OPTIONS
S – SUBSCRIBE
P – PUBLISH
N – NOTIFY
U – UPDATE
K – KDMQ
G – GET
T – POST
V – PUT
D – DELETE
if KSR.is_method_in("IABC") then
-- the method is INVITE, ACK, BYE or CANCEL
...
end
KSR.is_INVITE() : Return true if the method type of the SIP message is INVITE.
KSR.is_ACK() : true if the method type is ACK.
KSR.is_BYE() : true if the method type is BYE.
KSR.is_CANCEL() : true if CANCEL.
KSR.is_REGISTER() : true if REGISTER.
KSR.is_MESSAGE() : true if SIP message is MESSAGE.
KSR.is_SUBSCRIBE() : true if SIP message is SUBSCRIBE.
KSR.is_PUBLISH() : true if PUBLISH.
KSR.is_NOTIFY() : true if NOTIFY.
KSR.is_OPTIONS() : true if OPTIONS.
KSR.is_INFO() : true if INFO.
KSR.is_UPDATE() : true if UPDATE.
KSR.is_PRACK() : true if PRACK.
KSR.is_myself(…) : Return true of the URI address provided as parameter matches a local socket (IP) or local domain.
if KSR.is_myself("sip:127.0.0.1:5060") then
...
end
KSR.is_myself_furi() : Return true if the URI in From header matches a local socket (IP) or local domain.
KSR.is_myself_ruri() : Return true if the R-URI matches a local socket (IP) or local domain.
KSR.is_myself_turi() : Return true if the URI in To header matches a local socket (IP) or local domain.
KSR.is_myself_suri() : true if the URI built from source IP, source port and protocol matches a local socket (IP).
KSR.is_myself_suri() : true if the source IP matches a local socket (IP).
void KSR.log(…) : Write a log message specifying the level value. The level parameter can be: “dbg” , “info” , “warn” , “crit” , “err”
KSR.setflag(…) : Set the SIP message/transaction flag ( int from 0 to 31 ) at the index provided by the parameter.
KSR.resetflag(…) : Reset the SIP message/transaction flag ( int from 0 to 31 ) at the index provided by the parameter.
KSR.isflagset(…) :Return true if the message/transaction flag at the index provided by the parameter is set (the bit has value 1).
KSR.setbflag(…) : Set the branch flag(0-31) at the index provided by the parameter.
KSR.resetbflag(…) : Reset the branch flag(0-31) at the index provided by the parameter.
KSR.isbflagset(…) : Return true if the branch flag at the index provided by the parameter is set (the bit has value 1).
KSR.setbiflag(…) : Set the flag at the index provided by the first parameter to the branch number specified by the second parameter. The flag parameter has to be a number from 0 to 31. The branch parameter should be between 0 and 12 (a matter of max_branches global parameter).
KSR.resetbiflag(…) : Reset a branch flag by position and branch index.
KSR.isbiflagset(…) : Test if a branch flag is set by position and branch index.
KSR.setsflag(…) : Set a script flag.
KSR.resetsflag(…) : Reset a script flag.
KSR.issflagset(…) : Test if a script flag is set.
KSR.seturi(…) : Set the request URI (R-URI).
KSR.seturi("sip:alice@voip.com");
KSR.setuser(…)
KSR.setuser("alice");
KSR.sethost(…)
KSR.sethost("voip.com");
KSR.setdsturi(…)
KSR.setdsturi("sip:voip.com:5061;transport=tls");
KSR.resetdsturi(…) : Reset the destination URI (aka: outbound proxy address, dst_uri, $du).
KSR.isdsturiset(…) : Test if destination URI is set.
KSR.force_rport(…) : Set the flag for “rport” handling (send the reply based on source address instead of Via header).
KSR.set_drop(…) : Set the DROP flag, so at the end of KEMI script execution, the SIP request branch or the SIP response is not forwarded.
KSR.set_advertised_address() : Set the address (host or ip) to be advertised in Via header.
KSR.set_advertised_port() : Set the port (in string format) to be advertised in Via header.
KSR.set_forward_close(…) : Set the flag to close the connection after forwarding the message.
KSR.set_forward_no_connect(…) : Set the flag to not open a connection if the connection to the target does not exist when attempting to forward a message.
KSR.set_reply_close(…) : Set the flag to close the connection after sending a response.
KSR.set_reply_no_connect(…) : Set the flag to not open a connection if the connection for sending the response does not exist.
KSR.forward(…) : Forward the SIP request in stateless mode to the address set in destination URI ($du), or, if this is not set, to the address in request URI ($ru).
KSR.forward_uri(…) : Forward the SIP request in stateless mode to the address provided in the SIP URI parameter.
KSR.forward_uri("sip:127.0.0.1:5080;transport=tcp");
KSR.hdr.append(…) : Append header to current SIP message (request or reply).
KSR.hdr.append("X-My-Hdr: " + KSR.pv.getw("$si") + "\r\n");
KSR.hdr.append_after(…) : Append header to current SIP message (request or reply). It will be added after the first header matching the name hdrname.
KSR.hdr.append_after("X-My-Hdr: " + KSR.pv.getw("$si") + "\r\n", "Call-Id");
KSR.hdr.insert(…) : Insert header to current SIP message (request or reply). It will be added before the first header.
KSR.hdr.insert("X-My-Hdr: " + KSR.pv.getw("$si") + "\r\n");
KSR.hdr.insert_before(…) : Insert header to current SIP message (request or reply). It will be added before the header matching the name hdrname.
KSR.hdr.insert_before("X-My-Hdr: " + KSR.pv.getw("$si") + "\r\n", "Call-Id");
KSR.hdr.remove(…) : Remove all the headers with the name hdrval.
KSR.hdr.remove("X-My-Hdr");
KSR.hdr.is_present(…) : Return greater than 0 if a header with the name hdrval exists and less than 0 if there is no such header.
if(KSR.hdr.is_present("X-My-Hdr") > 0) {
...
}
KSR.hdr.append_to_reply(…) : Add a header to the SIP response to be generated by Kamailio for the current SIP request.
KSR.hdr.append_to_reply("X-My-Hdr: " + KSR.pv.getw("$si") + "\r\n");
PV And Special KEMI Functions
PV And Special KEMI Functions
KSR.pv submodule provides the functions to get, set and test the values of pseduo-variables.
The pvname that appears in the next sections in the function prototypes has to be a valid pseudo-variable name for Kamailio native configuration file (for example $ru, $var(x), $shv(z), …).
KSR.pv.get(…) : Return the value of pseudo-variable pvname. The returned value can be string or integer.
KSR.dbg("ruri is: " + KSR.pv.get("$ru") + "\n");
KSR.pv.gete(…) : Return the value of pseudo-variable pvname if it is different than $null or the empty string (“”) if the variable is having the $null value.
KSR.dbg("avp is: " + KSR.pv.gete("$avp(x)") + "\n");
KSR.pv.getvn(…) : Return the value of pseudo-variable pvname if it is different than $null or the parameter vn if the variable is having the $null value.
KSR.dbg("avp is: " + KSR.pv.getvn("$avp(x)", 0) + "\n");
KSR.pv.getvs(…) : Return the value of pseudo-variable pvname if it is different than $null or the parameter vs if the variable is having the $null value.
KSR.dbg("avp is: " + KSR.pv.getvs("$avp(x)", "foo") + "\n");
KSR.pv.getw(…) : Return the value of pseudo-variable pvname if it is different than $null or the string <> if the variable is having the $null value. This should be used instead of KSR.pv.get(…) in the scripting languages that throw and error when attempting to print a NULL (or NIL) value.
KSR.dbg("avp is: " + KSR.pv.getw("$avp(x)") + "\n");
KSR.pv.seti(…) : set the value of pseudo-variable pvname to integer value provided by parameter val.
KSR.pv.seti("$var(x)", 10);
KSR.pv.sets(…) : set the value of pseudo-variable pvname to string value provided by parameter val.
KSR.pv.sets("$var(x)", "kamailio");
KSR.pv.unset(…) : Set the value of pseudo-variable pvname to $null.
KSR.pv.unset("$avp(x)");
KSR.pv.is_null(…) : Return true if pseudo-variable pvname is $null.
if(KSR.pv.is_null("$avp(x)")) {
...
}
KSR.x.modf(…) : Execute a function (specified by fname) exported by a Kamailio module.
KSR.x.modf("sl_send_reply", "200", "OK");
KSR.x.exit(…) : stop the execution of the SIP routing script.
KSR.x.drop(…) : stop the execution of the SIP routing script and drop routing further the SIP request branch or response.
KEMI Module Functions
Module specific exported KEMI functions.
Acc
KSR.acc.acc_db_request()
KSR.acc.acc_log_request()
KSR.acc.acc_request()
app_lua
executing Lua scripts from config file.
KSR.app_lua.dofile()
KSR.app_lua.dostring()
KSR.app_lua.run()
KSR.app_lua.run_p1()
KSR.app_lua.run_p2()
KSR.app_lua.run_p3()
KSR.app_lua.runstring()
Async
asynchronous operations for handling SIP requests
KSR.async.route()
KSR.async.task_route()
auth
KSR.auth.auth_challenge()
KSR.auth.consume_credentials()
KSR.auth.has_credentials()
KSR.auth.pv_auth_check()
auth_db
KSR.auth_db.auth_check()
KSR.auth_db.is_subscriber()
-tbd –
Ref :