mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
567 lines
14 KiB
567 lines
14 KiB
#include "rtpengine.h"
|
|
#include "rtpengine_hash.h"
|
|
|
|
#include "../../str.h"
|
|
#include "../../dprint.h"
|
|
#include "../../mem/shm_mem.h"
|
|
#include "../../timer.h"
|
|
|
|
static void rtpengine_hash_table_free_row_lock(gen_lock_t *row_lock);
|
|
|
|
|
|
static struct rtpengine_hash_table *rtpengine_hash_table;
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* from sipwise rtpengine */
|
|
static int str_equal(str a, str b) {
|
|
return (str_cmp_str(a, b) == 0);
|
|
}
|
|
|
|
/* 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 % rtpengine_hash_table->size;
|
|
}
|
|
|
|
/* rtpengine hash API */
|
|
int rtpengine_hash_table_init(int size) {
|
|
int i;
|
|
int hash_table_size;
|
|
|
|
// init hash table size
|
|
if (size < 1) {
|
|
hash_table_size = 1;
|
|
} else {
|
|
hash_table_size = size;
|
|
}
|
|
LM_DBG("rtpengine_hash_table size = %d\n", hash_table_size);
|
|
|
|
// init hashtable
|
|
rtpengine_hash_table = shm_malloc(sizeof(struct rtpengine_hash_table));
|
|
if (!rtpengine_hash_table) {
|
|
LM_ERR("no shm left to create rtpengine_hash_table\n");
|
|
return 0;
|
|
}
|
|
memset(rtpengine_hash_table, 0, sizeof(struct rtpengine_hash_table));
|
|
rtpengine_hash_table->size = hash_table_size;
|
|
|
|
// init hashtable row_locks
|
|
rtpengine_hash_table->row_locks = shm_malloc(hash_table_size * sizeof(gen_lock_t*));
|
|
if (!rtpengine_hash_table->row_locks) {
|
|
LM_ERR("no shm left to create rtpengine_hash_table->row_locks\n");
|
|
rtpengine_hash_table_destroy();
|
|
return 0;
|
|
}
|
|
memset(rtpengine_hash_table->row_locks, 0, hash_table_size * sizeof(gen_lock_t*));
|
|
|
|
// init hashtable row_entry_list
|
|
rtpengine_hash_table->row_entry_list = shm_malloc(rtpengine_hash_table->size * sizeof(struct rtpengine_hash_entry*));
|
|
if (!rtpengine_hash_table->row_entry_list) {
|
|
LM_ERR("no shm left to create rtpengine_hash_table->row_entry_list\n");
|
|
rtpengine_hash_table_destroy();
|
|
return 0;
|
|
}
|
|
memset(rtpengine_hash_table->row_entry_list, 0, rtpengine_hash_table->size * sizeof(struct rtpengine_hash_entry*));
|
|
|
|
// init hashtable row_totals
|
|
rtpengine_hash_table->row_totals = shm_malloc(hash_table_size * sizeof(unsigned int));
|
|
if (!rtpengine_hash_table->row_totals) {
|
|
LM_ERR("no shm left to create rtpengine_hash_table->row_totals\n");
|
|
rtpengine_hash_table_destroy();
|
|
return 0;
|
|
}
|
|
memset(rtpengine_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]
|
|
rtpengine_hash_table->row_locks[i] = lock_alloc();
|
|
if (!rtpengine_hash_table->row_locks[i]) {
|
|
LM_ERR("no shm left to create rtpengine_hash_table->row_locks[%d]\n", i);
|
|
rtpengine_hash_table_destroy();
|
|
return 0;
|
|
}
|
|
|
|
// init hashtable row_locks[i]
|
|
if (!lock_init(rtpengine_hash_table->row_locks[i])) {
|
|
LM_ERR("fail to init rtpengine_hash_table->row_locks[%d]\n", i);
|
|
rtpengine_hash_table_destroy();
|
|
return 0;
|
|
}
|
|
|
|
// init hashtable row_entry_list[i]
|
|
rtpengine_hash_table->row_entry_list[i] = shm_malloc(sizeof(struct rtpengine_hash_entry));
|
|
if (!rtpengine_hash_table->row_entry_list[i]) {
|
|
LM_ERR("no shm left to create rtpengine_hash_table->row_entry_list[%d]\n", i);
|
|
rtpengine_hash_table_destroy();
|
|
return 0;
|
|
}
|
|
memset(rtpengine_hash_table->row_entry_list[i], 0, sizeof(struct rtpengine_hash_entry));
|
|
|
|
rtpengine_hash_table->row_entry_list[i]->tout = -1;
|
|
rtpengine_hash_table->row_entry_list[i]->next = NULL;
|
|
|
|
// init hashtable row_totals[i]
|
|
rtpengine_hash_table->row_totals[i] = 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int rtpengine_hash_table_destroy() {
|
|
int i;
|
|
|
|
// check rtpengine hashtable
|
|
if (!rtpengine_hash_table) {
|
|
LM_ERR("NULL rtpengine_hash_table\n");
|
|
return 1;
|
|
}
|
|
|
|
// check rtpengine hashtable->row_locks
|
|
if (!rtpengine_hash_table->row_locks) {
|
|
LM_ERR("NULL rtpengine_hash_table->row_locks\n");
|
|
shm_free(rtpengine_hash_table);
|
|
rtpengine_hash_table = NULL;
|
|
return 1;
|
|
}
|
|
|
|
// destroy hashtable content
|
|
for (i = 0; i < rtpengine_hash_table->size; i++) {
|
|
// lock
|
|
if (!rtpengine_hash_table->row_locks[i]) {
|
|
LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", i);
|
|
continue;
|
|
} else {
|
|
lock_get(rtpengine_hash_table->row_locks[i]);
|
|
}
|
|
|
|
// check rtpengine hashtable->row_entry_list
|
|
if (!rtpengine_hash_table->row_entry_list) {
|
|
LM_ERR("NULL rtpengine_hash_table->row_entry_list\n");
|
|
} else {
|
|
// destroy hashtable row_entry_list[i]
|
|
rtpengine_hash_table_free_row_entry_list(rtpengine_hash_table->row_entry_list[i]);
|
|
rtpengine_hash_table->row_entry_list[i] = NULL;
|
|
}
|
|
|
|
// unlock
|
|
lock_release(rtpengine_hash_table->row_locks[i]);
|
|
|
|
// destroy hashtable row_locks[i]
|
|
rtpengine_hash_table_free_row_lock(rtpengine_hash_table->row_locks[i]);
|
|
rtpengine_hash_table->row_locks[i] = NULL;
|
|
}
|
|
|
|
// destroy hashtable row_entry_list
|
|
if (!rtpengine_hash_table->row_entry_list) {
|
|
LM_ERR("NULL rtpengine_hash_table->row_entry_list\n");
|
|
} else {
|
|
shm_free(rtpengine_hash_table->row_entry_list);
|
|
rtpengine_hash_table->row_entry_list = NULL;
|
|
}
|
|
|
|
// destroy hashtable row_totals
|
|
if (!rtpengine_hash_table->row_totals) {
|
|
LM_ERR("NULL rtpengine_hash_table->row_totals\n");
|
|
} else {
|
|
shm_free(rtpengine_hash_table->row_totals);
|
|
rtpengine_hash_table->row_totals = NULL;
|
|
}
|
|
|
|
// destroy hashtable row_locks
|
|
if (!rtpengine_hash_table->row_locks) {
|
|
// should not be the case; just for code symmetry
|
|
LM_ERR("NULL rtpengine_hash_table->row_locks\n");
|
|
} else {
|
|
shm_free(rtpengine_hash_table->row_locks);
|
|
rtpengine_hash_table->row_locks = NULL;
|
|
}
|
|
|
|
// destroy hashtable
|
|
if (!rtpengine_hash_table) {
|
|
// should not be the case; just for code symmetry
|
|
LM_ERR("NULL rtpengine_hash_table\n");
|
|
} else {
|
|
shm_free(rtpengine_hash_table);
|
|
rtpengine_hash_table = NULL;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int rtpengine_hash_table_insert(str callid, str viabranch, struct rtpengine_hash_entry *value) {
|
|
struct rtpengine_hash_entry *entry, *last_entry;
|
|
struct rtpengine_hash_entry *new_entry = (struct rtpengine_hash_entry *) value;
|
|
unsigned int hash_index;
|
|
|
|
// sanity checks
|
|
if (!rtpengine_hash_table_sanity_checks()) {
|
|
LM_ERR("sanity checks failed\n");
|
|
return 0;
|
|
}
|
|
|
|
// get entry list
|
|
hash_index = str_hash(callid);
|
|
entry = rtpengine_hash_table->row_entry_list[hash_index];
|
|
last_entry = entry;
|
|
|
|
// lock
|
|
if (rtpengine_hash_table->row_locks[hash_index]) {
|
|
lock_get(rtpengine_hash_table->row_locks[hash_index]);
|
|
} else {
|
|
LM_ERR("NULL rtpengine_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(rtpengine_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
|
|
rtpengine_hash_table_free_entry(entry);
|
|
|
|
// set pointers
|
|
entry = last_entry;
|
|
|
|
// update total
|
|
rtpengine_hash_table->row_totals[hash_index]--;
|
|
}
|
|
|
|
// next entry in the list
|
|
last_entry = entry;
|
|
entry = entry->next;
|
|
}
|
|
|
|
last_entry->next = new_entry;
|
|
|
|
// update total
|
|
rtpengine_hash_table->row_totals[hash_index]++;
|
|
|
|
// unlock
|
|
lock_release(rtpengine_hash_table->row_locks[hash_index]);
|
|
|
|
return 1;
|
|
}
|
|
|
|
int rtpengine_hash_table_remove(str callid, str viabranch, enum rtpe_operation op) {
|
|
struct rtpengine_hash_entry *entry, *last_entry;
|
|
unsigned int hash_index;
|
|
int found = 0;
|
|
|
|
// sanity checks
|
|
if (!rtpengine_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 = rtpengine_hash_table->row_entry_list[hash_index];
|
|
last_entry = entry;
|
|
|
|
// lock
|
|
if (rtpengine_hash_table->row_locks[hash_index]) {
|
|
lock_get(rtpengine_hash_table->row_locks[hash_index]);
|
|
} else {
|
|
LM_ERR("NULL rtpengine_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
|
|
rtpengine_hash_table_free_entry(entry);
|
|
|
|
// set pointers
|
|
entry = last_entry;
|
|
|
|
// update total
|
|
rtpengine_hash_table->row_totals[hash_index]--;
|
|
|
|
found = 1;
|
|
|
|
if (!(viabranch.len == 0 && op == OP_DELETE)) {
|
|
// unlock
|
|
lock_release(rtpengine_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
|
|
rtpengine_hash_table_free_entry(entry);
|
|
|
|
// set pointers
|
|
entry = last_entry;
|
|
|
|
// update total
|
|
rtpengine_hash_table->row_totals[hash_index]--;
|
|
}
|
|
|
|
last_entry = entry;
|
|
entry = entry->next;
|
|
}
|
|
|
|
// unlock
|
|
lock_release(rtpengine_hash_table->row_locks[hash_index]);
|
|
|
|
return found;
|
|
}
|
|
|
|
struct rtpp_node *rtpengine_hash_table_lookup(str callid, str viabranch, enum rtpe_operation op) {
|
|
struct rtpengine_hash_entry *entry, *last_entry;
|
|
unsigned int hash_index;
|
|
struct rtpp_node *node;
|
|
|
|
// sanity checks
|
|
if (!rtpengine_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 = rtpengine_hash_table->row_entry_list[hash_index];
|
|
last_entry = entry;
|
|
|
|
// lock
|
|
if (rtpengine_hash_table->row_locks[hash_index]) {
|
|
lock_get(rtpengine_hash_table->row_locks[hash_index]);
|
|
} else {
|
|
LM_ERR("NULL rtpengine_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)) {
|
|
node = entry->node;
|
|
|
|
// unlock
|
|
lock_release(rtpengine_hash_table->row_locks[hash_index]);
|
|
|
|
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
|
|
rtpengine_hash_table_free_entry(entry);
|
|
|
|
// set pointers
|
|
entry = last_entry;
|
|
|
|
// update total
|
|
rtpengine_hash_table->row_totals[hash_index]--;
|
|
}
|
|
|
|
last_entry = entry;
|
|
entry = entry->next;
|
|
}
|
|
|
|
// unlock
|
|
lock_release(rtpengine_hash_table->row_locks[hash_index]);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// print hash table entries while deleting expired entries
|
|
void rtpengine_hash_table_print() {
|
|
int i;
|
|
struct rtpengine_hash_entry *entry, *last_entry;
|
|
|
|
// sanity checks
|
|
if (!rtpengine_hash_table_sanity_checks()) {
|
|
LM_ERR("sanity checks failed\n");
|
|
return ;
|
|
}
|
|
|
|
// print hashtable
|
|
for (i = 0; i < rtpengine_hash_table->size; i++) {
|
|
// lock
|
|
if (rtpengine_hash_table->row_locks[i]) {
|
|
lock_get(rtpengine_hash_table->row_locks[i]);
|
|
} else {
|
|
LM_ERR("NULL rtpengine_hash_table->row_locks[%d]\n", i);
|
|
return ;
|
|
}
|
|
|
|
entry = rtpengine_hash_table->row_entry_list[i];
|
|
last_entry = entry;
|
|
|
|
while (entry) {
|
|
// 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
|
|
rtpengine_hash_table_free_entry(entry);
|
|
|
|
// set pointers
|
|
entry = last_entry;
|
|
|
|
// update total
|
|
rtpengine_hash_table->row_totals[i]--;
|
|
} else {
|
|
LM_DBG("hash_index=%d callid=%.*s tout=%u\n",
|
|
i, entry->callid.len, entry->callid.s, entry->tout - get_ticks());
|
|
}
|
|
|
|
last_entry = entry;
|
|
entry = entry->next;
|
|
}
|
|
|
|
// unlock
|
|
lock_release(rtpengine_hash_table->row_locks[i]);
|
|
}
|
|
|
|
}
|
|
|
|
unsigned int rtpengine_hash_table_total() {
|
|
int i;
|
|
unsigned int total = 0;
|
|
|
|
// sanity checks
|
|
if (!rtpengine_hash_table_sanity_checks()) {
|
|
LM_ERR("sanity checks failed\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < rtpengine_hash_table->size; i++) {
|
|
total += rtpengine_hash_table->row_totals[i];
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
void rtpengine_hash_table_free_entry(struct rtpengine_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 rtpengine_hash_table_free_row_entry_list(struct rtpengine_hash_entry *row_entry_list) {
|
|
struct rtpengine_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;
|
|
rtpengine_hash_table_free_entry(last_entry);
|
|
last_entry = NULL;
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
static void rtpengine_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 rtpengine_hash_table_sanity_checks() {
|
|
// check rtpengine hashtable
|
|
if (!rtpengine_hash_table) {
|
|
LM_ERR("NULL rtpengine_hash_table\n");
|
|
return 0;
|
|
}
|
|
|
|
// check rtpengine hashtable->row_locks
|
|
if (!rtpengine_hash_table->row_locks) {
|
|
LM_ERR("NULL rtpengine_hash_table->row_locks\n");
|
|
return 0;
|
|
}
|
|
|
|
// check rtpengine hashtable->row_entry_list
|
|
if (!rtpengine_hash_table->row_entry_list) {
|
|
LM_ERR("NULL rtpengine_hash_table->row_entry_list\n");
|
|
return 0;
|
|
}
|
|
|
|
// check rtpengine hashtable->row_totals
|
|
if (!rtpengine_hash_table->row_totals) {
|
|
LM_ERR("NULL rtpengine_hash_table->row_totals\n");
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|