首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Kamailio rr 的秘密

Kamailio rr 的秘密

作者头像
杜金房
发布2025-11-25 14:45:09
发布2025-11-25 14:45:09
1690
举报

官方详解图:https://kamailio.org/docs/modules/5.7.x/modules/rr.html

代码语言:javascript
复制

UAC                       Kamailio PROXY                          UAS

---- INVITE ------>       record_route()          ----- INVITE ---->
                     add_rr_param(";foo=true")

--- reINVITE ----->        loose_route()          ---- reINVITE --->
                    check_route_param(";foo=true")

<-- reINVITE ------        loose_route()          <--- reINVITE ----
                    check_route_param(";foo=true")

<------ BYE -------        loose_route()          <----- BYE -------
                    check_route_param(";foo=true")

这意思就是说,UAC 和 UAS 之间所有的 SIP 消息都要经过 Kamailio,包括 Request 消息和 Reply 消息。

rr 模块函数很少,但不容易理解,能参考的路由脚本不多。笔者根据自己的经验整理此文,希望对读者有所裨益。

同网段路由

UAC、Kamailio 以及 UAS 都在同一个网段,这种情况下的路由非常简单,调用 record_route() 之后,Kamailio 自动加上自己的 IP 地址和端口。

代码语言:javascript
复制
listen=udp:KAM_IP:KAM_PORT

双网卡路由

最典型的属于一个网卡在内,另外一个网卡对公网。这种情况的路由也比较简单,调用 record_route() 之后,Kamailio 自动加上双 rr 头。

代码语言:javascript
复制
listen=udp:eth0:5060
listen=udp:eth1:5060

但需要注意下面几个要点:

  • modparam("rr", "enable_double_rr", 1),模块参数要使能双 rr
  • mhomed = 1,定义这个全局参数
  • 定义清楚主机上的路由,比如只能通过 eth0去到内网,只能通过 eth1去到公网。如果去到公网即可以通过 eth0,又可以通过 eth1,那可能就麻烦了

单网卡1:1NAT

阿里云的网络就是典型的1:1 NAT,只有一个网卡。

代码语言:javascript
复制
listen=udp:内网地址:SIP端口 advertise 公网地址:SIP端口
代码语言:javascript
复制
VOS-----Kamailio-----FreeSWITCH

比较典型的是:VOS 看到 Kamailio 的公网地址,FreeSWITCH 看到 Kamailio 的内网地址。

这里一般有二种处理办法,一种是单 rr,另外一种是双 rr。

我们一个一个讲。

单 rr 显然只能给 Kamailio 的公网地址,但 FreeSWITCH 虽然通常跟 Kamailio 同在一个内网网段,这就需要 FreeSWITCH 能访问 Kamaialio 的外网地址。

那有没有变通方法,让 FreeSWITCH 只访问 kamailio 的内网地址呢?

当然有!

就是做一条iptables nat规则,似外实内。

下面是一个例子:

代码语言:javascript
复制
iptables -t nat -A OUTPUT  -d 113.11.22.33 -p udp --dport 5060 -j DNAT --to-destination 10.167.82.4:5060

另外一个办法是使能双 rr,可以这样定义 Kamailio 网络拓扑:

代码语言:javascript
复制
listen=udp:KAM_IP4_PRIVATE_ADDR:KAM_SIP_LAN_PORT
listen=udp:KAM_IP4_PRIVATE_ADDR:KAM_SIP_WAN_PORT advertise MY_IP4_PUBLIC_ADDR:KAM_SIP_WAN_PORT

也就是定义二个不同的 SIP 端口

路由里面调用record_route, Kamaiilio 能理解上面的网络拓扑,自动加上双 rr

要避免这样定义:

代码语言:javascript
复制
listen=udp:KAM_IP4_PRIVATE_ADDR:KAM_SIP_LAN_PORT
listen=udp:KAM_IP4_PRIVATE_ADDR:KAM_SIP_WAN_PORT
advertise=MY_IP4_PUBLIC_ADDR

一般看到这里就可以收工了。

下面讲特别场景下的 rr。

MS Teams

Kamailio 可以作为微软 MS Teams 的 Direct Routing。

但微软 MS Teams 对 rr 头有特别的要求,只能用域名,不能用 IP 地址,那怎么办呢?

可参考下面的代码:

代码语言:javascript
复制
route[RECORD_ROUTE] {
if (isflagset(FLT_SRC_MS_TEAMS)) {
record_route_preset("KAM_IP4_ADDR:KAM_SIP_PORT;transport=tcp", "KAM_DOMAIN:KAM_SIPS_PORT;transport=tls");
add_rr_param(";r2=on");
	} else if isflagset(FLT_DST_MS_TEAMS) {
record_route_preset("KAM_DOMAIN:KAM_SIPS_PORT;transport=tls", "KAM_IP4_ADDR:KAM_SIP_PORT;transport=tcp");
add_rr_param(";r2=on");
	} else {
record_route();
	}
return;
}

特殊网络拓扑下的rr

下面讲到的更加特别。

Kamailio 单网卡。

代码语言:javascript
复制
IMS --------------   Kamailio ------------    FreeSWITCH

10.2.3.4        listen 192.168.1.100    172.16.1.99

                    外网地址 10.2.3.5

                    外网地址 172.16.1.100

对于 IMS 来说,只能用 10.2.3.5 访问 Kamailio

对于 FreeSWITCH 来说, 只能用 172.16.1.100 访问 Kamailio

Kamailio 配置如下:

代码语言:javascript
复制
listen=udp:192.168.1.100:5060
alias=10.2.3.5:5060
alias=172.16.1.100:5060

路由脚本需要特别处理:

如果 INVITE 来自 IMS ,设置分支标志 FLB_SRC_IMS_DST_FreeSWITCH,不能调用 record_route(Kamailio 理解不了这种网络拓扑),而是 route(rr)

如果 INVITE 来自 FreeSWITCH, 设置分支标志 FLB_SRC_FreeSWITCH_DST_IMS,同理,需要 route(rr)

下面是 route[rr] 的具体实现:

代码语言:javascript
复制
route[rr] {
    if (isflagset(FLT_SRC_FreeSWITCH_DST_IMS)) {
        record_route_preset("10.2.3.5:5060", "172.16.1.100:5060");
        add_rr_param(";r2=on");
    else if (isflagset(FLT_SRC_IMS_DST_FreeSWITCH)){
        record_route_preset("172.16.1.100:5060", "10.2.3.5:5060");
        add_rr_param(";r2=on");
    } else {
        record_route();
    }
    return;
}

rtpengine 要怎么处理呢?

可以这样定义 rtpengine 的 interface:

代码语言:javascript
复制
interface=IMS/192.168.1.100!10.2.3.5;FreeSWITCH/192.168.1.100!172.16.1.100

对应的路由为:

代码语言:javascript
复制
route[NATMANAGE] {
	...
$xavp(r=>$T_branch_idx) = "replace-origin replace-session-connection";
if (is_request() && !has_totag()) {
if (isflagset(FLT_SRC_FreeSWITCH_DST_IMS)) {
$xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + ”direction=FreeSWITCH direction=IMS"
		} else if (isflagset(FLT_SRC_IMS_DST_FreeSWITCH)){
$xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + ”direction=IMS direction=FreeSWITCH"
		}
	}
	rtpengine_manage($xavp(r=>$T_branch_idx));
	...
}

参考资料:

https://kb.smartvox.co.uk/opensips/contact-and-record-route-headers-explained/

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档