cr_data.c

Go to the documentation of this file.
00001 /*
00002  * $Id: cr_data.c 5227 2008-11-19 13:16:15Z henningw $
00003  *
00004  * Copyright (C) 2007-2008 1&1 Internet AG
00005  *
00006  * This file is part of Kamailio, a free SIP server.
00007  *
00008  * Kamailio is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * Kamailio is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License 
00019  * along with this program; if not, write to the Free Software 
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  */
00022 
00023 /**
00024  * \file cr_data.c
00025  * \brief Contains the functions to manage routing data.
00026  * \ingroup carrierroute
00027  * - Module; \ref carrierroute
00028  */
00029 
00030 #include <stdlib.h>
00031 #include "../../mem/shm_mem.h"
00032 #include "cr_data.h"
00033 #include "carrierroute.h"
00034 #include "cr_config.h"
00035 #include "cr_db.h"
00036 #include "cr_carrier.h"
00037 #include "cr_domain.h"
00038 #include "cr_rule.h"
00039 
00040 
00041 /**
00042  * Pointer to the routing data.
00043  */
00044 struct route_data_t ** global_data = NULL;
00045 
00046 
00047 static int carrier_data_fixup(struct route_data_t * rd){
00048    int i;
00049    str tmp;
00050    tmp = default_tree;
00051    rd->default_carrier_id = -1;
00052    for(i=0; i<rd->carrier_num; i++){
00053       if(rd->carriers[i]){
00054          if(str_strcmp(rd->carriers[i]->name, &tmp) == 0){
00055             rd->default_carrier_id = rd->carriers[i]->id;
00056          }
00057       }
00058    }
00059    if(rd->default_carrier_id < 0){
00060       LM_ERR("default_carrier not found\n");
00061    }
00062    return 0;
00063 }
00064 
00065 
00066 /**
00067  * initialises the routing data, initialises the global data pointer
00068  *
00069  * @return 0 on success, -1 on failure
00070  */
00071 int init_route_data(void) {
00072    if (global_data == NULL) {
00073       global_data = (struct route_data_t **)
00074                     shm_malloc(sizeof(struct route_data_t *));
00075       if (global_data == NULL) {
00076          SHM_MEM_ERROR;
00077          return -1;
00078       }
00079    }
00080    *global_data = NULL;
00081    return 0;
00082 }
00083 
00084 
00085 /**
00086  * Frees the routing data
00087  */
00088 void destroy_route_data(void){
00089    struct route_data_t * rd = get_data();
00090    clear_route_data(rd);
00091    if(global_data){
00092       *global_data = NULL;
00093       shm_free(global_data);
00094       global_data = NULL;
00095    }
00096 }
00097 
00098 
00099 /**
00100  * Clears the complete routing data.
00101  *
00102  * @param data route data to be cleared
00103  */
00104 void clear_route_data(struct route_data_t *data) {
00105    int i;
00106 
00107    if (data == NULL) {
00108       return;
00109    }
00110    if (data->carriers != NULL) {
00111       for (i = 0; i < data->carrier_num; ++i) {
00112          if (data->carriers[i] != NULL) {
00113             destroy_carrier_data(data->carriers[i]);
00114          }
00115       }
00116       shm_free(data->carriers);
00117    }
00118    if (data->carrier_map) {
00119       for (i = 0; i < data->carrier_num; ++i) {
00120          if (data->carrier_map[i].name.s) shm_free(data->carrier_map[i].name.s);
00121       }
00122       shm_free(data->carrier_map);
00123    }
00124    if (data->domain_map) {
00125       for (i = 0; i < data->domain_num; ++i) {
00126          if (data->domain_map[i].name.s) shm_free(data->domain_map[i].name.s);
00127       }
00128       shm_free(data->domain_map);
00129    }
00130    shm_free(data);
00131    return;
00132 }
00133 
00134 
00135 /**
00136  * adds a carrier_data struct for given carrier.
00137  *
00138  * @param rd route data to be searched
00139  * @param carrier_data the carrier data struct to be inserted
00140  *
00141  * @return 0 on success, -1 on failure
00142  */
00143 int add_carrier_data(struct route_data_t * rd, struct carrier_data_t * carrier_data) {
00144    if (rd->first_empty_carrier >= rd->carrier_num) {
00145       LM_ERR("carrier array already full");
00146       return -1;
00147    }
00148 
00149    if (rd->carriers[rd->first_empty_carrier] != 0) {
00150       LM_ERR("invalid pointer in first empty carrier entry");
00151       return -1;
00152    }
00153 
00154    rd->carriers[rd->first_empty_carrier] = carrier_data;
00155    rd->first_empty_carrier++;
00156    return 0;
00157 }
00158 
00159 
00160 /**
00161  * Loads the routing data into the routing trees and sets the
00162  * global_data pointer to the new data. The old_data is removed
00163  * when it is not locked anymore.
00164  *
00165  * @return 0 on success, -1 on failure
00166  */
00167 int reload_route_data(void) {
00168    struct route_data_t * old_data;
00169    struct route_data_t * new_data = NULL;
00170    int i;
00171 
00172    if ((new_data = shm_malloc(sizeof(struct route_data_t))) == NULL) {
00173       SHM_MEM_ERROR;
00174       return -1;
00175    }
00176    memset(new_data, 0, sizeof(struct route_data_t));
00177 
00178    switch (mode) {
00179    case CARRIERROUTE_MODE_DB:
00180       if (load_route_data_db(new_data) < 0) {
00181          LM_ERR("could not load routing data\n");
00182          goto errout;
00183       }
00184       break;
00185    case CARRIERROUTE_MODE_FILE:
00186       if (load_config(new_data) < 0) {
00187          LM_ERR("could not load routing data\n");
00188          goto errout;
00189       }
00190       break;
00191    default:
00192       LM_ERR("invalid mode");
00193       goto errout;
00194    }
00195    if (new_data == NULL) {
00196       LM_ERR("loading routing data failed (NULL pointer)");
00197       goto errout;
00198    }
00199 
00200    /* sort carriers by id for faster access */
00201    qsort(new_data->carriers, new_data->carrier_num, sizeof(new_data->carriers[0]), compare_carrier_data);
00202 
00203    /* sort domains by id for faster access */
00204    for (i=0; i<new_data->carrier_num; i++) {
00205       qsort(new_data->carriers[i]->domains, new_data->carriers[i]->domain_num, sizeof(new_data->carriers[i]->domains[0]), compare_domain_data);
00206    }
00207 
00208    if (rule_fixup(new_data) < 0) {
00209       LM_ERR("could not fixup rules\n");
00210       goto errout;
00211    }
00212 
00213    if (carrier_data_fixup(new_data) < 0){
00214       LM_ERR("could not fixup trees\n");
00215       goto errout;
00216    }
00217 
00218    new_data->proc_cnt = 0;
00219 
00220    if (*global_data == NULL) {
00221       *global_data = new_data;
00222    } else {
00223       old_data = *global_data;
00224       *global_data = new_data;
00225       i = 0;
00226       while (old_data->proc_cnt > 0) {
00227          LM_ERR("data is still locked after %i seconds\n", i);
00228          sleep_us(i*1000000);
00229          i++;
00230       }
00231       clear_route_data(old_data);
00232    }
00233    return 0;
00234 
00235  errout:
00236    clear_route_data(new_data);
00237    return -1;
00238 }
00239 
00240 
00241 /**
00242  * Increases lock counter and returns a pointer to the
00243  * current routing data
00244  *
00245  * @return pointer to the global routing data on success,
00246  * NULL on failure
00247 */
00248 struct route_data_t * get_data(void) {
00249    struct route_data_t *ret;
00250    if (!global_data || !*global_data) {
00251       return NULL;
00252    }
00253    ret = *global_data;
00254    lock_get(&ret->lock);
00255    ++ret->proc_cnt;
00256    lock_release(&ret->lock);
00257    if (ret == *global_data) {
00258       return ret;
00259    } else {
00260       lock_get(&ret->lock);
00261       --ret->proc_cnt;
00262       lock_release(&ret->lock);
00263       return NULL;
00264    }
00265 }
00266 
00267 
00268 /**
00269  * decrements the lock counter of the routing data
00270  *
00271  * @param data data to be released
00272  */
00273 void release_data(struct route_data_t *data) {
00274    lock_get(&data->lock);
00275    --data->proc_cnt;
00276    lock_release(&data->lock);
00277 }
00278 
00279 
00280 /**
00281  * Returns the carrier data for the given id by doing a binary search.
00282  * @note The carrier array must be sorted!
00283  *
00284  * @param rd route data to be searched
00285  * @param carrier_id the id of the desired carrier
00286  *
00287  * @return a pointer to the desired carrier data, NULL if not found.
00288  */
00289 struct carrier_data_t *get_carrier_data(struct route_data_t * rd, int carrier_id) {
00290    struct carrier_data_t **ret;
00291    struct carrier_data_t key;
00292    struct carrier_data_t *pkey = &key;
00293 
00294    if (!rd) {
00295       LM_ERR("NULL pointer in parameter\n");
00296       return NULL;
00297    }
00298    key.id = carrier_id;
00299    ret = bsearch(&pkey, rd->carriers, rd->carrier_num, sizeof(rd->carriers[0]), compare_carrier_data);
00300    if (ret) return *ret;
00301    return NULL;
00302 }
00303 
00304 
00305 typedef int (*cmpfunc_t)(const void *v1, const void *v2);
00306 
00307 
00308 /**
00309  * Implements a binary search algorithm using the function cmpfunc
00310  * for comparison.
00311  *
00312  * @param base pointer to the beginning of the array
00313  * @param len length of array
00314  * @param elemsize size of array elements
00315  * @param key pointer to the key we are looking for
00316  * @param cmpfunc function to be used for comparison
00317  * @param index  If index is not NULL it is set to:
00318  *     -1 if an error occured,
00319  *     the index of the first entry equal to v
00320  *     or the index of the first entry greater than v in the case v was not found.
00321  *   Be careful: The index returned can be greater than the length of the array!
00322  *
00323  * @return -1 on error, 0 if the value was not found, 1 if it was found.
00324  */
00325 static int binary_search(void *base, unsigned int len, int elemsize, void *key, cmpfunc_t cmpfunc, int *index) {
00326    int left, right, mid;
00327 
00328    if (index) *index=-1;
00329    if (!base) {
00330       LM_ERR("NULL pointer in parameter\n");
00331       return -1;
00332    }
00333    if (len == 0) {
00334       if (index) *index=0;
00335       return 0;
00336    }
00337 
00338    left=0;
00339    right=len-1;
00340    if (cmpfunc(base+elemsize*left, key) > 0) {
00341       LM_DBG("not found (out of left bound)\n");
00342       if (index) *index=0; /* not found, must be inserted at the beginning of array */
00343       return 0;
00344    }
00345    if (cmpfunc(base+elemsize*right, key) < 0) {
00346       LM_DBG("not found (out of right bound)\n");
00347       if (index) *index=len; /* not found, must be inserted at the end of array */
00348       return 0;
00349    }
00350 
00351    while (left < right) {
00352       mid = left + ((right - left) / 2);
00353       if (cmpfunc(base+elemsize*mid, key) < 0) left = mid + 1;
00354       else right = mid;
00355    }
00356 
00357    /* left == right here! */
00358    if (index) *index=left;
00359    if (cmpfunc(base+elemsize*left, key) == 0) return 1;
00360    else return 0;
00361 }
00362 
00363 
00364 /**
00365  * Returns the domain data for the given id by doing a binary search.
00366  * If not found, a new domain data structure is added.
00367  *
00368  * @param rd route data to used for name - id mapping
00369  * @param carrier_data carrier data to be searched
00370  * @param domain_id the id of desired domain
00371  *
00372  * @return a pointer to the desired domain data, NULL on error.
00373  */
00374 static struct domain_data_t * get_domain_data_or_add(struct route_data_t * rd, struct carrier_data_t * carrier_data, int domain_id) {
00375    struct domain_data_t key;
00376    struct domain_data_t *pkey = &key;
00377    struct domain_data_t *domain_data = NULL;
00378    str *domain_name;
00379    int i;
00380    int res;
00381 
00382    if ((!rd) || (!carrier_data)) {
00383       LM_ERR("NULL pointer in parameter\n");
00384       return NULL;
00385    }
00386 
00387    key.id = domain_id;
00388    res = binary_search(carrier_data->domains, carrier_data->first_empty_domain, sizeof(struct domain_data_t *), &pkey, compare_domain_data, &i);
00389    if (res<0) {
00390       LM_ERR("error while searching for domain_id %d\n", domain_id);
00391       return NULL;
00392    }
00393    else if (res>0) {
00394       /* found domain id */
00395       domain_data = carrier_data->domains[i];
00396    }
00397    else {
00398       /* did not find domain id - insert new entry! */
00399       if ((domain_name = map_id2name(rd->domain_map, rd->domain_num, domain_id)) == NULL) {
00400          LM_ERR("could not find domain name for id %d\n", domain_id);
00401          return NULL;
00402       }
00403       if ((domain_data = create_domain_data(domain_id, domain_name)) == NULL) {
00404          LM_ERR("could not create new domain data\n");
00405          return NULL;
00406       }
00407 
00408       /* keep the array sorted! */
00409       if (add_domain_data(carrier_data, domain_data, i) < 0) {
00410          LM_ERR("could not add domain data\n");
00411          destroy_domain_data(domain_data);
00412          return NULL;
00413       }
00414       LM_INFO("added domain %d '%.*s' to carrier %d '%.*s'", domain_id, domain_name->len, domain_name->s, carrier_data->id, carrier_data->name->len, carrier_data->name->s);
00415    }
00416 
00417    return domain_data;
00418 }
00419 
00420 
00421 /**
00422  * Adds the given route information to the routing domain identified by
00423  * domain. scan_prefix identifies the number for which the information
00424  * is and the rewrite_* parameters define what to do in case of a match.
00425  * prob gives the probability with which this rule applies if there are
00426  * more than one for a given prefix.
00427  *
00428  * @param rd the route data to which the route shall be added
00429  * @param carrier_id the carrier id of the route to be added
00430  * @param domain_id the routing domain id of the new route
00431  * @param scan_prefix the number prefix
00432  * @param flags user defined flags
00433  * @param mask mask for user defined flags
00434  * @param max_targets the number of targets
00435  * @param prob the weight of the rule
00436  * @param rewrite_hostpart the rewrite_host of the rule
00437  * @param strip the number of digits to be stripped off userpart before prepending prefix
00438  * @param rewrite_local_prefix the rewrite prefix
00439  * @param rewrite_local_suffix the rewrite suffix
00440  * @param status the status of the rule
00441  * @param hash_index the hash index of the rule
00442  * @param backup indicates if the route is backed up by another. only 
00443                  useful if status==0, if set, it is the hash value
00444                  of another rule
00445  * @param backed_up an -1-termintated array of hash indices of the route 
00446                     for which this route is backup
00447  * @param comment a comment for the route rule
00448  *
00449  * @return 0 on success, -1 on error in which case it LOGs a message.
00450  */
00451 int add_route(struct route_data_t * rd, int carrier_id,
00452       int domain_id, const str * scan_prefix, flag_t flags, flag_t mask, int max_targets,
00453       double prob, const str * rewrite_hostpart, int strip,
00454       const str * rewrite_local_prefix, const str * rewrite_local_suffix,
00455       int status, int hash_index, int backup, int * backed_up, const str * comment) {
00456    struct carrier_data_t * carrier_data = NULL;
00457    struct domain_data_t * domain_data = NULL;
00458    LM_INFO("adding prefix %.*s, prob %f\n", scan_prefix->len, scan_prefix->s, prob);
00459 
00460    if ((carrier_data = get_carrier_data(rd, carrier_id)) == NULL) {
00461       LM_ERR("could not retrieve carrier data for carrier id %d\n", carrier_id);
00462       return -1;
00463    }
00464 
00465    if ((domain_data = get_domain_data_or_add(rd, carrier_data, domain_id)) == NULL) {
00466       LM_ERR("could not retrieve domain data\n");
00467       return -1;
00468    }
00469 
00470    LM_INFO("found carrier and domain, now adding route\n");
00471    return add_route_to_tree(domain_data->tree, scan_prefix, flags, mask, scan_prefix, max_targets, prob, rewrite_hostpart,
00472                             strip, rewrite_local_prefix, rewrite_local_suffix, status,
00473                             hash_index, backup, backed_up, comment);
00474 }
00475 
00476 
00477 /**
00478  * Adds the given failure route information to the failure routing domain identified by
00479  * domain. scan_prefix, host, reply_code and flags identifies the number for which
00480  * the information is and the next_domain parameter defines where to continue routing
00481  * in case of a match.
00482  *
00483  * @param rd the route data to which the route shall be added
00484  * @param carrier_id the carrier id of the route to be added
00485  * @param domain_id the routing domain id of the new route
00486  * @param scan_prefix the number prefix
00487  * @param host the hostname last tried
00488  * @param reply_code the reply code 
00489  * @param flags user defined flags
00490  * @param mask for user defined flags
00491  * @param next_domain_id continue routing with this domain id
00492  * @param comment a comment for the failure route rule
00493  *
00494  * @return 0 on success, -1 on error in which case it LOGs a message.
00495  */
00496 int add_failure_route(struct route_data_t * rd, int carrier_id, int domain_id,
00497       const str * scan_prefix, const str * host, const str * reply_code,
00498       flag_t flags, flag_t mask, int next_domain_id, const str * comment) {
00499    struct carrier_data_t * carrier_data = NULL;
00500    struct domain_data_t * domain_data = NULL;
00501    LM_INFO("adding prefix %.*s, reply code %.*s\n", scan_prefix->len, scan_prefix->s, reply_code->len, reply_code->s);
00502       
00503    if (reply_code->len!=3) {
00504       LM_ERR("invalid reply_code '%.*s'!\n", reply_code->len, reply_code->s);
00505       return -1;
00506    }
00507    
00508    if ((carrier_data = get_carrier_data(rd, carrier_id)) == NULL) {
00509       LM_ERR("could not retrieve carrier data\n");
00510       return -1;
00511    }
00512    
00513    if ((domain_data = get_domain_data_or_add(rd, carrier_data, domain_id)) == NULL) {
00514       LM_ERR("could not retrieve domain data\n");
00515       return -1;
00516    }
00517 
00518    LM_INFO("found carrier and domain, now adding failure route\n");
00519    return add_failure_route_to_tree(domain_data->failure_tree, scan_prefix, scan_prefix, host, reply_code,
00520          flags, mask, next_domain_id, comment);
00521 }
00522 
00523 
00524 static int fixup_rule_backup(struct route_flags * rf, struct route_rule * rr){
00525    struct route_rule_p_list * rl;
00526    if(!rr->status && rr->backup){
00527       if((rr->backup->rr = find_rule_by_hash(rf, rr->backup->hash_index)) == NULL){
00528          LM_ERR("didn't find backup route\n");
00529          return -1;
00530       }
00531    }
00532    rl = rr->backed_up;
00533    while(rl){
00534       if((rl->rr = find_rule_by_hash(rf, rl->hash_index)) == NULL){
00535          LM_ERR("didn't find backed up route\n");
00536          return -1;
00537       }
00538       rl = rl->next;
00539    }
00540    return 0;
00541 }
00542 
00543 
00544 /**
00545  * Does the work for rule_fixup recursively.
00546  * First, it tries to set a pointer the rules with an existing hash index
00547  * at the marching array index. Afterward, remaining rules are populated
00548  * with incrementing hash indices.
00549  *
00550  * @param node the prefix tree node to be fixed up
00551  *
00552  * @return 0 on success, -1 on failure
00553  */
00554 static int rule_fixup_recursor(struct dtrie_node_t *node) {
00555    struct route_rule * rr;
00556    struct route_flags * rf;
00557    int i, p_dice, ret = 0;
00558 
00559    for (rf=(struct route_flags *)(node->data); rf!=NULL; rf=rf->next) {
00560       p_dice = 0;
00561       if (rf->rule_list) {
00562          rr = rf->rule_list;
00563          rf->rule_num = 0;
00564          while (rr) {
00565             rf->rule_num++;
00566             rf->dice_max += rr->prob * DICE_MAX;
00567             rr = rr->next;
00568          }
00569          rr = rf->rule_list;
00570          while (rr) {
00571             rr->dice_to = (rr->prob * DICE_MAX) + p_dice;
00572             p_dice = rr->dice_to;
00573             rr = rr->next;
00574          }
00575          
00576          if (rf->rule_num != rf->max_targets) {
00577             LM_ERR("number of rules(%i) differs from max_targets(%i), maybe your config is wrong?\n", rf->rule_num, rf->max_targets);
00578             return -1;
00579          }
00580          if(rf->rules) {
00581             shm_free(rf->rules);
00582             rf->rules = NULL;
00583          }
00584          if ((rf->rules = shm_malloc(sizeof(struct route_rule *) * rf->rule_num)) == NULL) {
00585             SHM_MEM_ERROR;
00586             return -1;
00587          }
00588          memset(rf->rules, 0, sizeof(struct route_rule *) * rf->rule_num);
00589          for (rr = rf->rule_list; rr; rr = rr->next) {
00590             if (rr->hash_index) {
00591                if (rr->hash_index > rf->rule_num) {
00592                   LM_ERR("too large hash index %i, max is %i\n", rr->hash_index, rf->rule_num);
00593                   shm_free(rf->rules);
00594                   return -1;
00595                }
00596                if (rf->rules[rr->hash_index - 1]) {
00597                   LM_ERR("duplicate hash index %i\n", rr->hash_index);
00598                   shm_free(rf->rules);
00599                   return -1;
00600                }
00601                rf->rules[rr->hash_index - 1] = rr;
00602                LM_INFO("rule with host %.*s hash has hashindex %i.\n", rr->host.len, rr->host.s, rr->hash_index);
00603             }
00604          }
00605          
00606          rr = rf->rule_list;
00607          i=0;
00608          while (rr && i < rf->rule_num) {
00609             if (!rr->hash_index) {
00610                if (rf->rules[i]) {
00611                   i++;
00612                } else {
00613                   rf->rules[i] = rr;
00614                   rr->hash_index = i + 1;
00615                   LM_INFO("hashless rule with host %.*s hash, hash_index %i\n", rr->host.len, rr->host.s, i+1);
00616                   rr = rr->next;
00617                }
00618             } else {
00619                rr = rr->next;
00620             }
00621          }
00622          if (rr) {
00623             LM_ERR("Could not populate rules: rr: %p\n", rr);
00624             return -1;
00625          }
00626          for(i=0; i<rf->rule_num; i++){
00627             ret += fixup_rule_backup(rf, rf->rules[i]);
00628          }
00629       }
00630    }
00631 
00632    for (i=0; i<cr_match_mode; i++) {
00633       if (node->child[i]) {
00634          ret += rule_fixup_recursor(node->child[i]);
00635       }
00636    }
00637 
00638    return ret;
00639 }
00640 
00641 
00642 /**
00643  * Fixes the route rules by creating an array for accessing
00644  * route rules by hash index directly
00645  *
00646  * @param rd route data to be fixed
00647  *
00648  * @return 0 on success, -1 on failure
00649  */
00650 int rule_fixup(struct route_data_t * rd) {
00651    int i,j;
00652    for (i=0; i<rd->carrier_num; i++) {
00653       for (j=0; j<rd->carriers[i]->domain_num; j++) {
00654          if (rd->carriers[i]->domains[j] && rd->carriers[i]->domains[j]->tree) {
00655             LM_INFO("fixing tree %.*s\n", rd->carriers[i]->domains[j]->name->len, rd->carriers[i]->domains[j]->name->s);
00656             if (rule_fixup_recursor(rd->carriers[i]->domains[j]->tree) < 0) {
00657                return -1;
00658             }
00659          } else {
00660             LM_NOTICE("empty tree at [%i][%i]\n", i, j);
00661          }
00662       }
00663    }
00664    return 0;
00665 }

Generated on Mon May 21 18:00:25 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6