Kamailio 里的“严格路由”和“松散路由”本质上是 SIP Route/Record-Route 机制的两种模式,分别来自较老的 RFC 2543 和较新的 RFC 3261。现在实际生产中几乎都使用松散路由。
1. 核心区别
| 对比项 | 严格路由 Strict Routing | 松散路由 Loose Routing |
|---|---|---|
| 标准来源 | 旧 RFC 2543 | RFC 3261 |
| Route URI 标识 | 没有;lr参数 | 有;lr参数 |
| Request-URI | 会被改成下一个代理的 URI | 保持为最终目标 URI |
| Route 头作用 | 通过不断改写 R-URI 实现路由 | Route 头明确表示下一跳 |
| 是否推荐 | 不推荐,主要为了兼容老设备 | 推荐,现代 SIP 标准方式 |
| Kamailio 常用处理函数 | 一般不主动使用 | loose_route() |
| 复杂度 | 高,容易和 NAT、转发逻辑冲突 | 简单清晰 |
2. 松散路由 Loose Routing
松散路由是现代 SIP 的标准做法。
典型 Record-Route 头类似:
Record-Route: <sip:proxy.example.com;lr>关键点是 URI 里有:
;lrlr代表 loose routing。
松散路由下的请求示例
假设 Kamailio 在初始 INVITE 中插入了:
Record-Route: <sip:kamailio.example.com;lr>后续 BYE、ACK、re-INVITE 等 in-dialog 请求会带上:
Route: <sip:kamailio.example.com;lr>但 Request-URI 仍然是最终目标,例如:
BYE sip:alice@192.168.1.100 SIP/2.0 Route: <sip:kamailio.example.com;lr>也就是说:
Request-URI = 最终目标 Route = 经过哪些代理Kamailio 收到后通常这样处理:
if (has_totag()) { if (loose_route()) { route(RELAY); exit; } }loose_route()会检查 Route 头,如果发现自己在 Route 头里,就消费掉对应 Route,然后继续转发。
3. 严格路由 Strict Routing
严格路由是旧 SIP 规范里的方式。
Record-Route 头里没有;lr:
Record-Route: <sip:proxy.example.com>这种情况下,后续请求的 Request-URI 会被改成下一个代理地址,而真正的目标 URI 会被放到 Route 头的末尾。
严格路由下的请求示例
可能变成:
BYE sip:kamailio.example.com SIP/2.0 Route: <sip:alice@192.168.1.100>也就是说:
Request-URI = 下一个代理 Route = 最终目标或后续路由集合这种方式的问题是:
- Request-URI 不再表示最终目标;
- 代理需要反复改写 Request-URI;
- 和 NAT、注册 Contact、负载均衡逻辑更容易冲突;
- 配置和排错更复杂;
- 现代 SIP 设备基本不推荐这样用。
4. Kamailio 中怎么体现
Kamailio 里通常通过rr模块处理 Record-Route 和 Route。
常见配置:
loadmodule "rr.so" modparam("rr", "enable_full_lr", 0) modparam("rr", "append_fromtag", 1)初始 INVITE 时让 Kamailio 留在信令路径中:
if (is_method("INVITE|SUBSCRIBE")) { record_route(); }后续 in-dialog 请求:
if (has_totag()) { if (loose_route()) { route(RELAY); exit; } if (is_method("ACK")) { exit; } sl_send_reply("404", "Not here"); exit; }这里的重点是:
loose_route()虽然名字叫loose_route(),但 Kamailio 的rr模块在一定程度上也能兼容一些严格路由场景。不过在现代配置中,一般只需要按松散路由设计。
5. Record-Route 里的;lr
这是判断松散路由的核心。
松散路由
Record-Route: <sip:1.2.3.4;lr>或者:
Record-Route: <sip:proxy.example.com;transport=udp;lr>严格路由
Record-Route: <sip:1.2.3.4>没有;lr,就可能被对端当成严格路由。
6. Kamailio 的enable_full_lr
Kamailio 的rr模块有一个参数:
modparam("rr", "enable_full_lr", 0)默认情况下,Record-Route 可能类似:
Record-Route: <sip:proxy.example.com;lr>如果设置为:
modparam("rr", "enable_full_lr", 1)可能生成:
Record-Route: <sip:proxy.example.com;lr=on>这是为了兼容某些有问题的 SIP 设备,它们可能不正确识别没有值的;lr参数。
一般情况下保持默认即可,除非遇到特定终端或运营商兼容性问题。
7. 实际使用建议
在 Kamailio 生产环境中,建议:
- 使用松散路由;
- 使用
record_route()插入带;lr的 Record-Route; - 使用
loose_route()处理后续 in-dialog 请求; - 不要主动生成没有
;lr的 Record-Route; - 严格路由只作为兼容旧设备考虑。
典型配置结构:
request_route { if (is_method("INVITE|SUBSCRIBE")) { record_route(); } if (has_totag()) { if (loose_route()) { route(RELAY); exit; } if (is_method("ACK")) { exit; } sl_send_reply("404", "Not here"); exit; } route(RELAY); } route[RELAY] { if (!t_relay()) { sl_reply_error(); } exit; }8. 一句话总结
严格路由是旧式 SIP 路由方式,会通过改写 Request-URI 控制下一跳;松散路由是现代标准方式,通过Route头和;lr参数控制路由,Request-URI 保持最终目标。Kamailio 生产环境中基本都应该使用record_route()+loose_route()的松散路由模式。