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