团队博客

Kamailio rr 的秘密

韩小仿  2024-04

官方给了这样一幅图

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 地址和端口。

listen=udp:KAM_IP:KAM_PORT

双网卡路由

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

listen=udp:eth0:5060
listen=udp:eth1:5060

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

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

单网卡

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

listen=udp:内网地址:SIP端口 advertise 公网地址:SIP端口
VOS-----Kamailio-----Fs

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

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

我们一个一个讲。

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

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

当然有!

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

下面是一个例子:

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 网络拓扑:

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

要避免这样定义:

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 地址,那怎么办呢?

可参考下面的代码:

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 单网卡。

IMS --------------   Kamailio ------------    Fs

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

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

Kamailio 配置如下:

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_FS,不能调用 record_route(Kamailio 理解不了这种网络拓扑),而是 route(rr)

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

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

route[rr] {
    if (isflagset(FLT_SRC_FS_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_FS)){
        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:

interface=IMS/192.168.1.100!10.2.3.5;FS/192.168.1.100!172.16.1.100

对应的路由为:

route[NATMANAGE] {
	...
	$xavp(r=>$T_branch_idx) = "replace-origin replace-session-connection";
	if (is_request() && !has_totag()) {
		if (isflagset(FLT_SRC_FS_DST_IMS)) {
			$xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + ”direction=FS direction=IMS"
		} else if (isflagset(FLT_SRC_IMS_DST_FS)){
			$xavp(r=>$T_branch_idx) = $xavp(r=>$T_branch_idx) + ”direction=IMS direction=FS"
		}
	}
	rtpengine_manage($xavp(r=>$T_branch_idx));
	...
}

参考资料:

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

Windows 开发环境搭建