pkg: fix wrong package name, closes FS#148, reported from Andrew Pogrebennyk
[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  * \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()
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 #ifdef USE_IPV6
342         if (e->flags & BLST_IS_IPV6){
343                 ip->af=AF_INET6;
344                 ip->len=16;
345         }else
346 #endif /* USE_IPV6 */
347         {
348                 ip->af=AF_INET;
349                 ip->len=4;
350         }
351         memcpy(ip->u.addr, e->ip, ip->len);
352 }
353
354
355
356 inline static unsigned short dst_blst_hash_no(unsigned char proto,
357                                                                                           struct ip_addr* ip,
358                                                                                           unsigned short port)
359 {
360         str s1;
361         str s2;
362
363         s1.s=(char*)ip->u.addr;
364         s1.len=ip->len;
365         s2.s=(char*)&port;
366         s2.len=sizeof(unsigned short);
367         return get_hash2_raw(&s1, &s2)%DST_BLST_HASH_SIZE;
368 }
369
370
371
372 void destroy_dst_blacklist()
373 {
374         int r;
375         struct dst_blst_entry** crt;
376         struct dst_blst_entry* e;
377
378         if (blst_timer_h){
379                 timer_del(blst_timer_h);
380                 timer_free(blst_timer_h);
381                 blst_timer_h=0;
382         }
383 #ifdef BLST_LOCK_PER_BUCKET
384         if (dst_blst_hash)
385                 for(r=0; r<DST_BLST_HASH_SIZE; r++)
386                         lock_destroy(&dst_blst_hash[r].lock);
387 #elif defined BLST_LOCK_SET
388                 if (blst_lock_set){
389                         lock_set_destroy(blst_lock_set);
390                         lock_set_dealloc(blst_lock_set);
391                         blst_lock_set=0;
392                 }
393 #else
394         if (blst_lock){
395                 lock_destroy(blst_lock);
396                 lock_dealloc(blst_lock);
397                 blst_lock=0;
398         }
399 #endif
400
401         if (dst_blst_hash){
402                 for(r=0; r<DST_BLST_HASH_SIZE; r++){
403                         crt=&dst_blst_hash[r].first;
404                         while(*crt){
405                                 e=*crt;
406                                 *crt=(*crt)->next;
407                                 blst_destroy_entry(e);
408                         }
409                 }
410                 shm_free(dst_blst_hash);
411                 dst_blst_hash=0;
412         }
413         if (blst_mem_used){
414                 shm_free((void*)blst_mem_used);
415                 blst_mem_used=0;
416         }
417 #ifdef DST_BLACKLIST_HOOKS
418         destroy_blacklist_hooks();
419 #endif
420
421 #ifdef USE_DST_BLACKLIST_STATS
422         if (dst_blacklist_stats)
423                 shm_free(dst_blacklist_stats);
424 #endif
425 }
426
427
428
429 int init_dst_blacklist()
430 {
431         int ret;
432 #ifdef BLST_LOCK_PER_BUCKET
433         int r;
434 #endif
435
436         if (dst_blacklist_init==0) {
437                 /* the dst blacklist is turned off */
438                 default_core_cfg.use_dst_blacklist=0;
439                 return 0;
440         }
441
442         ret=-1;
443 #ifdef DST_BLACKLIST_HOOKS
444         if (init_blacklist_hooks()!=0){
445                 ret=E_OUT_OF_MEM;
446                 goto error;
447         }
448 #endif
449         blst_mem_used=shm_malloc(sizeof(*blst_mem_used));
450         if (blst_mem_used==0){
451                 ret=E_OUT_OF_MEM;
452                 goto error;
453         }
454         *blst_mem_used=0;
455         dst_blst_hash=shm_malloc(sizeof(struct dst_blst_lst_head) *
456                                                                                         DST_BLST_HASH_SIZE);
457         if (dst_blst_hash==0){
458                 ret=E_OUT_OF_MEM;
459                 goto error;
460         }
461         memset(dst_blst_hash, 0, sizeof(struct dst_blst_lst_head) *
462                                                                 DST_BLST_HASH_SIZE);
463 #ifdef BLST_LOCK_PER_BUCKET
464         for (r=0; r<DST_BLST_HASH_SIZE; r++){
465                 if (lock_init(&dst_blst_hash[r].lock)==0){
466                         ret=-1;
467                         goto error;
468                 }
469         }
470 #elif defined BLST_LOCK_SET
471         blst_lock_set=lock_set_alloc(DST_BLST_HASH_SIZE);
472         if (blst_lock_set==0){
473                 ret=E_OUT_OF_MEM;
474                 goto error;
475         }
476         if (lock_set_init(blst_lock_set)==0){
477                 lock_set_dealloc(blst_lock_set);
478                 blst_lock_set=0;
479                 ret=-1;
480                 goto error;
481         }
482 #else /* BLST_ONE_LOCK */
483         blst_lock=lock_alloc();
484         if (blst_lock==0){
485                 ret=E_OUT_OF_MEM;
486                 goto error;
487         }
488         if (lock_init(blst_lock)==0){
489                 lock_dealloc(blst_lock);
490                 blst_lock=0;
491                 ret=-1;
492                 goto error;
493         }
494 #endif /* BLST*LOCK*/
495         blst_timer_h=timer_alloc();
496         if (blst_timer_h==0){
497                 ret=E_OUT_OF_MEM;
498                 goto error;
499         }
500         /* fix options */
501         default_core_cfg.blst_max_mem<<=10; /* in Kb */ /* TODO: test with 0 */
502         if (blst_timer_interval){
503                 timer_init(blst_timer_h, blst_timer, 0 ,0); /* slow timer */
504                 if (timer_add(blst_timer_h, S_TO_TICKS(blst_timer_interval))<0){
505                         LOG(L_CRIT, "BUG: init_dst_blacklist: failed to add the timer\n");
506                         timer_free(blst_timer_h);
507                         blst_timer_h=0;
508                         goto error;
509                 }
510         }
511         if (blst_init_ign_masks() < 0){
512                 ret=E_BUG;
513                 goto error;
514         }
515         return 0;
516 error:
517         destroy_dst_blacklist();
518         return ret;
519 }
520
521 #ifdef USE_DST_BLACKLIST_STATS
522 int init_dst_blacklist_stats(int iproc_num)
523 {
524         /* do not initialize the stats array if the dst blacklist will not be used */
525         if (dst_blacklist_init==0) return 0;
526
527         /* if it is already initialized */
528         if (dst_blacklist_stats)
529                 shm_free(dst_blacklist_stats);
530
531         dst_blacklist_stats=shm_malloc(sizeof(*dst_blacklist_stats) * iproc_num);
532         if (dst_blacklist_stats==0){
533                 return E_OUT_OF_MEM;
534         }
535         memset(dst_blacklist_stats, 0, sizeof(*dst_blacklist_stats) * iproc_num);
536
537         return 0;
538 }
539 #endif
540
541 /* must be called with the lock held
542  * struct dst_blst_entry** head, struct dst_blst_entry* e */
543 #define dst_blacklist_lst_add(head, e)\
544 do{ \
545         (e)->next=*(head); \
546         *(head)=(e); \
547 }while(0)
548
549
550
551 /* must be called with the lock held
552  * returns a pointer to the blacklist entry if found, 0 otherwise
553  * it also deletes expired elements (expire<=now) as it searches
554  * proto==PROTO_NONE = wildcard */
555 inline static struct dst_blst_entry* _dst_blacklist_lst_find(
556                                                                                                 unsigned short hash,
557                                                                                                 struct ip_addr* ip,
558                                                                                                 unsigned char proto,
559                                                                                                 unsigned short port,
560                                                                                                 ticks_t now)
561 {
562         struct dst_blst_entry** crt;
563         struct dst_blst_entry** tmp;
564         struct dst_blst_entry* e;
565         struct dst_blst_entry** head;
566         unsigned char type;
567
568         head=&dst_blst_hash[hash].first;
569 #ifdef USE_IPV6
570         type=(ip->af==AF_INET6)*BLST_IS_IPV6;
571 #else  /* USE_IPV6 */
572         if (unlikely(ip->af!=AF_INET)) return 0;
573         type=0;
574 #endif /* USE_IPV6 */
575         for (crt=head, tmp=&(*head)->next; *crt; crt=tmp, tmp=&(*crt)->next){
576                 e=*crt;
577                 prefetch_loc_r((*crt)->next, 1);
578                 /* remove old expired entries */
579                 if ((s_ticks_t)(now-(*crt)->expire)>=0){
580                         *crt=(*crt)->next;
581                         tmp=crt;
582                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
583                         BLST_HASH_STATS_DEC(hash);
584                         blst_destroy_entry(e);
585                 }else if ((e->port==port) && ((e->flags & BLST_IS_IPV6)==type) &&
586                                 ((e->proto==PROTO_NONE) || (proto==PROTO_NONE) ||
587                                         (e->proto==proto)) &&
588                                         (memcmp(ip->u.addr, e->ip, ip->len)==0)){
589                         return e;
590                 }
591         }
592         return 0;
593 }
594
595
596
597 /* must be called with the lock held
598  * returns 1 if a matching entry was deleted, 0 otherwise
599  * it also deletes expired elements (expire<=now) as it searches
600  * proto==PROTO_NONE = wildcard */
601 inline static int _dst_blacklist_del(
602                                                                                                 unsigned short hash,
603                                                                                                 struct ip_addr* ip,
604                                                                                                 unsigned char proto,
605                                                                                                 unsigned short port,
606                                                                                                 ticks_t now)
607 {
608         struct dst_blst_entry** crt;
609         struct dst_blst_entry** tmp;
610         struct dst_blst_entry* e;
611         struct dst_blst_entry** head;
612         unsigned char type;
613         
614         head=&dst_blst_hash[hash].first;
615 #ifdef USE_IPV6
616         type=(ip->af==AF_INET6)*BLST_IS_IPV6;
617 #else  /* USE_IPV6 */
618         if (unlikely(ip->af!=AF_INET)) return 0;
619         type=0;
620 #endif /* USE_IPV6 */
621         for (crt=head, tmp=&(*head)->next; *crt; crt=tmp, tmp=&(*crt)->next){
622                 e=*crt;
623                 prefetch_loc_r((*crt)->next, 1);
624                 /* remove old expired entries */
625                 if ((s_ticks_t)(now-(*crt)->expire)>=0){
626                         *crt=(*crt)->next;
627                         tmp=crt;
628                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
629                         BLST_HASH_STATS_DEC(hash);
630                         blst_destroy_entry(e);
631                 }else if ((e->port==port) && ((e->flags & BLST_IS_IPV6)==type) &&
632                                 ((e->proto==PROTO_NONE) || (proto==PROTO_NONE) ||
633                                         (e->proto==proto)) && 
634                                         (memcmp(ip->u.addr, e->ip, ip->len)==0)){
635                         *crt=(*crt)->next;
636                         tmp=crt;
637                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
638                         BLST_HASH_STATS_DEC(hash);
639                         blst_destroy_entry(e);
640                         return 1;
641                 }
642         }
643         return 0;
644 }
645
646
647
648 /* frees all the expired entries until either there are no more of them
649  *  or the total memory used is <= target (to free all of them use -1 for
650  *  targer)
651  *  params:   target  - free expired entries until no more then taget memory
652  *                      is used  (use 0 to free all of them)
653  *            delta   - consider an entry expired if it expires after delta
654  *                      ticks from now
655  *            timeout - exit after timeout ticks
656  *
657  *  returns: number of deleted entries
658  *  This function should be called periodically from a timer
659  */
660 inline static int dst_blacklist_clean_expired(unsigned int target,
661                                                                           ticks_t delta,
662                                                                           ticks_t timeout)
663 {
664         static unsigned int start=0;
665         unsigned int h;
666         struct dst_blst_entry** crt;
667         struct dst_blst_entry** tmp;
668         struct dst_blst_entry* e;
669         ticks_t start_time;
670         ticks_t now;
671         int no=0;
672         int i;
673
674         now=start_time=get_ticks_raw();
675         for(h=start; h!=(start+DST_BLST_HASH_SIZE); h++){
676                 i=h%DST_BLST_HASH_SIZE;
677                 if (dst_blst_hash[i].first){
678                         LOCK_BLST(i);
679                         for (crt=&dst_blst_hash[i].first, tmp=&(*crt)->next;
680                                         *crt; crt=tmp, tmp=&(*crt)->next){
681                                 e=*crt;
682                                 prefetch_loc_r((*crt)->next, 1);
683                                 if ((s_ticks_t)(now+delta-(*crt)->expire)>=0){
684                                         *crt=(*crt)->next;
685                                         tmp=crt;
686                                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
687                                         blst_destroy_entry(e);
688                                         BLST_HASH_STATS_DEC(i);
689                                         no++;
690                                         if (*blst_mem_used<=target){
691                                                 UNLOCK_BLST(i);
692                                                 goto skip;
693                                         }
694                                 }
695                         }
696                         UNLOCK_BLST(i);
697                         /* check for timeout only "between" hash cells */
698                         now=get_ticks_raw();
699                         if ((now-start_time)>=timeout){
700                                 DBG("_dst_blacklist_clean_expired_unsafe: timeout: %d > %d\n",
701                                                 TICKS_TO_MS(now-start_time), TICKS_TO_MS(timeout));
702                                 goto skip;
703                         }
704                 }
705         }
706 skip:
707         start=h; /* next time we start where we left */
708         if (no){
709                 DBG("dst_blacklist_clean_expired, %d entries removed\n", no);
710         }
711         return no;
712 }
713
714
715
716 /* timer */
717 static ticks_t blst_timer(ticks_t ticks, struct timer_ln* tl, void* data)
718 {
719         dst_blacklist_clean_expired(0, 0, 2); /*spend max. 2 ticks*/
720         return (ticks_t)(-1);
721 }
722
723
724
725 /* adds a proto ip:port combination to the blacklist
726  * returns 0 on success, -1 on error (blacklist full -- would use more then
727  *  blst:_max_mem, or out of shm. mem.)
728  */
729 inline static int dst_blacklist_add_ip(unsigned char err_flags,
730                                                                         unsigned char proto,
731                                                                         struct ip_addr* ip, unsigned short port,
732                                                                         ticks_t timeout)
733 {
734         int size;
735         struct dst_blst_entry* e;
736         unsigned short hash;
737         ticks_t now;
738         int ret;
739
740         ret=0;
741         if (ip->af==AF_INET){
742                 err_flags&=~BLST_IS_IPV6; /* make sure the ipv6 flag is reset */
743                 size=sizeof(struct dst_blst_entry);
744         }else{
745                 err_flags|=BLST_IS_IPV6;
746                 size=sizeof(struct dst_blst_entry)+12 /* ipv6 addr - 4 */;
747         }
748         now=get_ticks_raw();
749         hash=dst_blst_hash_no(proto, ip, port);
750         /* check if the entry already exists */
751         LOCK_BLST(hash);
752                 e=_dst_blacklist_lst_find(hash, ip, proto, port, now);
753                 if (e){
754                         e->flags|=err_flags;
755                         e->expire=now+timeout; /* update the timeout */
756                 }else{
757                         if (unlikely((*blst_mem_used+size) >=
758                                         cfg_get(core, core_cfg, blst_max_mem))){
759 #ifdef USE_DST_BLACKLIST_STATS
760                                 dst_blacklist_stats[process_no].bkl_lru_cnt++;
761 #endif
762                                 UNLOCK_BLST(hash);
763                                 /* first try to free some memory  (~ 12%), but don't
764                                  * spend more then 250 ms*/
765                                 dst_blacklist_clean_expired(*blst_mem_used/16*14, 0,
766                                                                                                                         MS_TO_TICKS(250));
767                                 if (unlikely(*blst_mem_used+size >=
768                                                 cfg_get(core, core_cfg, blst_max_mem))){
769                                         ret=-1;
770                                         goto error;
771                                 }
772                                 LOCK_BLST(hash);
773                         }
774                         e=shm_malloc(size);
775                         if (e==0){
776                                 UNLOCK_BLST(hash);
777                                 ret=E_OUT_OF_MEM;
778                                 goto error;
779                         }
780                         *blst_mem_used+=size;
781                         e->flags=err_flags;
782                         e->proto=proto;
783                         e->port=port;
784                         memcpy(e->ip, ip->u.addr, ip->len);
785                         e->expire=now+timeout; /* update the timeout */
786                         e->next=0;
787                         dst_blacklist_lst_add(&dst_blst_hash[hash].first, e);
788                         BLST_HASH_STATS_INC(hash);
789                 }
790         UNLOCK_BLST(hash);
791 error:
792         return ret;
793 }
794
795
796
797 /* if no blacklisted returns 0, else returns the blacklist flags */
798 inline static int dst_is_blacklisted_ip(unsigned char proto,
799                                                                                 struct ip_addr* ip,
800                                                                                 unsigned short port)
801 {
802         struct dst_blst_entry* e;
803         unsigned short hash;
804         ticks_t now;
805         int ret;
806
807         ret=0;
808         now=get_ticks_raw();
809         hash=dst_blst_hash_no(proto, ip, port);
810         if (unlikely(dst_blst_hash[hash].first)){
811                 LOCK_BLST(hash);
812                         e=_dst_blacklist_lst_find(hash, ip, proto, port, now);
813                         if (e){
814                                 ret=e->flags;
815                         }
816                 UNLOCK_BLST(hash);
817         }
818         return ret;
819 }
820
821
822
823 /** add dst to the blacklist, specifying the timeout.
824  * @param err_flags - reason (bitmap)
825  * @param si - destination (protocol, ip and port)
826  * @param msg - sip message that triggered the blacklisting (can be 0 if 
827  *               not known)
828  * @param timeout - timeout in ticks
829  * @return 0 on success, -1 on error
830  */
831 int dst_blacklist_force_add_to(unsigned char err_flags,  struct dest_info* si,
832                                                                 struct sip_msg* msg, ticks_t timeout)
833 {
834         struct ip_addr ip;
835
836 #ifdef DST_BLACKLIST_HOOKS
837         if (unlikely (blacklist_run_hooks(&blst_add_cb, si, &err_flags, msg) ==
838                                         DST_BLACKLIST_DENY))
839                 return 0;
840 #endif
841         su2ip_addr(&ip, &si->to);
842         return dst_blacklist_add_ip(err_flags, si->proto, &ip,
843                                                                 su_getport(&si->to), timeout);
844 }
845
846
847
848 /** add dst to the blacklist, specifying the timeout.
849  * (like @function dst_blacklist_force_add_to)= above, but uses 
850  * (proto, sockaddr_union) instead of struct dest_info)
851  */
852 int dst_blacklist_force_su_to(unsigned char err_flags, unsigned char proto,
853                                                                 union sockaddr_union* dst,
854                                                                 struct sip_msg* msg, ticks_t timeout)
855 {
856         struct ip_addr ip;
857 #ifdef DST_BLACKLIST_HOOKS
858         struct dest_info si;
859         
860         init_dest_info(&si);
861         si.to=*dst;
862         si.proto=proto;
863         if (unlikely (blacklist_run_hooks(&blst_add_cb, &si, &err_flags, msg) ==
864                                         DST_BLACKLIST_DENY))
865                 return 0;
866 #endif
867         su2ip_addr(&ip, dst);
868         return dst_blacklist_add_ip(err_flags, proto, &ip,
869                                                                 su_getport(dst), timeout);
870 }
871
872
873
874 int dst_is_blacklisted(struct dest_info* si, struct sip_msg* msg)
875 {
876         int ires;
877         struct ip_addr ip;
878 #ifdef DST_BLACKLIST_HOOKS
879         unsigned char err_flags;
880         int action;
881 #endif
882         su2ip_addr(&ip, &si->to);
883
884 #ifdef DST_BLACKLIST_HOOKS
885         err_flags=0;
886         if (unlikely((action=(blacklist_run_hooks(&blst_search_cb, si, &err_flags, msg))
887                                         ) != DST_BLACKLIST_CONTINUE)){
888                 if (action==DST_BLACKLIST_DENY)
889                         return 0;
890                 else  /* if (action==DST_BLACKLIST_ACCEPT) */
891                         return err_flags;
892         }
893 #endif
894         ires=dst_is_blacklisted_ip(si->proto, &ip, su_getport(&si->to));
895 #ifdef USE_DST_BLACKLIST_STATS
896         if (ires)
897                 dst_blacklist_stats[process_no].bkl_hit_cnt++;
898 #endif
899         return ires;
900 }
901
902
903
904 /* returns 1 if the entry was deleted, 0 if not found */
905 int dst_blacklist_del(struct dest_info* si, struct sip_msg* msg)
906 {
907         unsigned short hash;
908         struct ip_addr ip;
909         ticks_t now;
910         int ret;
911         unsigned short port;
912         
913         ret=0;
914         su2ip_addr(&ip, &si->to);
915         port=su_getport(&si->to);
916         now=get_ticks_raw();
917         hash=dst_blst_hash_no(si->proto, &ip, port);
918         if (unlikely(dst_blst_hash[hash].first)){
919                 LOCK_BLST(hash);
920                         ret=_dst_blacklist_del(hash, &ip, si->proto, port, now);
921                 UNLOCK_BLST(hash);
922         }
923         return ret;
924 }
925
926
927
928 /* rpc functions */
929 void dst_blst_mem_info(rpc_t* rpc, void* ctx)
930 {
931         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
932                 rpc->fault(ctx, 500, "dst blacklist support disabled");
933                 return;
934         }
935         rpc->add(ctx, "dd",  *blst_mem_used, cfg_get(core, core_cfg, blst_max_mem));
936 }
937
938
939
940 static char* get_proto_name(unsigned char proto)
941 {
942         switch(proto){
943                 case PROTO_NONE:
944                         return "*";
945                 case PROTO_UDP:
946                         return "udp";
947                 case PROTO_TCP:
948                         return "tcp";
949                 case PROTO_TLS:
950                         return "tls";
951                 case PROTO_SCTP:
952                         return "sctp";
953                 default:
954                         return "unknown";
955         }
956 }
957
958
959 #ifdef USE_DST_BLACKLIST_STATS
960
961 static unsigned long  stat_sum(int ivar, int breset) {
962         unsigned long isum=0;
963         int i1=0;
964
965         for (; i1 < get_max_procs(); i1++)
966                 switch (ivar) {
967                         case 0:
968                                 isum+=dst_blacklist_stats[i1].bkl_hit_cnt;
969                                 if (breset)
970                                         dst_blacklist_stats[i1].bkl_hit_cnt=0;
971                                 break;
972                         case 1:
973                                 isum+=dst_blacklist_stats[i1].bkl_lru_cnt;
974                                 if (breset)
975                                         dst_blacklist_stats[i1].bkl_lru_cnt=0;
976                                 break;
977                 }
978
979                 return isum;
980 }
981
982
983 void dst_blst_stats_get(rpc_t* rpc, void* c)
984 {
985         char *name=NULL;
986         void *handle;
987         int found=0,i=0;
988         int reset=0;
989         char* dst_blacklist_stats_names[] = {
990                 "bkl_hit_cnt",
991                 "bkl_lru_cnt",
992                 NULL
993         };
994         
995         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
996                 rpc->fault(c, 500, "dst blacklist support disabled");
997                 return;
998         }
999         if (rpc->scan(c, "s", &name) < 0)
1000                 return;
1001         if (rpc->scan(c, "d", &reset) < 0)
1002                 return;
1003         if (!strcasecmp(name, DST_BLACKLIST_ALL_STATS)) {
1004                 /* dump all the dns cache stat values */
1005                 rpc->add(c, "{", &handle);
1006                 for (i=0; dst_blacklist_stats_names[i]; i++)
1007                         rpc->struct_add(handle, "d",
1008                                                         dst_blacklist_stats_names[i],
1009                                                         stat_sum(i, reset));
1010
1011                 found=1;
1012         } else {
1013                 for (i=0; dst_blacklist_stats_names[i]; i++)
1014                         if (!strcasecmp(dst_blacklist_stats_names[i], name)) {
1015                         rpc->add(c, "{", &handle);
1016                         rpc->struct_add(handle, "d",
1017                                                         dst_blacklist_stats_names[i],
1018                                                         stat_sum(i, reset));
1019                         found=1;
1020                         break;
1021                         }
1022         }
1023         if(!found)
1024                 rpc->fault(c, 500, "unknown dst blacklist stat parameter");
1025
1026         return;
1027 }
1028 #endif /* USE_DST_BLACKLIST_STATS */
1029
1030 /* only for debugging, it helds the lock too long for "production" use */
1031 void dst_blst_debug(rpc_t* rpc, void* ctx)
1032 {
1033         int h;
1034         struct dst_blst_entry* e;
1035         ticks_t now;
1036         struct ip_addr ip;
1037
1038         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
1039                 rpc->fault(ctx, 500, "dst blacklist support disabled");
1040                 return;
1041         }
1042         now=get_ticks_raw();
1043                 for(h=0; h<DST_BLST_HASH_SIZE; h++){
1044                         LOCK_BLST(h);
1045                         for(e=dst_blst_hash[h].first; e; e=e->next){
1046                                 dst_blst_entry2ip(&ip, e);
1047                                 rpc->add(ctx, "ssddd", get_proto_name(e->proto),
1048                                                                                 ip_addr2a(&ip), e->port,
1049                                                                                 (s_ticks_t)(now-e->expire)<=0?
1050                                                                                 TICKS_TO_S(e->expire-now):
1051                                                                                 -TICKS_TO_S(now-e->expire) ,
1052                                                                                 e->flags);
1053                         }
1054                         UNLOCK_BLST(h);
1055                 }
1056 }
1057
1058 /* only for debugging, it helds the lock too long for "production" use */
1059 void dst_blst_hash_stats(rpc_t* rpc, void* ctx)
1060 {
1061         int h;
1062         struct dst_blst_entry* e;
1063 #ifdef BLST_HASH_STATS
1064         int n;
1065
1066         n=0;
1067 #endif
1068         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
1069                 rpc->fault(ctx, 500, "dst blacklist support disabled");
1070                 return;
1071         }
1072                 for(h=0; h<DST_BLST_HASH_SIZE; h++){
1073 #ifdef BLST_HASH_STATS
1074                         LOCK_BLST(h);
1075                         for(e=dst_blst_hash[h].first; e; e=e->next) n++;
1076                         UNLOCK_BLST(h);
1077                         rpc->add(ctx, "dd", h, n);
1078 #else
1079                         rpc->add(ctx, "dd", h, dst_blst_hash[h].entries);
1080 #endif
1081                 }
1082 }
1083
1084 /* dumps the content of the blacklist in a human-readable format */
1085 void dst_blst_view(rpc_t* rpc, void* ctx)
1086 {
1087         int h;
1088         int expires;
1089         struct dst_blst_entry* e;
1090         ticks_t now;
1091         struct ip_addr ip;
1092
1093         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
1094                 rpc->fault(ctx, 500, "dst blacklist support disabled");
1095                 return;
1096         }
1097         now=get_ticks_raw();
1098         for(h=0; h<DST_BLST_HASH_SIZE; h++) {
1099                 LOCK_BLST(h);
1100                 for(e=dst_blst_hash[h].first; e; e=e->next) {
1101                         expires = (s_ticks_t)(now-e->expire)<=0?
1102                                    TICKS_TO_S(e->expire-now): -TICKS_TO_S(now-e->expire);
1103                         /* don't include expired entries into view report */
1104                         if (expires < 0) {
1105                                 continue;
1106                         }
1107                         dst_blst_entry2ip(&ip, e);
1108                         rpc->printf(ctx, "{\n    protocol: %s", get_proto_name(e->proto));
1109                         rpc->printf(ctx, "    ip: %s", ip_addr2a(&ip));
1110                         rpc->printf(ctx, "    port: %d", e->port);
1111                         rpc->printf(ctx, "    expires in (s): %d", expires); 
1112                         rpc->printf(ctx, "    flags: %d\n}", e->flags);
1113                 }
1114                 UNLOCK_BLST(h);
1115         }
1116 }
1117
1118
1119 /* deletes all the entries from the blacklist except the permanent ones
1120  * (which are marked with BLST_PERMANENT)
1121  */
1122 void dst_blst_flush(void)
1123 {
1124         int h;
1125         struct dst_blst_entry* e;
1126         struct dst_blst_entry** crt;
1127         struct dst_blst_entry** tmp;
1128
1129         for(h=0; h<DST_BLST_HASH_SIZE; h++){
1130                 LOCK_BLST(h);
1131                 for (crt=&dst_blst_hash[h].first, tmp=&(*crt)->next;
1132                                 *crt; crt=tmp, tmp=&(*crt)->next){
1133                         e=*crt;
1134                         prefetch_loc_r((*crt)->next, 1);
1135                         if (!(e->flags &  BLST_PERMANENT)){
1136                                 *crt=(*crt)->next;
1137                                 tmp=crt;
1138                                 *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
1139                                 blst_destroy_entry(e);
1140                                 BLST_HASH_STATS_DEC(h);
1141                         }
1142                 }
1143                 UNLOCK_BLST(h);
1144         }
1145 }
1146
1147 /* rpc wrapper function for dst_blst_flush() */
1148 void dst_blst_delete_all(rpc_t* rpc, void* ctx)
1149 {
1150         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
1151                 rpc->fault(ctx, 500, "dst blacklist support disabled");
1152                 return;
1153         }
1154         dst_blst_flush();
1155 }
1156
1157 /* Adds a new entry to the blacklist */
1158 void dst_blst_add(rpc_t* rpc, void* ctx)
1159 {
1160         str ip;
1161         int port, proto, flags;
1162         unsigned char err_flags;
1163         struct ip_addr *ip_addr;
1164
1165         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
1166                 rpc->fault(ctx, 500, "dst blacklist support disabled");
1167                 return;
1168         }
1169         if (rpc->scan(ctx, "Sddd", &ip, &port, &proto, &flags) < 4)
1170                 return;
1171
1172         err_flags = (unsigned char)flags;
1173         /* sanity checks */
1174         if ((unsigned char)proto > PROTO_SCTP) {
1175                 rpc->fault(ctx, 400, "Unknown protocol");
1176                 return;
1177         }
1178
1179         if (err_flags & BLST_IS_IPV6) {
1180 #ifdef USE_IPV6
1181                 /* IPv6 address is specified */
1182                 ip_addr = str2ip6(&ip);
1183 #else  /* USE_IPV6 */
1184                 rpc->fault(ctx, 400, "IPv6 support disabled");
1185                 return;
1186 #endif /* USE_IPV6 */
1187         } else {
1188                 /* try IPv4 first, than IPv6 */
1189                 ip_addr = str2ip(&ip);
1190                 if (!ip_addr) {
1191 #ifdef USE_IPV6
1192                         ip_addr = str2ip6(&ip);
1193                         err_flags |= BLST_IS_IPV6;
1194 #else  /* USE_IPV6 */
1195                         rpc->fault(ctx, 400, "Malformed or IPv6 ip address");
1196                         return;
1197 #endif /* USE_IPV6 */
1198                 }
1199         }
1200         if (!ip_addr) {
1201                 rpc->fault(ctx, 400, "Malformed ip address");
1202                 return;
1203         }
1204
1205         if (dst_blacklist_add_ip(err_flags, proto, ip_addr, port, 
1206                                     S_TO_TICKS(cfg_get(core, core_cfg, blst_timeout))))
1207                 rpc->fault(ctx, 400, "Failed to add the entry to the blacklist");
1208 }
1209
1210 /* fixup function for use_dst_blacklist
1211  * verifies that dst_blacklist_init is set to 1
1212  */
1213 int use_dst_blacklist_fixup(void *handle, str *gname, str *name, void **val)
1214 {
1215         if ((int)(long)(*val) && !dst_blacklist_init) {
1216                 LOG(L_ERR, "ERROR: use_dst_blacklist_fixup(): "
1217                         "dst blacklist is turned off by dst_blacklist_init=0, "
1218                         "it cannot be enabled runtime.\n");
1219                 return -1;
1220         }
1221         return 0;
1222 }
1223
1224 /* KByte to Byte conversion */
1225 int blst_max_mem_fixup(void *handle, str *gname, str *name, void **val)
1226 {
1227         unsigned int    u;
1228
1229         u = ((unsigned int)(long)(*val))<<10;
1230         (*val) = (void *)(long)u;
1231         return 0;
1232 }
1233
1234
1235
1236 /** re-inint per child blst_proto_ign_mask array. */
1237 void blst_reinit_ign_masks(str* gname, str* name)
1238 {
1239         blst_init_ign_masks();
1240 }
1241
1242
1243 #endif /* USE_DST_BLACKLIST */
1244