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