Merge pull request #1610 from armenb/armenb/loglevel_then_log_prefix
authorDaniel-Constantin Mierla <miconda@gmail.com>
Tue, 14 Aug 2018 07:56:30 +0000 (09:56 +0200)
committerGitHub <noreply@github.com>
Tue, 14 Aug 2018 07:56:30 +0000 (09:56 +0200)
core: print log_prefix after loglevel for more intuitive parsing

45 files changed:
pkg/kamailio/deb/bionic/control
pkg/kamailio/deb/buster/control
pkg/kamailio/deb/debian/control
pkg/kamailio/deb/jessie/control
pkg/kamailio/deb/precise/control
pkg/kamailio/deb/sid/control
pkg/kamailio/deb/stretch/control
pkg/kamailio/deb/trusty/control
pkg/kamailio/deb/wheezy/control
pkg/kamailio/deb/xenial/control
pkg/kamailio/obs/kamailio.spec
src/Makefile.groups
src/core/dset.c
src/core/parser/parse_fline.c
src/core/parser/parse_fline.h
src/modules/app_lua/README
src/modules/app_lua/doc/app_lua_admin.xml
src/modules/blst/blst.c
src/modules/db_mysql/km_my_con.c
src/modules/db_mysql/my_fld.h
src/modules/dialog/dialog.c
src/modules/dialog/dlg_cb.c
src/modules/ims_ipsec_pcscf/Makefile [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/README [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/cmd.c [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/cmd.h [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/doc/Makefile [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf.xml [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/ipsec.c [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/ipsec.h [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/run_spi_list_tests.sh [new file with mode: 0755]
src/modules/ims_ipsec_pcscf/spi_gen.c [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/spi_gen.h [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/spi_list.c [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/spi_list.h [new file with mode: 0644]
src/modules/ims_ipsec_pcscf/spi_list_tests.c [new file with mode: 0644]
src/modules/presence/subscribe.c
src/modules/pv/pv_core.c
src/modules/smsops/smsops_impl.c
src/modules/tmx/tmx_mod.c
src/modules/uac/README
src/modules/uac/doc/uac_admin.xml
src/modules/usrloc/ul_rpc.c

index 8bcbae9..885b4ab 100644 (file)
@@ -27,6 +27,7 @@ Build-Depends:
  liblua5.1-0-dev,
  libmaxminddb-dev,
  libmemcached-dev,
+ libmnl-dev,
  libmongoc-dev,
  libmono-2.0-dev,
  libncurses5-dev,
index 8bcbae9..885b4ab 100644 (file)
@@ -27,6 +27,7 @@ Build-Depends:
  liblua5.1-0-dev,
  libmaxminddb-dev,
  libmemcached-dev,
+ libmnl-dev,
  libmongoc-dev,
  libmono-2.0-dev,
  libncurses5-dev,
index 820e230..9110f8e 100644 (file)
@@ -29,6 +29,7 @@ Build-Depends:
  libmaxminddb-dev,
  libmemcached-dev,
  libmono-2.0-dev,
+ libmnl-dev,
  libncurses5-dev,
  libpcre3-dev,
  libperl-dev,
index fe72220..4e1c0d4 100644 (file)
@@ -27,6 +27,7 @@ Build-Depends:
  libldap2-dev,
  liblua5.1-0-dev,
  libmemcached-dev,
+ libmnl-dev,
  libmono-2.0-dev,
  libmysqlclient-dev,
  libncurses5-dev,
index b334b05..fec8c75 100644 (file)
@@ -25,6 +25,7 @@ Build-Depends:
  libldap2-dev,
  liblua5.1-0-dev,
  libmemcached-dev,
+ libmnl-dev,
  libmono-2.0-dev,
  libmysqlclient-dev,
  libncurses5-dev,
index 8bcbae9..885b4ab 100644 (file)
@@ -27,6 +27,7 @@ Build-Depends:
  liblua5.1-0-dev,
  libmaxminddb-dev,
  libmemcached-dev,
+ libmnl-dev,
  libmongoc-dev,
  libmono-2.0-dev,
  libncurses5-dev,
index a8498f6..b663caf 100644 (file)
@@ -29,6 +29,7 @@ Build-Depends:
  liblua5.1-0-dev,
  libmaxminddb-dev,
  libmemcached-dev,
+ libmnl-dev,
  libmongoc-dev,
  libmono-2.0-dev,
  libncurses5-dev,
index 0d15f09..0ae8caa 100644 (file)
@@ -26,6 +26,7 @@ Build-Depends:
  libldap2-dev,
  liblua5.1-0-dev,
  libmemcached-dev,
+ libmnl-dev,
  libmono-2.0-dev,
  libmysqlclient-dev,
  libncurses5-dev,
index b334b05..fec8c75 100644 (file)
@@ -25,6 +25,7 @@ Build-Depends:
  libldap2-dev,
  liblua5.1-0-dev,
  libmemcached-dev,
+ libmnl-dev,
  libmono-2.0-dev,
  libmysqlclient-dev,
  libncurses5-dev,
index 44e399b..c2397dd 100644 (file)
@@ -29,6 +29,7 @@ Build-Depends:
  liblua5.1-0-dev,
  libmaxminddb-dev,
  libmemcached-dev,
+ libmnl-dev,
  libmongoc-dev,
  libmono-2.0-dev,
  libmysqlclient-dev,
index cc53dc9..c7ba3ce 100644 (file)
@@ -578,8 +578,8 @@ Requires:   kamailio = %ver
 Requires:   perl
 BuildRequires:  perl
 %else
-Requires:   mod_perl
-BuildRequires:  mod_perl-devel
+Requires:   perl-libs
+BuildRequires:  perl-ExtUtils-Embed
 %endif
 
 %description    perl
index 8274291..0c53863 100644 (file)
@@ -164,7 +164,8 @@ mod_list_mono=app_mono
 # - modules related to IMS extensions
 mod_list_ims=cdp cdp_avp ims_dialog ims_auth ims_isc ims_icscf ims_qos \
                           ims_registrar_pcscf ims_registrar_scscf ims_usrloc_pcscf \
-                          ims_usrloc_scscf ims_charging ims_ocs ims_diameter_server
+                          ims_usrloc_scscf ims_charging ims_ocs ims_diameter_server \
+                          ims_ipsec_pcscf
 
 # - modules depending on osp toolkit library
 mod_list_osp=osp
index 06083e7..1a10f2a 100644 (file)
@@ -763,7 +763,8 @@ int uri_add_rcv_alias(sip_msg_t *msg, str *uri, str *nuri)
        /*uri;alias=[ip]~port~proto*/
        len = uri->len+ip.len+port.len+12;
        if(len>=nuri->len) {
-               LM_ERR("not enough space for new uri: %d\n", len);
+               LM_ERR("not enough space - new uri len: %d (buf size: %d)\n",
+                               len, nuri->len);
                return -1;
        }
        p = nuri->s;
index 04758b2..e6b93ac 100644 (file)
@@ -125,6 +125,11 @@ char* parse_first_line(char* buffer, unsigned int len, struct msg_start* fl)
        else IFISMETHOD( UPDATE, 'U')
        else IFISMETHOD( REFER, 'R')
        else IFISMETHOD( PUBLISH, 'P')
+       else IFISMETHOD( KDMQ, 'K')
+       else IFISMETHOD( GET, 'G')
+       else IFISMETHOD( POST, 'P')
+       else IFISMETHOD( PUT, 'P')
+       else IFISMETHOD( DELETE, 'D')
        /* if you want to add another method XXX, include METHOD_XXX in
            H-file (this is the value which you will take later in
            processing and define XXX_LEN as length of method name;
index b10c02b..2add9b0 100644 (file)
 #define UPDATE_LEN 6
 #define REFER_LEN 5
 #define PUBLISH_LEN 7
+#define KDMQ_LEN 4
+#define GET_LEN 3
+#define POST_LEN 4
+#define PUT_LEN 3
+#define DELETE_LEN 6
 
 typedef struct msg_start {
        short type;                                     /*!< Type of the message - request/response */
index cac548c..3b42392 100644 (file)
@@ -33,7 +33,7 @@ Daniel-Constantin Mierla
 
               4.1. lua_dofile(path)
               4.2. lua_dostring(script)
-              4.3. lua_run(function, params)
+              4.3. lua_run(func [, params])
               4.4. lua_runstring(script)
 
         5. RPC Commands
@@ -75,7 +75,7 @@ Chapter 1. Admin Guide
 
         4.1. lua_dofile(path)
         4.2. lua_dostring(script)
-        4.3. lua_run(function, params)
+        4.3. lua_run(func [, params])
         4.4. lua_runstring(script)
 
    5. RPC Commands
@@ -213,7 +213,7 @@ modparam("app_lua", "reload", 0)
 
    4.1. lua_dofile(path)
    4.2. lua_dostring(script)
-   4.3. lua_run(function, params)
+   4.3. lua_run(func [, params])
    4.4. lua_runstring(script)
 
 4.1.  lua_dofile(path)
@@ -239,21 +239,24 @@ if(!lua_dostring("sr.log([[err]], [[----------- Hello World from $fU\n]])"))
 }
 ...
 
-4.3.  lua_run(function, params)
+4.3.  lua_run(func [, params])
 
-   Execute the Lua function 'func' giving params as parameters. There can
-   be up to 3 string parameters. The function must exist in the script
+   Execute the Lua function 'func' giving 'params' as parameters. There
+   can be up to 3 string parameters after 'func' (overall, max 4 params,
+   first is the function). The function must exist in the Lua script
    loaded at startup via parameter 'load'. Parameters can be strings with
    pseudo-variables that are evaluated at runtime.
 
    Example 1.7. lua_run usage
 ...
-if(!lua_run("sr_append_fu_to_reply"))
-{
+if(!lua_run("sr_append_fu_to_reply")) {
     xdbg("SCRIPT: failed to execute lua function!\n");
 }
 ...
-lua_run("lua_funcx", "$rU", "2");
+lua_run("lua_funcx0");
+lua_run("lua_funcx1", "$rU");
+lua_run("lua_funcx2", "$rU", "2");
+lua_run("lua_funcx3", "$rU", "2", "$si");
 ...
 
 4.4.  lua_runstring(script)
index 45ed3e2..74ea68e 100644 (file)
@@ -334,11 +334,12 @@ if(!lua_dostring("sr.log([[err]], [[----------- Hello World from $fU\n]])"))
 
        <section id="app_lua.f.lua_run">
            <title>
-               <function moreinfo="none">lua_run(function, params)</function>
+               <function moreinfo="none">lua_run(func [, params])</function>
            </title>
            <para>
-               Execute the Lua function 'func' giving params as parameters. There
-               can be up to 3 string parameters. The function must exist in the
+               Execute the Lua function 'func' giving 'params' as parameters. There
+               can be up to 3 string parameters after 'func' (overall, max 4 params,
+               first is the function). The function must exist in the Lua
                script loaded at startup via parameter 'load'. Parameters can be
                strings with pseudo-variables that are evaluated at runtime.
            </para>
@@ -346,12 +347,14 @@ if(!lua_dostring("sr.log([[err]], [[----------- Hello World from $fU\n]])"))
                <title><function>lua_run</function> usage</title>
                <programlisting format="linespecific">
 ...
-if(!lua_run("sr_append_fu_to_reply"))
-{
+if(!lua_run("sr_append_fu_to_reply")) {
     xdbg("SCRIPT: failed to execute lua function!\n");
 }
 ...
-lua_run("lua_funcx", "$rU", "2");
+lua_run("lua_funcx0");
+lua_run("lua_funcx1", "$rU");
+lua_run("lua_funcx2", "$rU", "2");
+lua_run("lua_funcx3", "$rU", "2", "$si");
 ...
 </programlisting>
            </example>
index 5fd654f..a3bbdb6 100644 (file)
@@ -105,7 +105,7 @@ static int ki_blst_add(sip_msg_t* msg, int t)
 {
 #ifdef USE_DST_BLACKLIST
        struct dest_info src;
-       
+
        if (likely(cfg_get(core, core_cfg, use_dst_blacklist))){
                if (t==0)
                        t=cfg_get(core, core_cfg, blst_timeout);
@@ -165,7 +165,7 @@ static int ki_blst_add_retry_after(sip_msg_t* msg, int t_min, int t_max)
        int t;
        struct dest_info src;
        struct hdr_field* hf;
-       
+
        if (likely(cfg_get(core, core_cfg, use_dst_blacklist))){
                init_dest_info(&src);
                src.send_sock=0;
@@ -173,7 +173,7 @@ static int ki_blst_add_retry_after(sip_msg_t* msg, int t_min, int t_max)
                src.id=msg->rcv.proto_reserved1;
                src.proto=msg->rcv.proto;
                t=-1;
-               if ((parse_headers(msg, HDR_RETRY_AFTER_F, 0)==0) && 
+               if ((parse_headers(msg, HDR_RETRY_AFTER_F, 0)==0) &&
                        (msg->parsed_flag & HDR_RETRY_AFTER_F)){
                        for (hf=msg->headers; hf; hf=hf->next)
                                if (hf->type==HDR_RETRY_AFTER_T){
@@ -184,7 +184,7 @@ static int ki_blst_add_retry_after(sip_msg_t* msg, int t_min, int t_max)
                }
                if (t<0)
                        return -1;
-               
+
                t=MAX_unsigned(t, t_min);
                t=MIN_unsigned(t, t_max);
                if (likely(t))
@@ -229,9 +229,9 @@ static int ki_blst_del(sip_msg_t* msg)
 {
 #ifdef USE_DST_BLACKLIST
        struct dest_info src;
-       
+
        if (likely(cfg_get(core, core_cfg, use_dst_blacklist))){
-       
+
                init_dest_info(&src);
                src.send_sock=0;
                src.to=msg->rcv.src_su;
@@ -266,7 +266,7 @@ static int ki_blst_is_blacklisted(sip_msg_t* msg)
 {
 #ifdef USE_DST_BLACKLIST
        struct dest_info src;
-       
+
        if (likely(cfg_get(core, core_cfg, use_dst_blacklist))){
                init_dest_info(&src);
                src.send_sock=0;
@@ -542,4 +542,4 @@ int mod_register(char *path, int *dlflags, void *p1, void *p2)
 {
        sr_kemi_modules_add(sr_kemi_blst_exports);
        return 0;
-}
\ No newline at end of file
+}
index 38e6875..242329e 100644 (file)
@@ -29,6 +29,7 @@
 #include "km_my_con.h"
 #include "km_db_mysql.h"
 #include <mysql.h>
+#include <stdbool.h>
 #include "../../core/mem/mem.h"
 #include "../../core/dprint.h"
 #include "../../core/ut.h"
@@ -44,7 +45,7 @@ struct my_con* db_mysql_new_connection(const struct db_id* id)
        char *host, *grp, *egrp;
        unsigned int connection_flag = 0;
 #if MYSQL_VERSION_ID > 50012
-       my_bool rec;
+       bool rec;
 #endif
 
        if (!id) {
index 4450a26..539a4df 100644 (file)
 #include "../../lib/srdb2/db_drv.h"
 #include "../../lib/srdb2/db_fld.h"
 #include <mysql.h>
+#include <stdbool.h>
 
 struct my_fld {
        db_drv_t gen;
 
        char* name;
-       my_bool is_null;
+       bool is_null;
        MYSQL_TIME time;
        unsigned long length;
        str buf;
index 84262ea..a7efe2d 100644 (file)
@@ -711,7 +711,6 @@ static int mod_init(void)
                        LM_ERR("failed to initialize the DB support\n");
                        return -1;
                }
-               run_load_callbacks();
        }
 
        destroy_dlg_callbacks( DLGCB_LOADED );
@@ -742,6 +741,13 @@ static int child_init(int rank)
 {
        dlg_db_mode = dlg_db_mode_param;
 
+
+       if(rank==PROC_INIT) {
+               if (dlg_db_mode!=DB_MODE_NONE) {
+                       run_load_callbacks();
+               }
+       }
+
        if(rank==PROC_MAIN) {
                if(dlg_timer_procs>0) {
                        if(fork_sync_timer(PROC_TIMER, "Dialog Main Timer", 1 /*socks flag*/,
index 6ba4851..1294699 100644 (file)
@@ -133,7 +133,7 @@ int register_dlgcb(struct dlg_cell *dlg, int types, dialog_cb f,
        cb->callback_param_free = ff;
 
        if ( types==DLGCB_CREATED ) {
-               if (load_cbs==POINTER_CLOSED_MARKER) {
+               if (create_cbs==POINTER_CLOSED_MARKER) {
                        LM_CRIT("DLGCB_CREATED type registered after shutdown!?!\n");
                        goto error;
                }
diff --git a/src/modules/ims_ipsec_pcscf/Makefile b/src/modules/ims_ipsec_pcscf/Makefile
new file mode 100644 (file)
index 0000000..f674898
--- /dev/null
@@ -0,0 +1,20 @@
+#
+# registrar module makefile
+#
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=ims_ipsec_pcscf.so
+
+ifneq ($(OS),darwin)
+       LIBS += -lpthread
+       LIBS += -lmnl
+endif
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/ims/kamailio_ims
+include ../../Makefile.modules
diff --git a/src/modules/ims_ipsec_pcscf/README b/src/modules/ims_ipsec_pcscf/README
new file mode 100644 (file)
index 0000000..0d354ee
--- /dev/null
@@ -0,0 +1,230 @@
+The IMS IPSec-Registrar Module
+
+Dragos Vingarzan
+
+   FhG Fokus
+   <Dragos.Vingarzan@fokus.fraunhofer.de>
+
+Jason Penton
+
+   Smile Communications
+   <jason.penton@smilecoms.com>
+
+Richard Good
+
+   Smile Communications
+   <richard.good@smilecoms.com>
+
+Carsten Bock
+
+   ng-voice GmbH
+   <carsten@ng-voice.com>
+
+Tsvetomir Dimitrov
+
+   <tsv.dimitrov@gmail.com>
+
+   Copyright © 2007 FhG FOKUS
+
+   Copyright © 2012 Smile Communications
+
+   Copyright © 2015 ng-voice GmbH
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1. Overview
+        2. Dependencies
+
+              2.1. Kamailio Modules
+              2.2. External Libraries or Applications
+
+        3. Parameters
+
+              3.1. ipsec_listen_addr (string)
+              3.2. ipsec_client_port (int)
+              3.3. ipsec_server_port (int)
+              3.4. ipsec_spi_id_start (int)
+              3.5. ipsec_spi_id_range (int)
+
+        4. Functions
+
+              4.1. ipsec_create(domain)
+              4.2. ipsec_forward(domain)
+              4.3. ipsec_destroy(domain)
+
+   List of Examples
+
+   1.1. ipsec_listen_addr parameter usage
+   1.2. ipsec_client_port parameter usage
+   1.3. ipsec_server_port parameter usage
+   1.4. ipsec_spi_id_start parameter usage
+   1.5. ipsec_spi_id_range parameter usage
+   1.6. ipsec_create
+   1.7. ipsec_forward
+   1.8. ipsec_forward
+
+Chapter 1. Admin Guide
+
+   Table of Contents
+
+   1. Overview
+   2. Dependencies
+
+        2.1. Kamailio Modules
+        2.2. External Libraries or Applications
+
+   3. Parameters
+
+        3.1. ipsec_listen_addr (string)
+        3.2. ipsec_client_port (int)
+        3.3. ipsec_server_port (int)
+        3.4. ipsec_spi_id_start (int)
+        3.5. ipsec_spi_id_range (int)
+
+   4. Functions
+
+        4.1. ipsec_create(domain)
+        4.2. ipsec_forward(domain)
+        4.3. ipsec_destroy(domain)
+
+1. Overview
+
+   This module contains methods for IPSec initialisation/deinitialisation
+   related for usage of Kamailio as a Proxy-CSCF.
+
+2. Dependencies
+
+   2.1. Kamailio Modules
+   2.2. External Libraries or Applications
+
+2.1. Kamailio Modules
+
+   The Following modules must be loaded before this module:
+     * Usrloc PCSCF
+     * TM
+
+2.2. External Libraries or Applications
+
+   This modules requires the internal IMS library and libmnl for operating
+   with netlink sockets.
+
+3. Parameters
+
+   3.1. ipsec_listen_addr (string)
+   3.2. ipsec_client_port (int)
+   3.3. ipsec_server_port (int)
+   3.4. ipsec_spi_id_start (int)
+   3.5. ipsec_spi_id_range (int)
+
+3.1. ipsec_listen_addr (string)
+
+   IP address which the Proxy-CSCF will use for incoming/outgoing SIP
+   traffic over IPSec.
+
+   Default value is "127.0.0.1"
+
+   Example 1.1. ipsec_listen_addr parameter usage
+...
+modparam("ims_ipsec_pcscf", "ipsec_listen_addr", "127.0.0.1")
+...
+
+3.2. ipsec_client_port (int)
+
+   Port number which will be bound for incoming (server) IPSec traffic.
+
+   Default value is 5963.
+
+   Example 1.2. ipsec_client_port parameter usage
+...
+modparam("ims_ipsec_pcscf", "ipsec_client_port", 5062)
+...
+
+3.3. ipsec_server_port (int)
+
+   Port number which will be bound for incoming (server) IPSec traffic.
+
+   Default value is 5063.
+
+   Example 1.3. ipsec_server_port parameter usage
+...
+modparam("ims_ipsec_pcscf", "ipsec_server_port", 5063)
+...
+
+3.4. ipsec_spi_id_start (int)
+
+   Each IPSec tunnel has a unique system-wide identifier. This and the
+   following option allows to tune the SPIs used by Kamailio in order to
+   avoid collisions with other IPSec useres. If Kamailio is the only
+   process on the system which uses IPSec, don't bother with this option.
+
+   Default value is 100.
+
+   Example 1.4. ipsec_spi_id_start parameter usage
+...
+modparam("ims_ipsec_pcscf", "ipsec_spi_id_start", 100)
+...
+
+3.5. ipsec_spi_id_range (int)
+
+   How many SPIs to be allocated for the process. E.g. if
+   ipsec_spi_id_start = 100 and ipsec_spi_id_range = 1000, SPIs between
+   100 and 1100 will be used.
+
+   Default value is 1000.
+
+   Example 1.5. ipsec_spi_id_range parameter usage
+...
+modparam("ims_ipsec_pcscf", "ipsec_spi_id_range", 1000)
+...
+
+4. Functions
+
+   4.1. ipsec_create(domain)
+   4.2. ipsec_forward(domain)
+   4.3. ipsec_destroy(domain)
+
+4.1. ipsec_create(domain)
+
+   This function creates IPSec SA and Policy based on the parameters sent
+   in Security-Client header in the REGISTER message. It's called when OK
+   is received. The function also adds Security-Server header to the
+   REGISTER.
+
+   Meaning of the parameters is as follows:
+     * domain - Logical domain within the registrar. If a database is used
+       then this must be name of the table which stores the contacts.
+
+   Example 1.6. ipsec_create
+...
+ipsec_create("location");
+...
+
+4.2. ipsec_forward(domain)
+
+   The function processes redirects outgoing message via the IPSec tunnel
+   initiated with ipsec_create().
+
+   Meaning of the parameters is as follows:
+     * domain - Logical domain within the registrar. If a database is used
+       then this must be name of the table which stores the contacts.
+
+   Example 1.7. ipsec_forward
+...
+ipsec_forward("location");
+...
+
+4.3. ipsec_destroy(domain)
+
+   The function destroys IPSec tunnel, created with ipsec_create.
+
+   Meaning of the parameters is as follows:
+     * domain - Logical domain within the registrar. If a database is used
+       then this must be name of the table which stores the contacts.
+
+   Example 1.8. ipsec_forward
+...
+ipsec_destroy("location");
+...
diff --git a/src/modules/ims_ipsec_pcscf/cmd.c b/src/modules/ims_ipsec_pcscf/cmd.c
new file mode 100644 (file)
index 0000000..b5741e3
--- /dev/null
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
+ * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
+ *
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ *
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus.
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "../../core/parser/msg_parser.h"
+#include "../../core/socket_info.h"
+#include "../../lib/ims/ims_getters.h"
+#include "../../modules/tm/tm_load.h"
+#include "../ims_usrloc_pcscf/usrloc.h"
+
+#include "ipsec.h"
+#include "spi_gen.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+
+extern str ipsec_listen_addr;
+extern short ipsec_listen_port;
+extern short ipsec_server_port;
+extern short ipsec_client_port;
+
+// check http://www.asipto.com/pub/kamailio-devel-guide/#c16return_values
+const int IPSEC_CMD_FAIL = -1;
+const int IPSEC_CMD_SUCCESS = 1;
+
+extern usrloc_api_t ul;
+extern struct tm_binds tmb;
+
+
+static str get_www_auth_param(const char* param_name, str www_auth)
+{
+    str val = {0,0};
+    int i = 0;
+    int param_len = strlen(param_name);
+    int start = 0;
+    int end = 0;
+
+    for(i = 0; i < www_auth.len; i++) {
+        if (www_auth.s[i] == ' ') continue;
+
+        if(strncasecmp(www_auth.s+i, param_name, param_len) == 0) {
+            i += param_len;
+
+            //find first double quote
+            while(www_auth.s[i] != '"' && i < www_auth.len) i++;
+            i++; //and skip it
+
+            if (i == www_auth.len)
+                return val; //error
+            start = i;
+            i++;
+
+            //find second double quote
+            while(www_auth.s[i] != '"' && i < www_auth.len) i++;
+            if (i == www_auth.len)
+                return val; //error
+            end = i;
+            i++;
+
+            val.s = www_auth.s + start;
+            val.len = end - start;
+            break;
+        }
+
+        //parameter not relevant - fast forward
+        do { i++; } while (www_auth.s[i] != ',' && i < www_auth.len);
+    }
+
+    return val;
+}
+
+
+static int fill_contact(struct pcontact_info* ci, struct sip_msg* m)
+{
+    contact_body_t* cb = NULL;
+    struct via_body* vb = NULL;
+    unsigned short port, proto = 0;
+    struct sip_msg* req = NULL;
+
+
+    if(!ci) {
+        LM_ERR("fill_contact() called with null ptr\n");
+        return -1;
+    }
+
+    memset(ci, 0, sizeof(struct pcontact_info));
+
+
+    if(m->first_line.type == SIP_REQUEST) {
+        struct sip_uri uri;
+        memset(&uri, 0, sizeof(struct sip_uri));
+
+        if(parse_uri(m->first_line.u.request.uri.s, m->first_line.u.request.uri.len, &uri)) {
+            LM_ERR("Can't parse the request URI from first line\n");
+            return -1;
+        }
+
+        // populate host,port, aor in CI
+        ci->via_host = uri.host;
+        ci->via_port = uri.port_no ? uri.port_no : 5060;
+        ci->via_prot = proto;
+        ci->aor = m->first_line.u.request.uri;
+
+        req = m;
+    }
+    else if(m->first_line.type == SIP_REPLY) {
+
+        cb = cscf_parse_contacts(m);
+        vb = cscf_get_ue_via(m);
+        port = vb->port?vb->port:5060;
+        proto = vb->proto;
+
+        struct cell *t = tmb.t_gett();
+        if (!t || t == (void*) -1) {
+            LM_ERR("fill_contact(): Reply without transaction\n");
+            return -1;
+        }
+
+        req = t->uas.request;
+
+        cb = cscf_parse_contacts(req);
+        if (!cb || (!cb->contacts)) {
+            LM_ERR("fill_contact(): No contact headers\n");
+            return -3;
+        }
+
+        // populate CI with bare minimum
+        ci->via_host = vb->host;
+        ci->via_port = port;
+        ci->via_prot = proto;
+        ci->aor = cb->contacts->uri;
+    }
+
+
+    char* srcip;
+    srcip = pkg_malloc(50);
+
+    ci->received_host.len = ip_addr2sbuf(&req->rcv.src_ip, srcip, 50);
+    ci->received_host.s = srcip;
+    ci->received_port = req->rcv.src_port;
+    ci->received_proto = req->rcv.proto;
+
+    // Set to default, if not set:
+    if (ci->received_port == 0)
+        ci->received_port = 5060;
+
+
+    return 0;
+}
+
+// Get CK and IK from WWW-Authenticate
+static int get_ck_ik(const struct sip_msg* m, str* ck, str* ik)
+{
+    struct hdr_field *www_auth_hdr = NULL;
+    str www_auth;
+    memset(&www_auth, 0, sizeof(str));
+
+    www_auth = cscf_get_authenticate((sip_msg_t*)m, &www_auth_hdr);
+
+    *ck = get_www_auth_param("ck", www_auth);
+    if (ck->len == 0) {
+        LM_ERR("Error getting CK\n");
+        return -1;
+    }
+
+    *ik = get_www_auth_param("ik", www_auth);
+    if (ck->len == 0) {
+        LM_ERR("Error getting IK\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int update_contact_ipsec_params(ipsec_t* s, const struct sip_msg* m)
+{
+    // Get CK and IK
+    str ck, ik;
+    if(get_ck_ik(m, &ck, &ik) != 0) {
+        return -1;
+    }
+
+    // Save CK and IK in the contact
+    s->ck.s = shm_malloc(ck.len);
+    if(s->ck.s == NULL) {
+        LM_ERR("Error allocating memory for CK\n");
+        return -1;
+    }
+    memcpy(s->ck.s, ck.s, ck.len);
+    s->ck.len = ck.len;
+
+    s->ik.s = shm_malloc(ik.len);
+    if(s->ik.s == NULL) {
+        LM_ERR("Error allocating memory for IK\n");
+        shm_free(s->ck.s);
+        s->ck.s = NULL; s->ck.len = 0;
+        s->ik.s = NULL; s->ik.len = 0;
+        return -1;
+    }
+    memcpy(s->ik.s, ik.s, ik.len);
+    s->ik.len = ik.len;
+
+    // Generate SPI
+    if((s->spi_pc = acquire_spi()) == 0) {
+        LM_ERR("Error generating client SPI for IPSEC tunnel creation\n");
+        shm_free(s->ck.s);
+        s->ck.s = NULL; s->ck.len = 0;
+        shm_free(s->ik.s);
+        s->ik.s = NULL; s->ik.len = 0;
+        return -1;
+    }
+
+    if((s->spi_ps = acquire_spi()) == 0) {
+        LM_ERR("Error generating server SPI for IPSEC tunnel creation\n");
+        shm_free(s->ck.s);
+        s->ck.s = NULL; s->ck.len = 0;
+        shm_free(s->ik.s);
+        s->ik.s = NULL; s->ik.len = 0;
+        return -1;
+    }
+
+    return 0;
+}
+
+static int create_ipsec_tunnel(const str remote_addr, ipsec_t* s)
+{
+    struct mnl_socket* sock = init_mnl_socket();
+    if (sock == NULL) {
+        return -1;
+    }
+
+    LM_DBG("Creating security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %.*s; client port %d server port %d\n",
+            ipsec_listen_addr.len, ipsec_listen_addr.s, ipsec_client_port, ipsec_server_port,
+            remote_addr.len, remote_addr.s, s->port_uc, s->port_us);
+
+    // P-CSCF 'client' tunnel to UE 'server'
+    add_sa    (sock, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, s->ck, s->ik);
+    add_policy(sock, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT);
+
+    // UE 'client' to P-CSCF 'server' tunnel
+    add_sa    (sock, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps, s->ck, s->ik);
+    add_policy(sock, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps, IPSEC_POLICY_DIRECTION_IN);
+
+    close_mnl_socket(sock);
+
+    return 0;
+}
+
+static int destroy_ipsec_tunnel(const str remote_addr, ipsec_t* s)
+{
+    struct mnl_socket* sock = init_mnl_socket();
+    if (sock == NULL) {
+        return -1;
+    }
+
+    LM_DBG("Destroying security associations: Local IP: %.*s client port: %d server port: %d; UE IP: %.*s; client port %d server port %d\n",
+            ipsec_listen_addr.len, ipsec_listen_addr.s, ipsec_client_port, ipsec_server_port,
+            remote_addr.len, remote_addr.s, s->port_uc, s->port_us);
+
+    // P-CSCF 'client' tunnel to UE 'server'
+    remove_sa    (sock, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us);
+    remove_policy(sock, ipsec_listen_addr, remote_addr, ipsec_client_port, s->port_us, s->spi_us, IPSEC_POLICY_DIRECTION_OUT);
+
+    // UE 'client' to P-CSCF 'server' tunnel
+    remove_sa    (sock, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps);
+    remove_policy(sock, remote_addr, ipsec_listen_addr, s->port_uc, ipsec_server_port, s->spi_ps, IPSEC_POLICY_DIRECTION_IN);
+
+    // Release SPIs
+    release_spi(s->spi_uc);
+    release_spi(s->spi_us);
+
+
+    close_mnl_socket(sock);
+    return 0;
+}
+
+static void on_expire(struct pcontact *c, int type, void *param)
+{
+    if(type != PCSCF_CONTACT_EXPIRE && type != PCSCF_CONTACT_DELETE) {
+        LM_ERR("Unexpected event type %d\n", type);
+        return;
+    }
+
+
+    if(c->security_temp == NULL) {
+        LM_ERR("No security parameters found in contact\n");
+        return;
+    }
+
+    //get security parameters
+    if(c->security_temp->type != SECURITY_IPSEC ) {
+        LM_ERR("Unsupported security type: %d\n", c->security_temp->type);
+        return;
+    }
+
+    destroy_ipsec_tunnel(c->received_host, c->security_temp->data.ipsec);
+}
+
+int add_supported_secagree_header(struct sip_msg* m)
+{
+    // Add sec-agree header in the reply
+    const char* supported_sec_agree = "Supported: sec-agree\r\n";
+    const int supported_sec_agree_len = 22;
+
+    str* supported = NULL;
+    if((supported = pkg_malloc(sizeof(str))) == NULL) {
+        LM_ERR("Error allocating pkg memory for supported header\n");
+        return -1;
+    }
+
+    if((supported->s = pkg_malloc(supported_sec_agree_len)) == NULL) {
+        LM_ERR("Error allcationg pkg memory for supported header str\n");
+        return -1;
+    }
+    memcpy(supported->s, supported_sec_agree, supported_sec_agree_len);
+    supported->len = supported_sec_agree_len;
+
+    if(cscf_add_header(m, supported, HDR_SUPPORTED_T) != 1) {
+        LM_ERR("Error adding security header to reply!\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int add_security_server_header(struct sip_msg* m, ipsec_t* s)
+{
+    // allocate memory for the header itself
+    str* sec_header = NULL;
+    if((sec_header = pkg_malloc(sizeof(str))) == NULL) {
+        LM_ERR("Error allocating pkg memory for security header\n");
+        return -1;
+    }
+    memset(sec_header, 0, sizeof(str));
+
+    // create a temporary buffer and set the value in it
+    char sec_hdr_buf[1024];
+    memset(sec_hdr_buf, 0, sizeof(sec_hdr_buf));
+    sec_header->len = snprintf(sec_hdr_buf, sizeof(sec_hdr_buf) - 1,
+                                "Security-Server: ipsec-3gpp;prot=esp;mod=trans;spi-c=%d;spi-s=%d;port-c=%d;port-s=%d;alg=%.*s;ealg=%.*s\r\n",
+                                s->spi_pc, s->spi_ps, ipsec_client_port, ipsec_server_port,
+                                s->r_alg.len, s->r_alg.s,
+                                s->r_ealg.len, s->r_ealg.s
+                              );
+
+    // copy to the header and add
+    if((sec_header->s = pkg_malloc(sec_header->len)) == NULL) {
+        LM_ERR("Error allocating pkg memory for security header payload\n");
+        return -1;
+    }
+    memcpy(sec_header->s, sec_hdr_buf, sec_header->len);
+
+    // add security-server header in reply
+    if(cscf_add_header(m, sec_header, HDR_OTHER_T) != 1) {
+        LM_ERR("Error adding security header to reply!\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int ipsec_create(struct sip_msg* m, udomain_t* d)
+{
+    pcontact_t* pcontact = NULL;
+    struct pcontact_info ci;
+    int ret = IPSEC_CMD_FAIL;   // FAIL by default
+
+    // Find the contact
+    if(fill_contact(&ci, m) != 0) {
+        LM_ERR("Error filling in contact data\n");
+        return ret;
+    }
+
+    ul.lock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
+
+    if (ul.get_pcontact(d, &ci, &pcontact) != 0) {
+        LM_ERR("Contact doesn't exist\n");
+        goto cleanup;
+    }
+
+    // Get security parameters
+    if(pcontact->security_temp == NULL) {
+        LM_ERR("No security parameters found in contact\n");
+        goto cleanup;
+    }
+
+    if(pcontact->security_temp->type != SECURITY_IPSEC ) {
+        LM_ERR("Unsupported security type: %d\n", pcontact->security_temp->type);
+        goto cleanup;
+    }
+
+    ipsec_t* s = pcontact->security_temp->data.ipsec;
+
+    if(update_contact_ipsec_params(s, m) != 0) {
+        goto cleanup;
+    }
+
+    if(create_ipsec_tunnel(ci.received_host, s) != 0) {
+        goto cleanup;
+    }
+
+    // TODO: Save security_tmp to security!!!!!
+
+    if (ul.update_pcontact(d, &ci, pcontact) != 0) {
+        LM_ERR("Error updating contact\n");
+        goto cleanup;
+    }
+
+    // Destroy the tunnel, if the contact expires
+    if(ul.register_ulcb(pcontact, PCSCF_CONTACT_EXPIRE|PCSCF_CONTACT_DELETE, on_expire, NULL) != 1) {
+        LM_ERR("Error subscribing for contact\n");
+        goto cleanup;
+    }
+
+
+    if(add_supported_secagree_header(m) != 0) {
+        goto cleanup;
+    }
+
+    if(add_security_server_header(m, s) != 0) {
+        goto cleanup;
+    }
+
+    ret = IPSEC_CMD_SUCCESS;    // all good, set ret to SUCCESS, and exit
+
+cleanup:
+    // Do not free str* sec_header! It will be freed in data_lump.c -> free_lump()
+    ul.unlock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
+    pkg_free(ci.received_host.s);
+    return ret;
+}
+
+
+int ipsec_forward(struct sip_msg* m, udomain_t* d)
+{
+    struct pcontact_info ci;
+    pcontact_t* pcontact = NULL;
+    int ret = IPSEC_CMD_FAIL; // FAIL by default
+
+    //
+    // Find the contact
+    //
+    if(fill_contact(&ci, m) != 0) {
+        LM_ERR("Error filling in contact data\n");
+        return ret;
+    }
+
+    ul.lock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
+
+    if (ul.get_pcontact(d, &ci, &pcontact) != 0) {
+        LM_ERR("Contact doesn't exist\n");
+        goto cleanup;
+    }
+
+
+    if(pcontact->security_temp == NULL) {
+        LM_ERR("No security parameters found in contact\n");
+        goto cleanup;
+    }
+
+    //get security parameters
+    if(pcontact->security_temp->type != SECURITY_IPSEC ) {
+        LM_ERR("Unsupported security type: %d\n", pcontact->security_temp->type);
+        goto cleanup;
+    }
+
+    ipsec_t* s = pcontact->security_temp->data.ipsec;
+
+
+    // Update the destination
+    //
+    //       from sec-agree
+    //            v
+    // sip:host:port
+    //       ^
+    //    from URI
+    //int uri_len = 4 /* strlen("sip:") */ + ci.via_host.len + 5 /* max len of port number */ ;
+
+    if(m->dst_uri.s) {
+        pkg_free(m->dst_uri.s);
+        m->dst_uri.s = NULL;
+        m->dst_uri.len = 0;
+    }
+
+    char buf[1024];
+    int buf_len = snprintf(buf, sizeof(buf) - 1, "sip:%.*s:%d", ci.via_host.len, ci.via_host.s, s->port_us);
+
+    if((m->dst_uri.s = pkg_malloc(buf_len)) == NULL) {
+        LM_ERR("Error allocating memory for dst_uri\n");
+        goto cleanup;
+    }
+
+    memcpy(m->dst_uri.s, buf, buf_len);
+    m->dst_uri.len = buf_len;
+
+    // Set send socket
+    struct socket_info * client_sock = grep_sock_info(&ipsec_listen_addr, ipsec_client_port, PROTO_UDP);
+    if(!client_sock) {
+        LM_ERR("Error calling grep_sock_info() for ipsec client port in ipsec_forward\n");
+        return -1;
+    }
+    m->force_send_socket = client_sock;
+
+   // Set destination info
+    struct dest_info dst_info;
+    dst_info.send_sock = client_sock;
+#ifdef USE_DNS_FAILOVER
+    if (!uri2dst(NULL, &dst_info, m, &m->dst_uri, PROTO_UDP)) {
+#else
+    if (!uri2dst(&dst_info, m, &m->dst_uri, PROTO_UDP)) {
+#endif
+        LM_ERR("Error converting dst_uri (%.*s) to struct dst_info\n", m->dst_uri.len, m->dst_uri.s);
+        goto cleanup;
+    }
+
+    // Update dst_info in message
+    if(m->first_line.type == SIP_REPLY) {
+        struct cell *t = tmb.t_gett();
+        if (!t) {
+            LM_ERR("Error getting transaction\n");
+            goto cleanup;
+        }
+        t->uas.response.dst = dst_info;
+    }
+
+    LM_DBG("Destination changed to %.*s\n", m->dst_uri.len, m->dst_uri.s);
+
+    ret = IPSEC_CMD_SUCCESS; // all good, return SUCCESS
+
+    if(add_supported_secagree_header(m) != 0) {
+        goto cleanup;
+    }
+
+    if(add_security_server_header(m, s) != 0) {
+        goto cleanup;
+    }
+
+    ret = IPSEC_CMD_SUCCESS;    // all good, set ret to SUCCESS, and exit
+
+cleanup:
+    ul.unlock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
+    pkg_free(ci.received_host.s);
+    return ret;
+}
+
+
+int ipsec_destroy(struct sip_msg* m, udomain_t* d)
+{
+    struct pcontact_info ci;
+    pcontact_t* pcontact = NULL;
+    int ret = IPSEC_CMD_FAIL; // FAIL by default
+
+    //
+    // Find the contact
+    //
+    if(fill_contact(&ci, m) != 0) {
+        LM_ERR("Error filling in contact data\n");
+        return ret;
+    }
+
+    ul.lock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
+
+    if (ul.get_pcontact(d, &ci, &pcontact) != 0) {
+        LM_ERR("Contact doesn't exist\n");
+        goto cleanup;
+    }
+
+
+    if(pcontact->security_temp == NULL) {
+        LM_ERR("No security parameters found in contact\n");
+        goto cleanup;
+    }
+
+    //get security parameters
+    if(pcontact->security_temp->type != SECURITY_IPSEC ) {
+        LM_ERR("Unsupported security type: %d\n", pcontact->security_temp->type);
+        goto cleanup;
+    }
+
+    destroy_ipsec_tunnel(ci.received_host, pcontact->security_temp->data.ipsec);
+
+    ret = IPSEC_CMD_SUCCESS;    // all good, set ret to SUCCESS, and exit
+
+cleanup:
+    ul.unlock_udomain(d, &ci.via_host, ci.via_port, ci.via_prot);
+    pkg_free(ci.received_host.s);
+    return ret;
+}
+
+int ipsec_cleanall()
+{
+    struct mnl_socket* nlsock = init_mnl_socket();
+    if(!nlsock) {
+        return -1;
+    }
+
+    if(clean_sa(nlsock) != 0) {
+        LM_WARN("Error cleaning IPSec Security associations during startup.\n");
+    }
+
+    if(clean_policy(nlsock) != 0) {
+        LM_WARN("Error cleaning IPSec Policies during startup.\n");
+    }
+
+    close_mnl_socket(nlsock);
+
+    return 0;
+}
diff --git a/src/modules/ims_ipsec_pcscf/cmd.h b/src/modules/ims_ipsec_pcscf/cmd.h
new file mode 100644 (file)
index 0000000..d3bbae3
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
+ * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
+ *
+ * The initial version of this code was written by Dragos Vingarzan
+ * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
+ * Fruanhofer Institute. It was and still is maintained in a separate
+ * branch of the original SER. We are therefore migrating it to
+ * Kamailio/SR and look forward to maintaining it from here on out.
+ * 2011/2012 Smile Communications, Pty. Ltd.
+ * ported/maintained/improved by
+ * Jason Penton (jason(dot)penton(at)smilecoms.com and
+ * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
+ * effort to add full IMS support to Kamailio/SR using a new and
+ * improved architecture
+ *
+ * NB: Alot of this code was originally part of OpenIMSCore,
+ * FhG Fokus.
+ * Copyright (C) 2004-2006 FhG Fokus
+ * Thanks for great work! This is an effort to
+ * break apart the various CSCF functions into logically separate
+ * components. We hope this will drive wider use. We also feel
+ * that in this way the architecture is more complete and thereby easier
+ * to manage in the Kamailio/SR environment
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef IPSEC_CMD_H
+#define IPSEC_CMD_H
+
+struct sip_msg;
+struct udomain_t;
+
+int ipsec_create(struct sip_msg* m, udomain_t* d);
+int ipsec_forward(struct sip_msg* m, udomain_t* d);
+int ipsec_destroy(struct sip_msg* m, udomain_t* d);
+int ipsec_cleanall();
+
+#endif /* IPSEC_CMD_H */
diff --git a/src/modules/ims_ipsec_pcscf/doc/Makefile b/src/modules/ims_ipsec_pcscf/doc/Makefile
new file mode 100644 (file)
index 0000000..df681ab
--- /dev/null
@@ -0,0 +1,4 @@
+docs = ims_ipsec_pcscf.xml
+
+docbook_dir = ../../../../doc/docbook
+include $(docbook_dir)/Makefile.module
diff --git a/src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf.xml b/src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf.xml
new file mode 100644 (file)
index 0000000..bcc96ae
--- /dev/null
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+]>
+<book>
+  <bookinfo>
+    <title>The IMS IPSec-Registrar Module</title>
+
+    <authorgroup>
+      <author>
+        <firstname>Dragos Vingarzan</firstname>
+
+        <surname/>
+
+        <affiliation>
+          <orgname>FhG Fokus</orgname>
+        </affiliation>
+
+        <email>Dragos.Vingarzan@fokus.fraunhofer.de</email>
+      </author>
+
+      <author>
+        <firstname>Jason</firstname>
+
+        <surname>Penton</surname>
+
+        <affiliation>
+          <orgname>Smile Communications</orgname>
+        </affiliation>
+
+        <email>jason.penton@smilecoms.com</email>
+      </author>
+
+      <author>
+        <firstname>Richard</firstname>
+
+        <surname>Good</surname>
+        <affiliation>
+          <orgname>Smile Communications</orgname>
+        </affiliation>
+
+        <email>richard.good@smilecoms.com</email>
+      </author>
+
+      <author>
+        <firstname>Carsten</firstname>
+
+        <surname>Bock</surname>
+        <affiliation>
+          <orgname>ng-voice GmbH</orgname>
+        </affiliation>
+
+        <email>carsten@ng-voice.com</email>
+      </author>
+
+      <author>
+        <firstname>Tsvetomir</firstname>
+
+        <surname>Dimitrov</surname>
+
+        <email>tsv.dimitrov@gmail.com</email>
+      </author>
+    </authorgroup>
+
+    <copyright>
+      <year>2007</year>
+      <holder>FhG FOKUS</holder>
+    </copyright>
+
+    <copyright>
+      <year>2012</year>
+
+      <holder>Smile Communications</holder>
+    </copyright>
+    <copyright>
+      <year>2015</year>
+      <holder>ng-voice GmbH</holder>
+    </copyright>
+  </bookinfo>
+
+  <toc/>
+
+  <xi:include href="ims_ipsec_pcscf_admin.xml"
+              xmlns:xi="http://www.w3.org/2001/XInclude"/>
+</book>
diff --git a/src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml b/src/modules/ims_ipsec_pcscf/doc/ims_ipsec_pcscf_admin.xml
new file mode 100644 (file)
index 0000000..5d15d1d
--- /dev/null
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
+%docentities;
+]>
+<chapter>
+  <title>&adminguide;</title>
+
+  <section>
+    <title>Overview</title>
+
+    <para>This module contains methods for IPSec initialisation/deinitialisation related for usage of Kamailio as a
+       Proxy-CSCF.</para>
+
+  </section>
+
+  <section>
+    <title>Dependencies</title>
+
+    <section>
+      <title>&kamailio; Modules</title>
+
+      <para>The Following modules must be loaded before this module:</para>
+
+      <itemizedlist>
+        <listitem>
+          <para>Usrloc PCSCF</para>
+        </listitem>
+
+        <listitem>
+          <para>TM</para>
+        </listitem>
+      </itemizedlist>
+    </section>
+
+    <section>
+      <title>External Libraries or Applications</title>
+
+      <para>This modules requires the internal IMS library and libmnl for operating with netlink sockets.</para>
+    </section>
+  </section>
+
+  <section>
+    <title>Parameters</title>
+
+    <section>
+      <title><varname>ipsec_listen_addr</varname> (string)</title>
+
+      <para>IP address which the Proxy-CSCF will use for incoming/outgoing SIP traffic over IPSec.</para>
+      <para><emphasis>Default value is "127.0.0.1"</emphasis></para>
+
+      <example>
+        <title><varname>ipsec_listen_addr</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("ims_ipsec_pcscf", "ipsec_listen_addr", "127.0.0.1")
+...
+        </programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>ipsec_client_port</varname> (int)</title>
+
+      <para>Port number which will be bound for incoming (server) IPSec traffic.</para>
+
+      <para><emphasis>Default value is 5963.</emphasis></para>
+
+      <example>
+        <title><varname>ipsec_client_port</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("ims_ipsec_pcscf", "ipsec_client_port", 5062)
+...
+        </programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>ipsec_server_port</varname> (int)</title>
+
+      <para>Port number which will be bound for incoming (server) IPSec traffic.</para>
+
+      <para><emphasis>Default value is 5063.</emphasis></para>
+
+      <example>
+        <title><varname>ipsec_server_port</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("ims_ipsec_pcscf", "ipsec_server_port", 5063)
+...
+        </programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>ipsec_spi_id_start</varname> (int)</title>
+
+      <para>Each IPSec tunnel has a unique system-wide identifier. This and the following option
+      allows to tune the SPIs used by Kamailio in order to avoid collisions with other IPSec useres.
+      If Kamailio is the only process on the system which uses IPSec,
+      don't bother with this option.</para>
+
+      <para><emphasis>Default value is 100.</emphasis></para>
+
+      <example>
+        <title><varname>ipsec_spi_id_start</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("ims_ipsec_pcscf", "ipsec_spi_id_start", 100)
+...
+        </programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><varname>ipsec_spi_id_range</varname> (int)</title>
+
+      <para>How many SPIs to be allocated for the process. E.g. if ipsec_spi_id_start = 100
+      and ipsec_spi_id_range = 1000, SPIs between 100 and 1100 will be used.</para>
+
+      <para><emphasis>Default value is 1000.</emphasis></para>
+
+      <example>
+        <title><varname>ipsec_spi_id_range</varname> parameter usage</title>
+
+        <programlisting format="linespecific">
+...
+modparam("ims_ipsec_pcscf", "ipsec_spi_id_range", 1000)
+...
+        </programlisting>
+      </example>
+    </section>
+  </section>
+
+  <section>
+    <title>Functions</title>
+
+    <section>
+      <title><function moreinfo="none">ipsec_create(domain)</function></title>
+
+      <para>This function creates IPSec SA and Policy based on the parameters sent
+    in Security-Client header in the REGISTER message. It's called when OK
+    is received. The function also adds Security-Server header to the
+    REGISTER.</para>
+
+      <para>Meaning of the parameters is as follows:</para>
+      <itemizedlist>
+        <listitem>
+          <para>
+          <emphasis>domain</emphasis> - Logical domain within the registrar.
+          If a database is used then this must be name of the table which
+          stores the contacts.
+          </para>
+        </listitem>
+                 </itemizedlist>
+      <example>
+        <title>ipsec_create</title>
+
+        <programlisting format="linespecific">
+...
+ipsec_create("location");
+...
+        </programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><function moreinfo="none">ipsec_forward(domain)</function></title>
+      <para>The function processes redirects outgoing message via the IPSec tunnel
+      initiated with ipsec_create().</para>
+                 <para>Meaning of the parameters is as follows:</para>
+      <itemizedlist>
+        <listitem>
+          <para>
+          <emphasis>domain</emphasis> - Logical domain within the registrar.
+          If a database is used then this must be name of the table which
+          stores the contacts.
+          </para>
+        </listitem>
+      </itemizedlist>
+      <example>
+        <title>ipsec_forward</title>
+
+        <programlisting format="linespecific">
+...
+ipsec_forward("location");
+...
+        </programlisting>
+      </example>
+    </section>
+
+    <section>
+      <title><function moreinfo="none">ipsec_destroy(domain)</function></title>
+      <para>The function destroys IPSec tunnel, created with ipsec_create.</para>
+      <para>Meaning of the parameters is as follows:</para>
+      <itemizedlist>
+        <listitem>
+          <para>
+          <emphasis>domain</emphasis> - Logical domain within the registrar.
+          If a database is used then this must be name of the table which
+          stores the contacts.
+          </para>
+        </listitem>
+                 </itemizedlist>
+      <example>
+        <title>ipsec_forward</title>
+
+        <programlisting format="linespecific">
+...
+ipsec_destroy("location");
+...
+        </programlisting>
+      </example>
+    </section>
+  </section>
+</chapter>
diff --git a/src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c b/src/modules/ims_ipsec_pcscf/ims_ipsec_pcscf_mod.c
new file mode 100644 (file)
index 0000000..779296e
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * IMS IPSEC PCSCF module
+ *
+ * Copyright (C) 2018 Tsvetomir Dimitrov
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "../../core/sr_module.h"
+#include "../../modules/tm/tm_load.h"
+#include "../ims_usrloc_pcscf/usrloc.h"
+
+#include "cmd.h"
+#include "spi_gen.h"
+
+
+MODULE_VERSION
+
+usrloc_api_t ul;                                               /**!< Structure containing pointers to usrloc functions*/
+struct tm_binds tmb;                                   /**!< TM API structure */
+
+
+str ipsec_listen_addr = str_init("127.0.0.1");
+int ipsec_client_port =  5062;
+int ipsec_server_port =  5063;
+int spi_id_start = 100;
+int spi_id_range = 1000;
+int xfrm_user_selector = 143956232;
+
+/*! \brief Module init & destroy function */
+static int  mod_init(void);
+static int  child_init(int);
+static void mod_destroy(void);
+static int w_create(struct sip_msg* _m, char* _d, char* _cflags);
+static int w_forward(struct sip_msg* _m, char* _d, char* _cflags);
+static int w_destroy(struct sip_msg* _m, char* _d, char* _cflags);
+
+/*! \brief Fixup functions */
+static int domain_fixup(void** param, int param_no);
+static int save_fixup2(void** param, int param_no);
+
+/*! \brief
+ * Exported functions
+ */
+static cmd_export_t cmds[] = {
+       {"ipsec_create",  (cmd_function)w_create,  1, save_fixup2, 0, ONREPLY_ROUTE },
+       {"ipsec_forward", (cmd_function)w_forward, 1, save_fixup2, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
+       {"ipsec_destroy", (cmd_function)w_destroy, 1, save_fixup2, 0, REQUEST_ROUTE | ONREPLY_ROUTE },
+       {0, 0, 0, 0, 0, 0}
+};
+
+/*! \brief
+ * Exported parameters
+ */
+static param_export_t params[] = {
+       {"ipsec_listen_addr",   PARAM_STR, &ipsec_listen_addr   },
+       {"ipsec_client_port",   INT_PARAM, &ipsec_client_port   },
+       {"ipsec_server_port",   INT_PARAM, &ipsec_server_port   },
+       {"ipsec_spi_id_start",  INT_PARAM, &spi_id_start},
+       {"ipsec_spi_id_range",  INT_PARAM, &spi_id_range},
+       {0, 0, 0}
+};
+
+
+/*! \brief We expose internal variables via the statistic framework below.*/
+stat_export_t mod_stats[] = {
+       {0, 0, 0}
+};
+
+
+static pv_export_t mod_pvs[] = {
+    {{0, 0}, 0, 0, 0, 0, 0, 0, 0}
+};
+
+/*! \brief
+ * Module exports structure
+ */
+struct module_exports exports = {
+       "ims_ipsec_pcscf",
+       DEFAULT_DLFLAGS, /* dlopen flags */
+       cmds,           /* Exported functions */
+       params,         /* Exported parameters */
+       mod_stats,      /* exported statistics */
+       0,              /* exported MI functions */
+       mod_pvs,        /* exported pseudo-variables */
+       0,              /* extra processes */
+       mod_init,       /* module initialization function */
+       0,
+       mod_destroy,    /* destroy function */
+       child_init,     /* Per-child init function */
+};
+
+
+/*! \brief
+ * Initialize parent
+ */
+static int mod_init(void) {
+    struct socket_info * bind_addr = NULL;
+    char addr[128];
+    if(ipsec_listen_addr.len > sizeof(addr)-1) {
+        LM_ERR("Bad value for ipsec listen address: %.*s\n", ipsec_listen_addr.len, ipsec_listen_addr.s);
+        return -1;
+    }
+    memset(addr, 0, sizeof(addr));
+    memcpy(addr, ipsec_listen_addr.s, ipsec_listen_addr.len);
+
+       bind_usrloc_t bind_usrloc;
+
+       bind_usrloc = (bind_usrloc_t) find_export("ul_bind_ims_usrloc_pcscf", 1, 0);
+       if (!bind_usrloc) {
+               LM_ERR("can't bind ims_usrloc_pcscf\n");
+               return -1;
+       }
+
+       if (bind_usrloc(&ul) < 0) {
+               return -1;
+       }
+       LM_DBG("Successfully bound to PCSCF Usrloc module\n");
+
+       /* load the TM API */
+       if (load_tm_api(&tmb) != 0) {
+               LM_ERR("can't load TM API\n");
+               return -1;
+       }
+       LM_DBG("Successfully bound to TM module\n");
+
+
+    //add listen interfaces
+    if(add_listen_iface(addr, NULL, ipsec_client_port, PROTO_UDP, 0) != 0) {
+        LM_ERR("Error adding listen ipsec client interface\n");
+        return -1;
+    }
+
+    if(add_listen_iface(addr, NULL, ipsec_server_port, PROTO_UDP, 0) != 0) {
+        LM_ERR("Error adding listen ipsec server interface\n");
+        return -1;
+    }
+
+    if(fix_all_socket_lists() != 0) {
+        LM_ERR("Error calling fix_all_socket_lists() during module initialisation\n");
+        return -1;
+    }
+
+    //bind client port
+    bind_addr = grep_sock_info(&ipsec_listen_addr, ipsec_client_port, PROTO_UDP);
+    if(!bind_addr) {
+        LM_ERR("Error calling grep_sock_info() for ipsec client port during module initialisation\n");
+        return -1;
+    }
+
+    if(udp_init(bind_addr) != 0) {
+        LM_ERR("Error calling udp_init() during for ipsec client port module initialisation\n");
+        return -1;
+    }
+
+    //bind server port
+    bind_addr = grep_sock_info(&ipsec_listen_addr, ipsec_server_port, PROTO_UDP);
+    if(!bind_addr) {
+        LM_ERR("Error calling grep_sock_info() for ipsec server port during module initialisation\n");
+        return -1;
+    }
+
+    if(udp_init(bind_addr) != 0) {
+        LM_ERR("Error calling udp_init() during for ipsec server port module initialisation\n");
+        return -1;
+    }
+
+    if(ipsec_cleanall() != 0) {
+        LM_ERR("Error ipsec tunnels during for module initialisation\n");
+        return -1;
+       }
+
+    int res = 0;
+    if((res = init_spi_gen(spi_id_start, spi_id_start + spi_id_range)) != 0) {
+        LM_ERR("Error initialising spi generator. Error: %d\n", res);
+        return -1;
+    }
+
+       return 0;
+}
+
+static void mod_destroy(void)
+{
+    if(ipsec_cleanall() != 0) {
+        LM_ERR("Error ipsec tunnels during for module cleanup\n");
+       }
+
+    if(destroy_spi_gen() != 0) {
+        LM_ERR("Error destroying spi generator\n");
+    }
+}
+
+static int child_init(int rank)
+{
+       return 0;
+}
+
+/* fixups */
+static int domain_fixup(void** param, int param_no)
+{
+       udomain_t* d;
+
+       if (param_no == 1) {
+               if (ul.register_udomain((char*)*param, &d) < 0) {
+                       LM_ERR("failed to register domain\n");
+                       return E_UNSPEC;
+               }
+               *param = (void*)d;
+       }
+       return 0;
+}
+
+/*! \brief
+ * Fixup for "save" function - both domain and flags
+ */
+static int save_fixup2(void** param, int param_no)
+{
+       if (param_no == 1) {
+               return domain_fixup(param,param_no);
+       }
+        return 0;
+}
+
+
+/*! \brief
+ * Wrapper to ipsec functions
+ */
+static int w_create(struct sip_msg* _m, char* _d, char* _cflags)
+{
+       return ipsec_create(_m, (udomain_t*)_d);
+}
+
+static int w_forward(struct sip_msg* _m, char* _d, char* _cflags)
+{
+       return ipsec_forward(_m, (udomain_t*)_d);
+}
+
+static int w_destroy(struct sip_msg* _m, char* _d, char* _cflags)
+{
+       return ipsec_destroy(_m, (udomain_t*)_d);
+}
diff --git a/src/modules/ims_ipsec_pcscf/ipsec.c b/src/modules/ims_ipsec_pcscf/ipsec.c
new file mode 100644 (file)
index 0000000..6b32e68
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * IMS IPSEC PCSCF module
+ *
+ * Copyright (C) 2018 Alexander Yosifov
+ * Copyright (C) 2018 Tsvetomir Dimitrov
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "ipsec.h"
+
+#include "../../core/dprint.h"
+#include "../../core/mem/pkg.h"
+
+#include <errno.h>
+#include <arpa/inet.h>
+#include <libmnl/libmnl.h>
+#include <linux/xfrm.h>
+#include <time.h>
+//#include <stdio.h>
+//#include <string.h>
+
+
+#define XFRM_TMPLS_BUF_SIZE 1024
+#define NLMSG_BUF_SIZE 4096
+#define NLMSG_DELETEALL_BUF_SIZE 8192
+
+
+extern int xfrm_user_selector;
+extern int spi_id_start;
+extern int spi_id_range;
+
+struct xfrm_buffer {
+    char buf[NLMSG_DELETEALL_BUF_SIZE];
+    int offset;
+};
+
+
+//
+// This file contains all Linux specific IPSec code.
+//
+
+struct mnl_socket* init_mnl_socket()
+{
+    struct mnl_socket*  mnl_socket = mnl_socket_open(NETLINK_XFRM);
+    if(NULL == mnl_socket) {
+        LM_ERR("Error opening a MNL socket\n");
+        return NULL;
+    }
+
+    if(mnl_socket_bind(mnl_socket, 0, MNL_SOCKET_AUTOPID) < 0) {
+        LM_ERR("Error binding a MNL socket\n");
+        return NULL;
+    }
+
+    return mnl_socket;
+}
+
+void close_mnl_socket(struct mnl_socket* sock)
+{
+    if(mnl_socket_close(sock) != 0) {
+        LM_WARN("Error closing netlink socket\n");
+    }
+}
+
+static void string_to_key(char* dst, const str key_string)
+{
+    int i = 0;
+    char *pos = key_string.s;
+
+    for (i = 0; i < key_string.len/2; i++) {
+        sscanf(pos, "%2hhx", &dst[i]);
+        pos += 2;
+    }
+}
+
+int add_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id, str ck, str ik)
+{
+    char l_msg_buf[MNL_SOCKET_BUFFER_SIZE];
+    char l_auth_algo_buf[XFRM_TMPLS_BUF_SIZE];
+    char l_enc_algo_buf[XFRM_TMPLS_BUF_SIZE];
+    struct nlmsghdr* l_nlh = NULL;
+    struct xfrm_usersa_info* l_xsainfo = NULL;
+
+    struct xfrm_algo* l_auth_algo = NULL;
+    struct xfrm_algo* l_enc_algo  = NULL;
+
+    char* src_addr = NULL;
+    char* dest_addr = NULL;
+
+    memset(l_msg_buf, 0, sizeof(l_msg_buf));
+    memset(l_auth_algo_buf, 0, sizeof(l_auth_algo_buf));
+    memset(l_enc_algo_buf, 0, sizeof(l_enc_algo_buf));
+
+    // convert input IP addresses and keys to char*
+    if((src_addr = pkg_malloc(src_addr_param.len+1)) == NULL) {
+        LM_ERR("Error allocating memory for src addr during SA creation\n");
+        return -1;
+    }
+
+    if((dest_addr = pkg_malloc(dest_addr_param.len+1)) == NULL) {
+        pkg_free(src_addr);
+        LM_ERR("Error allocating memory for dest addr during SA creation\n");
+        return -2;
+    }
+
+    memset(src_addr, 0, src_addr_param.len+1);
+    memset(dest_addr, 0, dest_addr_param.len+1);
+
+    memcpy(src_addr, src_addr_param.s, src_addr_param.len);
+    memcpy(dest_addr, dest_addr_param.s, dest_addr_param.len);
+
+    // nlmsghdr initialization
+    l_nlh = mnl_nlmsg_put_header(l_msg_buf);
+    l_nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
+    l_nlh->nlmsg_type = XFRM_MSG_NEWSA;
+    l_nlh->nlmsg_seq = time(NULL);
+    l_nlh->nlmsg_pid = id;
+
+    // add Security association
+    l_xsainfo = (struct xfrm_usersa_info*)mnl_nlmsg_put_extra_header(l_nlh, sizeof(struct xfrm_usersa_info));
+    l_xsainfo->sel.family       = AF_INET;
+    l_xsainfo->sel.daddr.a4     = inet_addr(dest_addr);
+    l_xsainfo->sel.saddr.a4     = inet_addr(src_addr);
+    l_xsainfo->sel.dport        = htons(d_port);
+    l_xsainfo->sel.dport_mask   = 0xFFFF;
+    l_xsainfo->sel.prefixlen_d  = 32;
+    l_xsainfo->sel.sport        = htons(s_port);
+    l_xsainfo->sel.sport_mask   = 0xFFFF;
+    l_xsainfo->sel.prefixlen_s  = 32;
+    l_xsainfo->sel.proto        = IPPROTO_UDP;
+    l_xsainfo->sel.user         = htonl(xfrm_user_selector);
+
+    l_xsainfo->saddr.a4         = inet_addr(src_addr);
+    l_xsainfo->id.daddr.a4      = inet_addr(dest_addr);
+    l_xsainfo->id.spi           = htonl(id);
+    l_xsainfo->id.proto         = IPPROTO_ESP;
+
+    l_xsainfo->lft.soft_byte_limit      = XFRM_INF;
+    l_xsainfo->lft.hard_byte_limit      = XFRM_INF;
+    l_xsainfo->lft.soft_packet_limit    = XFRM_INF;
+    l_xsainfo->lft.hard_packet_limit    = XFRM_INF;
+    l_xsainfo->reqid                    = id;
+    l_xsainfo->family                   = AF_INET;
+    l_xsainfo->mode                     = XFRM_MODE_TRANSPORT;
+    l_xsainfo->replay_window            = 32;
+    l_xsainfo->flags                    = XFRM_STATE_NOECN;
+
+    // char* ip addresses are no longer needed - free them
+    pkg_free(src_addr);
+    pkg_free(dest_addr);
+
+    // Add authentication algorithm for this SA
+
+    // The cast below is performed because alg_key from struct xfrm_algo is char[0]
+    // The point is to provide a continuous chunk of memory with the key in it
+    l_auth_algo = (struct xfrm_algo *)l_auth_algo_buf;
+
+    strcpy(l_auth_algo->alg_name,"md5");
+    l_auth_algo->alg_key_len = ik.len * 4;
+    string_to_key(l_auth_algo->alg_key, ik);
+
+    mnl_attr_put(l_nlh, XFRMA_ALG_AUTH, sizeof(struct xfrm_algo) + l_auth_algo->alg_key_len, l_auth_algo);
+
+    // add encription algorithm for this SA
+    l_enc_algo = (struct xfrm_algo *)l_enc_algo_buf;
+    strcpy(l_enc_algo->alg_name,"cipher_null");
+
+    mnl_attr_put(l_nlh, XFRMA_ALG_CRYPT, sizeof(struct xfrm_algo) + l_enc_algo->alg_key_len, l_enc_algo);
+
+    // send it to Netlink socket
+    if(mnl_socket_sendto(nl_sock, l_nlh, l_nlh->nlmsg_len) < 0)
+    {
+        LM_ERR("Failed to send Netlink message for SA creation, error: %s\n", strerror(errno));
+        return -3;
+    }
+
+    return 0;
+}
+
+
+int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id)
+{
+    char* src_addr = NULL;
+    char* dest_addr = NULL;
+
+    // convert input IP addresses to char*
+    if((src_addr = pkg_malloc(src_addr_param.len+1)) == NULL) {
+        LM_ERR("Error allocating memory for src addr during SA removal\n");
+        return -1;
+    }
+
+    if((dest_addr = pkg_malloc(dest_addr_param.len+1)) == NULL) {
+        pkg_free(src_addr);
+        LM_ERR("Error allocating memory for dest addr during SA removal\n");
+        return -2;
+    }
+
+    memset(src_addr, 0, src_addr_param.len+1);
+    memset(dest_addr, 0, dest_addr_param.len+1);
+
+    memcpy(src_addr, src_addr_param.s, src_addr_param.len);
+    memcpy(dest_addr, dest_addr_param.s, dest_addr_param.len);
+
+
+    struct {
+        struct nlmsghdr n;
+        struct xfrm_usersa_id   xsid;
+        char buf[XFRM_TMPLS_BUF_SIZE];
+
+    } req = {
+        .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xsid)),
+        .n.nlmsg_flags = NLM_F_REQUEST,
+        .n.nlmsg_type = XFRM_MSG_DELSA,
+        .xsid.spi = htonl(id),
+        .xsid.family = AF_INET,
+        .xsid.proto = IPPROTO_ESP,
+        .xsid.daddr.a4 = inet_addr(dest_addr)
+    };
+
+    // SADDR
+    xfrm_address_t saddr;
+    memset(&saddr, 0, sizeof(saddr));
+    saddr.a4 = inet_addr(src_addr);
+
+    mnl_attr_put(&req.n, XFRMA_SRCADDR, sizeof(saddr), (void *)&saddr);
+
+    if(mnl_socket_sendto(nl_sock, &req.n, req.n.nlmsg_len) < 0)
+    {
+        LM_ERR("Failed to send Netlink message, error: %s\n", strerror(errno));
+        pkg_free(src_addr);
+        pkg_free(dest_addr);
+        return -1;
+    }
+
+    pkg_free(src_addr);
+    pkg_free(dest_addr);
+
+    return 0;
+}
+
+
+int add_policy(struct mnl_socket* mnl_socket, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir)
+{
+    char                            l_msg_buf[MNL_SOCKET_BUFFER_SIZE];
+    char                            l_tmpls_buf[XFRM_TMPLS_BUF_SIZE];
+    struct nlmsghdr*                l_nlh;
+    struct xfrm_userpolicy_info*    l_xpinfo;
+
+    char* src_addr = NULL;
+    char* dest_addr = NULL;
+
+    //printf("Adding Policy\n");
+
+    memset(l_msg_buf, 0, sizeof(l_msg_buf));
+    memset(l_tmpls_buf, 0, sizeof(l_tmpls_buf));
+
+    // convert input IP addresses to char*
+    if((src_addr = pkg_malloc(src_addr_param.len+1)) == NULL) {
+        LM_ERR("Error allocating memory for src addr during Policy creation\n");
+        return -1;
+    }
+
+    if((dest_addr = pkg_malloc(dest_addr_param.len+1)) == NULL) {
+        pkg_free(src_addr);
+        LM_ERR("Error allocating memory for dest addr during Policy creation\n");
+        return -2;
+    }
+
+    memset(src_addr, 0, src_addr_param.len+1);
+    memset(dest_addr, 0, dest_addr_param.len+1);
+
+    memcpy(src_addr, src_addr_param.s, src_addr_param.len);
+    memcpy(dest_addr, dest_addr_param.s, dest_addr_param.len);
+
+    // nlmsghdr initialization
+    l_nlh = mnl_nlmsg_put_header(l_msg_buf);
+    l_nlh->nlmsg_flags  = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL;
+    l_nlh->nlmsg_type   = XFRM_MSG_NEWPOLICY;
+    l_nlh->nlmsg_seq    = time(NULL);
+    l_nlh->nlmsg_pid    = p_id;
+
+    // add OUT policy
+    l_xpinfo = (struct xfrm_userpolicy_info*)mnl_nlmsg_put_extra_header(l_nlh, sizeof(struct xfrm_userpolicy_info));
+    l_xpinfo->sel.family        = AF_INET;
+    l_xpinfo->sel.daddr.a4      = inet_addr(dest_addr);
+    l_xpinfo->sel.saddr.a4      = inet_addr(src_addr);
+    l_xpinfo->sel.dport         = htons(dst_port);
+    l_xpinfo->sel.dport_mask    = 0xFFFF;
+    l_xpinfo->sel.prefixlen_d   = 32;
+    l_xpinfo->sel.sport         = htons(src_port);
+    l_xpinfo->sel.sport_mask    = 0xFFFF;
+    l_xpinfo->sel.prefixlen_s   = 32;
+    l_xpinfo->sel.proto         = IPPROTO_UDP;
+    l_xpinfo->sel.user          = htonl(xfrm_user_selector);
+
+    l_xpinfo->lft.soft_byte_limit   = XFRM_INF;
+    l_xpinfo->lft.hard_byte_limit   = XFRM_INF;
+    l_xpinfo->lft.soft_packet_limit = XFRM_INF;
+    l_xpinfo->lft.hard_packet_limit = XFRM_INF;
+    l_xpinfo->priority              = 2080;
+    l_xpinfo->action                = XFRM_POLICY_ALLOW;
+    l_xpinfo->share                 = XFRM_SHARE_ANY;
+
+    if (dir == IPSEC_POLICY_DIRECTION_IN) {
+        l_xpinfo->dir               = XFRM_POLICY_IN;
+    }
+    else if(dir == IPSEC_POLICY_DIRECTION_OUT) {
+        l_xpinfo->dir               = XFRM_POLICY_OUT;
+    }
+    else {
+        LM_ERR("Invalid direction parameter passed to add_policy: %d\n", dir);
+        pkg_free(src_addr);
+        pkg_free(dest_addr);
+
+        return -3;
+    }
+
+
+    // xfrm_user_tmpl initialization
+    struct xfrm_user_tmpl* l_tmpl = (struct xfrm_user_tmpl*)l_tmpls_buf;
+    l_tmpl->id.proto    = IPPROTO_ESP;
+    l_tmpl->family      = AF_INET;
+    l_tmpl->reqid       = p_id;
+    l_tmpl->mode        = XFRM_MODE_TRANSPORT;
+    l_tmpl->aalgos      = (~(__u32)0);
+    l_tmpl->ealgos      = (~(__u32)0);
+    l_tmpl->calgos      = (~(__u32)0);
+
+
+    mnl_attr_put(l_nlh, XFRMA_TMPL, sizeof(struct xfrm_user_tmpl), l_tmpl);
+
+    if(mnl_socket_sendto(mnl_socket, l_nlh, l_nlh->nlmsg_len) < 0)
+    {
+        pkg_free(src_addr);
+        pkg_free(dest_addr);
+        LM_ERR("Failed to send Netlink message, error: %s\n", strerror(errno));
+        return -4;
+    }
+
+    // char* ip addresses are no longer needed - free them
+    pkg_free(src_addr);
+    pkg_free(dest_addr);
+
+    return 0;
+}
+
+
+int remove_policy(struct mnl_socket* mnl_socket, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir)
+{
+    unsigned char policy_dir = 0;
+
+    if(dir == IPSEC_POLICY_DIRECTION_IN) {
+         policy_dir = XFRM_POLICY_IN;
+    }
+    else if(dir == IPSEC_POLICY_DIRECTION_OUT) {
+         policy_dir = XFRM_POLICY_OUT;
+    }
+    else {
+        LM_ERR("Invalid direction parameter passed to add_policy: %d\n", dir);
+        return -1;
+    }
+
+    char* src_addr = NULL;
+    char* dest_addr = NULL;
+
+    // convert input IP addresses to char*
+    if((src_addr = pkg_malloc(src_addr_param.len+1)) == NULL) {
+        LM_ERR("Error allocating memory for src addr during SA removal\n");
+        return -1;
+    }
+
+    if((dest_addr = pkg_malloc(dest_addr_param.len+1)) == NULL) {
+        pkg_free(src_addr);
+        LM_ERR("Error allocating memory for dest addr during SA removal\n");
+        return -2;
+    }
+
+    memset(src_addr, 0, src_addr_param.len+1);
+    memset(dest_addr, 0, dest_addr_param.len+1);
+
+    memcpy(src_addr, src_addr_param.s, src_addr_param.len);
+    memcpy(dest_addr, dest_addr_param.s, dest_addr_param.len);
+
+    struct {
+        struct nlmsghdr n;
+        struct xfrm_userpolicy_id xpid;
+        char buf[XFRM_TMPLS_BUF_SIZE];
+    } req = {
+        .n.nlmsg_len = NLMSG_LENGTH(sizeof(req.xpid)),
+        .n.nlmsg_flags = NLM_F_REQUEST,
+        .n.nlmsg_type = XFRM_MSG_DELPOLICY,
+        .xpid.dir               = policy_dir,
+        .xpid.sel.family        = AF_INET,
+        .xpid.sel.daddr.a4      = inet_addr(dest_addr),
+        .xpid.sel.saddr.a4      = inet_addr(src_addr),
+        .xpid.sel.dport         = htons(dst_port),
+        .xpid.sel.dport_mask    = 0xFFFF,
+        .xpid.sel.prefixlen_d   = 32,
+        .xpid.sel.sport         = htons(src_port),
+        .xpid.sel.sport_mask    = 0xFFFF,
+        .xpid.sel.prefixlen_s   = 32,
+        .xpid.sel.proto         = IPPROTO_UDP
+    };
+
+    if(mnl_socket_sendto(mnl_socket, &req.n, req.n.nlmsg_len) < 0)
+    {
+        LM_ERR("Failed to send Netlink message, error: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int delsa_data_cb(const struct nlmsghdr *nlh, void *data)
+{
+    struct xfrm_usersa_info *xsinfo = NLMSG_DATA(nlh);
+    int xfrm_userid = ntohl(xsinfo->sel.user);
+
+    //Check if user id is different from Kamailio's
+    if(xfrm_userid != xfrm_user_selector)
+        return MNL_CB_OK;
+
+    struct xfrm_buffer* delmsg_buf = (struct xfrm_buffer*)data;
+    uint32_t new_delmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_id));
+
+    if(delmsg_buf->offset + new_delmsg_len > sizeof(delmsg_buf->buf)/sizeof(delmsg_buf->buf[0])) {
+        LM_ERR("Not enough memory allocated for delete SAs netlink command\n");
+        return MNL_CB_ERROR;
+    }
+
+    struct nlmsghdr *new_delmsg = (struct nlmsghdr *)&delmsg_buf->buf[delmsg_buf->offset];
+    new_delmsg->nlmsg_len = new_delmsg_len;
+    new_delmsg->nlmsg_flags = NLM_F_REQUEST;
+    new_delmsg->nlmsg_type = XFRM_MSG_DELSA;
+    new_delmsg->nlmsg_seq = time(NULL);
+
+    struct xfrm_usersa_id *xsid = NLMSG_DATA(new_delmsg);
+    xsid->family = xsinfo->family;
+    memcpy(&xsid->daddr, &xsinfo->id.daddr, sizeof(xsid->daddr));
+    xsid->spi = xsinfo->id.spi;
+    xsid->proto = xsinfo->id.proto;
+
+    mnl_attr_put(new_delmsg, XFRMA_SRCADDR, sizeof(xsid->daddr), &xsinfo->saddr);
+
+    delmsg_buf->offset += new_delmsg->nlmsg_len;
+
+    return MNL_CB_OK;
+}
+
+static int delpolicy_data_cb(const struct nlmsghdr *nlh, void *data)
+{
+    struct xfrm_userpolicy_info *xpinfo = NLMSG_DATA(nlh);
+    int xfrm_userid = ntohl(xpinfo->sel.user);
+
+    //Check if user id is different from Kamailio's
+    if(xfrm_userid != xfrm_user_selector)
+        return MNL_CB_OK;
+
+    struct xfrm_buffer* delmsg_buf = (struct xfrm_buffer*)data;
+    uint32_t new_delmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_userpolicy_id));
+
+    if(delmsg_buf->offset + new_delmsg_len > sizeof(delmsg_buf->buf)/sizeof(delmsg_buf->buf[0])) {
+        LM_ERR("Not enough memory allocated for delete policies netlink command\n");
+        return MNL_CB_ERROR;
+    }
+
+    struct nlmsghdr *new_delmsg = (struct nlmsghdr *)&delmsg_buf->buf[delmsg_buf->offset];
+    new_delmsg->nlmsg_len = new_delmsg_len;
+    new_delmsg->nlmsg_flags = NLM_F_REQUEST;
+    new_delmsg->nlmsg_type = XFRM_MSG_DELPOLICY;
+    new_delmsg->nlmsg_seq = time(NULL);
+
+    struct xfrm_userpolicy_id *xpid = NLMSG_DATA(new_delmsg);
+    memcpy(&xpid->sel, &xpinfo->sel, sizeof(xpid->sel));
+    xpid->dir = xpinfo->dir;
+    xpid->index = xpinfo->index;
+
+    delmsg_buf->offset += new_delmsg->nlmsg_len;
+
+    return MNL_CB_OK;
+}
+
+int clean_sa(struct mnl_socket*  mnl_socket)
+{
+    struct {
+        struct nlmsghdr n;
+        //char buf[NLMSG_BUF_SIZE];
+    } req = {
+        .n.nlmsg_len = NLMSG_HDRLEN,
+        .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+        .n.nlmsg_type = XFRM_MSG_GETSA,
+        .n.nlmsg_seq = time(NULL),
+    };
+
+    if(mnl_socket_sendto(mnl_socket, &req, req.n.nlmsg_len) == -1) {
+        LM_ERR("Error sending get all SAs command via netlink socket: %s\n", strerror(errno));
+        return 1;
+    }
+
+    char buf[NLMSG_BUF_SIZE];
+    memset(&buf, 0, sizeof(buf));
+
+    struct xfrm_buffer delmsg_buf;
+    memset(&delmsg_buf, 0, sizeof(struct xfrm_buffer));
+
+    int ret = mnl_socket_recvfrom(mnl_socket, buf, sizeof(buf));
+    while (ret > 0) {
+        ret = mnl_cb_run(buf, ret, req.n.nlmsg_seq, mnl_socket_get_portid(mnl_socket), delsa_data_cb, &delmsg_buf);
+        if (ret <= MNL_CB_STOP) {
+
+            break;
+        }
+        ret = mnl_socket_recvfrom(mnl_socket, buf, sizeof(buf));
+    }
+
+    // DELETE SAs
+    if(mnl_socket_sendto(mnl_socket, &delmsg_buf.buf, delmsg_buf.offset) == -1) {
+        LM_ERR("Error sending delete SAs command via netlink socket: %s\n", strerror(errno));
+        return 1;
+    }
+
+    return 0;
+}
+
+int clean_policy(struct mnl_socket*  mnl_socket)
+{
+    struct {
+        struct nlmsghdr n;
+        //char buf[NLMSG_BUF_SIZE];
+    } req = {
+        .n.nlmsg_len = NLMSG_HDRLEN,
+        .n.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST,
+        .n.nlmsg_type = XFRM_MSG_GETPOLICY,
+        .n.nlmsg_seq = time(NULL),
+    };
+
+    if(mnl_socket_sendto(mnl_socket, &req, req.n.nlmsg_len) == -1) {
+        LM_ERR("Error sending get all policies command via netlink socket: %s\n", strerror(errno));
+        return 1;
+    }
+
+    char buf[NLMSG_BUF_SIZE];
+    memset(&buf, 0, sizeof(buf));
+
+    struct xfrm_buffer delmsg_buf;
+    memset(&delmsg_buf, 0, sizeof(struct xfrm_buffer));
+
+    int ret = mnl_socket_recvfrom(mnl_socket, buf, sizeof(buf));
+    while (ret > 0) {
+        ret = mnl_cb_run(buf, ret, req.n.nlmsg_seq, mnl_socket_get_portid(mnl_socket), delpolicy_data_cb, &delmsg_buf);
+        if (ret <= MNL_CB_STOP) {
+
+            break;
+        }
+        ret = mnl_socket_recvfrom(mnl_socket, buf, sizeof(buf));
+    }
+
+    // DELETE POLICIES
+    if(mnl_socket_sendto(mnl_socket, &delmsg_buf.buf, delmsg_buf.offset) == -1) {
+        LM_ERR("Error sending delete policies command via netlink socket: %s\n", strerror(errno));
+        return 1;
+    }
+
+    return 0;
+}
diff --git a/src/modules/ims_ipsec_pcscf/ipsec.h b/src/modules/ims_ipsec_pcscf/ipsec.h
new file mode 100644 (file)
index 0000000..82ad405
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * IMS IPSEC PCSCF module
+ *
+ * Copyright (C) 2018 Alexander Yosifov
+ * Copyright (C) 2018 Tsvetomir Dimitrov
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef IMS_IPSEC_PCSCF_IPSEC
+#define IMS_IPSEC_PCSCF_IPSEC
+
+#include "../../core/str.h"
+
+
+struct mnl_socket;
+
+enum ipsec_policy_direction {
+    IPSEC_POLICY_DIRECTION_IN = 0,
+    IPSEC_POLICY_DIRECTION_OUT = 1
+};
+
+
+struct mnl_socket* init_mnl_socket();
+void close_mnl_socket(struct mnl_socket* sock);
+
+int add_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id, str ck, str ik);
+int remove_sa(struct mnl_socket* nl_sock, str src_addr_param, str dest_addr_param, int s_port, int d_port, int long id);
+
+int add_policy(struct mnl_socket* mnl_socket, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir);
+int remove_policy(struct mnl_socket* mnl_socket, str src_addr_param, str dest_addr_param, int src_port, int dst_port, int long p_id, enum ipsec_policy_direction dir);
+
+int clean_sa(struct mnl_socket*  mnl_socket);
+int clean_policy(struct mnl_socket*  mnl_socket);
+
+#endif /* IMS_IPSEC_PCSCF_IPSEC */
diff --git a/src/modules/ims_ipsec_pcscf/run_spi_list_tests.sh b/src/modules/ims_ipsec_pcscf/run_spi_list_tests.sh
new file mode 100755 (executable)
index 0000000..8e9efb6
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+gcc -D_IPSEC_SPI_LIST_TEST spi_list_tests.c spi_list.c -o spi_list_tests
+./spi_list_tests
diff --git a/src/modules/ims_ipsec_pcscf/spi_gen.c b/src/modules/ims_ipsec_pcscf/spi_gen.c
new file mode 100644 (file)
index 0000000..fa7a3e7
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * IMS IPSEC PCSCF module
+ *
+ * Copyright (C) 2018 Tsvetomir Dimitrov
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "spi_gen.h"
+#include "spi_list.h"
+#include <pthread.h>
+
+pthread_mutex_t spis_mut;
+spi_list_t used_spis;
+uint32_t spi_val;
+uint32_t min_spi;
+uint32_t max_spi;
+
+int init_spi_gen(uint32_t start_val, uint32_t range)
+{
+    if(start_val < 1) {
+        return 1;
+    }
+
+    if(UINT32_MAX - range < start_val)
+        return 2;
+
+    if(pthread_mutex_init(&spis_mut, NULL))
+        return 3;
+
+    used_spis = create_list();
+
+    spi_val = start_val;
+    min_spi = start_val;
+    max_spi = start_val + range;
+
+    return 0;
+}
+
+uint32_t acquire_spi()
+{
+    //save the initial value for the highly unlikely case where there are no free SPIs
+    uint32_t initial_val = spi_val;
+    uint32_t ret = 0; // by default return invalid SPI
+
+    if(pthread_mutex_lock(&spis_mut) != 0) {
+        return ret;
+    }
+
+    while(1) {
+        if(spi_in_list(&used_spis, spi_val) == 0) {
+            ret = spi_val;
+            spi_val++;
+            break;
+        }
+
+        spi_val++; //the current SPI is not available - increment
+
+        if(spi_val >= max_spi) { //reached the top of the range - reset
+            spi_val = min_spi;
+        }
+
+        if(spi_val == initial_val) { //there are no free SPIs
+            break;
+        }
+
+    }
+
+    //found unused SPI - add it to the used list
+    if(spi_add(&used_spis, ret) != 0) {
+        ret = 0;
+    }
+
+    pthread_mutex_unlock(&spis_mut);
+
+    return ret;
+}
+
+int release_spi(uint32_t id)
+{
+    if(pthread_mutex_lock(&spis_mut) != 0) {
+        return 1;
+    }
+
+    spi_remove(&used_spis, id);
+
+    pthread_mutex_unlock(&spis_mut);
+
+    return 0;
+}
+
+int destroy_spi_gen()
+{
+    return pthread_mutex_destroy(&spis_mut);
+}
diff --git a/src/modules/ims_ipsec_pcscf/spi_gen.h b/src/modules/ims_ipsec_pcscf/spi_gen.h
new file mode 100644 (file)
index 0000000..dcb8475
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * IMS IPSEC PCSCF module
+ *
+ * Copyright (C) 2018 Tsvetomir Dimitrov
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _SPI_GEN_H_
+
+#include <stdint.h>
+
+//
+// SPI GEN is based on SPI list.
+// It is used as a unique ID generator for the SPIs. As the range of IDs is limited it
+// is important not to use generate ID which is still in use. For this reason there are
+// acquire_spi() and release_spi(uint32_t id) functions.
+
+int init_spi_gen(uint32_t start_val, uint32_t range);
+int destroy_spi_gen();
+uint32_t acquire_spi();
+int release_spi(uint32_t id);
+
+#endif /*  _SPI_GEN_H_ */
diff --git a/src/modules/ims_ipsec_pcscf/spi_list.c b/src/modules/ims_ipsec_pcscf/spi_list.c
new file mode 100644 (file)
index 0000000..f57a45b
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * IMS IPSEC PCSCF module
+ *
+ * Copyright (C) 2018 Tsvetomir Dimitrov
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "spi_list.h"
+
+
+spi_list_t create_list()
+{
+    spi_list_t lst;
+    lst.head = NULL;
+    lst.tail = NULL;
+    return lst;
+}
+
+int spi_add(spi_list_t* list, uint32_t id)
+{
+    // create new node
+    spi_node_t* n = malloc(sizeof(spi_node_t));
+
+    if(!n)
+        return 1;
+
+    n->next = NULL;
+    n->id = id;
+
+    //when list is empty
+    if(!list->head) {
+        list->head = n;
+        list->tail = n;
+        return 0;
+    }
+
+    //all other cases - list should be sorted
+    spi_node_t* c = list->head;
+    spi_node_t* p = NULL;
+    while(c && (n->id > c->id)) {
+        p = c;
+        c = c->next;
+    }
+
+
+    if(c == NULL) { //first of all - at the end of the list?
+        list->tail->next = n;
+        list->tail = n;
+    }
+    else if(n->id == c->id) { //c is not NULL, so check for duplicates
+        free(n);
+        return 1;
+    }
+    else if(c == list->head) { //at the start of the list?
+        n->next = list->head;
+        list->head = n;
+    }
+    else {  //not a special case - just add it
+        p->next = n;
+        n->next = c;
+    }
+
+    return 0;
+}
+
+
+int spi_remove(spi_list_t* list, uint32_t id)
+{
+    //when list is empty
+    if(!list->head) {
+        return 0;
+    }
+
+    //when target is head
+    if(list->head->id == id) {
+        spi_node_t* t = list->head;
+        list->head = t->next;
+
+        //if head==tail, adjust tail too
+        if(t == list->tail) {
+            list->tail = list->head;
+        }
+
+        free(t);
+        return 0;
+    }
+
+
+    spi_node_t* prev = list->head;
+    spi_node_t* curr = list->head->next;
+
+    while(curr) {
+        if(curr->id == id) {
+            spi_node_t* t = curr;
+
+            //detach node
+            prev->next = curr->next;
+
+            //is it the last elemet
+            if(t == list->tail) {
+                list->tail = prev;
+            }
+
+            free(t);
+        }
+
+        prev = curr;
+        curr = curr->next;
+    }
+
+    return 0;
+}
+
+int spi_in_list(spi_list_t* list, uint32_t id)
+{
+    if(!list->head)
+        return 0;
+
+    if(id < list->head->id || id > list->tail->id)
+        return 0;
+
+    spi_node_t* n = list->head;
+    while(n) {
+        if (n->id == id)
+            return 1;
+    }
+
+    return 0;
+}
+
diff --git a/src/modules/ims_ipsec_pcscf/spi_list.h b/src/modules/ims_ipsec_pcscf/spi_list.h
new file mode 100644 (file)
index 0000000..05a555c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * IMS IPSEC PCSCF module
+ *
+ * Copyright (C) 2018 Tsvetomir Dimitrov
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _SPI_LIST_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+
+//
+// Single linked list implementation. The elements are kept sorted via insertion sort.
+//
+
+
+typedef struct _spi_node spi_node_t;
+
+struct _spi_node {
+    spi_node_t* next;
+    uint32_t id;
+};
+
+typedef struct _spi_list {
+    spi_node_t* head;
+    spi_node_t* tail;
+} spi_list_t;
+
+
+spi_list_t create_list();
+int spi_add(spi_list_t* list, uint32_t id);
+int spi_remove(spi_list_t* list, uint32_t id);
+int spi_in_list(spi_list_t* list, uint32_t id);
+
+#endif /* _SPI_LIST_H_ */
diff --git a/src/modules/ims_ipsec_pcscf/spi_list_tests.c b/src/modules/ims_ipsec_pcscf/spi_list_tests.c
new file mode 100644 (file)
index 0000000..876c474
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * IMS IPSEC PCSCF module
+ *
+ * Copyright (C) 2018 Tsvetomir Dimitrov
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef _IPSEC_SPI_LIST_TEST
+
+#include "spi_list.h"
+#include <stdio.h>
+
+void iterate(spi_list_t* list)
+{
+    spi_node_t* n = list->head;
+    printf("HEAD: %d TAIL: %d; [", list->head->id, list->tail->id);
+
+    while(n) {
+        printf("%d ", n->id);
+        n = n->next;
+    }
+    printf("]\n");
+}
+
+void check(spi_list_t* list, int* exp, int len, const char* func_name)
+{
+    //Special case for empty list
+    if(len == 0) {
+        if(list->head != NULL) {
+            printf("%s: Empty list but head is not NULL.\n", func_name);
+            return;
+        }
+
+        if(list->tail != NULL) {
+            printf("%s: Empty list, but tail is not NULL\n", func_name);
+            return;
+        }
+
+        goto success;
+    }
+
+    //Check head
+    if(exp[0] != list->head->id) {
+        printf("%s failed. Expected head: %d; Actual head: %d\n", func_name, exp[0], list->head->id);
+        return;
+    }
+
+    //Check list contents
+    spi_node_t* n = list->head;
+    int i;
+    for(i = 0; i < len; i++) {
+        if(exp[i] != n->id) {
+            printf("%s failed. list[%d] == %d; exp[%d] == %d\n", func_name, i, n->id, i, exp[i]);
+            return;
+        }
+        n = n->next;
+    }
+
+    //Check tail
+    if(exp[len-1] != list->tail->id) {
+        printf("%s failed. Expected tail: %d; Actual tail: %d\n", func_name, exp[len-1], list->tail->id);
+        return;
+    }
+
+success:
+    printf("%s: OK\n", func_name);
+}
+
+void case1() // One element list
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1};
+
+    spi_add(&list, 1);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case2() // Two element list
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1, 2};
+
+    spi_add(&list, 1);
+    spi_add(&list, 2);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case3() // Three element list
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1, 2, 3};
+
+    spi_add(&list, 1);
+    spi_add(&list, 2);
+    spi_add(&list, 3);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case4() // Delete head
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {2, 3};
+
+    spi_add(&list, 1);
+    spi_add(&list, 2);
+    spi_add(&list, 3);
+
+    spi_remove(&list, 1);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+
+void case5() // Delete tail
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1, 2 };
+
+    spi_add(&list, 1);
+    spi_add(&list, 2);
+    spi_add(&list, 3);
+
+    spi_remove(&list, 3);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case6() // Delete between
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1, 3 };
+
+    spi_add(&list, 1);
+    spi_add(&list, 2);
+    spi_add(&list, 3);
+
+    spi_remove(&list, 2);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case7() // Out of order add
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1, 2, 3};
+
+    spi_add(&list, 2);
+    spi_add(&list, 1);
+    spi_add(&list, 3);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case8() //Random operations
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1, 4, 6};
+
+    spi_add(&list, 2);
+    spi_add(&list, 1);
+    spi_add(&list, 3);
+
+    spi_remove(&list, 2);
+    spi_add(&list, 4);
+    spi_add(&list, 6);
+    spi_remove(&list, 3);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case9() // Empty list
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {};
+
+    spi_add(&list, 2);
+    spi_add(&list, 1);
+    spi_add(&list, 3);
+
+    spi_remove(&list, 1);
+    spi_remove(&list, 2);
+    spi_remove(&list, 3);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+
+void case10() //No duplicates
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1,2,3};
+
+    spi_add(&list, 1);
+    spi_add(&list, 2);
+    spi_add(&list, 2);
+    spi_add(&list, 2);
+    spi_add(&list, 3);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case11() //No duplicates
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1,2,3};
+
+    spi_add(&list, 1);
+    spi_add(&list, 2);
+    spi_add(&list, 3);
+    spi_add(&list, 3);
+    spi_add(&list, 3);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case12() //No duplicates
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1,2,3};
+
+    spi_add(&list, 1);
+    spi_add(&list, 1);
+    spi_add(&list, 2);
+    spi_add(&list, 3);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case13() //No duplicates
+{
+    spi_list_t list = create_list();
+
+    int exp[] = {1,2,3};
+
+    spi_add(&list, 1);
+    spi_add(&list, 2);
+    spi_add(&list, 3);
+    spi_add(&list, 1);
+
+    check(&list, exp, sizeof(exp)/sizeof(int), __func__);
+}
+
+void case14()
+{
+    spi_list_t list = create_list();
+    spi_add(&list, 1);
+    spi_add(&list, 2);
+
+    if(spi_in_list(&list, 1) != 1) {
+        printf("%s: failed. 1 is in list, but spi_in_list() returns false.\n", __func__);
+        return;
+    }
+
+    if(spi_in_list(&list, 3) != 0) {
+        printf("%s: failed. 3 is not in list, but spi_in_list() returns true.\n", __func__);
+        return;
+    }
+
+    printf("%s: OK\n", __func__);
+}
+
+
+int main()
+{
+
+    case1();
+    case2();
+    case3();
+    case4();
+    case5();
+    case6();
+    case7();
+    case8();
+    case9();
+    case10();
+    case11();
+    case12();
+    case13();
+    case14();
+
+    return 0;
+}
+
+#endif
index 4c74ac7..e3352b1 100644 (file)
@@ -2026,14 +2026,14 @@ void update_db_subs_timer_dbonly(void)
        db_val_t qvals[1];
        db_key_t result_cols[18];
        int pres_uri_col, to_user_col, to_domain_col, from_user_col, from_domain_col,
-               callid_col, totag_col, fromtag_col, event_col, event_id_col,
-               local_cseq_col, expires_col, rr_col, sockinfo_col,
-               contact_col, lcontact_col, watcher_user_col, watcher_domain_col;
+       callid_col, totag_col, fromtag_col, event_col, event_id_col,
+       local_cseq_col, expires_col, rr_col, sockinfo_col,
+       contact_col, lcontact_col, watcher_user_col, watcher_domain_col;
        int n_result_cols = 0;
        db1_res_t *result= NULL;
-       db_row_t *row = NULL;
+       db_row_t *rows;
        db_val_t *row_vals= NULL;
-       int i;
+       int i, res;
        subs_t s, *s_new, *s_array = NULL, *s_del;
        str ev_name;
        pres_ev_t* event;
@@ -2072,27 +2072,26 @@ void update_db_subs_timer_dbonly(void)
                return;
        }
 
-       if (pa_dbf.query(pa_db, qcols, qops, qvals, result_cols,
-                               1, n_result_cols, 0, &result) < 0) {
+       res = db_fetch_query(&pa_dbf, pres_fetch_rows, pa_db, qcols, qops, qvals, result_cols,1, n_result_cols, 0, &result );
+       if (res < 0)
+       {
                LM_ERR("failed to query database for expired subscriptions\n");
-               if(result)
+               if (result) {
                        pa_dbf.free_result(pa_db, result);
+               }
                return;
        }
 
-       if(result== NULL)
-               return;
-
-       if(result->n <=0 ) {
-               pa_dbf.free_result(pa_db, result);
+       if(result == NULL) {
+               LM_DBG("no results returned\n");
                return;
        }
-       LM_DBG("found %d dialogs\n", result->n);
 
-       for(i=0; i<result->n; i++)
-       {
-               row = &result->rows[i];
-               row_vals = ROW_VALUES(row);
+       LM_DBG("processing %d dialogs\n", RES_ROW_N(result));
+       s_array = NULL;
+       rows = RES_ROWS(result);
+       for (i = 0; i < RES_ROW_N(result); i++) {
+               row_vals = ROW_VALUES(&rows[i]);
 
                memset(&s, 0, sizeof(subs_t));
 
@@ -2173,12 +2172,6 @@ void update_db_subs_timer_dbonly(void)
                s_new = s_new->next;
                pkg_free(s_del);
        }
-
-       /* delete the expired subscriptions */
-       if(pa_dbf.delete(pa_db, qcols, qops, qvals, 1) < 0)
-       {
-               LM_ERR("deleting expired information from database\n");
-       }
 }
 
 void update_db_subs_timer_dbnone(int no_lock)
index 65e9491..01cebc8 100644 (file)
@@ -3458,16 +3458,22 @@ int pv_parse_msg_attrs_name(pv_spec_p sp, str *in)
                                sp->pvp.pvn.u.isname.name.n = 2;
                        else if(strncmp(in->s, "hdrs", 4)==0)
                                sp->pvp.pvn.u.isname.name.n = 3;
+                       else if(strncmp(in->s, "hdrc", 4)==0)
+                               sp->pvp.pvn.u.isname.name.n = 6;
                        else goto error;
                break;
                case 5:
                        if(strncmp(in->s, "fline", 5)==0)
                                sp->pvp.pvn.u.isname.name.n = 4;
+                       else if(strncmp(in->s, "fpart", 5)==0)
+                               sp->pvp.pvn.u.isname.name.n = 7;
                        else goto error;
                break;
                case 8:
                        if(strncmp(in->s, "body_len", 8)==0)
                                sp->pvp.pvn.u.isname.name.n = 5;
+                       else if(strncmp(in->s, "hdrs_len", 8)==0)
+                               sp->pvp.pvn.u.isname.name.n = 8;
                        else goto error;
                break;
                default:
@@ -3489,6 +3495,9 @@ error:
 int pv_get_msg_attrs(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
 {
        str s;
+       hdr_field_t* hdr;
+       int n;
+
        if(msg==NULL)
                return pv_get_null(msg, param, res);
 
@@ -3534,6 +3543,26 @@ int pv_get_msg_attrs(sip_msg_t *msg, pv_param_t *param, pv_value_t *res)
                        if (s.s != NULL)
                                s.len = msg->buf + msg->len - s.s;
                        return pv_get_sintval(msg, param, res, s.len);
+               case 6: /* headers count */
+                       n = 0;
+                       for(hdr=msg->headers; hdr!=NULL; hdr=hdr->next) {
+                               n++;
+                       }
+                       return pv_get_sintval(msg, param, res, n);
+               case 7: /* first part - first line + headers */
+                       if(msg->unparsed==NULL)
+                               return pv_get_null(msg, param, res);
+                       s.s = msg->buf;
+                       s.len = msg->unparsed - s.s;
+                       trim(&s);
+                       return pv_get_strval(msg, param, res, &s);
+               case 8: /* headers size */
+                       if(msg->unparsed==NULL)
+                               return pv_get_sintval(msg, param, res, 0);
+                       s.s = msg->buf + msg->first_line.len;
+                       s.len = msg->unparsed - s.s;
+                       trim(&s);
+                       return pv_get_sintval(msg, param, res, s.len);
 
                default:
                        return pv_get_null(msg, param, res);
index 6840530..efc5807 100644 (file)
@@ -67,6 +67,9 @@ enum SMS_DATA {
        SMS_TPDU_REFERENCE,
        SMS_TPDU_ORIGINATING_ADDRESS,
        SMS_TPDU_DESTINATION,
+       SMS_UDH_CONCATSM_REF,
+       SMS_UDH_CONCATSM_MAX_NUM_SM,
+       SMS_UDH_CONCATSM_SEQ
 };
 
 // Types of the PDU-Message
@@ -83,6 +86,34 @@ typedef enum _pdu_message_type {
 #define TP_UDHI 0x64;
 #define TP_RP 0x128;
 
+// Information element identifiers and corresponding structs.
+// Only the supported ones are listed.
+// Defined in TS 23.040, Sec. 9.2.3.24 and 9.2.3.24.1
+#define TP_UDH_IE_CONCAT_SM_8BIT_REF 0x00      // 9.2.3.24
+struct ie_concat_sm_8bit_ref {
+       unsigned char ref;                      //Concatenated short message reference number
+       unsigned char max_num_sm;       //Maximum number of short messages in the concatenated short message.
+       unsigned char seq;                      //Sequence number of the current short message
+};
+
+// Information element in User Data Header
+typedef struct _tp_udh_inf_element tp_udh_inf_element_t;
+struct _tp_udh_inf_element {
+       unsigned char identifier;
+       union {
+               str data;
+               struct ie_concat_sm_8bit_ref concat_sm_8bit_ref;
+       };
+
+       tp_udh_inf_element_t* next;
+};
+
+// TS 23.040, Sec. 9.2.3.24
+typedef struct _tp_user_data {
+       tp_udh_inf_element_t* header;
+       str sm;
+} tp_user_data_t;
+
 // PDU (GSM 03.40) of the SMS
 typedef struct _sms_pdu {
        pdu_message_type_t msg_type;
@@ -93,7 +124,7 @@ typedef struct _sms_pdu {
        unsigned char validity;
        str originating_address;
        str destination;
-       str payload;
+       tp_user_data_t payload;
 } sms_pdu_t;
 
 // RP-Data of the message
@@ -102,7 +133,7 @@ typedef struct _sms_rp_data {
        unsigned char reference;
        str originator;
        str destination;
-       int pdu_len;
+       unsigned char pdu_len;
        sms_pdu_t pdu;
 } sms_rp_data_t;
 
@@ -125,7 +156,15 @@ void freeRP_DATA(sms_rp_data_t * rpdata) {
                if (rpdata->destination.s) pkg_free(rpdata->destination.s);
                if (rpdata->pdu.originating_address.s) pkg_free(rpdata->pdu.originating_address.s);
                if (rpdata->pdu.destination.s) pkg_free(rpdata->pdu.destination.s);
-               if (rpdata->pdu.payload.s) pkg_free(rpdata->pdu.payload.s);
+               while (rpdata->pdu.payload.header) {
+                       tp_udh_inf_element_t* next = rpdata->pdu.payload.header->next;
+                       if(rpdata->pdu.payload.header->identifier != TP_UDH_IE_CONCAT_SM_8BIT_REF) {
+                               if(rpdata->pdu.payload.header->data.s) pkg_free(rpdata->pdu.payload.header->data.s);
+                       }
+                       pkg_free(rpdata->pdu.payload.header);
+                       rpdata->pdu.payload.header = next;
+               }
+               if (rpdata->pdu.payload.sm.s) pkg_free(rpdata->pdu.payload.sm.s);
        }
 }
 
@@ -133,6 +172,7 @@ void freeRP_DATA(sms_rp_data_t * rpdata) {
 #define BITMASK_8BITS 0xFF
 #define BITMASK_HIGH_4BITS 0xF0
 #define BITMASK_LOW_4BITS 0x0F
+#define BITMASK_TP_UDHI 0x40
 
 // Encode SMS-Message by merging 7 bit ASCII characters into 8 bit octets.
 static int ascii_to_gsm(str sms, char * output_buffer, int buffer_size) {
@@ -162,82 +202,136 @@ static int ascii_to_gsm(str sms, char * output_buffer, int buffer_size) {
 }
 
 // Decode 7bit encoded message by splitting 8 bit encoded buffer into 7 bit ASCII characters.
-int gsm_to_ascii(char* buffer, int buffer_length, str sms) {
-        int output_text_length = 0;
-        if (buffer_length > 0)
-                sms.s[output_text_length++] = BITMASK_7BITS & buffer[0];
+int gsm_to_ascii(char* buffer, int buffer_length, str sms, const int fill_bits) {
+               int output_text_length = 0;
+
+               if(buffer_length <= 2)
+                       return 0;
 
-        int carry_on_bits = 1;
-        int i = 1;
-        for (; i < buffer_length; ++i) {
-                sms.s[output_text_length++] = BITMASK_7BITS & ((buffer[i] << carry_on_bits) | (buffer[i - 1] >> (8 - carry_on_bits)));
+               // How many bits we have carried from the next octet. This number can be positive or negative:
+               // positive: We have carried n bits FROM the next octet.
+               // negative: We have to carry n bits TO the next octet.
+               // 0: Nothing carried. Default value!
+               int carry_on_bits = 0;
 
-                if (output_text_length == sms.len) break;
+               // Used to iterate over buffer. Declared here, because if there are fill_bits it have to be incremented
+               int i = 0;
 
-                carry_on_bits++;
+               // First remove the fill bits, if any
+               if(fill_bits) {
+                       // We need 7 bits in the first octet, so if there is only 1 fill bit, we don't have to
+                       // carry from the next octet.
 
-                if (carry_on_bits == 8) {
-                        carry_on_bits = 1;
-                        sms.s[output_text_length++] = buffer[i] & BITMASK_7BITS;
-                        if (output_text_length == sms.len) break;
-                }
+                       // cmask stands for carry mask or how many bits to carry from the 2nd octet
+                       unsigned char cmask = (1 << (fill_bits - 1)) - 1;
+
+                       sms.s[output_text_length++] = ( (buffer[0] >> fill_bits) |      // remove the fill bits from the first octet
+                                                                                       (buffer[1] & cmask << (8 - fill_bits)) // mask the required number of bits
+                                                                                                                                                                       //and shift them accordingly
+                                                                               ) & BITMASK_7BITS;      // mask just 7 bits from the first octet
+
+                       carry_on_bits = fill_bits - 1;
+                       i++;
+               }
+
+
+               for (; i < buffer_length; ++i) {
+                       if(carry_on_bits > 0) {
+                               unsigned char cmask = (1 << (carry_on_bits - 1)) - 1;   //mask for the rightmost carry_on_bits
+                                                                                                                                               //E.g. carry_on_bits=3 -> _ _ _ _ _ X X X
+                               sms.s[output_text_length++] = ( (buffer[i] >> carry_on_bits) | //shift right to remove carried bits
+                                                                                               (buffer[i+1] & cmask) << (8 - carry_on_bits)    // carry from the next
+                                                                                                                                                                                               // and shift accordingly
+                                                                                               ) & BITMASK_7BITS;      // mask just 7 bits from the first octet
+                       }
+                       else if(carry_on_bits < 0) {
+                               carry_on_bits = carry_on_bits * -1;     //make carry_on_bits positive for the bitwise ops
+                               unsigned char cmask = ((1 << carry_on_bits) - 1) << (8 - carry_on_bits);        //mask for the leftmost carry_on_bits.
+                                                                                                                                                                               //E.g. carry_on_bits=3 -> X X X _ _ _ _ _
+                               sms.s[output_text_length++] = ( (buffer[i] << carry_on_bits) | //shift left to make space for the carried bits
+                                                                                               (buffer[i-1] & cmask) >> (8 - carry_on_bits)    // get the bits from the previous octet
+                                                                                                                                                                                               // and shift accordingly
+                                                                                               ) & BITMASK_7BITS;      // mask just 7 bits from the first octet
+
+                               carry_on_bits = carry_on_bits * -1;     //return the original value
+                       }
+                       else {// carry_on_bits == 0
+                               sms.s[output_text_length++] = buffer[i] & BITMASK_7BITS;
+                       }
+
+                       //Update carry_on bits. It is always decremented, because we iterate over octests but read just septets
+                       carry_on_bits--;
+
+                       if (output_text_length == sms.len) break;
+
+                       if (carry_on_bits == -8) {
+                               carry_on_bits = -1;
+                               sms.s[output_text_length++] = buffer[i] & BITMASK_7BITS;
+                               if (output_text_length == sms.len) break;
+                       }
+
+                       if(carry_on_bits > 0 && (i + 2 >= buffer_length)) {
+                               //carry_on_bits is positive, which means thah we have to borrow from the next octet on next iteration
+                               //However i + 2 >= buffer_length so there is no next octet. This is error.
+                               break;
+                       }
+               }
 
-        }
-        if (output_text_length < sms.len)  // Add last remainder.
-                sms.s[output_text_length++] = buffer[i - 1] >> (8 - carry_on_bits);
+               if (output_text_length < sms.len)  // Add last remainder.
+                       sms.s[output_text_length++] = buffer[i - 1] >> (8 - carry_on_bits);
 
-        return output_text_length;
+               return output_text_length;
 }
 
 // Decode UCS2 message by splitting the buffer into utf8 characters
 int ucs2_to_utf8 (int ucs2, char * utf8) {
-    if (ucs2 < 0x80) {
-        utf8[0] = ucs2;
+       if (ucs2 < 0x80) {
+               utf8[0] = ucs2;
        utf8[1] = 0;
-        return 1;
-    }
-    if (ucs2 >= 0x80  && ucs2 < 0x800) {
-        utf8[0] = (ucs2 >> 6)   | 0xC0;
-        utf8[1] = (ucs2 & 0x3F) | 0x80;
-        return 2;
-    }
-    if (ucs2 >= 0x800 && ucs2 < 0xFFFF) {
-       if (ucs2 >= 0xD800 && ucs2 <= 0xDFFF) return -1;
-        utf8[0] = ((ucs2 >> 12)       ) | 0xE0;
-        utf8[1] = ((ucs2 >> 6 ) & 0x3F) | 0x80;
-        utf8[2] = ((ucs2      ) & 0x3F) | 0x80;
-        return 3;
-    }
-    if (ucs2 >= 0x10000 && ucs2 < 0x10FFFF) {
+               return 1;
+       }
+       if (ucs2 >= 0x80  && ucs2 < 0x800) {
+               utf8[0] = (ucs2 >> 6)   | 0xC0;
+               utf8[1] = (ucs2 & 0x3F) | 0x80;
+               return 2;
+       }
+       if (ucs2 >= 0x800 && ucs2 < 0xFFFF) {
+               if (ucs2 >= 0xD800 && ucs2 <= 0xDFFF) return -1;
+               utf8[0] = ((ucs2 >> 12)       ) | 0xE0;
+               utf8[1] = ((ucs2 >> 6 ) & 0x3F) | 0x80;
+               utf8[2] = ((ucs2      ) & 0x3F) | 0x80;
+               return 3;
+       }
+       if (ucs2 >= 0x10000 && ucs2 < 0x10FFFF) {
        utf8[0] = 0xF0 | (ucs2 >> 18);
        utf8[1] = 0x80 | ((ucs2 >> 12) & 0x3F);
        utf8[2] = 0x80 | ((ucs2 >> 6) & 0x3F);
        utf8[3] = 0x80 | ((ucs2 & 0x3F));
-        return 4;
-    }
-    return -1;
+               return 4;
+       }
+       return -1;
 }
 
 // Decode UTF8 to UCS2
 int utf8_to_ucs2 (const unsigned char * input, const unsigned char ** end_ptr) {
-    *end_ptr = input;
-    if (input[0] == 0)
-        return -1;
-    if (input[0] < 0x80) {
-        * end_ptr = input + 1;
-        return input[0];
-    }
-    if ((input[0] & 0xE0) == 0xE0) {
-        if (input[1] == 0 || input[2] == 0) return -1;
-        *end_ptr = input + 3;
-        return (input[0] & 0x0F) << 12 | (input[1] & 0x3F) << 6  | (input[2] & 0x3F);
-    }
-    if ((input[0] & 0xC0) == 0xC0) {
-        if (input[1] == 0) return -1;
-        * end_ptr = input + 2;
-        return (input[0] & 0x1F) << 6 | (input[1] & 0x3F);
-    }
-    return -1;
+       *end_ptr = input;
+       if (input[0] == 0)
+               return -1;
+       if (input[0] < 0x80) {
+               * end_ptr = input + 1;
+               return input[0];
+       }
+       if ((input[0] & 0xE0) == 0xE0) {
+               if (input[1] == 0 || input[2] == 0) return -1;
+               *end_ptr = input + 3;
+               return (input[0] & 0x0F) << 12 | (input[1] & 0x3F) << 6  | (input[2] & 0x3F);
+       }
+       if ((input[0] & 0xC0) == 0xC0) {
+               if (input[1] == 0) return -1;
+               * end_ptr = input + 2;
+               return (input[0] & 0x1F) << 6 | (input[1] & 0x3F);
+       }
+       return -1;
 }
 
 // Encode a digit based phone number for SMS based format.
@@ -254,7 +348,7 @@ static int EncodePhoneNumber(str phone, char * output_buffer, int buffer_size) {
                if (i % 2 == 0) {
                        output_buffer[output_buffer_length++] = BITMASK_HIGH_4BITS | (phone.s[i] - '0');
                } else {
-                       output_buffer[output_buffer_length - 1] = (output_buffer[output_buffer_length - 1] & BITMASK_LOW_4BITS) | ((phone.s[i] - '0') << 4); 
+                       output_buffer[output_buffer_length - 1] = (output_buffer[output_buffer_length - 1] & BITMASK_LOW_4BITS) | ((phone.s[i] - '0') << 4);
                }
        }
 
@@ -267,7 +361,7 @@ static int DecodePhoneNumber(char* buffer, int len, str phone) {
        for (; i < len; ++i) {
                if (i % 2 == 0)
                        phone.s[i] = (buffer[i / 2] & BITMASK_LOW_4BITS) + '0';
-               else
+               else
                        phone.s[i] = ((buffer[i / 2] & BITMASK_HIGH_4BITS) >> 4) + '0';
        }
        return i;
@@ -298,6 +392,44 @@ static void EncodeTime(char * buffer) {
        buffer[6] = 0; // Timezone, we use no time offset.
 }
 
+//The function is called GetXXX but it actually creates the IE if it doesn't exist
+static struct ie_concat_sm_8bit_ref* GetConcatShortMsg8bitRefIE(sms_rp_data_t* rp_data)
+{
+       tp_udh_inf_element_t* ie = rp_data->pdu.payload.header;
+       tp_udh_inf_element_t* prev = rp_data->pdu.payload.header;
+       //Look for Concatenated SM 8bit Reference IE
+       while(ie) {
+               if(ie->identifier == TP_UDH_IE_CONCAT_SM_8BIT_REF)
+                       break;
+               prev = ie;
+               ie = ie->next;
+       }
+
+       if(ie == NULL) {
+               //If not found - create it
+               ie = pkg_malloc(sizeof(tp_udh_inf_element_t));
+               if(ie == NULL) {
+                       LM_ERR("no more pkg\n");
+                       return NULL;
+               }
+               memset(ie, 0, sizeof(tp_udh_inf_element_t));
+               ie->identifier = TP_UDH_IE_CONCAT_SM_8BIT_REF;
+
+               if(prev) {
+                       //If the previous IE is not NULL - link to it
+                       prev->next = ie;
+               }
+               else {
+                       //There are not IEs at all
+                       rp_data->pdu.payload.header = ie;
+                       //Set TP-UDHI flag to 1
+                       rp_data->pdu.flags |= BITMASK_TP_UDHI;
+               }
+       }
+
+       return &(ie->concat_sm_8bit_ref);
+}
+
 // Decode SMS-Body into the given structure:
 int decode_3gpp_sms(struct sip_msg *msg) {
        str body;
@@ -394,26 +526,103 @@ int decode_3gpp_sms(struct sip_msg *msg) {
                                rp_data->pdu.pid = (unsigned char)body.s[p++];
                                rp_data->pdu.coding = (unsigned char)body.s[p++];
                                rp_data->pdu.validity = (unsigned char)body.s[p++];
-                               len = body.s[p++];
+
+                               //TP-User-Data-Length and TP-User-Data
+                               len = (unsigned char)body.s[p++];
+                               int fill_bits = 0;
                                if (len > 0) {
+                                       if((unsigned char)rp_data->pdu.flags & BITMASK_TP_UDHI) { //TP-UDHI
+                                               int udh_len = (unsigned char)body.s[p++];
+                                               int udh_read = 0;
+
+                                               if(rp_data->pdu.coding == 0) {
+                                                       //calcucate padding size for 7bit coding
+                                                       //udh_len + 1, because the length field itself should be included
+                                                       fill_bits = (7 - (udh_len + 1) % 7) % 7; //padding size is in bits!
+                                               }
+
+                                               // Check for malicious length, which might cause buffer overflow
+                                               if(udh_len > body.len - p) {
+                                                       LM_ERR("TP-User-Data-Lenght is bigger than the remaining message buffer!\n");
+                                                       return -1;
+                                               }
+
+                                               //User-Data-Header
+                                               tp_udh_inf_element_t* prev_ie = NULL;
+                                               // IE 'Concatenated short messages, 8-bit reference number' should not be repeated
+                                               int contains_8bit_refnum = 0;
+                                               while(udh_read < udh_len) {
+                                                       tp_udh_inf_element_t* ie = pkg_malloc(sizeof(tp_udh_inf_element_t));
+                                                       if(ie == NULL) {
+                                                               LM_ERR("no more pkg\n");
+                                                               return -1;
+                                                       }
+                                                       memset(ie, 0, sizeof(tp_udh_inf_element_t));
+
+                                                       ie->identifier = (unsigned char)body.s[p++];
+                                                       ie->data.len = (unsigned char)body.s[p++];
+
+                                                       // Check for malicious length, which might cause buffer overflow
+                                                       if(udh_read + ie->data.len + 2 /* two octets are read so far */ > udh_len) {
+                                                               LM_ERR("IE Lenght for IE id %d is bigger than the remaining User-Data element!\n",
+                                                                                                                                                                                                       ie->identifier);
+                                                               return -1;
+                                                       }
+
+                                                       if(ie->identifier == TP_UDH_IE_CONCAT_SM_8BIT_REF) {
+                                                               if(contains_8bit_refnum) {
+                                                                       LM_ERR("IE Concatenated Short Message 8bit Reference occured more than once in UDH\n");
+                                                                       return -1;
+                                                               }
+
+                                                               ie->concat_sm_8bit_ref.ref = body.s[p++];
+                                                               ie->concat_sm_8bit_ref.max_num_sm = body.s[p++];
+                                                               ie->concat_sm_8bit_ref.seq = body.s[p++];
+
+                                                               contains_8bit_refnum = 1;
+                                                       }
+                                                       else { /* Unsupported IE, save it as binary */
+                                                               ie->data.s = pkg_malloc(ie->data.len);
+                                                               if(ie->data.s == NULL) {
+                                                                       LM_ERR("no more pkg\n");
+                                                                       return -1;
+                                                               }
+                                                               memset(ie->data.s, 0, ie->data.len);
+                                                               memcpy(ie->data.s, &body.s[p], ie->data.len);
+                                                               p += ie->data.len;
+                                                       }
+
+                                                       if(prev_ie == NULL) {
+                                                               rp_data->pdu.payload.header = ie;
+                                                       }
+                                                       else {
+                                                               prev_ie->next = ie;
+                                                       }
+
+                                                       prev_ie = ie;
+                                                       udh_read += (1 /* IE ID */ + 1 /* IE Len */ + ie->data.len /* IE data */);
+                                               }
+
+                                       }
+
                                        blen = 2 + len*4;
-                                       rp_data->pdu.payload.s = pkg_malloc(blen);
-                                       if(rp_data->pdu.payload.s==NULL) {
+                                       rp_data->pdu.payload.sm.s = pkg_malloc(blen);
+                                       if(rp_data->pdu.payload.sm.s==NULL) {
                                                LM_ERR("no more pkg\n");
                                                return -1;
                                        }
-                                       memset(rp_data->pdu.payload.s, 0, blen);
+                                       memset(rp_data->pdu.payload.sm.s, 0, blen);
                                        // Coding: 7 Bit
                                        if (rp_data->pdu.coding == 0x00) {
                                                // We don't care about the extra used bytes here.
-                                               rp_data->pdu.payload.len = gsm_to_ascii(&body.s[p], blen, rp_data->pdu.payload);
+                                               rp_data->pdu.payload.sm.len = gsm_to_ascii(&body.s[p], len, rp_data->pdu.payload.sm, fill_bits);
                                        } else {
                                                // Length is worst-case 2 * len (UCS2 is 2 Bytes, UTF8 is worst-case 4 Bytes)
-                                               rp_data->pdu.payload.len = 0;
+                                               rp_data->pdu.payload.sm.len = 0;
                                                while (len > 0) {
                                                        j = (body.s[p] << 8) + body.s[p + 1];
                                                        p += 2;
-                                                       rp_data->pdu.payload.len += ucs2_to_utf8(j, &rp_data->pdu.payload.s[rp_data->pdu.payload.len]);
+                                                       rp_data->pdu.payload.sm.len += ucs2_to_utf8(j, &rp_data->pdu.payload.sm.s[rp_data->pdu.payload.sm.len]);
                                                        len -= 2;
                                                }
                                        }
@@ -446,7 +655,7 @@ int dumpRPData(sms_rp_data_t * rpdata, int level) {
                LOG(level, "  Coding:                     %x (%i)\n", rpdata->pdu.coding, rpdata->pdu.coding);
                LOG(level, "  Validity:                   %x (%i)\n", rpdata->pdu.validity, rpdata->pdu.validity);
 
-               LOG(level, "  Payload:                    %.*s (%i)\n", rpdata->pdu.payload.len, rpdata->pdu.payload.s, rpdata->pdu.payload.len);
+               LOG(level, "  Payload:                    %.*s (%i)\n", rpdata->pdu.payload.sm.len, rpdata->pdu.payload.sm.s, rpdata->pdu.payload.sm.len);
        }
        return 1;
 }
@@ -534,7 +743,7 @@ int pv_sms_body(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
        // Store the position of the length for later usage:
        lenpos = sms_body.len;
        sms_body.s[sms_body.len++] = 0x00;
-       
+
        ///////////////////////////////////////////////////
        // T-PDU
        ///////////////////////////////////////////////////
@@ -550,13 +759,13 @@ int pv_sms_body(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
        // Service-Center-Timestamp (always 7 octets)
        EncodeTime(&sms_body.s[sms_body.len]);
        sms_body.len += 7;
-       sms_body.s[sms_body.len++] = rp_send_data->pdu.payload.len;
-       i = ascii_to_gsm(rp_send_data->pdu.payload, &sms_body.s[sms_body.len], buffer_size - sms_body.len);
+       sms_body.s[sms_body.len++] = rp_send_data->pdu.payload.sm.len;
+       i = ascii_to_gsm(rp_send_data->pdu.payload.sm, &sms_body.s[sms_body.len], buffer_size - sms_body.len);
        sms_body.len += i - 1;
 
        // Update the len of the PDU
        sms_body.s[lenpos] = (unsigned char)(sms_body.len - lenpos - 1);
-       
+
        return pv_get_strval(msg, param, res, &sms_body);
 }
 
@@ -591,11 +800,38 @@ int pv_get_sms(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
                case SMS_TPDU_REFERENCE:
                        return  pv_get_sintval(msg, param, res, (int)rp_data->pdu.reference);
                case SMS_TPDU_PAYLOAD:
-                       return pv_get_strval(msg, param, res, &rp_data->pdu.payload);
+                       return pv_get_strval(msg, param, res, &rp_data->pdu.payload.sm);
                case SMS_TPDU_DESTINATION:
                        return pv_get_strval(msg, param, res, &rp_data->pdu.destination);
                case SMS_TPDU_ORIGINATING_ADDRESS:
                        return pv_get_strval(msg, param, res, &rp_data->pdu.originating_address);
+               case SMS_UDH_CONCATSM_REF: {
+                       tp_udh_inf_element_t* ie = rp_data->pdu.payload.header;
+                       while(ie) {
+                               if(ie->identifier == TP_UDH_IE_CONCAT_SM_8BIT_REF)
+                                       return pv_get_uintval(msg, param, res, (unsigned int)ie->concat_sm_8bit_ref.ref);
+                               ie = ie->next;
+                       }
+                       return -1;
+               }
+               case SMS_UDH_CONCATSM_MAX_NUM_SM: {
+                       tp_udh_inf_element_t* ie = rp_data->pdu.payload.header;
+                       while(ie) {
+                               if(ie->identifier == TP_UDH_IE_CONCAT_SM_8BIT_REF)
+                                       return pv_get_uintval(msg, param, res, (unsigned int)ie->concat_sm_8bit_ref.max_num_sm);
+                               ie = ie->next;
+                       }
+                       return -1;
+               }
+               case SMS_UDH_CONCATSM_SEQ: {
+                       tp_udh_inf_element_t* ie = rp_data->pdu.payload.header;
+                       while(ie) {
+                               if(ie->identifier == TP_UDH_IE_CONCAT_SM_8BIT_REF)
+                                       return pv_get_uintval(msg, param, res, (unsigned int)ie->concat_sm_8bit_ref.seq);
+                               ie = ie->next;
+                       }
+                       return -1;
+               }
        }
        return 0;
 }
@@ -750,10 +986,10 @@ int pv_set_sms(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val)
                        rp_send_data->pdu.validity = (unsigned char)val->ri;
                        break;
                case SMS_TPDU_PAYLOAD:
-                       if (rp_send_data->pdu.payload.s) {
-                               pkg_free(rp_send_data->pdu.payload.s);
-                               rp_send_data->pdu.payload.s = 0;
-                               rp_send_data->pdu.payload.len = 0;
+                       if (rp_send_data->pdu.payload.sm.s) {
+                               pkg_free(rp_send_data->pdu.payload.sm.s);
+                               rp_send_data->pdu.payload.sm.s = 0;
+                               rp_send_data->pdu.payload.sm.len = 0;
                        }
                        if (val == NULL)
                                return 0;
@@ -761,13 +997,13 @@ int pv_set_sms(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val)
                                LM_ERR("Invalid type\n");
                                return -1;
                        }
-                       rp_send_data->pdu.payload.s = pkg_malloc(val->rs.len);
-                       if(rp_send_data->pdu.payload.s==NULL) {
+                       rp_send_data->pdu.payload.sm.s = pkg_malloc(val->rs.len);
+                       if(rp_send_data->pdu.payload.sm.s==NULL) {
                                LM_ERR("no more pkg\n");
                                return -1;
                        }
-                       rp_send_data->pdu.payload.len = val->rs.len;
-                       memcpy(rp_send_data->pdu.payload.s, val->rs.s, val->rs.len);
+                       rp_send_data->pdu.payload.sm.len = val->rs.len;
+                       memcpy(rp_send_data->pdu.payload.sm.s, val->rs.s, val->rs.len);
                        break;
                case SMS_TPDU_DESTINATION:
                        if (rp_send_data->pdu.destination.s) {
@@ -809,7 +1045,48 @@ int pv_set_sms(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val)
                        rp_send_data->pdu.originating_address.len = val->rs.len;
                        memcpy(rp_send_data->pdu.originating_address.s, val->rs.s, val->rs.len);
                        break;
+               case SMS_UDH_CONCATSM_REF: {
+                       if (val == NULL)
+                               return 0;
+                       if (!(val->flags&PV_VAL_INT)) {
+                               LM_ERR("Invalid type\n");
+                               return -1;
+                       }
+                       struct ie_concat_sm_8bit_ref* concat = GetConcatShortMsg8bitRefIE(rp_data);
+                       if(concat == NULL)
+                               return -1;
+
+                       concat->ref = (unsigned char)val->ri;
+                       break;
+               }
+               case SMS_UDH_CONCATSM_MAX_NUM_SM: {
+                       if (val == NULL)
+                               return 0;
+                       if (!(val->flags&PV_VAL_INT)) {
+                               LM_ERR("Invalid type\n");
+                               return -1;
+                       }
+                       struct ie_concat_sm_8bit_ref* concat = GetConcatShortMsg8bitRefIE(rp_data);
+                       if(concat == NULL)
+                               return -1;
 
+                       concat->max_num_sm = (unsigned char)val->ri;
+                       break;
+               }
+               case SMS_UDH_CONCATSM_SEQ: {
+                       if (val == NULL)
+                               return 0;
+                       if (!(val->flags&PV_VAL_INT)) {
+                               LM_ERR("Invalid type\n");
+                               return -1;
+                       }
+                       struct ie_concat_sm_8bit_ref* concat = GetConcatShortMsg8bitRefIE(rp_data);
+                       if(concat == NULL)
+                               return -1;
+
+                       concat->seq = (unsigned char)val->ri;
+                       break;
+               }
        }
        return 0;
 }
@@ -818,23 +1095,23 @@ int pv_parse_rpdata_name(pv_spec_p sp, str *in) {
        if (sp==NULL || in==NULL || in->len<=0) return -1;
 
        switch(in->len) {
-               case 3: 
+               case 3:
                        if (strncmp(in->s, "all", 3) == 0) sp->pvp.pvn.u.isname.name.n = SMS_ALL;
                        else goto error;
                        break;
-               case 4: 
+               case 4:
                        if (strncmp(in->s, "type", 4) == 0) sp->pvp.pvn.u.isname.name.n = SMS_RPDATA_TYPE;
                        else goto error;
                        break;
-               case 9: 
+               case 9:
                        if (strncmp(in->s, "reference", 9) == 0) sp->pvp.pvn.u.isname.name.n = SMS_RPDATA_REFERENCE;
                        else goto error;
                        break;
-               case 10: 
+               case 10:
                        if (strncmp(in->s, "originator", 10) == 0) sp->pvp.pvn.u.isname.name.n = SMS_RPDATA_ORIGINATOR;
                        else goto error;
                        break;
-               case 11: 
+               case 11:
                        if (strncmp(in->s, "destination", 11) == 0) sp->pvp.pvn.u.isname.name.n = SMS_RPDATA_DESTINATION;
                        else goto error;
                        break;
@@ -855,38 +1132,41 @@ int pv_parse_tpdu_name(pv_spec_p sp, str *in) {
        if (sp==NULL || in==NULL || in->len<=0) return -1;
 
        switch(in->len) {
-               case 3: 
+               case 3:
                        if (strncmp(in->s, "all", 3) == 0) sp->pvp.pvn.u.isname.name.n = SMS_ALL;
                        else goto error;
                        break;
-               case 4: 
+               case 4:
                        if (strncmp(in->s, "type", 4) == 0) sp->pvp.pvn.u.isname.name.n = SMS_TPDU_TYPE;
                        else goto error;
                        break;
-               case 5: 
+               case 5:
                        if (strncmp(in->s, "flags", 5) == 0) sp->pvp.pvn.u.isname.name.n = SMS_TPDU_FLAGS;
+                       else if (strncmp(in->s, "mp_id", 5) == 0) sp->pvp.pvn.u.isname.name.n = SMS_UDH_CONCATSM_REF;
                        else goto error;
                        break;
-               case 6: 
+               case 6:
                        if (strncmp(in->s, "coding", 6) == 0) sp->pvp.pvn.u.isname.name.n = SMS_TPDU_CODING;
                        else if (strncmp(in->s, "origen", 6) == 0) sp->pvp.pvn.u.isname.name.n = SMS_TPDU_ORIGINATING_ADDRESS;
                        else goto error;
                        break;
-               case 7: 
+               case 7:
                        if (strncmp(in->s, "payload", 7) == 0) sp->pvp.pvn.u.isname.name.n = SMS_TPDU_PAYLOAD;
                        else goto error;
                        break;
-               case 8: 
+               case 8:
                        if (strncmp(in->s, "protocol", 8) == 0) sp->pvp.pvn.u.isname.name.n = SMS_TPDU_PROTOCOL;
                        else if (strncmp(in->s, "validity", 8) == 0) sp->pvp.pvn.u.isname.name.n = SMS_TPDU_VALIDITY;
+                       else if (strncmp(in->s, "mp_parts", 8) == 0) sp->pvp.pvn.u.isname.name.n = SMS_UDH_CONCATSM_MAX_NUM_SM;
                        else goto error;
                        break;
-               case 9: 
+               case 9:
                        if (strncmp(in->s, "reference", 9) == 0) sp->pvp.pvn.u.isname.name.n = SMS_TPDU_REFERENCE;
                        else goto error;
                        break;
-               case 11: 
+               case 11:
                        if (strncmp(in->s, "destination", 11) == 0) sp->pvp.pvn.u.isname.name.n = SMS_TPDU_DESTINATION;
+                       else if (strncmp(in->s, "mp_part_num", 11) == 0) sp->pvp.pvn.u.isname.name.n = SMS_UDH_CONCATSM_SEQ;
                        else goto error;
                        break;
                default:
index 2a70336..cf7c108 100644 (file)
@@ -321,6 +321,8 @@ static int t_cancel_branches_helper(sip_msg_t* msg, int n)
        if(tcx != NULL)
                idx = tcx->branch_index;
        init_cancel_info(&cancel_data);
+       /* tm function: prepare_to_cancel(struct cell *t, branch_bm_t *cancel_bm,
+                                                       branch_bm_t skip_branches) */
        switch(n) {
                case 1:
                        /* prepare cancel for every branch except idx (others) */
@@ -332,6 +334,7 @@ static int t_cancel_branches_helper(sip_msg_t* msg, int n)
                        if(msg->first_line.u.reply.statuscode>=200)
                                break;
                        cancel_data.cancel_bitmap = 1<<idx;
+                        _tmx_tmb.prepare_to_cancel(t, &cancel_data.cancel_bitmap, 0);
                        break;
                default:
                        /* prepare cancel for all branches */
index b833372..c3d2711 100644 (file)
@@ -892,7 +892,7 @@ event_route[uac:reply] {
    Return the details of a remote registration record based on a filter.
    The command has two parameter: attribute and value. The attribute can
    be: l_uuid, l_username, r_username or auth_username. The value is what
-   should be matcheg against the value of the attribute in the remote
+   should be matched against the value of the attribute in the remote
    registration record.
 
    The state of the registration is reflected in the flags field:
@@ -913,7 +913,7 @@ event_route[uac:reply] {
    Enable a remote registration record based on a filter. The command has
    two parameter: attribute and value. The attribute can be: l_uuid,
    l_username, r_username or auth_username. The value is what should be
-   matcheg against the value of the attribute in the remote registration
+   matched against the value of the attribute in the remote registration
    record.
 
    Example 1.36. uac.reg_enable usage
@@ -926,7 +926,7 @@ event_route[uac:reply] {
    Disable a remote registration record based on a filter. The command has
    two parameter: attribute and value. The attribute can be: l_uuid,
    l_username, r_username or auth_username. The value is what should be
-   matcheg against the value of the attribute in the remote registration
+   matched against the value of the attribute in the remote registration
    record.
 
    Example 1.37. uac.reg_disable usage
index 8047c67..650ec88 100644 (file)
@@ -1053,7 +1053,7 @@ event_route[uac:reply] {
                        Return the details of a remote registration record based on
                        a filter. The command has two parameter: attribute and value.
                        The attribute can be: l_uuid, l_username, r_username or auth_username.
-                       The value is what should be matcheg against the value of the attribute
+                       The value is what should be matched against the value of the attribute
                        in the remote registration record.
                </para>
                <para>
@@ -1095,7 +1095,7 @@ event_route[uac:reply] {
                        Enable a remote registration record based on
                        a filter. The command has two parameter: attribute and value.
                        The attribute can be: l_uuid, l_username, r_username or auth_username.
-                       The value is what should be matcheg against the value of the attribute
+                       The value is what should be matched against the value of the attribute
                        in the remote registration record.
                </para>
                <example>
@@ -1116,7 +1116,7 @@ event_route[uac:reply] {
                        Disable a remote registration record based on
                        a filter. The command has two parameter: attribute and value.
                        The attribute can be: l_uuid, l_username, r_username or auth_username.
-                       The value is what should be matcheg against the value of the attribute
+                       The value is what should be matched against the value of the attribute
                        in the remote registration record.
                </para>
                <example>
index ead017a..39d8aa2 100644 (file)
@@ -666,6 +666,7 @@ static void ul_rpc_add(rpc_t* rpc, void* ctx)
                return;
        }
        ci.ruid = _ul_sruid.uid;
+       ci.server_id = server_id;
 
        lock_udomain(dom, &aor);