460db1d56e9844d1ce36159a1ddd01765a9e4d85
[sip-router] / src / modules / lrkproxy / lrkproxy_hash.c
1 /*
2  * Copyright (C) 2003-2008 Sippy Software, Inc., http://www.sippysoft.com
3  * Copyright (C) 2014-2015 Sipwise GmbH, http://www.sipwise.com
4  * Copyright (C) 2020 Mojtaba Esfandiari.S, Nasim-Telecom
5  *
6  * This file is part of Kamailio, a free SIP server.
7  *
8  * Kamailio is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  * Kamailio is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24
25 #include "lrkproxy.h"
26 #include "lrkproxy_hash.h"
27
28 #include "../../core/str.h"
29 #include "../../core/dprint.h"
30 #include "../../core/mem/shm_mem.h"
31 #include "../../core/timer.h"
32
33 static void lrkproxy_hash_table_free_row_lock(gen_lock_t *row_lock);
34
35 static struct lrkproxy_hash_table *lrkproxy_hash_table;
36
37 /* get from sipwise rtpengine */
38 static int str_cmp_str(const str a, const str b) {
39     if (a.len < b.len)
40         return -1;
41     if (a.len > b.len)
42         return 1;
43     if (a.len == 0 && b.len == 0)
44         return 0;
45     return memcmp(a.s, b.s, a.len);
46 }
47
48 /* get from sipwise rtpengine */
49 static int str_equal(str a, str b) {
50     return (str_cmp_str(a, b) == 0);
51 }
52
53 /* get from sipwise rtpengine */
54 static unsigned int str_hash(str s) {
55     unsigned int ret = 5381;
56     str it = s;
57
58     while (it.len > 0) {
59         ret = (ret << 5) + ret + *it.s;
60         it.s++;
61         it.len--;
62     }
63
64     return ret % lrkproxy_hash_table->size;
65 }
66
67 /* lrkproxy hash API */
68 int lrkproxy_hash_table_init(int size) {
69     int i;
70     int hash_table_size;
71
72
73     hash_table_size = size;
74
75
76 //            LM_DBG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table size = %d\n", hash_table_size);
77             LM_INFO(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>lrkproxy_hash_table size = %d\n", hash_table_size);
78
79     // init hashtable
80     lrkproxy_hash_table = shm_malloc(sizeof(struct lrkproxy_hash_table));
81     if (!lrkproxy_hash_table) {
82                 LM_ERR("no shm left to create lrkproxy_hash_table\n");
83         return 0;
84     }
85     memset(lrkproxy_hash_table, 0, sizeof(struct lrkproxy_hash_table));
86     lrkproxy_hash_table->size = hash_table_size;
87
88     // init hashtable row_locks
89     lrkproxy_hash_table->row_locks = shm_malloc(hash_table_size * sizeof(gen_lock_t*));
90     if (!lrkproxy_hash_table->row_locks) {
91                 LM_ERR("no shm left to create lrkproxy_hash_table->row_locks\n");
92         lrkproxy_hash_table_destroy();
93         return 0;
94     }
95     memset(lrkproxy_hash_table->row_locks, 0, hash_table_size * sizeof(gen_lock_t*));
96
97     // init hashtable row_entry_list
98     lrkproxy_hash_table->row_entry_list = shm_malloc(lrkproxy_hash_table->size * sizeof(struct lrkproxy_hash_entry*));
99     if (!lrkproxy_hash_table->row_entry_list) {
100                 LM_ERR("no shm left to create lrkproxy_hash_table->row_entry_list\n");
101         lrkproxy_hash_table_destroy();
102         return 0;
103     }
104     memset(lrkproxy_hash_table->row_entry_list, 0, lrkproxy_hash_table->size * sizeof(struct lrkproxy_hash_entry*));
105
106     // init hashtable row_totals
107     lrkproxy_hash_table->row_totals = shm_malloc(hash_table_size * sizeof(unsigned int));
108     if (!lrkproxy_hash_table->row_totals) {
109                 LM_ERR("no shm left to create lrkproxy_hash_table->row_totals\n");
110         lrkproxy_hash_table_destroy();
111         return 0;
112     }
113     memset(lrkproxy_hash_table->row_totals, 0, hash_table_size * sizeof(unsigned int));
114
115     // init hashtable  row_locks[i], row_entry_list[i] and row_totals[i]
116     for (i = 0; i < hash_table_size; i++) {
117         // alloc hashtable row_locks[i]
118         lrkproxy_hash_table->row_locks[i] = lock_alloc();
119         if (!lrkproxy_hash_table->row_locks[i]) {
120                     LM_ERR("no shm left to create lrkproxy_hash_table->row_locks[%d]\n", i);
121             lrkproxy_hash_table_destroy();
122             return 0;
123         }
124
125         // init hashtable row_locks[i]
126         if (!lock_init(lrkproxy_hash_table->row_locks[i])) {
127                     LM_ERR("fail to init lrkproxy_hash_table->row_locks[%d]\n", i);
128             lrkproxy_hash_table_destroy();
129             return 0;
130         }
131
132         // init hashtable row_entry_list[i]
133         lrkproxy_hash_table->row_entry_list[i] = shm_malloc(sizeof(struct lrkproxy_hash_entry));
134         if (!lrkproxy_hash_table->row_entry_list[i]) {
135                     LM_ERR("no shm left to create lrkproxy_hash_table->row_entry_list[%d]\n", i);
136             lrkproxy_hash_table_destroy();
137             return 0;
138         }
139         memset(lrkproxy_hash_table->row_entry_list[i], 0, sizeof(struct lrkproxy_hash_entry));
140
141         lrkproxy_hash_table->row_entry_list[i]->tout = -1;
142         lrkproxy_hash_table->row_entry_list[i]->next = NULL;
143
144         // init hashtable row_totals[i]
145         lrkproxy_hash_table->row_totals[i] = 0;
146     }
147
148     return 1;
149 }
150
151 int lrkproxy_hash_table_destroy() {
152     int i;
153
154     // check lrkproxy hashtable
155     if (!lrkproxy_hash_table) {
156                 LM_ERR("NULL lrkproxy_hash_table\n");
157         return 1;
158     }
159
160     // check lrkproxy hashtable->row_locks
161     if (!lrkproxy_hash_table->row_locks) {
162                 LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
163         shm_free(lrkproxy_hash_table);
164         lrkproxy_hash_table = NULL;
165         return 1;
166     }
167
168     // destroy hashtable content
169     for (i = 0; i < lrkproxy_hash_table->size; i++) {
170         // lock
171         if (!lrkproxy_hash_table->row_locks[i]) {
172                     LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", i);
173             continue;
174         } else {
175             lock_get(lrkproxy_hash_table->row_locks[i]);
176         }
177
178         // check lrkproxy hashtable->row_entry_list
179         if (!lrkproxy_hash_table->row_entry_list) {
180                     LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
181         } else {
182             // destroy hashtable row_entry_list[i]
183             lrkproxy_hash_table_free_row_entry_list(lrkproxy_hash_table->row_entry_list[i]);
184             lrkproxy_hash_table->row_entry_list[i] = NULL;
185         }
186
187         // unlock
188         lock_release(lrkproxy_hash_table->row_locks[i]);
189
190         // destroy hashtable row_locks[i]
191         lrkproxy_hash_table_free_row_lock(lrkproxy_hash_table->row_locks[i]);
192         lrkproxy_hash_table->row_locks[i] = NULL;
193     }
194
195     // destroy hashtable row_entry_list
196     if (!lrkproxy_hash_table->row_entry_list) {
197                 LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
198     } else {
199         shm_free(lrkproxy_hash_table->row_entry_list);
200         lrkproxy_hash_table->row_entry_list = NULL;
201     }
202
203     // destroy hashtable row_totals
204     if (!lrkproxy_hash_table->row_totals) {
205                 LM_ERR("NULL lrkproxy_hash_table->row_totals\n");
206     } else {
207         shm_free(lrkproxy_hash_table->row_totals);
208         lrkproxy_hash_table->row_totals = NULL;
209     }
210
211     // destroy hashtable row_locks
212     if (!lrkproxy_hash_table->row_locks) {
213         // should not be the case; just for code symmetry
214                 LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
215     } else {
216         shm_free(lrkproxy_hash_table->row_locks);
217         lrkproxy_hash_table->row_locks = NULL;
218     }
219
220     // destroy hashtable
221     if (!lrkproxy_hash_table) {
222         // should not be the case; just for code symmetry
223                 LM_ERR("NULL lrkproxy_hash_table\n");
224     } else {
225         shm_free(lrkproxy_hash_table);
226         lrkproxy_hash_table = NULL;
227     }
228
229     return 1;
230 }
231
232
233 void lrkproxy_hash_table_free_entry(struct lrkproxy_hash_entry *entry) {
234     if (!entry) {
235                 LM_ERR("try to free a NULL entry\n");
236         return ;
237     }
238
239     // free callid
240     if (entry->callid.s) {
241         shm_free(entry->callid.s);
242     }
243
244     // free viabranch
245     if (entry->viabranch.s) {
246         shm_free(entry->viabranch.s);
247     }
248
249     // free entry
250     shm_free(entry);
251
252     return ;
253 }
254
255 void lrkproxy_hash_table_free_row_entry_list(struct lrkproxy_hash_entry *row_entry_list) {
256     struct lrkproxy_hash_entry *entry, *last_entry;
257
258     if (!row_entry_list) {
259                 LM_ERR("try to free a NULL row_entry_list\n");
260         return ;
261     }
262
263     entry = row_entry_list;
264     while (entry) {
265         last_entry = entry;
266         entry = entry->next;
267         lrkproxy_hash_table_free_entry(last_entry);
268         last_entry = NULL;
269     }
270
271     return ;
272 }
273
274 int lrkproxy_hash_table_insert(str callid, str viabranch, struct lrkproxy_hash_entry *value) {
275     struct lrkproxy_hash_entry *entry, *last_entry;
276     struct lrkproxy_hash_entry *new_entry = (struct lrkproxy_hash_entry *) value;
277     unsigned int hash_index;
278
279     // sanity checks
280     if (!lrkproxy_hash_table_sanity_checks()) {
281                 LM_ERR("sanity checks failed\n");
282         return 0;
283     }
284
285     // get entry list
286     hash_index = str_hash(callid);
287     entry = lrkproxy_hash_table->row_entry_list[hash_index];
288     last_entry = entry;
289
290     // lock
291     if (lrkproxy_hash_table->row_locks[hash_index]) {
292         lock_get(lrkproxy_hash_table->row_locks[hash_index]);
293     } else {
294                 LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
295         return 0;
296     }
297
298     while (entry) {
299         // if found, don't add new entry
300         if (str_equal(entry->callid, new_entry->callid) &&
301             str_equal(entry->viabranch, new_entry->viabranch)) {
302             // unlock
303             lock_release(lrkproxy_hash_table->row_locks[hash_index]);
304                     LM_NOTICE("callid=%.*s, viabranch=%.*s already in hashtable, ignore new value\n",
305                               entry->callid.len, entry->callid.s,
306                               entry->viabranch.len, entry->viabranch.s);
307             return 0;
308         }
309
310         // if expired entry discovered, delete it
311         if (entry->tout < get_ticks()) {
312             // set pointers; exclude entry
313             last_entry->next = entry->next;
314
315             // free current entry; entry points to unknown
316             lrkproxy_hash_table_free_entry(entry);
317
318             // set pointers
319             entry = last_entry;
320
321             // update total
322             lrkproxy_hash_table->row_totals[hash_index]--;
323         }
324
325         // next entry in the list
326         last_entry = entry;
327         entry = entry->next;
328     }
329
330     last_entry->next = new_entry;
331
332     // update total
333     lrkproxy_hash_table->row_totals[hash_index]++;
334
335     // unlock
336     lock_release(lrkproxy_hash_table->row_locks[hash_index]);
337
338     return 1;
339 }
340
341 int lrkproxy_hash_table_remove(str callid, str viabranch, enum lrk_operation op) {
342     struct lrkproxy_hash_entry *entry, *last_entry;
343     unsigned int hash_index;
344     int found = 0;
345
346     // sanity checks
347     if (!lrkproxy_hash_table_sanity_checks()) {
348                 LM_ERR("sanity checks failed\n");
349         return 0;
350     }
351
352     // get first entry from entry list; jump over unused list head
353     hash_index = str_hash(callid);
354     entry = lrkproxy_hash_table->row_entry_list[hash_index];
355     last_entry = entry;
356
357     // lock
358     if (lrkproxy_hash_table->row_locks[hash_index]) {
359         lock_get(lrkproxy_hash_table->row_locks[hash_index]);
360     } else {
361                 LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
362         return 0;
363     }
364
365     while (entry) {
366         // if callid found, delete entry
367         if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) ||
368             (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE)) {
369             // set pointers; exclude entry
370             last_entry->next = entry->next;
371
372             // free current entry; entry points to unknown
373             lrkproxy_hash_table_free_entry(entry);
374
375             // set pointers
376             entry = last_entry;
377
378             // update total
379             lrkproxy_hash_table->row_totals[hash_index]--;
380
381             found = 1;
382
383             if (!(viabranch.len == 0 && op == OP_DELETE)) {
384                 // unlock
385                 lock_release(lrkproxy_hash_table->row_locks[hash_index]);
386                 return found;
387             }
388
389             // try to also delete other viabranch entries for callid
390             last_entry = entry;
391             entry = entry->next;
392             continue;
393         }
394
395         // if expired entry discovered, delete it
396         if (entry->tout < get_ticks()) {
397             // set pointers; exclude entry
398             last_entry->next = entry->next;
399
400             // free current entry; entry points to unknown
401             lrkproxy_hash_table_free_entry(entry);
402
403             // set pointers
404             entry = last_entry;
405
406             // update total
407             lrkproxy_hash_table->row_totals[hash_index]--;
408         }
409
410         last_entry = entry;
411         entry = entry->next;
412     }
413
414     // unlock
415     lock_release(lrkproxy_hash_table->row_locks[hash_index]);
416
417     return found;
418 }
419 //struct lrkp_node *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation op) {
420 //struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch, enum lrk_operation op) {
421 struct lrkproxy_hash_entry *lrkproxy_hash_table_lookup(str callid, str viabranch) {
422     struct lrkproxy_hash_entry *entry, *last_entry;
423     unsigned int hash_index;
424 //    struct lrkp_node *node;
425
426     // sanity checks
427     if (!lrkproxy_hash_table_sanity_checks()) {
428                 LM_ERR("sanity checks failed\n");
429         return 0;
430     }
431
432     // get first entry from entry list; jump over unused list head
433     hash_index = str_hash(callid);
434     entry = lrkproxy_hash_table->row_entry_list[hash_index];
435     last_entry = entry;
436
437     // lock
438     if (lrkproxy_hash_table->row_locks[hash_index]) {
439         lock_get(lrkproxy_hash_table->row_locks[hash_index]);
440     } else {
441                 LM_ERR("NULL lrkproxy_hash_table->row_locks[%d]\n", hash_index);
442         return 0;
443     }
444
445     while (entry) {
446         // if callid found, return entry
447 //        if ((str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) ||
448 //            (str_equal(entry->callid, callid) && viabranch.len == 0 && op == OP_DELETE)) {
449         if (str_equal(entry->callid, callid) && str_equal(entry->viabranch, viabranch)) {
450 //            node = entry->node;
451             // unlock
452             lock_release(lrkproxy_hash_table->row_locks[hash_index]);
453             return entry;
454 //            return node;
455         }
456
457         // if expired entry discovered, delete it
458         if (entry->tout < get_ticks()) {
459             // set pointers; exclude entry
460             last_entry->next = entry->next;
461
462             // free current entry; entry points to unknown
463             lrkproxy_hash_table_free_entry(entry);
464
465             // set pointers
466             entry = last_entry;
467
468             // update total
469             lrkproxy_hash_table->row_totals[hash_index]--;
470         }
471
472         last_entry = entry;
473         entry = entry->next;
474     }
475
476     // unlock
477     lock_release(lrkproxy_hash_table->row_locks[hash_index]);
478
479     return NULL;
480 }
481
482
483 static void lrkproxy_hash_table_free_row_lock(gen_lock_t *row_lock) {
484     if (!row_lock) {
485                 LM_ERR("try to free a NULL lock\n");
486         return ;
487     }
488
489     lock_destroy(row_lock);
490     lock_dealloc(row_lock);
491
492     return ;
493 }
494
495 int lrkproxy_hash_table_sanity_checks() {
496     // check lrkproxy hashtable
497     if (!lrkproxy_hash_table) {
498                 LM_ERR("NULL lrkproxy_hash_table\n");
499         return 0;
500     }
501
502     // check lrkproxy hashtable->row_locks
503     if (!lrkproxy_hash_table->row_locks) {
504                 LM_ERR("NULL lrkproxy_hash_table->row_locks\n");
505         return 0;
506     }
507
508     // check lrkproxy hashtable->row_entry_list
509     if (!lrkproxy_hash_table->row_entry_list) {
510                 LM_ERR("NULL lrkproxy_hash_table->row_entry_list\n");
511         return 0;
512     }
513
514     // check lrkproxy hashtable->row_totals
515     if (!lrkproxy_hash_table->row_totals) {
516                 LM_ERR("NULL lrkproxy_hash_table->row_totals\n");
517         return 0;
518     }
519
520     return 1;
521 }
522