diff --git a/src/modules/lrkproxy/Makefile b/src/modules/lrkproxy/Makefile
new file mode 100644
index 00000000000..54159bcebf0
--- /dev/null
+++ b/src/modules/lrkproxy/Makefile
@@ -0,0 +1,17 @@
+#
+# lrkproxy module makefile
+#
+#
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=lrkproxy.so
+LIBS=
+
+DEFS+=-DKAMAILIO_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+#SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
+include ../../Makefile.modules
+
diff --git a/src/modules/lrkproxy/doc/Makefile b/src/modules/lrkproxy/doc/Makefile
new file mode 100644
index 00000000000..c8d8cd5de43
--- /dev/null
+++ b/src/modules/lrkproxy/doc/Makefile
@@ -0,0 +1,4 @@
+docs = lrkproxy.xml
+
+docbook_dir = ../../../../doc/docbook
+include $(docbook_dir)/Makefile.module
diff --git a/src/modules/lrkproxy/doc/lrkproxy.xml b/src/modules/lrkproxy/doc/lrkproxy.xml
new file mode 100644
index 00000000000..bfc71580c77
--- /dev/null
+++ b/src/modules/lrkproxy/doc/lrkproxy.xml
@@ -0,0 +1,33 @@
+
+
+
+%docentities;
+
+]>
+
+
+
+ lrkproxy Module
+ &kamailioname;
+
+
+ Mojtaba
+ Esfandiari.S
+ Nasim Telecom
+
+ esfandiari@nasimtelecom.com
+
+
+
+
+ 2020
+ Nasim Telecom Inc.
+
+
+
+
+
+
diff --git a/src/modules/lrkproxy/doc/lrkproxy_admin.xml b/src/modules/lrkproxy/doc/lrkproxy_admin.xml
new file mode 100644
index 00000000000..57cc2faab30
--- /dev/null
+++ b/src/modules/lrkproxy/doc/lrkproxy_admin.xml
@@ -0,0 +1,364 @@
+
+
+
+%docentities;
+
+]>
+
+
+
+
+
+ &adminguide;
+
+
+ Overview
+
+ This is a module that enables media streams to be relayed via an
+ lrkproxy. This module works with py_lrkproxy engine in:
+ https://github.com/mojtabaesfandiari/pylrkproxy
+ This module does relaying audio streams between peers in
+ PREROUTING netfilter-hooking section in kernel-space linux.
+ The LRKProxy architecture is composed of two
+ different layers. These layers are independent of each
+ other.
+
+
+
+
+ LRKProxy Architecture
+
+ LRKP_Controlling Layer (LRKP_CL)
+
+ The first layer is developed as User-Space
+ application that allows User-Space to directly
+ access and manipulate cache data
+ buffer and packet buffer in Kernel-Space. This layer
+ gets all information about creating new sessions,
+ active sessions and tear-down sessions which is
+ gotten from SDP body during signaling plan and relay
+ them to the LRKP-Transport Stateful Layer (LRKP-
+ TSL).
+
+
+
+ LRKP_Transport Stateful Layer (LRKP_TSL)
+
+ The second layer is developed in Kernel-Space as
+ a main decision point for RTP admission controller
+ and Quickpath selector to where a received packet
+ should be forwarded with power of packet mangling
+ framework in the network stack.
+
+
+
+ The LRKP_CL and LRKP-TSL could be run as
+ independence functions on different machines. We
+ could have one LRKP_CL with multiple LRKP-TSL
+ on different machines. The LRKP_CL could works
+ with all LRKP-TSL with different strategies(lrkp_alg parameter).
+
+
+
+ Multiple LRKProxy usage
+
+ The LRKP_CL Layer can support multiple LRKP_TSL Layer
+ for balancing/distribution and control/selection purposes.
+
+
+ The module allows definition of several sets of LRKP_TSL.
+ Load-balancing will be performed over predefine algorithm by setting lrkp_alg parameter.
+
+
+
+ IMPORTANT: This module does not support balancing inside a set like as is done RTPProxy module based on
+ the weight of each rtpproxy from the set. The balancing would be run on different machine
+
+
+
+
+ Dependencies
+
+ &kamailio; Modules
+
+ The following modules must be loaded before this module:
+
+
+
+ tm module - (optional) if you want to
+ have lrkproxy_manage() fully functional
+
+
+
+
+
+
+ External Libraries or Applications
+
+ The following libraries or applications must be installed before
+ running &kamailio; with this module loaded:
+
+
+
+ None.
+
+
+
+
+
+
+ Parameters
+
+ lrkproxy_sock (string)
+
+ Used to define the list of LRKP_TSL instances to connect to. These can
+ be UNIX sockets or IPv4/IPv6 UDP sockets. Each modparam entry will
+ insert sockets into a single set with default value set ID '0'.
+ To define multiple LRKP_TSL, just add the instances in each modparam.
+
+
+
+ Default value is NONE
(disabled).
+
+
+
+ Set lrkproxy_sock parameter
+
+...
+# single lrkproxy
+modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.108:8080")
+
+# multiple lrkproxies for LB in diffenrent machine
+modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.108:8080")
+modparam("lrkproxy", "lrkproxy_sock", "udp:192.168.122.109:8080")
+
+...
+
+
+
+
+ lrkproxy_disable_tout (integer)
+
+ Once LRKP_TSL was found unreachable and marked as disabled, the
+ LRKP_CL module will not attempt to establish communication to LRKP_TSL
+ for lrkproxy_disable_tout seconds.
+
+
+
+ Default value is 60
.
+
+
+
+ Set lrkproxy_disable_tout parameter
+
+...
+modparam("lrkproxy", "lrkproxy_disable_tout", 20)
+...
+
+
+
+
+ lrkproxy_tout (integer)
+
+ Timeout value in waiting for reply from LRKP_TSL.
+
+
+
+ Default value is 1
.
+
+
+
+ Set lrkproxy_tout parameter
+
+...
+modparam("lrkproxy", "lrkproxy_tout", 2)
+...
+
+
+
+
+ lrkproxy_retr (integer)
+
+ How many times the LRKP_CL should retry to send and receive after
+ timeout was generated.
+
+
+
+ Default value is 5
.
+
+
+
+ Set lrkproxy_retr parameter
+
+...
+modparam("lrkproxy", "lrkproxy_retr", 2)
+...
+
+
+
+
+ lrkp_alg (integer)
+
+ This parameter set the algorithm of LRKP_TSL selection.
+ lrk_LINER=0,
+ lrk_RR=1
+
+
+
+ Default value is 0
.
+
+
+
+ Set lrkp_alg parameter
+
+...
+modparam("lrkproxy", "lrkp_alg", 1)
+...
+
+
+
+
+
+ hash_table_tout (integer)
+
+ Number of seconds after an lrkproxy hash table entry is marked for
+ deletion. By default, this parameter is set to 3600 (seconds).
+
+
+ To maintain information about a selected rtp machine node, for a given
+ call, entries are added in a hashtable of (callid, viabranch) pairs. When
+ command comes, lookup callid, viabranch pairs. If found, return chosen node. If not
+ found, choose a new node, insert it in the hastable and return the
+ chosen node.
+
+
+ NOTE: In the current implementation, the actual deletion happens on the
+ fly, while insert/remove/lookup the hastable, only for the entries in
+ the insert/remove/lookup path.
+
+
+ NOTE: When configuring this parameter, one should consider maximum call
+ time VS share memory for unfinished calls.
+
+
+
+ Default value is 3600
.
+
+
+
+ Set hash_table_tout parameter
+
+...
+modparam("lrkproxy", "hash_table_tout", "3600")
+...
+
+
+
+
+ hash_table_size (integer)
+
+ Size of the hash table. Default value is 128.
+
+
+
+ Default value is 128
.
+
+
+
+ Set hash_table_size parameter
+
+...
+modparam("lrkproxy", "hash_table_size", 256)
+...
+
+
+
+
+
+ Functions
+
+
+ set_lrkproxy_set(setid)
+
+
+ Sets the Id of the lrkproxy set to be used for the next
+ lrkproxy_manage() command. The parameter can be an integer or a config
+ variable holding an integer.
+
+
+ This function can be used from REQUEST_ROUTE, ONREPLY_ROUTE,
+ BRANCH_ROUTE.
+
+
+ set_lrkproxy_set usage
+
+...
+set_lrkproxy_set("0");
+lrkproxy_manage();
+...
+
+
+
+
+
+ lrkproxy_manage([flags [, ip_address]])
+
+
+ Manage the LRKProxy session - it combines the functionality of
+ lrkproxy_offer(), lrkproxy_answer() and unforce_lrkproxy(), detecting
+ internally based on message type and method which one to execute.
+
+
+ IMPORTANT:The LRKProxy just has one function relating rtp packets.
+ It does not support combination of functionality of lrkproxy_offer(),
+ lrkproxy_answer() and unforce_lrkproxy() and other etc.
+ So you have to just use lrkproxy_manage.
+
+ Meaning of the parameters is as follows:
+
+
+
+ flags - flags to turn on some features.
+
+
+
+ internal,external - The shorthand of this flag is "ie".
+ This can be used to relay media sessions between two different NIC from internal to external path.
+
+
+
+
+ external,internal - The shorthand of this flag is "ei".
+ This can be used to relay media sessions between two different NIC from external to internal path.
+
+
+
+
+ ip_address - new SDP IP address.This optional parameter is under development.
+
+
+
+ This function can be used from ANY_ROUTE.
+
+
+ lrkproxy_manage usage
+
+...
+lrkproxy_manage();
+//or
+lrkproxy_manage("ie");
+//or
+lrkproxy_manage("ei");
+
+...
+
+
+
+
+
+
+
+
diff --git a/src/modules/lrkproxy/lrkproxy.c b/src/modules/lrkproxy/lrkproxy.c
new file mode 100644
index 00000000000..25ffdec2373
--- /dev/null
+++ b/src/modules/lrkproxy/lrkproxy.c
@@ -0,0 +1,1732 @@
+/*
+ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * 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
+#include
+#include
+#include
+#include
+#ifndef __USE_BSD
+#define __USE_BSD
+#endif
+#include
+#ifndef __FAVOR_BSD
+#define __FAVOR_BSD
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../../core/flags.h"
+#include "../../core/sr_module.h"
+#include "../../core/dprint.h"
+#include "../../core/data_lump.h"
+#include "../../core/data_lump_rpl.h"
+#include "../../core/error.h"
+#include "../../core/forward.h"
+#include "../../core/mem/mem.h"
+#include "../../core/parser/parse_from.h"
+#include "../../core/parser/parse_to.h"
+#include "../../core/parser/parse_uri.h"
+#include "../../core/parser/parser_f.h"
+#include "../../core/parser/sdp/sdp.h"
+#include "../../core/resolve.h"
+#include "../../core/timer.h"
+#include "../../core/trim.h"
+#include "../../core/ut.h"
+#include "../../core/pt.h"
+#include "../../core/timer_proc.h"
+#include "../../core/rpc.h"
+#include "../../core/rpc_lookup.h"
+#include "../../core/pvar.h"
+#include "../../core/lvalue.h"
+#include "../../core/msg_translator.h"
+#include "../../core/usr_avp.h"
+#include "../../core/socket_info.h"
+#include "../../core/mod_fix.h"
+#include "../../core/dset.h"
+#include "../../core/route.h"
+#include "../../core/kemi.h"
+#include "../../modules/tm/tm_load.h"
+#include "lrkproxy.h"
+#include "lrkproxy_hash.h"
+#include "lrkproxy_funcs.h"
+
+MODULE_VERSION
+
+
+#if !defined(AF_LOCAL)
+#define AF_LOCAL AF_UNIX
+#endif
+#if !defined(PF_LOCAL)
+#define PF_LOCAL PF_UNIX
+#endif
+
+///* NAT UAC test constants */
+//#define NAT_UAC_TEST_C_1918 0x01
+//#define NAT_UAC_TEST_RCVD 0x02
+//#define NAT_UAC_TEST_V_1918 0x04
+//#define NAT_UAC_TEST_S_1918 0x08
+//#define NAT_UAC_TEST_RPORT 0x10
+
+#define DEFAULT_LRKP_SET_ID 0
+static str DEFAULT_LRKP_SET_ID_STR = str_init("0");
+
+//#define RPC_DEFAULT_NATPING_STATE 1
+
+#define RPC_MIN_RECHECK_TICKS 0
+#define RPC_MAX_RECHECK_TICKS (unsigned int)-1
+
+
+/* Supported version of the LRK proxy command protocol */
+#define SUP_CPROTOVER "20190708"
+/* Required additional version of the LRK proxy command protocol */
+#define REQ_CPROTOVER "20190709"
+/* Additional version necessary for re-packetization support */
+#define REP_CPROTOVER "20190708"
+#define PTL_CPROTOVER "20190708"
+
+#define CPORT "22333"
+#define HASH_SIZE 128
+
+static char *gencookie();
+static int lrkp_test(struct lrkp_node*);
+static int lrkp_get_config(struct lrkp_node *node);
+static int lrkp_set_conntrack_rule(struct lrkproxy_hash_entry *e);
+
+
+static int lrkproxy_force(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more);
+static int lrkproxy_unforce(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more);
+
+static int lrkproxy_manage0(struct sip_msg *msg, char *flags, char *ip);
+static int lrkproxy_manage1(struct sip_msg *msg, char *flags, char *ip);
+static int lrkproxy_manage2(struct sip_msg *msg, char *flags, char *ip);
+
+static int change_media_sdp(sip_msg_t *msg, struct lrkproxy_hash_entry *e, const char *flags, enum lrk_operation op);
+
+static int add_lrkproxy_socks(struct lrkp_set * lrkp_list, char * lrkproxy);
+static int fixup_set_id(void ** param, int param_no);
+static int set_lrkproxy_set_f(struct sip_msg * msg, char * str1, char * str2);
+
+static struct lrkp_set * select_lrkp_set(int id_set);
+
+static int lrkproxy_set_store(modparam_t type, void * val);
+static int lrkproxy_add_lrkproxy_set( char * lrk_proxies);
+
+static int mod_init(void);
+static int child_init(int);
+static void mod_destroy(void);
+
+/* Pseudo-Variables */
+//static int pv_get_lrkstat_f(struct sip_msg *, pv_param_t *, pv_value_t *);
+
+static int lrkproxy_disable_tout = 60;
+static int lrkproxy_retr = 5;
+static int lrkproxy_tout = 1;
+static pid_t mypid;
+static unsigned int myseqn = 0;
+//static str nolrkproxy_str = str_init("a=nolrkproxy:yes");
+//static str extra_id_pv_param = {NULL, 0};
+
+static char ** lrkp_strings=0;
+static int lrkp_sets=0; /*used in lrkproxy_set_store()*/
+static int lrkp_set_count = 0;
+static unsigned int current_msg_id = (unsigned int)-1;
+/* LRK proxy balancing list */
+struct lrkp_set_head * lrkp_set_list =0;
+struct lrkp_set * selected_lrkp_set =0;
+struct lrkp_set * default_lrkp_set=0;
+struct lrkp_node *selected_lrkp_node = 0;
+int lrkp_algorithm = LRK_LINER;
+static int hash_table_size = 0;
+static int hash_table_tout = 3600;
+
+
+
+//static char *ice_candidate_priority_avp_param = NULL;
+//static int ice_candidate_priority_avp_type;
+//static int_str ice_candidate_priority_avp;
+//static str lrk_inst_pv_param = {NULL, 0};
+//static pv_spec_t *lrk_inst_pvar = NULL;
+
+/* array with the sockets used by lrkproxy (per process)*/
+static unsigned int lrkp_no = 0;
+static int *lrkp_socks = 0;
+
+
+typedef struct lrkp_set_link {
+ struct lrkp_set *rset;
+ pv_spec_t *rpv;
+} lrkp_set_link_t;
+
+/* tm */
+static struct tm_binds tmb;
+
+/*0-> disabled, 1 ->enabled*/
+//unsigned int *natping_state=0;
+
+static cmd_export_t cmds[] = {
+
+ {"set_lrkproxy_set", (cmd_function)set_lrkproxy_set_f, 1,
+ fixup_set_id, 0,
+ ANY_ROUTE},
+ {"lrkproxy_manage", (cmd_function)lrkproxy_manage0, 0,
+ 0, 0,
+ ANY_ROUTE},
+ {"lrkproxy_manage", (cmd_function)lrkproxy_manage1, 1,
+ fixup_spve_null, fixup_free_spve_null,
+ ANY_ROUTE},
+ {"lrkproxy_manage", (cmd_function)lrkproxy_manage2, 2,
+ fixup_spve_spve, fixup_free_spve_spve,
+ ANY_ROUTE},
+
+ {0, 0, 0, 0, 0, 0}
+};
+
+static param_export_t params[] = {
+ {"lrkproxy_sock", PARAM_STRING|USE_FUNC_PARAM,
+ (void*)lrkproxy_set_store },
+ {"lrkproxy_disable_tout", INT_PARAM, &lrkproxy_disable_tout },
+ {"lrkproxy_retr", INT_PARAM, &lrkproxy_retr },
+ {"lrkproxy_tout", INT_PARAM, &lrkproxy_tout },
+ {"lrkp_alg", INT_PARAM, &lrkp_algorithm },
+ {"hash_table_tout", INT_PARAM, &hash_table_tout },
+ {"hash_table_size", INT_PARAM, &hash_table_size },
+
+ {0, 0, 0}
+};
+
+/** module exports */
+struct module_exports exports= {
+ "lrkproxy", /* module name */
+ DEFAULT_DLFLAGS, /* dlopen flags */
+ cmds, /* cmd exports */
+ params, /* param exports */
+ 0, /* RPC method exports */
+ 0, /* exported pseudo-variables */
+ 0, /* response handling function */
+ mod_init, /* module initialization function */
+ child_init, /* per-child init function */
+ mod_destroy /* module destroy function */
+};
+
+
+static int lrkproxy_set_store(modparam_t type, void * val){
+
+ char * p;
+ int len;
+
+ p = (char* )val;
+
+ if(p==0 || *p=='\0'){
+ return 0;
+ }
+
+ if(lrkp_sets==0){
+ lrkp_strings = (char**)pkg_malloc(sizeof(char*));
+ if(!lrkp_strings){
+ LM_ERR("no pkg memory left\n");
+ return -1;
+ }
+ } else {/*realloc to make room for the current set*/
+ lrkp_strings = (char**)pkg_reallocxf(lrkp_strings,
+ (lrkp_sets+1)* sizeof(char*));
+ if(!lrkp_strings){
+ LM_ERR("no pkg memory left\n");
+ return -1;
+ }
+ }
+
+ /*allocate for the current set of urls*/
+ len = strlen(p);
+ lrkp_strings[lrkp_sets] = (char*)pkg_malloc((len+1)*sizeof(char));
+
+ if(!lrkp_strings[lrkp_sets]){
+ LM_ERR("no pkg memory left\n");
+ return -1;
+ }
+
+ memcpy(lrkp_strings[lrkp_sets], p, len);
+ lrkp_strings[lrkp_sets][len] = '\0';
+ lrkp_sets++;
+
+ return 0;
+}
+
+struct lrkp_set *get_lrkp_set(str *const set_name)
+{
+ unsigned int this_set_id;
+ struct lrkp_set *lrkp_list;
+ if (lrkp_set_list == NULL)
+ {
+ LM_ERR("lrkp set list not configured\n");
+ return NULL;
+ }
+ /* Only integer set_names are valid at the moment */
+ if ((set_name->s == NULL) || (set_name->len == 0))
+ {
+ LM_ERR("Invalid set name '%.*s'\n", set_name->len, set_name->s);
+ return NULL;
+ }
+ if (str2int(set_name, &this_set_id) < 0)
+ {
+ LM_ERR("Invalid set name '%.*s' - must be integer\n", set_name->len, set_name->s);
+ return NULL;
+ }
+
+ lrkp_list = select_lrkp_set(this_set_id);
+
+ if(lrkp_list==NULL){ /*if a new id_set : add a new set of lrkp*/
+ lrkp_list = shm_malloc(sizeof(struct lrkp_set));
+ if(!lrkp_list){
+ LM_ERR("no shm memory left\n");
+ return NULL;
+ }
+ memset(lrkp_list, 0, sizeof(struct lrkp_set));
+ lrkp_list->id_set = this_set_id;
+ if (lrkp_set_list->lset_first == NULL)
+ {
+ lrkp_set_list->lset_first = lrkp_list;
+ } else {
+ lrkp_set_list->lset_last->lset_next = lrkp_list;
+ }
+ lrkp_set_list->lset_last = lrkp_list;
+ lrkp_set_count++;
+
+ if (this_set_id == DEFAULT_LRKP_SET_ID)
+ {
+ default_lrkp_set = lrkp_list;
+ }
+ }
+ return lrkp_list;
+}
+
+int insert_lrkp_node(struct lrkp_set *const lrkp_list, const str *const url, const int weight, const int enable)
+{
+ struct lrkp_node *pnode;
+
+ if ((pnode = shm_malloc(sizeof(struct lrkp_node) + url->len + 1)) == NULL)
+ {
+ LM_ERR("out of shm memory\n");
+ return -1;
+ }
+
+ memset(pnode, 0, sizeof(struct lrkp_node) + url->len + 1);
+
+
+ struct lrkp_node_conf *node_conf;
+ node_conf = shm_malloc(sizeof(struct lrkp_node_conf));
+ if (!node_conf)
+ {
+ LM_ERR("out of shm memory\n");
+ return -1;
+ }
+
+ memset(node_conf, 0, sizeof(struct lrkp_node_conf));
+ pnode->lrkp_n_c = node_conf;
+
+ pnode->idx = lrkp_no++;
+ pnode->ln_weight = weight;
+ pnode->ln_umode = 0;
+ pnode->ln_enable = enable;
+ /* Permanently disable if marked as disabled */
+// pnode->ln_recheck_ticks = disabled ? RPC_MAX_RECHECK_TICKS : 0;
+ pnode->ln_url.s = (char*)(pnode + 1);
+ memcpy(pnode->ln_url.s, url->s, url->len);
+ pnode->ln_url.len = url->len;
+
+ LM_DBG("url is '%.*s'\n", pnode->ln_url.len, pnode->ln_url.s);
+
+ /* Find protocol and store address */
+ pnode->ln_address = pnode->ln_url.s;
+ if (strncasecmp(pnode->ln_address, "udp:", 4) == 0) {
+ pnode->ln_umode = 1;
+ pnode->ln_address += 4;
+ } else if (strncasecmp(pnode->ln_address, "udp6:", 5) == 0) {
+ pnode->ln_umode = 6;
+ pnode->ln_address += 5;
+ } else if (strncasecmp(pnode->ln_address, "unix:", 5) == 0) {
+ pnode->ln_umode = 0;
+ pnode->ln_address += 5;
+ }
+
+ if (lrkp_list->ln_first == NULL)
+ {
+ lrkp_list->ln_first = pnode;
+ } else {
+ lrkp_list->ln_last->ln_next = pnode;
+ }
+ lrkp_list->ln_last = pnode;
+ lrkp_list->lrkp_node_count++;
+
+ return 0;
+}
+
+static int add_lrkproxy_socks(struct lrkp_set * lrkp_list,
+ char * lrkproxy){
+ /* Make lrk proxies list. */
+ char *p, *p1, *p2, *plim;
+ int weight;
+ str url;
+
+ p = lrkproxy;
+ plim = p + strlen(p);
+
+ for(;;) {
+ weight = 1;
+ while (*p && isspace((int)*p))
+ ++p;
+ if (p >= plim)
+ break;
+ p1 = p;
+ while (*p && !isspace((int)*p))
+ ++p;
+ if (p <= p1)
+ break; /* may happen??? */
+ /* Have weight specified? If yes, scan it */
+ p2 = memchr(p1, '=', p - p1);
+ if (p2 != NULL) {
+ weight = strtoul(p2 + 1, NULL, 10);
+ } else {
+ p2 = p;
+ }
+
+ url.s = p1;
+ url.len = (p2-p1);
+ insert_lrkp_node(lrkp_list, &url, weight, 0);
+ }
+ return 0;
+}
+
+/* 0-succes
+ * -1 - erorr
+ * */
+static int lrkproxy_add_lrkproxy_set( char * lrk_proxies)
+{
+ char *p,*p2;
+ struct lrkp_set * lrkp_list;
+ str id_set;
+
+ /* empty definition? */
+ p= lrk_proxies;
+ if(!p || *p=='\0'){
+ return 0;
+ }
+
+ for(;*p && isspace(*p);p++);
+ if(*p=='\0'){
+ return 0;
+ }
+
+ lrk_proxies = strstr(p, "==");
+ if(lrk_proxies){
+ if(*(lrk_proxies +2)=='\0'){
+ LM_ERR("script error -invalid lrk proxy list!\n");
+ return -1;
+ }
+
+ *lrk_proxies = '\0';
+ p2 = lrk_proxies-1;
+ for(;isspace(*p2); *p2 = '\0',p2--);
+ id_set.s = p; id_set.len = p2 - p+1;
+
+ if(id_set.len <= 0){
+ LM_ERR("script error -invalid set_id value!\n");
+ return -1;
+ }
+
+ lrk_proxies+=2;
+ }else{
+ lrk_proxies = p;
+ id_set = DEFAULT_LRKP_SET_ID_STR;
+ }
+
+ for(;*lrk_proxies && isspace(*lrk_proxies);lrk_proxies++);
+
+ if(!(*lrk_proxies)){
+ LM_ERR("script error -empty lrkproxy list\n");
+ return -1;;
+ }
+
+ lrkp_list = get_lrkp_set(&id_set);
+ if (lrkp_list == NULL)
+ {
+ LM_ERR("Failed to get or create lrkp_list for '%.*s'\n", id_set.len, id_set.s);
+ return -1;
+ }
+
+ if(add_lrkproxy_socks(lrkp_list, lrk_proxies)!= 0){
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int fixup_set_id(void ** param, int param_no)
+{
+ int int_val, err;
+ struct lrkp_set* lrkp_list;
+ lrkp_set_link_t *lrkl = NULL;
+ str s;
+
+ lrkl = (lrkp_set_link_t*)pkg_malloc(sizeof(lrkp_set_link_t));
+ if(lrkl==NULL) {
+ LM_ERR("no more pkg memory\n");
+ return -1;
+ }
+ memset(lrkl, 0, sizeof(lrkp_set_link_t));
+ s.s = (char*)*param;
+ s.len = strlen(s.s);
+
+ if(s.s[0] == PV_MARKER) {
+ int_val = pv_locate_name(&s);
+ if(int_val<0 || int_val!=s.len) {
+ LM_ERR("invalid parameter %s\n", s.s);
+ pkg_free(lrkl);
+ return -1;
+ }
+ lrkl->rpv = pv_cache_get(&s);
+ if(lrkl->rpv == NULL) {
+ LM_ERR("invalid pv parameter %s\n", s.s);
+ pkg_free(lrkl);
+ return -1;
+ }
+ } else {
+ int_val = str2s(*param, strlen(*param), &err);
+ if (err == 0) {
+ pkg_free(*param);
+ if((lrkp_list = select_lrkp_set(int_val)) ==0){
+ LM_ERR("lrkp_proxy set %i not configured\n", int_val);
+ pkg_free(lrkl);
+ return E_CFG;
+ }
+ lrkl->rset = lrkp_list;
+ } else {
+ LM_ERR("bad number <%s>\n", (char *)(*param));
+ pkg_free(lrkl);
+ return E_CFG;
+ }
+ }
+ *param = (void*)lrkl;
+ return 0;
+}
+
+
+static int
+mod_init(void)
+{
+ int i;
+// pv_spec_t avp_spec;
+// str s;
+// unsigned short avp_flags;
+
+// if(lrkproxy_rpc_init()<0)
+// {
+// LM_ERR("failed to register RPC commands\n");
+// return -1;
+// }
+
+ /* Configure the head of the lrkp_set_list */
+ lrkp_set_list = shm_malloc(sizeof(struct lrkp_set_head));
+ if (lrkp_set_list == NULL)
+ {
+ LM_ERR("no shm memory for lrkp_set_list\n");
+ return -1;
+ }
+ memset(lrkp_set_list, 0, sizeof(struct lrkp_set_head));
+
+
+ /* storing the list of lrk proxy sets in shared memory*/
+ for(i=0;i callid& via-branch relation */
+ if (hash_table_size < 1){
+ hash_table_size = HASH_SIZE; //the default size 128 entry.
+ }
+
+ if (!lrkproxy_hash_table_init(hash_table_size)) {
+ LM_ERR("lrkproxy_hash_table_init(%d) failed!\n", hash_table_size);
+ return -1;
+ } else {
+// LM_DBG("lrkproxy_hash_table_init(%d) success!\n", hash_table_size);
+ LM_INFO(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table_init(%d) success!\n", hash_table_size);
+ }
+
+
+
+ /* load tm module*/
+ if (load_tm_api( &tmb ) < 0)
+ {
+ LM_DBG("could not load the TM-functions - answer-offer model"
+ " auto-detection is disabled\n");
+ memset(&tmb, 0, sizeof(struct tm_binds));
+ }
+
+ return 0;
+}
+
+
+static int
+child_init(int rank)
+{
+ int n;
+ char *cp;
+ struct addrinfo hints, *res;
+ struct lrkp_set *lrkp_list;
+ struct lrkp_node *pnode;
+
+ if(lrkp_set_list==NULL )
+ return 0;
+
+ /* do not init sockets for PROC_INIT and main process when fork=yes */
+ if(rank==PROC_INIT || (rank==PROC_MAIN && dont_fork==0)) {
+ return 0;
+ }
+
+ /* Iterate known LRK proxies - create sockets */
+ mypid = getpid();
+
+ lrkp_socks = (int*)pkg_malloc( sizeof(int)*lrkp_no );
+ if (lrkp_socks==NULL) {
+ LM_ERR("no more pkg memory\n");
+ return -1;
+ }
+ memset(lrkp_socks, -1, sizeof(int)*lrkp_no);
+
+ for(lrkp_list = lrkp_set_list->lset_first; lrkp_list != 0;
+ lrkp_list = lrkp_list->lset_next){
+
+ for (pnode=lrkp_list->ln_first; pnode!=0; pnode = pnode->ln_next){
+ char *hostname;
+
+ if (pnode->ln_umode == 0) {
+ lrkp_socks[pnode->idx] = -1;
+ goto rptest;
+ }
+
+ /*
+ * This is UDP or UDP6. Detect host and port; lookup host;
+ * do connect() in order to specify peer address
+ */
+ hostname = (char*)pkg_malloc(sizeof(char) * (strlen(pnode->ln_address) + 1));
+ if (hostname==NULL) {
+ LM_ERR("no more pkg memory\n");
+ return -1;
+ }
+ strcpy(hostname, pnode->ln_address);
+
+ cp = strrchr(hostname, ':');
+ if (cp != NULL) {
+ *cp = '\0';
+ cp++;
+ }
+ if (cp == NULL || *cp == '\0')
+ cp = CPORT;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = 0;
+ hints.ai_family = (pnode->ln_umode == 6) ? AF_INET6 : AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ if ((n = getaddrinfo(hostname, cp, &hints, &res)) != 0) {
+ LM_ERR("%s\n", gai_strerror(n));
+ pkg_free(hostname);
+ return -1;
+ }
+ pkg_free(hostname);
+
+ lrkp_socks[pnode->idx] = socket((pnode->ln_umode == 6)
+ ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
+ if ( lrkp_socks[pnode->idx] == -1) {
+ LM_ERR("can't create socket\n");
+ freeaddrinfo(res);
+ return -1;
+ }
+
+ if (connect( lrkp_socks[pnode->idx], res->ai_addr, res->ai_addrlen) == -1) {
+ LM_ERR("can't connect to a LRK proxy\n");
+ close( lrkp_socks[pnode->idx] );
+ lrkp_socks[pnode->idx] = -1;
+ freeaddrinfo(res);
+ return -1;
+ }
+ freeaddrinfo(res);
+rptest:
+ pnode->ln_enable = lrkp_test(pnode);
+ if (pnode->ln_enable) { //get lrk proxy config if it is enable.
+// LM_INFO("lrkp_test test is calling here\n"); //enable next line.
+ lrkp_get_config(pnode);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static void mod_destroy(void)
+{
+ struct lrkp_set * crt_list, * last_list;
+ struct lrkp_node * crt_lrkp, *last_lrkp;
+
+ /*free the shared memory*/
+// if (natping_state)
+// shm_free(natping_state);
+
+ if(lrkp_set_list == NULL)
+ return;
+
+ for(crt_list = lrkp_set_list->lset_first; crt_list != NULL; ){
+
+ for(crt_lrkp = crt_list->ln_first; crt_lrkp != NULL; ){
+
+ last_lrkp = crt_lrkp;
+ crt_lrkp = last_lrkp->ln_next;
+ shm_free(last_lrkp);
+ }
+
+ last_list = crt_list;
+ crt_list = last_list->lset_next;
+ shm_free(last_list);
+ }
+
+ shm_free(lrkp_set_list);
+
+ /* destroy the hash table */
+ if (!lrkproxy_hash_table_destroy()) {
+ LM_ERR("lrkproxy_hash_table_destroy() failed!\n");
+ } else {
+ LM_DBG("lrkproxy_hash_table_destroy() success!\n");
+ }
+
+}
+
+
+static char * gencookie(void)
+{
+ static char cook[34];
+
+ sprintf(cook, "%d_%u ", (int)mypid, myseqn);
+ myseqn++;
+ return cook;
+}
+
+static int lrkp_test(struct lrkp_node *node)
+{
+ int buflen = 256;
+ char buf[buflen];
+ struct iovec v[2] = {{NULL, 0}, {"P", 1}};
+
+ memset(buf, 0, buflen);
+ memcpy(buf, send_lrkp_command(node, v, 2, 0), buflen);
+
+// if (buf == NULL) {
+ if (!buf[0]) {
+ LM_ERR("can't ping the lrk proxy %s, Disable it right now.\n", node->ln_url.s);
+ return 0;
+ }
+
+ char *resp = buf + v[0].iov_len + v[1].iov_len + 1;
+ if (memcmp(resp, "PONG", 4) == 0)
+// LM_DBG("Recieve PONG response from lrk proxy server %s, Enable it right now.\n", node->ln_url.s);
+ LM_INFO("Recieve PONG response from lrk proxy server %s, Enable it right now.\n", node->ln_url.s);
+
+ return 1;
+
+}
+
+static int lrkp_get_config(struct lrkp_node *node){
+
+ int buflen = 256;
+ char buf[buflen];
+ struct iovec v[2] = {{NULL, 0}, {"G", 1}};
+ struct lrkp_node_conf *lnconf = NULL;
+
+ memset(buf, 0, buflen);
+ memcpy(buf, send_lrkp_command(node, v, 2, 0), buflen);
+
+// if (buf == NULL) {
+ if (!buf[0]) {
+ LM_ERR("can't get config of the lrk proxy %s, Disable it right now.\n", node->ln_url.s);
+ return 0;
+ }
+
+ lnconf = (struct lrkp_node_conf *)(buf + v[0].iov_len + v[1].iov_len + 1);
+
+ if (lnconf == NULL){
+ LM_ERR("can't get config of the lrk proxy %s, Disable it right now.\n", node->ln_url.s);
+ return 0;
+ }
+
+
+ memcpy(node->lrkp_n_c, lnconf, sizeof(struct lrkp_node_conf));
+
+// node->lrkp_n_c = lnconf;
+ LM_INFO("the lrk proxy %s is configured successfully right now.\n", node->ln_url.s);
+ LM_INFO("buffer internal:%s\n", node->lrkp_n_c->internal_ip);
+ LM_INFO("buffer external:%s\n", node->lrkp_n_c->external_ip);
+ LM_INFO("buffer start_port:%d\n", node->lrkp_n_c->start_port);
+ LM_INFO("buffer end_port:%d\n", node->lrkp_n_c->end_port);
+ LM_INFO("buffer current_port:%d\n", node->lrkp_n_c->current_port);
+
+ return 1;
+
+
+}
+
+static int lrkp_set_conntrack_rule(struct lrkproxy_hash_entry *e) {
+ int buflen = 254;
+ char buf[buflen];
+ int v_len = 0;
+
+ char src_ipv4[20];
+ char src_port[20];
+ char dst_ipv4[20];
+ char dst_port[20];
+ char snat_ipv4[20];
+ char snat_port[20];
+ char dnat_ipv4[20];
+ char dnat_port[20];
+ char timeout[20];
+ char callid[50];
+
+ struct iovec v[] = {
+ {NULL, 0}, /* reserved (cookie) */
+ {"S", 1}, /* command & common options */
+ {NULL, 0}, /* src_ipv4 */
+ {NULL, 0}, /* dst_ipnv4 */
+ {NULL, 0}, /* snat_ipv4 */
+ {NULL, 0}, /* dnat_ipv4 */
+ {NULL, 0}, /* src_port */
+ {NULL, 0}, /* dst_port*/
+ {NULL, 0}, /* snat_port */
+ {NULL, 0}, /* dnat_port*/
+ {NULL, 0}, /* timeout to clear conntrack entry*/
+ {NULL, 0}, /* callid of session */
+ };
+
+ v_len += v[1].iov_len;
+
+ //set src_ipv4 to buffer.
+ sprintf(src_ipv4, " %.*s ", e->src_ipv4.len, e->src_ipv4.s);
+ v[2].iov_base = src_ipv4;
+ v[2].iov_len = strlen(v[2].iov_base);
+ v_len += v[2].iov_len;
+
+ //set dst_ipv4 to buffer.
+ sprintf(dst_ipv4, "%.*s ", e->dst_ipv4.len, e->dst_ipv4.s);
+ v[3].iov_base = dst_ipv4;
+ v[3].iov_len = strlen(v[3].iov_base);
+ v_len += v[3].iov_len;
+
+ //set snat_ipv4 to buffer.
+ sprintf(snat_ipv4, "%.*s ", e->snat_ipv4.len, e->snat_ipv4.s);
+ v[4].iov_base = snat_ipv4;
+ v[4].iov_len = strlen(v[4].iov_base);
+ v_len += v[4].iov_len;
+
+ //set dnat_ipv4 to buffer.
+ sprintf(dnat_ipv4, "%.*s ", e->dnat_ipv4.len, e->dnat_ipv4.s);
+ v[5].iov_base = dnat_ipv4;
+ v[5].iov_len = strlen(v[5].iov_base);
+ v_len += v[5].iov_len;
+
+ //set src_port to buffer.
+ sprintf(src_port, "%.*s ", e->src_port.len, e->src_port.s);
+ v[6].iov_base = src_port;
+ v[6].iov_len = strlen(v[6].iov_base);
+ v_len += v[6].iov_len;
+
+ //set dst_port to buffer.
+ sprintf(dst_port, "%.*s ", e->dst_port.len, e->dst_port.s);
+ v[7].iov_base = dst_port;
+ v[7].iov_len = strlen(v[7].iov_base);
+ v_len += v[7].iov_len;
+
+ //set snat_port to buffer.
+ sprintf(snat_port, "%.*s ", e->snat_port.len, e->snat_port.s);
+ v[8].iov_base = snat_port;
+ v[8].iov_len = strlen(v[8].iov_base);
+ v_len += v[8].iov_len;
+
+ //set dnat_port to buffer.
+ sprintf(dnat_port, "%.*s ", e->dnat_port.len, e->dnat_port.s);
+ v[9].iov_base = dnat_port;
+ v[9].iov_len = strlen(v[9].iov_base);
+ v_len += v[9].iov_len;
+
+ //set timeout to buffer. Set to 60 sec for default.
+ sprintf(timeout, "%d ", 60);
+ v[10].iov_base = timeout;
+ v[10].iov_len = strlen(v[10].iov_base);
+ v_len += v[10].iov_len;
+
+ //set callid to buffer.
+ sprintf(callid, "%.*s ", e->callid.len, e->callid.s);
+ v[11].iov_base = callid;
+ v[11].iov_len = strlen(v[11].iov_base);
+ v_len += v[11].iov_len;
+// LM_ERR("e->callid.len is:%d right now.\n\n", e->callid.len);
+
+ memset(buf, 0, buflen);
+ memcpy(buf, send_lrkp_command(e->node, v, 12, v_len), buflen);
+//
+
+// if (buf == NULL) {
+ if (!buf[0]) {
+ LM_ERR("can't ping the lrk proxy %s, Disable it right now.\n", e->node->ln_url.s);
+ return 0;
+ }
+
+ v_len += v[0].iov_len;
+
+
+// char *resp = buf + v[0].iov_len + v[1].iov_len + v[2].iov_len;
+ char *resp = buf + v_len;
+ if (memcmp(resp, "OK", 2) == 0) {
+ LM_INFO("Recieve OK response from lrk proxy server %s, Rule set successfully.\n", e->node->ln_url.s);
+ LM_DBG("Recieve OK response from lrk proxy server %s, Rule set successfully.\n", e->node->ln_url.s);
+ }
+ return 1;
+
+}
+
+
+char *send_lrkp_command(struct lrkp_node *node, struct iovec *v, int vcnt, int more)
+{
+ struct sockaddr_un addr;
+ int fd, len, i;
+// char *cp;
+ static char buf[256];
+ struct pollfd fds[1];
+
+ memset(buf, 0, 256);
+ len = 0;
+// cp = buf;
+ if (node->ln_umode == 0) {
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_LOCAL;
+ strncpy(addr.sun_path, node->ln_address,
+ sizeof(addr.sun_path) - 1);
+#ifdef HAVE_SOCKADDR_SA_LEN
+ addr.sun_len = strlen(addr.sun_path);
+#endif
+
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (fd < 0) {
+ LM_ERR("can't create socket\n");
+ goto badproxy;
+ }
+ if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(fd);
+ LM_ERR("can't connect to lrk proxy\n");
+ goto badproxy;
+ }
+
+ do {
+ len = writev(fd, v + 1, vcnt - 1);
+ } while (len == -1 && errno == EINTR);
+ if (len <= 0) {
+ close(fd);
+ LM_ERR("can't send command to a lrk proxy %s\n", node->ln_url.s);
+ goto badproxy;
+ }
+ do {
+ len = read(fd, buf, sizeof(buf) - 1);
+ } while (len == -1 && errno == EINTR);
+ close(fd);
+ if (len <= 0) {
+ LM_ERR("can't read reply from a lrk proxy %s\n", node->ln_url.s);
+ goto badproxy;
+ }
+ } else {
+ fds[0].fd = lrkp_socks[node->idx];
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ /* Drain input buffer */
+ while ((poll(fds, 1, 0) == 1) &&
+ ((fds[0].revents & POLLIN) != 0)) {
+ recv(lrkp_socks[node->idx], buf, sizeof(buf) - 1, 0);
+ fds[0].revents = 0;
+ }
+ v[0].iov_base = gencookie();
+ v[0].iov_len = strlen(v[0].iov_base);
+ for (i = 0; i < lrkproxy_retr; i++) {
+ do {
+ len = writev(lrkp_socks[node->idx], v, vcnt);
+ } while (len == -1 && (errno == EINTR || errno == ENOBUFS));
+ if (len <= 0) {
+ LM_ERR("can't send command to a lrk proxy %s\n", node->ln_url.s);
+ goto badproxy;
+ }
+ while ((poll(fds, 1, lrkproxy_tout * 1000) == 1) &&
+ (fds[0].revents & POLLIN) != 0) {
+ do {
+ len = recv(lrkp_socks[node->idx], buf, sizeof(buf) - 1, 0);
+ } while (len == -1 && errno == EINTR);
+ if (len <= 0) {
+ LM_ERR("can't read reply from a lrk proxy %s\n", node->ln_url.s);
+ goto badproxy;
+ }
+ if (len >= (v[0].iov_len - 1) &&
+ memcmp(buf, v[0].iov_base, (v[0].iov_len - 1)) == 0) { //check coocke validation.
+ char *command = buf + v[0].iov_len;
+ switch (*command) {
+ case 'P':
+ if (len == v[0].iov_len + v[1].iov_len + 4 + 1)
+ goto out;
+// break;
+ case 'G':
+ if (len == v[0].iov_len + v[1].iov_len + sizeof(struct lrkp_node_conf) + 1)
+ goto out;
+// break;
+ case 'S':
+ if (len == more + v[0].iov_len + 2)
+ goto out;
+// break;
+ }
+
+ }
+ fds[0].revents = 0;
+ }
+ }
+ if (i == lrkproxy_tout) {
+ LM_ERR("timeout waiting reply from a lrk proxy server %s\n", node->ln_url.s);
+ goto badproxy;
+
+ }
+ }
+ out:
+ return buf;
+ badproxy:
+ LM_ERR("lrk proxy <%s> does not respond, disable it\n", node->ln_url.s);
+ node->ln_enable = 0;
+// node->ln_recheck_ticks = get_ticks() + lrkproxy_disable_tout;
+ return buf;
+}
+
+/*
+ * select the set with the id_set id
+ */
+static struct lrkp_set * select_lrkp_set(int id_set ){
+
+ struct lrkp_set * lrkp_list;
+ /*is it a valid set_id?*/
+
+ if(!lrkp_set_list)
+ {
+ LM_ERR("lrkproxy set list not initialised\n");
+ return NULL;
+ }
+
+ for(lrkp_list=lrkp_set_list->lset_first; lrkp_list!=NULL &&
+ lrkp_list->id_set!=id_set; lrkp_list=lrkp_list->lset_next);
+
+ return lrkp_list;
+}
+
+
+struct lrkp_node *select_lrkp_node(int do_test)
+{
+// unsigned sum, sumcut, weight_sum;
+ unsigned weight_sum;
+ struct lrkp_node* node;
+ int was_forced;
+ int was_forced2;
+ int was_forced3;
+
+ if(!selected_lrkp_set){
+ LM_ERR("script error -no valid set selected\n");
+ return NULL;
+ }
+ /* Most popular case: 1 proxy, nothing to calculate */
+ if (selected_lrkp_set->lrkp_node_count == 1) {
+ node = selected_lrkp_set->ln_first;
+// if (node->rn_disabled && node->rn_recheck_ticks <= get_ticks())
+ if (!node->ln_enable) {
+ node->ln_enable = lrkp_test(node);
+ if (node->ln_enable) { //get lrk proxy config if it is enable.
+ lrkp_get_config(node);
+ return node;
+ }
+ }
+ return node->ln_enable ? node : NULL;
+// return NULL;
+ }
+
+
+ /* Check node is enable and test it again*/
+ was_forced = 0;
+retry:
+ weight_sum = 0;
+ for (node=selected_lrkp_set->ln_first; node!=NULL; node=node->ln_next) {
+
+ if (!node->ln_enable) {
+ /* Try to enable if it's time to try. */
+ node->ln_enable = lrkp_test(node);
+ if (node->ln_enable) //get lrk proxy config if it is enable.
+ lrkp_get_config(node);
+ }
+
+// if (!node->rn_disabled)
+// weight_sum += node->rn_weight;
+ if (node->ln_enable)
+ weight_sum += node->ln_weight;
+ }
+
+ if (weight_sum == 0) {
+ /* No proxies? Force all to be redetected, if not yet */
+ if (was_forced)
+ return NULL;
+ was_forced = 1;
+// for(node=selected_lrkp_set->ln_first; node!=NULL; node=node->ln_next) {
+// node->ln_enable = lrkp_test(node);
+// }
+ goto retry;
+ }
+
+ if (lrkp_algorithm == LRK_LINER) {
+ was_forced2 = 0;
+retry2:
+ for (node=selected_lrkp_set->ln_first; node != NULL; node = node->ln_next)
+ if (node->ln_enable)
+ goto found;
+ was_forced2 = 1;
+ if (was_forced2)
+ return NULL;
+
+ goto retry2;
+ }
+ else if(lrkp_algorithm == LRK_RR) {
+ was_forced3 = 0;
+retry3:
+ if (!selected_lrkp_node) {
+ selected_lrkp_node = selected_lrkp_set->ln_first;
+ was_forced3 = 1;
+ }
+ for (node = selected_lrkp_node; node != NULL; node = node->ln_next) {
+ if (!node->ln_enable)
+ continue;
+ selected_lrkp_node = node->ln_next;
+// if (sumcut < node->ln_weight)
+ goto found;
+// sumcut -= node->ln_weight;
+ }
+
+ if (was_forced3)
+ return NULL;
+
+ selected_lrkp_node = NULL;
+ goto retry3;
+ }
+
+ found:
+ if (do_test) {
+// //todo...
+ node->ln_enable = lrkp_test(node);
+ if (!node->ln_enable)
+ goto retry;
+ }
+ return node;
+}
+
+//static int change_media_sdp(sip_msg_t *msg, struct lrkp_node *n, const char *flags, int type) {
+static int change_media_sdp(sip_msg_t *msg, struct lrkproxy_hash_entry *e, const char *flags, enum lrk_operation op) {
+ struct lump *l;
+ str body;
+ str newbody;
+
+ int len;
+ char *start_sdp_o = NULL; //"o=";
+ char *start_sdp_s = NULL; //"s=";
+ char *start_sdp_c = NULL; //"c=IN IP4";
+ char *start_sdp_m = NULL; //"m=audio";
+ char *ip_selected = NULL;
+ char *sdp_param_start = NULL;
+ char *sdp_param_end = NULL;
+ char *off=NULL;
+ char sdp_new_o[128];
+ char sdp_new_s[128];
+ char sdp_new_c[128];
+ char sdp_new_m[128];
+
+ body.s = get_body(msg);
+ if (body.s == 0) {
+ LM_ERR("failed to get the message body\n");
+ return -1;
+ }
+
+ body.len = msg->len - (int) (body.s - msg->buf);
+ if (body.len == 0) {
+ LM_DBG("message body has zero length\n");
+ return -1;
+ }
+// LM_INFO("body:<%.*s>\n", body.len, body.s);
+
+ //allocate new buffer to new sdp buffer.
+ newbody.len = 1024;
+ newbody.s = pkg_malloc(newbody.len);
+ if (newbody.s == NULL) {
+ LM_ERR("out of pkg memory\n");
+ return -1;
+ }
+ memset(newbody.s, 0, 1024);
+
+ off = body.s;
+ start_sdp_o = strstr(off, "o=");
+ start_sdp_s = strstr(off, "s=");
+ start_sdp_c = strstr(off, "c=IN IP4");
+ start_sdp_m = strstr(off, "m=audio");
+
+ //The external_ip should be set in config file for relaying RTP media between NIC.
+ if ((e->node->lrkp_n_c->external_ip != NULL) && flags) {
+ if (strstr(flags, "ei")) {
+ ip_selected = e->node->lrkp_n_c->internal_ip;// lrk_node->internal_ip;
+ } else if (strstr(flags, "ie")) {
+ ip_selected = e->node->lrkp_n_c->external_ip; //lrk_node->external_ip;
+ } else {
+ LM_INFO("no flags found\n");
+ return 0;
+ }
+ } else {
+ ip_selected = e->node->lrkp_n_c->internal_ip;
+ }
+
+ if (op == OP_OFFER) {
+ e->dst_ipv4.s = ip_selected;
+ e->dst_ipv4.len = strlen(e->dst_ipv4.s);
+
+ str current_port;
+ current_port.s = int2str(e->node->lrkp_n_c->current_port, ¤t_port.len);
+
+ if (shm_str_dup(&e->dst_port, ¤t_port) < 0) {
+ LM_ERR("lrkproxy fail to insert dst_port, calllen=%d dst_port=%.*s\n",
+ e->callid.len, current_port.len, current_port.s);
+ lrkproxy_hash_table_free_entry(e);
+ return 0;
+ }
+
+// e->dst_port = e->node->lrkp_n_c->current_port;
+ }
+ else if (op == OP_ANSWER){
+ e->snat_ipv4.s = ip_selected;
+ e->snat_ipv4.len = strlen(e->snat_ipv4.s);
+
+ str current_port;
+ unsigned int snat_port;
+
+ str2int(&e->dst_port, &snat_port);
+ snat_port += 2;
+
+ current_port.s = int2str(snat_port, ¤t_port.len);
+
+ if (shm_str_dup(&e->snat_port, ¤t_port) < 0) {
+ LM_ERR("lrkproxy fail to insert snat_port, calllen=%d snat_port=%.*s\n",
+ e->callid.len, current_port.len, current_port.s);
+ lrkproxy_hash_table_free_entry(e);
+ return 0;
+ }
+
+// e->snat_port = e->dst_port + 2;
+ }
+
+
+ while (*off != EOB) //while end of body.
+ {
+ sdp_param_start = off;
+ sdp_param_end = sdp_param_start;
+ while (*sdp_param_end != CR && *sdp_param_end != LF && *sdp_param_end != EOB) sdp_param_end++;
+ len = (int) (sdp_param_end - sdp_param_start);
+ if ((int) (start_sdp_o - off) == 0) {
+ memset(sdp_new_o, 0, 128);
+ snprintf(sdp_new_o, 128, "o=lrkproxy %s %s IN IP4 %s\r", SUP_CPROTOVER, REQ_CPROTOVER, ip_selected);
+ strncat(newbody.s, sdp_new_o, strlen(sdp_new_o));
+ off += len + 1;
+ continue;
+ }
+ if ((int) (start_sdp_s - off) == 0) {
+ memset(sdp_new_s, 0, 128);
+ snprintf(sdp_new_s, 128, "s=lrkproxy Support only Audio Call\r");
+ strncat(newbody.s, sdp_new_s, strlen(sdp_new_s));
+ off += len + 1;
+ continue;
+ }
+ if ((int) (start_sdp_c - off) == 0) {
+ memset(sdp_new_c, 0, 128);
+ snprintf(sdp_new_c, 128, "c=IN IP4 %s\r", ip_selected);
+ strncat(newbody.s, sdp_new_c, strlen(sdp_new_c));
+ off += len + 1;
+ continue;
+ }
+ if ((int)(start_sdp_m - off) == 0){
+ memset(sdp_new_m, 0, 128);
+ char *avp_flags = off;
+// int occure = 0;
+ for (;*avp_flags && !isspace(*avp_flags); avp_flags++);
+ for (avp_flags++;*avp_flags && !isspace(*avp_flags); avp_flags++);
+ avp_flags++;
+ if (op == OP_OFFER)
+ snprintf(sdp_new_m, 128, "m=audio %.*s %.*s\r",e->dst_port.len, e->dst_port.s, (int)(len - (avp_flags-off)), avp_flags);
+// snprintf(sdp_new_m, 128, "m=audio %d %.*s\r",e->node->lrkp_n_c->current_port, (int)(len - (avp_flags-off)), avp_flags);
+ else if (op == OP_ANSWER)
+ snprintf(sdp_new_m, 128, "m=audio %.*s %.*s\r",e->snat_port.len, e->snat_port.s, (int)(len - (avp_flags-off)), avp_flags);
+// snprintf(sdp_new_m, 128, "m=audio %d %.*s\r",e->node->lrkp_n_c->current_port, (int)(len - (avp_flags-off)), avp_flags);
+// printf("%.*s\n\n", len - (avp_flags-off), avp_flags);
+ strncat(newbody.s,sdp_new_m, strlen(sdp_new_m));
+ off += len+1;
+ continue;
+ }
+
+ strncat(newbody.s, off, len + 1);
+ off += len + 1;
+ }
+
+
+// LM_INFO("%.*s", (int)strlen(newbody.s), newbody.s);
+ l = del_lump(msg, body.s - msg->buf, body.len, 0);
+ if (!l) {
+ LM_ERR("del_lump failed\n");
+ return -1;
+ }
+
+
+ if (insert_new_lump_after(l, newbody.s, strlen(newbody.s), 0) == 0) {
+ LM_ERR("could not insert new lump\n");
+ pkg_free(newbody.s);
+ return -1;
+ }
+
+ LM_BUG("Insert_new_lump successfully\n");
+
+ return 1;
+}
+
+/* This function assumes p points to a line of requested type. */
+
+ static int
+set_lrkproxy_set_f(struct sip_msg * msg, char * str1, char * str2)
+{
+ lrkp_set_link_t *lrkl;
+ pv_value_t val;
+
+ lrkl = (lrkp_set_link_t*)str1;
+
+ current_msg_id = 0;
+ selected_lrkp_set = 0;
+
+ if(lrkl->rset != NULL) {
+ current_msg_id = msg->id;
+ selected_lrkp_set = lrkl->rset;
+ } else {
+ if(pv_get_spec_value(msg, lrkl->rpv, &val)<0) {
+ LM_ERR("cannot evaluate pv param\n");
+ return -1;
+ }
+ if(!(val.flags & PV_VAL_INT)) {
+ LM_ERR("pv param must hold an integer value\n");
+ return -1;
+ }
+ selected_lrkp_set = select_lrkp_set(val.ri);
+ if(selected_lrkp_set==NULL) {
+ LM_ERR("could not locate lrkproxy set %d\n", val.ri);
+ return -1;
+ }
+ current_msg_id = msg->id;
+ }
+ return 1;
+}
+
+static int
+lrkproxy_manage(struct sip_msg *msg, char *flags, char *ip)
+{
+ int method;
+ int nosdp;
+ tm_cell_t *t = NULL;
+
+ if (msg->cseq==NULL && ((parse_headers(msg, HDR_CSEQ_F, 0)==-1) ||
+ (msg->cseq==NULL)))
+ {
+ LM_ERR("no CSEQ header\n");
+ return -1;
+ }
+
+ method = get_cseq(msg)->method_id;
+
+ if (!(method==METHOD_INVITE || method==METHOD_ACK || method==METHOD_CANCEL
+ || method==METHOD_BYE || method==METHOD_UPDATE))
+ return -1;
+
+ if (method==METHOD_CANCEL || method==METHOD_BYE)
+ return lrkproxy_unforce(msg, flags, OP_DELETE, 1);
+
+ if (msg->msg_flags & FL_SDP_BODY)
+ nosdp = 0;
+ else
+ nosdp = parse_sdp(msg);
+
+ if (msg->first_line.type == SIP_REQUEST) {
+ if(method==METHOD_ACK && nosdp==0)
+ return lrkproxy_force(msg, flags, OP_ANSWER, 1);
+ if(method==METHOD_UPDATE && nosdp==0)
+ return lrkproxy_force(msg, flags, OP_OFFER, 1);
+ if(method==METHOD_INVITE && nosdp==0) {
+ msg->msg_flags |= FL_SDP_BODY;
+ if(tmb.t_gett!=NULL) {
+ t = tmb.t_gett();
+ if(t!=NULL && t!=T_UNDEFINED && t->uas.request!=NULL) {
+ t->uas.request->msg_flags |= FL_SDP_BODY;
+ }
+ }
+ if(route_type==FAILURE_ROUTE)
+ return lrkproxy_unforce(msg, flags, OP_DELETE, 1);
+ return lrkproxy_force(msg, flags, OP_OFFER, 1);
+ }
+ } else if (msg->first_line.type == SIP_REPLY) {
+ if (msg->first_line.u.reply.statuscode>=300)
+ return lrkproxy_unforce(msg, flags, OP_DELETE, 2);
+ if (nosdp==0) {
+ if (method==METHOD_UPDATE)
+ return lrkproxy_force(msg, flags, OP_ANSWER, 2);
+ if (tmb.t_gett==NULL || tmb.t_gett()==NULL
+ || tmb.t_gett()==T_UNDEFINED)
+ return lrkproxy_force(msg, flags, OP_ANSWER, 2);
+ if (tmb.t_gett()->uas.request->msg_flags & FL_SDP_BODY)
+ return lrkproxy_force(msg, flags, OP_ANSWER, 2);
+ return lrkproxy_force(msg, flags, OP_OFFER, 2);
+ }
+ }
+
+ return -1;
+}
+
+static int
+lrkproxy_manage0(struct sip_msg *msg, char *flags, char *ip)
+{
+ return lrkproxy_manage(msg, 0, 0);
+}
+
+static int
+lrkproxy_manage1(struct sip_msg *msg, char *flags, char *ip)
+{
+ str flag_str;
+ if(fixup_get_svalue(msg, (gparam_p)flags, &flag_str)<0)
+ {
+ LM_ERR("invalid flags parameter\n");
+ return -1;
+ }
+ return lrkproxy_manage(msg, flag_str.s, 0);
+}
+
+static int
+lrkproxy_manage2(struct sip_msg *msg, char *flags, char *ip)
+{
+ str flag_str;
+ str ip_str;
+ if(fixup_get_svalue(msg, (gparam_p)flags, &flag_str)<0)
+ {
+ LM_ERR("invalid flags parameter\n");
+ return -1;
+ }
+ if(fixup_get_svalue(msg, (gparam_p)ip, &ip_str)<0)
+ {
+ LM_ERR("invalid IP parameter\n");
+ return -1;
+ }
+ return lrkproxy_manage(msg, flag_str.s, ip_str.s);
+}
+
+
+static int lrkproxy_force(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more) {
+
+// lrk_sdp_info_t lrk_sdp_info;
+ struct lrkproxy_hash_entry *entry = NULL;
+ str viabranch = STR_NULL;
+ str call_id;
+ int via_id;
+
+ if (get_callid(msg, &call_id) == -1) {
+ LM_ERR("can't get Call-Id field\n");
+ return -1;
+ }
+
+ /*We have to choice VIA id,
+ * for SIP_REQUEST we use VIA1 and for SIP_REPLY we use VIA2 */
+ via_id = more;
+
+ if (get_via_branch(msg, via_id, &viabranch) == -1) {
+ LM_ERR("can't get Call-Id field\n");
+ return -1;
+ }
+
+ if (op == OP_OFFER) {
+ LM_INFO ("Here is SIP_REQUEST & METHOD_INVITE\n");
+ int more_test = 1;
+
+ //select new node based on lrkp_algorithm param.
+ struct lrkp_node *node = select_lrkp_node(more_test);
+ if (!node) {
+ LM_ERR("can't ping any lrk proxy right now.\n");
+ return -1;
+ }
+
+ LM_DBG("selected lrk proxy node: %s\n", node->ln_url.s);
+
+ //check if entry not exist.
+ if (!lrkproxy_hash_table_lookup(call_id, viabranch)) {
+
+// lrk_get_sdp_info(msg, &lrk_sdp_info);
+
+ //build new entry for hash table.
+// struct lrkproxy_hash_entry *entry = shm_malloc(sizeof(struct lrkproxy_hash_entry));
+ entry = shm_malloc(sizeof(struct lrkproxy_hash_entry));
+ if (!entry) {
+ LM_ERR("lrkproxy hash table fail to create entry for calllen=%d callid=%.*s viabranch=%.*s\n",
+ call_id.len, call_id.len, call_id.s,
+ viabranch.len, viabranch.s);
+ return 0;
+ }
+ memset(entry, 0, sizeof(struct lrkproxy_hash_entry));
+
+ // fill the entry
+ if (call_id.s && call_id.len > 0) {
+ if (shm_str_dup(&entry->callid, &call_id) < 0) {
+ LM_ERR("lrkproxy hash table fail to instert call_id, calllen=%d callid=%.*s\n",
+ call_id.len, call_id.len, call_id.s);
+ lrkproxy_hash_table_free_entry(entry);
+ return 0;
+ }
+ }
+
+ if (viabranch.s && viabranch.len > 0) {
+ if (shm_str_dup(&entry->viabranch, &viabranch) < 0) {
+ LM_ERR("lrkproxy hash table fail to insert viabranch, calllen=%d viabranch=%.*s\n",
+ call_id.len, viabranch.len, viabranch.s);
+ lrkproxy_hash_table_free_entry(entry);
+ return 0;
+ }
+ }
+
+ //fill src_ipv4 and src_port for entry.
+ str src_ipv4;
+ if (get_sdp_ipaddr_media(msg, &src_ipv4) == -1) {
+ LM_ERR("can't get media src_ipv4 from sdp field\n");
+ return -1;
+ }
+
+ if(src_ipv4.s && src_ipv4.len > 0) {
+ LM_DBG("src_ipv4 from sdp:%.*s\n", src_ipv4.len, src_ipv4.s);
+ if (shm_str_dup(&entry->src_ipv4, &src_ipv4) < 0) {
+ LM_ERR("lrkproxy hash table fail to insert src_ipv4, calllen=%d src_ipv4=%.*s\n",
+ call_id.len, src_ipv4.len, src_ipv4.s);
+ lrkproxy_hash_table_free_entry(entry);
+ return 0;
+ }
+ }
+
+ str src_port;
+ if (get_sdp_port_media(msg, &src_port) == -1) {
+ LM_ERR("can't get media src_port from sdp field\n");
+ return -1;
+ }
+
+
+ if(src_port.s && src_port.len > 0) {
+ LM_DBG("src_port from sdp:%.*s\n", src_port.len, src_port.s);
+ if (shm_str_dup(&entry->src_port, &src_port) < 0) {
+ LM_ERR("lrkproxy hash table fail to insert src_port, calllen=%d src_port=%.*s\n",
+ call_id.len, src_port.len, src_port.s);
+ lrkproxy_hash_table_free_entry(entry);
+ return 0;
+ }
+ }
+
+// entry->
+ entry->node = node;
+ entry->next = NULL;
+ entry->tout = get_ticks() + hash_table_tout;
+
+ // insert the key<->entry from the hashtable
+ if (!lrkproxy_hash_table_insert(call_id, viabranch, entry)) {
+ LM_ERR(
+ "lrkproxy hash table fail to insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+ node->ln_url.len, node->ln_url.s, call_id.len,
+ call_id.len, call_id.s, viabranch.len, viabranch.s);
+ lrkproxy_hash_table_free_entry(entry);
+ return 0;
+ } else {
+ LM_INFO("lrkproxy hash table insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+ node->ln_url.len, node->ln_url.s, call_id.len,
+ call_id.len, call_id.s, viabranch.len, viabranch.s);
+
+ LM_DBG("lrkproxy hash table insert node=%.*s for calllen=%d callid=%.*s viabranch=%.*s\n",
+ node->ln_url.len, node->ln_url.s, call_id.len,
+ call_id.len, call_id.s, viabranch.len, viabranch.s);
+ }
+ }
+
+ if (flags)
+ change_media_sdp(msg, entry, flags, op);
+ else
+ change_media_sdp(msg, entry, NULL, op);
+
+ if (node->lrkp_n_c->current_port >= node->lrkp_n_c->end_port)
+ node->lrkp_n_c->current_port = node->lrkp_n_c->start_port;
+ else
+ node->lrkp_n_c->current_port += 4;
+
+ } else if (op == OP_ANSWER) {
+ LM_INFO ("Here is SIP_REPLY of METHOD_INVITE\n");
+
+
+ entry = lrkproxy_hash_table_lookup(call_id, viabranch);
+ if (!entry){
+ LM_ERR("No found entry in hash table\n");
+ //todo...
+ return 0;
+ }
+
+ //fill other data for entry
+ str dnat_ipv4;
+ if (get_sdp_ipaddr_media(msg, &dnat_ipv4) == -1) {
+ LM_ERR("can't get media dnat_ipv4 from sdp field\n");
+ return -1;
+ }
+
+ if(dnat_ipv4.s && dnat_ipv4.len > 0) {
+ LM_DBG("dnat_ipv4 from sdp:%.*s\n", dnat_ipv4.len, dnat_ipv4.s);
+ if (shm_str_dup(&entry->dnat_ipv4, &dnat_ipv4) < 0) {
+ LM_ERR("lrkproxy hash table fail to insert dnat_ipv4, calllen=%d dnat_ipv4=%.*s\n",
+ call_id.len, dnat_ipv4.len, dnat_ipv4.s);
+ lrkproxy_hash_table_free_entry(entry);
+ return 0;
+ }
+ }
+
+ str dnat_port;
+ if (get_sdp_port_media(msg, &dnat_port) == -1) {
+ LM_ERR("can't get media port from sdp field\n");
+ return -1;
+ }
+
+
+ if(dnat_port.s && dnat_port.len > 0) {
+ LM_DBG("port from sdp:%.*s\n", dnat_port.len, dnat_port.s);
+ if (shm_str_dup(&entry->dnat_port, &dnat_port) < 0) {
+ LM_ERR("lrkproxy hash table fail to insert dnat_port, calllen=%d dnat_port=%.*s\n",
+ call_id.len, dnat_port.len, dnat_port.s);
+ lrkproxy_hash_table_free_entry(entry);
+ return 0;
+ }
+ }
+
+
+ if (flags)
+ change_media_sdp(msg, entry, flags, op);
+ else
+ change_media_sdp(msg, entry, NULL, op);
+
+
+ LM_INFO("selected node: %s\n",entry->node->ln_url.s);
+ LM_INFO("call_is: %.*s\n",entry->callid.len, entry->callid.s);
+ LM_INFO("viabranch: %.*s\n",entry->viabranch.len, entry->viabranch.s);
+ LM_INFO("src_ipv4: %.*s\n",entry->src_ipv4.len, entry->src_ipv4.s);
+ LM_INFO("src_port: %.*s\n",entry->src_port.len, entry->src_port.s);
+ LM_INFO("dst_ipv4: %.*s\n",entry->dst_ipv4.len, entry->dst_ipv4.s);
+ LM_INFO("dst_port: %.*s\n",entry->dst_port.len, entry->dst_port.s);
+
+ LM_INFO("dnat_ipv4: %.*s\n",entry->dnat_ipv4.len, entry->dnat_ipv4.s);
+ LM_INFO("dnat_port: %.*s\n",entry->dnat_port.len, entry->dnat_port.s);
+ LM_INFO("snat_ipv4: %.*s\n",entry->snat_ipv4.len, entry->snat_ipv4.s);
+ LM_INFO("snat_port: %.*s\n",entry->snat_port.len, entry->snat_port.s);
+
+
+ lrkp_set_conntrack_rule(entry);
+
+ }
+ return 1;
+}
+
+static int lrkproxy_unforce(struct sip_msg *msg, const char *flags, enum lrk_operation op, int more){
+// LM_INFO ("Here is lrkproxy_unforce\n");
+// struct lrkproxy_hash_entry *entry = NULL;
+ str viabranch = STR_NULL;
+ str call_id;
+ int via_id;
+
+ if (get_callid(msg, &call_id) == -1) {
+ LM_ERR("can't get Call-Id field\n");
+ return -1;
+ }
+
+ /*We have to choice VIA id,
+ * for SIP_REQUEST we use VIA1 and for SIP_REPLY we use VIA2 */
+ via_id = more;
+
+ if (get_via_branch(msg, via_id, &viabranch) == -1) {
+ LM_ERR("can't get Call-Id field\n");
+ return -1;
+ }
+
+ if (op == OP_DELETE) {
+ /* Delete the key<->value from the hashtable */
+ if (!lrkproxy_hash_table_remove(call_id, viabranch, op)) {
+ LM_ERR("lrkproxy hash table failed to remove entry for callen=%d callid=%.*s viabranch=%.*s\n",
+ call_id.len, call_id.len, call_id.s,
+ viabranch.len, viabranch.s);
+ } else {
+ LM_DBG("lrkproxy hash table remove entry for callen=%d callid=%.*s viabranch=%.*s\n",
+ call_id.len, call_id.len, call_id.s,
+ viabranch.len, viabranch.s);
+ }
+ }
+ LM_INFO("lrkproxy hash table remove entry for callen=%d callid=%.*s viabranch=%.*s successfully\n",
+ call_id.len, call_id.len, call_id.s,
+ viabranch.len, viabranch.s);
+ return 1;
+}
diff --git a/src/modules/lrkproxy/lrkproxy.h b/src/modules/lrkproxy/lrkproxy.h
new file mode 100644
index 00000000000..2cf855e419c
--- /dev/null
+++ b/src/modules/lrkproxy/lrkproxy.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * 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 _LRKPROXY_H
+#define _LRKPROXY_H
+
+#include
+#include "../../core/str.h"
+
+/* Handy macros */
+#define STR2IOVEC(sx, ix) do {(ix).iov_base = (sx).s; (ix).iov_len = (sx).len;} while(0)
+#define SZ2IOVEC(sx, ix) do {(ix).iov_base = (sx); (ix).iov_len = strlen(sx);} while(0)
+
+#define CR '\r'
+#define LF '\n'
+#define EOB '\0'
+
+enum lrk_operation {
+ OP_OFFER = 1,
+ OP_ANSWER,
+ OP_DELETE,
+ OP_PING,
+ OP_GETINFO,
+ OP_SETCONNT,
+
+ OP_ANY,
+};
+
+
+enum lrk_alg{
+ LRK_LINER=0,
+ LRK_RR
+};
+
+struct lrkp_node_conf
+{
+ int start_port;
+ int end_port;
+ int current_port;
+ char internal_ip[20];
+ char external_ip[20];
+};
+
+struct lrkp_node {
+ unsigned int idx; /* overall index */
+ str ln_url; /* unparsed, deletable */
+ int ln_umode;
+ char *ln_address; /* substring of rn_url */
+ int ln_enable; /* found unaccessible? */
+ unsigned ln_weight; /* for load balancing */
+// unsigned int ln_recheck_ticks;
+// int ln_rep_supported;
+// int ln_ptl_supported;
+ struct lrkp_node_conf *lrkp_n_c;
+ struct lrkp_node *ln_next;
+};
+
+
+
+struct lrkp_set{
+ unsigned int id_set;
+ unsigned weight_sum;
+ unsigned int lrkp_node_count;
+ int set_disabled;
+ unsigned int set_recheck_ticks;
+ struct lrkp_node *ln_first;
+ struct lrkp_node *ln_last;
+ struct lrkp_set *lset_next;
+};
+
+
+
+struct lrkp_set_head{
+ struct lrkp_set *lset_first;
+ struct lrkp_set *lset_last;
+};
+/* Functions from nathelper */
+//struct lrkp_node *lrkp_node(str, int);
+struct lrkp_node *select_lrkp_node(int);
+char *send_lrkp_command(struct lrkp_node *, struct iovec *, int, int);
+
+struct lrkp_set *get_lrkp_set(str *set_name);
+int insert_lrkp_node(struct lrkp_set *const lrkp_list, const str *const url,
+ const int weight, const int enable);
+
+
+#endif //_LRKPROXY_H
diff --git a/src/modules/lrkproxy/lrkproxy_funcs.c b/src/modules/lrkproxy/lrkproxy_funcs.c
new file mode 100644
index 00000000000..08a41f27cfe
--- /dev/null
+++ b/src/modules/lrkproxy/lrkproxy_funcs.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * 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
+#include
+#include
+#include
+#include "lrkproxy_funcs.h"
+#include "../../core/dprint.h"
+#include "../../core/config.h"
+#include "../../core/ut.h"
+#include "../../core/forward.h"
+#include "../../core/resolve.h"
+#include "../../core/globals.h"
+#include "../../core/udp_server.h"
+#include "../../core/pt.h"
+#include "../../core/parser/msg_parser.h"
+#include "../../core/trim.h"
+#include "../../core/parser/parse_from.h"
+#include "../../core/parser/contact/parse_contact.h"
+#include "../../core/parser/parse_uri.h"
+#include "../../core/parser/parse_content.h"
+#include "../../core/parser/parser_f.h"
+#include "../../core/parser/sdp/sdp_helpr_funcs.h"
+
+#define READ(val) \
+ (*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(val + 3) << 24))
+#define advance(_ptr,_n,_str,_error) \
+ do{\
+ if ((_ptr)+(_n)>(_str).s+(_str).len)\
+ goto _error;\
+ (_ptr) = (_ptr) + (_n);\
+ }while(0);
+#define one_of_16( _x , _t ) \
+ (_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
+ ||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\
+ ||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14])
+#define one_of_8( _x , _t ) \
+ (_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
+ ||_x==_t[5]||_x==_t[6])
+
+
+
+/**
+ * return:
+ * -1: error
+ * 1: text or sdp
+ * 2: multipart
+ * 3: trickle ice sdp fragment
+ */
+int check_content_type(struct sip_msg *msg)
+{
+ static unsigned int appl[16] = {
+ 0x6c707061/*appl*/,0x6c707041/*Appl*/,0x6c705061/*aPpl*/,
+ 0x6c705041/*APpl*/,0x6c507061/*apPl*/,0x6c507041/*ApPl*/,
+ 0x6c505061/*aPPl*/,0x6c505041/*APPl*/,0x4c707061/*appL*/,
+ 0x4c707041/*AppL*/,0x4c705061/*aPpL*/,0x4c705041/*APpL*/,
+ 0x4c507061/*apPL*/,0x4c507041/*ApPL*/,0x4c505061/*aPPL*/,
+ 0x4c505041/*APPL*/};
+ static unsigned int icat[16] = {
+ 0x74616369/*icat*/,0x74616349/*Icat*/,0x74614369/*iCat*/,
+ 0x74614349/*ICat*/,0x74416369/*icAt*/,0x74416349/*IcAt*/,
+ 0x74414369/*iCAt*/,0x74414349/*ICAt*/,0x54616369/*icaT*/,
+ 0x54616349/*IcaT*/,0x54614369/*iCaT*/,0x54614349/*ICaT*/,
+ 0x54416369/*icAT*/,0x54416349/*IcAT*/,0x54414369/*iCAT*/,
+ 0x54414349/*ICAT*/};
+ static unsigned int ion_[8] = {
+ 0x006e6f69/*ion_*/,0x006e6f49/*Ion_*/,0x006e4f69/*iOn_*/,
+ 0x006e4f49/*IOn_*/,0x004e6f69/*ioN_*/,0x004e6f49/*IoN_*/,
+ 0x004e4f69/*iON_*/,0x004e4f49/*ION_*/};
+ static unsigned int sdp_[8] = {
+ 0x00706473/*sdp_*/,0x00706453/*Sdp_*/,0x00704473/*sDp_*/,
+ 0x00704453/*SDp_*/,0x00506473/*sdP_*/,0x00506453/*SdP_*/,
+ 0x00504473/*sDP_*/,0x00504453/*SDP_*/};
+ str str_type;
+ unsigned int x;
+ char *p;
+
+ if (!msg->content_type)
+ {
+ LM_WARN("the header Content-TYPE is absent!"
+ "let's assume the content is text/plain ;-)\n");
+ return 1;
+ }
+
+ trim_len(str_type.len,str_type.s,msg->content_type->body);
+ if (str_type.len>=15 && (*str_type.s=='m' || *str_type.s=='M')
+ && strncasecmp(str_type.s, "multipart/mixed", 15) == 0) {
+ return 2;
+ }
+ p = str_type.s;
+ advance(p,4,str_type,error_1);
+ x = READ(p-4);
+ if (!one_of_16(x,appl))
+ goto other;
+ advance(p,4,str_type,error_1);
+ x = READ(p-4);
+ if (!one_of_16(x,icat))
+ goto other;
+ advance(p,3,str_type,error_1);
+ x = READ(p-3) & 0x00ffffff;
+ if (!one_of_8(x,ion_))
+ goto other;
+
+ /* skip spaces and tabs if any */
+ while (*p==' ' || *p=='\t')
+ advance(p,1,str_type,error_1);
+ if (*p!='/')
+ {
+ LM_ERR("no / found after primary type\n");
+ goto error;
+ }
+ advance(p,1,str_type,error_1);
+ while ((*p==' ' || *p=='\t') && p+1 found valid\n", (int)(p-str_type.s), str_type.s);
+ return 1;
+ } else {
+ LM_ERR("bad end for type!\n");
+ return -1;
+ }
+
+ error_1:
+ LM_ERR("body ended :-(!\n");
+ error:
+ return -1;
+ other:
+ LM_ERR("invalid type for a message\n");
+ return -1;
+}
+
+
+/*
+ * Get message body and check Content-Type header field
+ */
+int extract_body(struct sip_msg *msg, str *body )
+{
+ char c;
+ int ret;
+ str mpdel;
+ char *rest, *p1, *p2;
+ struct hdr_field hf;
+ unsigned int mime;
+
+ body->s = get_body(msg);
+ if (body->s==0) {
+ LM_ERR("failed to get the message body\n");
+ goto error;
+ }
+
+ /*
+ * Better use the content-len value - no need of any explicit
+ * parcing as get_body() parsed all headers and Conten-Length
+ * body header is automaticaly parsed when found.
+ */
+ if (msg->content_length==0) {
+ LM_ERR("failed to get the content length in message\n");
+ goto error;
+ }
+
+ body->len = get_content_length(msg);
+ if (body->len==0) {
+ LM_ERR("message body has length zero\n");
+ goto error;
+ }
+
+ if (body->len + body->s > msg->buf + msg->len) {
+ LM_ERR("content-length exceeds packet-length by %d\n",
+ (int)((body->len + body->s) - (msg->buf + msg->len)));
+ goto error;
+ }
+
+ /* no need for parse_headers(msg, EOH), get_body will
+ * parse everything */
+ /*is the content type correct?*/
+ if((ret = check_content_type(msg))==-1)
+ {
+ LM_ERR("content type mismatching\n");
+ goto error;
+ }
+
+ if(ret!=2)
+ goto done;
+
+ /* multipart body */
+ if(get_mixed_part_delimiter(&msg->content_type->body,&mpdel) < 0) {
+ goto error;
+ }
+ p1 = find_sdp_line_delimiter(body->s, body->s+body->len, mpdel);
+ if (p1 == NULL) {
+ LM_ERR("empty multipart content\n");
+ return -1;
+ }
+ p2=p1;
+ c = 0;
+ for(;;)
+ {
+ p1 = p2;
+ if (p1 == NULL || p1 >= body->s+body->len)
+ break; /* No parts left */
+ p2 = find_next_sdp_line_delimiter(p1, body->s+body->len,
+ mpdel, body->s+body->len);
+ /* p2 is text limit for application parsing */
+ rest = eat_line(p1 + mpdel.len + 2, p2 - p1 - mpdel.len - 2);
+ if ( rest > p2 ) {
+ LM_ERR("Unparsable <%.*s>\n", (int)(p1-p1), p1);
+ return -1;
+ }
+ while( rest>16) == TYPE_APPLICATION)
+ && ((mime&0x00ff) == SUBTYPE_SDP)) {
+ c = 1;
+ }
+ }
+ } /* end of while */
+ if(c==1)
+ {
+ if (rest < p2 && *rest == '\r') rest++;
+ if (rest < p2 && *rest == '\n') rest++;
+ if (rest < p2 && p2[-1] == '\n') p2--;
+ if (rest < p2 && p2[-1] == '\r') p2--;
+ body->s = rest;
+ body->len = p2-rest;
+ goto done;
+ }
+ }
+
+ error:
+ return -1;
+
+ done:
+ /*LM_DBG("DEBUG:extract_body:=|%.*s|\n",body->len,body->s);*/
+ return ret; /* mirrors return type of check_content_type */
+}
+
+/*
+ * Some helper functions taken verbatim from tm module.
+ */
+
+/*
+ * Extract Call-ID value
+ * assumes the callid header is already parsed
+ * (so make sure it is, before calling this function or
+ * it might fail even if the message _has_ a callid)
+ */
+int
+get_callid(struct sip_msg* _m, str* _cid)
+{
+
+ if ((parse_headers(_m, HDR_CALLID_F, 0) == -1)) {
+ LM_ERR("failed to parse call-id header\n");
+ return -1;
+ }
+
+ if (_m->callid == NULL) {
+ LM_ERR("call-id not found\n");
+ return -1;
+ }
+
+ _cid->s = _m->callid->body.s;
+ _cid->len = _m->callid->body.len;
+ trim(_cid);
+ return 0;
+}
+
+/*
+ * Extract tag from To header field of a response
+ */
+int
+get_to_tag(struct sip_msg* _m, str* _tag)
+{
+
+ if (parse_to_header(_m) < 0) {
+ LM_ERR("To header field missing\n");
+ return -1;
+ }
+
+ if (get_to(_m)->tag_value.len) {
+ _tag->s = get_to(_m)->tag_value.s;
+ _tag->len = get_to(_m)->tag_value.len;
+ } else {
+ _tag->s = NULL; /* fixes gcc 4.0 warnings */
+ _tag->len = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Extract tag from From header field of a request
+ */
+int
+get_from_tag(struct sip_msg* _m, str* _tag)
+{
+
+ if (parse_from_header(_m)<0) {
+ LM_ERR("failed to parse From header\n");
+ return -1;
+ }
+
+ if (get_from(_m)->tag_value.len) {
+ _tag->s = get_from(_m)->tag_value.s;
+ _tag->len = get_from(_m)->tag_value.len;
+ } else {
+ _tag->s = NULL; /* fixes gcc 4.0 warnings */
+ _tag->len = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Extract URI from the Contact header field
+ */
+int
+get_contact_uri(struct sip_msg* _m, struct sip_uri *uri, contact_t** _c)
+{
+
+ if ((parse_headers(_m, HDR_CONTACT_F, 0) == -1) || !_m->contact)
+ return -1;
+ if (!_m->contact->parsed && parse_contact(_m->contact) < 0) {
+ LM_ERR("failed to parse Contact body\n");
+ return -1;
+ }
+ *_c = ((contact_body_t*)_m->contact->parsed)->contacts;
+ if (*_c == NULL)
+ /* no contacts found */
+ return -1;
+
+ if (parse_uri((*_c)->uri.s, (*_c)->uri.len, uri) < 0 || uri->host.len <= 0) {
+ LM_ERR("failed to parse Contact URI [%.*s]\n",
+ (*_c)->uri.len, ((*_c)->uri.s)?(*_c)->uri.s:"");
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Extract branch from Via header
+ */
+int
+get_via_branch(struct sip_msg* msg, int vianum, str* _branch)
+{
+ struct via_body *via;
+ struct via_param *p;
+
+ if (parse_via_header(msg, vianum, &via) < 0)
+ return -1;
+
+ for (p = via->param_lst; p; p = p->next)
+ {
+ if (p->name.len == strlen("branch")
+ && strncasecmp(p->name.s, "branch", strlen("branch")) == 0) {
+ _branch->s = p->value.s;
+ _branch->len = p->value.len;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int get_sdp_ipaddr_media(struct sip_msg *msg, str *ip_addr) {
+ sdp_session_cell_t *sdp_session;
+ sdp_stream_cell_t *sdp_stream;
+ sdp_info_t *sdp = (sdp_info_t *) msg->body;
+ if (!sdp) {
+ LM_INFO("sdp null\n");
+ return -1;
+ }
+
+
+ int sdp_session_num = 0;
+ sdp_session = get_sdp_session(msg, sdp_session_num);
+
+ if (!sdp_session) {
+ LM_INFO("can not get the sdp session\n");
+ return 0;
+ }
+
+ if (sdp_session->ip_addr.s && sdp_session->ip_addr.len > 0) {
+ LM_INFO("sdp_session->ip_addr:%.*s\n", sdp_session->ip_addr.len, sdp_session->ip_addr.s);
+ ip_addr->s = sdp_session->ip_addr.s;
+ ip_addr->len = sdp_session->ip_addr.len;
+ trim(ip_addr);
+ }
+ else {
+ int sdp_stream_num = 0;
+ sdp_stream = get_sdp_stream(msg, sdp_session_num, sdp_stream_num);
+ if (!sdp_stream) {
+ LM_INFO("can not get the sdp stream\n");
+ return 0;
+ }
+ if (sdp_stream->ip_addr.s && sdp_stream->ip_addr.len > 0) {
+ LM_INFO("sdp_stream->ip_addr:%.*s\n", sdp_stream->ip_addr.len, sdp_stream->ip_addr.s);
+ ip_addr->s = sdp_stream->ip_addr.s;
+ ip_addr->len = sdp_stream->ip_addr.len;
+ trim(ip_addr);
+ }
+ }
+
+
+ return 0;
+}
+
+
+int get_sdp_port_media(struct sip_msg *msg, str *port){
+// sdp_session_cell_t *sdp_session;
+ sdp_stream_cell_t *sdp_stream;
+ int sdp_session_num = 0;
+
+ sdp_info_t *sdp = (sdp_info_t *)msg->body;
+ if(!sdp) {
+ LM_INFO("sdp null\n");
+ return -1;
+ }
+
+// sdp_session = get_sdp_session(msg, sdp_session_num);
+// if(!sdp_session) {
+// LM_INFO("can not get the sdp session\n");
+// return 0;
+// } else {
+// LM_INFO("NEW_IP_ADDRESS:>%.*s>\n", sdp_session->ip_addr.len, sdp_session->ip_addr.s);
+// lrk_sdp_info->ip_addr.s = sdp_session->ip_addr;
+ int sdp_stream_num = 0;
+ sdp_stream = get_sdp_stream(msg, sdp_session_num, sdp_stream_num);
+ if (!sdp_stream) {
+ LM_INFO("can not get the sdp stream\n");
+ return -1;
+ } else {
+// LM_INFO ("PORT:<%.*s>\n", sdp_stream->port.len, sdp_stream->port.s);
+// str2int(&sdp_stream->port, lrk_sdp_info->port)
+ port->s = sdp_stream->port.s;
+ port->len = sdp_stream->port.len;
+ trim(port);
+ }
+// }
+ return 0;
+
+}
+
diff --git a/src/modules/lrkproxy/lrkproxy_funcs.h b/src/modules/lrkproxy/lrkproxy_funcs.h
new file mode 100644
index 00000000000..d644235226f
--- /dev/null
+++ b/src/modules/lrkproxy/lrkproxy_funcs.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * 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 _LRKPROXY_FUNCS_H
+#define _LRKPROXY_FUNCS_H
+
+
+#include "../../core/str.h"
+#include "../../core/parser/msg_parser.h"
+#include "../../core/parser/contact/contact.h"
+
+int extract_body(struct sip_msg * , str *);
+int check_content_type(struct sip_msg * );
+int get_callid(struct sip_msg *, str *);
+int get_to_tag(struct sip_msg *, str *);
+int get_from_tag(struct sip_msg *, str *);
+int get_contact_uri(struct sip_msg *, struct sip_uri *, contact_t **);
+int get_via_branch(struct sip_msg *, int, str *);
+int get_sdp_ipaddr_media(struct sip_msg *msg, str *ip_addr);
+int get_sdp_port_media(struct sip_msg *msg, str *port);
+
+
+#endif //_LRKPROXY_FUNCS_H
diff --git a/src/modules/lrkproxy/lrkproxy_hash.c b/src/modules/lrkproxy/lrkproxy_hash.c
new file mode 100644
index 00000000000..460db1d56e9
--- /dev/null
+++ b/src/modules/lrkproxy/lrkproxy_hash.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
+ * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * 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 "lrkproxy.h"
+#include "lrkproxy_hash.h"
+
+#include "../../core/str.h"
+#include "../../core/dprint.h"
+#include "../../core/mem/shm_mem.h"
+#include "../../core/timer.h"
+
+static void lrkproxy_hash_table_free_row_lock(gen_lock_t *row_lock);
+
+static struct lrkproxy_hash_table *lrkproxy_hash_table;
+
+/* get from sipwise rtpengine */
+static int str_cmp_str(const str a, const str b) {
+ if (a.len < b.len)
+ return -1;
+ if (a.len > b.len)
+ return 1;
+ if (a.len == 0 && b.len == 0)
+ return 0;
+ return memcmp(a.s, b.s, a.len);
+}
+
+/* get from sipwise rtpengine */
+static int str_equal(str a, str b) {
+ return (str_cmp_str(a, b) == 0);
+}
+
+/* get from sipwise rtpengine */
+static unsigned int str_hash(str s) {
+ unsigned int ret = 5381;
+ str it = s;
+
+ while (it.len > 0) {
+ ret = (ret << 5) + ret + *it.s;
+ it.s++;
+ it.len--;
+ }
+
+ return ret % lrkproxy_hash_table->size;
+}
+
+/* lrkproxy hash API */
+int lrkproxy_hash_table_init(int size) {
+ int i;
+ int hash_table_size;
+
+
+ hash_table_size = size;
+
+
+// LM_DBG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table size = %d\n", hash_table_size);
+ LM_INFO(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table size = %d\n", hash_table_size);
+
+ // init hashtable
+ lrkproxy_hash_table = shm_malloc(sizeof(struct lrkproxy_hash_table));
+ if (!lrkproxy_hash_table) {
+ LM_ERR("no shm left to create lrkproxy_hash_table\n");
+ return 0;
+ }
+ memset(lrkproxy_hash_table, 0, sizeof(struct lrkproxy_hash_table));
+ lrkproxy_hash_table->size = hash_table_size;
+
+ // init hashtable row_locks
+ lrkproxy_hash_table->row_locks = shm_malloc(hash_table_size * sizeof(gen_lock_t*));
+ if (!lrkproxy_hash_table->row_locks) {
+ LM_ERR("no shm left to create lrkproxy_hash_table->row_locks\n");
+ lrkproxy_hash_table_destroy();
+ return 0;
+ }
+ memset(lrkproxy_hash_table->row_locks, 0, hash_table_size * sizeof(gen_lock_t*));
+
+ // init hashtable row_entry_list
+ lrkproxy_hash_table->row_entry_list = shm_malloc(lrkproxy_hash_table->size * sizeof(struct lrkproxy_hash_entry*));
+ if (!lrkproxy_hash_table->row_entry_list) {
+ LM_ERR("no shm left to create lrkproxy_hash_table->row_entry_list\n");
+ lrkproxy_hash_table_destroy();
+ return 0;
+ }
+ memset(lrkproxy_hash_table->row_entry_list, 0, lrkproxy_hash_table->size * sizeof(struct lrkproxy_hash_entry*));
+
+ // init hashtable row_totals
+ lrkproxy_hash_table->row_totals = shm_malloc(hash_table_size * sizeof(unsigned int));
+ if (!lrkproxy_hash_table->row_totals) {
+ LM_ERR("no shm left to create lrkproxy_hash_table->row_totals\n");
+ lrkproxy_hash_table_destroy();
+ return 0;
+ }
+ memset(lrkproxy_hash_table->row_totals, 0, hash_table_size * sizeof(unsigned int));
+
+ // init hashtable row_locks[i], row_entry_list[i] and row_totals[i]
+ for (i = 0; i < hash_table_size; i++) {
+ // alloc hashtable row_locks[i]
+ lrkproxy_hash_table->row_locks[i] = lock_alloc();
+ if (!lrkproxy_hash_table->row_locks[i]) {
+ LM_ERR("no shm left to create lrkproxy_hash_table->row_locks[%d]\n", i);
+ lrkproxy_hash_table_destroy();
+ return 0;
+ }
+
+ // init hashtable row_locks[i]
+ if (!lock_init(lrkproxy_hash_table->row_locks[i])) {
+ LM_ERR("fail to init lrkproxy_hash_table->row_locks[%d]\n", i);
+ lrkproxy_hash_table_destroy();
+ return 0;
+ }
+
+ // init hashtable row_entry_list[i]
+ lrkproxy_hash_table->row_entry_list[i] = shm_malloc(sizeof(struct lrkproxy_hash_entry));
+ if (!lrkproxy_hash_table->row_entry_list[i]) {
+ LM_ERR("no shm left to create lrkproxy_hash_table->row_entry_list[%d]\n", i);
+ lrkproxy_hash_table_destroy();
+ return 0;
+ }
+ memset(lrkproxy_hash_table->row_entry_list[i], 0, sizeof(struct lrkproxy_hash_entry));
+
+ lrkproxy_hash_table->row_entry_list[i]->tout = -1;
+ lrkproxy_hash_table->row_entry_list[i]->next = NULL;
+
+ // init hashtable row_totals[i]
+ lrkproxy_hash_table->row_totals[i] = 0;
+ }
+
+ return 1;
+}
+
+int lrkproxy_hash_table_destroy() {
+ int i;
+
+ // check lrkproxy hashtable
+ if (!lrkproxy_hash_table) {
+ LM_ERR("NULL lrkproxy_hash_table\n");
+ return 1;
+ }
+
+ // check lrkproxy hashtable->row_locks
+ if (!lrkproxy_hash_table->row_locks) {
+ LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
+ shm_free(lrkproxy_hash_table);
+ lrkproxy_hash_table = NULL;
+ return 1;
+ }
+
+ // destroy hashtable content
+ for (i = 0; i < lrkproxy_hash_table->size; i++) {
+ // lock
+ if (!lrkproxy_hash_table->row_locks[i]) {
+ LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", i);
+ continue;
+ } else {
+ lock_get(lrkproxy_hash_table->row_locks[i]);
+ }
+
+ // check lrkproxy hashtable->row_entry_list
+ if (!lrkproxy_hash_table->row_entry_list) {
+ LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
+ } else {
+ // destroy hashtable row_entry_list[i]
+ lrkproxy_hash_table_free_row_entry_list(lrkproxy_hash_table->row_entry_list[i]);
+ lrkproxy_hash_table->row_entry_list[i] = NULL;
+ }
+
+ // unlock
+ lock_release(lrkproxy_hash_table->row_locks[i]);
+
+ // destroy hashtable row_locks[i]
+ lrkproxy_hash_table_free_row_lock(lrkproxy_hash_table->row_locks[i]);
+ lrkproxy_hash_table->row_locks[i] = NULL;
+ }
+
+ // destroy hashtable row_entry_list
+ if (!lrkproxy_hash_table->row_entry_list) {
+ LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
+ } else {
+ shm_free(lrkproxy_hash_table->row_entry_list);
+ lrkproxy_hash_table->row_entry_list = NULL;
+ }
+
+ // destroy hashtable row_totals
+ if (!lrkproxy_hash_table->row_totals) {
+ LM_ERR("NULL lrkproxy_hash_table->row_totals\n");
+ } else {
+ shm_free(lrkproxy_hash_table->row_totals);
+ lrkproxy_hash_table->row_totals = NULL;
+ }
+
+ // destroy hashtable row_locks
+ if (!lrkproxy_hash_table->row_locks) {
+ // should not be the case; just for code symmetry
+ LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
+ } else {
+ shm_free(lrkproxy_hash_table->row_locks);
+ lrkproxy_hash_table->row_locks = NULL;
+ }
+
+ // destroy hashtable
+ if (!lrkproxy_hash_table) {
+ // should not be the case; just for code symmetry
+ LM_ERR("NULL lrkproxy_hash_table\n");
+ } else {
+ shm_free(lrkproxy_hash_table);
+ lrkproxy_hash_table = NULL;
+ }
+
+ return 1;
+}
+
+
+void lrkproxy_hash_table_free_entry(struct lrkproxy_hash_entry *entry) {
+ if (!entry) {
+ LM_ERR("try to free a NULL entry\n");
+ return ;
+ }
+
+ // free callid
+ if (entry->callid.s) {
+ shm_free(entry->callid.s);
+ }
+
+ // free viabranch
+ if (entry->viabranch.s) {
+ shm_free(entry->viabranch.s);
+ }
+
+ // free entry
+ shm_free(entry);
+
+ return ;
+}
+
+void lrkproxy_hash_table_free_row_entry_list(struct lrkproxy_hash_entry *row_entry_list) {
+ struct lrkproxy_hash_entry *entry, *last_entry;
+
+ if (!row_entry_list) {
+ LM_ERR("try to free a NULL row_entry_list\n");
+ return ;
+ }
+
+ entry = row_entry_list;
+ while (entry) {
+ last_entry = entry;
+ entry = entry->next;
+ lrkproxy_hash_table_free_entry(last_entry);
+ last_entry = NULL;
+ }
+
+ return ;
+}
+
+int lrkproxy_hash_table_insert(str callid, str viabranch, struct lrkproxy_hash_entry *value) {
+ struct lrkproxy_hash_entry *entry, *last_entry;
+ struct lrkproxy_hash_entry *new_entry = (struct lrkproxy_hash_entry *) value;
+ unsigned int hash_index;
+
+ // sanity checks
+ if (!lrkproxy_hash_table_sanity_checks()) {
+ LM_ERR("sanity checks failed\n");
+ return 0;
+ }
+
+ // get entry list
+ hash_index = str_hash(callid);
+ entry = lrkproxy_hash_table->row_entry_list[hash_index];
+ last_entry = entry;
+
+ // lock
+ if (lrkproxy_hash_table->row_locks[hash_index]) {
+ lock_get(lrkproxy_hash_table->row_locks[hash_index]);
+ } else {
+ LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
+ return 0;
+ }
+
+ while (entry) {
+ // if found, don't add new entry
+ if (str_equal(entry->callid, new_entry->callid) &&
+ str_equal(entry->viabranch, new_entry->viabranch)) {
+ // unlock
+ lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+ LM_NOTICE("callid=%.*s, viabranch=%.*s already in hashtable, ignore new value\n",
+ entry->callid.len, entry->callid.s,
+ entry->viabranch.len, entry->viabranch.s);
+ return 0;
+ }
+
+ // if expired entry discovered, delete it
+ if (entry->tout < get_ticks()) {
+ // set pointers; exclude entry
+ last_entry->next = entry->next;
+
+ // free current entry; entry points to unknown
+ lrkproxy_hash_table_free_entry(entry);
+
+ // set pointers
+ entry = last_entry;
+
+ // update total
+ lrkproxy_hash_table->row_totals[hash_index]--;
+ }
+
+ // next entry in the list
+ last_entry = entry;
+ entry = entry->next;
+ }
+
+ last_entry->next = new_entry;
+
+ // update total
+ lrkproxy_hash_table->row_totals[hash_index]++;
+
+ // unlock
+ lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+
+ return 1;
+}
+
+int lrkproxy_hash_table_remove(str callid, str viabranch, enum lrk_operation op) {
+ struct lrkproxy_hash_entry *entry, *last_entry;
+ unsigned int hash_index;
+ int found = 0;
+
+ // sanity checks
+ if (!lrkproxy_hash_table_sanity_checks()) {
+ LM_ERR("sanity checks failed\n");
+ return 0;
+ }
+
+ // get first entry from entry list; jump over unused list head
+ hash_index = str_hash(callid);
+ entry = lrkproxy_hash_table->row_entry_list[hash_index];
+ last_entry = entry;
+
+ // lock
+ if (lrkproxy_hash_table->row_locks[hash_index]) {
+ lock_get(lrkproxy_hash_table->row_locks[hash_index]);
+ } else {
+ LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
+ return 0;
+ }
+
+ while (entry) {
+ // if callid found, delete entry
+ if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) ||
+ (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE)) {
+ // set pointers; exclude entry
+ last_entry->next = entry->next;
+
+ // free current entry; entry points to unknown
+ lrkproxy_hash_table_free_entry(entry);
+
+ // set pointers
+ entry = last_entry;
+
+ // update total
+ lrkproxy_hash_table->row_totals[hash_index]--;
+
+ found = 1;
+
+ if (!(viabranch.len == 0 && op == OP_DELETE)) {
+ // unlock
+ lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+ return found;
+ }
+
+ // try to also delete other viabranch entries for callid
+ last_entry = entry;
+ entry = entry->next;
+ continue;
+ }
+
+ // if expired entry discovered, delete it
+ if (entry->tout < get_ticks()) {
+ // set pointers; exclude entry
+ last_entry->next = entry->next;
+
+ // free current entry; entry points to unknown
+ lrkproxy_hash_table_free_entry(entry);
+
+ // set pointers
+ entry = last_entry;
+
+ // update total
+ lrkproxy_hash_table->row_totals[hash_index]--;
+ }
+
+ last_entry = entry;
+ entry = entry->next;
+ }
+
+ // unlock
+ lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+
+ return found;
+}
+//struct lrkp_node *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation op) {
+//struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation op) {
+struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch) {
+ struct lrkproxy_hash_entry *entry, *last_entry;
+ unsigned int hash_index;
+// struct lrkp_node *node;
+
+ // sanity checks
+ if (!lrkproxy_hash_table_sanity_checks()) {
+ LM_ERR("sanity checks failed\n");
+ return 0;
+ }
+
+ // get first entry from entry list; jump over unused list head
+ hash_index = str_hash(callid);
+ entry = lrkproxy_hash_table->row_entry_list[hash_index];
+ last_entry = entry;
+
+ // lock
+ if (lrkproxy_hash_table->row_locks[hash_index]) {
+ lock_get(lrkproxy_hash_table->row_locks[hash_index]);
+ } else {
+ LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
+ return 0;
+ }
+
+ while (entry) {
+ // if callid found, return entry
+// if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) ||
+// (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE)) {
+ if (str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) {
+// node = entry->node;
+ // unlock
+ lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+ return entry;
+// return node;
+ }
+
+ // if expired entry discovered, delete it
+ if (entry->tout < get_ticks()) {
+ // set pointers; exclude entry
+ last_entry->next = entry->next;
+
+ // free current entry; entry points to unknown
+ lrkproxy_hash_table_free_entry(entry);
+
+ // set pointers
+ entry = last_entry;
+
+ // update total
+ lrkproxy_hash_table->row_totals[hash_index]--;
+ }
+
+ last_entry = entry;
+ entry = entry->next;
+ }
+
+ // unlock
+ lock_release(lrkproxy_hash_table->row_locks[hash_index]);
+
+ return NULL;
+}
+
+
+static void lrkproxy_hash_table_free_row_lock(gen_lock_t *row_lock) {
+ if (!row_lock) {
+ LM_ERR("try to free a NULL lock\n");
+ return ;
+ }
+
+ lock_destroy(row_lock);
+ lock_dealloc(row_lock);
+
+ return ;
+}
+
+int lrkproxy_hash_table_sanity_checks() {
+ // check lrkproxy hashtable
+ if (!lrkproxy_hash_table) {
+ LM_ERR("NULL lrkproxy_hash_table\n");
+ return 0;
+ }
+
+ // check lrkproxy hashtable->row_locks
+ if (!lrkproxy_hash_table->row_locks) {
+ LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
+ return 0;
+ }
+
+ // check lrkproxy hashtable->row_entry_list
+ if (!lrkproxy_hash_table->row_entry_list) {
+ LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
+ return 0;
+ }
+
+ // check lrkproxy hashtable->row_totals
+ if (!lrkproxy_hash_table->row_totals) {
+ LM_ERR("NULL lrkproxy_hash_table->row_totals\n");
+ return 0;
+ }
+
+ return 1;
+}
+
diff --git a/src/modules/lrkproxy/lrkproxy_hash.h b/src/modules/lrkproxy/lrkproxy_hash.h
new file mode 100644
index 00000000000..0943021b526
--- /dev/null
+++ b/src/modules/lrkproxy/lrkproxy_hash.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
+ * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
+ * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
+ *
+ * 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 LRKPROXY_HASH_H
+#define LRKPROXY_HASH_H
+
+#include "../../core/str.h"
+#include "../../core/locking.h"
+
+
+/* table entry */
+struct lrkproxy_hash_entry {
+ str src_ipv4; //media ip address of initiator call in INVITE SIP message.
+ str dst_ipv4; //media ip address of selected node in 200Ok SIP message.
+ str snat_ipv4; //change media ip address to selected node.
+ str dnat_ipv4; //change media ip address to orgin destination party.
+ str src_port; //media port of initiator call in INVITE SIP message
+ str dst_port; //media port of selected node in 200Ok SIP message.
+ str snat_port; //change media port to selected node.
+ str dnat_port; //change media port to orgin destination party.
+
+ str callid; // call callid
+ str viabranch; // call viabranch
+ struct lrkp_node *node; // call selected node
+
+ unsigned int tout; // call timeout
+ struct lrkproxy_hash_entry *next; // call next
+};
+
+/* table */
+struct lrkproxy_hash_table {
+ struct lrkproxy_hash_entry **row_entry_list; // vector of size pointers to entry
+ gen_lock_t **row_locks; // vector of size pointers to locks
+ unsigned int *row_totals; // vector of size numbers of entries in the hashtable rows
+ unsigned int size; // hash table size
+};
+
+
+
+int lrkproxy_hash_table_init(int hsize);
+int lrkproxy_hash_table_destroy();
+int lrkproxy_hash_table_insert(str callid, str viabranch, struct lrkproxy_hash_entry *value);
+int lrkproxy_hash_table_remove(str callid, str viabranch, enum lrk_operation);
+struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch);
+//struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation);
+//struct lrkp_node *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation);
+//void lrkproxy_hash_table_print();
+//unsigned int lrkproxy_hash_table_total();
+
+void lrkproxy_hash_table_free_entry(struct lrkproxy_hash_entry *entry);
+void lrkproxy_hash_table_free_row_entry_list(struct lrkproxy_hash_entry *row_entry_list);
+
+int lrkproxy_hash_table_sanity_checks();
+
+#endif //LRKPROXY_HASH_H