OpenSIPS Module Interface – Part 2 (C Development)

.

Introduction

This is Part 2 of the topic “OpenSIPS Module Interface”. In Part 1,I have talked about the definition of the module interface, some fields of its structure, and how to export function from the module to the routing script. So you can call this function in the routing script. Also i have talked about how to export a module parameter. I have talked about the OpenSIPS management interface in the article “OpenSIPS Management Interface (C Development)“. In this article, i will explain a little bit in the module interface and i will show you how to compile a new module.

The definition of the module interface is defined in the header file: “sr_module.h” as following:

struct module_exports {.

……… /* Fields that are explained in the Previous Article are missed here*/

……..  /* Some other fields are missed to be explained in the Next Article */

init_function   init_f;                              /* Initialization function */

destroy_function destroy_f;                /* Function called when the module

.                                                                       should be “destroyed” */
.child_init_function init_child_f;            /* Function called by all processes after the fork */

}

init_f function which will be called at OpenSIPS startup time. In OpenSIPS 1.x which is multi process application, this function will be called when OpenSIPS is just one process (attendant process). For example checking for the database connection can be done here or allocating a memory that will be as shared memory for the forked processes (after forking). You can define your init_f function in the “exports” which is the actual module interface. Lets say “mod_init” is the initialization function in the module interface:

static int mod_init(void){

……

}

The initialization of the exported variables in the routing script will be done before calling the function mod_init. The full OpenSIPS configuration file will be parsed and the own module parameters will be included. For example the parameter “db_url” which will be configured in the routing script:  modparam(“module_name”,”db_url”,”mysql://user:passwd@host/database”) and the “db_url” will be evaluated and can be used in the function “mod_init”. So a connection to the database can be opened in the body of the “mod_init” function.

Also the helper API like shared memory, locking, timer, ..etc. will be available and can be used. Note some resources like new timer processes for the module can only be initialized in the “mod_init” function.

After forking, each process will get a copy of what the OpenSIPS attendant process had before forking (structures/connections).

init_child_f function will be called after OpenSIPS has been forked (It will be called by each process individually). For example if each process will have its separate DB connection, the creation of the DB connection will be in this function.

static int child_init(int rank){

……..

}

The rank parameter allows you to know which process is calling the function. For example it can be called from SIP worker or MI worker (e.g. fifo worker).

destroy_f function will be called when OpenSIPS will shutdown. Here you can do some cleaning of resources(memory, DB connections, and so on). For example you can free the memory for some module-created variables (e.g. pkg_free(Pointer to the Variable)). OpenSIPS 1.X has its own internal memory manager and the function pkg_free is like the function free(pointer to a variable) of glibc standard library. The pointer must not be null otherwise it will crash. Also here in this function, you can save a state that the module have into persistent storage so they can be loaded again at the next OpenSIPS startup. For example in this function, the “dialog” module saves the dialog states in the database.

OpenSIPS 2.X is multi thread application. I will back to it in one or two upcoming articles.

Add Include Directives

You need to add the include statements to the new module code to be able to compile it. For example: the “string.h”, “sr_module.h” which includes the definitions of the structures, “dprint.h” which is used to print stuffs to the syslog or standard error, “mem.h” which is used to allocate/free a memory, and the header file which contains the structures of the new module (e.g. new_module.h).

How to Compile a New OpenSIPS Module

Each OpenSIPS module has its own regular Makefile. It will be run by the master Makefile. The file name is “Makefile” and its content looks like this:

include ../../Makefile.defs
auto_gen=
NAME=new_module.so
LIBS=

include ../../Makefile.modules

The “Makefile.defs” must be included from the OpenSIPS source folder in addition to the name of the shared object “new_module.so” which will be generated and the file “Makefile.modules”. If the module has external libraries dependencies, they should be linked in the module’s Makefile.  Here we have two Makefile variables:

  • DEFS: This is used if the module has additional definitions that you want to pass it to the compiler. Add this line to the module’s Makefile if the module has additional definitions:

DEFS+=-I$(LOCALBASE)/include

OR DEFS+=-I../../../exdef/include

+= is a Makefile operator.

  • LIBS: This is if the  module requires external libraries. So these options:  -llibrary and the -Lpath_to_library will be set to LIBS variable. For example for “cachedb_memcashed module”, add this line to the module’s Makefile:

LIBS=-L$(LOCALBASE)/lib -lmemcashed

To compile the module:

  • The module source files (.c and .h files) and the Makefile should be in the folder “/usr/local/src/opensips_1_11/modules/new_module”
  • From the OpenSIPS ‘s src  folder (“/usr/local/src/opensips_1_11″), execute:

# make include_modules=”new_module” modules OR # make modules modules=modules/new_module

# make install

  • To see if the module is compiled and installed , check having the file “new_module.so” installed in the subdirectory “/usr/local/opensips_1_11/lib64/opensips/modules/“.

Testing

Write log statements (e.g. LM_INFO(….)) in the C code of the new module. Compile it , and restart opensips. Open the log file and search for the name of the module (for example search for “new_module”) to see what is happening to the module “new_module”.


Notes

1- If you’ve got a compilation error (e.g “….. No rule to make target ….”) and you want to clean everything (delete the generated object files and dependencies files (files with extensions “.d”), execute “make proper” and then “make”.

2- If the module depends on external libraries, the module must NOT be compiled by default. To disable this, we edit the file “Makefile.conf.template” which specifies the modules that are not compiles by default.

  • Add a line for your module in “Makefile.conf.template” file in this format:

modulename= Module-Description | module-dependency

For example for the “xcap” module:

xcap= XCAP utility functions for OpenSIPS. | libxml-dev

  • Add the module name to “exclude_modules” list in “Makefile.conf.template” file.
  • After this compile the module as explained in this article.

3- Each OpenSIPS ‘s module has a section in its documentation called “Dependencies -External Libraries” which contains the external libraries that must be installed for this module.


Next

I will continue explaining the module interface.

More Information


Linux Tuning For SIP Routers – Part 3 (Memory)

.

Introduction

This is Part  3 of “Linux Tuning For SIP Routers” topic. In Part 1 i have talked about Interrupts and IRQ Tuning. In Part 2 i have talked about File System Tuning, Journaling File System, and Swappiness . In this part i will talk about OOM killer, Private Momory, and Shared Memory Tuning.


Out-Of-Memory (OOM) Killer

Linux kernel uses Out-Of-Memory (OOM) killing technique to recover the memory in “out-of-memory” situation. The OOM killer will select one or more processes to kill. You can make a specific process less likely to be killed by the OOM killer as following:

# echo -15 /proc/PID/oom_adj  where the PID is the process ID and oom_adj represents how likely is the process to be killed. oom_adj takes values between -16 to +15. Setting oom_adj to -17 exempts the process entirely from the OOM killer.

To know how likely is a process to be killed, view the content of the file “oom_score”:

# cat /proc/PID/oom_score


Private Memory (Per Process Memory) Settings

Check if there is any memory limitation for a user process. The command “ulimit -a” display the limitations on all resources not only the memory. Use the option -m to display the max memory size:

# ulimit -m

The output: unlimited

The command “ulimit” is built into the bash shell and used for read and write the limitations on the resources available to the shell and the processes started by it. The system must allow such control on the resources.

What remained is configuring the private memory of SIP router’s process. See the example below:

Example: Tuning the Private Memory of OpenSIPS Process

OpenSIPS private memory (file: mem/mem.h) is a memory dedicated to each process and used for process own business like building buffer for sending SIP message. The default size of private memory used by each OpenSIPS process is 1 MB. Common cases that need large private memory are loading user location contacts and doing NAT pinging. You can set the size of private memory (in megabytes) at startup by using the command parameter -M:

# ./opensips -M 10


Shared Memory Settings

The shared memory is memory shared between processes. The data that are stored  in the shared memory can be accessed by many processes. You need to care about shared memory if your SIP router is multi process and the processes/threads from different processes share some data. To see the shared memory settings in your system, type: # ipcs -lm

—— Shared Memory Limits ——–
max number of segments = 4096
max seg size (kbytes) = 32768
max total shared memory (kbytes) = 8388608
min seg size (bytes) = 1

Usually the SIP router has its own memory manager which allocates and frees the memory offered by the operating system between its processes. So tuning the memory involves tuning the memory at the operating system level and at the router level. Linux as an operating system has some parameters that you can configure:

SHMMAX Parameter

It is the maximum size (in bytes) of a single shared segment that a Linux process can allocate in its virtual address space. The value of this parameter depends on whether you are using 32 bit system or 64 bit system (size of the available address space).

What to do

  • Check your system (# uname -a): I am using Fedora Red Hat SMP kernel on 64 bit platform (X86-64) so the maximum size of a shared memory segment theoretically is 2^64bytes. This is correspond to all physical RAM that you have.
  • As a test case, I will set the value of the SHMMAX parameter to “1/4 of physical RAM”. I have 4 GB of RAM so I will set SHMMAX value to 1*1024*1024*1024=1,073,741,824 (bytes):
    • Temporarily:   echo 1073741824> /proc/sys/kernel/shmmax
    • Permanently: Edit the file “/etc/sysctl.conf” and set the entry: kernel.shmmax = 1073741824. This change can be reflected without restart by executing: “/sbin/sysctl -p“.

SHMALL Parameter

It is the total amount of shared memory pages that can be used system wide. Hence, SHMALL should always be at least SHMMAX / PAGE_SIZE. Execute “getconf PAGE_SIZE” to get the page size. In my case the page size is 4096 So the SHMALL value will be 1073741824/ 4096 = 262,144.

After configuring what the operating system allocate to your SIP router application, we need to configure the application itself. The following is an example:

Example: Tuning the Shared Memory of OpenSIPS SIP Router

OpenSIPS 1.X is pure multi process SIP server (version 2.x is multi threads). It stores the state of the calls (SIP transactions or SIP dialogs) in the shared memory accessed by all its processes (file: mem/shm_mem.h). Lets say the SHMEM_SIZE is the size of the shared memory. The recommended value of SHMEM_SIZE depends on the complexity of the routing script. When you run OpenSIPS, you can specify the entire shared memory (in MB) by using the command parameter -m. Ideally it is preferable to have SHMEM_SIZE equal to SHMMAX kernel parameter. This means OpenSIPS shared memory will fit in one shared memory segment. This leads to less job to memory manager and less memory errors. If  SHMEM_SIZE > SHMMAX, this means OpenSIPS memory manager will take more than one segment and manage access to them between all processes.
Here i will show you how to set OpenSIPS shared memory to 1 GB (SHMEM_SIZE = SHMMAX):

# ./opensips -m 1024 (1GB * 1024= 1024 MB)

  • Test the shared memory: You can get some statistics about the shared memory used and managed by OpenSIPS by sending FIFO commands to OpenSIPS. Have a look here on the SHMEM class. Use also system commands like “free” (MemShared or Shmem value) to get information about the memory.
  • The size of the recommended shared memory depends on the complexity of the routing script. So do the test many times for different values and choose the best for your system. Don’t forget to tune the value of  SHMMAX kernel parameter.

Note: Configure both the private memory (-M parameter) and the shared memory (-m parameter) at OpenSIPS startup. Test for different values and choose the one which suitable to your routing script.


Semaphores Settings

Semaphores are like counters that are used for controlling access by multiple processes to shared resources like shared memories. To see all semaphores settings: # ipcs -ls

—— Semaphore Limits ——–
max number of arrays = 128                      # SEMMNI Parameter
max semaphores per array = 250               # SEMMSL Parameter
max semaphores system wide = 32000      # SEMMNS Parameter
max ops per semop call = 32                     # SEMOPM Parameter
semaphore max value = 32767

or you can do this: # cat /proc/sys/kernel/sem

The output will be in this order (SEMMSL, SEMMNS, SEMOPM, SEMMNI):

250     32000   32      128

Tuning these values also depends on the complexity of the routing script. More complexity –> More loaded modules (shared libraries) –> more sharing –> more need for tuning the synchronization between processes.

Keep these values AS IS  if you don’t have problems with performance. To change these values temporarily:

# echo 250 32000 100 128 > /proc/sys/kernel/sem  

OR 

# sysctl -w kernel.sem=”250 32000 100 128″

To permanently change these values, Edit the file “/etc/sysctl.conf” and override the settings: kernel.sem=250 32000 100 128

Check The System Limits using “ulimit” System Property

Follow these steps to monitor the resources offered by the system to specific process:

  • Get the ID of the proess using # ps -A | grep …
  • Execute this: # cat /proc/$ProcessID/limits. Replace the ProcessID with the ID you get using the “ps” above.

The output will look like this:

Limit                     Soft Limit           Hard Limit           Units
Max cpu time              unlimited            unlimited            seconds
Max file size             unlimited            unlimited            bytes
Max data size             unlimited            unlimited            bytes
Max stack size            8388608              unlimited            bytes
Max core file size        0                    unlimited            bytes
Max resident set          unlimited            unlimited            bytes
Max processes             511830               511830               processes
Max open files            1024                 1024                 files
Max locked memory         65536                65536                bytes
Max address space         unlimited            unlimited            bytes
Max file locks            unlimited            unlimited            locks
Max pending signals       511830               511830               signals
Max msgqueue size         819200               819200               bytes
Max nice priority         0                    0
Max realtime priority     0                    0
Max realtime timeout      unlimited            unlimited            us


Next

After tuning the memory of the operating system, you need to care about the memory of  the SIP application itself. Here is an example (OpenSIPS Memory Debugging) .In the Next Part, I will talk about network tuning to optimize SIP Routers.