• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Data Structures
  • Files
  • Directories
  • File List
  • Globals

dst_blacklist.c

Go to the documentation of this file.
00001 /*
00002  * $Id$
00003  *
00004  * resolver related functions
00005  *
00006  * Copyright (C) 2006 iptelorg GmbH
00007  *
00008  * This file is part of ser, a free SIP server.
00009  *
00010  * ser is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version
00014  *
00015  * For a license to use the ser software under conditions
00016  * other than those described here, or to purchase support for this
00017  * software, please contact iptel.org by e-mail at the following addresses:
00018  *    info@iptel.org
00019  *
00020  * ser is distributed in the hope that it will be useful,
00021  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00022  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023  * GNU General Public License for more details.
00024  *
00025  * You should have received a copy of the GNU General Public License
00026  * along with this program; if not, write to the Free Software
00027  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00028  */
00029 /* History:
00030  * --------
00031  *  2006-07-29  created by andrei
00032  *  2007-05-39  added hooks for add; more locks to reduce contention (andrei)
00033  *  2007-06-26  added hooks for search (andrei)
00034  *  2007-07-30  added dst_blacklist_del() and dst_blacklist_add_to()  (andrei)
00035  *  2007-07-30  dst blacklist measurements added (Gergo)
00036  *  2008-02-11  dns_blacklist_init cfg parameter is introduced (Miklos)
00037  *  2009-02-26  added dst_blacklist_su* variant (andrei)
00038  */
00039 
00048 #ifdef USE_DST_BLACKLIST
00049 
00050 #include "dst_blacklist.h"
00051 #include "globals.h"
00052 #include "cfg_core.h"
00053 #include "mem/shm_mem.h"
00054 #include "hashes.h"
00055 #include "locking.h"
00056 #include "timer.h"
00057 #include "timer_ticks.h"
00058 #include "ip_addr.h"
00059 #include "error.h"
00060 #include "rpc.h"
00061 #include "compiler_opt.h"
00062 #include "resolve.h" /* for str2ip */
00063 #ifdef USE_DST_BLACKLIST_STATS
00064 #include "pt.h"
00065 #endif
00066 
00067 
00068 
00069 
00070 struct dst_blst_entry{
00071         struct dst_blst_entry* next;
00072         ticks_t expire;
00073         unsigned short port;
00074         unsigned char proto;
00075         unsigned char flags; /* contains the address type + error flags */
00076         unsigned char ip[4]; /* 4 for ipv4, 16 for ipv6 */
00077 };
00078 
00079 #define DST_BLST_ENTRY_SIZE(b) \
00080                 (sizeof(struct dst_blst_entry)+((b).flags&BLST_IS_IPV6)*12)
00081 
00082 
00083 #define DST_BLST_HASH_SIZE              1024
00084 #define DEFAULT_BLST_TIMER_INTERVAL             60 /* 1 min */
00085 
00086 
00087 /* lock method */
00088 #ifdef GEN_LOCK_T_UNLIMITED
00089 #define BLST_LOCK_PER_BUCKET
00090 #elif defined GEN_LOCK_SET_T_UNLIMITED
00091 #define BLST_LOCK_SET
00092 #else
00093 #define BLST_ONE_LOCK
00094 #endif
00095 
00096 
00097 #ifdef BLST_LOCK_PER_BUCKET
00098 /* lock included in the hash bucket */
00099 #define LOCK_BLST(h)            lock_get(&dst_blst_hash[(h)].lock)
00100 #define UNLOCK_BLST(h)          lock_release(&dst_blst_hash[(h)].lock)
00101 #elif defined BLST_LOCK_SET
00102 static gen_lock_set_t* blst_lock_set=0;
00103 #define LOCK_BLST(h)            lock_set_get(blst_lock_set, (h))
00104 #define UNLOCK_BLST(h)          lock_set_release(blst_lock_set, (h))
00105 #else
00106 /* use only one lock */
00107 static gen_lock_t* blst_lock=0;
00108 #define LOCK_BLST(h)            lock_get(blst_lock)
00109 #define UNLOCK_BLST(h)          lock_release(blst_lock)
00110 #endif
00111 
00112 
00113 
00114 
00115 #define BLST_HASH_STATS
00116 
00117 #ifdef BLST_HASH_STATS
00118 #define BLST_HASH_STATS_DEC(h) dst_blst_hash[(h)].entries--
00119 #define BLST_HASH_STATS_INC(h) dst_blst_hash[(h)].entries++
00120 #else
00121 #define BLST_HASH_STATS_DEC(h) do{}while(0)
00122 #define BLST_HASH_STATS_INC(h) do{}while(0)
00123 #endif
00124 
00125 struct dst_blst_lst_head{
00126         struct dst_blst_entry* first;
00127 #ifdef BLST_LOCK_PER_BUCKET
00128         gen_lock_t      lock;
00129 #endif
00130 #ifdef BLST_HASH_STATS
00131         unsigned int entries;
00132 #endif
00133 };
00134 
00135 int dst_blacklist_init=1; /* if 0, the dst blacklist is not initialized at startup */
00136 static struct timer_ln* blst_timer_h=0;
00137 
00138 static volatile unsigned int* blst_mem_used=0;
00139 unsigned int blst_timer_interval=DEFAULT_BLST_TIMER_INTERVAL;
00140 struct dst_blst_lst_head* dst_blst_hash=0;
00141 
00142 #ifdef USE_DST_BLACKLIST_STATS
00143 struct t_dst_blacklist_stats* dst_blacklist_stats=0;
00144 #endif
00145 
00146 /* blacklist per protocol event ignore mask array */
00147 unsigned blst_proto_imask[PROTO_LAST+1];
00148 
00149 #ifdef DST_BLACKLIST_HOOKS
00150 
00151 /* there 2 types of callbacks supported: on add new entry to the blacklist
00152  *  (DST_BLACKLIST_ADD_CB) and on blacklist search (DST_BLACKLIST_SEARCH_CB).
00153  *  Both of them take a struct dest_info*, a flags pointer(unsigned char*),
00154  *  and a struct sip_msg* as parameters. The flags can be changed.
00155  *  A callback should return one of:
00156  *    DST_BLACKLIST_CONTINUE - do nothing, let other callbacks run
00157  *    DST_BLACKLIST_ACCEPT   - for blacklist add: force accept immediately,
00158  *                             for blacklist search: force match and use
00159  *                              the flags as the blacklist search return.
00160  *                              ( so the flags should be set to some valid
00161  *                                non zero BLST flags value )
00162  *   DST_BLACKLIST_DENY      - for blacklist add: don't allow adding the
00163  *                              destination to the blacklist.
00164  *                             for blacklist search: force return not found
00165  */
00166 
00167 #define MAX_BLST_HOOKS 1
00168 
00169 struct blst_callbacks_lst{
00170         struct blacklist_hook* hooks;
00171         unsigned int max_hooks;
00172         int last_idx;
00173 };
00174 
00175 static struct blst_callbacks_lst blst_add_cb;
00176 static struct blst_callbacks_lst blst_search_cb;
00177 
00178 static int init_blst_callback_lst(struct blst_callbacks_lst*  cb_lst, int max)
00179 {
00180 
00181         cb_lst->max_hooks=MAX_BLST_HOOKS;
00182         cb_lst->last_idx=0;
00183         cb_lst->hooks=pkg_malloc(cb_lst->max_hooks*sizeof(struct blacklist_hook));
00184         if (cb_lst->hooks==0)
00185                 goto error;
00186         memset(cb_lst->hooks, 0, cb_lst->max_hooks*sizeof(struct blacklist_hook));
00187         return 0;
00188 error:
00189         return -1;
00190 }
00191 
00192 
00193 static void destroy_blst_callback_lst(struct blst_callbacks_lst* cb_lst)
00194 {
00195         int r;
00196         if (cb_lst && cb_lst->hooks){
00197                 for (r=0; r<cb_lst->last_idx; r++){
00198                         if (cb_lst->hooks[r].destroy)
00199                                 cb_lst->hooks[r].destroy();
00200                 }
00201                 pkg_free(cb_lst->hooks);
00202                 cb_lst->hooks=0;
00203                 cb_lst->last_idx=0;
00204                 cb_lst->max_hooks=0;
00205         }
00206 }
00207 
00208 
00209 static void destroy_blacklist_hooks()
00210 {
00211         destroy_blst_callback_lst(&blst_add_cb);
00212         destroy_blst_callback_lst(&blst_search_cb);
00213 }
00214 
00215 
00216 static int init_blacklist_hooks()
00217 {
00218 
00219         if (init_blst_callback_lst(&blst_add_cb, MAX_BLST_HOOKS)!=0)
00220                 goto error;
00221         if (init_blst_callback_lst(&blst_search_cb, MAX_BLST_HOOKS)!=0)
00222                 goto error;
00223         return 0;
00224 error:
00225         LOG(L_ERR, "blacklist_hooks: failure initializing internal lists\n");
00226         destroy_blacklist_hooks();
00227         return -1;
00228 }
00229 
00230 
00231 
00232 
00233 /* allocates a new hook
00234  * returns 0 on success and -1 on error
00235  * must be called from mod init (from the main process, before forking)*/
00236 int register_blacklist_hook(struct blacklist_hook *h, int type)
00237 {
00238         struct blst_callbacks_lst* cb_lst;
00239         struct blacklist_hook* tmp;
00240         int new_max_hooks;
00241 
00242         if (dst_blacklist_init==0) {
00243                 LOG(L_ERR, "register_blacklist_hook: blacklist is turned off, "
00244                         "the hook cannot be registered\n");
00245                 goto error;
00246         }
00247 
00248         switch(type){
00249                 case DST_BLACKLIST_ADD_CB:
00250                         cb_lst=&blst_add_cb;
00251                         break;
00252                 case DST_BLACKLIST_SEARCH_CB:
00253                         cb_lst=&blst_search_cb;
00254                         break;
00255                 default:
00256                         BUG("register_blacklist_hook: invalid type %d\n", type);
00257                         goto error;
00258         }
00259         if (cb_lst==0 || cb_lst->hooks==0 || cb_lst->max_hooks==0){
00260                 BUG("register_blacklist_hook: intialization error\n");
00261                 goto error;
00262         }
00263 
00264         if (cb_lst->last_idx >= cb_lst->max_hooks){
00265                 new_max_hooks=2*cb_lst->max_hooks;
00266                 tmp=pkg_realloc(cb_lst->hooks,
00267                                 new_max_hooks*sizeof(struct blacklist_hook));
00268                 if (tmp==0){
00269                         goto error;
00270                 }
00271                 cb_lst->hooks=tmp;
00272                 /* init the new chunk (but not the current entry which is
00273                  * overwritten anyway) */
00274                 memset(&cb_lst->hooks[cb_lst->max_hooks+1], 0,
00275                                         (new_max_hooks-cb_lst->max_hooks-1)*
00276                                                 sizeof(struct blacklist_hook));
00277                 cb_lst->max_hooks=new_max_hooks;
00278         }
00279         cb_lst->hooks[cb_lst->last_idx]=*h;
00280         cb_lst->last_idx++;
00281         return 0;
00282 error:
00283         return -1;
00284 }
00285 
00286 
00287 inline static int blacklist_run_hooks(struct blst_callbacks_lst *cb_lst,
00288                                                         struct dest_info* si, unsigned char* flags,
00289                                                         struct sip_msg* msg)
00290 {
00291         int r;
00292         int ret;
00293 
00294         ret=DST_BLACKLIST_CONTINUE; /* default, if no hook installed accept
00295                                                                 blacklist operation */
00296         if (likely(cb_lst->last_idx==0))
00297                 return ret;
00298         for (r=0; r<cb_lst->last_idx; r++){
00299                 ret=cb_lst->hooks[r].on_blst_action(si, flags, msg);
00300                 if (ret!=DST_BLACKLIST_CONTINUE) break;
00301         }
00302         return ret;
00303 }
00304 
00305 
00306 #endif /* DST_BLACKLIST_HOOKS */
00307 
00308 
00312 int blst_init_ign_masks(void)
00313 {
00314         if ((PROTO_UDP > PROTO_LAST) || (PROTO_TCP > PROTO_LAST) ||
00315                 (PROTO_TLS > PROTO_LAST) || (PROTO_SCTP > PROTO_LAST)){
00316                 BUG("protocol array too small\n");
00317                 return -1;
00318         }
00319         blst_proto_imask[PROTO_UDP]=cfg_get(core, core_cfg, blst_udp_imask);
00320         blst_proto_imask[PROTO_TCP]=cfg_get(core, core_cfg, blst_tcp_imask);
00321         blst_proto_imask[PROTO_TLS]=cfg_get(core, core_cfg, blst_tls_imask);
00322         blst_proto_imask[PROTO_SCTP]=cfg_get(core, core_cfg, blst_sctp_imask);
00323         blst_proto_imask[PROTO_NONE]=blst_proto_imask[PROTO_UDP];
00324         return 0;
00325 }
00326 
00327 
00328 
00329 inline static void blst_destroy_entry(struct dst_blst_entry* e)
00330 {
00331         shm_free(e);
00332 }
00333 
00334 
00335 static ticks_t blst_timer(ticks_t ticks, struct timer_ln* tl, void* data);
00336 
00337 
00338 inline static void dst_blst_entry2ip(struct ip_addr* ip,
00339                                                                                 struct dst_blst_entry* e)
00340 {
00341 #ifdef USE_IPV6
00342         if (e->flags & BLST_IS_IPV6){
00343                 ip->af=AF_INET6;
00344                 ip->len=16;
00345         }else
00346 #endif /* USE_IPV6 */
00347         {
00348                 ip->af=AF_INET;
00349                 ip->len=4;
00350         }
00351         memcpy(ip->u.addr, e->ip, ip->len);
00352 }
00353 
00354 
00355 
00356 inline static unsigned short dst_blst_hash_no(unsigned char proto,
00357                                                                                           struct ip_addr* ip,
00358                                                                                           unsigned short port)
00359 {
00360         str s1;
00361         str s2;
00362 
00363         s1.s=(char*)ip->u.addr;
00364         s1.len=ip->len;
00365         s2.s=(char*)&port;
00366         s2.len=sizeof(unsigned short);
00367         return get_hash2_raw(&s1, &s2)%DST_BLST_HASH_SIZE;
00368 }
00369 
00370 
00371 
00372 void destroy_dst_blacklist()
00373 {
00374         int r;
00375         struct dst_blst_entry** crt;
00376         struct dst_blst_entry* e;
00377 
00378         if (blst_timer_h){
00379                 timer_del(blst_timer_h);
00380                 timer_free(blst_timer_h);
00381                 blst_timer_h=0;
00382         }
00383 #ifdef BLST_LOCK_PER_BUCKET
00384         if (dst_blst_hash)
00385                 for(r=0; r<DST_BLST_HASH_SIZE; r++)
00386                         lock_destroy(&dst_blst_hash[r].lock);
00387 #elif defined BLST_LOCK_SET
00388                 if (blst_lock_set){
00389                         lock_set_destroy(blst_lock_set);
00390                         lock_set_dealloc(blst_lock_set);
00391                         blst_lock_set=0;
00392                 }
00393 #else
00394         if (blst_lock){
00395                 lock_destroy(blst_lock);
00396                 lock_dealloc(blst_lock);
00397                 blst_lock=0;
00398         }
00399 #endif
00400 
00401         if (dst_blst_hash){
00402                 for(r=0; r<DST_BLST_HASH_SIZE; r++){
00403                         crt=&dst_blst_hash[r].first;
00404                         while(*crt){
00405                                 e=*crt;
00406                                 *crt=(*crt)->next;
00407                                 blst_destroy_entry(e);
00408                         }
00409                 }
00410                 shm_free(dst_blst_hash);
00411                 dst_blst_hash=0;
00412         }
00413         if (blst_mem_used){
00414                 shm_free((void*)blst_mem_used);
00415                 blst_mem_used=0;
00416         }
00417 #ifdef DST_BLACKLIST_HOOKS
00418         destroy_blacklist_hooks();
00419 #endif
00420 
00421 #ifdef USE_DST_BLACKLIST_STATS
00422         if (dst_blacklist_stats)
00423                 shm_free(dst_blacklist_stats);
00424 #endif
00425 }
00426 
00427 
00428 
00429 int init_dst_blacklist()
00430 {
00431         int ret;
00432 #ifdef BLST_LOCK_PER_BUCKET
00433         int r;
00434 #endif
00435 
00436         if (dst_blacklist_init==0) {
00437                 /* the dst blacklist is turned off */
00438                 default_core_cfg.use_dst_blacklist=0;
00439                 return 0;
00440         }
00441 
00442         ret=-1;
00443 #ifdef DST_BLACKLIST_HOOKS
00444         if (init_blacklist_hooks()!=0){
00445                 ret=E_OUT_OF_MEM;
00446                 goto error;
00447         }
00448 #endif
00449         blst_mem_used=shm_malloc(sizeof(*blst_mem_used));
00450         if (blst_mem_used==0){
00451                 ret=E_OUT_OF_MEM;
00452                 goto error;
00453         }
00454         *blst_mem_used=0;
00455         dst_blst_hash=shm_malloc(sizeof(struct dst_blst_lst_head) *
00456                                                                                         DST_BLST_HASH_SIZE);
00457         if (dst_blst_hash==0){
00458                 ret=E_OUT_OF_MEM;
00459                 goto error;
00460         }
00461         memset(dst_blst_hash, 0, sizeof(struct dst_blst_lst_head) *
00462                                                                 DST_BLST_HASH_SIZE);
00463 #ifdef BLST_LOCK_PER_BUCKET
00464         for (r=0; r<DST_BLST_HASH_SIZE; r++){
00465                 if (lock_init(&dst_blst_hash[r].lock)==0){
00466                         ret=-1;
00467                         goto error;
00468                 }
00469         }
00470 #elif defined BLST_LOCK_SET
00471         blst_lock_set=lock_set_alloc(DST_BLST_HASH_SIZE);
00472         if (blst_lock_set==0){
00473                 ret=E_OUT_OF_MEM;
00474                 goto error;
00475         }
00476         if (lock_set_init(blst_lock_set)==0){
00477                 lock_set_dealloc(blst_lock_set);
00478                 blst_lock_set=0;
00479                 ret=-1;
00480                 goto error;
00481         }
00482 #else /* BLST_ONE_LOCK */
00483         blst_lock=lock_alloc();
00484         if (blst_lock==0){
00485                 ret=E_OUT_OF_MEM;
00486                 goto error;
00487         }
00488         if (lock_init(blst_lock)==0){
00489                 lock_dealloc(blst_lock);
00490                 blst_lock=0;
00491                 ret=-1;
00492                 goto error;
00493         }
00494 #endif /* BLST*LOCK*/
00495         blst_timer_h=timer_alloc();
00496         if (blst_timer_h==0){
00497                 ret=E_OUT_OF_MEM;
00498                 goto error;
00499         }
00500         /* fix options */
00501         default_core_cfg.blst_max_mem<<=10; /* in Kb */ /* TODO: test with 0 */
00502         if (blst_timer_interval){
00503                 timer_init(blst_timer_h, blst_timer, 0 ,0); /* slow timer */
00504                 if (timer_add(blst_timer_h, S_TO_TICKS(blst_timer_interval))<0){
00505                         LOG(L_CRIT, "BUG: init_dst_blacklist: failed to add the timer\n");
00506                         timer_free(blst_timer_h);
00507                         blst_timer_h=0;
00508                         goto error;
00509                 }
00510         }
00511         if (blst_init_ign_masks() < 0){
00512                 ret=E_BUG;
00513                 goto error;
00514         }
00515         return 0;
00516 error:
00517         destroy_dst_blacklist();
00518         return ret;
00519 }
00520 
00521 #ifdef USE_DST_BLACKLIST_STATS
00522 int init_dst_blacklist_stats(int iproc_num)
00523 {
00524         /* do not initialize the stats array if the dst blacklist will not be used */
00525         if (dst_blacklist_init==0) return 0;
00526 
00527         /* if it is already initialized */
00528         if (dst_blacklist_stats)
00529                 shm_free(dst_blacklist_stats);
00530 
00531         dst_blacklist_stats=shm_malloc(sizeof(*dst_blacklist_stats) * iproc_num);
00532         if (dst_blacklist_stats==0){
00533                 return E_OUT_OF_MEM;
00534         }
00535         memset(dst_blacklist_stats, 0, sizeof(*dst_blacklist_stats) * iproc_num);
00536 
00537         return 0;
00538 }
00539 #endif
00540 
00541 /* must be called with the lock held
00542  * struct dst_blst_entry** head, struct dst_blst_entry* e */
00543 #define dst_blacklist_lst_add(head, e)\
00544 do{ \
00545         (e)->next=*(head); \
00546         *(head)=(e); \
00547 }while(0)
00548 
00549 
00550 
00551 /* must be called with the lock held
00552  * returns a pointer to the blacklist entry if found, 0 otherwise
00553  * it also deletes expired elements (expire<=now) as it searches
00554  * proto==PROTO_NONE = wildcard */
00555 inline static struct dst_blst_entry* _dst_blacklist_lst_find(
00556                                                                                                 unsigned short hash,
00557                                                                                                 struct ip_addr* ip,
00558                                                                                                 unsigned char proto,
00559                                                                                                 unsigned short port,
00560                                                                                                 ticks_t now)
00561 {
00562         struct dst_blst_entry** crt;
00563         struct dst_blst_entry** tmp;
00564         struct dst_blst_entry* e;
00565         struct dst_blst_entry** head;
00566         unsigned char type;
00567 
00568         head=&dst_blst_hash[hash].first;
00569 #ifdef USE_IPV6
00570         type=(ip->af==AF_INET6)*BLST_IS_IPV6;
00571 #else  /* USE_IPV6 */
00572         if (unlikely(ip->af!=AF_INET)) return 0;
00573         type=0;
00574 #endif /* USE_IPV6 */
00575         for (crt=head, tmp=&(*head)->next; *crt; crt=tmp, tmp=&(*crt)->next){
00576                 e=*crt;
00577                 prefetch_loc_r((*crt)->next, 1);
00578                 /* remove old expired entries */
00579                 if ((s_ticks_t)(now-(*crt)->expire)>=0){
00580                         *crt=(*crt)->next;
00581                         tmp=crt;
00582                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
00583                         BLST_HASH_STATS_DEC(hash);
00584                         blst_destroy_entry(e);
00585                 }else if ((e->port==port) && ((e->flags & BLST_IS_IPV6)==type) &&
00586                                 ((e->proto==PROTO_NONE) || (proto==PROTO_NONE) ||
00587                                         (e->proto==proto)) &&
00588                                         (memcmp(ip->u.addr, e->ip, ip->len)==0)){
00589                         return e;
00590                 }
00591         }
00592         return 0;
00593 }
00594 
00595 
00596 
00597 /* must be called with the lock held
00598  * returns 1 if a matching entry was deleted, 0 otherwise
00599  * it also deletes expired elements (expire<=now) as it searches
00600  * proto==PROTO_NONE = wildcard */
00601 inline static int _dst_blacklist_del(
00602                                                                                                 unsigned short hash,
00603                                                                                                 struct ip_addr* ip,
00604                                                                                                 unsigned char proto,
00605                                                                                                 unsigned short port,
00606                                                                                                 ticks_t now)
00607 {
00608         struct dst_blst_entry** crt;
00609         struct dst_blst_entry** tmp;
00610         struct dst_blst_entry* e;
00611         struct dst_blst_entry** head;
00612         unsigned char type;
00613         
00614         head=&dst_blst_hash[hash].first;
00615 #ifdef USE_IPV6
00616         type=(ip->af==AF_INET6)*BLST_IS_IPV6;
00617 #else  /* USE_IPV6 */
00618         if (unlikely(ip->af!=AF_INET)) return 0;
00619         type=0;
00620 #endif /* USE_IPV6 */
00621         for (crt=head, tmp=&(*head)->next; *crt; crt=tmp, tmp=&(*crt)->next){
00622                 e=*crt;
00623                 prefetch_loc_r((*crt)->next, 1);
00624                 /* remove old expired entries */
00625                 if ((s_ticks_t)(now-(*crt)->expire)>=0){
00626                         *crt=(*crt)->next;
00627                         tmp=crt;
00628                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
00629                         BLST_HASH_STATS_DEC(hash);
00630                         blst_destroy_entry(e);
00631                 }else if ((e->port==port) && ((e->flags & BLST_IS_IPV6)==type) &&
00632                                 ((e->proto==PROTO_NONE) || (proto==PROTO_NONE) ||
00633                                         (e->proto==proto)) && 
00634                                         (memcmp(ip->u.addr, e->ip, ip->len)==0)){
00635                         *crt=(*crt)->next;
00636                         tmp=crt;
00637                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
00638                         BLST_HASH_STATS_DEC(hash);
00639                         blst_destroy_entry(e);
00640                         return 1;
00641                 }
00642         }
00643         return 0;
00644 }
00645 
00646 
00647 
00648 /* frees all the expired entries until either there are no more of them
00649  *  or the total memory used is <= target (to free all of them use -1 for
00650  *  targer)
00651  *  params:   target  - free expired entries until no more then taget memory
00652  *                      is used  (use 0 to free all of them)
00653  *            delta   - consider an entry expired if it expires after delta
00654  *                      ticks from now
00655  *            timeout - exit after timeout ticks
00656  *
00657  *  returns: number of deleted entries
00658  *  This function should be called periodically from a timer
00659  */
00660 inline static int dst_blacklist_clean_expired(unsigned int target,
00661                                                                           ticks_t delta,
00662                                                                           ticks_t timeout)
00663 {
00664         static unsigned int start=0;
00665         unsigned int h;
00666         struct dst_blst_entry** crt;
00667         struct dst_blst_entry** tmp;
00668         struct dst_blst_entry* e;
00669         ticks_t start_time;
00670         ticks_t now;
00671         int no=0;
00672         int i;
00673 
00674         now=start_time=get_ticks_raw();
00675         for(h=start; h!=(start+DST_BLST_HASH_SIZE); h++){
00676                 i=h%DST_BLST_HASH_SIZE;
00677                 if (dst_blst_hash[i].first){
00678                         LOCK_BLST(i);
00679                         for (crt=&dst_blst_hash[i].first, tmp=&(*crt)->next;
00680                                         *crt; crt=tmp, tmp=&(*crt)->next){
00681                                 e=*crt;
00682                                 prefetch_loc_r((*crt)->next, 1);
00683                                 if ((s_ticks_t)(now+delta-(*crt)->expire)>=0){
00684                                         *crt=(*crt)->next;
00685                                         tmp=crt;
00686                                         *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
00687                                         blst_destroy_entry(e);
00688                                         BLST_HASH_STATS_DEC(i);
00689                                         no++;
00690                                         if (*blst_mem_used<=target){
00691                                                 UNLOCK_BLST(i);
00692                                                 goto skip;
00693                                         }
00694                                 }
00695                         }
00696                         UNLOCK_BLST(i);
00697                         /* check for timeout only "between" hash cells */
00698                         now=get_ticks_raw();
00699                         if ((now-start_time)>=timeout){
00700                                 DBG("_dst_blacklist_clean_expired_unsafe: timeout: %d > %d\n",
00701                                                 TICKS_TO_MS(now-start_time), TICKS_TO_MS(timeout));
00702                                 goto skip;
00703                         }
00704                 }
00705         }
00706 skip:
00707         start=h; /* next time we start where we left */
00708         if (no){
00709                 DBG("dst_blacklist_clean_expired, %d entries removed\n", no);
00710         }
00711         return no;
00712 }
00713 
00714 
00715 
00716 /* timer */
00717 static ticks_t blst_timer(ticks_t ticks, struct timer_ln* tl, void* data)
00718 {
00719         dst_blacklist_clean_expired(0, 0, 2); /*spend max. 2 ticks*/
00720         return (ticks_t)(-1);
00721 }
00722 
00723 
00724 
00725 /* adds a proto ip:port combination to the blacklist
00726  * returns 0 on success, -1 on error (blacklist full -- would use more then
00727  *  blst:_max_mem, or out of shm. mem.)
00728  */
00729 inline static int dst_blacklist_add_ip(unsigned char err_flags,
00730                                                                         unsigned char proto,
00731                                                                         struct ip_addr* ip, unsigned short port,
00732                                                                         ticks_t timeout)
00733 {
00734         int size;
00735         struct dst_blst_entry* e;
00736         unsigned short hash;
00737         ticks_t now;
00738         int ret;
00739 
00740         ret=0;
00741         if (ip->af==AF_INET){
00742                 err_flags&=~BLST_IS_IPV6; /* make sure the ipv6 flag is reset */
00743                 size=sizeof(struct dst_blst_entry);
00744         }else{
00745                 err_flags|=BLST_IS_IPV6;
00746                 size=sizeof(struct dst_blst_entry)+12 /* ipv6 addr - 4 */;
00747         }
00748         now=get_ticks_raw();
00749         hash=dst_blst_hash_no(proto, ip, port);
00750         /* check if the entry already exists */
00751         LOCK_BLST(hash);
00752                 e=_dst_blacklist_lst_find(hash, ip, proto, port, now);
00753                 if (e){
00754                         e->flags|=err_flags;
00755                         e->expire=now+timeout; /* update the timeout */
00756                 }else{
00757                         if (unlikely((*blst_mem_used+size) >=
00758                                         cfg_get(core, core_cfg, blst_max_mem))){
00759 #ifdef USE_DST_BLACKLIST_STATS
00760                                 dst_blacklist_stats[process_no].bkl_lru_cnt++;
00761 #endif
00762                                 UNLOCK_BLST(hash);
00763                                 /* first try to free some memory  (~ 12%), but don't
00764                                  * spend more then 250 ms*/
00765                                 dst_blacklist_clean_expired(*blst_mem_used/16*14, 0,
00766                                                                                                                         MS_TO_TICKS(250));
00767                                 if (unlikely(*blst_mem_used+size >=
00768                                                 cfg_get(core, core_cfg, blst_max_mem))){
00769                                         ret=-1;
00770                                         goto error;
00771                                 }
00772                                 LOCK_BLST(hash);
00773                         }
00774                         e=shm_malloc(size);
00775                         if (e==0){
00776                                 UNLOCK_BLST(hash);
00777                                 ret=E_OUT_OF_MEM;
00778                                 goto error;
00779                         }
00780                         *blst_mem_used+=size;
00781                         e->flags=err_flags;
00782                         e->proto=proto;
00783                         e->port=port;
00784                         memcpy(e->ip, ip->u.addr, ip->len);
00785                         e->expire=now+timeout; /* update the timeout */
00786                         e->next=0;
00787                         dst_blacklist_lst_add(&dst_blst_hash[hash].first, e);
00788                         BLST_HASH_STATS_INC(hash);
00789                 }
00790         UNLOCK_BLST(hash);
00791 error:
00792         return ret;
00793 }
00794 
00795 
00796 
00797 /* if no blacklisted returns 0, else returns the blacklist flags */
00798 inline static int dst_is_blacklisted_ip(unsigned char proto,
00799                                                                                 struct ip_addr* ip,
00800                                                                                 unsigned short port)
00801 {
00802         struct dst_blst_entry* e;
00803         unsigned short hash;
00804         ticks_t now;
00805         int ret;
00806 
00807         ret=0;
00808         now=get_ticks_raw();
00809         hash=dst_blst_hash_no(proto, ip, port);
00810         if (unlikely(dst_blst_hash[hash].first)){
00811                 LOCK_BLST(hash);
00812                         e=_dst_blacklist_lst_find(hash, ip, proto, port, now);
00813                         if (e){
00814                                 ret=e->flags;
00815                         }
00816                 UNLOCK_BLST(hash);
00817         }
00818         return ret;
00819 }
00820 
00821 
00822 
00831 int dst_blacklist_force_add_to(unsigned char err_flags,  struct dest_info* si,
00832                                                                 struct sip_msg* msg, ticks_t timeout)
00833 {
00834         struct ip_addr ip;
00835 
00836 #ifdef DST_BLACKLIST_HOOKS
00837         if (unlikely (blacklist_run_hooks(&blst_add_cb, si, &err_flags, msg) ==
00838                                         DST_BLACKLIST_DENY))
00839                 return 0;
00840 #endif
00841         su2ip_addr(&ip, &si->to);
00842         return dst_blacklist_add_ip(err_flags, si->proto, &ip,
00843                                                                 su_getport(&si->to), timeout);
00844 }
00845 
00846 
00847 
00852 int dst_blacklist_force_su_to(unsigned char err_flags, unsigned char proto,
00853                                                                 union sockaddr_union* dst,
00854                                                                 struct sip_msg* msg, ticks_t timeout)
00855 {
00856         struct ip_addr ip;
00857 #ifdef DST_BLACKLIST_HOOKS
00858         struct dest_info si;
00859         
00860         init_dest_info(&si);
00861         si.to=*dst;
00862         si.proto=proto;
00863         if (unlikely (blacklist_run_hooks(&blst_add_cb, &si, &err_flags, msg) ==
00864                                         DST_BLACKLIST_DENY))
00865                 return 0;
00866 #endif
00867         su2ip_addr(&ip, dst);
00868         return dst_blacklist_add_ip(err_flags, proto, &ip,
00869                                                                 su_getport(dst), timeout);
00870 }
00871 
00872 
00873 
00874 int dst_is_blacklisted(struct dest_info* si, struct sip_msg* msg)
00875 {
00876         int ires;
00877         struct ip_addr ip;
00878 #ifdef DST_BLACKLIST_HOOKS
00879         unsigned char err_flags;
00880         int action;
00881 #endif
00882         su2ip_addr(&ip, &si->to);
00883 
00884 #ifdef DST_BLACKLIST_HOOKS
00885         err_flags=0;
00886         if (unlikely((action=(blacklist_run_hooks(&blst_search_cb, si, &err_flags, msg))
00887                                         ) != DST_BLACKLIST_CONTINUE)){
00888                 if (action==DST_BLACKLIST_DENY)
00889                         return 0;
00890                 else  /* if (action==DST_BLACKLIST_ACCEPT) */
00891                         return err_flags;
00892         }
00893 #endif
00894         ires=dst_is_blacklisted_ip(si->proto, &ip, su_getport(&si->to));
00895 #ifdef USE_DST_BLACKLIST_STATS
00896         if (ires)
00897                 dst_blacklist_stats[process_no].bkl_hit_cnt++;
00898 #endif
00899         return ires;
00900 }
00901 
00902 
00903 
00904 /* returns 1 if the entry was deleted, 0 if not found */
00905 int dst_blacklist_del(struct dest_info* si, struct sip_msg* msg)
00906 {
00907         unsigned short hash;
00908         struct ip_addr ip;
00909         ticks_t now;
00910         int ret;
00911         unsigned short port;
00912         
00913         ret=0;
00914         su2ip_addr(&ip, &si->to);
00915         port=su_getport(&si->to);
00916         now=get_ticks_raw();
00917         hash=dst_blst_hash_no(si->proto, &ip, port);
00918         if (unlikely(dst_blst_hash[hash].first)){
00919                 LOCK_BLST(hash);
00920                         ret=_dst_blacklist_del(hash, &ip, si->proto, port, now);
00921                 UNLOCK_BLST(hash);
00922         }
00923         return ret;
00924 }
00925 
00926 
00927 
00928 /* rpc functions */
00929 void dst_blst_mem_info(rpc_t* rpc, void* ctx)
00930 {
00931         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
00932                 rpc->fault(ctx, 500, "dst blacklist support disabled");
00933                 return;
00934         }
00935         rpc->add(ctx, "dd",  *blst_mem_used, cfg_get(core, core_cfg, blst_max_mem));
00936 }
00937 
00938 
00939 
00940 
00941 #ifdef USE_DST_BLACKLIST_STATS
00942 
00943 static unsigned long  stat_sum(int ivar, int breset) {
00944         unsigned long isum=0;
00945         int i1=0;
00946 
00947         for (; i1 < get_max_procs(); i1++)
00948                 switch (ivar) {
00949                         case 0:
00950                                 isum+=dst_blacklist_stats[i1].bkl_hit_cnt;
00951                                 if (breset)
00952                                         dst_blacklist_stats[i1].bkl_hit_cnt=0;
00953                                 break;
00954                         case 1:
00955                                 isum+=dst_blacklist_stats[i1].bkl_lru_cnt;
00956                                 if (breset)
00957                                         dst_blacklist_stats[i1].bkl_lru_cnt=0;
00958                                 break;
00959                 }
00960 
00961                 return isum;
00962 }
00963 
00964 
00965 void dst_blst_stats_get(rpc_t* rpc, void* c)
00966 {
00967         char *name=NULL;
00968         void *handle;
00969         int found=0,i=0;
00970         int reset=0;
00971         char* dst_blacklist_stats_names[] = {
00972                 "bkl_hit_cnt",
00973                 "bkl_lru_cnt",
00974                 NULL
00975         };
00976         
00977         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
00978                 rpc->fault(c, 500, "dst blacklist support disabled");
00979                 return;
00980         }
00981         if (rpc->scan(c, "s", &name) < 0)
00982                 return;
00983         if (rpc->scan(c, "d", &reset) < 0)
00984                 return;
00985         if (!strcasecmp(name, DST_BLACKLIST_ALL_STATS)) {
00986                 /* dump all the dns cache stat values */
00987                 rpc->add(c, "{", &handle);
00988                 for (i=0; dst_blacklist_stats_names[i]; i++)
00989                         rpc->struct_add(handle, "d",
00990                                                         dst_blacklist_stats_names[i],
00991                                                         stat_sum(i, reset));
00992 
00993                 found=1;
00994         } else {
00995                 for (i=0; dst_blacklist_stats_names[i]; i++)
00996                         if (!strcasecmp(dst_blacklist_stats_names[i], name)) {
00997                         rpc->add(c, "{", &handle);
00998                         rpc->struct_add(handle, "d",
00999                                                         dst_blacklist_stats_names[i],
01000                                                         stat_sum(i, reset));
01001                         found=1;
01002                         break;
01003                         }
01004         }
01005         if(!found)
01006                 rpc->fault(c, 500, "unknown dst blacklist stat parameter");
01007 
01008         return;
01009 }
01010 #endif /* USE_DST_BLACKLIST_STATS */
01011 
01012 /* only for debugging, it helds the lock too long for "production" use */
01013 void dst_blst_debug(rpc_t* rpc, void* ctx)
01014 {
01015         int h;
01016         struct dst_blst_entry* e;
01017         ticks_t now;
01018         struct ip_addr ip;
01019 
01020         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
01021                 rpc->fault(ctx, 500, "dst blacklist support disabled");
01022                 return;
01023         }
01024         now=get_ticks_raw();
01025                 for(h=0; h<DST_BLST_HASH_SIZE; h++){
01026                         LOCK_BLST(h);
01027                         for(e=dst_blst_hash[h].first; e; e=e->next){
01028                                 dst_blst_entry2ip(&ip, e);
01029                                 rpc->add(ctx, "ssddd", get_proto_name(e->proto),
01030                                                                                 ip_addr2a(&ip), e->port,
01031                                                                                 (s_ticks_t)(now-e->expire)<=0?
01032                                                                                 TICKS_TO_S(e->expire-now):
01033                                                                                 -TICKS_TO_S(now-e->expire) ,
01034                                                                                 e->flags);
01035                         }
01036                         UNLOCK_BLST(h);
01037                 }
01038 }
01039 
01040 /* only for debugging, it helds the lock too long for "production" use */
01041 void dst_blst_hash_stats(rpc_t* rpc, void* ctx)
01042 {
01043         int h;
01044         struct dst_blst_entry* e;
01045 #ifdef BLST_HASH_STATS
01046         int n;
01047 
01048         n=0;
01049 #endif
01050         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
01051                 rpc->fault(ctx, 500, "dst blacklist support disabled");
01052                 return;
01053         }
01054                 for(h=0; h<DST_BLST_HASH_SIZE; h++){
01055 #ifdef BLST_HASH_STATS
01056                         LOCK_BLST(h);
01057                         for(e=dst_blst_hash[h].first; e; e=e->next) n++;
01058                         UNLOCK_BLST(h);
01059                         rpc->add(ctx, "dd", h, n);
01060 #else
01061                         rpc->add(ctx, "dd", h, dst_blst_hash[h].entries);
01062 #endif
01063                 }
01064 }
01065 
01066 /* dumps the content of the blacklist in a human-readable format */
01067 void dst_blst_view(rpc_t* rpc, void* ctx)
01068 {
01069         int h;
01070         int expires;
01071         struct dst_blst_entry* e;
01072         ticks_t now;
01073         struct ip_addr ip;
01074 
01075         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
01076                 rpc->fault(ctx, 500, "dst blacklist support disabled");
01077                 return;
01078         }
01079         now=get_ticks_raw();
01080         for(h=0; h<DST_BLST_HASH_SIZE; h++) {
01081                 LOCK_BLST(h);
01082                 for(e=dst_blst_hash[h].first; e; e=e->next) {
01083                         expires = (s_ticks_t)(now-e->expire)<=0?
01084                                    TICKS_TO_S(e->expire-now): -TICKS_TO_S(now-e->expire);
01085                         /* don't include expired entries into view report */
01086                         if (expires < 0) {
01087                                 continue;
01088                         }
01089                         dst_blst_entry2ip(&ip, e);
01090                         rpc->printf(ctx, "{\n    protocol: %s", get_proto_name(e->proto));
01091                         rpc->printf(ctx, "    ip: %s", ip_addr2a(&ip));
01092                         rpc->printf(ctx, "    port: %d", e->port);
01093                         rpc->printf(ctx, "    expires in (s): %d", expires); 
01094                         rpc->printf(ctx, "    flags: %d\n}", e->flags);
01095                 }
01096                 UNLOCK_BLST(h);
01097         }
01098 }
01099 
01100 
01101 /* deletes all the entries from the blacklist except the permanent ones
01102  * (which are marked with BLST_PERMANENT)
01103  */
01104 void dst_blst_flush(void)
01105 {
01106         int h;
01107         struct dst_blst_entry* e;
01108         struct dst_blst_entry** crt;
01109         struct dst_blst_entry** tmp;
01110 
01111         for(h=0; h<DST_BLST_HASH_SIZE; h++){
01112                 LOCK_BLST(h);
01113                 for (crt=&dst_blst_hash[h].first, tmp=&(*crt)->next;
01114                                 *crt; crt=tmp, tmp=&(*crt)->next){
01115                         e=*crt;
01116                         prefetch_loc_r((*crt)->next, 1);
01117                         if (!(e->flags &  BLST_PERMANENT)){
01118                                 *crt=(*crt)->next;
01119                                 tmp=crt;
01120                                 *blst_mem_used-=DST_BLST_ENTRY_SIZE(*e);
01121                                 blst_destroy_entry(e);
01122                                 BLST_HASH_STATS_DEC(h);
01123                         }
01124                 }
01125                 UNLOCK_BLST(h);
01126         }
01127 }
01128 
01129 /* rpc wrapper function for dst_blst_flush() */
01130 void dst_blst_delete_all(rpc_t* rpc, void* ctx)
01131 {
01132         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
01133                 rpc->fault(ctx, 500, "dst blacklist support disabled");
01134                 return;
01135         }
01136         dst_blst_flush();
01137 }
01138 
01139 /* Adds a new entry to the blacklist */
01140 void dst_blst_add(rpc_t* rpc, void* ctx)
01141 {
01142         str ip;
01143         int port, proto, flags;
01144         unsigned char err_flags;
01145         struct ip_addr *ip_addr;
01146 
01147         if (!cfg_get(core, core_cfg, use_dst_blacklist)){
01148                 rpc->fault(ctx, 500, "dst blacklist support disabled");
01149                 return;
01150         }
01151         if (rpc->scan(ctx, "Sddd", &ip, &port, &proto, &flags) < 4)
01152                 return;
01153 
01154         err_flags = (unsigned char)flags;
01155         /* sanity checks */
01156         if ((unsigned char)proto > PROTO_SCTP) {
01157                 rpc->fault(ctx, 400, "Unknown protocol");
01158                 return;
01159         }
01160 
01161         if (err_flags & BLST_IS_IPV6) {
01162 #ifdef USE_IPV6
01163                 /* IPv6 address is specified */
01164                 ip_addr = str2ip6(&ip);
01165 #else  /* USE_IPV6 */
01166                 rpc->fault(ctx, 400, "IPv6 support disabled");
01167                 return;
01168 #endif /* USE_IPV6 */
01169         } else {
01170                 /* try IPv4 first, than IPv6 */
01171                 ip_addr = str2ip(&ip);
01172                 if (!ip_addr) {
01173 #ifdef USE_IPV6
01174                         ip_addr = str2ip6(&ip);
01175                         err_flags |= BLST_IS_IPV6;
01176 #else  /* USE_IPV6 */
01177                         rpc->fault(ctx, 400, "Malformed or IPv6 ip address");
01178                         return;
01179 #endif /* USE_IPV6 */
01180                 }
01181         }
01182         if (!ip_addr) {
01183                 rpc->fault(ctx, 400, "Malformed ip address");
01184                 return;
01185         }
01186 
01187         if (dst_blacklist_add_ip(err_flags, proto, ip_addr, port, 
01188                                     S_TO_TICKS(cfg_get(core, core_cfg, blst_timeout))))
01189                 rpc->fault(ctx, 400, "Failed to add the entry to the blacklist");
01190 }
01191 
01192 /* fixup function for use_dst_blacklist
01193  * verifies that dst_blacklist_init is set to 1
01194  */
01195 int use_dst_blacklist_fixup(void *handle, str *gname, str *name, void **val)
01196 {
01197         if ((int)(long)(*val) && !dst_blacklist_init) {
01198                 LOG(L_ERR, "ERROR: use_dst_blacklist_fixup(): "
01199                         "dst blacklist is turned off by dst_blacklist_init=0, "
01200                         "it cannot be enabled runtime.\n");
01201                 return -1;
01202         }
01203         return 0;
01204 }
01205 
01206 /* KByte to Byte conversion */
01207 int blst_max_mem_fixup(void *handle, str *gname, str *name, void **val)
01208 {
01209         unsigned int    u;
01210 
01211         u = ((unsigned int)(long)(*val))<<10;
01212         (*val) = (void *)(long)u;
01213         return 0;
01214 }
01215 
01216 
01217 
01219 void blst_reinit_ign_masks(str* gname, str* name)
01220 {
01221         blst_init_ign_masks();
01222 }
01223 
01224 
01225 #endif /* USE_DST_BLACKLIST */
01226 

Generated on Tue May 22 2012 13:10:06 for SIP Router by  doxygen 1.7.1