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 +
+ <varname>lrkproxy_sock</varname> (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 <varname>lrkproxy_sock</varname> 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") + +... + + +
+
+ <varname>lrkproxy_disable_tout</varname> (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 <varname>lrkproxy_disable_tout</varname> parameter + +... +modparam("lrkproxy", "lrkproxy_disable_tout", 20) +... + + +
+
+ <varname>lrkproxy_tout</varname> (integer) + + Timeout value in waiting for reply from LRKP_TSL. + + + + Default value is 1. + + + + Set <varname>lrkproxy_tout</varname> parameter + +... +modparam("lrkproxy", "lrkproxy_tout", 2) +... + + +
+
+ <varname>lrkproxy_retr</varname> (integer) + + How many times the LRKP_CL should retry to send and receive after + timeout was generated. + + + + Default value is 5. + + + + Set <varname>lrkproxy_retr</varname> parameter + +... +modparam("lrkproxy", "lrkproxy_retr", 2) +... + + +
+
+ <varname>lrkp_alg</varname> (integer) + + This parameter set the algorithm of LRKP_TSL selection. + lrk_LINER=0, + lrk_RR=1 + + + + Default value is 0. + + + + Set <varname>lrkp_alg</varname> parameter + +... +modparam("lrkproxy", "lrkp_alg", 1) +... + + +
+ +
+ <varname>hash_table_tout</varname> (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 <varname>hash_table_tout</varname> parameter + +... +modparam("lrkproxy", "hash_table_tout", "3600") +... + + +
+
+ <varname>hash_table_size</varname> (integer) + + Size of the hash table. Default value is 128. + + + + Default value is 128. + + + + Set <varname>hash_table_size</varname> parameter + +... +modparam("lrkproxy", "hash_table_size", 256) +... + + +
+
+
+ Functions +
+ + <function moreinfo="none">set_lrkproxy_set(setid)</function> + + + 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. + + + <function>set_lrkproxy_set</function> usage + +... +set_lrkproxy_set("0"); +lrkproxy_manage(); +... + + +
+
+ + <function moreinfo="none">lrkproxy_manage([flags [, ip_address]])</function> + + + 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. + + + <function>lrkproxy_manage</function> 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