Skip to content
项目
群组
代码片段
帮助
正在加载...
登录
切换导航
F
freeswitch-contrib
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
张华
freeswitch-contrib
Commits
c1a3bce0
提交
c1a3bce0
authored
12月 11, 2012
作者:
Christopher Rienzo
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
add mod_simple_lcr to contrib
上级
657d5b14
隐藏空白字符变更
内嵌
并排
正在显示
10 个修改的文件
包含
2121 行增加
和
0 行删除
+2121
-0
Makefile.am
crienzo/mod_simple_lcr/Makefile.am
+9
-0
simple_lcr.conf.xml
.../mod_simple_lcr/conf/autoload_configs/simple_lcr.conf.xml
+21
-0
areas.csv
crienzo/mod_simple_lcr/conf/ratedecks/areas.csv
+292
-0
ratedeck1.csv
crienzo/mod_simple_lcr/conf/ratedecks/ratedeck1.csv
+10
-0
ratedeck2.csv
crienzo/mod_simple_lcr/conf/ratedecks/ratedeck2.csv
+10
-0
mod_simple_lcr.c
crienzo/mod_simple_lcr/mod_simple_lcr.c
+1080
-0
route_trie.c
crienzo/mod_simple_lcr/route_trie.c
+413
-0
route_trie.h
crienzo/mod_simple_lcr/route_trie.h
+170
-0
Makefile
crienzo/mod_simple_lcr/test/Makefile
+3
-0
test.c
crienzo/mod_simple_lcr/test/test.c
+113
-0
没有找到文件。
crienzo/mod_simple_lcr/Makefile.am
0 → 100644
浏览文件 @
c1a3bce0
include
$(top_srcdir)/build/modmake.rulesam
MODNAME
=
mod_simple_lcr
mod_LTLIBRARIES
=
mod_simple_lcr.la
mod_simple_lcr_la_SOURCES
=
mod_simple_lcr.c route_trie.c
mod_simple_lcr_la_CFLAGS
=
$(AM_CFLAGS)
mod_simple_lcr_la_LIBADD
=
$(switch_builddir)
/libfreeswitch.la
mod_simple_lcr_la_LDFLAGS
=
-avoid-version
-module
-no-undefined
-shared
crienzo/mod_simple_lcr/conf/autoload_configs/simple_lcr.conf.xml
0 → 100644
浏览文件 @
c1a3bce0
<configuration
name=
"simple_lcr.conf"
description=
"fast and simple in-memory LCR"
>
<settings>
<!-- number of routes to reply with on queries -->
<param
name=
"max-routes"
value=
"3"
/>
<!-- NPANXX to state mappings -->
<param
name=
"areas"
value=
"$${base_dir}/conf/ratedecks/areas.csv"
/>
<param
name=
"areas-cid-strip"
value=
"0"
/>
<param
name=
"areas-cid-prefix"
value=
""
/>
<param
name=
"areas-dest-strip"
value=
"0"
/>
<param
name=
"areas-dest-prefix"
value=
""
/>
</settings>
<!-- routes -->
<carrier
name=
"A"
ratedeck=
"$${base_dir}/conf/ratedecks/ratedeck1.csv"
>
<gateway
prefix=
"sip:"
suffix=
"@127.0.0.1"
/>
</carrier>
<carrier
name=
"B"
ratedeck=
"$${base_dir}/conf/ratedecks/ratedeck2.csv"
>
<gateway
prefix=
"sip:"
suffix=
"@127.0.0.2"
/>
</carrier>
</configuration>
crienzo/mod_simple_lcr/conf/ratedecks/areas.csv
0 → 100644
浏览文件 @
c1a3bce0
201,NJ
202,DC
203,CT
205,AL
206,WA
207,ME
208,ID
209,CA
210,TX
212,NY
213,CA
214,TX
215,PA
216,OH
217,IL
218,MN
219,IN
224,IL
225,LA
228,MS
229,GA
231,MI
234,OH
239,FL
240,MD
248,MI
251,AL
252,NC
253,WA
254,TX
256,AL
260,IN
262,WI
267,PA
269,MI
270,KY
276,VA
281,TX
289,ON
301,MD
302,DE
303,CO
304,WV
305,FL
307,WY
308,NE
309,IL
310,CA
312,IL
313,MI
314,MO
315,NY
316,KS
317,IN
318,LA
319,IA
320,MN
321,FL
323,CA
325,TX
330,OH
331,IL
334,AL
336,NC
337,LA
339,MA
340,VI
347,NY
351,MA
352,FL
360,WA
361,TX
386,FL
401,RI
402,NE
404,GA
405,OK
406,MT
407,FL
408,CA
409,TX
410,MD
412,PA
413,MA
414,WI
415,CA
417,MO
419,OH
423,TN
424,CA
425,WA
430,TX
432,TX
434,VA
435,UT
440,OH
443,MD
464,IL
469,TX
470,GA
475,CT
478,GA
479,AR
480,AZ
484,PA
501,AR
502,KY
503,OR
504,LA
505,NM
507,MN
508,MA
509,WA
510,CA
512,TX
513,OH
515,IA
516,NY
517,MI
518,NY
520,AZ
530,CA
540,VA
541,OR
551,NJ
559,CA
561,FL
562,CA
563,IA
567,OH
570,PA
571,VA
573,MO
574,IN
580,OK
585,NY
586,MI
601,MS
602,AZ
603,NH
605,SD
606,KY
607,NY
608,WI
609,NJ
610,PA
612,MN
614,OH
615,TN
616,MI
617,MA
618,IL
619,CA
620,KS
623,AZ
626,CA
630,IL
631,NY
636,MO
641,IA
646,NY
650,CA
651,MN
660,MO
661,CA
662,MS
670,MP
671,GU
678,GA
679,MI
682,TX
684,AS
701,ND
702,NV
703,VA
704,NC
706,GA
707,CA
708,IL
712,IA
713,TX
714,CA
715,WI
716,NY
717,PA
718,NY
719,CO
720,CO
724,PA
727,FL
731,TN
732,NJ
734,MI
740,OH
747,CA
754,FL
757,VA
760,CA
762,GA
763,MN
765,IN
769,MS
770,GA
772,FL
773,IL
774,MA
775,NV
779,IL
781,MA
785,KS
786,FL
787,PR
801,UT
802,VT
803,SC
804,VA
805,CA
806,TX
808,HI
810,MI
812,IN
813,FL
814,PA
815,IL
816,MO
817,TX
818,CA
819,QC
828,NC
830,TX
831,CA
832,TX
843,SC
845,NY
847,IL
848,NJ
850,FL
856,NJ
857,MA
858,CA
859,KY
860,CT
862,NJ
863,FL
864,SC
865,TN
870,AR
872,IL
878,PA
901,TN
903,TX
904,FL
905,ON
906,MI
907,AK
908,NJ
909,CA
910,NC
912,GA
913,KS
914,NY
915,TX
916,CA
917,NY
918,OK
919,NC
920,WI
925,CA
928,AZ
931,TN
936,TX
937,OH
939,PR
940,TX
941,FL
947,MI
949,CA
951,CA
952,MN
954,FL
956,TX
959,CT
970,CO
971,OR
972,TX
973,NJ
978,MA
979,TX
980,NC
985,LA
989,MI
575,NM
crienzo/mod_simple_lcr/conf/ratedecks/ratedeck1.csv
0 → 100644
浏览文件 @
c1a3bce0
1,0.0032,0.0031
2,0.0032,0.0031
3,0.0032,0.0031
4,0.0032,0.0031
5,0.0032,0.0031
6,0.0032,0.0031
7,0.0032,0.0031
8,0.0032,0.0031
9,0.0032,0.0031
0,0.0032,0.0031
crienzo/mod_simple_lcr/conf/ratedecks/ratedeck2.csv
0 → 100644
浏览文件 @
c1a3bce0
1,0.0042,0.0021
2,0.0042,0.0021
3,0.0042,0.0021
4,0.0042,0.0021
5,0.0042,0.0021
6,0.0042,0.0021
7,0.0042,0.0021
8,0.0042,0.0021
9,0.0042,0.0021
0,0.0042,0.0021
crienzo/mod_simple_lcr/mod_simple_lcr.c
0 → 100644
浏览文件 @
c1a3bce0
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Christopher M. Rienzo <chris@rienzo.com>
* Ken Rice <krice@rmktek.com> (dialplan and LCR ideas based on mod_biwara)
*
* Maintainer: Christopher M. Rienzo <chris@rienzo.com>
*
* mod_simple_lcr.c -- Simple in-memory LCR.
*
*/
#include <switch.h>
#include "route_trie.h"
SWITCH_MODULE_LOAD_FUNCTION
(
mod_simple_lcr_load
);
SWITCH_MODULE_SHUTDOWN_FUNCTION
(
mod_simple_lcr_shutdown
);
SWITCH_MODULE_DEFINITION
(
mod_simple_lcr
,
mod_simple_lcr_load
,
mod_simple_lcr_shutdown
,
NULL
);
#define MAX_CARRIERS 256
#define MAX_GATEWAYS 8
#define MAX_ROUTES 8
#define MAX_INTER_AREAS 16
#define DEFAULT_MAX_ROUTES 3
/**
* A carrier gateway
*/
struct
gateway
{
/** prefix to prepend to dial string */
char
*
prefix
;
/** suffix to append to dial string */
char
*
suffix
;
};
/**
* A carrier to route calls to
*/
struct
carrier
{
/** ID of this carrier */
route_value_t
id
;
/** The name of the carrier */
char
*
name
;
/** The number of gateways */
int
num_gateways
;
/** The carrier gateway addresses */
struct
gateway
*
gateway
[
MAX_GATEWAYS
];
/** Load balancing synchronization */
switch_mutex_t
*
mutex
;
/** Next gateway to select */
int
next_gateway
;
};
/**
* LCR database
*/
struct
lcr
{
/** memory pool */
switch_memory_pool_t
*
pool
;
/** The carrier inter-area route prefix tree */
struct
route_trie
*
inter_trie
;
/** The carrier intra-area route prefix tree */
struct
route_trie
*
intra_trie
;
/** map of prefixes to area */
struct
route_trie
*
areas
;
/** Caller ID prefixes that will always be rated as inter calls */
struct
route_trie
*
inter_areas
;
/** The carriers. 1-based index */
struct
carrier
*
carriers
[
MAX_CARRIERS
+
1
];
/** The number of carriers stored in this database */
int
num_carriers
;
/** Maximum number of routes to return on queries */
int
max_routes
;
/** Digits to strip from caller ID when looking up area */
int
areas_caller_id_strip
;
/** Digits to prefix to caller ID when looking up area */
char
*
areas_caller_id_prefix
;
/** Digits to strip from dialed number when looking up area */
int
areas_dest_strip
;
/** Digits to prefix to caller ID when looking up area */
char
*
areas_dest_prefix
;
/** prevents destruction of database while readers exist */
switch_thread_rwlock_t
*
mutex
;
};
/**
* Module global variables
*/
static
struct
{
/** synchronizes access to the LCR database pointer */
switch_mutex_t
*
update_lock
;
/** lcr database */
struct
lcr
*
lcr
;
}
globals
;
/**
* Create a new carrier gateway
* @param pool the memory pool to use
* @param prefix the prefix to prepend to dial strings
* @param suffix the suffix to append to dial strings
* @return the gateway
*/
static
struct
gateway
*
gateway_create
(
switch_memory_pool_t
*
pool
,
const
char
*
prefix
,
const
char
*
suffix
)
{
struct
gateway
*
gateway
=
switch_core_alloc
(
pool
,
sizeof
(
*
gateway
));
gateway
->
prefix
=
switch_core_strdup
(
pool
,
prefix
);
gateway
->
suffix
=
switch_core_strdup
(
pool
,
suffix
);
return
gateway
;
}
/**
* Build a dial string for this gateway
* @param gateway the gateway
* @param buf the string to fill
* @param max_len the buf length
* @param dial_digits the digits to dial
* @return SWITCH_STATUS_SUCCESS if succesful
*/
static
switch_status_t
gateway_get_dialstring
(
struct
gateway
*
gateway
,
char
*
buf
,
size_t
max_len
,
const
char
*
dial_digits
)
{
if
(
snprintf
(
buf
,
max_len
,
"%s%s%s"
,
gateway
->
prefix
,
dial_digits
,
gateway
->
suffix
)
>
max_len
)
{
return
SWITCH_STATUS_FALSE
;
}
return
SWITCH_STATUS_SUCCESS
;
}
/**
* Create a new carrier to route calls to
* @param pool the memory pool to use
* @param name the name of the carrier
* @param id the carrier ID
* @return the carrier
*/
static
struct
carrier
*
carrier_create
(
switch_memory_pool_t
*
pool
,
const
char
*
name
,
route_value_t
id
)
{
struct
carrier
*
carrier
=
switch_core_alloc
(
pool
,
sizeof
(
*
carrier
));
carrier
->
name
=
switch_core_strdup
(
pool
,
name
);
carrier
->
id
=
id
;
if
(
switch_mutex_init
(
&
carrier
->
mutex
,
SWITCH_MUTEX_UNNESTED
,
pool
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to create carrier mutex!
\n
"
);
return
NULL
;
}
return
carrier
;
}
/**
* Add a gateway to a carrier
* @param pool the memory pool to use
* @param carrier the carrier
* @param prefix the gateway prefix
* @param suffix the gateway suffix
* @return SWITCH_STATUS_SUCCESS if successful
*/
static
switch_status_t
carrier_add_gateway
(
switch_memory_pool_t
*
pool
,
struct
carrier
*
carrier
,
const
char
*
prefix
,
const
char
*
suffix
)
{
if
(
carrier
->
num_gateways
<
MAX_GATEWAYS
)
{
carrier
->
gateway
[
carrier
->
num_gateways
]
=
gateway_create
(
pool
,
prefix
,
suffix
);
carrier
->
num_gateways
++
;
return
SWITCH_STATUS_SUCCESS
;
}
return
SWITCH_STATUS_FALSE
;
}
/**
* Pick a gateway round-robin from a carrier
* @param carrier the carrier to pick a gateway from
* @return gateway the gateway
*/
static
struct
gateway
*
carrier_select_gateway
(
struct
carrier
*
carrier
)
{
struct
gateway
*
gateway
=
NULL
;
if
(
carrier
->
num_gateways
>
0
)
{
switch_mutex_lock
(
carrier
->
mutex
);
gateway
=
carrier
->
gateway
[
carrier
->
next_gateway
];
carrier
->
next_gateway
=
(
carrier
->
next_gateway
+
1
)
%
carrier
->
num_gateways
;
switch_mutex_unlock
(
carrier
->
mutex
);
}
return
gateway
;
}
/**
* Build a dial string for this carrier
* @param carrier the carrier
* @param buf the string to fill
* @param max_len the buf length
* @param dial_digits the digits to dial
* @return SWITCH_STATUS_SUCCESS if succesful
*/
static
switch_status_t
carrier_get_dialstring
(
struct
carrier
*
carrier
,
char
*
buf
,
size_t
max_len
,
const
char
*
dial_digits
)
{
struct
gateway
*
gateway
=
carrier_select_gateway
(
carrier
);
if
(
gateway
)
{
return
gateway_get_dialstring
(
gateway
,
buf
,
max_len
,
dial_digits
);
}
return
SWITCH_STATUS_FALSE
;
}
/**
* Get read access to LCR database
* @return LCR database
*/
static
struct
lcr
*
lcr_rdlock
(
void
)
{
struct
lcr
*
lcr
;
switch_mutex_lock
(
globals
.
update_lock
);
lcr
=
globals
.
lcr
;
switch_thread_rwlock_rdlock
(
lcr
->
mutex
);
switch_mutex_unlock
(
globals
.
update_lock
);
return
lcr
;
}
/**
* Release access to LCR database
*/
static
void
lcr_unlock
(
struct
lcr
*
lcr
)
{
if
(
lcr
)
{
switch_thread_rwlock_unlock
(
lcr
->
mutex
);
}
}
/**
* Replace the LCR database
* @param new_lcr the new LCR database
* @return lcr the old LCR database
*/
static
struct
lcr
*
lcr_replace
(
struct
lcr
*
new_lcr
)
{
struct
lcr
*
old_lcr
;
switch_mutex_lock
(
globals
.
update_lock
);
old_lcr
=
globals
.
lcr
;
globals
.
lcr
=
new_lcr
;
switch_mutex_unlock
(
globals
.
update_lock
);
return
old_lcr
;
}
/**
* Create a new LCR database
* @param num_carriers the maximum number of carriers to store
* @return the LCR database
*/
static
struct
lcr
*
lcr_create
(
int
num_carriers
)
{
switch_memory_pool_t
*
pool
=
NULL
;
struct
lcr
*
lcr
=
NULL
;
if
(
switch_core_new_memory_pool
(
&
pool
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to create LCR db memory pool!
\n
"
);
return
NULL
;
}
lcr
=
switch_core_alloc
(
pool
,
sizeof
(
*
lcr
));
lcr
->
pool
=
pool
;
if
(
switch_thread_rwlock_create
(
&
lcr
->
mutex
,
lcr
->
pool
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to create LCR mutex!
\n
"
);
return
NULL
;
}
lcr
->
max_routes
=
DEFAULT_MAX_ROUTES
;
lcr
->
num_carriers
=
num_carriers
;
lcr
->
inter_trie
=
route_trie_create
(
num_carriers
);
lcr
->
intra_trie
=
route_trie_create
(
num_carriers
);
lcr
->
areas
=
route_trie_create
(
1
);
lcr
->
areas_caller_id_strip
=
0
;
lcr
->
areas_caller_id_prefix
=
""
;
lcr
->
areas_dest_strip
=
1
;
lcr
->
areas_dest_prefix
=
""
;
lcr
->
inter_areas
=
route_trie_create
(
num_carriers
);
return
lcr
;
}
/**
* Set the maximum number of routes to return on lookup
* @param lcr the LCR database
* @param max_routes the maximum number of routes to return
*/
static
void
lcr_set_max_routes
(
struct
lcr
*
lcr
,
int
max_routes
)
{
if
(
max_routes
>=
MAX_ROUTES
)
{
lcr
->
max_routes
=
MAX_ROUTES
;
}
else
if
(
max_routes
>=
1
)
{
lcr
->
max_routes
=
max_routes
;
}
}
/**
* Search for call area
* @param lcr the LCR database
* @param lookup_number the number to check
* @return area ID or -1
*/
static
int
lcr_find_area
(
struct
lcr
*
lcr
,
const
char
*
lookup_number
)
{
int
area_id
=
-
1
;
struct
route_array
*
result
=
NULL
;
if
(
zstr
(
lookup_number
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"Skipping due to empty lookup_number
\n
"
);
return
-
1
;
}
/* search prefix tree for area ID */
result
=
route_array_create
(
1
);
if
(
route_trie_find
(
lcr
->
areas
,
lookup_number
,
result
))
{
struct
route
*
route
=
route_array_get_route
(
result
,
0
);
area_id
=
route_get_value
(
route
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG1
,
"Found area %d
\n
"
,
area_id
);
}
route_array_destroy
(
result
);
return
area_id
;
}
/**
* Check if call falls in an inter or intra area
* @param lcr the LCR database
* @param caller_id the caller ID
* @param dest_number the number to dial
* @return 1 if inter-area, 0 if intra-area
*/
static
int
lcr_is_inter_area
(
struct
lcr
*
lcr
,
const
char
*
caller_id
,
const
char
*
dest_number
)
{
char
*
lcaller_id
=
NULL
;
char
*
ldest_number
=
NULL
;
int
src_area
=
-
1
;
int
dest_area
=
-
1
;
/* check input and settings */
if
(
zstr
(
caller_id
)
||
lcr
->
areas_caller_id_strip
>=
strlen
(
caller_id
)
||
lcr
->
areas_caller_id_strip
<
0
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG1
,
"Skipping due to bad caller-id input
\n
"
);
return
0
;
}
if
(
zstr
(
dest_number
)
||
lcr
->
areas_dest_strip
>=
strlen
(
dest_number
)
||
lcr
->
areas_dest_strip
<
0
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG1
,
"Skipping due to bad dest input
\n
"
);
return
0
;
}
/* strip/add prefix to caller ID */
caller_id
+=
lcr
->
areas_caller_id_strip
;
if
(
!
zstr
(
lcr
->
areas_caller_id_prefix
))
{
lcaller_id
=
switch_mprintf
(
"%s%s"
,
lcr
->
areas_caller_id_prefix
,
caller_id
);
caller_id
=
lcaller_id
;
}
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG1
,
"Caller ID = %s
\n
"
,
caller_id
);
/* strip/add prefix to dest number */
dest_number
+=
lcr
->
areas_dest_strip
;
if
(
!
zstr
(
lcr
->
areas_dest_prefix
))
{
ldest_number
=
switch_mprintf
(
"%s%s"
,
lcr
->
areas_dest_prefix
,
dest_number
);
dest_number
=
ldest_number
;
}
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG1
,
"Dest = %s
\n
"
,
dest_number
);
/* get areas from caller ID and destination number. If they are different, it is inter-area */
src_area
=
lcr_find_area
(
lcr
,
caller_id
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG1
,
"Src area = %d
\n
"
,
src_area
);
dest_area
=
lcr_find_area
(
lcr
,
dest_number
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG1
,
"Dest area = %d
\n
"
,
dest_area
);
switch_safe_free
(
lcaller_id
);
switch_safe_free
(
ldest_number
);
return
src_area
>=
0
&&
dest_area
>=
0
&&
src_area
!=
dest_area
;
}
/**
* Search an LCR database for best routes
* @param lcr the LCR database
* @param caller_id the caller ID
* @param dest_number the number to dial
* @param results the search results
* @return the number of matches
*/
static
int
lcr_find
(
struct
lcr
*
lcr
,
const
char
*
caller_id
,
const
char
*
dest_number
,
struct
route_array
*
results
)
{
int
num_inter_carriers
=
0
;
if
(
zstr
(
dest_number
))
{
return
0
;
}
/* strip leading + */
if
(
*
dest_number
==
'+'
)
{
dest_number
++
;
}
/* strip leading + */
if
(
!
zstr
(
caller_id
)
&&
*
caller_id
==
'+'
)
{
caller_id
++
;
}
/* do lookup, determining if inter/intra then searching the respective trie */
if
(
lcr_is_inter_area
(
lcr
,
caller_id
,
dest_number
))
{
/* inter-routing */
route_trie_find
(
lcr
->
inter_trie
,
dest_number
,
results
);
}
else
if
((
num_inter_carriers
=
route_trie_find_count
(
lcr
->
inter_areas
,
caller_id
))
>=
lcr
->
num_carriers
)
{
/* inter-routing (all carriers treat caller ID as inter) */
route_trie_find
(
lcr
->
inter_trie
,
dest_number
,
results
);
}
else
if
(
num_inter_carriers
==
0
)
{
/* intra-routing */
route_trie_find
(
lcr
->
intra_trie
,
dest_number
,
results
);
}
else
{
int
num_routes
=
0
;
/* mixed routing */
struct
route_array
*
inter_carriers
=
route_array_create
(
num_inter_carriers
);
struct
route_array
*
inter_results
=
route_array_create
(
lcr
->
num_carriers
);
struct
route_array
*
intra_results
=
route_array_create
(
lcr
->
num_carriers
);
/* which carriers are inter? */
route_trie_find
(
lcr
->
inter_areas
,
caller_id
,
inter_carriers
);
/* add intra results */
num_routes
=
route_trie_find
(
lcr
->
intra_trie
,
dest_number
,
intra_results
);
for
(
int
i
=
0
;
i
<
num_routes
;
i
++
)
{
struct
route
*
route
=
route_array_get_route
(
intra_results
,
i
);
/* if route is intra and carrier is intra, add it */
if
(
!
route_get_type
(
route
)
&&
!
route_array_contains
(
inter_carriers
,
route_get_value
(
route
)))
{
route_array_add
(
results
,
route
);
}
}
/* add inter results */
num_routes
=
route_trie_find
(
lcr
->
inter_trie
,
dest_number
,
inter_results
);
for
(
int
i
=
0
;
i
<
num_routes
;
i
++
)
{
struct
route
*
route
=
route_array_get_route
(
inter_results
,
i
);
/* if route is inter and carrier is inter, add it */
if
(
route_get_type
(
route
)
&&
route_array_contains
(
inter_carriers
,
route_get_value
(
route
)))
{
route_array_add
(
results
,
route
);
}
}
route_array_destroy
(
inter_carriers
);
route_array_destroy
(
intra_results
);
route_array_destroy
(
inter_results
);
}
return
route_array_get_size
(
results
);
}
/**
* Destroy an LCR database
* @param lcr the LCR database
*/
static
void
lcr_destroy
(
struct
lcr
*
lcr
)
{
if
(
lcr
)
{
/* wait for all readers to finish */
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"Waiting to destroy LCR db
\n
"
);
switch_thread_rwlock_wrlock
(
lcr
->
mutex
);
/* destroy routes */
route_trie_destroy
(
lcr
->
inter_trie
);
route_trie_destroy
(
lcr
->
intra_trie
);
/* destroy area map */
route_trie_destroy
(
lcr
->
areas
);
/* destroy inter-area map */
route_trie_destroy
(
lcr
->
inter_areas
);
/* destroy LCR carriers/gateways */
switch_core_destroy_memory_pool
(
&
lcr
->
pool
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"LCR db destroyed
\n
"
);
}
}
/**
* Concatenate LCR routes into a dial string
* @param buf the buffer to hold the dial string
* @param buf_len the buffer length
* @param sep the dial string separator ',', '|', etc.
* @param routes the routes
* @param dial_digits the digits to dial
* @return SWITCH_STATUS_SUCCESS if there is enough buffer space for the dial string
*/
static
switch_status_t
build_dialstring_list
(
char
*
buf
,
size_t
buf_len
,
char
sep
,
struct
lcr
*
lcr
,
struct
route_array
*
routes
,
const
char
*
dial_digits
)
{
int
offset
=
0
;
int
i
;
struct
route
*
route
=
route_array_get_route
(
routes
,
0
);
route_value_t
carrier_id
=
route_get_value
(
route
);
struct
carrier
*
carrier
=
lcr
->
carriers
[
carrier_id
];
if
(
carrier_get_dialstring
(
carrier
,
buf
,
buf_len
,
dial_digits
)
>
buf_len
)
{
return
SWITCH_STATUS_FALSE
;
}
for
(
i
=
1
;
i
<
route_array_get_size
(
routes
);
i
++
)
{
struct
route
*
route
=
route_array_get_route
(
routes
,
i
);
carrier_id
=
route_get_value
(
route
);
carrier
=
lcr
->
carriers
[
carrier_id
];
offset
+=
strlen
(
buf
+
offset
);
if
(
buf_len
-
offset
>
0
)
{
buf
[
offset
++
]
=
sep
;
}
else
{
return
SWITCH_STATUS_FALSE
;
}
if
(
carrier_get_dialstring
(
carrier
,
buf
+
offset
,
buf_len
-
offset
,
dial_digits
)
>
buf_len
-
offset
)
{
return
SWITCH_STATUS_FALSE
;
}
}
return
SWITCH_STATUS_SUCCESS
;
}
/**
* Read line from file, trimming the newline character
* @param line the buffer to fill with the line
* @param size the size of the buffer
* @param in the FILE to read
* @return the line or NULL if end of file
*/
static
char
*
fgets_trimmed
(
char
*
line
,
int
size
,
FILE
*
in
)
{
char
*
retval
=
fgets
(
line
,
255
,
in
);
if
(
retval
)
{
/* trim newline */
char
*
pos
;
if
((
pos
=
strchr
(
line
,
'\n'
))
!=
NULL
)
{
*
pos
=
'\0'
;
}
}
return
retval
;
}
/**
* Load a area CSV file
* @param area_trie the area database to add to
* @param area the area file name
* @return SWITCH_STATUS_SUCCESS if successful
*/
static
switch_status_t
load_area_file
(
struct
route_trie
*
area_trie
,
const
char
*
area
)
{
switch_status_t
status
=
SWITCH_STATUS_SUCCESS
;
route_value_t
next_id
=
0
;
switch_hash_t
*
area_hash
=
NULL
;
switch_memory_pool_t
*
pool
=
NULL
;
int
line_num
=
0
;
char
line
[
256
];
FILE
*
in
=
fopen
(
area
,
"r"
);
if
(
!
in
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to open area file %s
\n
"
,
area
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
if
(
switch_core_new_memory_pool
(
&
pool
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to create area ID memory pool!
\n
"
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
/* maps area external IDs to internal IDs */
switch_core_hash_init_case
(
&
area_hash
,
pool
,
SWITCH_TRUE
);
line
[
255
]
=
'\0'
;
while
(
fgets_trimmed
(
line
,
255
,
in
))
{
char
*
field
[
3
];
line_num
++
;
if
(
switch_separate_string
(
line
,
','
,
field
,
3
)
>=
2
)
{
char
*
digits
=
field
[
0
];
char
*
ext_id
=
field
[
1
];
/* find internal ID for the external ID */
route_value_t
*
id_ptr
=
switch_core_hash_find
(
area_hash
,
ext_id
);
if
(
!
id_ptr
)
{
/* assign new internal ID to this area external ID */
id_ptr
=
switch_core_alloc
(
pool
,
sizeof
(
*
id_ptr
));
*
id_ptr
=
++
next_id
;
switch_core_hash_insert
(
area_hash
,
switch_core_strdup
(
pool
,
ext_id
),
id_ptr
);
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"%s: new area %s => %d
\n
"
,
area
,
ext_id
,
*
id_ptr
);
}
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_DEBUG
,
"%s: mapping prefix %s, %s => %d
\n
"
,
area
,
digits
,
ext_id
,
*
id_ptr
);
/* map digits to internal ID */
if
(
!
route_trie_add
(
area_trie
,
digits
,
0
,
*
id_ptr
,
0
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"%s: Bad input on line %i!
\n
"
,
area
,
line_num
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"%s: Bad input on line %i!
\n
"
,
area
,
line_num
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
}
done:
if
(
area_hash
)
{
switch_core_hash_destroy
(
&
area_hash
);
}
if
(
pool
)
{
switch_core_destroy_memory_pool
(
&
pool
);
}
if
(
in
)
{
fclose
(
in
);
}
return
status
;
}
#define COST_NO_INPUT -1
#define COST_BAD_INPUT -2
/**
* Convert the string representation of route cost
* to the internal cost
* @param cost string cost
* @return internal cost value, or maximum cost if NULL
*/
static
int
to_route_cost
(
char
*
cost
)
{
if
(
zstr
(
cost
))
{
return
COST_NO_INPUT
;
}
if
(
!
zstr
(
cost
)
&&
switch_is_number
(
cost
))
{
double
costf
=
atof
(
cost
);
if
(
costf
>=
0
&&
costf
<
6
.
5535
f
)
{
return
costf
*
10000
;
}
if
(
costf
>=
6
.
5535
f
)
{
return
6
.
5535
f
;
}
}
return
COST_BAD_INPUT
;
}
/**
* Load a ratedeck CSV file
* @param inter_trie the inter-area database to add routes to
* @param intra_trie the intra-area database to add routes to
* @param carrier the carrier the ratedeck belongs to
* @param ratedeck the ratedeck file name
* @return SWITCH_STATUS_SUCCESS if successful
*/
static
switch_status_t
load_ratedeck_file
(
struct
route_trie
*
inter_trie
,
struct
route_trie
*
intra_trie
,
struct
carrier
*
carrier
,
const
char
*
ratedeck
)
{
switch_status_t
status
=
SWITCH_STATUS_SUCCESS
;
int
line_num
=
0
;
char
line
[
256
];
FILE
*
in
=
fopen
(
ratedeck
,
"r"
);
if
(
!
in
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to open ratedeck %s
\n
"
,
ratedeck
);
return
SWITCH_STATUS_FALSE
;
}
line
[
255
]
=
'\0'
;
while
(
fgets_trimmed
(
line
,
255
,
in
))
{
char
*
field
[
4
]
=
{
0
};
line_num
++
;
if
(
switch_separate_string
(
line
,
','
,
field
,
4
)
>
2
)
{
char
*
digits
=
field
[
0
];
int
inter_cost
=
to_route_cost
(
field
[
1
]);
int
intra_cost
=
to_route_cost
(
field
[
2
]);
if
(
inter_cost
==
COST_BAD_INPUT
||
(
inter_cost
>=
0
&&
!
route_trie_add
(
inter_trie
,
digits
,
inter_cost
,
carrier
->
id
,
1
)))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"%s: Bad inter cost on line %i column 1
\n
"
,
ratedeck
,
line_num
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
else
if
(
inter_cost
==
COST_NO_INPUT
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_INFO
,
"%s: Missing inter cost on line %i column 1
\n
"
,
ratedeck
,
line_num
);
}
if
(
intra_cost
==
COST_BAD_INPUT
||
(
intra_cost
>=
0
&&
!
route_trie_add
(
intra_trie
,
digits
,
intra_cost
,
carrier
->
id
,
0
)))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"%s: Bad intra cost on line %i column 2
\n
"
,
ratedeck
,
line_num
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
else
if
(
intra_cost
==
COST_NO_INPUT
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_INFO
,
"%s: Missing intra cost on line %i column 1
\n
"
,
ratedeck
,
line_num
);
}
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"%s: Missing field on line %i
\n
"
,
ratedeck
,
line_num
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
}
done:
fclose
(
in
);
return
status
;
}
/**
* Configure module from configuration file
*/
static
switch_status_t
load_config
(
void
)
{
switch_xml_t
cfg
,
xml
=
NULL
,
carrier
,
settings
;
switch_status_t
status
=
SWITCH_STATUS_SUCCESS
;
struct
lcr
*
new_lcr
=
NULL
;
route_value_t
carrier_id
=
0
;
int
num_carriers
=
0
;
if
(
!
(
xml
=
switch_xml_open_cfg
(
"simple_lcr.conf"
,
&
cfg
,
NULL
)))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"open of simple_lcr.conf failed
\n
"
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_CONSOLE
,
"Loading LCR configuration
\n
"
);
/* size the LCR database based on the number of carriers defined */
for
(
carrier
=
switch_xml_child
(
cfg
,
"carrier"
);
carrier
;
carrier
=
carrier
->
next
)
{
num_carriers
++
;
if
(
num_carriers
>
MAX_CARRIERS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Too many carriers!
\n
"
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
}
new_lcr
=
lcr_create
(
num_carriers
);
if
((
settings
=
switch_xml_child
(
cfg
,
"settings"
)))
{
switch_xml_t
param
;
for
(
param
=
switch_xml_child
(
settings
,
"param"
);
param
;
param
=
param
->
next
)
{
const
char
*
name
=
switch_xml_attr_soft
(
param
,
"name"
);
const
char
*
value
=
switch_xml_attr_soft
(
param
,
"value"
);
if
(
zstr
(
name
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Param name must be specified
\n
"
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
if
(
!
strcasecmp
(
"max-routes"
,
name
))
{
int
val
=
atoi
(
value
);
if
(
switch_is_number
(
value
)
&&
val
>
0
)
{
if
(
val
>
MAX_ROUTES
)
{
val
=
MAX_ROUTES
;
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_WARNING
,
"reducing max-routes to %d
\n
"
,
MAX_ROUTES
);
lcr_set_max_routes
(
new_lcr
,
val
);
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_CONSOLE
,
"setting max-routes to %d
\n
"
,
val
);
lcr_set_max_routes
(
new_lcr
,
val
);
}
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_WARNING
,
"Ignoring invalid value %s for %s
\n
"
,
value
,
name
);
}
}
else
if
(
!
strcasecmp
(
"areas"
,
name
))
{
if
(
!
zstr
(
value
))
{
/* load the area prefix map */
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_CONSOLE
,
"Loading areas from: %s
\n
"
,
value
);
if
(
load_area_file
(
new_lcr
->
areas
,
value
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Error loading areas from: %s
\n
"
,
value
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
}
}
else
if
(
!
strcasecmp
(
"areas-cid-strip"
,
name
))
{
int
val
=
atoi
(
value
);
if
(
switch_is_number
(
value
)
&&
val
>=
0
)
{
new_lcr
->
areas_caller_id_strip
=
val
;
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_WARNING
,
"ignoring invalid areas-cid-strip value of %s
\n
"
,
value
);
}
}
else
if
(
!
strcasecmp
(
"areas-cid-prefix"
,
name
))
{
if
(
!
zstr
(
value
))
{
new_lcr
->
areas_caller_id_prefix
=
switch_core_strdup
(
new_lcr
->
pool
,
value
);
}
}
else
if
(
!
strcasecmp
(
"areas-dest-strip"
,
name
))
{
int
val
=
atoi
(
value
);
if
(
switch_is_number
(
value
)
&&
val
>=
0
)
{
new_lcr
->
areas_dest_strip
=
val
;
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_WARNING
,
"ignoring invalid areas-dest-strip value of %s
\n
"
,
value
);
}
}
else
if
(
!
strcasecmp
(
"areas-dest-prefix"
,
name
))
{
if
(
!
zstr
(
value
))
{
new_lcr
->
areas_dest_prefix
=
switch_core_strdup
(
new_lcr
->
pool
,
value
);
}
}
else
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_WARNING
,
"Ignoring unknown parameter: %s
\n
"
,
name
);
}
}
}
for
(
carrier
=
switch_xml_child
(
cfg
,
"carrier"
);
carrier
;
carrier
=
carrier
->
next
)
{
switch_xml_t
gateway
;
const
char
*
name
=
switch_xml_attr_soft
(
carrier
,
"name"
);
const
char
*
ratedeck_file
=
switch_xml_attr_soft
(
carrier
,
"ratedeck"
);
const
char
*
inter_areas
=
switch_xml_attr_soft
(
carrier
,
"interAreas"
);
struct
carrier
*
cr
;
/* add a carrier */
if
(
zstr
(
name
)
||
zstr
(
ratedeck_file
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Carrier name and ratedeck must be specified
\n
"
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_CONSOLE
,
"Adding carrier %s
\n
"
,
name
);
carrier_id
++
;
cr
=
carrier_create
(
new_lcr
->
pool
,
name
,
carrier_id
);
new_lcr
->
carriers
[
carrier_id
]
=
cr
;
/* add inter-areas LCR database for this carrier */
if
(
!
zstr
(
inter_areas
))
{
int
argc
=
0
;
char
*
argv
[
MAX_INTER_AREAS
]
=
{
0
};
char
*
mydata
=
NULL
;
if
(
!
(
mydata
=
strdup
(
inter_areas
)))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_CRIT
,
"Memory error!
\n
"
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
if
((
argc
=
switch_separate_string
(
mydata
,
','
,
argv
,
(
sizeof
(
argv
)
/
sizeof
(
argv
[
0
]))))
>
1
)
{
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_CONSOLE
,
"Adding carrier %s interArea %s
\n
"
,
name
,
argv
[
i
]);
if
(
!
route_trie_add
(
new_lcr
->
inter_areas
,
argv
[
i
],
0
,
carrier_id
,
0
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Bad input: non-numeric interArea %s for carrier %s
\n
"
,
argv
[
i
],
name
);
switch_safe_free
(
mydata
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
}
}
switch_safe_free
(
mydata
);
}
/* add gateways to the carrier */
for
(
gateway
=
switch_xml_child
(
carrier
,
"gateway"
);
gateway
;
gateway
=
gateway
->
next
)
{
const
char
*
gateway_prefix
=
switch_xml_attr_soft
(
gateway
,
"prefix"
);
const
char
*
gateway_suffix
=
switch_xml_attr_soft
(
gateway
,
"suffix"
);
if
(
zstr
(
gateway_prefix
)
&&
zstr
(
gateway_suffix
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"A gateway prefix or suffix needs to be specified!
\n
"
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_CONSOLE
,
"Adding carrier %s gateway: (%s, %s)
\n
"
,
name
,
gateway_prefix
,
gateway_suffix
);
if
(
carrier_add_gateway
(
new_lcr
->
pool
,
cr
,
gateway_prefix
,
gateway_suffix
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Too many gateways!
\n
"
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
}
/* load the ratedeck associated with the carrier */
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_CONSOLE
,
"Adding carrier %s ratedeck: %s
\n
"
,
name
,
ratedeck_file
);
if
(
load_ratedeck_file
(
new_lcr
->
inter_trie
,
new_lcr
->
intra_trie
,
cr
,
ratedeck_file
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Error loading ratedeck: %s
\n
"
,
ratedeck_file
);
status
=
SWITCH_STATUS_FALSE
;
goto
done
;
}
}
done:
if
(
status
!=
SWITCH_STATUS_SUCCESS
)
{
/* configuration failed.. clean up */
lcr_destroy
(
new_lcr
);
}
else
{
/* swap old LCR database with new LCR database */
struct
lcr
*
old_lcr
=
lcr_replace
(
new_lcr
);
lcr_destroy
(
old_lcr
);
}
if
(
xml
)
{
switch_xml_free
(
xml
);
}
return
status
;
}
/**
* LCR lookup and redirect dialplan
*/
SWITCH_STANDARD_DIALPLAN
(
simple_lcr_dialplan_redirect
)
{
switch_caller_extension_t
*
extension
=
NULL
;
struct
route_array
*
results
=
NULL
;
route_size_t
num_routes
;
char
*
lookup_number
=
""
;
const
char
*
caller_id
=
""
;
switch_time_t
start
=
switch_time_now
();
struct
lcr
*
lcr
=
NULL
;
if
(
!
caller_profile
)
{
caller_profile
=
switch_channel_get_caller_profile
(
switch_core_session_get_channel
(
session
));
}
if
((
extension
=
switch_caller_extension_new
(
session
,
caller_profile
->
destination_number
,
caller_profile
->
destination_number
))
==
0
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_CRIT
,
"memory error!
\n
"
);
goto
done
;
}
/* get dialed number */
if
(
!
zstr
(
caller_profile
->
destination_number
))
{
lookup_number
=
caller_profile
->
destination_number
;
if
(
*
lookup_number
==
'+'
)
{
lookup_number
++
;
}
}
/* get caller ID */
if
(
!
zstr
(
caller_profile
->
caller_id_number
))
{
caller_id
=
caller_profile
->
caller_id_number
;
if
(
*
caller_id
==
'+'
)
{
caller_id
++
;
}
}
/* DO LCR Lookup */
lcr
=
lcr_rdlock
();
results
=
route_array_create
(
lcr
->
max_routes
);
num_routes
=
lcr_find
(
lcr
,
caller_id
,
lookup_number
,
results
);
if
(
num_routes
)
{
char
routes_str
[
1024
];
size_t
max_len
=
sizeof
(
routes_str
)
-
1
;
routes_str
[
max_len
]
=
'\0'
;
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"Number of Routes Returned[%i]
\n
"
,
num_routes
);
if
(
build_dialstring_list
(
routes_str
,
max_len
,
','
,
lcr
,
results
,
caller_profile
->
destination_number
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_CRIT
,
"dial string buffer overflow
\n
"
);
extension
=
NULL
;
goto
done
;
}
/* redirect the call to the carrier gateways */
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_NOTICE
,
"Redirecting call from %s to %s to %s
\n
"
,
caller_profile
->
caller_id_number
,
caller_profile
->
destination_number
,
routes_str
);
switch_caller_extension_add_application
(
session
,
extension
,
"redirect"
,
switch_core_session_strdup
(
session
,
routes_str
));
}
else
{
/* no route found - reject the call */
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_NOTICE
,
"Rejecting call from %s to %s
\n
"
,
caller_profile
->
caller_id_number
,
caller_profile
->
destination_number
);
switch_caller_extension_add_application
(
session
,
extension
,
"respond"
,
"404 No route found"
);
}
done
:
lcr_unlock
(
lcr
);
route_array_destroy
(
results
);
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_DEBUG
,
"processing time = %"
SWITCH_TIME_T_FMT
" ns
\n
"
,
switch_time_now
()
-
start
);
return
extension
;
}
#define LCR_API "simple_lcr"
#define LCR_API_SYNTAX "<cid> <dest> [csv]"
SWITCH_STANDARD_API
(
simple_lcr_function
)
{
int
argc
=
0
;
char
*
argv
[
4
]
=
{
0
};
char
*
mydata
=
NULL
;
struct
route_array
*
results
=
NULL
;
struct
lcr
*
lcr
=
NULL
;
if
(
session
)
{
switch_log_printf
(
SWITCH_CHANNEL_SESSION_LOG
(
session
),
SWITCH_LOG_ERROR
,
"This function cannot be called from the dialplan.
\n
"
);
return
SWITCH_STATUS_FALSE
;
}
if
(
zstr
(
cmd
))
{
stream
->
write_function
(
stream
,
"Usage: "
LCR_API
" "
LCR_API_SYNTAX
"
\n
"
);
return
SWITCH_STATUS_SUCCESS
;
}
if
(
!
(
mydata
=
strdup
(
cmd
)))
{
stream
->
write_function
(
stream
,
"Memory error!
\n
"
);
return
SWITCH_STATUS_SUCCESS
;
}
if
((
argc
=
switch_separate_string
(
mydata
,
' '
,
argv
,
(
sizeof
(
argv
)
/
sizeof
(
argv
[
0
]))))
>
1
)
{
int
i
=
0
;
int
csv
=
0
;
char
*
dest_arg
=
argv
[
1
];
char
*
lookup_number
=
dest_arg
;
char
*
caller_id
=
argv
[
0
];
const
char
*
format
;
route_size_t
num_routes
;
/* is CSV format requested? */
if
(
argc
>
2
)
{
csv
=
!
strcasecmp
(
"csv"
,
argv
[
2
]);
}
/* do lookup */
lcr
=
lcr_rdlock
();
results
=
route_array_create
(
lcr
->
num_carriers
);
num_routes
=
lcr_find
(
lcr
,
caller_id
,
lookup_number
,
results
);
if
(
num_routes
==
0
)
{
stream
->
write_function
(
stream
,
"No Match!
\n
"
);
goto
done
;
}
/* output results of lookup in a tabular or CSV format */
if
(
csv
){
stream
->
write_function
(
stream
,
"carrier_id,type,cost,dial string
\n
"
);
format
=
"%s,%s,%f,%s
\n
"
;
}
else
{
stream
->
write_function
(
stream
,
"Routes to %s%s%s
\n
"
,
dest_arg
,
caller_id
?
" from "
:
""
,
caller_id
?
caller_id
:
""
);
stream
->
write_function
(
stream
,
"Carrier
\t\t
Type
\t\t
Cost
\t\t
Dial string
\n
"
);
format
=
"%s
\t\t
%s
\t\t
%f
\t
%s
\n
"
;
}
for
(
i
=
0
;
i
<
num_routes
;
i
++
)
{
int
j
;
struct
route
*
route
=
route_array_get_route
(
results
,
i
);
route_value_t
carrier_id
=
route_get_value
(
route
);
struct
carrier
*
carrier
=
lcr
->
carriers
[
carrier_id
];
for
(
j
=
0
;
carrier
->
gateway
[
j
]
&&
j
<
MAX_GATEWAYS
;
j
++
)
{
char
dialstring
[
256
];
dialstring
[
255
]
=
'\0'
;
gateway_get_dialstring
(
carrier
->
gateway
[
j
],
dialstring
,
255
,
dest_arg
);
stream
->
write_function
(
stream
,
format
,
carrier
->
name
,
route_get_type
(
route
)
?
"inter"
:
"intra"
,
(
float
)
route_get_cost
(
route
)
/
10000
.
0
f
,
dialstring
);
}
}
}
else
{
stream
->
write_function
(
stream
,
"Usage: "
LCR_API
" "
LCR_API_SYNTAX
"
\n
"
);
}
done
:
lcr_unlock
(
lcr
);
route_array_destroy
(
results
);
switch_safe_free
(
mydata
);
return
SWITCH_STATUS_SUCCESS
;
}
/**
* Reload the configuration whenever there is a "reloadxml" event
*/
static
void
reloadxml_event_handler
(
switch_event_t
*
event
)
{
load_config
();
}
SWITCH_MODULE_LOAD_FUNCTION
(
mod_simple_lcr_load
)
{
switch_api_interface_t
*
api_interface
;
switch_dialplan_interface_t
*
dp_interface
;
memset
(
&
globals
,
0
,
sizeof
(
globals
));
if
(
switch_mutex_init
(
&
globals
.
update_lock
,
SWITCH_MUTEX_UNNESTED
,
pool
)
!=
SWITCH_STATUS_SUCCESS
)
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Failed to create update_lock mutex!
\n
"
);
return
SWITCH_STATUS_FALSE
;
}
load_config
();
*
module_interface
=
switch_loadable_module_create_module_interface
(
pool
,
modname
);
SWITCH_ADD_API
(
api_interface
,
LCR_API
,
"LCR"
,
simple_lcr_function
,
LCR_API_SYNTAX
);
SWITCH_ADD_DIALPLAN
(
dp_interface
,
"simple_lcr_redirect"
,
simple_lcr_dialplan_redirect
);
if
((
switch_event_bind
(
modname
,
SWITCH_EVENT_RELOADXML
,
NULL
,
reloadxml_event_handler
,
NULL
)
!=
SWITCH_STATUS_SUCCESS
))
{
switch_log_printf
(
SWITCH_CHANNEL_LOG
,
SWITCH_LOG_ERROR
,
"Couldn't bind our reloadxml handler!
\n
"
);
return
SWITCH_STATUS_FALSE
;
}
/* indicate that the module should continue to be loaded */
return
SWITCH_STATUS_SUCCESS
;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION
(
mod_simple_lcr_shutdown
)
{
switch_event_unbind_callback
(
reloadxml_event_handler
);
lcr_destroy
(
globals
.
lcr
);
return
SWITCH_STATUS_SUCCESS
;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/
crienzo/mod_simple_lcr/route_trie.c
0 → 100644
浏览文件 @
c1a3bce0
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Christopher M. Rienzo <chris@rienzo.com>
*
* Maintainer: Christopher M. Rienzo <chris@rienzo.com>
*
* route_trie.c -- Implementation of an uncompressed trie for fast least cost routing
* lookups.
*
*/
#include "route_trie.h"
#include <stdlib.h>
#include <string.h>
/**
* A route in the database
*/
struct
route
{
/** cost of route */
route_cost_t
cost
;
/** routing information */
route_value_t
value
;
/** route type */
route_type_t
type
;
};
/**
* An array of routes
*/
struct
route_array
{
/** maximum size of this array */
route_size_t
max_size
;
/** Array size */
route_size_t
size
;
/** The routes */
struct
route
route
[];
};
/**
* A trie node. Each node represents a digit in a dial string (0-9).
*/
struct
route_trie_node
{
/** child nodes, indexed by dial string digit */
struct
route_trie_node
*
child
[
10
];
/** array of routes stored in this node (nullable) */
struct
route_array
*
array
;
};
/**
* route trie instance
*/
struct
route_trie
{
/** max routes per node */
int
max_routes
;
/** root of the trie */
struct
route_trie_node
*
root
;
};
route_value_t
route_get_value
(
struct
route
*
route
)
{
return
route
->
value
;
}
route_type_t
route_get_type
(
struct
route
*
route
)
{
return
route
->
type
;
}
route_cost_t
route_get_cost
(
struct
route
*
route
)
{
return
route
->
cost
;
}
struct
route_array
*
route_array_create
(
route_size_t
max_size
)
{
struct
route_array
*
array
=
calloc
(
1
,
sizeof
(
struct
route_array
)
+
max_size
*
sizeof
(
struct
route
));
array
->
max_size
=
max_size
;
return
array
;
}
route_size_t
route_array_get_size
(
struct
route_array
*
array
)
{
return
array
->
size
;
}
struct
route
*
route_array_get_route
(
struct
route_array
*
array
,
route_size_t
index
)
{
if
(
index
<
array
->
size
)
{
return
&
array
->
route
[
index
];
}
return
NULL
;
}
void
route_array_reset
(
struct
route_array
*
array
)
{
array
->
size
=
0
;
}
void
route_array_destroy
(
struct
route_array
*
array
)
{
if
(
array
)
{
free
(
array
);
}
}
int
route_array_contains
(
struct
route_array
*
array
,
route_value_t
value
)
{
int
i
;
for
(
i
=
0
;
i
<
array
->
size
;
i
++
)
{
if
(
array
->
route
[
i
].
value
==
value
)
{
return
1
;
}
}
return
0
;
}
/**
* Find the index in the array that contains the same value and type
* @param array the array to search
* @param value the value to search for
* @param type the type to search for
* @return the index
*/
static
int
route_array_find_by_value_and_type
(
struct
route_array
*
array
,
route_value_t
value
,
route_type_t
type
)
{
int
i
;
for
(
i
=
0
;
i
<
array
->
size
;
i
++
)
{
if
(
array
->
route
[
i
].
value
==
value
&&
array
->
route
[
i
].
type
==
type
)
{
return
i
;
}
}
/* no match */
return
array
->
max_size
;
}
/**
* Find the index in the array to insert a new route
* @param array the array to search
* @param cost the route cost
* @return the index
*/
static
int
route_array_find_by_cost
(
struct
route_array
*
array
,
route_cost_t
cost
)
{
int
i
;
/* TODO implement heap or do binary search if max_size needs to be larger than 3 */
/* linear search for first index where rte can be placed */
for
(
i
=
0
;
i
<
array
->
max_size
;
i
++
)
{
if
(
!
array
->
route
[
i
].
value
||
array
->
route
[
i
].
cost
>
cost
)
{
return
i
;
}
}
/* no room in array */
return
array
->
max_size
;
}
/**
* Remove a route from the array
* @param array the array
* @param index the route index to remove
*/
void
route_array_remove
(
struct
route_array
*
array
,
int
index
)
{
if
(
index
>=
0
&&
index
<
array
->
size
&&
array
->
size
>
0
)
{
/* remove this route from the array */
for
(;
index
>=
0
&&
index
<
array
->
size
-
1
;
index
++
)
{
array
->
route
[
index
]
=
array
->
route
[
index
+
1
];
}
array
->
size
--
;
}
}
/**
* Add a route to the array preserving the order of cost, ascending. The highest
* cost route is removed from the array to make room for a lower cost route.
* @param array the array
* @param route the route to add
*/
void
route_array_add
(
struct
route_array
*
array
,
struct
route
*
route
)
{
struct
route
cur
=
*
route
;
struct
route
swap
;
int
i
;
/* check if route is already in the array */
if
((
i
=
route_array_find_by_value_and_type
(
array
,
route
->
value
,
route
->
type
))
!=
array
->
max_size
)
{
/* don't add route if existing route is cheaper than the route to add */
if
(
array
->
route
[
i
].
cost
<
route
->
cost
)
{
return
;
}
/* remove more expensive existing route */
route_array_remove
(
array
,
i
);
}
/* insert route into array if it costs less than the most expensive route or
the array is not filled */
for
(
i
=
route_array_find_by_cost
(
array
,
route
->
cost
);
cur
.
value
&&
i
<
array
->
max_size
;
i
++
)
{
swap
=
array
->
route
[
i
];
array
->
route
[
i
]
=
cur
;
cur
=
swap
;
}
/* check for discarded route */
array
->
size
++
;
if
(
array
->
size
>
array
->
max_size
)
{
array
->
size
=
array
->
max_size
;
}
}
/**
* Create a route trie node
* @return the node
*/
struct
route_trie_node
*
route_trie_node_create
()
{
return
calloc
(
1
,
sizeof
(
struct
route_trie_node
));
}
/**
* Add a route to the route trie
* @param node the route trie node
* @param prefix the prefix string
* @param route the route to add
* @param max_routes the maximum # of routes allowed per node
* @return 1 if successful
*/
static
int
route_trie_node_add
(
struct
route_trie_node
*
node
,
const
char
*
prefix
,
struct
route
*
route
,
int
max_routes
)
{
struct
route_trie_node
*
cur_node
,
*
child_node
;
/* Find the node to store the route in. Create nodes as needed. */
for
(
cur_node
=
node
;
prefix
&&
prefix
[
0
]
!=
'\0'
;
cur_node
=
child_node
)
{
int
i
=
(
int
)(
prefix
[
0
]
-
'0'
);
/* convert char to digit */
child_node
=
NULL
;
if
(
i
>=
0
&&
i
<=
9
)
{
if
(
!
cur_node
->
child
[
i
])
{
cur_node
->
child
[
i
]
=
route_trie_node_create
();
}
child_node
=
cur_node
->
child
[
i
];
prefix
++
;
/* advance to next character in prefix */
}
else
{
return
0
;
/* bad input */
}
}
/* Store the route. Create route arrays as needed. */
if
(
cur_node
)
{
if
(
!
cur_node
->
array
)
{
cur_node
->
array
=
route_array_create
(
max_routes
);
}
route_array_add
(
cur_node
->
array
,
route
);
return
1
;
}
return
0
;
}
/**
* Search trie for lowest cost routes
* @param node the trie to search
* @param prefix the route prefix
* @param array the array to fill
* @return the number of routes returned
*/
static
route_size_t
route_trie_node_find
(
struct
route_trie_node
*
node
,
const
char
*
prefix
,
struct
route_array
*
array
)
{
struct
route_trie_node
*
cur_node
,
*
child_node
;
for
(
cur_node
=
node
;
cur_node
;
cur_node
=
child_node
)
{
/* check if any routes at this node */
if
(
cur_node
->
array
)
{
int
i
;
for
(
i
=
0
;
i
<
cur_node
->
array
->
size
;
i
++
)
{
route_array_add
(
array
,
&
cur_node
->
array
->
route
[
i
]);
}
}
/* advance to child node if prefix still has more digits */
child_node
=
NULL
;
if
(
prefix
&&
prefix
[
0
]
!=
'\0'
)
{
int
i
=
(
int
)(
prefix
[
0
]
-
'0'
);
if
(
i
>=
0
&&
i
<=
9
)
{
if
(
cur_node
->
child
[
i
])
{
child_node
=
cur_node
->
child
[
i
];
prefix
++
;
/* advance to next character in prefix */
}
}
else
{
return
0
;
/* bad input */
}
}
}
return
array
->
size
;
}
/**
* Search trie for count of routes
* @param node the trie to search
* @param prefix the route prefix
* @return the number of routes
*/
static
int
route_trie_node_find_count
(
struct
route_trie_node
*
node
,
const
char
*
prefix
)
{
int
count
=
0
;
struct
route_trie_node
*
cur_node
,
*
child_node
;
for
(
cur_node
=
node
;
cur_node
;
cur_node
=
child_node
)
{
/* check if any routes at this node */
if
(
cur_node
->
array
)
{
count
+=
cur_node
->
array
->
size
;
}
/* advance to child node if prefix still has more digits */
child_node
=
NULL
;
if
(
prefix
&&
prefix
[
0
]
!=
'\0'
)
{
int
i
=
(
int
)(
prefix
[
0
]
-
'0'
);
if
(
i
>=
0
&&
i
<=
9
)
{
if
(
cur_node
->
child
[
i
])
{
child_node
=
cur_node
->
child
[
i
];
prefix
++
;
/* advance to next character in prefix */
}
}
else
{
return
0
;
/* bad input */
}
}
}
return
count
;
}
/**
* Destroy the route trie node and its children
* @param node the trie node
*/
static
void
route_trie_node_destroy
(
struct
route_trie_node
*
node
)
{
if
(
node
)
{
int
i
;
for
(
i
=
0
;
i
<
10
;
i
++
)
{
struct
route_trie_node
*
child
=
node
->
child
[
i
];
route_trie_node_destroy
(
child
);
}
if
(
node
->
array
)
{
free
(
node
->
array
);
}
free
(
node
);
}
}
struct
route_trie
*
route_trie_create
(
route_size_t
max_routes
)
{
struct
route_trie
*
trie
=
malloc
(
sizeof
(
*
trie
));
trie
->
root
=
route_trie_node_create
();
trie
->
max_routes
=
max_routes
;
return
trie
;
}
int
route_trie_add
(
struct
route_trie
*
trie
,
const
char
*
prefix
,
route_cost_t
cost
,
route_value_t
value
,
route_type_t
type
)
{
struct
route
route
=
{
cost
,
value
,
type
};
return
route_trie_node_add
(
trie
->
root
,
prefix
,
&
route
,
trie
->
max_routes
);
}
route_size_t
route_trie_find
(
struct
route_trie
*
trie
,
const
char
*
prefix
,
struct
route_array
*
array
)
{
return
route_trie_node_find
(
trie
->
root
,
prefix
,
array
);
}
int
route_trie_find_count
(
struct
route_trie
*
trie
,
const
char
*
key
)
{
return
route_trie_node_find_count
(
trie
->
root
,
key
);
}
void
route_trie_destroy
(
struct
route_trie
*
trie
)
{
if
(
trie
)
{
route_trie_node_destroy
(
trie
->
root
);
free
(
trie
);
}
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/
crienzo/mod_simple_lcr/route_trie.h
0 → 100644
浏览文件 @
c1a3bce0
/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2012, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Christopher M. Rienzo <chris@rienzo.com>
*
* Maintainer: Christopher M. Rienzo <chris@rienzo.com>
*
* route_trie.h -- prefix tree of routes ordered by least cost ascending.
*
*/
#ifndef ROUTE_TRIE_H
#define ROUTE_TRIE_H
#include <stdint.h>
struct
route_trie
;
struct
route_array
;
struct
route
;
typedef
uint16_t
route_value_t
;
typedef
uint8_t
route_type_t
;
typedef
uint16_t
route_cost_t
;
typedef
uint8_t
route_size_t
;
/**
* Get the route value
* @param route the route
* @return the value
*/
route_value_t
route_get_value
(
struct
route
*
route
);
/**
* Get the user-defined value
* @param route the route
* @return type the type of route
*/
route_type_t
route_get_type
(
struct
route
*
route
);
/**
* Get the route cost
* @param route the route
* @return the cost
*/
route_cost_t
route_get_cost
(
struct
route
*
route
);
/**
* Create an array to store lookup results
*
* @param max_size the maximum number of results to store
* @return the array
*/
struct
route_array
*
route_array_create
(
route_size_t
max_size
);
/**
* Add a route to the array preserving the order of cost, ascending. The highest
* cost route is removed from the array to make room for a lower cost route.
* @param array the array
* @param route the route to add
*/
void
route_array_add
(
struct
route_array
*
array
,
struct
route
*
route
);
/**
* Get the size of the array
* @param array the array
* @return the array size
*/
route_size_t
route_array_get_size
(
struct
route_array
*
array
);
/**
* Get a route from the array
* @param array the array
* @param index the route index
* @return the route or NULL if out of bounds
*/
struct
route
*
route_array_get_route
(
struct
route_array
*
array
,
route_size_t
index
);
/**
* Reset the array for a new query
* @param array
*/
void
route_array_reset
(
struct
route_array
*
array
);
/**
* Destroy the route array
* @param array the array
*/
void
route_array_destroy
(
struct
route_array
*
array
);
/**
* Search array for matching value
* @param value to match
* @return 1 if array contains value
*/
int
route_array_contains
(
struct
route_array
*
array
,
route_value_t
value
);
/**
* Create a new route_trie
* @param max_routes the maximum number of routes to store per node
* @return the trie
*/
struct
route_trie
*
route_trie_create
(
route_size_t
max_routes
);
/**
* Add a route to the trie
* @param trie the trie
* @param prefix the route prefix
* @param cost the route cost
* @param value the routing information
* @param type the type of route
* @return 1 if successful
*/
int
route_trie_add
(
struct
route_trie
*
trie
,
const
char
*
prefix
,
route_cost_t
cost
,
route_value_t
value
,
route_type_t
type
);
/**
* Search for least cost routes.
* @param trie the trie
* @param key the number to search
* @param array the array to fill with routes, ordered by cost ascending
* @return the number of routes found
*/
route_size_t
route_trie_find
(
struct
route_trie
*
trie
,
const
char
*
key
,
struct
route_array
*
array
);
/**
* Get count of routes
* @param trie the trie
* @param key the number to search
* @return the number of routes
*/
int
route_trie_find_count
(
struct
route_trie
*
trie
,
const
char
*
key
);
/**
* Destroy the trie
* @param trie the trie
*/
void
route_trie_destroy
(
struct
route_trie
*
trie
);
#endif
/* ROUTE_TRIE_H */
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/
crienzo/mod_simple_lcr/test/Makefile
0 → 100644
浏览文件 @
c1a3bce0
all
:
gcc
-g
test.c
-I
.
-I
.. ../route_trie.c
-o
test
-lrt
crienzo/mod_simple_lcr/test/test.c
0 → 100644
浏览文件 @
c1a3bce0
/*
* test.c
*
* Christopher M. Rienzo <chris@rienzo.com>
*
* test application for route_trie
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "../route_trie.h"
void
test_lookups
()
{
char
*
search
[]
=
{
""
,
"0"
,
"00"
,
"000"
,
"0000"
,
"23"
,
"234"
,
"2345"
,
"23456"
,
"234567"
,
"2345678"
,
"111111111"
,
"222222222"
,
"333333333"
,
"444444444"
,
"555555555"
,
"666666666"
,
"777777777"
,
"888888888"
,
"99999999"
,
"0000000000"
,
"abcd"
,
"+12345"
,
"61723+"
,
"aasdgs"
,
"!@#$%^&*"
,
"+1234"
,
NULL
};
char
*
routes
[]
=
{
"null"
,
"P1"
,
"CV"
,
"IP"
,
"FR"
,
"FW"
,
"L3"
,
"GX"
,
"IC"
,
"ERROR"
};
char
*
types
[]
=
{
"intra"
,
"inter"
};
int
i
;
int
success
=
0
;
struct
route_array
*
results
=
route_array_create
(
3
);
struct
route_trie
*
trie
=
route_trie_create
(
3
);
success
+=
route_trie_add
(
trie
,
"781897"
,
0
.
001
f
*
10000
,
1
,
0
);
success
+=
route_trie_add
(
trie
,
"774266"
,
0
.
0005
f
*
10000
,
1
,
1
);
success
+=
route_trie_add
(
trie
,
"234567"
,
0
.
01
f
*
10000
,
2
,
0
);
success
+=
route_trie_add
(
trie
,
"234567"
,
0
.
09
f
*
10000
,
3
,
1
);
success
+=
route_trie_add
(
trie
,
"234567"
,
0
.
07
f
*
10000
,
4
,
0
);
success
+=
route_trie_add
(
trie
,
"234567"
,
0
.
02
f
*
10000
,
5
,
1
);
success
+=
route_trie_add
(
trie
,
"234567"
,
0
.
025
f
*
10000
,
6
,
0
);
success
+=
route_trie_add
(
trie
,
"23"
,
0
.
05
f
*
10000
,
7
,
1
);
success
+=
route_trie_add
(
trie
,
"234"
,
0
.
02
f
*
10000
,
8
,
0
);
success
+=
route_trie_add
(
trie
,
"1234+"
,
0
.
1
f
*
10000
,
9
,
0
);
success
+=
route_trie_add
(
trie
,
"+1234"
,
0
.
1
f
*
10000
,
9
,
0
);
success
+=
route_trie_add
(
trie
,
"asdvasvn3q8a-vw9"
,
0
.
1
f
*
10000
,
9
,
0
);
success
+=
route_trie_add
(
trie
,
"!@#$%^&*()m"
,
0
.
1
f
*
10000
,
9
,
0
);
if
(
success
>
9
)
{
printf
(
"Bad data inserted into trie!
\n
"
);
}
else
if
(
success
<
9
)
{
printf
(
"Not enough data inserted into trie!
\n
"
);
}
for
(
i
=
0
;
search
[
i
];
i
++
)
{
route_array_reset
(
results
);
route_size_t
num_routes
=
route_trie_find
(
trie
,
search
[
i
],
results
);
if
(
num_routes
)
{
int
j
;
for
(
j
=
0
;
j
<
num_routes
;
j
++
)
{
struct
route
*
route
=
route_array_get_route
(
results
,
j
);
printf
(
"%s, route %s: type = %s cost = %f
\n
"
,
search
[
i
],
routes
[
route_get_value
(
route
)],
types
[
route_get_type
(
route
)],
(
float
)
route_get_cost
(
route
)
/
10000
.
0
f
);
}
}
else
{
printf
(
"%s: NO MATCH
\n
"
,
search
[
i
]);
}
}
route_array_destroy
(
results
);
route_trie_destroy
(
trie
);
}
void
test_performance
()
{
int
i
,
z
;
struct
route_array
*
results
=
route_array_create
(
4
);
char
dialstring
[
11
]
=
{
0
};
char
*
routes
[]
=
{
"null"
,
"A"
,
"B"
,
"C"
,
"D"
,
"E"
};
struct
timespec
ts
,
te
;
struct
route_trie
*
trie
=
route_trie_create
(
5
);
printf
(
"building trie of 1 mil nodes...
\n
"
);
for
(
i
=
0
;
i
<=
999999
;
i
++
)
{
char
prefix
[
7
]
=
{
0
};
sprintf
(
prefix
,
"%06d"
,
i
);
route_trie_add
(
trie
,
prefix
,
0
.
01
f
*
10000
,
1
,
0
);
route_trie_add
(
trie
,
prefix
,
0
.
005
f
*
10000
,
2
,
0
);
route_trie_add
(
trie
,
prefix
,
0
.
003
f
*
10000
,
3
,
0
);
route_trie_add
(
trie
,
prefix
,
0
.
002
f
*
10000
,
4
,
0
);
route_trie_add
(
trie
,
prefix
,
0
.
015
f
*
10000
,
5
,
0
);
}
printf
(
"executing 30 million lookups...
\n
"
);
clock_gettime
(
CLOCK_MONOTONIC
,
&
ts
);
for
(
z
=
0
;
z
<=
30000000
;
z
++
)
{
char
prefix
[
7
]
=
{
0
};
sprintf
(
prefix
,
"%06d"
,
z
%
1000000
);
route_array_reset
(
results
);
route_trie_find
(
trie
,
dialstring
,
results
);
}
clock_gettime
(
CLOCK_MONOTONIC
,
&
te
);
printf
(
"destroying trie
\n
"
);
route_array_destroy
(
results
);
route_trie_destroy
(
trie
);
printf
(
"Start time = %lu sec
\n
"
,
ts
.
tv_sec
);
printf
(
"End time = %lu sec
\n
"
,
te
.
tv_sec
);
printf
(
"Lookups per sec = %d
\n
"
,
30000000
/
(
te
.
tv_sec
-
ts
.
tv_sec
));
printf
(
"Mean lookup time = %f nsec
\n
"
,
(
te
.
tv_sec
-
ts
.
tv_sec
)
/
(
30000000
*
1e-9
));
}
int
main
(
int
argc
,
char
**
argv
)
{
test_lookups
();
test_performance
();
return
0
;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论