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