blst: global config variables for ignoring blacklist events
[sip-router] / dst_blacklist.c
1 /*
2  * $Id$
3  *
4  * resolver related functions
5  *
6  * Copyright (C) 2006 iptelorg GmbH
7  *
8  * This file is part of ser, a free SIP server.
9  *
10  * ser is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version
14  *
15  * For a license to use the ser software under conditions
16  * other than those described here, or to purchase support for this
17  * software, please contact iptel.org by e-mail at the following addresses:
18  *    info@iptel.org
19  *
20  * ser is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28  */
29 /* History:
30  * --------
31  *  2006-07-29  created by andrei
32  *  2007-05-39  added hooks for add; more locks to reduce contention (andrei)
33  *  2007-06-26  added hooks for search (andrei)
34  *  2007-07-30  added dst_blacklist_del() and dst_blacklist_add_to()  (andrei)
35  *  2007-07-30  dst blacklist measurements added (Gergo)
36  *  2008-02-11  dns_blacklist_init cfg parameter is introduced (Miklos)
37  *  2009-02-26  added dst_blacklist_su* variant (andrei)
38  */
39
40
41 #ifdef USE_DST_BLACKLIST
42
43 #include "dst_blacklist.h"
44 #include "globals.h"
45 #include "cfg_core.h"
46 #include "mem/shm_mem.h"
47 #include "hashes.h"
48 #include "locking.h"
49 #include "timer.h"
50 #include "timer_ticks.h"
51 #include "ip_addr.h"
52 #include "error.h"
53 #include "rpc.h"
54 #include "compiler_opt.h"
55 #include "resolve.h" /* for str2ip */
56 #ifdef USE_DST_BLACKLIST_STATS
57 #include "pt.h"
58 #endif
59
60
61
62
63 struct dst_blst_entry{
64         struct dst_blst_entry* next;
65         ticks_t expire;
66         unsigned short port;
67         unsigned char proto;
68         unsigned char flags; /* contains the address type + error flags */
69         unsigned char ip[4]; /* 4 for ipv4, 16 for ipv6 */
70 };
71
72 #define DST_BLST_ENTRY_SIZE(b) \
73                 (sizeof(struct dst_blst_entry)+((b).flags&BLST_IS_IPV6)*12)
74
75
76 #define DST_BLST_HASH_SIZE              1024
77 #define DEFAULT_BLST_TIMER_INTERVAL             60 /* 1 min */
78
79
80 /* lock method */
81 #ifdef GEN_LOCK_T_UNLIMITED
82 #define BLST_LOCK_PER_BUCKET
83 #elif defined GEN_LOCK_SET_T_UNLIMITED
84 #define BLST_LOCK_SET
85 #else
86 #define BLST_ONE_LOCK
87 #endif
88
89
90 #ifdef BLST_LOCK_PER_BUCKET
91 /* lock included in the hash bucket */
92 #define LOCK_BLST(h)            lock_get(&dst_blst_hash[(h)].lock)
93 #define UNLOCK_BLST(h)          lock_release(&dst_blst_hash[(h)].lock)
94 #elif defined BLST_LOCK_SET
95 static gen_lock_set_t* blst_lock_set=0;
96 #define LOCK_BLST(h)            lock_set_get(blst_lock_set, (h))
97 #define UNLOCK_BLST(h)          lock_set_release(blst_lock_set, (h))
98 #else
99 /* use only one lock */
100 static gen_lock_t* blst_lock=0;
101 #define LOCK_BLST(h)            lock_get(blst_lock)
102 #define UNLOCK_BLST(h)          lock_release(blst_lock)
103 #endif
104
105
106
107
108 #define BLST_HASH_STATS
109
110 #ifdef BLST_HASH_STATS
111 #define BLST_HASH_STATS_DEC(h) dst_blst_hash[(h)].entries--
112 #define BLST_HASH_STATS_INC(h) dst_blst_hash[(h)].entries++
113 #else
114 #define BLST_HASH_STATS_DEC(h) do{}while(0)
115 #define BLST_HASH_STATS_INC(h) do{}while(0)
116 #endif
117
118 struct dst_blst_lst_head{
119         struct dst_blst_entry* first;
120 #ifdef BLST_LOCK_PER_BUCKET
121         gen_lock_t      lock;
122 #endif
123 #ifdef BLST_HASH_STATS
124         unsigned int entries;
125 #endif
126 };
127
128 int dst_blacklist_init=1; /* if 0, the dst blacklist is not initialized at startup */
129 static struct timer_ln* blst_timer_h=0;
130
131 static volatile unsigned int* blst_mem_used=0;
132 unsigned int blst_timer_interval=DEFAULT_BLST_TIMER_INTERVAL;
133 struct dst_blst_lst_head* dst_blst_hash=0;
134
135 #ifdef USE_DST_BLACKLIST_STATS
136 struct t_dst_blacklist_stats* dst_blacklist_stats=0;
137 #endif
138
139 /* blacklist per protocol event ignore mask array */
140 unsigned blst_proto_imask[PROTO_LAST+1];
141
142 #ifdef DST_BLACKLIST_HOOKS
143
144 /* there 2 types of callbacks supported: on add new entry to the blacklist
145  *  (DST_BLACKLIST_ADD_CB) and on blacklist search (DST_BLACKLIST_SEARCH_CB).
146  *  Both of them take a struct dest_info*, a flags pointer(unsigned char*),
147  *  and a struct sip_msg* as parameters. The flags can be changed.
148  *  A callback should return one of:
149  *    DST_BLACKLIST_CONTINUE - do nothing, let other callbacks run
150  *    DST_BLACKLIST_ACCEPT   - for blacklist add: force accept immediately,
151  *                             for blacklist search: force match and use
152  *                              the flags as the blacklist search return.
153  *                              ( so the flags should be set to some valid
154  *                                non zero BLST flags value )
155  *   DST_BLACKLIST_DENY      - for blacklist add: don't allow adding the
156  *                              destination to the blacklist.
157  *                             for blacklist search: force return not found
158  */
159
160 #define MAX_BLST_HOOKS 1
161
162 struct blst_callbacks_lst{
163         struct blacklist_hook* hooks;
164         unsigned int max_hooks;
165         int last_idx;
166 };
167
168 static struct blst_callbacks_lst blst_add_cb;
169 static struct blst_callbacks_lst blst_search_cb;
170
171 static int init_blst_callback_lst(struct blst_callbacks_lst*  cb_lst, int max)
172 {
173
174         cb_lst->max_hooks=MAX_BLST_HOOKS;
175         cb_lst->last_idx=0;
176         cb_lst->hooks=pkg_malloc(cb_lst->max_hooks*sizeof(struct blacklist_hook));
177         if (cb_lst->hooks==0)
178                 goto error;
179         memset(cb_lst->hooks, 0, cb_lst->max_hooks*sizeof(struct blacklist_hook));
180         return 0;
181 error:
182         return -1;
183 }
184
185
186 static void destroy_blst_callback_lst(struct blst_callbacks_lst* cb_lst)
187 {
188         int r;
189         if (cb_lst && cb_lst->hooks){
190                 for (r=0; r<cb_lst->last_idx; r++){
191                         if (cb_lst->hooks[r].destroy)
192                                 cb_lst->hooks[r].destroy();
193                 }
194                 pkg_free(cb_lst->hooks);
195                 cb_lst->hooks=0;
196                 cb_lst->last_idx=0;
197                 cb_lst->max_hooks=0;
198         }
199 }
200
201
202 static void destroy_blacklist_hooks()
203 {
204         destroy_blst_callback_lst(&blst_add_cb);
205         destroy_blst_callback_lst(&blst_search_cb);
206 }
207
208
209 static int init_blacklist_hooks()
210 {
211
212         if (init_blst_callback_lst(&blst_add_cb, MAX_BLST_HOOKS)!=0)
213                 goto error;
214         if (init_blst_callback_lst(&blst_search_cb, MAX_BLST_HOOKS)!=0)
215                 goto error;
216         return 0;
217 error:
218         LOG(L_ERR, "blacklist_hooks: failure initializing internal lists\n");
219         destroy_blacklist_hooks();
220         return -1;
221 }
222
223
224
225
226 /* allocates a new hook
227  * returns 0 on success and -1 on error
228  * must be called from mod init (from the main process, before forking)*/
229 int register_blacklist_hook(struct blacklist_hook *h, int type)
230 {
231         struct blst_callbacks_lst* cb_lst;
232         struct blacklist_hook* tmp;
233         int new_max_hooks;
234
235         if (dst_blacklist_init==0) {
236                 LOG(L_ERR, "register_blacklist_hook: blacklist is turned off, "
237                         "the hook cannot be registered\n");
238                 goto error;
239         }
240
241         switch(type){
242                 case DST_BLACKLIST_ADD_CB:
243                         cb_lst=&blst_add_cb;
244                         break;
245                 case DST_BLACKLIST_SEARCH_CB:
246                         cb_lst=&blst_search_cb;
247                         break;
248                 default:
249                         BUG("register_blacklist_hook: invalid type %d\n", type);
250                         goto error;
251         }
252         if (cb_lst==0 || cb_lst->hooks==0 || cb_lst->max_hooks==0){
253                 BUG("register_blacklist_hook: intialization error\n");
254                 goto error;
255         }
256
257         if (cb_lst->last_idx >= cb_lst->max_hooks){
258                 new_max_hooks=2*cb_lst->max_hooks;
259                 tmp=pkg_realloc(cb_lst->hooks,
260                                 new_max_hooks*sizeof(struct blacklist_hook));
261                 if (tmp==0){
262                         goto error;
263                 }
264                 cb_lst->hooks=tmp;
265                 /* init the new chunk (but not the current entry which is
266                  * overwritten anyway) */
267                 memset(&cb_lst->hooks[cb_lst->max_hooks+1], 0,
268                                         (new_max_hooks-cb_lst->max_hooks-1)*
269                                                 sizeof(struct blacklist_hook));
270                 cb_lst->max_hooks=new_max_hooks;
271         }
272         cb_lst->hooks[cb_lst->last_idx]=*h;
273         cb_lst->last_idx++;
274         return 0;
275 error:
276         return -1;
277 }
278
279
280 inline static int blacklist_run_hooks(struct blst_callbacks_lst *cb_lst,
281                                                         struct dest_info* si, unsigned char* flags,
282                                                         struct sip_msg* msg)
283 {
284         int r;
285         int ret;
286
287         ret=DST_BLACKLIST_CONTINUE; /* default, if no hook installed accept
288                                                                 blacklist operation */
289         if (likely(cb_lst->last_idx==0))
290                 return ret;
291         for (r=0; r<cb_lst->last_idx; r++){
292                 ret=cb_lst->hooks[r].on_blst_action(si, flags, msg);
293                 if (ret!=DST_BLACKLIST_CONTINUE) break;
294         }
295         return ret;
296 }
297
298
299 #endif /* DST_BLACKLIST_HOOKS */
300
301
302 /** init per protocol blacklist event ignore masks.
303  * @return 0 on success, < 0 on error.
304  */
305 int blst_init_ign_masks()
306 {
307         if ((PROTO_UDP > PROTO_LAST) || (PROTO_TCP > PROTO_LAST) ||
308                 (PROTO_TLS > PROTO_LAST) || (PROTO_SCTP > PROTO_LAST)){
309                 BUG("protocol array too small\n");
310                 return -1;
311         }
312         blst_proto_imask[PROTO_UDP]=cfg_get(core, core_cfg, blst_udp_imask);
313         blst_proto_imask[PROTO_TCP]=cfg_get(core, core_cfg, blst_tcp_imask);
314         blst_proto_imask[PROTO_TLS]=cfg_get(core, core_cfg, blst_tls_imask);
315         blst_proto_imask[PROTO_SCTP]=cfg_get(core, core_cfg, blst_sctp_imask);
316         blst_proto_imask[PROTO_NONE]=blst_proto_imask[PROTO_UDP];
317         return 0;
318 }
319
320
321
322 inline static void blst_destroy_entry(struct dst_blst_entry* e)
323 {
324         shm_free(e);
325 }
326
327
328 static ticks_t blst_timer(ticks_t ticks, struct timer_ln* tl, void* data);
329
330
331 inline static void dst_blst_entry2ip(struct ip_addr* ip,
332                                                                                 struct dst_blst_entry* e)
333 {
334 #ifdef USE_IPV6
335         if (e->flags & BLST_IS_IPV6){
336                 ip->af=AF_INET6;
337                 ip->len=16;
338         }else
339 #endif /* USE_IPV6 */
340         {
341                 ip->af=AF_INET;
342                 ip->len=4;
343         }
344         memcpy(ip->u.addr, e->ip, ip->len);
345 }
346
347
348
349 inline static unsigned short dst_blst_hash_no(unsigned char proto,
350                                                                                           struct ip_addr* ip,
351                                                                                           unsigned short port)
352 {
353         str s1;
354         str s2;
355
356         s1.s=(char*)ip->u.addr;
357         s1.len=ip->len;
358         s2.s=(char*)&port;
359         s2.len=sizeof(unsigned short);
360         return get_hash2_raw(&s1, &s2)%DST_BLST_HASH_SIZE;
361 }
362
363
364
365 void destroy_dst_blacklist()
366 {
367         int r;
368         struct dst_blst_entry** crt;
369         struct dst_blst_entry* e;
370
371         if (blst_timer_h){
372                 timer_del(blst_timer_h);
373                 timer_free(blst_timer_h);
374                 blst_timer_h=0;
375         }
376 #ifdef BLST_LOCK_PER_BUCKET
377         if (dst_blst_hash)
378                 for(r=0; r<DST_BLST_HASH_SIZE; r++)
379                         lock_destroy(&dst_blst_hash[r].lock);
380 #elif defined BLST_LOCK_SET
381                 if (blst_lock_set){
382                         lock_set_destroy(blst_lock_set);
383                         lock_set_dealloc(blst_lock_set);
384                         blst_lock_set=0;
385                 }
386 #else
387         if (blst_lock){
388                 lock_destroy(blst_lock);
389                 lock_dealloc(blst_lock);
390                 blst_lock=0;
391         }
392 #endif
393
394         if (dst_blst_hash){
395                 for(r=0; r<DST_BLST_HASH_SIZE; r++){
396                         crt=&dst_blst_hash[r].first;
397                         while(*crt){
398                                 e=*crt;
399                                 *crt=(*crt)->next;
400                                 blst_destroy_entry(e);
401                         }
402                 }
403                 shm_free(dst_blst_hash);
404                 dst_blst_hash=0;
405         }
406         if (blst_mem_used){
407                 shm_free((void*)blst_mem_used);
408                 blst_mem_used=0;
409         }
410 #ifdef DST_BLACKLIST_HOOKS
411         destroy_blacklist_hooks();
412 #endif
413
414 #ifdef USE_DST_BLACKLIST_STATS
415         if (dst_blacklist_stats)
416                 shm_free(dst_blacklist_stats);
417 #endif
418 }
419
420
421
422 int init_dst_blacklist()
423 {
424         int ret;
425 #ifdef BLST_LOCK_PER_BUCKET
426         int r;
427 #endif
428
429         if (dst_blacklist_init==0) {
430                 /* the dst blacklist is turned off */
431                 default_core_cfg.use_dst_blacklist=0;
432                 return 0;
433         }
434
435         ret=-1;
436 #ifdef DST_BLACKLIST_HOOKS
437         if (init_blacklist_hooks()!=0){
438                 ret=E_OUT_OF_MEM;
439                 goto error;
440         }
441 #endif
442         blst_mem_used=shm_malloc(sizeof(*blst_mem_used));
443         if (blst_mem_used==0){
444                 ret=E_OUT_OF_MEM;
445                 goto error;
446         }
447         *blst_mem_used=0;
448         dst_blst_hash=shm_malloc(sizeof(struct dst_blst_lst_head) *
449                                                                                         DST_BLST_HASH_SIZE);
450         if (dst_blst_hash==0){
451                 ret=E_OUT_OF_MEM;
452                 goto error;
453         }
454         memset(dst_blst_hash, 0, sizeof(struct dst_blst_lst_head) *
455                                                                 DST_BLST_HASH_SIZE);
456 #ifdef BLST_LOCK_PER_BUCKET
457         for (r=0; r<DST_BLST_HASH_SIZE; r++){
458                 if (lock_init(&dst_blst_hash[r].lock)==0){
459                         ret=-1;
460                         goto error;
461                 }
462         }
463 #elif defined BLST_LOCK_SET
464         blst_lock_set=lock_set_alloc(DST_BLST_HASH_SIZE);
465         if (blst_lock_set==0){
466                 ret=E_OUT_OF_MEM;
467                 goto error;
468         }
469         if (lock_set_init(blst_lock_set)==0){
470                 lock_set_dealloc(blst_lock_set);
471                 blst_lock_set=0;
472                 ret=-1;
473                 goto error;
474         }
475 #else /* BLST_ONE_LOCK */
476         blst_lock=lock_alloc();
477         if (blst_lock==0){
478                 ret=E_OUT_OF_MEM;
479                 goto error;
480         }
481         if (lock_init(blst_lock)==0){
482                 lock_dealloc(blst_lock);
483                 blst_lock=0;
484                 ret=-1;
485                 goto error;
486         }
487 #endif /* BLST*LOCK*/
488         blst_timer_h=timer_alloc();
489         if (blst_timer_h==0){
490                 ret=E_OUT_OF_MEM;
491                 goto error;
492         }
493         /* fix options */
494         default_core_cfg.blst_max_mem<<=10; /* in Kb */ /* TODO: test with 0 */
495         if (blst_timer_interval){
496                 timer_init(blst_timer_h, blst_timer, 0 ,0); /* slow timer */
497                 if (timer_add(blst_timer_h, S_TO_TICKS(blst_timer_interval))<0){
498                         LOG(L_CRIT, "BUG: init_dst_blacklist: failed to add the timer\n");
499                         timer_free(blst_timer_h);
500                         blst_timer_h=0;
501                         goto error;
502                 }
503         }
504         if (blst_init_ign_masks() < 0){
505                 ret=E_BUG;
506                 goto error;
507         }
508         return 0;
509 error:
510         destroy_dst_blacklist();
511         return ret;
512 }
513
514 #ifdef USE_DST_BLACKLIST_STATS
515 int init_dst_blacklist_stats(int iproc_num)
516 {
517         /* do not initialize the stats array if the dst blacklist will not be used */
518         if (dst_blacklist_init==0) return 0;
519
520         /* if it is already initialized */
521         if (dst_blacklist_stats)
522                 shm_free(dst_blacklist_stats);
523
524         dst_blacklist_stats=shm_malloc(sizeof(*dst_blacklist_stats) * iproc_num);
525         if (dst_blacklist_stats==0){
526                 return E_OUT_OF_MEM;
527         }
528         memset(dst_blacklist_stats, 0, sizeof(*dst_blacklist_stats) * iproc_num);
529
530         return 0;
531 }
532 #endif
533
534 /* must be called with the lock held
535  * struct dst_blst_entry** head, struct dst_blst_entry* e */
536 #define dst_blacklist_lst_add(head, e)\
537 do{ \
538         (e)->next=*(head); \
539         *(head)=(e); \
540 }while(0)
541
542
543
544 /* must be called with the lock held
545  * returns a pointer to the blacklist entry if found, 0 otherwise
546  * it also deletes expired elements (expire<=now) as it searches
547  * proto==PROTO_NONE = wildcard */
548 inline static struct dst_blst_entry* _dst_blacklist_lst_find(
549                                                                                                 unsigned short hash,
550                                                                                                 struct ip_addr* ip,
551                                                                                                 unsigned char proto,
552                                                                                                 unsigned short port,
553                                                                                                 ticks_t now)
554 {
555         struct dst_blst_entry** crt;
556         struct dst_blst_entry** tmp;
557         struct dst_blst_entry* e;
558         struct dst_blst_entry** head;
559         unsigned char type;
560
561         head=&dst_blst_hash[hash].first;
562 #ifdef USE_IPV6
563         type=(ip->af==AF_INET6)*BLST_IS_IPV6;
564 #else  /* USE_IPV6 */
565         if (unlikely(ip->af!=AF_INET)) return 0;
566         type=0;
567 #endif /* USE_IPV6 */
568         for (crt=head, tmp=&(*head)->next; *crt; crt=tmp, tmp=&(*crt)->next){
569                 e=*crt;
570                 prefetch_loc_r((*crt)->next, 1);
571                 /* remove old expired entries */
572                 if ((s_ticks_t)(now-(*crt)->expire)>=0){
573                         *crt=(*crt)->next;
574                         tmp=crt;
575                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
576                         BLST_HASH_STATS_DEC(hash);
577                         blst_destroy_entry(e);
578                 }else if ((e->port==port) && ((e->flags & BLST_IS_IPV6)==type) &&
579                                 ((e->proto==PROTO_NONE) || (proto==PROTO_NONE) ||
580                                         (e->proto==proto)) &&
581                                         (memcmp(ip->u.addr, e->ip, ip->len)==0)){
582                         return e;
583                 }
584         }
585         return 0;
586 }
587
588
589
590 /* must be called with the lock held
591  * returns 1 if a matching entry was deleted, 0 otherwise
592  * it also deletes expired elements (expire<=now) as it searches
593  * proto==PROTO_NONE = wildcard */
594 inline static int _dst_blacklist_del(
595                                                                                                 unsigned short hash,
596                                                                                                 struct ip_addr* ip,
597                                                                                                 unsigned char proto,
598                                                                                                 unsigned short port,
599                                                                                                 ticks_t now)
600 {
601         struct dst_blst_entry** crt;
602         struct dst_blst_entry** tmp;
603         struct dst_blst_entry* e;
604         struct dst_blst_entry** head;
605         unsigned char type;
606         
607         head=&dst_blst_hash[hash].first;
608 #ifdef USE_IPV6
609         type=(ip->af==AF_INET6)*BLST_IS_IPV6;
610 #else  /* USE_IPV6 */
611         if (unlikely(ip->af!=AF_INET)) return 0;
612         type=0;
613 #endif /* USE_IPV6 */
614         for (crt=head, tmp=&(*head)->next; *crt; crt=tmp, tmp=&(*crt)->next){
615                 e=*crt;
616                 prefetch_loc_r((*crt)->next, 1);
617                 /* remove old expired entries */
618                 if ((s_ticks_t)(now-(*crt)->expire)>=0){
619                         *crt=(*crt)->next;
620                         tmp=crt;
621                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
622                         BLST_HASH_STATS_DEC(hash);
623                         blst_destroy_entry(e);
624                 }else if ((e->port==port) && ((e->flags & BLST_IS_IPV6)==type) &&
625                                 ((e->proto==PROTO_NONE) || (proto==PROTO_NONE) ||
626                                         (e->proto==proto)) && 
627                                         (memcmp(ip->u.addr, e->ip, ip->len)==0)){
628                         *crt=(*crt)->next;
629                         tmp=crt;
630                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
631                         BLST_HASH_STATS_DEC(hash);
632                         blst_destroy_entry(e);
633                         return 1;
634                 }
635         }
636         return 0;
637 }
638
639
640
641 /* frees all the expired entries until either there are no more of them
642  *  or the total memory used is <= target (to free all of them use -1 for
643  *  targer)
644  *  params:   target  - free expired entries until no more then taget memory
645  *                      is used  (use 0 to free all of them)
646  *            delta   - consider an entry expired if it expires after delta
647  *                      ticks from now
648  *            timeout - exit after timeout ticks
649  *
650  *  returns: number of deleted entries
651  *  This function should be called periodically from a timer
652  */
653 inline static int dst_blacklist_clean_expired(unsigned int target,
654                                                                           ticks_t delta,
655                                                                           ticks_t timeout)
656 {
657         static unsigned int start=0;
658         unsigned int h;
659         struct dst_blst_entry** crt;
660         struct dst_blst_entry** tmp;
661         struct dst_blst_entry* e;
662         ticks_t start_time;
663         ticks_t now;
664         int no=0;
665         int i;
666
667         now=start_time=get_ticks_raw();
668         for(h=start; h!=(start+DST_BLST_HASH_SIZE); h++){
669                 i=h%DST_BLST_HASH_SIZE;
670                 if (dst_blst_hash[i].first){
671                         LOCK_BLST(i);
672                         for (crt=&dst_blst_hash[i].first, tmp=&(*crt)->next;
673                                         *crt; crt=tmp, tmp=&(*crt)->next){
674                                 e=*crt;
675                                 prefetch_loc_r((*crt)->next, 1);
676                                 if ((s_ticks_t)(now+delta-(*crt)->expire)>=0){
677                                         *crt=(*crt)->next;
678                                         tmp=crt;
679                                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
680                                         blst_destroy_entry(e);
681                                         BLST_HASH_STATS_DEC(i);
682                                         no++;
683                                         if (*blst_mem_used<=target){
684                                                 UNLOCK_BLST(i);
685                                                 goto skip;
686                                         }
687                                 }
688                         }
689                         UNLOCK_BLST(i);
690                         /* check for timeout only "between" hash cells */
691                         now=get_ticks_raw();
692                         if ((now-start_time)>=timeout){
693                                 DBG("_dst_blacklist_clean_expired_unsafe: timeout: %d > %d\n",
694                                                 TICKS_TO_MS(now-start_time), TICKS_TO_MS(timeout));
695                                 goto skip;
696                         }
697                 }
698         }
699 skip:
700         start=h; /* next time we start where we left */
701         if (no){
702                 DBG("dst_blacklist_clean_expired, %d entries removed\n", no);
703         }
704         return no;
705 }
706
707
708
709 /* timer */
710 static ticks_t blst_timer(ticks_t ticks, struct timer_ln* tl, void* data)
711 {
712         dst_blacklist_clean_expired(0, 0, 2); /*spend max. 2 ticks*/
713         return (ticks_t)(-1);
714 }
715
716
717
718 /* adds a proto ip:port combination to the blacklist
719  * returns 0 on success, -1 on error (blacklist full -- would use more then
720  *  blst:_max_mem, or out of shm. mem.)
721  */
722 inline static int dst_blacklist_add_ip(unsigned char err_flags,
723                                                                         unsigned char proto,
724                                                                         struct ip_addr* ip, unsigned short port,
725                                                                         ticks_t timeout)
726 {
727         int size;
728         struct dst_blst_entry* e;
729         unsigned short hash;
730         ticks_t now;
731         int ret;
732
733         ret=0;
734         if (ip->af==AF_INET){
735                 err_flags&=~BLST_IS_IPV6; /* make sure the ipv6 flag is reset */
736                 size=sizeof(struct dst_blst_entry);
737         }else{
738                 err_flags|=BLST_IS_IPV6;
739                 size=sizeof(struct dst_blst_entry)+12 /* ipv6 addr - 4 */;
740         }
741         now=get_ticks_raw();
742         hash=dst_blst_hash_no(proto, ip, port);
743         /* check if the entry already exists */
744         LOCK_BLST(hash);
745                 e=_dst_blacklist_lst_find(hash, ip, proto, port, now);
746                 if (e){
747                         e->flags|=err_flags;
748                         e->expire=now+timeout; /* update the timeout */
749                 }else{
750                         if (unlikely((*blst_mem_used+size) >=
751                                         cfg_get(core, core_cfg, blst_max_mem))){
752 #ifdef USE_DST_BLACKLIST_STATS
753                                 dst_blacklist_stats[process_no].bkl_lru_cnt++;
754 #endif
755                                 UNLOCK_BLST(hash);
756                                 /* first try to free some memory  (~ 12%), but don't
757                                  * spend more then 250 ms*/
758                                 dst_blacklist_clean_expired(*blst_mem_used/16*14, 0,
759                                                                                                                         MS_TO_TICKS(250));
760                                 if (unlikely(*blst_mem_used+size >=
761                                                 cfg_get(core, core_cfg, blst_max_mem))){
762                                         ret=-1;
763                                         goto error;
764                                 }
765                                 LOCK_BLST(hash);
766                         }
767                         e=shm_malloc(size);
768                         if (e==0){
769                                 UNLOCK_BLST(hash);
770                                 ret=E_OUT_OF_MEM;
771                                 goto error;
772                         }
773                         *blst_mem_used+=size;
774                         e->flags=err_flags;
775                         e->proto=proto;
776                         e->port=port;
777                         memcpy(e->ip, ip->u.addr, ip->len);
778                         e->expire=now+timeout; /* update the timeout */
779                         e->next=0;
780                         dst_blacklist_lst_add(&dst_blst_hash[hash].first, e);
781                         BLST_HASH_STATS_INC(hash);
782                 }
783         UNLOCK_BLST(hash);
784 error:
785         return ret;
786 }
787
788
789
790 /* if no blacklisted returns 0, else returns the blacklist flags */
791 inline static int dst_is_blacklisted_ip(unsigned char proto,
792                                                                                 struct ip_addr* ip,
793                                                                                 unsigned short port)
794 {
795         struct dst_blst_entry* e;
796         unsigned short hash;
797         ticks_t now;
798         int ret;
799
800         ret=0;
801         now=get_ticks_raw();
802         hash=dst_blst_hash_no(proto, ip, port);
803         if (unlikely(dst_blst_hash[hash].first)){
804                 LOCK_BLST(hash);
805                         e=_dst_blacklist_lst_find(hash, ip, proto, port, now);
806                         if (e){
807                                 ret=e->flags;
808                         }
809                 UNLOCK_BLST(hash);
810         }
811         return ret;
812 }
813
814
815
816 /** add dst to the blacklist, specifying the timeout.
817  * @param err_flags - reason (bitmap)
818  * @param si - destination (protocol, ip and port)
819  * @param msg - sip message that triggered the blacklisting (can be 0 if 
820  *               not known)
821  * @param timeout - timeout in ticks
822  * @return 0 on success, -1 on error
823  */
824 int dst_blacklist_force_add_to(unsigned char err_flags,  struct dest_info* si,
825                                                                 struct sip_msg* msg, ticks_t timeout)
826 {
827         struct ip_addr ip;
828
829 #ifdef DST_BLACKLIST_HOOKS
830         if (unlikely (blacklist_run_hooks(&blst_add_cb, si, &err_flags, msg) ==
831                                         DST_BLACKLIST_DENY))
832                 return 0;
833 #endif
834         su2ip_addr(&ip, &si->to);
835         return dst_blacklist_add_ip(err_flags, si->proto, &ip,
836                                                                 su_getport(&si->to), timeout);
837 }
838
839
840
841 /** add dst to the blacklist, specifying the timeout.
842  * (like @function dst_blacklist_force_add_to)= above, but uses 
843  * (proto, sockaddr_union) instead of struct dest_info)
844  */
845 int dst_blacklist_force_su_to(unsigned char err_flags, unsigned char proto,
846                                                                 union sockaddr_union* dst,
847                                                                 struct sip_msg* msg, ticks_t timeout)
848 {
849         struct ip_addr ip;
850 #ifdef DST_BLACKLIST_HOOKS
851         struct dest_info si;
852         
853         init_dest_info(&si);
854         si.to=*dst;
855         si.proto=proto;
856         if (unlikely (blacklist_run_hooks(&blst_add_cb, &si, &err_flags, msg) ==
857                                         DST_BLACKLIST_DENY))
858                 return 0;
859 #endif
860         su2ip_addr(&ip, dst);
861         return dst_blacklist_add_ip(err_flags, proto, &ip,
862                                                                 su_getport(dst), timeout);
863 }
864
865
866
867 int dst_is_blacklisted(struct dest_info* si, struct sip_msg* msg)
868 {
869         int ires;
870         struct ip_addr ip;
871 #ifdef DST_BLACKLIST_HOOKS
872         unsigned char err_flags;
873         int action;
874 #endif
875         su2ip_addr(&ip, &si->to);
876
877 #ifdef DST_BLACKLIST_HOOKS
878         err_flags=0;
879         if (unlikely((action=(blacklist_run_hooks(&blst_search_cb, si, &err_flags, msg))
880                                         ) != DST_BLACKLIST_CONTINUE)){
881                 if (action==DST_BLACKLIST_DENY)
882                         return 0;
883                 else  /* if (action==DST_BLACKLIST_ACCEPT) */
884                         return err_flags;
885         }
886 #endif
887         ires=dst_is_blacklisted_ip(si->proto, &ip, su_getport(&si->to));
888 #ifdef USE_DST_BLACKLIST_STATS
889         if (ires)
890                 dst_blacklist_stats[process_no].bkl_hit_cnt++;
891 #endif
892         return ires;
893 }
894
895
896
897 /* returns 1 if the entry was deleted, 0 if not found */
898 int dst_blacklist_del(struct dest_info* si, struct sip_msg* msg)
899 {
900         unsigned short hash;
901         struct ip_addr ip;
902         ticks_t now;
903         int ret;
904         unsigned short port;
905         
906         ret=0;
907         su2ip_addr(&ip, &si->to);
908         port=su_getport(&si->to);
909         now=get_ticks_raw();
910         hash=dst_blst_hash_no(si->proto, &ip, port);
911         if (unlikely(dst_blst_hash[hash].first)){
912                 LOCK_BLST(hash);
913                         ret=_dst_blacklist_del(hash, &ip, si->proto, port, now);
914                 UNLOCK_BLST(hash);
915         }
916         return ret;
917 }
918
919
920
921 /* rpc functions */
922 void dst_blst_mem_info(rpc_t* rpc, void* ctx)
923 {
924         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
925                 rpc->fault(ctx, 500, "dst blacklist support disabled");
926                 return;
927         }
928         rpc->add(ctx, "dd",  *blst_mem_used, cfg_get(core, core_cfg, blst_max_mem));
929 }
930
931
932
933 static char* get_proto_name(unsigned char proto)
934 {
935         switch(proto){
936                 case PROTO_NONE:
937                         return "*";
938                 case PROTO_UDP:
939                         return "udp";
940                 case PROTO_TCP:
941                         return "tcp";
942                 case PROTO_TLS:
943                         return "tls";
944                 case PROTO_SCTP:
945                         return "sctp";
946                 default:
947                         return "unknown";
948         }
949 }
950
951
952 #ifdef USE_DST_BLACKLIST_STATS
953
954 static unsigned long  stat_sum(int ivar, int breset) {
955         unsigned long isum=0;
956         int i1=0;
957
958         for (; i1 < get_max_procs(); i1++)
959                 switch (ivar) {
960                         case 0:
961                                 isum+=dst_blacklist_stats[i1].bkl_hit_cnt;
962                                 if (breset)
963                                         dst_blacklist_stats[i1].bkl_hit_cnt=0;
964                                 break;
965                         case 1:
966                                 isum+=dst_blacklist_stats[i1].bkl_lru_cnt;
967                                 if (breset)
968                                         dst_blacklist_stats[i1].bkl_lru_cnt=0;
969                                 break;
970                 }
971
972                 return isum;
973 }
974
975
976 void dst_blst_stats_get(rpc_t* rpc, void* c)
977 {
978         char *name=NULL;
979         void *handle;
980         int found=0,i=0;
981         int reset=0;
982         char* dst_blacklist_stats_names[] = {
983                 "bkl_hit_cnt",
984                 "bkl_lru_cnt",
985                 NULL
986         };
987         
988         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
989                 rpc->fault(c, 500, "dst blacklist support disabled");
990                 return;
991         }
992         if (rpc->scan(c, "s", &name) < 0)
993                 return;
994         if (rpc->scan(c, "d", &reset) < 0)
995                 return;
996         if (!strcasecmp(name, DST_BLACKLIST_ALL_STATS)) {
997                 /* dump all the dns cache stat values */
998                 rpc->add(c, "{", &handle);
999                 for (i=0; dst_blacklist_stats_names[i]; i++)
1000                         rpc->struct_add(handle, "d",
1001                                                         dst_blacklist_stats_names[i],
1002                                                         stat_sum(i, reset));
1003
1004                 found=1;
1005         } else {
1006                 for (i=0; dst_blacklist_stats_names[i]; i++)
1007                         if (!strcasecmp(dst_blacklist_stats_names[i], name)) {
1008                         rpc->add(c, "{", &handle);
1009                         rpc->struct_add(handle, "d",
1010                                                         dst_blacklist_stats_names[i],
1011                                                         stat_sum(i, reset));
1012                         found=1;
1013                         break;
1014                         }
1015         }
1016         if(!found)
1017                 rpc->fault(c, 500, "unknown dst blacklist stat parameter");
1018
1019         return;
1020 }
1021 #endif /* USE_DST_BLACKLIST_STATS */
1022
1023 /* only for debugging, it helds the lock too long for "production" use */
1024 void dst_blst_debug(rpc_t* rpc, void* ctx)
1025 {
1026         int h;
1027         struct dst_blst_entry* e;
1028         ticks_t now;
1029         struct ip_addr ip;
1030
1031         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
1032                 rpc->fault(ctx, 500, "dst blacklist support disabled");
1033                 return;
1034         }
1035         now=get_ticks_raw();
1036                 for(h=0; h<DST_BLST_HASH_SIZE; h++){
1037                         LOCK_BLST(h);
1038                         for(e=dst_blst_hash[h].first; e; e=e->next){
1039                                 dst_blst_entry2ip(&ip, e);
1040                                 rpc->add(ctx, "ssddd", get_proto_name(e->proto),
1041                                                                                 ip_addr2a(&ip), e->port,
1042                                                                                 (s_ticks_t)(now-e->expire)<=0?
1043                                                                                 TICKS_TO_S(e->expire-now):
1044                                                                                 -TICKS_TO_S(now-e->expire) ,
1045                                                                                 e->flags);
1046                         }
1047                         UNLOCK_BLST(h);
1048                 }
1049 }
1050
1051 /* only for debugging, it helds the lock too long for "production" use */
1052 void dst_blst_hash_stats(rpc_t* rpc, void* ctx)
1053 {
1054         int h;
1055         struct dst_blst_entry* e;
1056 #ifdef BLST_HASH_STATS
1057         int n;
1058
1059         n=0;
1060 #endif
1061         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
1062                 rpc->fault(ctx, 500, "dst blacklist support disabled");
1063                 return;
1064         }
1065                 for(h=0; h<DST_BLST_HASH_SIZE; h++){
1066 #ifdef BLST_HASH_STATS
1067                         LOCK_BLST(h);
1068                         for(e=dst_blst_hash[h].first; e; e=e->next) n++;
1069                         UNLOCK_BLST(h);
1070                         rpc->add(ctx, "dd", h, n);
1071 #else
1072                         rpc->add(ctx, "dd", h, dst_blst_hash[h].entries);
1073 #endif
1074                 }
1075 }
1076
1077 /* dumps the content of the blacklist in a human-readable format */
1078 void dst_blst_view(rpc_t* rpc, void* ctx)
1079 {
1080         int h;
1081         int expires;
1082         struct dst_blst_entry* e;
1083         ticks_t now;
1084         struct ip_addr ip;
1085
1086         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
1087                 rpc->fault(ctx, 500, "dst blacklist support disabled");
1088                 return;
1089         }
1090         now=get_ticks_raw();
1091         for(h=0; h<DST_BLST_HASH_SIZE; h++) {
1092                 LOCK_BLST(h);
1093                 for(e=dst_blst_hash[h].first; e; e=e->next) {
1094                         expires = (s_ticks_t)(now-e->expire)<=0?
1095                                    TICKS_TO_S(e->expire-now): -TICKS_TO_S(now-e->expire);
1096                         /* don't include expired entries into view report */
1097                         if (expires < 0) {
1098                                 continue;
1099                         }
1100                         dst_blst_entry2ip(&ip, e);
1101                         rpc->printf(ctx, "{\n    protocol: %s", get_proto_name(e->proto));
1102                         rpc->printf(ctx, "    ip: %s", ip_addr2a(&ip));
1103                         rpc->printf(ctx, "    port: %d", e->port);
1104                         rpc->printf(ctx, "    expires in (s): %d", expires); 
1105                         rpc->printf(ctx, "    flags: %d\n}", e->flags);
1106                 }
1107                 UNLOCK_BLST(h);
1108         }
1109 }
1110
1111
1112 /* deletes all the entries from the blacklist except the permanent ones
1113  * (which are marked with BLST_PERMANENT)
1114  */
1115 void dst_blst_flush(void)
1116 {
1117         int h;
1118         struct dst_blst_entry* e;
1119         struct dst_blst_entry** crt;
1120         struct dst_blst_entry** tmp;
1121
1122         for(h=0; h<DST_BLST_HASH_SIZE; h++){
1123                 LOCK_BLST(h);
1124                 for (crt=&dst_blst_hash[h].first, tmp=&(*crt)->next;
1125                                 *crt; crt=tmp, tmp=&(*crt)->next){
1126                         e=*crt;
1127                         prefetch_loc_r((*crt)->next, 1);
1128                         if (!(e->flags &  BLST_PERMANENT)){
1129                                 *crt=(*crt)->next;
1130                                 tmp=crt;
1131                                 *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
1132                                 blst_destroy_entry(e);
1133                                 BLST_HASH_STATS_DEC(h);
1134                         }
1135                 }
1136                 UNLOCK_BLST(h);
1137         }
1138 }
1139
1140 /* rpc wrapper function for dst_blst_flush() */
1141 void dst_blst_delete_all(rpc_t* rpc, void* ctx)
1142 {
1143         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
1144                 rpc->fault(ctx, 500, "dst blacklist support disabled");
1145                 return;
1146         }
1147         dst_blst_flush();
1148 }
1149
1150 /* Adds a new entry to the blacklist */
1151 void dst_blst_add(rpc_t* rpc, void* ctx)
1152 {
1153         str ip;
1154         int port, proto, flags;
1155         unsigned char err_flags;
1156         struct ip_addr *ip_addr;
1157
1158         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
1159                 rpc->fault(ctx, 500, "dst blacklist support disabled");
1160                 return;
1161         }
1162         if (rpc->scan(ctx, "Sddd", &ip, &port, &proto, &flags) < 4)
1163                 return;
1164
1165         err_flags = (unsigned char)flags;
1166         /* sanity checks */
1167         if ((unsigned char)proto > PROTO_SCTP) {
1168                 rpc->fault(ctx, 400, "Unknown protocol");
1169                 return;
1170         }
1171
1172         if (err_flags & BLST_IS_IPV6) {
1173 #ifdef USE_IPV6
1174                 /* IPv6 address is specified */
1175                 ip_addr = str2ip6(&ip);
1176 #else  /* USE_IPV6 */
1177                 rpc->fault(ctx, 400, "IPv6 support disabled");
1178                 return;
1179 #endif /* USE_IPV6 */
1180         } else {
1181                 /* try IPv4 first, than IPv6 */
1182                 ip_addr = str2ip(&ip);
1183                 if (!ip_addr) {
1184 #ifdef USE_IPV6
1185                         ip_addr = str2ip6(&ip);
1186                         err_flags |= BLST_IS_IPV6;
1187 #else  /* USE_IPV6 */
1188                         rpc->fault(ctx, 400, "Malformed or IPv6 ip address");
1189                         return;
1190 #endif /* USE_IPV6 */
1191                 }
1192         }
1193         if (!ip_addr) {
1194                 rpc->fault(ctx, 400, "Malformed ip address");
1195                 return;
1196         }
1197
1198         if (dst_blacklist_add_ip(err_flags, proto, ip_addr, port, 
1199                                     S_TO_TICKS(cfg_get(core, core_cfg, blst_timeout))))
1200                 rpc->fault(ctx, 400, "Failed to add the entry to the blacklist");
1201 }
1202
1203 /* fixup function for use_dst_blacklist
1204  * verifies that dst_blacklist_init is set to 1
1205  */
1206 int use_dst_blacklist_fixup(void *handle, str *gname, str *name, void **val)
1207 {
1208         if ((int)(long)(*val) && !dst_blacklist_init) {
1209                 LOG(L_ERR, "ERROR: use_dst_blacklist_fixup(): "
1210                         "dst blacklist is turned off by dst_blacklist_init=0, "
1211                         "it cannot be enabled runtime.\n");
1212                 return -1;
1213         }
1214         return 0;
1215 }
1216
1217 /* KByte to Byte conversion */
1218 int blst_max_mem_fixup(void *handle, str *gname, str *name, void **val)
1219 {
1220         unsigned int    u;
1221
1222         u = ((unsigned int)(long)(*val))<<10;
1223         (*val) = (void *)(long)u;
1224         return 0;
1225 }
1226
1227
1228
1229 /** re-inint per child blst_proto_ign_mask array. */
1230 void blst_reinit_ign_masks(str* gname, str* name)
1231 {
1232         blst_init_ign_masks();
1233 }
1234
1235
1236 #endif /* USE_DST_BLACKLIST */
1237