Kamailio UAC 模块简述

Kamailio 是一款非常强大的 SIP 代理服务器,Kamailio 一般转发 SIP 信令,不主动产生和发送 SIP 信令。但有时您可能希望 Kamailio 向 IPPBX 注册、主动发 SIP 消息,等等,也就是让 Kamailio 起到客户端的作用,这就需要用到 UAC 模块。

UAC 模块包含的内容很多,但官方手册给的例子比较少,想要熟练掌握就需要做很多练习。本文分几个方面进行说明,希望起到抛砖引玉的效果。

Kamailio UAC 模块简述

作者:韩小仿
来源:FreeSWITCH中文社区
原文:https://mp.weixin.qq.com/s/xfra6LUedHYngC5s6gaFxw

本文内提到的《Kamailio实战》,您可关注小樱桃科技公众号,在菜单栏选择小樱桃商城跳转购买。

Kamailio 是一款非常强大的 SIP 代理服务器,Kamailio 一般转发 SIP 信令,不主动产生和发送 SIP 信令。但有时您可能希望 Kamailio 向 IPPBX 注册、主动发 SIP 消息,等等,也就是让 Kamailio 起到客户端的作用,这就需要用到 UAC 模块。

UAC 模块包含的内容很多,但官方手册给的例子比较少,想要熟练掌握就需要做很多练习。本文分几个方面进行说明,希望起到抛砖引玉的效果

uac_replace、uac_restore 

先看下面的流程:

UAC                   Kamailio                 UAS
     <----->                      <----->
   原始主被叫保持不变             新的主叫和被叫

例如 1001 呼叫 1002,通过下面的路由脚本就可以把主叫号码修改成 alice,被叫号码修改成 bob:

uac_replace_from('"alice"', "sip:" + "alice" + "@" + $fd);
uac_replace_to('"bob"', "sip:" + "bob" + "@" + $td);

但问题是如何处理后续的 SIP 请求(比如ACK、Re-Invite、BYE等),UAC 跟 Kamailio 之间始终保持 1001 是主叫,1002 是被叫,Kamailio 跟 UAS 之间始终都是 alice 是主叫,bob 是被叫。

这涉及到下面三个问题:

  • 需要保存新的主被叫号码
  • 主被叫号码保存到哪里,rr 头 或者对话变量
  • 恢复主被叫号码的方式有自动和手工

先看下面的模块参数配置:

modparam("rr", "append_fromtag", 1)                    # 必须为 1

modparam("uac", "restore_mode", "auto")                # 自动恢复方式
modparam("uac", "restore_dlg", 0)                      # 不从对话变量中恢复
modparam("uac", "rr_from_store_param", "vsf")          # 利用 rr 头的 vsf 参数来进行 from 的保存和恢复
modparam("uac", "rr_to_store_param", "vst")            # 利用 rr 头的 vst 参数来进行 to 的保存和恢复
modparam("uac", "restore_passwd", "my_secret_passwd")  # 密码保存到 rr 头的 my_secret_passwd, 密码加密后保存

用 sngrep 跟踪呼叫,下面是 Kamailio 收到的 INVITE 包:

INVITE sip:1002@192.168.0.103 SIP/2.0
Via: SIP/2.0/UDP 192.168.0.100:22040;branch=z9hG4bK-d87543-8a6dd8262a483446-1--d87543-;rport
Max-Forwards: 70
Contact: <sip:1001@192.168.0.100:22040>
To: "1002"<sip:1002@192.168.0.103>
From: <sip:1001@192.168.0.103>;tag=4955d54f
...

经过 Kamailio 路由处理之后,发出来下面这个包:

INVITE sip:1002@192.168.0.100:5066;ob SIP/2.0
Record-Route: <sip:192.168.0.103;lr;ftag=4955d54f;vsf=bXlfczU/KzdRLnhqb2xwantnQWxkYEE-;vst=bXlfczY8IBcFV3t9bHR5cnNnQHJmUA-->
Via: SIP/2.0/UDP 192.168.0.103;branch=z9hG4bKdeb8.9ad64c208ecf50f14f8fff328827fc07.0
Via: SIP/2.0/UDP 192.168.0.100:22040;received=192.168.0.100;branch=z9hG4bK-d87543-8a6dd8262a483446-1--d87543-;rport=22040
Max-Forwards: 69
Contact: <sip:1001@192.168.0.100:22040>
To: "bob"<sip:bob@192.168.0.103>
From: "alice" <sip:alice@192.168.0.103>;tag=4955d54f
...

很明显,rr 头多了 vsf 和 vst 参数。

下面尝试把主被叫号码保存到对话变量,模块参数配置如下:

modparam("rr", "append_fromtag", 1)                    # 必须为 1
modparam("dialog", "db_mode", 0)                       # dialog 的对话变量不保存到数据库
modparam("uac", "restore_mode", "auto")                # 自动恢复方式
modparam("uac", "restore_dlg", 1)                      # 从对话变量中恢复

现在发起一个呼叫,被叫应答之后,运行 kamcmd dlg.list,输出为:

{
  h_entry: 4021
  h_id: 10506
  ref: 2
  call-id: MGUwYjRiNjEzMTA5MGUxOGZhMDljYWZjNDdkOTQyYTM.
  from_uri: sip:1001@192.168.0.103
  to_uri: sip:1002@192.168.0.103
  state: 4
  start_ts: 1726108372
  init_ts: 1726108372
  end_ts: 0
  duration: 2
  timeout: 1726151572
  lifetime: 43200
  dflags: 643
  sflags: 0
  iflags: 0
  caller: {
    tag: 024f2837
    contact: sip:1001@192.168.0.100:25946
    cseq: 1
    route_set:
    socket: udp:192.168.0.103:5060
  }
  callee: {
    tag: 42bca8c3647e483d926a125b9ddc671b
    contact: sip:1002@192.168.0.100:5066;ob
    cseq: 0
    route_set:
    socket: udp:192.168.0.103:5060
  }
  profiles: {
  }
  variables: {
    {
        _uac_tdpnew: "bob"
    }
    {
        _uac_tdp: "1002"
    }
    {
        _uac_tonew: sip:bob@192.168.0.103
    }
    {
        _uac_to: sip:1002@192.168.0.103
    }
    {
        _uac_fdpnew: "alice"
    }
    {
        _uac_fdp:
    }
    {
        _uac_funew: sip:alice@192.168.0.103
    }
    {
        _uac_fu: sip:1001@192.168.0.103
    }
  }
}

可以看到,原始的主被叫号码和修改后的主被叫号码都记录到了对话变量,方便以后做恢复处理。

顺便提下,如果把 dialog 模块的 db_mode 的值从 0 改成 2,那么 kamailio 重启时会自动从数据库里面读入对话变量的值。

uac_reg_send 

一般用 uac_reg_send 发送 OPTIONS 或者 MESSAGE,下面是一段路由代码(任意路由都可以执行):

loadmodule "uas.so"
loadmodule "jansson.so"
...

route[UAC_MESSAGE] {
  $var(aor) = "1001@192.168.0.103";            # 发送 MESSAGE 到 1001 这个注册用户
  $var(text) = "Hello, World!\r\n";            # 待发送的文本内容
  $var(from) = "sip:admin@192.168.0.103";

  # rpc 请求报文
  $var(req) = $_s({"jsonrpc":"2.0", "method":"ul.lookup","params":["location","$var(aor)"], "id":1});
  jsonrpc_exec("$var(req)");
  if ($jsonrpl(code) != 200) return;

  # 暂时只处理一个 contact
  jansson_get("result.Contacts[0].Contact.Address", "$jsonrpl(body)", "$var(address)");
  if (jansson_get("result.Contacts[0].Contact.Received", "$jsonrpl(body)", "$var(received)")) {
    $var(outbound_proxy) = 0;
  } else {
    $var(outbound_proxy) = 1;
  }

  # uac 伪变量可参考这里:https://www.kamailio.org/wikidocs/cookbooks/devel/pseudovariables/#uac_reqkey
  $uac_req(method) = "MESSAGE";
  if ($var(outbound_proxy)) {
    $uac_req(ruri) = $var(received);
  }
  $uac_req(ruri) = $var(address);
  $uac_req(furi) = $var(from);
  $uac_req(turi) = $var(address);
  $uac_req(hdrs) = "Subject: Emergency Alert\r\n";
  $uac_req(hdrs) = $uac_req(hdrs) + "Content-Type: text/plain\r\n";
  $uac_req(body) = $var(text);
  $uac_req(evroute) = 1;                       # 触发 event_route[uac:reply]
  uac_req_send();                              # 发送
}

event_route[uac:reply]
{
  xinfo("===uac reply received, callid = $uac_req(callid), tu = $uac_req(turi), code = $uac_req(evcode)\n");
}

event_route [tm:local-request] {
  if ($rm == "MESSAGE") {
    xinfo("$ci|Routing locally generated $rm to $ru, callid = $ci\n");
    t_set_fr(1000, 10000);
  }
}

早期版本有个 BUG, $uac_req(callid) 最多只能到 128 字节,超过了就会崩溃,但早已修复。

 uac_reg、uac_auth uac_reg 是 Kamailio 作为 SIP 客户端 向 IPPBX(例如 FreeSWITCH)或者 SIP 代理服务器(例如 OpenSIPS)注册。uac_auth 是 Kamailio 自己完成 SIP 认证(而不是转发 UAC 认证请求)。

《Kamailio实战》的第八章的第十一节对此已有很详细的介绍,这里仅补充几点:

  • l_uuid 是 uacreg 表的主键,不能包含逗号(“,”)、艾特(“@”) 等符号,但可以有下划线。逗号在 SIP 协议里面有专门的含义,不能用。艾特是 UAC 模块不让用
  • uacreg 表的 realm 字段可以是默认值(默认值是”),这样就会采用收到的 SIP 包里面的 realm 字段(一般是 401/407),减少发生冲突的可能性
  • uacreg 表的 contact_addr 字段可以是默认值(默认值是”),如果有特殊考虑,也可以进行配置,以便覆盖 UAC 模块的 reg_contact_addr 参数
  • UAC 模块发出的 REGISTER 请求,其 contact 头一般是 l_uuid@contact_addr,contact_addr 支持 ;transport=tcp

笔者专门做过 uac_reg 的压力测试,稳定且效率高,值得您一试。

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论