前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kamailio Tips

Kamailio Tips

作者头像
Seven Du
发布2022-12-12 15:27:02
2.1K0
发布2022-12-12 15:27:02
举报

Kamailio有很多小技巧,这里略举一二,作抛砖引玉之用。

uri == myself

编辑/etc/kamailio/kamailio.cfg

代码语言:javascript
复制
debug=3
log_stderror=yes
children=1
listen=udp:0.0.0.0:5060
loadmodule "pv.so"
alias="test.com" /* 别名,可以定义多次 */
loadmodule "sl.so"
loadmodule "corex.so"
loadmodule "kex.so"


request_route {
  if (uri != myself) {
    sl_send_reply("403", "Not relaying");
    exit;
  }
  sl_send_reply("200", "OK");
  exit;
}

启动kamailio

用下面的命令行启动sipsak:

代码语言:javascript
复制
sipsak -p 127.0.0.1 -r 5060 -s sip:test.com -vvv

这是sipsak发出去的请求:

代码语言:javascript
复制
OPTIONS sip:test.com SIP/2.0
Via: SIP/2.0/UDP 127.0.1.1:44245;branch=z9hG4bK.2eaeb15b;rport;alias
From: sip:sipsak@127.0.1.1:44245;tag=2568ab41
To: sip:test.com
Call-ID: 627616577@127.0.1.1
CSeq: 1 OPTIONS
Contact: sip:sipsak@127.0.1.1:44245
Content-Length: 0
Max-Forwards: 70
User-Agent: sipsak 0.9.8.1
Accept: text/plain

得到的回复是200 OK(为了节省篇幅,这里就不贴出来了)。

我们接下来看kamailio的日志:

代码语言:javascript
复制
DEBUG: <core> [core/parser/parse_fline.c:249]: parse_first_line(): first line type 1 (request) flags 1
DEBUG: <core> [core/parser/msg_parser.c:677]: parse_msg(): SIP Request:
DEBUG: <core> [core/parser/msg_parser.c:678]: parse_msg():  method:  <OPTIONS>
DEBUG: <core> [core/parser/msg_parser.c:680]: parse_msg():  uri:     <sip:test.com>
DEBUG: <core> [core/parser/msg_parser.c:682]: parse_msg():  version: <SIP/2.0>
DEBUG: <core> [core/parser/parse_hname2.c:293]: parse_sip_header_name(): parsed header name [Via] type 1
DEBUG: <core> [core/parser/parse_via.c:1300]: parse_via_param(): Found param type 232, <branch> = <z9hG4bK.2eaeb15b>; state=6
DEBUG: <core> [core/parser/parse_via.c:1300]: parse_via_param(): Found param type 235, <rport> = <n/a>; state=6
DEBUG: <core> [core/parser/parse_via.c:1300]: parse_via_param(): Found param type 237, <alias> = <n/a>; state=16
DEBUG: <core> [core/parser/parse_via.c:2639]: parse_via(): end of header reached, state=5
DEBUG: <core> [core/parser/msg_parser.c:555]: parse_headers(): Via found, flags=2
DEBUG: <core> [core/parser/msg_parser.c:557]: parse_headers(): this is the first via
DEBUG: <core> [core/parser/parse_hname2.c:293]: parse_sip_header_name(): parsed header name [From] type 4
DEBUG: <core> [core/parser/parse_hname2.c:293]: parse_sip_header_name(): parsed header name [To] type 3
DEBUG: <core> [core/parser/parse_addr_spec.c:884]: parse_addr_spec(): end of header reached, state=9
DEBUG: <core> [core/parser/msg_parser.c:172]: get_hdr_field(): <To> [14]; uri=[sip:test.com]
DEBUG: <core> [core/parser/msg_parser.c:174]: get_hdr_field(): to body (14)[sip:test.com
]g (0)[]
DEBUG: <core> [core/parser/parse_hname2.c:293]: parse_sip_header_name(): parsed header name [Call-ID] type 6
DEBUG: <core> [core/parser/parse_hname2.c:293]: parse_sip_header_name(): parsed header name [CSeq] type 5
DEBUG: <core> [core/parser/msg_parser.c:152]: get_hdr_field(): cseq <CSeq>: <1> <OPTIONS>
DEBUG: <core> [core/receive.c:387]: receive_msg(): --- received sip message - request - call-id: [627616577@127.0.1.1] - cseq: [1 OPTIONS]
DEBUG: <core> [core/receive.c:259]: ksr_evrt_pre_routing(): event route core:pre-routing not defined
DEBUG: <core> [core/receive.c:457]: receive_msg(): preparing to run routing scripts...
DEBUG: <core> [core/socket_info.c:641]: grep_sock_info(): checking if host==us: 8==7 && [test.com] == [0.0.0.0]
DEBUG: <core> [core/socket_info.c:648]: grep_sock_info(): checking if port 5060 (advertise 0) matches port 5060
DEBUG: <core> [core/name_alias.h:62]: grep_aliases(): matching (0:test.com:5060) vs. (0:test.com:0)
DEBUG: <core> [core/forward.c:429]: check_self(): host (0:test.com:5060) == me
DEBUG: <core> [core/parser/parse_hname2.c:293]: parse_sip_header_name(): parsed header name [Contact] type 7
DEBUG: <core> [core/parser/parse_hname2.c:293]: parse_sip_header_name(): parsed header name [Content-Length] type 12
DEBUG: <core> [core/parser/msg_parser.c:187]: get_hdr_field(): content_length=0
DEBUG: <core> [core/parser/parse_hname2.c:293]: parse_sip_header_name(): parsed header name [Max-Forwards] type 8
DEBUG: <core> [core/parser/parse_hname2.c:293]: parse_sip_header_name(): parsed header name [User-Agent] type 28
DEBUG: <core> [core/parser/parse_hname2.c:293]: parse_sip_header_name(): parsed header name [Accept] type 23
DEBUG: <core> [core/parser/msg_parser.c:91]: get_hdr_field(): found end of header
DEBUG: <core> [core/receive.c:514]: receive_msg(): request-route executed in: 305 usec

日志的前面几行表明收到了OPTIONS请求,其中uri是sip:test.com。

那么这个uri到底是不是myself呢?我们接着看日志。

代码语言:javascript
复制
grep_sock_info(): checking if host==us: 8==7 && [test.com] == [0.0.0.0]

这行日志是uri跟listen的地址(0.0.0.0)进行比较,显然不匹配。

代码语言:javascript
复制
grep_aliases(): matching (0:test.com:5060) vs. (0:test.com:0)

这行日志是uri跟别名(alias)进行比较,匹配成功后就走下面这段路由,回复200 OK:

代码语言:javascript
复制
sl_send_reply("200", "OK");

uri是否等于myself,步骤是这样的:

  • uri是否等于listen的地址(如果有多个listen地址,匹配一个就行)
  • uri是否等于advertise的地址
  • uri是否等于别名

上面三个条件满足其中一个就行。

有兴趣的可以试试下面这个命令:

代码语言:javascript
复制
sipsak -p 127.0.0.1 -r 5060 -s sip:abc.com -vvv

分支(branch)

我们常用lookup("location")来呼叫用户。

假如有三个contact同时在线,那么第一个叫主分支,剩下两个叫附加分支。

主分支的地址是du,附加分支的地址是(branch(uri)[0])和

如果需求是先查数据库,然后再创建多个分支,那要怎么处理呢?

针对数据库结果集的第一行设置$du,针对数据库结果集的其他行,调用append_branch(),最后调用t_relay()。

顺便提下,全局参数max_branches默认是12,最大可以设置为31。

ds_select_dst()成功后修改SIP包

ds_select_dst仅仅设置了$du,并不会自动修改sip包。

如果有需要,可参考下面的例子修改SIP包:

代码语言:javascript
复制
if (ds_select_dst("1", "4")) {
xlog("will update <$ru>");
$rd = $dd;
$rp = $dp;
xlog("final RURI is <$ru>");
}

使能/etc/hosts里面的主机名/域名

配置下面两个全局参数:

代码语言:javascript
复制
dns_use_search_list=yes

use_dns_cache=no

之后Kamailio的配置文件(或者KEMI)就可以成功查询到`/etc/hosts`里面的主机名/域名。

cat /etc/hosts

代码语言:javascript
复制
127.0.0.1       localhost
192.168.100.132 fs

cat /etc/kamailio/kamailio.cfg

代码语言:javascript
复制
dns_use_search_list=yes
use_dns_cache=no
...

request_route {
route(FS);
...
}

route[FS] {
$du = "sip:fs:5080";
t_relay();
exit;
}

auth(认证)

一般用auth_db模块来做认证,是否有类似FreeSWITCH的mod_xml_curl的机制,把请求发到让HTTP服务器,让后者提供密码?

回答是YES,借助auth模块的pv_auth_check函数即可。

Native路由如下:

代码语言:javascript
复制
loadmodule "auth.so"
loadmodule "registrar.so"
loadmodule "usrloc.so"
loadmodule "jansson.so"
loadmodule "http_client.so"


modparam("http_client", "httpcon", "apiserver=>http://localhost/api");

...


route[AUTH] {
if (is_method("REGISTER") || from_uri==myself) {
jansson_set("string", "src_ip", $si, "$var(body)");
jansson_set("integer", "src_port", $sp, "$var(body)");
jansson_set("string", "username",  $Au, "$var(body)");
$var(res) = http_connect("apiserver", "/auth", "application/json", $var(body), "$avp(gurka)");
jansson_get("password", $avp(gurka), "$avp(password)");


if (!pv_auth_check("$fd", "$avp(password)", "0", "1")) {
auth_challenge("$fd", "0");
exit;
}


# user authenticated - remove auth header
if(!is_method("REGISTER|PUBLISH"))
consume_credentials();
}
}

Native和Lua互相调用

编辑/etc/kamailio/kamailio.cfg

代码语言:javascript
复制
debug=2
log_stderror=yes
children=1
listen=udp:127.0.0.1:5060


loadmodule "jsonrpcs.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "pv.so"
loadmodule "ctl.so"
loadmodule "cfg_rpc.so"
loadmodule "xlog.so"
loadmodule "rtimer.so"
loadmodule "jansson.so"
loadmodule "app_lua.so"


modparam("rtimer", "timer", "name=ta;interval=10;mode=1;")
modparam("rtimer", "exec", "timer=ta;route=ONTIMER")
modparam("app_lua", "load", "/etc/kamailio/kamailio.lua")


request_route {
if (uri != myself) {
sl_send_reply("403", "Not relaying");
exit;
}
sl_send_reply("200", "OK");
exit;
}


route[ONTIMER] {
lua_run("test_fun1", "1", "2", "3"); /* 第一个参数是Lua函数名称,其他的是Lua函数的参数,参数个数不能超过三个 */
lua_runstring("test_fun2([[$timef(%Y-%m-%d %H:%M:%S)]], [[$TS]])"); /* lua_runstring跟lua_run的作用是一样的,仅仅是传参数的方式不一样 */


lua_dofile("/etc/kamailio/test.lua");
xinfo("luaret = $var(luaret)\n");
}


route[TEST] {
xinfo("json = $var(j)\n");
jansson_array_size("bancc", $var(j), "$var(size)");
xinfo("size = $var(size)\n");
$var(count) = 0;
while($var(count) < $var(size)) {
jansson_get("bancc[$var(count)]", $var(j), "$var(cc)");
xinfo("cc = $var(cc)\n");
$var(count) = $var(count) + 1;
}
}

编辑/etc/kamailio/kamailio.lua

代码语言:javascript
复制
function test_fun1(param1, param2, param3)
KSR.info("param1 = " .. param1 .. "\n")
KSR.info("param2 = " .. param2 .. "\n")
KSR.info("param3 = " .. param3 .. "\n")
end


function test_fun2(localtime, timestamp)
KSR.info("Localtime = " .. localtime .. "\n")
KSR.info("Unix timestamp = " .. timestamp .. "\n")
end
```


编辑`/etc/kamailio/test.lua`


```lua
KSR.info("Hello, Kamailio\n")
KSR.pv.sets("$var(luaret)", "OK")


KSR.pv.sets("$var(j)", '{"bancc":["ES","FR","DE","US"]}')
KSR.route("TEST")

Native路由里面调用RPC命令

很多模块都有RPC命令,比如:

https://kamailio.org/docs/modules/5.5.x/modules/dispatcher.html#dispatcher.r.add

那么在Native路由里面怎么调用RPC命令?下面就是一个例子:

代码语言:javascript
复制
debug=2
log_stderror=yes
children=1
listen=udp:0.0.0.0:5060
#alias="test.com"


loadmodule "jsonrpcs.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "pv.so"
loadmodule "ctl.so"
loadmodule "cfg_rpc.so"
loadmodule "xlog.so"
loadmodule "htable.so"
loadmodule "jansson.so"
loadmodule "dispatcher.so"


modparam("dispatcher", "list_file", "/etc/kamailio/dispatcher.list")
modparam("dispatcher", "ds_probing_mode", 3)
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "ds_probing_threshold", 3)
modparam("dispatcher", "ds_ping_interval", 5)


request_route {
if (uri != myself) {
sl_send_reply("403", "Not relaying");
exit;
}
sl_send_reply("200", "OK");
exit;
}


# kamcmd dispatcher.add 1 sip:192.168.1.100:5080 8 rweight=50;weight=50;cc=1


route[RPC] {
$var(setid) = 1;
$var(dest) = "sip:192.168.1.100:5080";
$var(flag) = 8;
$var(attribute) = "rweight=50;weight=50;cc=1";


jansson_set("string", "jsonrpc", "2.0", "$var(req)");
jansson_set("string", "method", "dispatcher.add", "$var(req)");


$var(params) = "[]";
jansson_append("int", "", "$var(setid)", "$var(params)");
jansson_append("string", "", "$var(dest)", "$var(params)");
jansson_append("int", "", "$var(flag)", "$var(params)");
jansson_append("string", "", $var(attribute), "$var(params)");
# $var(params): [1,"sip:192.168.1.100:5080",8,"rweight=50;weight=50;cc=1"]


jansson_set("array", "params", $var(params), "$var(req)");
jansson_set("int", "id", 1, "$var(req)");


# $var(req): {"jsonrpc":"2.0","method":"dispatcher.add","params":[1,"sip:192.168.1.100:5080",8,"rweight=50;weight=50;cc=1"],"id":1}
xinfo("req = $var(req)\n");


jsonrpc_exec("$var(req)");


xinfo("code = $jsonrpl(code)\n");
xinfo("body = $jsonrpl(body)\n");
}


event_route[htable:mod-init] {
route(RPC);
}

好了,今天就说这么多,祝玩得开心。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-10-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FreeSWITCH中文社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档