OpenSIPS IP 地址:192.168.31.213
FreeSWITCH IP 地址: 192.168.31.166
转发注册消息的路由代码应该是:
if (is_method("REGISTER")) {save("location", "r");if ($pr == "ws" || $pr == "wss") {$var(new)= $ct;xlog("L_INFO", "[$cfg_line]|$ci|new contact=$var(new)\n");# 删除 ";transport=ws" (如果有)$var(new) = $(var(new){re.subst,/^(.*)(;transport=ws)(.*)/\1\3/});# 删除 ";transport=wss" (如果有)$var(new) = $(var(new){re.subst,/^(.*)(;transport=wss)(.*)/\1\3/});xlog("L_INFO", "[$cfg_line]|$ci|transformed contact $var(new)\n");remove_hf("Contact");append_hf("Contact: $var(new)\r\n");}$du = "sip:192.168.31.166";append_hf("Path: <sip:$tU@192.168.31.213:5060;r2=on;lr;pr=$pr>\r\n");t_relay();exit;
}
有几个细节需要留意下:
1. save() 函数的 第二个参数是 "r", 就是 no reply 的意思,查下文档就知道了。no reply 的意思其实就是 REGISTER 不是我处理,但从我这经过,我偷偷存一份。
2. 如果是 JSSIP 注册,那么修改 Contact 头,删除 ";transport=ws" 或者删除 ";transport=wss" (转换函数支持正则表达式,想清楚了之后发现并不复杂)。
3. 增加 Path 头,这样 FreeSWITCH 回叫该注册用户的时候会把 INVITE 消息发到 OpenSIPS
上面仅仅讲了个大概,还有很多细节需要进一步说明
1. 不调用 save 行不行?当然可以,但 Path 头要增加一个参数,要把 JSSIP 的 地址记录下来。Kamailio 管这个叫 $sut(可以增加这个参数";sut=$sut"),其实就是 $si + $sp + $pr 的组合,但 我查了下 OpenSIPS 的文档,好像并没有对应的伪变量(换言之,只能自己组合)。
2. 为什么要修改 JSSIP 注册请求的 Contact 头,回答是跟 FreeSWITCH 的处理有关,如果发现Contact 里面的 transport=ws 或者是 wss, 那么会自动激活 media_webrtc(FS 通道变量)。
3. Path 是 一个 SIP 规范,FreeSWITCH 支持该规范, FreeSWITCH 回叫该注册用户时,INVITE 发到 OpenSIPS, Route 头等于 Path 头。换言之,Path 增加了任何参数, INVITE 的 Route 都会完整地还回来。
JSSIP 成功注册后, 在 FS1.10.7 一侧输入 fs_cli -x 'show registrations as xml',得到的是:
<result row_count="1">
<row row_id="1">
<reg_user>1002</reg_user>
<realm>192.168.31.166</realm>
<token>uq472289ha85maa9g4m304</token>
<url>sofia/internal/sip:c5pjist8@0flvmn2gtt72.invalid;fs_path=sip%3A1002%40192.168.31.213%3A5060%3Br2%3Don%3Blr%3Bpr%3Dws</url>
<expires>1731241805</expires>
<network_ip>192.168.31.213</network_ip>
<network_port>5060</network_port>
<network_proto>udp</network_proto>
<hostname>DESKTOP-QEPDSM1</hostname>
<metadata></metadata>
</row>
</result>
我们重点看 url 的内容,解码后的内容为:
sofia/internal/sip:c5pjist8@0flvmn2gtt72.invalid;fs_path=sip:1002@192.168.31.213:5060;r2=on;lr;pr=ws
FS 回叫时是这样的:
INVITE sip:c5pjist8@0flvmn2gtt72.invalid SIP/2.0
Via: SIP/2.0/UDP 120.229.53.249;rport;branch=z9hG4bK5vgj6Q04S7gHQ
Route: <sip:1002@192.168.31.213:5060>;r2=on;lr;pr=ws
Max-Forwards: 70
From: <sip:0000000000@192.168.31.166>;tag=pr7cU4X9cHm5j
To: <sip:c5pjist8@0flvmn2gtt72.invalid>
Call-ID: d5a5bb3d-1a01-123e-b7a9-c1d2a5f21113
CSeq: 91066998 INVITE
Contact: <sip:mod_sofia@120.229.53.249:5060>
User-Agent: FreeSWITCH-mod_sofia/1.10.7-release~883d2cb662~64bit...
我们重点看 Route 头,其实是有问题的
我们换成 FreeSWITCH 1.10.12 (1.10.8开始修复了该问题),再来测试一遍
<result row_count="1">
<row row_id="1">
<reg_user>1002</reg_user>
<realm>192.168.31.166</realm>
<token>0d4jd4fp5o4kksjdj8rr22</token>
<url>sofia/internal/sip:fl4lcbh8@hta3sufe4u62.invalid;fs_path=%3Csip%3A1002%40192.168.31.213%3A5060%3Br2%3Don%3Blr%3Bpr%3Dws%3E</url>
<expires>1731242441</expires>
<network_ip>192.168.31.213</network_ip>
<network_port>5060</network_port>
<network_proto>udp</network_proto>
<hostname>DESKTOP-QEPDSM1</hostname>
<metadata></metadata>
</row>
</result>
url 为 sofia/internal/sip:fl4lcbh8@hta3sufe4u62.invalid;fs_path=<sip:1002@192.168.31.213:5060;r2=on;lr;pr=ws>
INVITE 消息为:
INVITE sip:fl4lcbh8@hta3sufe4u62.invalid SIP/2.0
Via: SIP/2.0/UDP 120.229.53.249;rport;branch=z9hG4bKS702tQDXH37HS
Route: <sip:1002@192.168.31.213:5060;r2=on;lr;pr=ws>
Max-Forwards: 70
From: <sip:0000000000@192.168.31.166>;tag=veecU60t7QDUK
To: <sip:fl4lcbh8@hta3sufe4u62.invalid>
Call-ID: a4ccc9d4-1a02-123e-67a0-1b1b392ee782
CSeq: 91067172 INVITE...
现在再看 Route 头,就是对的了(之前尖括号的位置不对)
OpenSIPS 测试的版本是 2.4.11,但是本文给的路由代码应该是所有 OpenSIPS 版本都通用的。
建议您仔细读下本文,至少我没找到很多质量还不错的 OpenSIPS 中文资料。
怎样处理 FS INVITE 呢?这个留着以后讲。
FS INVITE 路由应该怎么写呢?
首先,我们修改写上面的 REGISTER 路由,把 username 放 Contact 里面,这并不难
其次,针对 FS 的不同版本,路由代码要做适配
$var(route) = $hdr(Route);
xlog("route = $var(route)\n");
一种是这样: <sip:1002@192.168.31.213:5060;r2=on;lr;pr=ws>
还有一种是这样: <sip:1002@192.168.31.213:5060>;r2=on;lr;pr=ws
路由代码如下:
if (!is_method("INVITE") return;
$var(route) = $hdr(Route);
if ($var(route) == NULL) return;
xlog("+++route = $var(route)\n");
# 删除开始的 "<"
$var(route) = $(var(route){re.subst,/^<(.*)/\1/});
# 删除 ">",不管它在中间还是在末尾
$var(route) = $(var(route){re.subst,/^(.*)>(.*)/\1\2/});
# 取 user
$var(user) = $(var(route){uri.user});
# 取 协议
$var(proto) = $(var(route){uri.params}{param.value,pr});$var(aor) = "sip:" + $var(user) + "@" + $rd;
if (!lookup("location", "m", "$var(aor)")) {
t_reply("404", "not found");
exit();
}if ($var(proto) == "ws" || $var(proto) == "wss"){
setbflag(DST_WS);
}
route(relay);
exit
如果 route 头有别的参数,照这个例子解析,并不难。如果有 sut 参数,更方便。
# $var(sut) = ...
$du = $var(sut);
route(relay);
exit;
if ($var(proto) == "ws" || $var(proto) == "wss"){
setbflag(DST_WS);
}
这一段貌似也可以注释掉, 处理注册时 只要设置了 bflag, lookup 时 会自动还回来
OpenSIPS 是个好软件,但入门有门槛