Kamailio Tips

2022-12-12 15:27:02 浏览数 (2)

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, Kamailion")
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);
}

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

0 人点赞