要彻底明白三者之间的区别,首先得弄清楚三者的定义是什么?(见rfc3261)
via:
The Via header field indicates the path taken by the request so far and indicates the path that should be followed in routing responses. The branch ID parameter in the Via header field values serves as a transaction identifier, and is used by proxies to detect loops.
Route:
The Route header field is used to force routing for a request through the listed set of proxies. Examples of the use of the Route header field are in Section 16.12.1.
Record-Route:
The Record-Route header field is inserted by proxies in a request to force future requests in the dialog to be routed through the proxy.
简单来说,via的作用就是标记请求所经过的节点,好让其对应的响应能按照via标记好的路径返回回去;Route set(一个请求或应答中可以包含多个Route标记,这些Route标记称为Route Set)的作用是强制请求必须从Route set中设定的节点通过,Route set的生成可以是手动配置也可以是协议自己生成,需要手动配置的情况如,客户端向registar注册的时候,registar的路径就应该是通过手动配置,协议自己生成的情况如在对话生成前各个proxy往请求中添加Record-Route为了让后续的请求能继续通过该proxy发送,当对话建立好后就确定了后续的请求该走的路由,之后将Record-Route中的记录登记在Route set中来强制请求的路由。
Record-Route的是各个想在后续请求对话中还继续接收请求和应答的Proxy,将自己的地址信息添加在Record-Route中来帮助协议生成最终的Route set(也就是为什么说对话中的请求和应答包含的是Route-set,对话外的请求包含的Record-Route的原因,Route set用在对话外的情况就是在注册时客户端最初不清楚该往哪里去注册,需要手动配置,总之一句话,Route set的作用就是强制让请求通过其包含的路由列表传输请求和应答)。
SIP路由机制解析(zz)
在前面已经陆续介绍了SIP重要头域、注册流程、会话流程等SIP相关知识,现在再来介绍一下SIP中的路由机制。
总的来说,SIP中存在两种路由场景:
1,请求消息的路由
2,响应消息的路由
其中,响应消息的路由非常简单,就是完全依靠Via来完成的,具体请见我关于RFC3261中会话流程的分析。
下面我们只谈SIP请求消息的路由。
首先我们要搞清楚什么是严格路由和松散路由。
严格路由(Strict Routing):
可以理解为比较“死板”的理由机制,这种路由机制在SIP协议的前身RFC 2534中定义,其机制非常简单。
要求接收到的消息的request-URI必须是自己的URI,然后它会把第一个Route头域“弹”出来,并把其中的URI作为新的request-RUI,然后把该消息路由给该URI。
松散路由(Louse Routing,lr):
该路由机制较为灵活,也是SIP路由机制的灵魂所在,在SIP根本大典RFC 3261中定义。
下面介绍一下一个松散路由的Proxy的路由决策过程:
1,Proxy首先会检查消息的request-URI是不是自己属于自己所负责的域。如果是,它就会通过定位服务将该地址“翻译”成具体的联系地址并以此替换掉原来的request-URI;否则,它不会动request-URI。
2,Proxy检查第一个Route头域中的URI是不是自己的,如果是,则移除之。
3,前面两项都是准备工作,下面该进行真正的路由了。如果还有Route头域,则Proxy会把消息路由给该头域中的URI,否则就路由给request-URI。至于如何从下一跳URI确定出IP地址,端口以及传输协议那是另外一回事了。
对于前面的3条规则,我们可以简单总结为一句话:Route的优先级高于request-URI的。
好,了解了两种路由机制,我们再来了解一下Route和Record-Route。
如果说Via是为了给一个请求消息的响应消息留后路,那么Record-Route就是为了给该请求消息之后的请求消息留后路。
【说明】一个SIP消息每经过一个Proxy(包括主叫),都会被加上一个Via头域,当消息到达被叫后,Via头域就记录了请求消息经过的完整路径。被叫将这些Via头域原样copy到响应消息中(包括各Via的参数,以及各Via的顺序),然后下发给第一个Via中的URI,每个Proxy转发响应消息前都会把第一个Via(也就是它自己添加的Via)删除,然后将消息转发给新的第一个Via中的URI,直到消息到达主叫。
而在一个请求消息的传输过程中,Proxy也可能(纯粹自愿,如果它希望还能接收到本次会话的后续请求消息的话)会添加一个Record-Route头域,这样当消息到达被叫后里面就有会有0个或若干个Record-Route头域。被叫会将这些Record-Route头域并入路由集,并并入自己的路由集,随后被叫在发送请求消息时就会使用该路由集构造一系列Route头域,以便对消息进行路由。
然后,被叫会像上面对待Via头域一样,将Record-Route头域全部原样copy到响应消息中返回给主叫。
主叫收到响应消息后也会将这些Record-Route头域并入路由集,只是它会将其反序。该会话中的后续请求消息的Route头域就会通过路由集构造。
【注意】Record-Route头域不用来路由,而只是起到传递信息的作用。
Record-Route头域不是路由集的唯一来源,路由集还可以通过手工配置等方式得到。
只是描述还是比较抽象,下面就以RFC 3261中的两个实例来解释一下。
路由示例1:
场景:
两个UE间有两个Proxy,U1 -> P1 -> P2 -> U2,并且两个Proxy都乐意添加Record-Route头域。
消息流:
【说明】由于我们在此只关心SIP路由机制,因此下面消息中跟路由机制无关的头域都省略了。
U1发出一个INVITE请求给P1(P1是U1的外拨代理服务器):
INVITE sip:[email protected] SIP/2.0
Contact: sip:[email protected]
P1不负责域domain.com,消息中也没有Route头域,因此通过DNS查询得到负责该域的Proxy的地址并且把消息转发过去。这里P1在转发前就添加了一个Record-Route头域,里面有一个lr参数,说明P1是一个松散路由器,遵循RFC3261中的路由机制。
INVITE sip:[email protected] SIP/2.0
Contact: sip:[email protected]
Record-Route: sip:p1.example.com;lr
P2负责域domain.com,因此它通过定位服务得到[email protected] 对应的设备地址是[email protected] ,因此用新的URI重写request-URI。消息中没有Route头域,因此它就把该消息转发给request-URI中的URI,转发前它也增加了一个Record-Route头域,并且也有lr参数。
INVITE sip:[email protected] SIP/2.0
Contact: sip:[email protected]
Record-Route: sip:p2.domain.com;lr
Record-Route: sip:p1.example.com;lr
位于u2.domain.com的被叫收到了该INVITE消息,并且返回一个200 OK响应。其中就包括了INVITE中的Record-Route头域。
SIP/2.0 200 OK
Contact: sip:[email protected]
Record-Route: sip:p2.domain.com;lr
Record-Route: sip:p1.example.com;lr
被叫此时也就有了自己的路由集:
(sip:p2.domain.com;lr,sip:p1.example.com;lr)
并且它本次会话的远端目的地址设置为INVITE中Contact中的URI:[email protected],此后被叫在该会话中的请求消息就发到这个URI。同样,被叫在200 OK响应中也携带了自己的联系地址,主叫收到该响应消息后也会把本次会话的远端目的地址设置为:[email protected],此后主机在该会话中的请求消息就发到这个URI。
同样,主叫也有了自己的路由集,只是跟被叫的是反序的:
(sip:p1.example.com;lr,sip:p2.domain.com;lr)
通话完毕后,我们架设主叫先挂机,则主叫发出BYE请求:
BYE sip:[email protected] SIP/2.0
Route: sip:p1.example.com;lr,sip:p2.domain.com;lr
可以看到,BYE的Route头域正是主机的路由集构造来的。
由于p1在第一个Route中,因此BYE首先发给P1。
P1收到该消息后,发现request-URI中的URI不属于自己负责的域,而消息有Route头域,并且第一个Route头域中的URI正是自己,因此删除之,并且把消息转发给新的第一个Route头域中的URI,也就是P2:
BYE sip:[email protected] SIP/2.0
Route: sip:p2.domain.com;lr
P2收到该消息后,发现request-URI中的URI不属于自己负责的域(P2负责的是domain.com,而不是u2.domain.com),第一个Route头域中的URI正是自己,因此删除之,此时已经没有Route头域了,因此就转发给了request-URI中的URI。
被叫就会收到BYE消息:
BYE sip:[email protected] SIP/2.0
路由示例2:
如果说上面的示例主要关注的是路由流程,那么本示例关注的则是严格路由与松散路由的区别。
场景:
U1->P1->P2->P3->P4->U2
其中,P3是严格路由的,其余Proxy都是松散路由的,并且4个Proxy都很乐意增加Record-Route头域。
消息流:
我们直接给出了到达被叫的INVITE消息:
INVITE sip:[email protected] SIP/2.0
Contact: sip:[email protected]
Record-Route: sip:p4.domain.com;lr
Record-Route: sip:p3.middle.com
Record-Route: sip:p2.example.com;lr
Record-Route: sip:p1.example.com;lr
这中间的其他消息我们就不过问了,直接看一下被叫最后发出的BYE消息大概是什么样子:
BYE sip:[email protected] SIP/2.0
Route: sip:p4.domain.com;lr
Route: sip:p3.middle.com
Route: sip:p2.example.com;lr
Route: sip:p1.example.com;lr
因为P4在第一个Route里,因此被叫将BYE消息发给了P4。
P4收到该消息后,发现自己不负责域u1.example.com,但是第一个Route头域中的URI正是自己,因此删除之。P4还发现新的第一个Route头域中的URI是一个严格路由器,因此它把request-URI中的URI添加到最后一个Route的位置,并且将第一个Route“弹出”并且覆盖原来的request-URI。然后将消息转发给当前的request-URI,也就是P3。
BYE sip:p3.middle.com SIP/2.0
Route: sip:p2.example.com;lr
Route: sip:p1.example.com;lr
Route: sip:[email protected]
P3收到该消息后,直接把消息作出如下变换并且发给P2:
BYE sip:p2.example.com;lr SIP/2.0
Route: sip:p1.example.com;lr
Route: sip:[email protected]
P2收到该消息后,发现消息中的request-URI是自己的,因此在进一步处理先首先对消息做如下变换:
BYE sip:[email protected] SIP/2.0
Route: sip:p1.example.com;lr
然后,P2发现自己不负责域u1.example.com,第一个Route中的URI也不是自己的,因此将消息转发给该URI,也就是P1。
P1收到该消息后,发现自己不负责域u1.example.com,但是第一个Route头域中的URI正是自己,因此删除之。消息变成下面的样子:
BYE sip:[email protected] SIP/2.0
既然Route头域已经是空,因此P1把消息发给u1.example.com。
我们经常可以看到在Router字段中设置的SIP URI经常有一个lr的属性,例如sip:a.b.c.d;lr,这就是表示这个地址所在的Proxy Server是一个Loose Router,如果没有lr属性,它就是一个Strict Router。
Loose Router实际上表示Proxy Server根据的是RFC3261处理Route字段的规则,而Strict Router表示Proxy Server根据的是RFC2357处理Route字段的规则。Strict Router要求SIP消息的Request URI为其自身的地址。
1、Loose Router和Strict Router首先都会检查Router字段的第一个地址是否为自己,如果是则从Router字段中删除自己。
2、Strict Router在发往下一跳时将使用Router字段中的下一跳地址更新Request URI。
3、Loose Router首先会检查Request URI是否为自己:如果不是,则不作处理;如果是,则取出Route字段的最后一个地址作为Request URI地址,并从Route字段中删去最后一个地址。
4、Loose Router其次会检查下一跳是否为Strict Router:如果不是,则不作处理;如果是,则将Request URI添加为Route的最后一个字段,并用下一跳Strict Router的地址更新Request URI。
可以看到步骤3、4其实是Loose Router为了兼容Strict Router而做的额外工作。
IMS中的说明:
需要指出一点,标题“IMS中松散路由(Loose Router)和严格路由(Strict Router)”建议改为“SIP中对松散路由(Loose Router)和严格路由(Strict Router)的处理 ”。楼主所说的是RFC3261中的内容。该RFC定义了SIP基本协议,而不是IMS的协议。
而对IMS而言,3GPP要求该系统中都是松散路由。详见3GPP TS24.229 v730第4.3节。当然,对那些要与非IMS网络设备打交道的IMS网元,如I-CSCF、IBCF、S-CSCF、E-CSCF,也要提供对松散路由的支持。
对RFC 3665上的3.2的例子的个人理解:
1>sip中二个端点的应用route的形成是通过proxy在处理的过程中,通过Record-Route记录来形成的,并且在200Ok后,Alice 进行ACk的时候,已经将Record-Route的记录转化到route域了。
2>Bob在第一个invite到达的时候,形成了route;Alice Alice在从Bob返回的第一个请求中学习到了路由,这里是180
3>Via字段主要用来Proxy Server使用,从RFC 3261中可以看到,主要是为了Proxy标识事务ID,还有其他的一些东西