瀏覽代碼

- finish refactoring of carrierroute module
- replace O(n) matching logic for carrier and domain names with a efficient
binary search implementation
- use qsort and bsearch of glibc in most of the cases, where its possible
(basically all carrier/domain searches are O(log n) now, only when dynamic
strings for are used in the cfg, it needs to search the whole list)
- change carrier and domain names from string to integer, to allow the lookup
- instead of storing the carrier/domain name string in the memory structure,
a pointer to the name is used to save space
- get rid of this internal ID vs. external ID stuff, we use now only one
- rename the route_tree table to carrier_name
- add a new table domain_name, to hold the domain names (like route_tree tbl)
- adapt tests for the new or changed functionality
- extend documentation with a paragraph about the used matching logic
- Credits for this work belongs to Hardy Kahl, hardy dot kahl at 1und1 dot de
- fix a few errors in the postgres cr test, fix a few doxygen statements
- move some parts of log messages to DBG log level
- update documentation and database schemes


git-svn-id: https://openser.svn.sourceforge.net/svnroot/openser/trunk@5189 689a6050-402a-0410-94f2-e92a70836424

Henning Westerholt 17 年之前
父節點
當前提交
8efd99bc89

+ 128 - 52
modules/carrierroute/README

@@ -96,9 +96,12 @@ Henning Westerholt
         2.23. carrierfailureroute_mask_col (string)
         2.24. carrierfailureroute_next_domain_col (string)
         2.25. carrierfailureroute_description_col (string)
-        2.26. route_tree_table (String)
-        2.27. route_tree_id_col (string)
-        2.28. route_tree_carrier_col (string)
+        2.26. carrier_name_table (String)
+        2.27. carrier_name_id_col (string)
+        2.28. carrier_name_carrier_col (string)
+        2.29. domain_name_table (String)
+        2.30. domain_name_id_col (string)
+        2.31. domain_name_domain_col (string)
 
    List of Examples
 
@@ -127,7 +130,7 @@ Henning Westerholt
    1.21. Example database content - more complex
           carrierfailureroute table
 
-   1.22. Example database content - route_tree table
+   1.22. Example database content - carrier_name table
    1.23. Necessary extensions for the user table
    2.1. Set db_url parameter
    2.2. Set carrierroute_table parameter
@@ -154,9 +157,12 @@ Henning Westerholt
    2.23. Set carrierfailureroute_mask_col parameter
    2.24. Set carrierfailureroute_next_domain_col parameter
    2.25. Set carrierfailureroute_description_col parameter
-   2.26. Set route_tree_table parameter
-   2.27. Set route_tree_id_col parameter
-   2.28. Set route_tree_carrier_col parameter
+   2.26. Set carrier_name_table parameter
+   2.27. Set carrier_name_id_col parameter
+   2.28. Set carrier_name_carrier_col parameter
+   2.29. Set domain_name_table parameter
+   2.30. Set domain_name_id_col parameter
+   2.31. Set domain_name_domain_col parameter
 
 Chapter 1. Admin Guide
 
@@ -183,10 +189,13 @@ Chapter 1. Admin Guide
 
    This modules scales up to more than a few million users, and is
    able to handle more than several hundred thousand routing table
-   entries. It should be able to handle more, but this is not that
-   much tested at the moment. In load balancing scenarios the
-   usage of the config file mode is recommended, to avoid the
-   additional complexity that the database driven routing creates.
+   entries. We recieved reports of some setups that used more than
+   a million routing table entries. It also supports a large
+   number of carriers and domains which can be efficiently looked
+   up in most of the cases (see below for more informations). In
+   load balancing scenarios the usage of the config file mode is
+   recommended, to avoid the additional complexity that the
+   database driven routing creates.
 
    Routing tables can be reloaded and edited (in config file mode)
    with the MI interface, the config file is updated according the
@@ -201,6 +210,17 @@ Chapter 1. Admin Guide
    sections. For user based routing or LCR you should use the
    database mode.
 
+   In database mode, this module supports names and IDs for the
+   carriers and domains. When using IDs for the routing functions,
+   efficient binary search is used to find the needed data
+   structures. If you are using constant strings as parameter,
+   these will be converted to IDs during the fixup procedure.
+   However, if you are using AVPs as parameter and they contain
+   strings, this cannot be converted to IDs during the fixup
+   procedure. In that case linear search is performed to find the
+   needed data structures. So from a performance point of view it
+   is better to pass only IDs in AVPs to the routing functions.
+
    Basically this module could be used as an replacement for the
    lcr and the dispatcher module, if you have certain performance,
    flexibility and/or integration requirements that these modules
@@ -800,8 +820,8 @@ domain register {
 | 9  |       2 |      0 | 49          |     0 |  0.5 | de-2.carrier2 |
 | 10 |       2 |      0 |             |     0 |    1 | gw.carrier2   |
 | 11 |       2 |      1 | 49          |     0 |    1 | gw.carrier2   |
-| 12 |       3 |  start | 49          |     0 |    1 | de-gw.default |
-| 13 |       3 |  start |             |     0 |    1 | gw.default    |
+| 12 |       3 |      8 | 49          |     0 |    1 | de-gw.default |
+| 13 |       3 |      8 |             |     0 |    1 | gw.default    |
 +----+---------+--------+-------------+-------+------+---------------+
 ...
 
@@ -854,7 +874,7 @@ domain register {
 |  1 |      99 |           | 408        |    16 |   16 |             |
 |  2 |      99 | gw1       | 404        |     0 |    0 | 100         |
 |  3 |      99 | gw2       | 50.        |     0 |    0 | 100         |
-|  4 |      99 |           | 404        |  2048 | 2112 | asterisk-1  |
+|  4 |      99 |           | 404        |  2048 | 2112 | 101         |
 +----+---------+-----------+------------+-------+------+-------------+
 ...
 
@@ -872,7 +892,7 @@ domain register {
    holds domain entries for this routing rules. Not all table
    colums are show here for brevity.
 
-   Example 1.22. Example database content - route_tree table
+   Example 1.22. Example database content - carrier_name table
 ...
 +----+----------+
 | id | carrier  |
@@ -926,7 +946,7 @@ modparam("carrierroute", "carrierroute_table", "carrierroute")
 
 2.3. carrierroute_id_col (string)
 
-   unique ID
+   Name of the column contains the unique identifier of a route.
 
    Example 2.3. Set carrierroute_id_col parameter
 ...
@@ -944,8 +964,10 @@ modparam("carrierroute", "carrierroute_carrier_col", "carrier")
 
 2.5. carrierroute_domain_col (string)
 
-   This column contains the route domain. Additional domains could
-   be used for example as fallback.
+   This column contains the routing domain id. You can define
+   several routing domains to have different routing rules. Maybe
+   you use domain 0 for normal routing and domain 1 if domain 0
+   failed.
 
    Example 2.5. Set carrierroute_domain_col parameter
 ...
@@ -954,8 +976,12 @@ modparam("carrierroute", "carrierroute_domain_col", "domain")
 
 2.6. carrierroute_scan_prefix_col (string)
 
-   This column contains the scan prefix, which define the matching
-   portion of a phone number.
+   Name of column contains the scan prefixes. Scan prefixes define
+   the matching portion of a phone number, e.g. when we have the
+   scan prefixes 49721 and 49, the called number is 49721913740,
+   it matches 49721, because the longest match is taken. If no
+   prefix matches, the number is not routed. To prevent this, an
+   empty prefix value of could be added.
 
    Example 2.6. Set carrierroute_scan_prefix_col parameter
 ...
@@ -983,9 +1009,18 @@ modparam("carrierroute", "carrierroute_mask_col", "mask")
 
 2.9. carrierroute_prob_col (string)
 
-   Name of column containing the probability. The probability
-   value is used to distribute the traffic between several
-   gateways.
+   Name of column contains the probability. The probability value
+   is used to distribute the traffic between several gateways.
+   Let's say 70 % of the traffic shall be routed to gateway A, the
+   other 30 % shall be routed to gateway B, we define a rule for
+   gateway A with a prob value of 0.7 and a rule for gateway B
+   with a prob value of 0.3. If all probabilities for a given
+   prefix, tree and domain don't add to 100%, the prefix values
+   will be adjusted according the given prob values. E.g. if three
+   hosts with prob values of 0.5, 0.5 and 0.4 are defined, the
+   resulting probabilities are 35.714, 35.714 and 28.571%. But its
+   better to choose meaningful values in the first place because
+   of clarity.
 
    Example 2.9. Set carrierroute_prob_col parameter
 ...
@@ -994,9 +1029,8 @@ modparam("carrierroute", "carrierroute_prob_col", "prob")
 
 2.10. carrierroute_strip_col (string)
 
-   Name of the column containing the number of digits to be
-   stripped of the userpart of an URI before prepending
-   rewrite_prefix.
+   Name of the column contains the number of digits to be stripped
+   of the userpart of an URI before prepending rewrite_prefix.
 
    Example 2.10. Set carrierroute_strip_col parameter
 ...
@@ -1005,8 +1039,10 @@ modparam("carrierroute", "carrierroute_strip_col", "strip")
 
 2.11. carrierroute_rewrite_host_col (string)
 
-   Name of column containing rewrite prefixes. Here you can define
-   a rewrite prefix for the localpart of the SIP URI.
+   Name of column contains the rewrite prefixes. Here you can
+   define a rewrite prefix for the localpart of the SIP URI. An
+   empty field represents a blacklist entry, anything else is put
+   as domain part into the Request URI of the SIP message.
 
    Example 2.11. Set carrierroute_rewrite_host_col parameter
 ...
@@ -1016,7 +1052,8 @@ modparam("carrierroute", "carrierroute_rewrite_host_col", "rewrite_host"
 
 2.12. carrierroute_rewrite_prefix_col (string)
 
-   Rewrite prefix for the localpart of the SIP URI.
+   Name of column contains the rewrite prefixes. Here you can
+   define a rewrite prefix for the localpart of the SIP URI.
 
    Example 2.12. Set carrierroute_rewrite_prefix_col parameter
 ...
@@ -1026,7 +1063,8 @@ fix")
 
 2.13. carrierroute_rewrite_suffix_col (string)
 
-   Rewrite suffix for the localpart of the SIP URI.
+   Name of column contains the rewrite suffixes. Here you can
+   define a rewrite suffix for the localpart of the SIP URI.
 
    Example 2.13. Set carrierroute_rewrite_suffix_col parameter
 ...
@@ -1037,7 +1075,8 @@ fix")
 2.14. carrierroute_description_col (string)
 
    A comment for the route entry, useful for larger routing
-   tables.
+   tables. The comment is also displayed by the fifo cmd
+   "cr_dump_routes".
 
    Example 2.14. Set carrierroute_description_col parameter
 ...
@@ -1059,7 +1098,7 @@ te")
 
 2.16. carrierfailureroute_id_col (string)
 
-   unique ID
+   This column contains the unique identifier of a failure route.
 
    Example 2.16. Set carrierfailureroute_id_col parameter
 ...
@@ -1077,8 +1116,10 @@ modparam("carrierroute", "carrierfailureroute_carrier_col", "carrier")
 
 2.18. carrierfailureroute_domain_col (string)
 
-   This column contains the route domain. Additional domains could
-   be used for example as fallback.
+   This column contains the routing domain id. You can define
+   several routing domains to have different routing rules. Maybe
+   you use domain 0 for normal routing and domain 1 if domain 0
+   failed.
 
    Example 2.18. Set carrierfailureroute_domain_col parameter
 ...
@@ -1087,8 +1128,12 @@ modparam("carrierroute", "carrierfailureroute_domain_col", "domain")
 
 2.19. carrierfailureroute_scan_prefix_col (string)
 
-   This column contains the scan prefix, which define the matching
-   portion of a phone number.
+   Name of column contains the the scan prefixes. Scan prexies
+   define the matching portion of a phone number, e.g. we have the
+   scan prefixes 49721 and 49, the called number is 49721913740,
+   it matches 49721, because the longest match is taken. If no
+   prefix matches, the number is not failure routed. To prevent
+   this, an empty prefix value of could be added.
 
    Example 2.19. Set carrierfailureroute_scan_prefix_col parameter
 ...
@@ -1098,8 +1143,8 @@ efix")
 
 2.20. carrierfailureroute_host_name_col (string)
 
-   This column contains the routing destination used for rule
-   matching.
+   Name of the column containing the host name of the last routing
+   destination, using for rules matching.
 
    Example 2.20. Set carrierfailureroute_host_name_col parameter
 ...
@@ -1138,8 +1183,8 @@ modparam("carrierroute", "carrierfailureroute_mask_col", "mask")
 
 2.24. carrierfailureroute_next_domain_col (string)
 
-   This column contains the route domain that should be used for
-   the next routing attempt.
+   This column contains the route domain id that should be used
+   for the next routing attempt.
 
    Example 2.24. Set carrierfailureroute_next_domain_col parameter
 ...
@@ -1158,31 +1203,62 @@ modparam("carrierroute", "carrierfailureroute_description_col", "descrip
 tion")
 ...
 
-2.26. route_tree_table (String)
+2.26. carrier_name_table (String)
 
-   Name of the route_tree table for the carrierroute module.
+   Name of the carrier_name table for the carrierroute module.
 
-   Default value is "route_tree".
+   Default value is "carrier_name".
 
-   Example 2.26. Set route_tree_table parameter
+   Example 2.26. Set carrier_name_table parameter
 ...
-modparam("carrierroute", "route_tree_table", "route_tree")
+modparam("carrierroute", "carrier_name_table", "carrier_name")
 ...
 
-2.27. route_tree_id_col (string)
+2.27. carrier_name_id_col (string)
 
-   unique ID
+   Name of the column containing the unique identifier of a
+   carrier.
 
-   Example 2.27. Set route_tree_id_col parameter
+   Example 2.27. Set carrier_name_id_col parameter
 ...
-modparam("carrierroute", "route_tree_id_col", "id")
+modparam("carrierroute", "carrier_name_id_col", "id")
 ...
 
-2.28. route_tree_carrier_col (string)
+2.28. carrier_name_carrier_col (string)
 
    This column contains the carrier name.
 
-   Example 2.28. Set route_tree_carrier_col parameter
+   Example 2.28. Set carrier_name_carrier_col parameter
+...
+modparam("carrierroute", "carrier_name_carrier_col", "carrier")
+...
+
+2.29. domain_name_table (String)
+
+   Name of the domain_name table for the carrierroute module.
+
+   Default value is "domain_name".
+
+   Example 2.29. Set domain_name_table parameter
+...
+modparam("carrierroute", "domain_name_table", "domain_name")
+...
+
+2.30. domain_name_id_col (string)
+
+   Name of the column containing the unique identifier of a
+   domain.
+
+   Example 2.30. Set domain_name_id_col parameter
+...
+modparam("carrierroute", "domain_name_id_col", "id")
+...
+
+2.31. domain_name_domain_col (string)
+
+   This column contains the domain name.
+
+   Example 2.31. Set domain_name_domain_col parameter
 ...
-modparam("carrierroute", "route_tree_carrier_col", "carrier")
+modparam("carrierroute", "domain_name_domain_col", "domain")
 ...

+ 4 - 4
modules/carrierroute/carrierroute.c

@@ -98,10 +98,12 @@ static param_export_t params[]= {
 	carrierroute_DB_URL
 	carrierroute_DB_TABLE
 	carrierfailureroute_DB_TABLE
-	route_tree_DB_TABLE
+	carrier_name_DB_TABLE
+	domain_name_DB_TABLE
 	carrierroute_DB_COLS
 	carrierfailureroute_DB_COLS
-	route_tree_DB_COLS
+	carrier_name_DB_COLS
+	domain_name_DB_COLS
 	{"subscriber_table",       STR_PARAM, &subscriber_table.s },
 	{"subscriber_user_col",    STR_PARAM, &subscriber_username_col.s },
 	{"subscriber_domain_col",  STR_PARAM, &subscriber_domain_col.s },
@@ -234,6 +236,4 @@ static void mod_destroy(void) {
 		carrierroute_db_close();
 	}
 	destroy_route_data();
-	destroy_domain_map();
-	destroy_carrier_map();
 }

+ 71 - 385
modules/carrierroute/cr_carrier.c

@@ -27,201 +27,37 @@
  * - Module; \ref carrierroute
  */
 
+#include <stdlib.h>
 #include "../../mem/shm_mem.h"
 #include "../../ut.h"
 #include "cr_carrier.h"
-#include "carrierroute.h"
-#include "cr_rule.h"
-#include "cr_config.h"
-#include "cr_db.h"
-#include "cr_map.h"
 #include "cr_domain.h"
-
-
-/**
- * Adds the given route information to the routing domain identified by
- * domain. scan_prefix identifies the number for which the information
- * is and the rewrite_* parameters define what to do in case of a match.
- * prob gives the probability with which this rule applies if there are
- * more than one for a given prefix.
- *
- * @param rd the route data to which the route shall be added
- * @param carrier_id the carrier id of the route to be added
- * @param domain the routing domain of the new route
- * @param scan_prefix the number prefix
- * @param flags user defined flags
- * @param mask mask for user defined flags
- * @param max_targets the number of targets
- * @param prob the weight of the rule
- * @param rewrite_hostpart the rewrite_host of the rule
- * @param strip the number of digits to be stripped off userpart before prepending prefix
- * @param rewrite_local_prefix the rewrite prefix
- * @param rewrite_local_suffix the rewrite suffix
- * @param status the status of the rule
- * @param hash_index the hash index of the rule
- * @param backup indicates if the route is backed up by another. only 
-                 useful if status==0, if set, it is the hash value
-                 of another rule
- * @param backed_up an -1-termintated array of hash indices of the route 
-                    for which this route is backup
- * @param comment a comment for the route rule
- *
- * @return 0 on success, -1 on error in which case it LOGs a message.
- */
-int add_route(struct route_data_t * rd, int carrier_id,
-		const str * domain, const str * scan_prefix, flag_t flags, flag_t mask, int max_targets,
-		double prob, const str * rewrite_hostpart, int strip,
-		const str * rewrite_local_prefix, const str * rewrite_local_suffix,
-		int status, int hash_index, int backup, int * backed_up, const str * comment) {
-	struct carrier_data_t * carrier_data = NULL;
-	struct domain_data_t * domain_data = NULL;
-	LM_INFO("adding prefix %.*s, prob %f\n", scan_prefix->len, scan_prefix->s, prob);
-
-	if ((carrier_data = get_carrier_data(rd, carrier_id)) == NULL) {
-		LM_ERR("could not retrieve carrier data\n");
-		return -1;
-	}
-
-	if ((domain_data = get_domain_data_by_name(carrier_data,domain)) == NULL) {
-		LM_ERR("could not retrieve domain data\n");
-		return -1;
-	}
-	LM_INFO("found route, now adding\n");
-	return add_route_to_tree(domain_data->tree, scan_prefix, flags, mask, scan_prefix, max_targets, prob, rewrite_hostpart,
-	                         strip, rewrite_local_prefix, rewrite_local_suffix, status,
-	                         hash_index, backup, backed_up, comment);
-}
-
-
-/**
- * Adds the given failure route information to the failure routing domain identified by
- * domain. scan_prefix, host, reply_code and flags identifies the number for which
- * the information is and the next_domain parameter defines where to continue routing
- * in case of a match.
- *
- * @param rd the route data to which the route shall be added
- * @param carrier_id the carrier id of the route to be added
- * @param domain the routing domain of the new route
- * @param scan_prefix the number prefix
- * @param host the hostname last tried
- * @param reply_code the reply code 
- * @param flags user defined flags
- * @param mask for user defined flags
- * @param next_domain continue routing with this domain
- * @param comment a comment for the failure route rule
- *
- * @return 0 on success, -1 on error in which case it LOGs a message.
- */
-int add_failure_route(struct route_data_t * rd, int carrier_id, const str * domain,
-		const str * scan_prefix, const str * host, const str * reply_code,
-		flag_t flags, flag_t mask, const str * next_domain, const str * comment) {
-	int next_domain_id;
-	struct carrier_data_t * carrier_data = NULL;
-	struct domain_data_t * domain_data = NULL;
-	LM_INFO("adding prefix %.*s, reply code %.*s\n", scan_prefix->len, scan_prefix->s, reply_code->len, reply_code->s);
-		
-	if (reply_code->len!=3) {
-		LM_ERR("invalid reply_code '%.*s'!\n", reply_code->len, reply_code->s);
-		return -1;
-	}
-	
-	if ((carrier_data = get_carrier_data(rd, carrier_id)) == NULL) {
-		LM_ERR("could not retrieve carrier data\n");
-		return -1;
-	}
-	
-	if ((domain_data = get_domain_data_by_name(carrier_data, domain)) == NULL) {
-		LM_ERR("could not retrieve domain data\n");
-		return -1;
-	}
-
-	if ((next_domain_id = add_domain(next_domain)) < 0) {
-		LM_ERR("add_domain failed\n");
-		return -1;
-	}
-	
-	LM_INFO("found failure route, now adding\n");
-	return add_failure_route_to_tree(domain_data->failure_tree, scan_prefix, scan_prefix, host, reply_code,
-			flags, mask, next_domain_id, comment);
-}
-
-
-/**
- * adds a carrier_data struct for given carrier
- *
- * @param rd route data to be searched
- * @param carrier the name of desired carrier
- * @param carrier_id the id of the carrier
- * @param domains number of domains for that carrier
- *
- * @return a pointer to the root node of the desired routing tree,
- * NULL on failure
- */
-struct carrier_data_t * add_carrier_data(struct route_data_t * rd, const str * carrier, int carrier_id, int domains) {
-	int i, index;
-	if (!rd) {
-		LM_ERR("NULL pointer in parameter\n");
-		return NULL;
-	}
-	LM_INFO("add carrier %.*s\n", carrier->len, carrier->s);
-	for (i=0; i<rd->carrier_num; i++) {
-		if (rd->carriers[i]) {
-			if (rd->carriers[i]->id == carrier_id) {
-				LM_INFO("found carrier %i: %.*s\n", rd->carriers[i]->id, rd->carriers[i]->name.len, rd->carriers[i]->name.s);
-				return rd->carriers[i];
-			}
-		}
-	}
-	LM_INFO("carrier %.*s not found, add it\n", carrier->len, carrier->s);
-	if ((index = add_carrier(carrier, carrier_id)) < 0) {
-		LM_ERR("could not add carrier\n");
-		return NULL;
-	}
-	if (index > rd->carrier_num) {
-		LM_ERR("weird: to large tree index\n");
-		return NULL;
-	}
-	if ((rd->carriers[index] = create_carrier_data(carrier, carrier_id, index, domains)) == NULL) {
-		return NULL;
-	}
-	rd->carriers[index]->index = index;
-	LM_INFO("created carrier data: %.*s, with id %i and %ld domains\n", 
-		rd->carriers[index]->name.len, rd->carriers[index]->name.s, rd->carriers[index]->id, 
-		(long)rd->carriers[index]->domain_num);
-	return rd->carriers[index];
-}
+#include "cr_map.h"
 
 
 /**
  * Create a new carrier_data struct in shared memory and set it up.
  *
- * @param carrier_name the name of the carrier
  * @param carrier_id id of carrier
- * @param index the index for that carrier
+ * @param carrier_name pointer to the name of the carrier
  * @param domains number of domains for that carrier
  *
  * @return a pointer to the newly allocated carrier data or NULL on
  * error, in which case it LOGs an error message.
  */
-struct carrier_data_t * create_carrier_data(const str *carrier_name, int carrier_id, int index, int domains) {
+struct carrier_data_t * create_carrier_data(int carrier_id, str *carrier_name, int domains) {
 	struct carrier_data_t * tmp;
 	if ((tmp = shm_malloc(sizeof(struct carrier_data_t))) == NULL) {
-		LM_ERR("out of shared memory\n");
+		SHM_MEM_ERROR;
 		return NULL;
 	}
 	memset(tmp, 0, sizeof(struct carrier_data_t));
-	if (shm_str_dup(&tmp->name, carrier_name)!=0) {
-		LM_ERR("cannot duplicate string\n");
-		shm_free(tmp);
-		return NULL;
-	}
 	tmp->id = carrier_id;
-	tmp->index = index;
+	tmp->name = carrier_name;
 	tmp->domain_num = domains;
 	if(domains > 0){
 		if ((tmp->domains = shm_malloc(sizeof(struct domain_data_t *) * domains)) == NULL) {
-			LM_ERR("out of shared memory\n");
-			shm_free(tmp->name.s);
+			SHM_MEM_ERROR;
 			shm_free(tmp);
 			return NULL;
 		}
@@ -232,253 +68,103 @@ struct carrier_data_t * create_carrier_data(const str *carrier_name, int carrier
 
 
 /**
- * returns the routing tree for the given domain, if domain's tree
- * doesnt exist, it will be created. If the trees are completely
- * filled and a not existing domain shall be added, an error is
- * returned
+ * Destroys the given carrier and frees the used memory.
  *
- * @param rd route data to be searched
- * @param carrier_id the id of the desired carrier
- *
- * @return a pointer to the root node of the desired routing tree,
- * NULL on failure
+ * @param carrier_data the structure to be destroyed.
  */
-struct carrier_data_t *get_carrier_data(struct route_data_t * rd, int carrier_id) {
-	int i;
-	if (!rd) {
-		LM_ERR("NULL pointer in parameter\n");
-		return NULL;
-	}
-	for (i=0; i<rd->carrier_num; i++) {
-		if (rd->carriers[i]->id == carrier_id) {
-			return rd->carriers[i];
-		}
-	}
-	return NULL;
-}
-
-
-static int add_domain_data(struct carrier_data_t * carrier_data, struct domain_data_t * domain_data) {
+void destroy_carrier_data(struct carrier_data_t *carrier_data) {
 	int i;
-	LM_INFO("tree %.*s has %ld trees\n",
-			carrier_data->name.len, carrier_data->name.s, (long)carrier_data->domain_num);
-	for (i=0; i<carrier_data->domain_num; i++) {
-		LM_DBG("tree %p", carrier_data->domains[i]);
-		if (carrier_data->domains[i] == 0) {
-			carrier_data->domains[i] = domain_data;
-			return 0;
+	if (carrier_data) {
+		if (carrier_data->domains != NULL) {
+			for (i=0; i<carrier_data->domain_num; i++) {
+				destroy_domain_data(carrier_data->domains[i]);
+			}
+			shm_free(carrier_data->domains);
 		}
+		shm_free(carrier_data);
 	}
-	return -1;
 }
 
 
 /**
- * Returns the domain data for the given name. If it doesnt exist,
- * it will be created. If the domain list is completely
- * filled and a not existing domain shall be added, an error is
- * returned
+ * Adds a domain_data struct to the given carrier data structure at the given index.
+ * Other etries are moved one position up to make space for the new one.
  *
- * @param carrier_data carrier data to be searched
- * @param domain the name of desired domain
+ * @param carrier_data the carrier data struct where domain_data should be inserted
+ * @param domain_data the domain data struct to be inserted
+ * @param index the index where to insert the domain_data structure in the domain array
  *
- * @return a pointer to the desired domain data, NULL on failure.
+ * @return 0 on success, -1 on failure
  */
-struct domain_data_t *get_domain_data_by_name(struct carrier_data_t * carrier_data, const str * domain) {
-	int i, id;
-	struct domain_data_t * domain_data = NULL;
-	if (!carrier_data) {
-		LM_ERR("NULL pointer in parameter\n");
-		return NULL;
-	}
-	for (i=0; i<carrier_data->domain_num; i++) {
-		if (carrier_data->domains[i] && carrier_data->domains[i]->name.s) {
-			if (str_strcmp(&carrier_data->domains[i]->name, domain) == 0) {
-				LM_INFO("found domain %.*s\n", carrier_data->domains[i]->name.len, carrier_data->domains[i]->name.s);
-				return carrier_data->domains[i];
-			}
-		}
-	}
-	LM_INFO("domain %.*s not found, add it\n", domain->len, domain->s);
-	if ((id = add_domain(domain)) < 0) {
-		LM_ERR("could not add domain\n");
-		return NULL;
-	}
-	if ((domain_data = create_domain_data(domain, id)) == NULL) {
-		return NULL;
+int add_domain_data(struct carrier_data_t * carrier_data, struct domain_data_t * domain_data, int index) {
+	LM_INFO("adding domain %d '%.*s' to carrier %d '%.*s'", domain_data->id, domain_data->name->len, domain_data->name->s, carrier_data->id, carrier_data->name->len, carrier_data->name->s);
+ 	LM_DBG("domain position %d (domain_num=%d, first_empty_domain=%d)", index, carrier_data->domain_num, carrier_data->first_empty_domain);
+
+	if ((index < 0) || (index > carrier_data->first_empty_domain)) {
+		LM_ERR("got invalid index during binary search\n");
+		return -1;
 	}
-	if (add_domain_data(carrier_data, domain_data) < 0) {
-		LM_ERR("couldn't add domain data\n");
-		destroy_domain_data(domain_data);
-		return NULL;
+		
+	if (carrier_data->first_empty_domain >= carrier_data->domain_num) {
+		LM_ERR("cannot add new domain '%.*s' into carrier '%.*s' - array already full\n", domain_data->name->len, domain_data->name->s, carrier_data->name->len, carrier_data->name->s);
+		return -1;
 	}
-	LM_INFO("created domain data: %.*s, with id %i\n", domain_data->name.len, domain_data->name.s, domain_data->id);
-	return domain_data;
-}
-
 
-/**
- * Returns the domain data for the given id.
- *
- * @param carrier_data carrier data to be searched
- * @param domain the name of desired domain
- *
- * @return a pointer to the desired domain data, NULL if not found.
- */
-struct domain_data_t * get_domain_data_by_id(struct carrier_data_t * carrier_data, int id) {
-	int i;
-	LM_DBG("searching in carrier %.*s, id %d\n", carrier_data->name.len, carrier_data->name.s, carrier_data->id);
-	for (i=0; i<carrier_data->domain_num; i++) {
-		if (carrier_data->domains[i]) {
-			LM_DBG("tree %.*s, domain %.*s : %i\n", carrier_data->name.len, carrier_data->name.s, carrier_data->domains[i]->name.len, carrier_data->domains[i]->name.s, carrier_data->domains[i]->id);
-			if (carrier_data->domains[i]->id == id) {
-				return carrier_data->domains[i];
-			}
-		}
+	if (index < carrier_data->first_empty_domain) {
+		/* move other entries one position up */
+		memmove(&carrier_data->domains[index+1], &carrier_data->domains[index], sizeof(struct domain_data_t *)*(carrier_data->first_empty_domain-index));
 	}
-	return NULL;
-}
-
+	carrier_data->domains[index] = domain_data;
+	carrier_data->first_empty_domain++;
 
-static int fixup_rule_backup(struct route_flags * rf, struct route_rule * rr){
-	struct route_rule_p_list * rl;
-	if(!rr->status && rr->backup){
-		if((rr->backup->rr = find_rule_by_hash(rf, rr->backup->hash_index)) == NULL){
-			LM_ERR("didn't find backup route\n");
-			return -1;
-		}
-	}
-	rl = rr->backed_up;
-	while(rl){
-		if((rl->rr = find_rule_by_hash(rf, rl->hash_index)) == NULL){
-			LM_ERR("didn't find backed up route\n");
-			return -1;
-		}
-		rl = rl->next;
-	}
 	return 0;
 }
 
 
 /**
- * Does the work for rule_fixup recursively.
- * First, it tries to set a pointer the rules with an existing hash index
- * at the marching array index. Afterward, remaining rules are populated
- * with incrementing hash indices.
+ * Returns the domain data for the given id by doing a binary search.
+ * @note The domain array must be sorted!
  *
- * @param node the prefix tree node to be fixed up
+ * @param carrier_data carrier data to be searched
+ * @param domain_id the id of desired domain
  *
- * @return 0 on success, -1 on failure
+ * @return a pointer to the desired domain data, NULL if not found.
  */
-static int rule_fixup_recursor(struct dtrie_node_t *node) {
-	struct route_rule * rr;
-	struct route_flags * rf;
-	int i, p_dice, ret = 0;
+struct domain_data_t * get_domain_data(struct carrier_data_t * carrier_data, int domain_id) {
+	struct domain_data_t **ret;
+	struct domain_data_t key;
+	struct domain_data_t *pkey = &key;
 
-	for (rf=(struct route_flags *)(node->data); rf!=NULL; rf=rf->next) {
-		p_dice = 0;
-		if (rf->rule_list) {
-			rr = rf->rule_list;
-			rf->rule_num = 0;
-			while (rr) {
-				rf->rule_num++;
-				rf->dice_max += rr->prob * DICE_MAX;
-				rr = rr->next;
-			}
-			rr = rf->rule_list;
-			while (rr) {
-				rr->dice_to = (rr->prob * DICE_MAX) + p_dice;
-				p_dice = rr->dice_to;
-				rr = rr->next;
-			}
-			
-			if (rf->rule_num != rf->max_targets) {
-				LM_ERR("number of rules(%i) differs from max_targets(%i), maybe your config is wrong?\n", rf->rule_num, rf->max_targets);
-				return -1;
-			}
-			if(rf->rules) {
-				shm_free(rf->rules);
-				rf->rules = NULL;
-			}
-			if ((rf->rules = shm_malloc(sizeof(struct route_rule *) * rf->rule_num)) == NULL) {
-				LM_ERR("out of shared memory\n");
-				return -1;
-			}
-			memset(rf->rules, 0, sizeof(struct route_rule *) * rf->rule_num);
-			for (rr = rf->rule_list; rr; rr = rr->next) {
-				if (rr->hash_index) {
-					if (rr->hash_index > rf->rule_num) {
-						LM_ERR("too large hash index %i, max is %i\n", rr->hash_index, rf->rule_num);
-						shm_free(rf->rules);
-						return -1;
-					}
-					if (rf->rules[rr->hash_index - 1]) {
-						LM_ERR("duplicate hash index %i\n", rr->hash_index);
-						shm_free(rf->rules);
-						return -1;
-					}
-					rf->rules[rr->hash_index - 1] = rr;
-					LM_INFO("rule with host %.*s hash has hashindex %i.\n", rr->host.len, rr->host.s, rr->hash_index);
-				}
-			}
-			
-			rr = rf->rule_list;
-			i=0;
-			while (rr && i < rf->rule_num) {
-				if (!rr->hash_index) {
-					if (rf->rules[i]) {
-						i++;
-					} else {
-						rf->rules[i] = rr;
-						rr->hash_index = i + 1;
-						LM_INFO("hashless rule with host %.*s hash hash_index %i\n", rr->host.len, rr->host.s, i+1);
-						rr = rr->next;
-					}
-				} else {
-					rr = rr->next;
-				}
-			}
-			if (rr) {
-				LM_ERR("Could not populate rules: rr: %p\n", rr);
-				return -1;
-			}
-			for(i=0; i<rf->rule_num; i++){
-				ret += fixup_rule_backup(rf, rf->rules[i]);
-			}
-		}
-	}
-
-	for (i=0; i<10; i++) {
-		if (node->child[i]) {
-			ret += rule_fixup_recursor(node->child[i]);
-		}
+	if (!carrier_data) {
+		LM_ERR("NULL pointer in parameter\n");
+		return NULL;
 	}
-
-	return ret;
+	key.id = domain_id;
+	ret = bsearch(&pkey, carrier_data->domains, carrier_data->domain_num, sizeof(carrier_data->domains[0]), compare_domain_data);
+	if (ret) return *ret;
+	return NULL;
 }
 
 
 /**
- * Fixes the route rules by creating an array for accessing
- * route rules by hash index directly
- *
- * @param rd route data to be fixed
+ * Compares the IDs of two carrier data structures.
+ * A NULL pointer is always greater than any ID.
  *
- * @return 0 on success, -1 on failure
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
  */
-int rule_fixup(struct route_data_t * rd) {
-	int i,j;
-	for (i=0; i<rd->carrier_num; i++) {
-		for (j=0; j<rd->carriers[i]->domain_num; j++) {
-			if (rd->carriers[i]->domains[j] && rd->carriers[i]->domains[j]->tree) {
-				LM_INFO("fixing tree %.*s\n", rd->carriers[i]->domains[j]->name.len, rd->carriers[i]->domains[j]->name.s);
-				if (rule_fixup_recursor(rd->carriers[i]->domains[j]->tree) < 0) {
-					return -1;
-				}
-			} else {
-				LM_NOTICE("empty tree at [%i][%i]\n", i, j);
-			}
+int compare_carrier_data(const void *v1, const void *v2) {
+  struct carrier_data_t *c1 = *(struct carrier_data_t * const *)v1;
+	struct carrier_data_t *c2 = *(struct carrier_data_t * const *)v2;
+	if (c1 == NULL) {
+		if (c2 == NULL) return 0;
+		else return 1;
+	}
+	else {
+		if (c2 == NULL) return -1;
+		else {
+			if (c1->id < c2->id) return -1;
+			else if (c1->id > c2->id) return 1;
+			else return 0;
 		}
 	}
-	return 0;
 }

+ 24 - 111
modules/carrierroute/cr_carrier.h

@@ -32,160 +32,73 @@
 
 #include <sys/types.h>
 #include "../../str.h"
-#include "../../locking.h"
-#include "../../flags.h"
-#include "cr_data.h"
 
 
 /**
  * The struct for a carrier.
  */
 struct carrier_data_t {
+	int id; /*!< id of the carrier */
+	str * name; /*!< name of the carrier. This points to the name in carrier_map to avoid duplication. */
 	struct domain_data_t ** domains; /*!< array of routing domains */
 	size_t domain_num; /*!< number of routing domains */
-	str name; /*!< name of the carrier */
-	int id; /*!< id of the carrier */
-	int index; /*!< index of the carrier */
+	size_t first_empty_domain; /*!< the index of the first empty entry in domains */
 };
 
 
 /**
  * Create a new carrier_data struct in shared memory and set it up.
  *
- * @param carrier_name the name of the carrier
  * @param carrier_id id of carrier
- * @param index the index for that carrier
+ * @param carrier_name pointer to the name of the carrier
  * @param domains number of domains for that carrier
  *
  * @return a pointer to the newly allocated carrier data or NULL on
  * error, in which case it LOGs an error message.
  */
-struct carrier_data_t * create_carrier_data(const str *carrier_name, int carrier_id, int index, int domains);
-
-
-/**
- * Adds the given route information to the routing domain identified by
- * domain. scan_prefix identifies the number for which the information
- * is and the rewrite_* parameters define what to do in case of a match.
- * prob gives the probability with which this rule applies if there are
- * more than one for a given prefix.
- *
- * @param rd the route data to which the route shall be added
- * @param carrier_id the carrier id of the route to be added
- * @param domain the routing domain of the new route
- * @param scan_prefix the number prefix
- * @param flags user defined flags
- * @param mask mask for user defined flags
- * @param max_targets the number of targets
- * @param prob the weight of the rule
- * @param strip the number of digits to be stripped off userpart before prepending prefix
- * @param rewrite_hostpart the rewrite_host of the rule
- * @param rewrite_local_prefix the rewrite prefix
- * @param rewrite_local_suffix the rewrite suffix
- * @param status the status of the rule
- * @param hash_index the hash index of the rule
- * @param backup indicates if the route is backed up by another. only
-                 useful if status==0, if set, it is the hash value
-                 of another rule
-  * @param backed_up an -1-termintated array of hash indices of the route
-                    for which this route is backup
- * @param comment a comment for the route rule
- *
- * @return 0 on success, -1 on error in which case it LOGs a message.
- */
-int add_route(struct route_data_t * rd, int carrier_id,
-		const str * domain, const str * scan_prefix, flag_t flags, flag_t mask, int max_targets,
-		double prob, const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix,
-		const str * rewrite_local_suffix, int status, int hash_index, int backup, int * backed_up,
-		const str * comment);
+struct carrier_data_t * create_carrier_data(int carrier_id, str *carrier_name, int domains);
 
 
 /**
- * Adds the given failure route information to the failure routing domain identified by
- * domain. scan_prefix, host, reply_code and flags identifies the number for which
- * the information is and the next_domain parameter defines where to continue routing
- * in case of a match.
- *
- * @param rd the route data to which the route shall be added
- * @param carrier_id the carrier id of the route to be added
- * @param domain the routing domain of the new route
- * @param scan_prefix the number prefix
- * @param host the hostname last tried
- * @param reply_code the reply code 
- * @param flags user defined flags
- * @param mask mask for user defined flags
- * @param next_domain continue routing with this domain
- * @param comment a comment for the failure route rule
+ * Destroys the given carrier and frees the used memory.
  *
- * @return 0 on success, -1 on error in which case it LOGs a message.
+ * @param carrier_data the structure to be destroyed.
  */
-int add_failure_route(struct route_data_t * rd, int carrier_id, const str * domain,
-		const str * scan_prefix, const str * host, const str * reply_code,
-		flag_t flags, flag_t mask, const str * next_domain, const str * comment);
+void destroy_carrier_data(struct carrier_data_t *carrier_data);
 
 
 /**
- * adds a carrier_data struct for given carrier
+ * Adds a domain_data struct to the given carrier data structure at the given index.
+ * Other etries are moved one position up to make space for the new one.
  *
- * @param rd route data to be searched
- * @param carrier the name of desired carrier
- * @param carrier_id the id of the carrier
- * @param domains number of domains for that carrier
+ * @param carrier_data the carrier data struct where domain_data should be inserted
+ * @param domain_data the domain data struct to be inserted
+ * @param index the index where to insert the domain_data structure in the domain array
  *
- * @return a pointer to the root node of the desired routing tree,
- * NULL on failure
- */
-struct carrier_data_t * add_carrier_data(struct route_data_t * rd, const str * carrier, int carrier_id, int domains);
-
-
-/**
- * returns the routing tree for the given domain, if domain's tree
- * doesnt exist, it will be created. If the trees are completely
- * filled and a not existing domain shall be added, an error is
- * returned
- *
- * @param carrier_id the id of the desired carrier
- * @param rd route data to be searched
- *
- * @return a pointer to the root node of the desired routing tree,
- * NULL on failure
- */
-struct carrier_data_t *get_carrier_data(struct route_data_t * rd, int carrier_id);
-
-
-/**
- * Returns the domain data for the given name. If it doesnt exist,
- * it will be created. If the domain list is completely
- * filled and a not existing domain shall be added, an error is
- * returned
- *
- * @param carrier_data carrier data to be searched
- * @param domain the name of desired domain
- *
- * @return a pointer to the desired domain data, NULL on failure.
+ * @return 0 on success, -1 on failure
  */
-struct domain_data_t *get_domain_data_by_name(struct carrier_data_t * carrier_data, const str * domain);
+int add_domain_data(struct carrier_data_t * carrier_data, struct domain_data_t * domain_data, int index);
 
 
 /**
- * Returns the domain data for the given id.
+ * Returns the domain data for the given id by doing a binary search.
+ * @note The domain array must be sorted!
  *
  * @param carrier_data carrier data to be searched
- * @param domain the name of desired domain
+ * @param domain_id the id of desired domain
  *
  * @return a pointer to the desired domain data, NULL if not found.
  */
-struct domain_data_t *get_domain_data_by_id(struct carrier_data_t * carrier_data, int id);
+struct domain_data_t *get_domain_data(struct carrier_data_t * carrier_data, int domain_id);
 
 
 /**
- * Fixes the route rules by creating an array for accessing
- * route rules by hash index directly
+ * Compares the IDs of two carrier data structures.
+ * A NULL pointer is always greater than any ID.
  *
- * @param rd route data to be fixed
- *
- * @return 0 on success, -1 on failure
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
  */
-int rule_fixup(struct route_data_t * rd);
+int compare_carrier_data(const void *v1, const void *v2);
+
 
 #endif

+ 67 - 8
modules/carrierroute/cr_config.c

@@ -31,6 +31,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include <stdlib.h>
 #include "../../mem/shm_mem.h"
 #include "../../mem/mem.h"
 #include "../../ut.h"
@@ -180,8 +181,10 @@ errout:
  */
 int load_config(struct route_data_t * rd) {
 	cfg_t * cfg = NULL;
-	int n, m, o, i, j, k,l, status, hash_index, max_targets, strip;
+	int m, o, i, j, k,l, status, hash_index, max_targets, strip;
 	cfg_t * d, * p, * t;
+	struct carrier_data_t * tmp_carrier_data;
+	int domain_id;
 	str domain, prefix, rewrite_prefix, rewrite_suffix, rewrite_host, comment;
 	double prob;
 	int * backed_up = NULL;
@@ -192,21 +195,68 @@ int load_config(struct route_data_t * rd) {
 		return -1;
 	}
 
+	rd->carrier_num = 1;
+	rd->first_empty_carrier = 0;
+	rd->domain_num = cfg_size(cfg, "domain");
+
 	if ((rd->carriers = shm_malloc(sizeof(struct carrier_data_t *))) == NULL) {
 		SHM_MEM_ERROR;
 		return -1;
 	}
 	memset(rd->carriers, 0, sizeof(struct carrier_data_t *));
 
-	rd->carrier_num = 1;
-	n = cfg_size(cfg, "domain");
-	if (add_carrier_data(rd, &default_tree, 1, n) == NULL) {
+	/* Create carrier map */
+	if ((rd->carrier_map = shm_malloc(sizeof(struct name_map_t))) == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memset(rd->carrier_map, 0, sizeof(struct name_map_t));
+	rd->carrier_map[0].id = 1;
+	rd->carrier_map[0].name.len = default_tree.len;
+	rd->carrier_map[0].name.s = shm_malloc(rd->carrier_map[0].name.len);
+	if (rd->carrier_map[0].name.s == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memcpy(rd->carrier_map[0].name.s, default_tree.s, rd->carrier_map[0].name.len);
+
+	/* Create domain map */
+	if ((rd->domain_map = shm_malloc(sizeof(struct name_map_t) * rd->domain_num)) == NULL) {
+		SHM_MEM_ERROR;
+		return -1;
+	}
+	memset(rd->domain_map, 0, sizeof(struct name_map_t) * rd->domain_num);
+	for (i=0; i<rd->domain_num; i++) {
+		d = cfg_getnsec(cfg, "domain", i);
+		domain.s = (char *)cfg_title(d);
+		if (domain.s==NULL) domain.s="";
+		domain.len = strlen(domain.s);
+		rd->domain_map[i].id = i+1;
+		rd->domain_map[i].name.len = domain.len;
+		rd->domain_map[i].name.s = shm_malloc(rd->domain_map[i].name.len);
+		if (rd->domain_map[i].name.s == NULL) {
+			SHM_MEM_ERROR;
+			return -1;
+		}
+		memcpy(rd->domain_map[i].name.s, domain.s, rd->domain_map[i].name.len);
+	}
+	/* sort domain map by id for faster access */
+	qsort(rd->domain_map, rd->domain_num, sizeof(rd->domain_map[0]), compare_name_map);
+
+	/* Create and insert carrier data structure */
+	tmp_carrier_data = create_carrier_data(1, &rd->carrier_map[0].name, rd->domain_num);
+	if (tmp_carrier_data == NULL) {
+		LM_ERR("can't create new carrier\n");
+		return -1;
+	}
+	if (add_carrier_data(rd, tmp_carrier_data) < 0) {
 		LM_ERR("couldn't add carrier data\n");
+		destroy_carrier_data(tmp_carrier_data);
 		return -1;
 	}
 
-	memset(rd->carriers[0]->domains, 0, sizeof(struct domain_data_t *) * n);
-	for (i = 0; i < n; i++) {
+	/* add all routes */
+	for (i = 0; i < rd->domain_num; i++) {
 		d = cfg_getnsec(cfg, "domain", i);
 		domain.s = (char *)cfg_title(d);
 		if (domain.s==NULL) domain.s="";
@@ -264,9 +314,18 @@ int load_config(struct route_data_t * rd) {
 				}
 				backup = cfg_getint(t, "backup");
 
+				domain_id = map_name2id(rd->domain_map, rd->domain_num, &domain);
+				if (domain_id < 0) {
+					LM_ERR("cannot find id for domain '%.*s'", domain.len, domain.s);
+					if (backed_up) {
+						pkg_free(backed_up);
+					}
+					return -1;
+				}
+
 				LM_INFO("adding route for prefix %.*s, to host %.*s, prob %f, backed up: %i, backup: %i\n",
 				    prefix.len, prefix.s, rewrite_host.len, rewrite_host.s, prob, backed_up_size, backup);
-				if (add_route(rd, 1, &domain, &prefix, 0, 0, max_targets, prob, &rewrite_host,
+				if (add_route(rd, 1, domain_id, &prefix, 0, 0, max_targets, prob, &rewrite_host,
 				              strip, &rewrite_prefix, &rewrite_suffix, status,
 				              hash_index, backup, backed_up, &comment) < 0) {
 					LM_INFO("Error while adding route\n");
@@ -386,7 +445,7 @@ int save_config(struct route_data_t * rd) {
 	i = 0;
 	if (rd->carrier_num>=1) {
 		for (j=0; j< rd->carriers[i]->domain_num; j++) {
-			fprintf(outfile, "domain %.*s {\n", rd->carriers[i]->domains[j]->name.len, rd->carriers[i]->domains[j]->name.s);
+			fprintf(outfile, "domain %.*s {\n", rd->carriers[i]->domains[j]->name->len, rd->carriers[i]->domains[j]->name->s);
 			if (save_route_data_recursor(rd->carriers[i]->domains[j]->tree, outfile) < 0) {
 				goto errout;
 			}

+ 451 - 39
modules/carrierroute/cr_data.c

@@ -27,6 +27,7 @@
  * - Module; \ref carrierroute
  */
 
+#include <stdlib.h>
 #include "../../mem/shm_mem.h"
 #include "cr_data.h"
 #include "carrierroute.h"
@@ -34,6 +35,7 @@
 #include "cr_db.h"
 #include "cr_carrier.h"
 #include "cr_domain.h"
+#include "cr_rule.h"
 
 
 /**
@@ -42,46 +44,19 @@
 struct route_data_t ** global_data = NULL;
 
 
-/**
- * Destroys a carrier
- *
- * @param tree route data to be destroyed
- */
-static void destroy_carrier_data(struct carrier_data_t * carrier_data) {
-	int i;
-
-	if (carrier_data == NULL) {
-		return;
-	}
-	if (carrier_data->domains != NULL) {
-		for (i = 0; i < carrier_data->domain_num; ++i) {
-			if (carrier_data->domains[i] != NULL) {
-				destroy_domain_data(carrier_data->domains[i]);
-			}
-		}
-		shm_free(carrier_data->domains);
-	}
-	if(carrier_data->name.s){
-		shm_free(carrier_data->name.s);
-	}
-	shm_free(carrier_data);
-	return;
-}
-
-
 static int carrier_data_fixup(struct route_data_t * rd){
 	int i;
 	str tmp;
 	tmp = default_tree;
-	rd->default_carrier_index = -1;
+	rd->default_carrier_id = -1;
 	for(i=0; i<rd->carrier_num; i++){
 		if(rd->carriers[i]){
-			if(str_strcmp(&(rd->carriers[i]->name), &tmp) == 0){
-				rd->default_carrier_index = i;
+			if(str_strcmp(rd->carriers[i]->name, &tmp) == 0){
+				rd->default_carrier_id = rd->carriers[i]->id;
 			}
 		}
 	}
-	if(rd->default_carrier_index < 0){
+	if(rd->default_carrier_id < 0){
 		LM_ERR("default_carrier not found\n");
 	}
 	return 0;
@@ -98,7 +73,7 @@ int init_route_data(void) {
 		global_data = (struct route_data_t **)
 		              shm_malloc(sizeof(struct route_data_t *));
 		if (global_data == NULL) {
-			LM_ERR("Out of shared memory before even doing anything.\n");
+			SHM_MEM_ERROR;
 			return -1;
 		}
 	}
@@ -140,11 +115,48 @@ void clear_route_data(struct route_data_t *data) {
 		}
 		shm_free(data->carriers);
 	}
+	if (data->carrier_map) {
+		for (i = 0; i < data->carrier_num; ++i) {
+			if (data->carrier_map[i].name.s) shm_free(data->carrier_map[i].name.s);
+		}
+		shm_free(data->carrier_map);
+	}
+	if (data->domain_map) {
+		for (i = 0; i < data->domain_num; ++i) {
+			if (data->domain_map[i].name.s) shm_free(data->domain_map[i].name.s);
+		}
+		shm_free(data->domain_map);
+	}
 	shm_free(data);
 	return;
 }
 
 
+/**
+ * adds a carrier_data struct for given carrier.
+ *
+ * @param rd route data to be searched
+ * @param carrier_data the carrier data struct to be inserted
+ *
+ * @return 0 on success, -1 on failure
+ */
+int add_carrier_data(struct route_data_t * rd, struct carrier_data_t * carrier_data) {
+	if (rd->first_empty_carrier >= rd->carrier_num) {
+		LM_ERR("carrier array already full");
+		return -1;
+	}
+
+	if (rd->carriers[rd->first_empty_carrier] != 0) {
+		LM_ERR("invalid pointer in first empty carrier entry");
+		return -1;
+	}
+
+	rd->carriers[rd->first_empty_carrier] = carrier_data;
+	rd->first_empty_carrier++;
+	return 0;
+}
+
+
 /**
  * Loads the routing data into the routing trees and sets the
  * global_data pointer to the new data. The old_data is removed
@@ -158,7 +170,7 @@ int reload_route_data(void) {
 	int i;
 
 	if ((new_data = shm_malloc(sizeof(struct route_data_t))) == NULL) {
-		LM_ERR("out of shared memory\n");
+		SHM_MEM_ERROR;
 		return -1;
 	}
 	memset(new_data, 0, sizeof(struct route_data_t));
@@ -167,32 +179,40 @@ int reload_route_data(void) {
 	case CARRIERROUTE_MODE_DB:
 		if (load_route_data_db(new_data) < 0) {
 			LM_ERR("could not load routing data\n");
-			return -1;
+			goto errout;
 		}
 		break;
 	case CARRIERROUTE_MODE_FILE:
 		if (load_config(new_data) < 0) {
 			LM_ERR("could not load routing data\n");
-			return -1;
+			goto errout;
 		}
 		break;
 	default:
 		LM_ERR("invalid mode");
-		return -1;
+		goto errout;
 	}
 	if (new_data == NULL) {
 		LM_ERR("loading routing data failed (NULL pointer)");
-		return -1;
+		goto errout;
+	}
+
+	/* sort carriers by id for faster access */
+	qsort(new_data->carriers, new_data->carrier_num, sizeof(new_data->carriers[0]), compare_carrier_data);
+
+	/* sort domains by id for faster access */
+	for (i=0; i<new_data->carrier_num; i++) {
+		qsort(new_data->carriers[i]->domains, new_data->carriers[i]->domain_num, sizeof(new_data->carriers[i]->domains[0]), compare_domain_data);
 	}
 
 	if (rule_fixup(new_data) < 0) {
 		LM_ERR("could not fixup rules\n");
-		return -1;
+		goto errout;
 	}
 
 	if (carrier_data_fixup(new_data) < 0){
 		LM_ERR("could not fixup trees\n");
-		return -1;
+		goto errout;
 	}
 
 	new_data->proc_cnt = 0;
@@ -211,6 +231,10 @@ int reload_route_data(void) {
 		clear_route_data(old_data);
 	}
 	return 0;
+
+ errout:
+	clear_route_data(new_data);
+	return -1;
 }
 
 
@@ -251,3 +275,391 @@ void release_data(struct route_data_t *data) {
 	--data->proc_cnt;
 	lock_release(&data->lock);
 }
+
+
+/**
+ * Returns the carrier data for the given id by doing a binary search.
+ * @note The carrier array must be sorted!
+ *
+ * @param rd route data to be searched
+ * @param carrier_id the id of the desired carrier
+ *
+ * @return a pointer to the desired carrier data, NULL if not found.
+ */
+struct carrier_data_t *get_carrier_data(struct route_data_t * rd, int carrier_id) {
+	struct carrier_data_t **ret;
+	struct carrier_data_t key;
+	struct carrier_data_t *pkey = &key;
+
+	if (!rd) {
+		LM_ERR("NULL pointer in parameter\n");
+		return NULL;
+	}
+	key.id = carrier_id;
+	ret = bsearch(&pkey, rd->carriers, rd->carrier_num, sizeof(rd->carriers[0]), compare_carrier_data);
+	if (ret) return *ret;
+	return NULL;
+}
+
+
+typedef int (*cmpfunc_t)(const void *v1, const void *v2);
+
+
+/**
+ * Implements a binary search algorithm using the function cmpfunc
+ * for comparison.
+ *
+ * @param base pointer to the beginning of the array
+ * @param len length of array
+ * @param elemsize size of array elements
+ * @param key pointer to the key we are looking for
+ * @param cmpfunc function to be used for comparison
+ * @param index  If index is not NULL it is set to:
+ *     -1 if an error occured,
+ *     the index of the first entry equal to v
+ *     or the index of the first entry greater than v in the case v was not found.
+ *   Be careful: The index returned can be greater than the length of the array!
+ *
+ * @return -1 on error, 0 if the value was not found, 1 if it was found.
+ */
+static int binary_search(void *base, unsigned int len, int elemsize, void *key, cmpfunc_t cmpfunc, int *index) {
+	int left, right, mid;
+
+	if (index) *index=-1;
+	if (!base) {
+		LM_ERR("NULL pointer in parameter\n");
+		return -1;
+	}
+	if (len == 0) {
+		if (index) *index=0;
+		return 0;
+	}
+
+	left=0;
+	right=len-1;
+	if (cmpfunc(base+elemsize*left, key) > 0) {
+		LM_DBG("not found (out of left bound)\n");
+		if (index) *index=0; /* not found, must be inserted at the beginning of array */
+		return 0;
+	}
+	if (cmpfunc(base+elemsize*right, key) < 0) {
+		LM_DBG("not found (out of right bound)\n");
+		if (index) *index=len; /* not found, must be inserted at the end of array */
+		return 0;
+	}
+
+	while (left < right) {
+		mid = left + ((right - left) / 2);
+		if (cmpfunc(base+elemsize*mid, key) < 0) left = mid + 1;
+		else right = mid;
+	}
+
+	/* left == right here! */
+	if (index) *index=left;
+	if (cmpfunc(base+elemsize*left, key) == 0) return 1;
+	else return 0;
+}
+
+
+/**
+ * Returns the domain data for the given id by doing a binary search.
+ * If not found, a new domain data structure is added.
+ *
+ * @param rd route data to used for name - id mapping
+ * @param carrier_data carrier data to be searched
+ * @param domain_id the id of desired domain
+ *
+ * @return a pointer to the desired domain data, NULL on error.
+ */
+static struct domain_data_t * get_domain_data_or_add(struct route_data_t * rd, struct carrier_data_t * carrier_data, int domain_id) {
+	struct domain_data_t key;
+	struct domain_data_t *pkey = &key;
+	struct domain_data_t *domain_data = NULL;
+	str *domain_name;
+	int i;
+	int res;
+
+	if ((!rd) || (!carrier_data)) {
+		LM_ERR("NULL pointer in parameter\n");
+		return NULL;
+	}
+
+	key.id = domain_id;
+	res = binary_search(carrier_data->domains, carrier_data->first_empty_domain, sizeof(struct domain_data_t *), &pkey, compare_domain_data, &i);
+	if (res<0) {
+		LM_ERR("error while searching for domain_id %d\n", domain_id);
+		return NULL;
+	}
+	else if (res>0) {
+		/* found domain id */
+		domain_data = carrier_data->domains[i];
+	}
+	else {
+		/* did not find domain id - insert new entry! */
+		if ((domain_name = map_id2name(rd->domain_map, rd->domain_num, domain_id)) == NULL) {
+			LM_ERR("could not find domain name for id %d\n", domain_id);
+			return NULL;
+		}
+		if ((domain_data = create_domain_data(domain_id, domain_name)) == NULL) {
+			LM_ERR("could not create new domain data\n");
+			return NULL;
+		}
+
+		/* keep the array sorted! */
+		if (add_domain_data(carrier_data, domain_data, i) < 0) {
+			LM_ERR("could not add domain data\n");
+			destroy_domain_data(domain_data);
+			return NULL;
+		}
+		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);
+	}
+
+	return domain_data;
+}
+
+
+/**
+ * Adds the given route information to the routing domain identified by
+ * domain. scan_prefix identifies the number for which the information
+ * is and the rewrite_* parameters define what to do in case of a match.
+ * prob gives the probability with which this rule applies if there are
+ * more than one for a given prefix.
+ *
+ * @param rd the route data to which the route shall be added
+ * @param carrier_id the carrier id of the route to be added
+ * @param domain_id the routing domain id of the new route
+ * @param scan_prefix the number prefix
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ * @param max_targets the number of targets
+ * @param prob the weight of the rule
+ * @param rewrite_hostpart the rewrite_host of the rule
+ * @param strip the number of digits to be stripped off userpart before prepending prefix
+ * @param rewrite_local_prefix the rewrite prefix
+ * @param rewrite_local_suffix the rewrite suffix
+ * @param status the status of the rule
+ * @param hash_index the hash index of the rule
+ * @param backup indicates if the route is backed up by another. only 
+                 useful if status==0, if set, it is the hash value
+                 of another rule
+ * @param backed_up an -1-termintated array of hash indices of the route 
+                    for which this route is backup
+ * @param comment a comment for the route rule
+ *
+ * @return 0 on success, -1 on error in which case it LOGs a message.
+ */
+int add_route(struct route_data_t * rd, int carrier_id,
+		int domain_id, const str * scan_prefix, flag_t flags, flag_t mask, int max_targets,
+		double prob, const str * rewrite_hostpart, int strip,
+		const str * rewrite_local_prefix, const str * rewrite_local_suffix,
+		int status, int hash_index, int backup, int * backed_up, const str * comment) {
+	struct carrier_data_t * carrier_data = NULL;
+	struct domain_data_t * domain_data = NULL;
+	LM_INFO("adding prefix %.*s, prob %f\n", scan_prefix->len, scan_prefix->s, prob);
+
+	if ((carrier_data = get_carrier_data(rd, carrier_id)) == NULL) {
+		LM_ERR("could not retrieve carrier data for carrier id %d\n", carrier_id);
+		return -1;
+	}
+
+	if ((domain_data = get_domain_data_or_add(rd, carrier_data, domain_id)) == NULL) {
+		LM_ERR("could not retrieve domain data\n");
+		return -1;
+	}
+
+	LM_INFO("found carrier and domain, now adding route\n");
+	return add_route_to_tree(domain_data->tree, scan_prefix, flags, mask, scan_prefix, max_targets, prob, rewrite_hostpart,
+	                         strip, rewrite_local_prefix, rewrite_local_suffix, status,
+	                         hash_index, backup, backed_up, comment);
+}
+
+
+/**
+ * Adds the given failure route information to the failure routing domain identified by
+ * domain. scan_prefix, host, reply_code and flags identifies the number for which
+ * the information is and the next_domain parameter defines where to continue routing
+ * in case of a match.
+ *
+ * @param rd the route data to which the route shall be added
+ * @param carrier_id the carrier id of the route to be added
+ * @param domain_id the routing domain id of the new route
+ * @param scan_prefix the number prefix
+ * @param host the hostname last tried
+ * @param reply_code the reply code 
+ * @param flags user defined flags
+ * @param mask for user defined flags
+ * @param next_domain_id continue routing with this domain id
+ * @param comment a comment for the failure route rule
+ *
+ * @return 0 on success, -1 on error in which case it LOGs a message.
+ */
+int add_failure_route(struct route_data_t * rd, int carrier_id, int domain_id,
+		const str * scan_prefix, const str * host, const str * reply_code,
+		flag_t flags, flag_t mask, int next_domain_id, const str * comment) {
+	struct carrier_data_t * carrier_data = NULL;
+	struct domain_data_t * domain_data = NULL;
+	LM_INFO("adding prefix %.*s, reply code %.*s\n", scan_prefix->len, scan_prefix->s, reply_code->len, reply_code->s);
+		
+	if (reply_code->len!=3) {
+		LM_ERR("invalid reply_code '%.*s'!\n", reply_code->len, reply_code->s);
+		return -1;
+	}
+	
+	if ((carrier_data = get_carrier_data(rd, carrier_id)) == NULL) {
+		LM_ERR("could not retrieve carrier data\n");
+		return -1;
+	}
+	
+	if ((domain_data = get_domain_data_or_add(rd, carrier_data, domain_id)) == NULL) {
+		LM_ERR("could not retrieve domain data\n");
+		return -1;
+	}
+
+	LM_INFO("found carrier and domain, now adding failure route\n");
+	return add_failure_route_to_tree(domain_data->failure_tree, scan_prefix, scan_prefix, host, reply_code,
+			flags, mask, next_domain_id, comment);
+}
+
+
+static int fixup_rule_backup(struct route_flags * rf, struct route_rule * rr){
+	struct route_rule_p_list * rl;
+	if(!rr->status && rr->backup){
+		if((rr->backup->rr = find_rule_by_hash(rf, rr->backup->hash_index)) == NULL){
+			LM_ERR("didn't find backup route\n");
+			return -1;
+		}
+	}
+	rl = rr->backed_up;
+	while(rl){
+		if((rl->rr = find_rule_by_hash(rf, rl->hash_index)) == NULL){
+			LM_ERR("didn't find backed up route\n");
+			return -1;
+		}
+		rl = rl->next;
+	}
+	return 0;
+}
+
+
+/**
+ * Does the work for rule_fixup recursively.
+ * First, it tries to set a pointer the rules with an existing hash index
+ * at the marching array index. Afterward, remaining rules are populated
+ * with incrementing hash indices.
+ *
+ * @param node the prefix tree node to be fixed up
+ *
+ * @return 0 on success, -1 on failure
+ */
+static int rule_fixup_recursor(struct dtrie_node_t *node) {
+	struct route_rule * rr;
+	struct route_flags * rf;
+	int i, p_dice, ret = 0;
+
+	for (rf=(struct route_flags *)(node->data); rf!=NULL; rf=rf->next) {
+		p_dice = 0;
+		if (rf->rule_list) {
+			rr = rf->rule_list;
+			rf->rule_num = 0;
+			while (rr) {
+				rf->rule_num++;
+				rf->dice_max += rr->prob * DICE_MAX;
+				rr = rr->next;
+			}
+			rr = rf->rule_list;
+			while (rr) {
+				rr->dice_to = (rr->prob * DICE_MAX) + p_dice;
+				p_dice = rr->dice_to;
+				rr = rr->next;
+			}
+			
+			if (rf->rule_num != rf->max_targets) {
+				LM_ERR("number of rules(%i) differs from max_targets(%i), maybe your config is wrong?\n", rf->rule_num, rf->max_targets);
+				return -1;
+			}
+			if(rf->rules) {
+				shm_free(rf->rules);
+				rf->rules = NULL;
+			}
+			if ((rf->rules = shm_malloc(sizeof(struct route_rule *) * rf->rule_num)) == NULL) {
+				SHM_MEM_ERROR;
+				return -1;
+			}
+			memset(rf->rules, 0, sizeof(struct route_rule *) * rf->rule_num);
+			for (rr = rf->rule_list; rr; rr = rr->next) {
+				if (rr->hash_index) {
+					if (rr->hash_index > rf->rule_num) {
+						LM_ERR("too large hash index %i, max is %i\n", rr->hash_index, rf->rule_num);
+						shm_free(rf->rules);
+						return -1;
+					}
+					if (rf->rules[rr->hash_index - 1]) {
+						LM_ERR("duplicate hash index %i\n", rr->hash_index);
+						shm_free(rf->rules);
+						return -1;
+					}
+					rf->rules[rr->hash_index - 1] = rr;
+					LM_INFO("rule with host %.*s hash has hashindex %i.\n", rr->host.len, rr->host.s, rr->hash_index);
+				}
+			}
+			
+			rr = rf->rule_list;
+			i=0;
+			while (rr && i < rf->rule_num) {
+				if (!rr->hash_index) {
+					if (rf->rules[i]) {
+						i++;
+					} else {
+						rf->rules[i] = rr;
+						rr->hash_index = i + 1;
+						LM_INFO("hashless rule with host %.*s hash, hash_index %i\n", rr->host.len, rr->host.s, i+1);
+						rr = rr->next;
+					}
+				} else {
+					rr = rr->next;
+				}
+			}
+			if (rr) {
+				LM_ERR("Could not populate rules: rr: %p\n", rr);
+				return -1;
+			}
+			for(i=0; i<rf->rule_num; i++){
+				ret += fixup_rule_backup(rf, rf->rules[i]);
+			}
+		}
+	}
+
+	for (i=0; i<10; i++) {
+		if (node->child[i]) {
+			ret += rule_fixup_recursor(node->child[i]);
+		}
+	}
+
+	return ret;
+}
+
+
+/**
+ * Fixes the route rules by creating an array for accessing
+ * route rules by hash index directly
+ *
+ * @param rd route data to be fixed
+ *
+ * @return 0 on success, -1 on failure
+ */
+int rule_fixup(struct route_data_t * rd) {
+	int i,j;
+	for (i=0; i<rd->carrier_num; i++) {
+		for (j=0; j<rd->carriers[i]->domain_num; j++) {
+			if (rd->carriers[i]->domains[j] && rd->carriers[i]->domains[j]->tree) {
+				LM_INFO("fixing tree %.*s\n", rd->carriers[i]->domains[j]->name->len, rd->carriers[i]->domains[j]->name->s);
+				if (rule_fixup_recursor(rd->carriers[i]->domains[j]->tree) < 0) {
+					return -1;
+				}
+			} else {
+				LM_NOTICE("empty tree at [%i][%i]\n", i, j);
+			}
+		}
+	}
+	return 0;
+}

+ 103 - 1
modules/carrierroute/cr_data.h

@@ -32,15 +32,21 @@
 
 #include <sys/types.h>
 #include "../../locking.h"
+#include "../../flags.h"
+#include "cr_map.h"
 
 
 /**
  * contains all routing data.
  */
 struct route_data_t {
+	struct name_map_t * carrier_map; /*!< holds the map between carrier names and numbers */
+	struct name_map_t * domain_map; /*!< holds the map between domain names and numbers */
 	struct carrier_data_t ** carriers; /*!< array of carriers */
 	size_t carrier_num; /*!< number of carriers */
-	int default_carrier_index;
+	size_t first_empty_carrier; /*!< the index of the first empty entry in carriers */
+	size_t domain_num; /*!< total number of different domains */
+	int default_carrier_id;
 	int proc_cnt; /*!< a ref counter for the shm data */
 	gen_lock_t lock; /*!< lock for ref counter updates */
 };
@@ -67,6 +73,17 @@ void destroy_route_data(void);
 void clear_route_data(struct route_data_t *data);
 
 
+/**
+ * adds a carrier_data struct for given carrier
+ *
+ * @param rd route data to be searched
+ * @param carrier_data the carrier data struct to be inserted
+ *
+ * @return 0 on success, -1 on failure
+ */
+int add_carrier_data(struct route_data_t * rd, struct carrier_data_t * carrier_data);
+
+
 /**
  * Loads the routing data into the routing trees and sets the
  * global_data pointer to the new data. The old_data is removed
@@ -94,4 +111,89 @@ struct route_data_t * get_data(void);
  */
 void release_data(struct route_data_t *data);
 
+
+/**
+ * Returns the carrier data for the given id by doing a binary search.
+ * @note The carrier array must be sorted!
+ *
+ * @param rd route data to be searched
+ * @param carrier_id the id of the desired carrier
+ *
+ * @return a pointer to the desired carrier data, NULL if not found.
+ */
+struct carrier_data_t *get_carrier_data(struct route_data_t * rd, int carrier_id);
+
+
+/**
+ * Adds the given route information to the routing domain identified by
+ * domain. scan_prefix identifies the number for which the information
+ * is and the rewrite_* parameters define what to do in case of a match.
+ * prob gives the probability with which this rule applies if there are
+ * more than one for a given prefix.
+ *
+ * @param rd the route data to which the route shall be added
+ * @param carrier_id the carrier id of the route to be added
+ * @param domain_id the routing domain id of the new route
+ * @param scan_prefix the number prefix
+ * @param flags user defined flags
+ * @param mask mask for user defined flags
+ * @param max_targets the number of targets
+ * @param prob the weight of the rule
+ * @param strip the number of digits to be stripped off userpart before prepending prefix
+ * @param rewrite_hostpart the rewrite_host of the rule
+ * @param rewrite_local_prefix the rewrite prefix
+ * @param rewrite_local_suffix the rewrite suffix
+ * @param status the status of the rule
+ * @param hash_index the hash index of the rule
+ * @param backup indicates if the route is backed up by another. only
+                 useful if status==0, if set, it is the hash value
+                 of another rule
+  * @param backed_up an -1-termintated array of hash indices of the route
+                    for which this route is backup
+ * @param comment a comment for the route rule
+ *
+ * @return 0 on success, -1 on error in which case it LOGs a message.
+ */
+int add_route(struct route_data_t * rd, int carrier_id,
+		int domain_id, const str * scan_prefix, flag_t flags, flag_t mask, int max_targets,
+		double prob, const str * rewrite_hostpart, int strip, const str * rewrite_local_prefix,
+		const str * rewrite_local_suffix, int status, int hash_index, int backup, int * backed_up,
+		const str * comment);
+
+
+/**
+ * Adds the given failure route information to the failure routing domain identified by
+ * domain. scan_prefix, host, reply_code and flags identifies the number for which
+ * the information is and the next_domain parameter defines where to continue routing
+ * in case of a match.
+ *
+ * @param rd the route data to which the route shall be added
+ * @param carrier_id the carrier id of the route to be added
+ * @param domain_id the routing domain id of the new route
+ * @param scan_prefix the number prefix
+ * @param host the hostname last tried
+ * @param reply_code the reply code 
+ * @param flags user defined flags
+ * @param mask for user defined flags
+ * @param next_domain_id continue routing with this domain id
+ * @param comment a comment for the failure route rule
+ *
+ * @return 0 on success, -1 on error in which case it LOGs a message.
+ */
+int add_failure_route(struct route_data_t * rd, int carrier_id, int domain_id,
+		const str * scan_prefix, const str * host, const str * reply_code,
+		flag_t flags, flag_t mask, int next_domain_id, const str * comment);
+
+
+/**
+ * Fixes the route rules by creating an array for accessing
+ * route rules by hash index directly
+ *
+ * @param rd route data to be fixed
+ *
+ * @return 0 on success, -1 on failure
+ */
+int rule_fixup(struct route_data_t * rd);
+
+
 #endif

+ 140 - 103
modules/carrierroute/cr_db.c

@@ -34,20 +34,10 @@
 #include "cr_db.h"
 #include "cr_carrier.h"
 #include <stdio.h>
+#include <stdlib.h>
 
 #define QUERY_LEN 2048
 
-/*! carrier list */
-struct carrier {
-	int id;
-	char * name;
-	struct carrier * next;
-};
-
-static int store_carriers(struct carrier ** start);
-
-static void destroy_carriers(struct carrier * start);
-
 static char query[QUERY_LEN];
 
 str * columns[COLUMN_NUM] = { &carrierroute_id_col, &carrierroute_carrier_col,
@@ -63,9 +53,14 @@ str * columns[COLUMN_NUM] = { &carrierroute_id_col, &carrierroute_carrier_col,
 	&carrierroute_description_col
 };
 
-str * carrier_columns[CARRIER_COLUMN_NUM] = {
-	&route_tree_id_col,
-	&route_tree_carrier_col
+str * carrier_name_columns[CARRIER_NAME_COLUMN_NUM] = {
+	&carrier_name_id_col,
+	&carrier_name_carrier_col
+};
+
+str * domain_name_columns[DOMAIN_NAME_COLUMN_NUM] = {
+	&domain_name_id_col,
+	&domain_name_domain_col
 };
 
 str * failure_columns[FAILURE_COLUMN_NUM] = {
@@ -82,6 +77,108 @@ str * failure_columns[FAILURE_COLUMN_NUM] = {
 };
 
 
+static int load_carrier_map(struct route_data_t *rd) {
+	db_res_t * res = NULL;
+	int i, count;
+	if(!rd){
+		LM_ERR("invalid parameter\n");
+		return -1;
+	}
+	if (carrierroute_dbf.use_table(carrierroute_dbh, &carrier_name_table) < 0) {
+		LM_ERR("couldn't use table\n");
+		return -1;
+	}
+
+	if (carrierroute_dbf.query(carrierroute_dbh, 0, 0, 0, (db_key_t *)carrier_name_columns, 0, CARRIER_NAME_COLUMN_NUM, 0, &res) < 0) {
+		LM_ERR("couldn't query table\n");
+		return -1;
+	}
+
+	count = RES_ROW_N(res);
+
+	rd->carrier_map = shm_malloc(sizeof(struct name_map_t) * count);
+	if (rd->carrier_map == NULL) {
+		SHM_MEM_ERROR;
+		carrierroute_dbf.free_result(carrierroute_dbh, res);
+		return -1;
+	}
+	memset(rd->carrier_map, 0, sizeof(struct name_map_t) * count);
+
+	for (i=0; i<count; i++) {
+		rd->carrier_map[i].id = res->rows[i].values[CARRIER_NAME_ID_COL].val.int_val;
+		rd->carrier_map[i].name.len = strlen(res->rows[i].values[CARRIER_NAME_NAME_COL].val.string_val);
+		rd->carrier_map[i].name.s = shm_malloc(rd->carrier_map[i].name.len);
+		if (rd->carrier_map[i].name.s == NULL) {
+			SHM_MEM_ERROR;
+			carrierroute_dbf.free_result(carrierroute_dbh, res);
+			shm_free(rd->carrier_map);
+			rd->carrier_map = NULL;
+			return -1;
+		}
+		memcpy(rd->carrier_map[i].name.s, res->rows[i].values[CARRIER_NAME_NAME_COL].val.string_val, rd->carrier_map[i].name.len);
+	}
+
+	/* sort carrier map by id for faster access */
+	qsort(rd->carrier_map, count, sizeof(rd->carrier_map[0]), compare_name_map);
+
+	carrierroute_dbf.free_result(carrierroute_dbh, res);
+	return count;
+}
+
+
+
+
+static int load_domain_map(struct route_data_t *rd) {
+	db_res_t * res = NULL;
+	int i, count;
+	if(!rd){
+		LM_ERR("invalid parameter\n");
+		return -1;
+	}
+	if (carrierroute_dbf.use_table(carrierroute_dbh, &domain_name_table) < 0) {
+		LM_ERR("couldn't use table\n");
+		return -1;
+	}
+
+	if (carrierroute_dbf.query(carrierroute_dbh, 0, 0, 0, (db_key_t *)domain_name_columns, 0, DOMAIN_NAME_COLUMN_NUM, 0, &res) < 0) {
+		LM_ERR("couldn't query table\n");
+		return -1;
+	}
+
+	count = RES_ROW_N(res);
+
+	rd->domain_map = shm_malloc(sizeof(struct name_map_t) * count);
+	if (rd->domain_map == NULL) {
+		SHM_MEM_ERROR;
+		carrierroute_dbf.free_result(carrierroute_dbh, res);
+		return -1;
+	}
+	memset(rd->domain_map, 0, sizeof(struct name_map_t) * count);
+
+	for (i=0; i<count; i++) {
+		rd->domain_map[i].id = res->rows[i].values[DOMAIN_NAME_ID_COL].val.int_val;
+		rd->domain_map[i].name.len = strlen(res->rows[i].values[DOMAIN_NAME_NAME_COL].val.string_val);
+		rd->domain_map[i].name.s = shm_malloc(rd->domain_map[i].name.len);
+		if (rd->domain_map[i].name.s == NULL) {
+			SHM_MEM_ERROR;
+			carrierroute_dbf.free_result(carrierroute_dbh, res);
+			shm_free(rd->domain_map);
+			rd->domain_map = NULL;
+			return -1;
+		}
+		memcpy(rd->domain_map[i].name.s, res->rows[i].values[DOMAIN_NAME_NAME_COL].val.string_val, rd->domain_map[i].name.len);
+	}
+
+	/* sort domain map by id for faster access */
+	qsort(rd->domain_map, count, sizeof(rd->domain_map[0]), compare_name_map);
+
+	carrierroute_dbf.free_result(carrierroute_dbh, res);
+	return count;
+}
+
+
+
+
 int load_user_carrier(str * user, str * domain) {
 	db_res_t * res;
 	db_key_t cols[1];
@@ -134,6 +231,8 @@ int load_user_carrier(str * user, str * domain) {
 }
 
 
+
+
 /**
  * Loads the routing data from the database given in global
  * variable db_url and stores it in routing tree rd.
@@ -147,11 +246,11 @@ int load_user_carrier(str * user, str * domain) {
 int load_route_data_db(struct route_data_t * rd) {
 	db_res_t * res = NULL;
 	db_row_t * row = NULL;
-	int i, ret, carrier_count = 0;
-	struct carrier * carriers = NULL, * tmp = NULL;
+	int i, ret;
+	struct carrier_data_t * tmp_carrier_data;
 	static str query_str;
-	str tmp_carrier, tmp_domain, tmp_scan_prefix, tmp_rewrite_host, tmp_rewrite_prefix,
-		tmp_rewrite_suffix, tmp_host_name, tmp_reply_code, tmp_next_domain, tmp_comment;
+	str tmp_scan_prefix, tmp_rewrite_host, tmp_rewrite_prefix,
+		tmp_rewrite_suffix, tmp_host_name, tmp_reply_code, tmp_comment;
 
 	if( (strlen("SELECT DISTINCT  FROM  WHERE = ")
 			+ carrierroute_table.len + columns[COL_DOMAIN]->len
@@ -160,24 +259,27 @@ int load_route_data_db(struct route_data_t * rd) {
 		return -1;
 	}
 
-	if((carrier_count = store_carriers(&carriers)) <= 0){
+	if((rd->carrier_num = load_carrier_map(rd)) <= 0){
 		LM_ERR("error while retrieving carriers\n");
 		goto errout;
 	}
 
-	if ((rd->carriers = shm_malloc(sizeof(struct carrier_data_t *) * carrier_count)) == NULL) {
+	if((rd->domain_num = load_domain_map(rd)) <= 0){
+		LM_ERR("error while retrieving domains\n");
+		goto errout;
+	}
+
+	if ((rd->carriers = shm_malloc(sizeof(struct carrier_data_t *) * rd->carrier_num)) == NULL) {
 		SHM_MEM_ERROR;
 		goto errout;
 	}
-	memset(rd->carriers, 0, sizeof(struct carrier_data_t *) * carrier_count);
-	rd->carrier_num = carrier_count;
+	memset(rd->carriers, 0, sizeof(struct carrier_data_t *) * rd->carrier_num);
 
-	tmp = carriers;
-	for (i=0; i<carrier_count; i++) {
+	for (i=0; i<rd->carrier_num; i++) {
 		memset(query, 0, QUERY_LEN);
 		ret = snprintf(query, QUERY_LEN, "SELECT DISTINCT %.*s FROM %.*s WHERE %.*s=%i",
 		columns[COL_DOMAIN]->len, columns[COL_DOMAIN]->s, carrierroute_table.len,
-		carrierroute_table.s, columns[COL_CARRIER]->len, columns[COL_CARRIER]->s, tmp->id);
+		carrierroute_table.s, columns[COL_CARRIER]->len, columns[COL_CARRIER]->s, rd->carrier_map[i].id);
 		if (ret < 0) {
 			LM_ERR("error in snprintf");
 			goto errout;
@@ -189,16 +291,19 @@ int load_route_data_db(struct route_data_t * rd) {
 			LM_ERR("Failed to query database.\n");
 			goto errout;
 		}
-		LM_INFO("name %s, id %i, trees: %i\n", tmp->name, tmp->id, RES_ROW_N(res));
-		tmp_carrier.s=tmp->name;
-		tmp_carrier.len=strlen(tmp_carrier.s);
-		if (add_carrier_data(rd, &tmp_carrier, tmp->id, RES_ROW_N(res)) == NULL) {
-			LM_ERR("can't add carrier %s\n", tmp->name);
+		LM_INFO("carrier '%.*s' (id %i) has %i domains\n", rd->carrier_map[i].name.len, rd->carrier_map[i].name.s, rd->carrier_map[i].id, RES_ROW_N(res));
+		tmp_carrier_data = create_carrier_data(rd->carrier_map[i].id, &rd->carrier_map[i].name, RES_ROW_N(res));
+		if (tmp_carrier_data == NULL) {
+			LM_ERR("can't create new carrier '%.*s'\n", rd->carrier_map[i].name.len, rd->carrier_map[i].name.s);
+			goto errout;
+		}
+		if (add_carrier_data(rd, tmp_carrier_data) < 0) {
+			LM_ERR("can't add carrier '%.*s'\n", rd->carrier_map[i].name.len, rd->carrier_map[i].name.s);
+			destroy_carrier_data(tmp_carrier_data);
 			goto errout;
 		}
 		carrierroute_dbf.free_result(carrierroute_dbh, res);
 		res = NULL;
-		tmp = tmp->next;
 	}
 
 	if (carrierroute_dbf.use_table(carrierroute_dbh, &carrierroute_table) < 0) {
@@ -218,7 +323,7 @@ int load_route_data_db(struct route_data_t * rd) {
 		}
 	} else {
 		if (carrierroute_dbf.query(carrierroute_dbh, NULL, NULL, NULL, (db_key_t *) columns, 0,
-					COLUMN_NUM, NULL, &res) < 0) {
+				 COLUMN_NUM, NULL, &res) < 0) {
 			LM_ERR("Failed to query database.\n");
 			return -1;
 		}
@@ -228,19 +333,16 @@ int load_route_data_db(struct route_data_t * rd) {
 		LM_DBG("loading, cycle %d", n++);
 		for (i = 0; i < RES_ROW_N(res); ++i) {
 			row = &RES_ROWS(res)[i];
-			tmp_domain.s=(char *)row->values[COL_DOMAIN].val.string_val;
 			tmp_scan_prefix.s=(char *)row->values[COL_SCAN_PREFIX].val.string_val;
 			tmp_rewrite_host.s=(char *)row->values[COL_REWRITE_HOST].val.string_val;
 			tmp_rewrite_prefix.s=(char *)row->values[COL_REWRITE_PREFIX].val.string_val;
 			tmp_rewrite_suffix.s=(char *)row->values[COL_REWRITE_SUFFIX].val.string_val;
 			tmp_comment.s=(char *)row->values[COL_COMMENT].val.string_val;
-			if (tmp_domain.s==NULL) tmp_domain.s="";
 			if (tmp_scan_prefix.s==NULL) tmp_scan_prefix.s="";
 			if (tmp_rewrite_host.s==NULL) tmp_rewrite_host.s="";
 			if (tmp_rewrite_prefix.s==NULL) tmp_rewrite_prefix.s="";
 			if (tmp_rewrite_suffix.s==NULL) tmp_rewrite_suffix.s="";
 			if (tmp_comment.s==NULL) tmp_comment.s="";
-			tmp_domain.len=strlen(tmp_domain.s);
 			tmp_scan_prefix.len=strlen(tmp_scan_prefix.s);
 			tmp_rewrite_host.len=strlen(tmp_rewrite_host.s);
 			tmp_rewrite_prefix.len=strlen(tmp_rewrite_prefix.s);
@@ -248,7 +350,7 @@ int load_route_data_db(struct route_data_t * rd) {
 			tmp_comment.len=strlen(tmp_comment.s);
 			if (add_route(rd,
 					row->values[COL_CARRIER].val.int_val,
-					&tmp_domain,
+					row->values[COL_DOMAIN].val.int_val,
 					&tmp_scan_prefix,
 					row->values[COL_FLAGS].val.int_val,
 					row->values[COL_MASK].val.int_val,
@@ -292,103 +394,38 @@ int load_route_data_db(struct route_data_t * rd) {
 	}
 	for (i = 0; i < RES_ROW_N(res); ++i) {
 		row = &RES_ROWS(res)[i];
-		tmp_domain.s=(char *)row->values[FCOL_DOMAIN].val.string_val;
 		tmp_scan_prefix.s=(char *)row->values[FCOL_SCAN_PREFIX].val.string_val;
 		tmp_host_name.s=(char *)row->values[FCOL_HOST_NAME].val.string_val;
 		tmp_reply_code.s=(char *)row->values[FCOL_REPLY_CODE].val.string_val;
-		tmp_next_domain.s=(char *)row->values[FCOL_NEXT_DOMAIN].val.string_val;
 		tmp_comment.s=(char *)row->values[FCOL_COMMENT].val.string_val;
-		if (tmp_domain.s==NULL) tmp_domain.s="";
 		if (tmp_scan_prefix.s==NULL) tmp_scan_prefix.s="";
 		if (tmp_host_name.s==NULL) tmp_host_name.s="";
 		if (tmp_reply_code.s==NULL) tmp_reply_code.s="";
-		if (tmp_next_domain.s==NULL) tmp_next_domain.s="";
 		if (tmp_comment.s==NULL) tmp_comment.s="";
-		tmp_domain.len=strlen(tmp_domain.s);
 		tmp_scan_prefix.len=strlen(tmp_scan_prefix.s);
 		tmp_host_name.len=strlen(tmp_host_name.s);
 		tmp_reply_code.len=strlen(tmp_reply_code.s);
-		tmp_next_domain.len=strlen(tmp_next_domain.s);
 		tmp_comment.len=strlen(tmp_comment.s);
 		if (add_failure_route(rd,
 				row->values[FCOL_CARRIER].val.int_val,
-				&tmp_domain,
+				row->values[COL_DOMAIN].val.int_val,
 				&tmp_scan_prefix,
 				&tmp_host_name,
 				&tmp_reply_code,
 				row->values[FCOL_FLAGS].val.int_val,
 				row->values[FCOL_MASK].val.int_val,
-				&tmp_next_domain,
+				row->values[FCOL_NEXT_DOMAIN].val.int_val,
 				&tmp_comment) == -1) {
 			goto errout;
 		}
 	}
 
-	destroy_carriers(carriers);
 	carrierroute_dbf.free_result(carrierroute_dbh, res);
 	return 0;
 
 errout:
-	destroy_carriers(carriers);
 	if (res) {
 		carrierroute_dbf.free_result(carrierroute_dbh, res);
 	}
 	return -1;
 }
-
-
-static int store_carriers(struct carrier ** start){
-	db_res_t * res = NULL;
-	int i, count;
-	struct carrier * nc;
-	if(!start){
-		LM_ERR("invalid parameter\n");
-		return -1;
-	}
-	if (carrierroute_dbf.use_table(carrierroute_dbh, &route_tree_table) < 0) {
-		LM_ERR("couldn't use table\n");
-		return -1;
-	}
-
-	if (carrierroute_dbf.query(carrierroute_dbh, 0, 0, 0, (db_key_t *)carrier_columns, 0, CARRIER_COLUMN_NUM, 0, &res) < 0) {
-		LM_ERR("couldn't query table\n");
-		return -1;
-	}
-	count = RES_ROW_N(res);
-	for(i=0; i<RES_ROW_N(res); i++){
-		if((nc = pkg_malloc(sizeof(struct carrier))) == NULL){
-			PKG_MEM_ERROR;
-			return -1;
-		}
-		nc->id = res->rows[i].values[0].val.int_val;
-		if((nc->name = pkg_malloc(strlen(res->rows[i].values[1].val.string_val) + 1)) == NULL){
-			PKG_MEM_ERROR;
-			pkg_free(nc);
-			goto errout;
-		}
-		strcpy(nc->name, res->rows[i].values[1].val.string_val);
-		nc->next = *start;
-		*start = nc;
-	}
-	carrierroute_dbf.free_result(carrierroute_dbh, res);
-	return count;
-errout:
-if(res){
-	carrierroute_dbf.free_result(carrierroute_dbh, res);
-}
-	return -1;
-}
-
-
-static void destroy_carriers(struct carrier * start){
-	struct carrier * tmp, * tmp2;
-	tmp = start;
-	
-	while(tmp){
-		tmp2 = tmp;
-		tmp = tmp->next;
-		pkg_free(tmp2->name);
-		pkg_free(tmp2);
-	}
-	return;
-}

+ 7 - 3
modules/carrierroute/cr_db.h

@@ -61,9 +61,13 @@
 #define FCOL_NEXT_DOMAIN    8
 #define FCOL_COMMENT        9
 
-#define CARRIER_COLUMN_NUM 2
-#define CARRIER_ID_COL 0
-#define CARRIER_NAME_COL 1
+#define CARRIER_NAME_COLUMN_NUM 2
+#define CARRIER_NAME_ID_COL 0
+#define CARRIER_NAME_NAME_COL 1
+
+#define DOMAIN_NAME_COLUMN_NUM 2
+#define DOMAIN_NAME_ID_COL 0
+#define DOMAIN_NAME_NAME_COL 1
 
 extern str * columns[];
 extern str * carrier_columns[];

+ 34 - 16
modules/carrierroute/cr_domain.c

@@ -31,7 +31,6 @@
 #include "../../mem/shm_mem.h"
 #include "../../ut.h"
 #include "cr_domain.h"
-#include "cr_map.h"
 #include "cr_rule.h"
 
 
@@ -72,33 +71,27 @@ static void destroy_failure_route_rule_list(void *data) {
 /**
  * Create a new domain in shared memory and set it up.
  *
- * @param domain_name the name of the domain
  * @param domain_id the id of the domain
+ * @param domain_name the name of the domain
  *
  * @return a pointer to the newly allocated domain data or NULL on
  * error, in which case it LOGs an error message.
  */
-struct domain_data_t * create_domain_data(const str * domain_name, int domain_id) {
+struct domain_data_t * create_domain_data(int domain_id, str * domain_name) {
 	struct domain_data_t * tmp;
 	if ((tmp = shm_malloc(sizeof(struct domain_data_t))) == NULL) {
-		LM_ERR("out of shared memory\n");
+		SHM_MEM_ERROR;
 		return NULL;
 	}
 	memset(tmp, 0, sizeof(struct domain_data_t));
-	if (shm_str_dup(&tmp->name, domain_name)!=0) {
-		LM_ERR("cannot duplicate string\n");
-		shm_free(tmp);
-		return NULL;
-	}
 	tmp->id = domain_id;
+	tmp->name = domain_name;
 	if ((tmp->tree = dtrie_init()) == NULL) {
-		shm_free(tmp->name.s);
 		shm_free(tmp);
 		return NULL;
 	}
 	if ((tmp->failure_tree = dtrie_init()) == NULL) {
 		dtrie_destroy(&tmp->tree, NULL);
-		shm_free(tmp->name.s);
 		shm_free(tmp);
 		return NULL;
 	}
@@ -109,13 +102,14 @@ struct domain_data_t * create_domain_data(const str * domain_name, int domain_id
 /**
  * Destroys the given domain and frees the used memory.
  *
- * @param domain_data the to the structure to be destroyed.
+ * @param domain_data the structure to be destroyed.
  */
 void destroy_domain_data(struct domain_data_t *domain_data) {
-	dtrie_destroy(&domain_data->tree, destroy_route_flags_list);
-	dtrie_destroy(&domain_data->failure_tree, destroy_failure_route_rule_list);
-	shm_free(domain_data->name.s);
-	shm_free(domain_data);
+	if (domain_data) {
+		dtrie_destroy(&domain_data->tree, destroy_route_flags_list);
+		dtrie_destroy(&domain_data->failure_tree, destroy_failure_route_rule_list);
+		shm_free(domain_data);
+	}
 }
 
 
@@ -225,3 +219,27 @@ int add_failure_route_to_tree(struct dtrie_node_t * failure_node, const str * sc
 
 	return 0;
 }
+
+
+/**
+ * Compares the IDs of two domain data structures.
+ * A NULL pointer is always greater than any ID.
+ *
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
+ */
+int compare_domain_data(const void *v1, const void *v2) {
+  struct domain_data_t *d1 = *(struct domain_data_t * const *)v1;
+	struct domain_data_t *d2 = *(struct domain_data_t * const *)v2;
+	if (d1 == NULL) {
+		if (d2 == NULL) return 0;
+		else return 1;
+	}
+	else {
+		if (d2 == NULL) return -1;
+		else {
+			if (d1->id < d2->id) return -1;
+			else if (d1->id > d2->id) return 1;
+			else return 0;
+		}
+	}
+}

+ 13 - 3
modules/carrierroute/cr_domain.h

@@ -41,7 +41,7 @@
  */
 struct domain_data_t {
 	int id; /*!< the numerical id of the routing tree */
-	str name; /*!< the name of the routing tree */
+	str * name; /*!< the name of the routing tree. This points to the name in domain_map to avoid duplication. */
 	struct dtrie_node_t * tree; /*!< the root node of the routing tree. Payload is of type (struct route_flags *) */
 	struct dtrie_node_t * failure_tree; /*!< the root node of the failure routing tree. Payload is of type (struct failure_route_rule *) */
 };
@@ -50,13 +50,13 @@ struct domain_data_t {
 /**
  * Create a new domain in shared memory and set it up.
  *
- * @param domain_name the name of the domain
  * @param domain_id the id of the domain
+ * @param domain_name the name of the domain
  *
  * @return a pointer to the newly allocated domain data or NULL on
  * error, in which case it LOGs an error message.
  */
-struct domain_data_t * create_domain_data(const str * domain, int id);
+struct domain_data_t * create_domain_data(int id, str * domain);
 
 
 /**
@@ -129,4 +129,14 @@ int add_failure_route_to_tree(struct dtrie_node_t * failure_node, const str * sc
 		const str * full_prefix, const str * host, const str * reply_code,
 		const flag_t flags, const flag_t mask, const int next_domain, const str * comment);
 
+
+/**
+ * Compares the IDs of two domain data structures.
+ * A NULL pointer is always greater than any ID.
+ *
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
+ */
+int compare_domain_data(const void *v1, const void *v2);
+
+
 #endif

+ 20 - 14
modules/carrierroute/cr_fifo.c

@@ -131,7 +131,7 @@ struct mi_root* dump_fifo (struct mi_root* cmd_tree, void *param) {
 		LM_ERR("error during retrieve data\n");
 		return init_mi_tree(500, "error during command processing", 31);
 	}
-		
+	
 	struct mi_root* rpl_tree;
 	struct mi_node* node = NULL;
 	rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
@@ -145,14 +145,14 @@ struct mi_root* dump_fifo (struct mi_root* cmd_tree, void *param) {
 	int i, j;
  	for (i = 0; i < rd->carrier_num; i++) {
  		if (rd->carriers[i]) {
-			tmp_str = (rd->carriers[i] ? &rd->carriers[i]->name : &empty_str);
-			node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "Printing tree for carrier %.*s (%i)\n", tmp_str->len, tmp_str->s, rd->carriers[i] ? rd->carriers[i]->id : 0);
+			tmp_str = (rd->carriers[i] ? rd->carriers[i]->name : &empty_str);
+			node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "Printing tree for carrier '%.*s' (%i)\n", tmp_str->len, tmp_str->s, rd->carriers[i] ? rd->carriers[i]->id : 0);
 			if(node == NULL)
 				goto error;
  			for (j=0; j<rd->carriers[i]->domain_num; j++) {
  				if (rd->carriers[i]->domains[j] && rd->carriers[i]->domains[j]->tree) {
-					tmp_str = (rd->carriers[i]->domains[j] ? &rd->carriers[i]->domains[j]->name : &empty_str);
-					node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "Printing tree for domain %.*s\n", tmp_str->len, tmp_str->s);
+					tmp_str = (rd->carriers[i]->domains[j] ? rd->carriers[i]->domains[j]->name : &empty_str);
+					node = addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "Printing tree for domain '%.*s' (%i)\n", tmp_str->len, tmp_str->s, rd->carriers[i]->domains[j]->id);
 					if(node == NULL)
 						goto error;
  					dump_tree_recursor (&rpl_tree->node, rd->carriers[i]->domains[j]->tree, "");
@@ -605,6 +605,7 @@ static int get_fifo_opts(str * buf, fifo_opt_t * opts, unsigned int opt_set[]) {
 static int update_route_data(fifo_opt_t * opts) {
 	struct route_data_t * rd;
 	int i,j;
+	int domain_id;
 	str tmp_domain;
 	str tmp_prefix;
 	str tmp_host;
@@ -613,7 +614,7 @@ static int update_route_data(fifo_opt_t * opts) {
 	str tmp_comment = str_init("");
 
 	if ((rd = shm_malloc(sizeof(struct route_data_t))) == NULL) {
-		LM_ERR("out of shared memory\n");
+		SHM_MEM_ERROR;
 		return -1;
 	}
 	memset(rd, 0, sizeof(struct route_data_t));
@@ -657,7 +658,13 @@ static int update_route_data(fifo_opt_t * opts) {
 			tmp_rewrite_suffix.len=0;
 		}
 
-		if (add_route(rd, 1, &tmp_domain, &tmp_prefix, 0, 0, 0, opts->prob,
+		domain_id = map_name2id(rd->domain_map, rd->domain_num, &tmp_domain);
+		if (domain_id < 0) {
+			LM_ERR("cannot find id for domain '%.*s'", tmp_domain.len, tmp_domain.s);
+			goto errout;
+		}
+
+		if (add_route(rd, 1, domain_id, &tmp_prefix, 0, 0, 0, opts->prob,
 		              &tmp_host, opts->strip, &tmp_rewrite_prefix, &tmp_rewrite_suffix,
 		              opts->status, opts->hash_index, -1, NULL, &tmp_comment) < 0) {
 			goto errout;
@@ -666,20 +673,19 @@ static int update_route_data(fifo_opt_t * opts) {
 		if (rule_fixup(rd) < 0) {
 			LM_ERR("could not fixup rules after route appending");
 			FIFO_ERR(E_RULEFIXUP);
-			return -1;
+			goto errout;
 		}
-
 	} else {
 		for (i=0; i<rd->carrier_num; i++) {
 			if(rd->carriers[i]){
-			for (j=0; j<rd->carriers[i]->domain_num; j++) {
-				if (rd->carriers[i]->domains[j] && rd->carriers[i]->domains[j]->tree) {
-					if (update_route_data_recursor(rd->carriers[i]->domains[j]->tree, &rd->carriers[i]->domains[j]->name, opts) < 0) {
-						goto errout;
+				for (j=0; j<rd->carriers[i]->domain_num; j++) {
+					if (rd->carriers[i]->domains[j] && rd->carriers[i]->domains[j]->tree) {
+						if (update_route_data_recursor(rd->carriers[i]->domains[j]->tree, rd->carriers[i]->domains[j]->name, opts) < 0) {
+							goto errout;
+						}
 					}
 				}
 			}
-			}
 		}
 	}
 

+ 67 - 6
modules/carrierroute/cr_fixup.c

@@ -34,6 +34,55 @@
 #include "cr_map.h"
 #include "cr_domain.h"
 #include "prime_hash.h"
+#include "cr_data.h"
+
+
+/**
+ * The fixup funcions will use the initial mapping.
+ * If the mapping changes afterwards (eg. due to cr_reload_routes),
+ * the names used in the routing script will not be mapped
+ * to the correct IDs!
+ * @param name carrier name
+ * @return carrier id
+ */
+static int carrier_name_2_id(const str *name) {
+	int id;
+	struct route_data_t * rd;
+	
+	do {
+		rd = get_data();
+	} while (rd == NULL);
+
+	id = map_name2id(rd->carrier_map, rd->carrier_num, name);
+	
+	release_data(rd);
+
+	return id;
+}
+
+
+/**
+ * The fixup funcions will use the initial mapping.
+ * If the mapping changes afterwards (eg. due to cr_reload_routes),
+ * the names used in the routing script will not be mapped
+ * to the correct IDs!
+ * @param name domain name
+ * @return domain id 
+ */
+static int domain_name_2_id(const str *name) {
+	int id;
+	struct route_data_t * rd;
+
+	do {
+		rd = get_data();
+	} while (rd == NULL);
+	
+	id = map_name2id(rd->domain_map, rd->domain_num, name);
+	
+	release_data(rd);
+
+	return id;
+}
 
 
 /**
@@ -69,6 +118,7 @@ static enum hash_source hash_fixup(const char * my_hash_source) {
  * @return 0 on success, -1 on failure
  */
 static int carrier_fixup(void ** param) {
+	int id;
 
 	if (fixup_spve_null(param, 1) !=0) {
 		LM_ERR("could not fixup parameter");
@@ -79,11 +129,12 @@ static int carrier_fixup(void ** param) {
 		/* This is a name string, convert to a int */
 		((gparam_p)(*param))->type=GPARAM_TYPE_INT;
 		/* get carrier id */
-		if ((((gparam_p)(*param))->v.ival = find_carrier(&((gparam_p)(*param))->v.sval)) < 0) {
-			LM_ERR("could not add carrier\n");
+		if ((id = carrier_name_2_id(&((gparam_p)(*param))->v.sval)) < 0) {
+			LM_ERR("could not find carrier name '%.*s' in map\n", ((gparam_p)(*param))->v.sval.len, ((gparam_p)(*param))->v.sval.s);
 			pkg_free(*param);
 			return -1;
 		}
+		((gparam_p)(*param))->v.ival = id;
 	}
 	return 0;
 }
@@ -98,7 +149,8 @@ static int carrier_fixup(void ** param) {
  * @return 0 on success, -1 on failure
  */
 static int domain_fixup(void ** param) {
-	
+	int id;
+
 	if (fixup_spve_null(param, 1) !=0) {
 		LM_ERR("could not fixup parameter");
 		return -1;
@@ -108,11 +160,12 @@ static int domain_fixup(void ** param) {
 		/* This is a name string, convert to a int */
 		((gparam_p)(*param))->type=GPARAM_TYPE_INT;
 		/* get domain id */
-		if ((((gparam_p)(*param))->v.ival = add_domain(&(((gparam_p)(*param))->v.sval))) < 0) {
-			LM_ERR("could not add domain\n");
+		if ((id = domain_name_2_id(&(((gparam_p)(*param))->v.sval))) < 0) {
+			LM_ERR("could not find domain name '%.*s' in map\n", ((gparam_p)(*param))->v.sval.len, ((gparam_p)(*param))->v.sval.s);
 			pkg_free(*param);
 			return -1;
 		}
+		((gparam_p)(*param))->v.ival = id;
 	}
 	return 0;
 }
@@ -142,7 +195,7 @@ static int avp_name_fixup(void ** param) {
 
 
 /**
- * fixes the module functions' parameters, i.e. it maps
+ * Fixes the module functions' parameters, i.e. it maps
  * the routing domain names to numbers for faster access
  * at runtime
  *
@@ -240,6 +293,14 @@ int cr_load_next_domain_fixup(void ** param, int param_no) {
 }
 
 
+/**
+ * Fixes the module functions' parameters.
+ *
+ * @param param the parameter
+ * @param param_no the number of the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
 int cr_load_user_carrier_fixup(void ** param, int param_no) {
 	if (mode == CARRIERROUTE_MODE_FILE) {
 		LM_ERR("command cr_user_rewrite_uri can't be used in file mode\n");

+ 30 - 0
modules/carrierroute/cr_fixup.h

@@ -32,10 +32,40 @@
 #define CR_FIXUP_H
 
 
+/**
+ * fixes the module functions' parameters, i.e. it maps
+ * the routing domain names to numbers for faster access
+ * at runtime
+ *
+ * @param param the parameter
+ * @param param_no the number of the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
 int cr_route_fixup(void ** param, int param_no);
 
+
+/**
+ * Fixes the module functions' parameters.
+ *
+ * @param param the parameter
+ * @param param_no the number of the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
 int cr_load_user_carrier_fixup(void ** param, int param_no);
 
+
+/**
+ * Fixes the module functions' parameters, i.e. it maps
+ * the routing domain names to numbers for faster access
+ * at runtime
+ *
+ * @param param the parameter
+ * @param param_no the number of the parameter
+ *
+ * @return 0 on success, -1 on failure
+ */
 int cr_load_next_domain_fixup(void ** param, int param_no);
 
 #endif

+ 39 - 25
modules/carrierroute/cr_func.c

@@ -71,7 +71,7 @@ static const str AT_SIGN  = { .s="@",     .len=1 };
  * @param search_if lookup function
  * @return id on success, -1 otherwise
  */
-static inline int cr_gp2id(struct sip_msg *_msg, gparam_t *gp, int (*search_id)(const str* name)) {
+static inline int cr_gp2id(struct sip_msg *_msg, gparam_t *gp, struct name_map_t *map, int size) {
 	int id;
 	struct usr_avp *avp;
 	int_str avp_val;
@@ -94,7 +94,7 @@ static inline int cr_gp2id(struct sip_msg *_msg, gparam_t *gp, int (*search_id)(
 			if ((avp->flags&AVP_VAL_STR)==0) {
 				return avp_val.n;
 			} else {
-				id = search_id(&avp_val.s);
+				id = map_name2id(map, size, &avp_val.s);
 				if (id < 0) {
 					LM_ERR("could not find id '%.*s' from AVP\n",
 							gp->v.pve->spec.pvp.pvn.u.isname.name.s.len,
@@ -109,7 +109,7 @@ static inline int cr_gp2id(struct sip_msg *_msg, gparam_t *gp, int (*search_id)(
 				LM_ERR("cannot print the name from PV\n");
 				return -1;
 			}
-			id = search_id(&tmp);
+			id = map_name2id(map, size, &tmp);
 			if (id < 0) {
 				LM_ERR("could not find id '%.*s' from PV\n", tmp.len, tmp.s);
 				return -1;
@@ -493,13 +493,6 @@ int cr_do_route(struct sip_msg * _msg, gparam_t *_carrier,
 	struct domain_data_t * domain_data;
 	struct action act;
 
-	carrier_id = cr_gp2id(_msg, _carrier, find_carrier);
-	domain_id = cr_gp2id(_msg, _domain, add_domain);
-	if (domain_id < 0) {
-		LM_ERR("invalid domain id %d\n", domain_id);
-		return -1;
-	}
-
 	if (fixup_get_svalue(_msg, _rewrite_user, &rewrite_user)<0) {
 		LM_ERR("cannot print the rewrite_user\n");
 		return -1;
@@ -515,21 +508,35 @@ int cr_do_route(struct sip_msg * _msg, gparam_t *_carrier,
 	do {
 		rd = get_data();
 	} while (rd == NULL);
+
+	carrier_id = cr_gp2id(_msg, _carrier, rd->carrier_map, rd->carrier_num);
+	if (carrier_id < 0) {
+		LM_ERR("invalid carrier id %d\n", carrier_id);
+		release_data(rd);
+		return -1;
+	}
+
+	domain_id = cr_gp2id(_msg, _domain, rd->domain_map, rd->domain_num);
+	if (domain_id < 0) {
+		LM_ERR("invalid domain id %d\n", domain_id);
+		release_data(rd);
+		return -1;
+	}
 	
 	carrier_data=NULL;
 	if (carrier_id < 0) {
 		if (fallback_default) {
 			LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id);
-			carrier_data = rd->carriers[rd->default_carrier_index];
+			carrier_data = get_carrier_data(rd, rd->default_carrier_id);
 		}
 	} else if (carrier_id == 0) {
-		carrier_data = rd->carriers[rd->default_carrier_index];
+		carrier_data = get_carrier_data(rd, rd->default_carrier_id);
 	} else {
 		carrier_data = get_carrier_data(rd, carrier_id);
 		if (carrier_data == NULL) {
 			if (fallback_default) {
 				LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id);
-				carrier_data = rd->carriers[rd->default_carrier_index];
+				carrier_data = get_carrier_data(rd, rd->default_carrier_id);
 			}
 		}
 	}
@@ -538,7 +545,7 @@ int cr_do_route(struct sip_msg * _msg, gparam_t *_carrier,
 		goto unlock_and_out;
 	}
 
-	domain_data = get_domain_data_by_id(carrier_data, domain_id);
+	domain_data = get_domain_data(carrier_data, domain_id);
 	if (domain_data == NULL) {
 		LM_ERR("desired routing domain doesn't exist, prefix %.*s, carrier %d, domain %d\n",
 			prefix_matching.len, prefix_matching.s, carrier_id, domain_id);
@@ -683,13 +690,6 @@ int cr_load_next_domain(struct sip_msg * _msg, gparam_t *_carrier,
 	struct carrier_data_t * carrier_data;
 	struct domain_data_t * domain_data;
 
-	carrier_id = cr_gp2id(_msg, _carrier, find_carrier);
-	domain_id = cr_gp2id(_msg, _domain, add_domain);
-	if (domain_id < 0) {
-		LM_ERR("invalid domain id %d\n", domain_id);
-		return -1;
-	}
-
 	if (fixup_get_svalue(_msg, _prefix_matching, &prefix_matching)<0) {
 		LM_ERR("cannot print the prefix_matching\n");
 		return -1;
@@ -709,20 +709,34 @@ int cr_load_next_domain(struct sip_msg * _msg, gparam_t *_carrier,
 		rd = get_data();
 	} while (rd == NULL);
 	
+	carrier_id = cr_gp2id(_msg, _carrier, rd->carrier_map, rd->carrier_num);
+	if (carrier_id < 0) {
+		LM_ERR("invalid carrier id %d\n", carrier_id);
+		release_data(rd);
+		return -1;
+	}
+
+	domain_id = cr_gp2id(_msg, _domain, rd->domain_map, rd->domain_num);
+	if (domain_id < 0) {
+		LM_ERR("invalid domain id %d\n", domain_id);
+		release_data(rd);
+		return -1;
+	}
+
 	carrier_data=NULL;
 	if (carrier_id < 0) {
 		if (fallback_default) {
 			LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id);
-			carrier_data = rd->carriers[rd->default_carrier_index];
+			carrier_data = get_carrier_data(rd, rd->default_carrier_id);
 		}
 	} else if (carrier_id == 0) {
-		carrier_data = rd->carriers[rd->default_carrier_index];
+		carrier_data = get_carrier_data(rd, rd->default_carrier_id);
 	} else {
 		carrier_data = get_carrier_data(rd, carrier_id);
 		if (carrier_data == NULL) {
 			if (fallback_default) {
 				LM_NOTICE("invalid tree id %i specified, using default tree\n", carrier_id);
-				carrier_data = rd->carriers[rd->default_carrier_index];
+				carrier_data = get_carrier_data(rd, rd->default_carrier_id);
 			}
 		}
 	}
@@ -731,7 +745,7 @@ int cr_load_next_domain(struct sip_msg * _msg, gparam_t *_carrier,
 		goto unlock_and_out;
 	}
 
-	domain_data = get_domain_data_by_id(carrier_data, domain_id);
+	domain_data = get_domain_data(carrier_data, domain_id);
 	if (domain_data == NULL) {
 		LM_ERR("desired routing domain doesn't exist, prefix %.*s, carrier %d, domain %d\n",
 			prefix_matching.len, prefix_matching.s, carrier_id, domain_id);

+ 31 - 175
modules/carrierroute/cr_map.c

@@ -27,207 +27,63 @@
  * - Module; \ref carrierroute
  */
 
+#include <stdlib.h>
 #include "cr_map.h"
 #include "../../mem/shm_mem.h"
 #include "../../ut.h"
 
 
-/**
- * used to map routing domain names to numbers for
- * faster access.
- */
-struct domain_map_t {
-	str name; /*!< name of the routing domain */
-	int index; /*!< domain index */
-	struct domain_map_t * next; /*!< pointer to the next element */
-};
-
-/**
- * used to map carrier names to numbers for
- * faster access.
- */
-struct carrier_map_t {
-	str name; /*!< name of the carrier */
-	int id; /*!< id of the carrier */
-	int index; /*!< number of carrier array index for rewrite_data.trees */
-	struct carrier_map_t * next; /*!< pointer to the next element */
-};
-
-
-/**
- * holds the map between routing domain names and numbers
- */
-static struct domain_map_t ** domain_map = NULL;
-
-
-/**
- * holds the map between carrier names and numbers
- */
-static struct carrier_map_t ** carrier_map = NULL;
-
 
 /**
- * Tries to add a domain to the domain map. If the given domain doesn't
- * exist, it is added. Otherwise, nothing happens.
+ * Searches for the ID of a name
  *
- * @param domain the domain to be added
+ * @param map the mapping list to search in
+ * @param size the size of the list
+ * @param name the name, we are looking for
  *
- * @return values: on succcess the numerical index of the given domain,
- * -1 on failure
+ * @return values: on succcess the id for this name, -1 on failure
  */
-int add_domain(const str * domain) {
-	struct domain_map_t * tmp, * prev = NULL;
-	int index = 0;
-	if (!domain_map) {
-		if ((domain_map = shm_malloc(sizeof(struct domain_map_t *))) == NULL) {
-			LM_ERR("out of shared memory\n");
-			return -1;
-		}
-		memset(domain_map, 0, sizeof(struct domain_map_t *));
-	}
+int map_name2id(struct name_map_t * map, int size, const str * name) {
+	int i;
 
-	tmp = *domain_map;
-
-	while (tmp) {
-		if (str_strcmp(&tmp->name, domain) == 0) {
-			return tmp->index;
-		}
-		index = tmp->index + 1;
-		prev = tmp;
-		tmp = tmp->next;
-	}
-	if ((tmp = shm_malloc(sizeof(struct domain_map_t))) == NULL) {
-		LM_ERR("out of shared memory\n");
-		return -1;
-	}
-	memset(tmp, 0, sizeof(struct domain_map_t));
-	if (shm_str_dup(&tmp->name, domain) != 0) {
-		LM_ERR("cannot duplicate string\n");
-		shm_free(tmp);
+	if ((!name) || (name->len <= 0)) {
 		return -1;
 	}
-	tmp->index = index;
-	if (!prev) {
-		*domain_map = tmp;
-	} else {
-		prev->next = tmp;
-	}
-	LM_INFO("domain %.*s has index %i\n", domain->len, domain->s, index);
-	return index;
-}
 
-
-/**
- * Destroy the domain map by freeing its memory.
- */
-void destroy_domain_map(void) {
-	struct domain_map_t * tmp;
-	if (domain_map) {
-		tmp = *domain_map;
-		while (*domain_map) {
-			tmp = *domain_map;
-			*domain_map = tmp->next;
-			shm_free(tmp);
-		}
-		shm_free(domain_map);
-		domain_map = NULL;
+	for (i=0; i<size; i++) {
+		if (str_strcmp(&map[i].name, name) == 0) return map[i].id;
 	}
+	return -1;
 }
 
 
 /**
- * Tries to add a carrier name to the carrier map. If the given carrier
- * doesn't exist, it is added. Otherwise, nothing happens.
+ * Searches for the name of an ID
  *
- * @param carrier_name the carrier name to be added
- * @param carrier_id the corresponding id
+ * @param map the mapping list to search in
+ * @param size the size of the list
+ * @param id the id, we are looking for
  *
- * @return values: on succcess the numerical index of the given carrier,
- * -1 on failure
+ * @return values: on succcess the name for this id, NULL on failure
  */
-int add_carrier(const str * tree, int carrier_id) {
-	struct carrier_map_t * tmp, * prev = NULL;
-	int index = 0;
-	if (!carrier_map) {
-		if ((carrier_map = shm_malloc(sizeof(struct carrier_map_t *))) == NULL) {
-			LM_ERR("out of shared memory\n");
-			return -1;
-		}
-		*carrier_map = NULL;
-	}
-	tmp = *carrier_map;
-
-	while (tmp) {
-		if (carrier_id == tmp->id) {
-			return tmp->index;
-		}
-		index = tmp->index + 1;
-		prev = tmp;
-		tmp = tmp->next;
-	}
-	if ((tmp = shm_malloc(sizeof(struct carrier_map_t))) == NULL) {
-		LM_ERR("out of shared memory\n");
-		return -1;
-	}
-	memset(tmp, 0, sizeof(struct carrier_map_t));
-	if (shm_str_dup(&tmp->name, tree)!=0) {
-		LM_ERR("cannot duplicate string\n");
-		shm_free(tmp);
-		return -1;
-	}
-	tmp->index = index;
-	tmp->id = carrier_id;
-	if (!prev) {
-		*carrier_map = tmp;
-	} else {
-		prev->next = tmp;
-	}
-	LM_INFO("tree %.*s has internal id %i\n", tree->len, tree->s, index);
-	return index;
+str * map_id2name(struct name_map_t * map, int size, int id) {
+	struct name_map_t key;
+	struct name_map_t * tmp;
+
+	key.id = id;
+	tmp = bsearch(&key, map, size, sizeof(struct name_map_t), compare_name_map);
+	if (tmp == NULL) return NULL;
+	return &tmp->name;
 }
 
 
 /**
- * Searches for the ID for a Carrier-Name
+ * Compares the IDs of two name_map_t structures.
  *
- * @param carrier_name the carrier name, we are looking for
- *
- * @return values: on succcess the id for this carrier name,
- * -1 on failure
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
  */
-int find_carrier(const str * carrier_name) {
-	struct carrier_map_t * tmp;
-	if (!carrier_map) {
-		return -1;
-	}
-	if (carrier_name->len <= 0) {
-		return -1;
-	}
-	tmp = *carrier_map;
-
-	while (tmp) {
-		if (str_strcmp(carrier_name, &tmp->name) == 0) {
-			return tmp->id;
-		}
-		tmp = tmp->next;
-	}
-	return -1;
-}
-
-
-/**
- * Destroy the carrier map by freeing its memory.
- */
-void destroy_carrier_map(void) {
-	struct carrier_map_t * tmp;
-	if (carrier_map) {
-		tmp = *carrier_map;
-		while (*carrier_map) {
-			tmp = *carrier_map;
-			*carrier_map = tmp->next;
-			shm_free(tmp);
-		}
-		shm_free(carrier_map);
-		carrier_map = NULL;
-	}
+int compare_name_map(const void *v1, const void *v2) {
+	if (((struct name_map_t *)v1)->id < ((struct name_map_t *)v2)->id) return -1;
+	else if (((struct name_map_t *)v1)->id > ((struct name_map_t *)v2)->id) return 1;
+	else return 0;
 }

+ 22 - 26
modules/carrierroute/cr_map.h

@@ -34,50 +34,46 @@
 
 
 /**
- * Tries to add a domain to the domain map. If the given domain doesn't
- * exist, it is added. Otherwise, nothing happens.
- *
- * @param domain the domain to be added
- *
- * @return values: on succcess the numerical index of the given domain,
- * -1 on failure
+ * used to map names to numbers for faster access.
  */
-int add_domain(const str * domain);
+struct name_map_t {
+	str name; /*!< name of the routing domain or carrier */
+	int id; /*!< the corresponding id */
+};
 
 
-/**
- * Destroy the domain map by freeing its memory.
- */
-void destroy_domain_map(void);
 
 
 /**
- * Tries to add a carrier name to the carrier map. If the given carrier
- * doesn't exist, it is added. Otherwise, nothing happens.
+ * Searches for the ID of a name
  *
- * @param carrier_name the carrier name to be added
- * @param carrier_id the corresponding id
+ * @param map the mapping list to search in
+ * @param size the size of the list
+ * @param name the name, we are looking for
  *
- * @return values: on succcess the numerical index of the given carrier,
- * -1 on failure
+ * @return values: on succcess the id for this name, -1 on failure
  */
-int add_carrier(const str * tree, int carrier_id);
+int map_name2id(struct name_map_t * map, int size, const str * name);
 
 
 /**
- * Searches for the ID for a Carrier-Name
+ * Searches for the name of an ID
  *
- * @param carrier_name the carrier name, we are looking for
+ * @param map the mapping list to search in
+ * @param size the size of the list
+ * @param id the id, we are looking for
  *
- * @return values: on succcess the id for this carrier name,
- * -1 on failure
+ * @return values: on succcess the name for this id, NULL on failure
  */
-int find_carrier(const str * carrier_name);
+str * map_id2name(struct name_map_t * map, int size, int id);
 
 
 /**
- * Destroy the carrier map by freeing its memory.
+ * Compares the IDs of two name_map_t structures.
+ *
+ * @return -1 if v1 < v2, 0 if v1 == v2, 1 if v1 > v2
  */
-void destroy_carrier_map(void);
+int compare_name_map(const void *v1, const void *v2);
+
 
 #endif

+ 1 - 1
modules/carrierroute/cr_rule.c

@@ -386,7 +386,7 @@ struct failure_route_rule *add_failure_route_rule(struct failure_route_rule **fr
 	return shm_frr;
 	
 mem_error:
-	LM_ERR("out of shared memory\n");
+	SHM_MEM_ERROR;
 	destroy_failure_route_rule(shm_frr);
 	return NULL;
 }

+ 21 - 8
modules/carrierroute/db_carrierroute.c

@@ -62,14 +62,23 @@ str carrierfailureroute_description_col = str_init("description");
 /* table version */
 const unsigned int carrierfailureroute_version = 1;
 
-str route_tree_table = str_init("route_tree");
+str carrier_name_table = str_init("carrier_name");
 
 /* column names */
-str route_tree_id_col = str_init("id");
-str route_tree_carrier_col = str_init("carrier");
+str carrier_name_id_col = str_init("id");
+str carrier_name_carrier_col = str_init("carrier");
 
 /* table version */
-const unsigned int route_tree_version = 1;
+const unsigned int carrier_name_version = 1;
+
+str domain_name_table = str_init("domain_name");
+
+/* column names */
+str domain_name_id_col = str_init("id");
+str domain_name_domain_col = str_init("domain");
+
+/* table version */
+const unsigned int domain_name_version = 1;
 
 
 /*
@@ -105,7 +114,8 @@ int carrierroute_db_init(void) {
 	if (
 	(db_check_table_version(&carrierroute_dbf, carrierroute_dbh, &carrierroute_table, carrierroute_version) < 0) ||
 	(db_check_table_version(&carrierroute_dbf, carrierroute_dbh, &carrierfailureroute_table, carrierfailureroute_version) < 0) ||
-	(db_check_table_version(&carrierroute_dbf, carrierroute_dbh, &route_tree_table, route_tree_version) < 0)
+	(db_check_table_version(&carrierroute_dbf, carrierroute_dbh, &carrier_name_table, carrier_name_version) < 0) ||
+	(db_check_table_version(&carrierroute_dbf, carrierroute_dbh, &domain_name_table, domain_name_version) < 0)
 	) {
 		LM_ERR("during table version check.\n");
 		carrierroute_db_close();
@@ -165,8 +175,11 @@ void carrierroute_db_vars(void) {
 	carrierfailureroute_mask_col.len = strlen(carrierfailureroute_mask_col.s);
 	carrierfailureroute_next_domain_col.len = strlen(carrierfailureroute_next_domain_col.s);
 	carrierfailureroute_description_col.len = strlen(carrierfailureroute_description_col.s);
-	route_tree_table.len = strlen(route_tree_table.s);
-	route_tree_id_col.len = strlen(route_tree_id_col.s);
-	route_tree_carrier_col.len = strlen(route_tree_carrier_col.s);
+	carrier_name_table.len = strlen(carrier_name_table.s);
+	carrier_name_id_col.len = strlen(carrier_name_id_col.s);
+	carrier_name_carrier_col.len = strlen(carrier_name_carrier_col.s);
+	domain_name_table.len = strlen(domain_name_table.s);
+	domain_name_id_col.len = strlen(domain_name_id_col.s);
+	domain_name_domain_col.len = strlen(domain_name_domain_col.s);
 }
 

+ 22 - 8
modules/carrierroute/db_carrierroute.h

@@ -101,19 +101,33 @@ extern str carrierfailureroute_description_col;
 /* table version */
 extern const unsigned int carrierfailureroute_version;
 
-#define route_tree_DB_TABLE { "route_tree_table", STR_PARAM, &carrierroute_table.s },
+#define carrier_name_DB_TABLE { "carrier_name_table", STR_PARAM, &carrierroute_table.s },
 
-extern str route_tree_table;
+extern str carrier_name_table;
 
 /* column names */
-extern str route_tree_id_col;
-extern str route_tree_carrier_col;
-#define route_tree_DB_COLS \
-{ "route_tree_id_col", STR_PARAM, &route_tree_id_col.s }, \
-{ "route_tree_carrier_col", STR_PARAM, &route_tree_carrier_col.s }, \
+extern str carrier_name_id_col;
+extern str carrier_name_carrier_col;
+#define carrier_name_DB_COLS \
+{ "carrier_name_id_col", STR_PARAM, &carrier_name_id_col.s }, \
+{ "carrier_name_carrier_col", STR_PARAM, &carrier_name_carrier_col.s }, \
 
 /* table version */
-extern const unsigned int route_tree_version;
+extern const unsigned int carrier_name_version;
+
+#define domain_name_DB_TABLE { "domain_name_table", STR_PARAM, &carrierroute_table.s },
+
+extern str domain_name_table;
+
+/* column names */
+extern str domain_name_id_col;
+extern str domain_name_domain_col;
+#define domain_name_DB_COLS \
+{ "domain_name_id_col", STR_PARAM, &domain_name_id_col.s }, \
+{ "domain_name_domain_col", STR_PARAM, &domain_name_domain_col.s }, \
+
+/* table version */
+extern const unsigned int domain_name_version;
 
 
 /*

+ 25 - 13
modules/carrierroute/doc/carrierroute_admin.xml

@@ -12,7 +12,7 @@
 		startup. It can uses one routing tree (for one carrier), or if needed for every user
 		a different routing tree (unique for each carrier) for number prefix based routing.
 		It supports several route tree domains,	e.g. for failback routes or different routing
-		rules for VoIP and PSTN targets. 
+		rules for VoIP and PSTN targets.
 	</para>
 	<para>
 		Based on the tree, the module decides which number prefixes are forwarded to which
@@ -22,16 +22,18 @@
 	</para>
 	<para>
 		This modules scales up to more than a few million users, and is able to handle
-		more than several hundred thousand routing table entries. It should be able to handle
-		more, but this is not that much tested at the moment. In load balancing scenarios the
-		usage of the config file mode is recommended, to avoid the additional complexity that
-		the database driven routing creates.
+		more than several hundred thousand routing table entries. We recieved reports of
+		some setups that used more than a million routing table entries. It also supports
+		a large number of carriers and domains which can be efficiently looked up in most of
+		the cases (see below for more informations). In load balancing scenarios the usage
+		of the config file mode is recommended, to avoid the additional complexity that the
+		database driven routing creates.
 	</para>
 	<para>
-		Routing tables can be reloaded and edited (in config file mode) with the MI 
-		interface, the config file is updated according the changes. This is not 
-		implemented for the db interface, because its easier to do the changes 
-		directly on the db. But the reload and dump functions works of course here 
+		Routing tables can be reloaded and edited (in config file mode) with the MI
+		interface, the config file is updated according the changes. This is not
+		implemented for the db interface, because its easier to do the changes
+		directly on the db. But the reload and dump functions works of course here
 		too.
 	</para>
 	<para>
@@ -40,6 +42,16 @@
 		tables in the config file. Further information about these limitations is given
 		in later sections. For user based routing or LCR you should use the database mode.
 	</para>
+	<para>
+		In database mode, this module supports names and IDs for the carriers and domains.
+		When using IDs for the routing functions, efficient binary search is used to find the
+		needed data structures. If you are using constant strings as parameter, these will
+		be converted to IDs during the fixup procedure. However, if you are using AVPs as
+		parameter and they contain strings, this cannot be converted to IDs during the
+		fixup procedure. In that case linear search is performed to find the needed data
+		structures. So from a performance point of view it is better to pass only IDs in
+		AVPs to the routing functions.
+	</para>
 	<para>
 		Basically this module could be used as an replacement for the lcr and the 
 		dispatcher module, if you have certain performance, flexibility and/or 
@@ -943,8 +955,8 @@ domain register {
 | 9  |       2 |      0 | 49          |     0 |  0.5 | de-2.carrier2 |
 | 10 |       2 |      0 |             |     0 |    1 | gw.carrier2   |
 | 11 |       2 |      1 | 49          |     0 |    1 | gw.carrier2   |
-| 12 |       3 |  start | 49          |     0 |    1 | de-gw.default |
-| 13 |       3 |  start |             |     0 |    1 | gw.default    |
+| 12 |       3 |      8 | 49          |     0 |    1 | de-gw.default |
+| 13 |       3 |      8 |             |     0 |    1 | gw.default    |
 +----+---------+--------+-------------+-------+------+---------------+
 ...
 		</programlisting>
@@ -1003,7 +1015,7 @@ domain register {
 |  1 |      99 |           | 408        |    16 |   16 |             |
 |  2 |      99 | gw1       | 404        |     0 |    0 | 100         |
 |  3 |      99 | gw2       | 50.        |     0 |    0 | 100         |
-|  4 |      99 |           | 404        |  2048 | 2112 | asterisk-1  |
+|  4 |      99 |           | 404        |  2048 | 2112 | 101         |
 +----+---------+-----------+------------+-------+------+-------------+
 ...
 </programlisting>
@@ -1023,7 +1035,7 @@ domain register {
 		</para>
 
 	<example>
-		<title>Example database content - route_tree table</title>
+		<title>Example database content - carrier_name table</title>
 		<programlisting format="linespecific">
 ...
 +----+----------+

+ 98 - 27
modules/carrierroute/doc/carrierroute_db.xml

@@ -42,7 +42,7 @@ modparam("carrierroute", "carrierroute_table", "carrierroute")
   </section>
   <section>
     <title><varname>carrierroute_id_col</varname> (string)</title>
-    <para>unique ID</para>
+    <para>Name of the column contains the unique identifier of a route.</para>
     <example>
       <title>Set <varname>carrierroute_id_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -66,7 +66,9 @@ modparam("carrierroute", "carrierroute_carrier_col", "carrier")
   </section>
   <section>
     <title><varname>carrierroute_domain_col</varname> (string)</title>
-    <para>This column contains the route domain. Additional domains could be used for example as fallback.</para>
+    <para>This column contains the routing domain id. You can define several routing
+			  domains to have different routing rules. Maybe you use domain 0 for normal routing and
+			  domain 1 if domain 0 failed.</para>
     <example>
       <title>Set <varname>carrierroute_domain_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -78,7 +80,11 @@ modparam("carrierroute", "carrierroute_domain_col", "domain")
   </section>
   <section>
     <title><varname>carrierroute_scan_prefix_col</varname> (string)</title>
-    <para>This column contains the scan prefix, which define the matching portion of a phone number.</para>
+    <para>Name of column contains the scan prefixes. Scan prefixes define
+			  the matching portion of a phone number, e.g. when we have the scan prefixes 49721
+			  and 49, the called number is 49721913740, it matches 49721, because the longest
+			  match is taken. If no prefix matches, the number is not routed. To prevent this,
+			  an empty prefix value of  could be added.</para>
     <example>
       <title>Set <varname>carrierroute_scan_prefix_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -102,7 +108,8 @@ modparam("carrierroute", "carrierroute_flags_col", "flags")
   </section>
   <section>
     <title><varname>carrierroute_mask_col</varname> (string)</title>
-    <para>This column contains the mask that is applied to the message flags before rule matching.</para>
+    <para>This column contains the mask that is applied to the message flags before rule 
+			  matching.</para>
     <example>
       <title>Set <varname>carrierroute_mask_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -114,7 +121,20 @@ modparam("carrierroute", "carrierroute_mask_col", "mask")
   </section>
   <section>
     <title><varname>carrierroute_prob_col</varname> (string)</title>
-    <para>Name of column containing the probability. The probability value is used to distribute the traffic between several gateways.</para>
+    <para>
+			  Name of column contains the probability. The probability value is used to
+			  distribute the traffic between several gateways. Let's say 70 % of the
+			  traffic shall be routed to gateway A, the other 30 % shall be routed to
+			  gateway B, we define a rule for gateway A with a prob value of 0.7 and a
+			  rule for gateway B with a prob value of 0.3.
+			  
+			  
+			  If all probabilities for a given prefix, tree and domain don't add to 100%,
+			  the prefix values will be adjusted according the given prob values. E.g. if
+			  three hosts with prob values of 0.5, 0.5 and 0.4 are defined, the resulting
+			  probabilities are 35.714, 35.714 and 28.571%. But its better to choose meaningful
+			  values in the first place because of clarity.
+			  </para>
     <example>
       <title>Set <varname>carrierroute_prob_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -126,7 +146,7 @@ modparam("carrierroute", "carrierroute_prob_col", "prob")
   </section>
   <section>
     <title><varname>carrierroute_strip_col</varname> (string)</title>
-    <para>Name of the column containing the number of digits to be stripped of the userpart of an URI before prepending rewrite_prefix.</para>
+    <para>Name of the column contains the number of digits to be stripped of the userpart of an URI before prepending rewrite_prefix.</para>
     <example>
       <title>Set <varname>carrierroute_strip_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -138,7 +158,9 @@ modparam("carrierroute", "carrierroute_strip_col", "strip")
   </section>
   <section>
     <title><varname>carrierroute_rewrite_host_col</varname> (string)</title>
-    <para>Name of column containing rewrite prefixes. Here you can define a rewrite prefix for the localpart of the SIP URI.</para>
+    <para>Name of column contains the rewrite prefixes. Here you can define a rewrite prefix
+			  for the localpart of the SIP URI. An empty field represents a blacklist entry, anything else
+			  is put as domain part into the Request URI of the SIP message.</para>
     <example>
       <title>Set <varname>carrierroute_rewrite_host_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -150,7 +172,8 @@ modparam("carrierroute", "carrierroute_rewrite_host_col", "rewrite_host")
   </section>
   <section>
     <title><varname>carrierroute_rewrite_prefix_col</varname> (string)</title>
-    <para>Rewrite prefix for the localpart of the SIP URI.</para>
+    <para>Name of column contains the rewrite prefixes. Here you can define a rewrite
+			  prefix for the localpart of the SIP URI.</para>
     <example>
       <title>Set <varname>carrierroute_rewrite_prefix_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -162,7 +185,8 @@ modparam("carrierroute", "carrierroute_rewrite_prefix_col", "rewrite_prefix")
   </section>
   <section>
     <title><varname>carrierroute_rewrite_suffix_col</varname> (string)</title>
-    <para>Rewrite suffix for the localpart of the SIP URI.</para>
+    <para>Name of column contains the rewrite suffixes. Here you can define a rewrite
+			  suffix for the localpart of the SIP URI.</para>
     <example>
       <title>Set <varname>carrierroute_rewrite_suffix_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -174,7 +198,8 @@ modparam("carrierroute", "carrierroute_rewrite_suffix_col", "rewrite_suffix")
   </section>
   <section>
     <title><varname>carrierroute_description_col</varname> (string)</title>
-    <para>A comment for the route entry, useful for larger routing tables.</para>
+    <para>A comment for the route entry, useful for larger routing tables.
+			  The comment is also displayed by the fifo cmd "cr_dump_routes".</para>
     <example>
       <title>Set <varname>carrierroute_description_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -201,7 +226,7 @@ modparam("carrierroute", "carrierfailureroute_table", "carrierfailureroute")
   </section>
   <section>
     <title><varname>carrierfailureroute_id_col</varname> (string)</title>
-    <para>unique ID</para>
+    <para>This column contains the unique identifier of a failure route.</para>
     <example>
       <title>Set <varname>carrierfailureroute_id_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -225,7 +250,9 @@ modparam("carrierroute", "carrierfailureroute_carrier_col", "carrier")
   </section>
   <section>
     <title><varname>carrierfailureroute_domain_col</varname> (string)</title>
-    <para>This column contains the route domain. Additional domains could be used for example as fallback.</para>
+    <para>This column contains the routing domain id. You can define several routing domains
+			  to have different routing rules. Maybe you use domain 0 for normal routing and domain 1 if
+			  domain 0 failed.</para>
     <example>
       <title>Set <varname>carrierfailureroute_domain_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -237,7 +264,11 @@ modparam("carrierroute", "carrierfailureroute_domain_col", "domain")
   </section>
   <section>
     <title><varname>carrierfailureroute_scan_prefix_col</varname> (string)</title>
-    <para>This column contains the scan prefix, which define the matching portion of a phone number.</para>
+    <para>Name of column contains the the scan prefixes. Scan prexies define the matching
+			  portion of a phone number, e.g. we have the scan prefixes 49721 and 49, the called number is
+			  49721913740, it matches 49721, because the longest match is taken. If no prefix matches,
+			  the number is not failure routed. To prevent this, an empty prefix value of 
+			  could be added.</para>
     <example>
       <title>Set <varname>carrierfailureroute_scan_prefix_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -249,7 +280,8 @@ modparam("carrierroute", "carrierfailureroute_scan_prefix_col", "scan_prefix")
   </section>
   <section>
     <title><varname>carrierfailureroute_host_name_col</varname> (string)</title>
-    <para>This column contains the routing destination used for rule matching.</para>
+    <para>Name of the column containing the host name of the last routing destination,
+			  using for rules matching.</para>
     <example>
       <title>Set <varname>carrierfailureroute_host_name_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -297,7 +329,7 @@ modparam("carrierroute", "carrierfailureroute_mask_col", "mask")
   </section>
   <section>
     <title><varname>carrierfailureroute_next_domain_col</varname> (string)</title>
-    <para>This column contains the route domain that should be used for the next routing attempt.</para>
+    <para>This column contains the route domain id that should be used for the next routing attempt.</para>
     <example>
       <title>Set <varname>carrierfailureroute_next_domain_col</varname> parameter</title>
       <programlisting format="linespecific">
@@ -320,40 +352,79 @@ modparam("carrierroute", "carrierfailureroute_description_col", "description")
     </example>
   </section>
   <section>
-    <title><varname>route_tree_table</varname> (String)</title>
-    <para>Name of the route_tree table for the carrierroute module.</para>
+    <title><varname>carrier_name_table</varname> (String)</title>
+    <para>Name of the carrier_name table for the carrierroute module.</para>
     <para>
-      <emphasis>Default value is <quote>route_tree</quote>.</emphasis>
+      <emphasis>Default value is <quote>carrier_name</quote>.</emphasis>
     </para>
     <example>
-      <title>Set <varname>route_tree_table</varname> parameter</title>
+      <title>Set <varname>carrier_name_table</varname> parameter</title>
       <programlisting format="linespecific">
 ...
-modparam("carrierroute", "route_tree_table", "route_tree")
+modparam("carrierroute", "carrier_name_table", "carrier_name")
 ...
 </programlisting>
     </example>
   </section>
   <section>
-    <title><varname>route_tree_id_col</varname> (string)</title>
-    <para>unique ID</para>
+    <title><varname>carrier_name_id_col</varname> (string)</title>
+    <para>Name of the column containing the unique identifier of a carrier.</para>
     <example>
-      <title>Set <varname>route_tree_id_col</varname> parameter</title>
+      <title>Set <varname>carrier_name_id_col</varname> parameter</title>
       <programlisting format="linespecific">
 ...
-modparam("carrierroute", "route_tree_id_col", "id")
+modparam("carrierroute", "carrier_name_id_col", "id")
 ...
 </programlisting>
     </example>
   </section>
   <section>
-    <title><varname>route_tree_carrier_col</varname> (string)</title>
+    <title><varname>carrier_name_carrier_col</varname> (string)</title>
     <para>This column contains the carrier name.</para>
     <example>
-      <title>Set <varname>route_tree_carrier_col</varname> parameter</title>
+      <title>Set <varname>carrier_name_carrier_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "carrier_name_carrier_col", "carrier")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>domain_name_table</varname> (String)</title>
+    <para>Name of the domain_name table for the carrierroute module.</para>
+    <para>
+      <emphasis>Default value is <quote>domain_name</quote>.</emphasis>
+    </para>
+    <example>
+      <title>Set <varname>domain_name_table</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "domain_name_table", "domain_name")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>domain_name_id_col</varname> (string)</title>
+    <para>Name of the column containing the unique identifier of a domain.</para>
+    <example>
+      <title>Set <varname>domain_name_id_col</varname> parameter</title>
+      <programlisting format="linespecific">
+...
+modparam("carrierroute", "domain_name_id_col", "id")
+...
+</programlisting>
+    </example>
+  </section>
+  <section>
+    <title><varname>domain_name_domain_col</varname> (string)</title>
+    <para>This column contains the domain name.</para>
+    <example>
+      <title>Set <varname>domain_name_domain_col</varname> parameter</title>
       <programlisting format="linespecific">
 ...
-modparam("carrierroute", "route_tree_carrier_col", "carrier")
+modparam("carrierroute", "domain_name_domain_col", "domain")
 ...
 </programlisting>
     </example>