# 介绍kamailio的dialog模块
`kamailio`的`dialog`模块一般有四个作用:
- 读写对话变量
- 跟`uac`模块配合,完成`uac trunk auth`功能
- 统计`early_dialogs`和`active_dialogs`等
- 利用`dialog profile`实现分类统计功能或者实现呼叫限制功能
`dialog`模块的参数可以配置如下:
```
modparam("dialog", "db_url", DBURL)
modparam("dialog", "enable_stats", 1) # 使能统计功能
modparam("dialog", "db_mode", 1)
modparam("dialog", "dlg_flag", DLG_FLAG) # 范围是0:31
modparam("dialog", "dlg_match_mode", 1)
modparam("dialog", "default_timeout", 43200 ) # 12小时,设置dialog超时时间
modparam("dialog", "track_cseq_updates", 1)
modparam("dialog", "profiles_no_value", "total; emergency")
modparam("dialog", "profiles_with_value", "user; type; account")
```
下面这段路由脚本创建对话:
```
route[INVITE] {
if (is_method("INVITE") && !has_totag()) {
setflag(DLG_FLAG); # 创建对话,跟dlg_flag参数的值对应起来
# dlg_manage(); # 调用这个函数也可以创建对话
}
return;
}
```
目前对话变量仅支持字符串类型,下面这段代码可以证明这点:
```
$dlg_var(test_i) = 1;
if (!pv_isset("$dlg_var(test_i)")) {
xerr("route run here, file=$cfg(file) line=$cfg(line)\n");
}
$dlg_var(test_s) = '1';
if (pv_isset("$dlg_var(test_s)")) {
xinfo("route run here, file=$cfg(file) line=$cfg(line)\n");
}
```
我们知道`avp`变量仅在事务期间有效,如果想在整个对话期间都有效那就需要用到对话变量了
对话变量常见的使用场景可能是写自己的话单,这里给出路由脚本:
```
route[INVITE] {
if (is_method("INVITE") && !has_totag()) {
dlg_manage();
$dlg_var(SetupTime) = $TS;
}
return;
}
event_route[dialog:start] {
$dlg_var(AnswerTime) = $TS;
}
event_route[dialog:end] {
$dlg_var(EndTime) = $TS;
$dlg_var(BillSec) = (str)($dlg_var(EndTime) - $dlg_var(AnswerTime)); # 转成字符串类型
xinfo("+++$dlg_var(BillSec)\n");
$var(x) = $_s({"Event":"Call_End", "CallID":"$dlg(callid)", "From":"$dlg(from_uri)", "To":"$dlg(to_uri), ");
$var(x) = $var(x) + $_s("SetupTime":$dlg_var(SetupTime), "AnswerTime":$dlg_var(AnswerTime), "EndTime":$dlg_var(EndTime), "BillSec":$dlg_var(BillSec)});
xinfo("$var(x)\n");
# http post
}
```
接下来讨论`uac trunk auth`,流程如下:
```
1. A -> INVITE -> kamailio B
2. A kamailio -> INVITE -> B CSeq
3. A kamailio <- 401(7) <- B
4. A kamailio -> INVITE (auth) -> B CSeq+1
5. A kamailio <- 200 <- B
6. A <- 200 <- kamailio
```
把`dialog`模块的`track_cseq_updates`参数配置为1,第四步`CSeq`就会自动加一
模块配置和路由脚本示意如下:
```
#!define UAC_CONTACT_ADDRESS "192.168.1.100:5060"
modparam("uac", "reg_db_url", DBURL)
modparam("uac", "reg_timer_interval", 3)
modparam("uac", "reg_retry_interval", 28)
modparam("uac", "reg_gc_interval", 30)
modparam("uac", "reg_contact_addr", UAC_CONTACT_ADDRESS)
modparam("uac", "auth_realm_avp", "$avp(arealm)")
modparam("uac", "auth_username_avp", "$avp(auser)")
modparam("uac", "auth_password_avp", "$avp(apasswd)")
modparam("uac", "reg_keep_callid", 1)
route[GW] {
$du = "sip:192.168.1.101:5060";
t_on_failure("TRUNKAUTH");
t_relay();
exit;
}
failure_route[TRUNKAUTH] {
if (t_is_canceled()) {
exit;
}
if(t_check_status("401|407")) {
$avp(auser) = "test"; # 实际使用时需从数据库取出用户名和密码
$avp(apasswd) = "test"; # 同上
if (uac_auth()) {
t_relay();
}
exit;
}
}
```
接下来我们讨论`dialog`自带的统计功能
`enable_stats`参数配置为1就可以使能了
这里有二个`shell`命令,都可以查到`dialog`模块的统计:
```shell
kamcmd stats.get_statistics all | grep dialog
```
```shell
kamcmd dlg.stats_active
```
最后我们讨论`dialog profile`方面的问题
比如,`dialog`这样配置模块参数:
```
modparam("dialog", "profiles_no_value", "total; emergency")
modparam("dialog", "profiles_with_value", "user; type; account")
```
那么我们这样写路由:
```
route[INVITE] {
if (is_method("INVITE") && !has_totag()) {
dlg_manage();
set_dlg_profile("total");
}
route(LOCATION);
return;
}
```
现在做一个呼叫测试,6001呼叫6002
```shell
$kamcmd dlg.profile_get_size total
1
```
结果是1
下面这个命令得到的输出更加详细:
```shell
$kamcmd dlg.profile_list total
{
h_entry: 3702
h_id: 5131
ref: 2
call-id: ef6fcea66f0f40938cc3060226340f39
from_uri: sip:[email protected]
to_uri: sip:[email protected]
state: 4
start_ts: 1691650230
init_ts: 1691650229
end_ts: 0
duration: 35
timeout: 1691693429
lifetime: 43200
dflags: 1536
sflags: 0
iflags: 0
caller: {
tag: fa86e515d20348c6b217ae3bd4efcefc
contact: sip:[email protected]:61224;ob
cseq: 32127
route_set:
socket: udp:192.168.100.200:5060
}
callee: {
tag: 8CC433477696B38087EC8FFAB0858E00
contact: sip:[email protected]:5060;transport=udp
cseq: 0
route_set:
socket: udp:192.168.100.200:5060
}
profiles: {
total
}
variables: {
}
}
```
请注意,`variables`无值
下面是进一步的说明:
- set_dlg_profile("total"); # 没问题,因为profiles_no_value里面已定义total
- set_dlg_profile("emergency"); # 没问题,因为profiles_no_value里面已定义emergency
- set_dlg_profile("total", "$fu"); # 不行,因为profiles_with_value没有定义total
- set_dlg_profile("user", "$fu"); # 没问题,因为profiles_with_value已定义user
分类统计方面我们可以给一个例子:
```
modparam("dialog", "profiles_no_value", "total; local; domestic; international")
route[INVITE] {
if (is_method("INVITE") && !has_totag()) {
dlg_manage();
set_dlg_profile("total"); # 总的呼叫数加一
if ($tU =~ "^00") {
set_dlg_profile("international"); # 国际长途呼叫数加一
} else if ($tU =~ "^0") {
set_dlg_profile("domestic"); # 国内长途呼叫数加一
} else {
set_dlg_profile("local"); # 本地呼叫数加一
}
}
route(LOCATION);
return;
}
```
至于如何实现呼叫限制功能,网上能查到的资料非常丰富,这里就不再赘述了。