User Tools

Site Tools


devel:config-engines

This is an old revision of the document!


Configuration File Engines

Kamailio implements from scratch the interpreter for its configuration file scripting language(used inside kamailio.cfg).

Starting with v5.0.0, the routing blocks can be written in some other (well known) scripting languages and run via their embedded interpreters inside Kamailio. Known to work:

  • Lua, implemented by app_lua module, as “lua” config engine

Setting a configuration engine can be done with the global setting:

cfgengine = "name"

If the name is “native” or “default”, then is own language interpreter will be used (this is same behaviour as when cfgengine is not set at all).

This setting must be after loading the module that implements the engine. For example:

...
loadmodule "app_lua.so"
...
cfgengine = "lua"
...

The aim is that the runtime active part of kamailio.cfg (the routing blocks) to be implemented in an embedded scripting language. Core parameters, loading modules and modules parameters will still be set via kamailio.cfg.

Among benefits of using different scripting languages:

  • ability to benefit from what a well established language already provides:
    • more people are expected to be familiar with
    • an extended set of data types, expressions and statements already available
    • a large set of extensions and libraries already available
  • reload the SIP routing logic without restarting Kamailio

Internally, the support for implementing routing logic in an embedded language is codenamed KEMI - Kamailio EMbedded Interface.

Exporting Functions To KEMI

The current implementation relies on the ability of Lua to allow hash tables with custom index. This is like an object that can have methods defined on the fly. Kamailio registers an empty object with a custom index function implemented in Kamailio code, whenever a member of that object is accessed, the index function is executed and Kamailio will resolve it to one of its functions, corresponding by name.

Each existing component of Kamailio (e.g., module), can export new functions to KEMI in the following way:

  • declare an array of type sr_kemi_t
  • register it to KEMI in mod_register() function (or at startup for core components) using sr_kemi_modules_add()

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/BOOL) */
	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:

  • C function 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 - all have to be str* (other combinations to be added as needed)
  • 5 params - all have to be str* (other combinations to be added as needed)
  • 6 params - all have to be str* (other combinations to be added as needed)

Lua Config KEMI Engine

The app_lua module must be loaded and the Lua script with routing logic must be set to its load parameter.

Inside the Lua script, following functions are relevant:

  • 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(…)
  • TBD: the options for specific event_route blocks. Meanwhile, should work using hybrid configuration with request_route/reply_route/… in embedded interpreter and the other routing blocks in native kamailio.cfg.

The following objects are available inside the Lua script:

  • sr - provided by the old way of exporting functions to Lua (https://www.kamailio.org/wiki/embeddedapi/devel/lua)
  • KSR - provided via KEMI interface (not many functions exported at this moment, but expected to take over all sr module). The functions exported to KEMI are accessible as KSR.submodule.function(…). If submodule name is empty (reserved for core functions), then they are available as KSR.function(…).

Lua Embedded Config Example

The kamailio.cfg with global/core and module settings:

#!KAMAILIO
 
 
debug=3
log_stderror=yes
fork=yes
children=2
 
memdbg=5
memlog=5
 
auto_aliases=no
 
listen=udp:127.0.0.1:5060
 
mpath="modules"
 
loadmodule "mi_fifo.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_lua.so"
 
# ----------------- setting module-specific parameters ---------------
 
# ----- mi_fifo params -----
modparam("mi_fifo", "fifo_name", "/var/run/kamailio/kamailio_fifo")
 
 
# ----- 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")
modparam("app_lua", "register", "maxfwd")
modparam("app_lua", "register", "sl")
 
cfgengine "lua"

The /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 sr.maxfwd.process_maxfwd(10) < 0 then
		KSR.sl.send_reply(483, "Too Many Hops");
		return;
	end
 
	-- KSR.sl.sreply(200, "OK Lua");
 
	sr.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

Python Config KEMI Engine

The app_python module must be loaded and the Python script with routing logic must be set to its script_name parameter.

The kemi engine is built reusing the exiting app_python way of executing C code from Kamailio. In the Python script you have to declare the 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(…)
  • TBD: the options for specific event_route blocks. Meanwhile, should work using hybrid configuration with request_route/reply_route/… in embedded interpreter and the other routing blocks in native kamailio.cfg.

The following objects are available inside the Python script:

  • Router - provided by the old way of exporting functions to Python (https://www.kamailio.org/wiki/embeddedapi/devel/python)
  • KSR - provided via KEMI interface (not many functions exported at this moment, but expected to take over all Python module). The functions exported to KEMI are accessible as KSR.submodule.function(…). If submodule name is empty (reserved for core functions), then they are available as KSR.function(…).

Python Embedded Config Example

The kamailio.cfg with the global/core and modules settings:

#!KAMAILIO
#
# Kamailio (OpenSER) SIP Server v5.0
#     - web: http://www.kamailio.org
#
####### 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
 
mpath="modules"
 
loadmodule "mi_fifo.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 ---------------
 
# ----- mi_fifo params -----
modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo")
 
 
# ----- 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", "script_name", "/etc/kamailio/kamailio.py")
 
cfgengine "python"

The /etc/kamailio/kamailio.py with the routing logic for runtime:

import sys
import Router.Logger as Logger
import KSR as KSR
 
def dumpObj(obj):
    for attr in dir(obj):
        # KSR.info("obj.%s = %s\n" % (attr, getattr(obj, attr)));
        Logger.LM_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");
        msg.rewrite_ruri("sip:alice@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");
        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_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;

To-Do

  • see if same dynamic object mechanism can be implemented for Perl, Python, etc.
  • discuss and decide the way to generate documentation of available functions via KEMI
  • analyze if worth using a declarative language for specifying the exported functions and then generate necessary C code from them (could be useful for generating docs as well)
  • optimize the indexing of exported functions (now indexed by module name, then walk through the exported list of functions)
devel/config-engines.1461072471.txt.gz · Last modified: 2016/04/19 15:27 by miconda