一、请求路由
1、参考IP中的概念:严格路由是must,松弛路由是preferred.
严格路由:
实际指发送者指明了必须经过的路由,如果下一跳路由找不到就返回错误;
松弛路由:
只是指出一个routelist,但并不要求消息必须严格经过任意两个相邻路由记录,可以经过其他路由器后再到下一跳指定地点。
Eg:如路由指定A-B
严格路由要求到了A后下一跳必须是B
松弛路由要求到了A后可以先到C再到B,只要B即可。
2、SIP中的路由:严格路由和松散路由
我们经常可以看到在Router字段中设置的SIP URI经常有一个lr的属性,例如
严格路由(Strict Routing):可以理解为比较“死板”的理由机制,这种路由机制在SIP协议的前身RFC 2534中定义,其机制非常简单。
处理步骤:
S_1、接收到的消息的request-URI必须是自己的URI
S_2、把第一个Route头域“弹”出来,并把其中的URI作为新的request-RUI.
S_3、然后把该消息路由到新的request-URI。
松散路由(Loose Routing,lr):该路由机制较为灵活,也是SIP路由机制的灵魂所在,在SIP根本大典RFC 3261中定义。
处理步骤:
L_1、Proxy首先会检查消息的request-URI是不是自己属于自己所负责的域。如果是,它就会通过定位服务将该地址“翻译”成具体的联系地址
并以此替换掉原来的request-URI;否则,它不会动request-URI。
L_2、Proxy检查路由表中的第一个地址是否为自己,如果是则从表中删除。
L_3、Loose Router首先会检查Request URI是否为自己插入到路由表中的地址, 如果不是,则不作处理;如果是,则取出Route字段的最后
一个地址作为Request URI地址,并从Route字段中删去最后一个地址。
L_4、Loose Router检查下一跳是否为Strict Router。如果不是,不处理;否则,将Request URI插入到路由表表尾,并用下一跳地址
(Strict Router的地址)更新Request URI。
L_5、如果路由表为空,则路由给Request URI(如果路由表非空,且request-uri不是自身,那么应该路由到最上面的路由表去???)。
总结:Route的优先级高于request-URI的
可以看到步骤L_3、L_4其实是Loose Router为了兼容Strict Router而做的额外工作.
(如:步骤L_4中如果下一跳为Strict Router,那么必须将下一跳的Strict Router地址更新request-URI,这样这样下一条在受到消息时,检查request-URI时才会和自己匹配,不至于发生错误,因为StrictRouter要求request-URI必须是自己的URI)。
3、SIP中Route和Record-Route头域。
SIP中的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头域并入路由集,并并入自己的路由集,随后被叫在发送请求(和Record-Route属于同一个dialog里面)消息时就会使用该路由集构造一系列Route头域,以便对消息进行路由。
然后,被叫会像上面对待Via头域一样,将Record-Route头域全部原样copy到响应消息中返回给主叫。
主叫收到响应消息后也会将这些Record-Route头域并入路由集,只是它会将其反序。该会话中的后续请求(和Record-Route属于同一个dialog里面)消息的Route头域就会通过路由集构造。
【注意】
Record-Route头域不用来路由,而只是起到传递信息的作用
Record-Route头域不是路由集的唯一来源,路由集还可以通过手工配置等方式得到。
二:应答消息路由
SIP应答消息的路由机制,相对请求来说,比较简单,基本思想就是,请求从哪里来,应答回哪里去。那是如何实现的呢?很简单,Via头域就是完成这个差事的。Via头域表说明了SIP请求实际的路由过程,用于应答消息的回程路由。
三:RFC3261例子解析:
下面就以RFC 3261中的两个实例解释,包括每个步骤的解析。
路由示例1:
场景:
两个UE间有两个Proxy,U1 -> P1 -> P2 -> U2,并且两个Proxy都乐意添加Record-Route头域。
消息流:
【说明】由于我们在此只关心SIP路由机制,因此下面消息中跟路由机制无关的头域都省略了。
U1发出一个INVITE请求给P1(P1是U1的外拨代理服务器):
INVITEsip:[email protected] SIP/2.0
Contact: sip:[email protected]
P1不负责域domain.com,消息中也没有Route头域,因此通过DNS查询得到负责该域的Proxy的地址并且把消息转发过去。这里P1在转发前就添加了一个Record-Route头域,里面有一个lr参数,说明P1是一个松散路由器,遵循RFC3261中的路由机制。
INVITEsip:[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参数。
INVITEsip:[email protected] SIP/2.0
Contact: sip:[email protected]
Record-Route:
Record-Route:
位于u2.domain.com的被叫收到了该INVITE消息,并且返回一个200 OK响应。其中就包括了INVITE中的Record-Route头域。
SIP/2.0 200 OK
Contact: sip:[email protected]
Record-Route:
Record-Route: sip:p1.example.com;lr
被叫此时也就有了自己的路由集:
(
并且它本次会话的远端目的地址设置为INVITE中Contact中的URI:
此后被叫在该会话中的请求消息就发到这个URI。同样,被叫在200OK响应中也携带了自己的联系地址,主叫收到该响应消息后也会把本次会话的远端目的地址设置为:
此后主机在该会话中的请求消息就发到这个URI。
同样,主叫也有了自己的路由集,只是跟被叫的是反序的:
(
通话完毕后,我们假设主叫先挂机,则主叫发出BYE请求:
BYEsip:[email protected] SIP/2.0
Route:
可以看到,BYE的Route头域正是主机的路由集构造来的。
由于p1在第一个Route中,因此BYE首先发给P1。
P1收到该消息后,发现request-URI中的URI不属于自己负责的域,而消息有Route头域,并且第一个Route头域中的URI正是自己,因此删除之(步骤L_2),并且把消息转发给新的第一个Route头域中的URI,也就是P2:
BYEsip:[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(步骤L_5)。
被叫就会收到BYE消息:
BYEsip:[email protected] SIP/2.0
路由示例2:
如果说上面的示例主要关注的是路由流程,那么本示例关注的则是严格路由与松散路由的区别。
场景:
U1->P1->P2->P3->P4->U2
其中,P3是严格路由的,其余Proxy都是松散路由的,并且4个Proxy都很乐意增加Record-Route头域。
消息流:
我们直接给出了到达被叫的INVITE消息:
INVITEsip:[email protected] SIP/2.0
Contact: sip:[email protected]
Record-Route:
Record-Route:
Record-Route:
这中间的其他消息我们就不过问了,直接看一下被叫最后发出的BYE消息大概是什么样子:
BYEsip:[email protected] SIP/2.0
Route:
Route:
Route:
Route: sip:p1.example.com;lr
因为P4在第一个Route里,因此被叫将BYE消息发给了P4(route 优先级大于request-uri)。
P4收到该消息后,发现自己不负责域u1.example.com,但是第一个Route头域中的URI正是自己,因此删除之(步骤L_2)。
P4还发现新的第一个Route头域中的URI是一个严格路由器,因此它把request-URI中的URI添加到最后一个Route的位置,并且将第一个Route“弹出”并且覆盖原来的request-URI(步骤_4)。
然后将消息转发给当前的request-URI,也就是P3。
BYE sip:p3.middle.com SIP/2.0
Route:
Route:
Route: sip:[email protected]
P3收到该消息后,直接把消息作出如下变换并且发给P2:(步骤S_1 S_2 S_3严格路由操作,因为P3是严格路由)
BYE sip:p2.example.com;lr SIP/2.0
Route:
Route: sip:[email protected]
P2收到该消息后,发现消息中的request-URI是自己的,因此在进一步处理先首先对消息做如下变换(步骤L_3):
BYEsip:[email protected] SIP/2.0
Route: sip:p1.example.com;lr
然后,P2发现自己不负责域u1.example.com,第一个Route中的URI也不是自己的,因此将消息转发给该Route去,也就是P1。
P1收到该消息后,发现自己不负责域u1.example.com,但是第一个Route头域中的URI正是自己,因此删除之。消息变成下面的样子:
BYEsip:[email protected] SIP/2.0
既然Route头域已经是空,因此P1把消息发给u1.example.com。
四:备注:
Loose Routing为了实现对Strict Routing的兼容,做了很多额外的工作.
注:本文在网上收集资料以及RFC3261的进程进行整理和修改,转载请标明出处.