dispatcher.c

Go to the documentation of this file.
00001 /**
00002  * $Id: dispatcher.c 5456 2009-01-14 11:52:18Z miconda $
00003  *
00004  * dispatcher module -- stateless load balancing
00005  *
00006  * Copyright (C) 2004-2005 FhG Fokus
00007  * Copyright (C) 2006 Voice Sistem SRL
00008  *
00009  * This file is part of Kamailio, a free SIP server.
00010  *
00011  * Kamailio is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version
00015  *
00016  * Kamailio is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU General Public License 
00022  * along with this program; if not, write to the Free Software 
00023  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00024  *
00025  * History
00026  * -------
00027  * 2004-07-31  first version, by daniel
00028  * 2007-01-11  Added a function to check if a specific gateway is in a group
00029  *          (carsten - Carsten Bock, BASIS AudioNet GmbH)
00030  * 2007-02-09  Added active probing of failed destinations and automatic
00031  *          re-enabling of destinations (carsten)
00032  * 2007-05-08  Ported the changes to SVN-Trunk and renamed ds_is_domain
00033  *          to ds_is_from_list.  (carsten)
00034  * 2007-07-18  Added support for load/reload groups from DB 
00035  *             reload triggered from ds_reload MI_Command (ancuta)
00036  */
00037 
00038 /*! \file
00039  * \ingroup dispatcher
00040  * \brief Dispatcher :: Dispatch
00041  */
00042 
00043 /*! \defgroup dispatcher Dispatcher :: Load balancing and failover module
00044  *    The dispatcher module implements a set of functions for distributing SIP requests on a 
00045  * set of servers, but also grouping of server resources.
00046  *
00047  * - The module has an internal API exposed to other modules.
00048  * - The module implements a couple of MI functions for managing the list of server resources
00049  */
00050 
00051 #include <stdio.h>
00052 #include <string.h>
00053 #include <stdlib.h>
00054 #include <sys/types.h>
00055 #include <unistd.h>
00056 
00057 #include "../../mi/mi.h"
00058 #include "../../sr_module.h"
00059 #include "../../dprint.h"
00060 #include "../../error.h"
00061 #include "../../ut.h"
00062 #include "../../route.h"
00063 #include "../../mem/mem.h"
00064 #include "../../mod_fix.h"
00065 
00066 #include "dispatch.h"
00067 
00068 MODULE_VERSION
00069 
00070 #define DS_SET_ID_COL         "setid"
00071 #define DS_DEST_URI_COL       "destination"
00072 #define DS_DEST_FLAGS_COL     "flags"
00073 #define DS_DEST_PRIORITY_COL  "priority"
00074 #define DS_TABLE_NAME         "dispatcher"
00075 
00076 /** parameters */
00077 char *dslistfile = CFG_DIR"dispatcher.list";
00078 int  ds_force_dst   = 0;
00079 int  ds_flags       = 0; 
00080 int  ds_use_default = 0; 
00081 static str dst_avp_param = {NULL, 0};
00082 static str grp_avp_param = {NULL, 0};
00083 static str cnt_avp_param = {NULL, 0};
00084 str hash_pvar_param = {NULL, 0};
00085 
00086 int_str dst_avp_name;
00087 unsigned short dst_avp_type;
00088 int_str grp_avp_name;
00089 unsigned short grp_avp_type;
00090 int_str cnt_avp_name;
00091 unsigned short cnt_avp_type;
00092 
00093 pv_elem_t * hash_param_model = NULL;
00094 
00095 int probing_threshhold = 3; /* number of failed requests, before a destination
00096                         is taken into probing */
00097 str ds_ping_method = {"OPTIONS",7};
00098 str ds_ping_from   = {"sip:dispatcher@localhost", 24};
00099 static int ds_ping_interval = 0;
00100 int ds_probing_mode  = 0;
00101 int ds_append_branch = 1;
00102 
00103 /*db */
00104 str ds_db_url            = {NULL, 0};
00105 str ds_set_id_col        = str_init(DS_SET_ID_COL);
00106 str ds_dest_uri_col      = str_init(DS_DEST_URI_COL);
00107 str ds_dest_flags_col    = str_init(DS_DEST_FLAGS_COL);
00108 str ds_dest_priority_col = str_init(DS_DEST_PRIORITY_COL);
00109 str ds_table_name        = str_init(DS_TABLE_NAME);
00110 
00111 str ds_setid_pvname   = {NULL, 0};
00112 pv_spec_t ds_setid_pv;
00113 
00114 /** module functions */
00115 static int mod_init(void);
00116 static int child_init(int);
00117 
00118 static int w_ds_select_dst(struct sip_msg*, char*, char*);
00119 static int w_ds_select_domain(struct sip_msg*, char*, char*);
00120 static int w_ds_next_dst(struct sip_msg*, char*, char*);
00121 static int w_ds_next_domain(struct sip_msg*, char*, char*);
00122 static int w_ds_mark_dst0(struct sip_msg*, char*, char*);
00123 static int w_ds_mark_dst1(struct sip_msg*, char*, char*);
00124 
00125 static int w_ds_is_from_list0(struct sip_msg*, char*, char*);
00126 static int w_ds_is_from_list1(struct sip_msg*, char*, char*);
00127 
00128 static void destroy(void);
00129 
00130 static int ds_warn_fixup(void** param, int param_no);
00131 
00132 static struct mi_root* ds_mi_set(struct mi_root* cmd, void* param);
00133 static struct mi_root* ds_mi_list(struct mi_root* cmd, void* param);
00134 static struct mi_root* ds_mi_reload(struct mi_root* cmd_tree, void* param);
00135 static int mi_child_init(void);
00136 
00137 static cmd_export_t cmds[]={
00138    {"ds_select_dst",    (cmd_function)w_ds_select_dst,    2, fixup_igp_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
00139    {"ds_select_domain", (cmd_function)w_ds_select_domain, 2, fixup_igp_igp, 0, REQUEST_ROUTE|FAILURE_ROUTE},
00140    {"ds_next_dst",      (cmd_function)w_ds_next_dst,      0, ds_warn_fixup, 0, FAILURE_ROUTE},
00141    {"ds_next_domain",   (cmd_function)w_ds_next_domain,   0, ds_warn_fixup, 0, FAILURE_ROUTE},
00142    {"ds_mark_dst",      (cmd_function)w_ds_mark_dst0,     0, ds_warn_fixup, 0, FAILURE_ROUTE},
00143    {"ds_mark_dst",      (cmd_function)w_ds_mark_dst1,     1, ds_warn_fixup, 0, FAILURE_ROUTE},
00144    {"ds_is_from_list",  (cmd_function)w_ds_is_from_list0, 0, 0, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE},
00145    {"ds_is_from_list",  (cmd_function)w_ds_is_from_list1, 1, fixup_uint_null, 0, REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE},
00146    {0,0,0,0,0,0}
00147 };
00148 
00149 
00150 static param_export_t params[]={
00151    {"list_file",       STR_PARAM, &dslistfile},
00152    {"db_url",         STR_PARAM, &ds_db_url.s},
00153    {"table_name",        STR_PARAM, &ds_table_name.s},
00154    {"setid_col",       STR_PARAM, &ds_set_id_col.s},
00155    {"destination_col", STR_PARAM, &ds_dest_uri_col.s},
00156    {"flags_col",       STR_PARAM, &ds_dest_flags_col.s},
00157    {"priority_col",    STR_PARAM, &ds_dest_priority_col.s},
00158    {"force_dst",       INT_PARAM, &ds_force_dst},
00159    {"flags",           INT_PARAM, &ds_flags},
00160    {"use_default",     INT_PARAM, &ds_use_default},
00161    {"dst_avp",         STR_PARAM, &dst_avp_param.s},
00162    {"grp_avp",         STR_PARAM, &grp_avp_param.s},
00163    {"cnt_avp",         STR_PARAM, &cnt_avp_param.s},
00164    {"hash_pvar",       STR_PARAM, &hash_pvar_param.s},
00165    {"setid_pvname",    STR_PARAM, &ds_setid_pvname.s},
00166    {"ds_probing_threshhold", INT_PARAM, &probing_threshhold},
00167    {"ds_ping_method",     STR_PARAM, &ds_ping_method.s},
00168    {"ds_ping_from",       STR_PARAM, &ds_ping_from.s},
00169    {"ds_ping_interval",   INT_PARAM, &ds_ping_interval},
00170    {"ds_probing_mode",    INT_PARAM, &ds_probing_mode},
00171    {"ds_append_branch",   INT_PARAM, &ds_append_branch},
00172    {0,0,0}
00173 };
00174 
00175 
00176 static mi_export_t mi_cmds[] = {
00177    { "ds_set_state",   ds_mi_set,     0,                 0,  0            },
00178    { "ds_list",        ds_mi_list,    MI_NO_INPUT_FLAG,  0,  0            },
00179    { "ds_reload",      ds_mi_reload,  0,                 0,  mi_child_init},
00180    { 0, 0, 0, 0, 0}
00181 };
00182 
00183 
00184 /** module exports */
00185 struct module_exports exports= {
00186    "dispatcher",
00187    DEFAULT_DLFLAGS, /* dlopen flags */
00188    cmds,
00189    params,
00190    0,          /* exported statistics */
00191    mi_cmds,    /* exported MI functions */
00192    0,          /* exported pseudo-variables */
00193    0,          /* extra processes */
00194    mod_init,   /* module initialization function */
00195    0,
00196    (destroy_function) destroy,
00197    child_init  /* per-child init function */
00198 };
00199 
00200 /**
00201  * init module function
00202  */
00203 static int mod_init(void)
00204 {
00205    pv_spec_t avp_spec;
00206 
00207    if (dst_avp_param.s)
00208       dst_avp_param.len = strlen(dst_avp_param.s);
00209    if (grp_avp_param.s)
00210       grp_avp_param.len = strlen(grp_avp_param.s);
00211    if (cnt_avp_param.s)
00212       cnt_avp_param.len = strlen(cnt_avp_param.s); 
00213    if (hash_pvar_param.s)
00214       hash_pvar_param.len = strlen(hash_pvar_param.s);
00215    if (ds_setid_pvname.s)
00216       ds_setid_pvname.len = strlen(ds_setid_pvname.s);
00217    if (ds_ping_from.s) ds_ping_from.len = strlen(ds_ping_from.s);
00218    if (ds_ping_method.s) ds_ping_method.len = strlen(ds_ping_method.s);
00219 
00220    if(init_data()!= 0)
00221       return -1;
00222 
00223    if(ds_db_url.s)
00224    {
00225       ds_db_url.len     = strlen(ds_db_url.s);
00226       ds_table_name.len = strlen(ds_table_name.s);
00227       ds_set_id_col.len     = strlen(ds_set_id_col.s);
00228       ds_dest_uri_col.len   = strlen(ds_dest_uri_col.s);
00229       ds_dest_flags_col.len = strlen(ds_dest_flags_col.s);
00230 
00231       if(init_ds_db()!= 0)
00232       {
00233          LM_ERR("could not initiate a connect to the database\n");
00234          return -1;
00235       }
00236    } else {
00237       if(ds_load_list(dslistfile)!=0) {
00238          LM_ERR("no dispatching list loaded from file\n");
00239          return -1;
00240       } else {
00241          LM_DBG("loaded dispatching list\n");
00242       }
00243    }
00244    
00245    if (dst_avp_param.s && dst_avp_param.len > 0)
00246    {
00247       if (pv_parse_spec(&dst_avp_param, &avp_spec)==0
00248             || avp_spec.type!=PVT_AVP)
00249       {
00250          LM_ERR("malformed or non AVP %.*s AVP definition\n",
00251                dst_avp_param.len, dst_avp_param.s);
00252          return -1;
00253       }
00254 
00255       if(pv_get_avp_name(0, &(avp_spec.pvp), &dst_avp_name, &dst_avp_type)!=0)
00256       {
00257          LM_ERR("[%.*s]- invalid AVP definition\n", dst_avp_param.len,
00258                dst_avp_param.s);
00259          return -1;
00260       }
00261    } else {
00262       dst_avp_name.n = 0;
00263       dst_avp_type = 0;
00264    }
00265    if (grp_avp_param.s && grp_avp_param.len > 0)
00266    {
00267       if (pv_parse_spec(&grp_avp_param, &avp_spec)==0
00268             || avp_spec.type!=PVT_AVP)
00269       {
00270          LM_ERR("malformed or non AVP %.*s AVP definition\n",
00271                grp_avp_param.len, grp_avp_param.s);
00272          return -1;
00273       }
00274 
00275       if(pv_get_avp_name(0, &(avp_spec.pvp), &grp_avp_name, &grp_avp_type)!=0)
00276       {
00277          LM_ERR("[%.*s]- invalid AVP definition\n", grp_avp_param.len,
00278                grp_avp_param.s);
00279          return -1;
00280       }
00281    } else {
00282       grp_avp_name.n = 0;
00283       grp_avp_type = 0;
00284    }
00285    if (cnt_avp_param.s && cnt_avp_param.len > 0)
00286    {
00287       if (pv_parse_spec(&cnt_avp_param, &avp_spec)==0
00288             || avp_spec.type!=PVT_AVP)
00289       {
00290          LM_ERR("malformed or non AVP %.*s AVP definition\n",
00291                cnt_avp_param.len, cnt_avp_param.s);
00292          return -1;
00293       }
00294 
00295       if(pv_get_avp_name(0, &(avp_spec.pvp), &cnt_avp_name, &cnt_avp_type)!=0)
00296       {
00297          LM_ERR("[%.*s]- invalid AVP definition\n", cnt_avp_param.len,
00298                cnt_avp_param.s);
00299          return -1;
00300       }
00301    } else {
00302       cnt_avp_name.n = 0;
00303       cnt_avp_type = 0;
00304    }
00305 
00306    if (hash_pvar_param.s && *hash_pvar_param.s) {
00307       if(pv_parse_format(&hash_pvar_param, &hash_param_model) < 0
00308             || hash_param_model==NULL) {
00309          LM_ERR("malformed PV string: %s\n", hash_pvar_param.s);
00310          return -1;
00311       }     
00312    } else {
00313       hash_param_model = NULL;
00314    }
00315    
00316    if(ds_setid_pvname.s!=0)
00317    {
00318       if(pv_parse_spec(&ds_setid_pvname, &ds_setid_pv)==NULL
00319             || !pv_is_w(&ds_setid_pv))
00320       {
00321          LM_ERR("[%s]- invalid setid_pvname\n", ds_setid_pvname.s);
00322          return -1;
00323       }
00324    }
00325    /* Only, if the Probing-Timer is enabled the TM-API needs to be loaded: */
00326    if (ds_ping_interval > 0)
00327    {
00328       /*****************************************************
00329        * TM-Bindings
00330        *****************************************************/
00331       load_tm_f load_tm;
00332       load_tm=(load_tm_f)find_export("load_tm", 0, 0);
00333    
00334       /* import the TM auto-loading function */
00335       if (load_tm)
00336       {
00337          /* let the auto-loading function load all TM stuff */
00338          if (load_tm( &tmb ) == -1)
00339          {
00340             LM_ERR("could not load the TM-functions - disable DS ping\n");
00341             return -1;
00342          }
00343          /*****************************************************
00344           * Register the PING-Timer
00345           *****************************************************/
00346          register_timer(ds_check_timer, NULL, ds_ping_interval);
00347       } else {
00348          LM_WARN("could not bind to the TM-Module, automatic"
00349                " re-activation disabled.\n");
00350       }
00351    }
00352 
00353    return 0;
00354 }
00355 
00356 /*! \brief
00357  * Initialize children
00358  */
00359 static int child_init(int rank)
00360 {
00361    srand((11+rank)*getpid()*7);
00362 
00363    return 0;
00364 }
00365 
00366 static int mi_child_init(void)
00367 {
00368    
00369    if(ds_db_url.s)
00370       return ds_connect_db();
00371    return 0;
00372 
00373 }
00374 
00375 /*! \brief
00376  * destroy function
00377  */
00378 static void destroy(void)
00379 {
00380    ds_destroy_list();
00381    if(ds_db_url.s)
00382       ds_disconnect_db();
00383 }
00384 
00385 /**
00386  *
00387  */
00388 static int w_ds_select_dst(struct sip_msg* msg, char* set, char* alg)
00389 {
00390    int a, s;
00391    
00392    if(msg==NULL)
00393       return -1;
00394    if(fixup_get_ivalue(msg, (gparam_p)set, &s)!=0)
00395    {
00396       LM_ERR("no dst set value\n");
00397       return -1;
00398    }
00399    if(fixup_get_ivalue(msg, (gparam_p)alg, &a)!=0)
00400    {
00401       LM_ERR("no alg value\n");
00402       return -1;
00403    }
00404 
00405    return ds_select_dst(msg, s, a, 0 /*set dst uri*/);
00406 }
00407 
00408 /**
00409  *
00410  */
00411 static int w_ds_select_domain(struct sip_msg* msg, char* set, char* alg)
00412 {
00413    int a, s;
00414    if(msg==NULL)
00415       return -1;
00416 
00417    if(fixup_get_ivalue(msg, (gparam_p)set, &s)!=0)
00418    {
00419       LM_ERR("no dst set value\n");
00420       return -1;
00421    }
00422    if(fixup_get_ivalue(msg, (gparam_p)alg, &a)!=0)
00423    {
00424       LM_ERR("no alg value\n");
00425       return -1;
00426    }
00427 
00428    return ds_select_dst(msg, s, a, 1/*set host port*/);
00429 }
00430 
00431 /**
00432  *
00433  */
00434 static int w_ds_next_dst(struct sip_msg *msg, char *str1, char *str2)
00435 {
00436    return ds_next_dst(msg, 0/*set dst uri*/);
00437 }
00438 
00439 /**
00440  *
00441  */
00442 static int w_ds_next_domain(struct sip_msg *msg, char *str1, char *str2)
00443 {
00444    return ds_next_dst(msg, 1/*set host port*/);
00445 }
00446 
00447 /**
00448  *
00449  */
00450 static int w_ds_mark_dst0(struct sip_msg *msg, char *str1, char *str2)
00451 {
00452    return ds_mark_dst(msg, 0);
00453 }
00454 
00455 /**
00456  *
00457  */
00458 static int w_ds_mark_dst1(struct sip_msg *msg, char *str1, char *str2)
00459 {
00460    if(str1 && (str1[0]=='i' || str1[0]=='I' || str1[0]=='0'))
00461       return ds_mark_dst(msg, 0);
00462    else if(str1 && (str1[0]=='p' || str1[0]=='P' || str1[0]=='2'))
00463       return ds_mark_dst(msg, 2);
00464    else
00465       return ds_mark_dst(msg, 1);
00466 }
00467 
00468 static int ds_warn_fixup(void** param, int param_no)
00469 {
00470    if(!dst_avp_param.s || !grp_avp_param.s || !cnt_avp_param.s)
00471    {
00472       LM_ERR("failover functions used, but AVPs paraamters required"
00473             " are NULL -- feature disabled\n");
00474    }
00475    return 0;
00476 }
00477 
00478 /************************** MI STUFF ************************/
00479 
00480 static struct mi_root* ds_mi_set(struct mi_root* cmd_tree, void* param)
00481 {
00482    str sp;
00483    int ret;
00484    unsigned int group, state;
00485    struct mi_node* node;
00486 
00487    node = cmd_tree->node.kids;
00488    if(node == NULL)
00489       return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00490    sp = node->value;
00491    if(sp.len<=0 || !sp.s)
00492    {
00493       LM_ERR("bad state value\n");
00494       return init_mi_tree( 500, "bad state value", 15);
00495    }
00496 
00497    state = 1;
00498    if(sp.s[0]=='0' || sp.s[0]=='I' || sp.s[0]=='i')
00499       state = 0;
00500    node = node->next;
00501    if(node == NULL)
00502       return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00503    sp = node->value;
00504    if(sp.s == NULL)
00505    {
00506       return init_mi_tree(500, "group not found", 15);
00507    }
00508 
00509    if(str2int(&sp, &group))
00510    {
00511       LM_ERR("bad group value\n");
00512       return init_mi_tree( 500, "bad group value", 16);
00513    }
00514 
00515    node= node->next;
00516    if(node == NULL)
00517       return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
00518 
00519    sp = node->value;
00520    if(sp.s == NULL)
00521    {
00522       return init_mi_tree(500,"address not found", 18 );
00523    }
00524 
00525    if(state==1)
00526       ret = ds_set_state(group, &sp, DS_INACTIVE_DST, 0);
00527    else
00528       ret = ds_set_state(group, &sp, DS_INACTIVE_DST, 1);
00529 
00530    if(ret!=0)
00531    {
00532       return init_mi_tree(404, "destination not found", 21);
00533    }
00534 
00535    return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
00536 }
00537 
00538 
00539 
00540 
00541 static struct mi_root* ds_mi_list(struct mi_root* cmd_tree, void* param)
00542 {
00543    struct mi_root* rpl_tree;
00544 
00545    rpl_tree = init_mi_tree(200, MI_OK_S, MI_OK_LEN);
00546    if (rpl_tree==NULL)
00547       return 0;
00548 
00549    if( ds_print_mi_list(&rpl_tree->node)< 0 )
00550    {
00551       LM_ERR("failed to add node\n");
00552       free_mi_tree(rpl_tree);
00553       return 0;
00554    }
00555 
00556    return rpl_tree;
00557 }
00558 
00559 #define MI_ERR_RELOAD         "ERROR Reloading data"
00560 #define MI_ERR_RELOAD_LEN     (sizeof(MI_ERR_RELOAD)-1)
00561 #define MI_NOT_SUPPORTED      "DB mode not configured"
00562 #define MI_NOT_SUPPORTED_LEN  (sizeof(MI_NOT_SUPPORTED)-1)
00563 
00564 static struct mi_root* ds_mi_reload(struct mi_root* cmd_tree, void* param)
00565 {
00566    if(!ds_db_url.s) {
00567       if (ds_load_list(dslistfile)!=0)
00568          return init_mi_tree(500, MI_ERR_RELOAD, MI_ERR_RELOAD_LEN);
00569    } else {
00570       if(ds_load_db()<0)
00571          return init_mi_tree(500, MI_ERR_RELOAD, MI_ERR_RELOAD_LEN);
00572    }
00573    return init_mi_tree(200, MI_OK_S, MI_OK_LEN);
00574 }
00575 
00576 
00577 static int w_ds_is_from_list0(struct sip_msg *msg, char *str1, char *str2)
00578 {
00579    return ds_is_from_list(msg, -1);
00580 }
00581 
00582 
00583 static int w_ds_is_from_list1(struct sip_msg *msg, char *set, char *str2)
00584 {
00585    return ds_is_from_list(msg, (int)(long)set);
00586 }

Generated on Tue May 22 16:00:26 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6