SIP协议的传输层原理&报文解析(解读rfc3581)(待排版) && opensips
关于rfc3581/rport参数的阐述
一:简述
一般情况下,服务器在接收到request后,应答发向哪里呢?服务器在计算回应应答的算法是一种混合模式,具体说来是这样的:
1,IP:从哪里接收到的就会给哪里,即ip包上记录的源地址
2,port:根据sip报文的头解析出来,
对于处理被NAT包裹的环境中的客户端,请求是可以发出去的,但是应答却无法穿透NAT,就要借助rport这个位于via 类型header的param来处理了
附:目前还借助了“received”这个param in topmost via header,原理是这样的
服务器会将自己实际从哪个ip上接收到的请求记录在“received”,通过他可以有助于让应答穿越NAT。但是没有指定port,那怎么半呢?
二,客户端
可以在其声称的请求中,包含"rport" 这个参数in the top via header,但是不能有值,这个只是表示我支持该扩展属性。
当客户端使用udp将请求发送出去后,他必须在其发送请求的那个ip:port上准备好接收回来的应答,
同时,他还需要在“sent-by”中指定的端口上准备接收。
一旦在客户端和服务器之间有一个NAT存在,那么当请求经过NAT的时候会在nat上创建一条绑定,这个绑定记录还要保有一个超时时间,保证在这个时间内能够接收到服务器端的应答。
大部分的udp NAT,这个超时时间设置的是1min,这个远远超过了non-invite传输的时间。所以,对于non-invite请求,接收到应答是没问题的。
而,INVITE传输则可以是任意的超时时长,所以为了不让nat把我关掉,客户端应该每20s就传一次,这种重传必须一直延续着,尽管接收到临时应答了
三,服务端
服务器(这里的服务器包括proxy 或者 UAS)在接收到请求之后,会检查topmost via的头域,如果头域中包含“rport”参数 with no value,则把该值设置成接收到请求的源端口号
这个原理等同于服务端在向topmost via中insert “reseived”一样。实际上,服务端必须要insert一个包含接收请求的源地值的 “reseived”到via中,即使这个源地值域sent-by是一样的。
注:以上处理域传输层协议无关
当一个服务器想要发送应答的时候,他会检查应答中的topmost via ,如果“sent-protocol”组件指示要使用不可靠传输协议比如udp,那么时不可以有“maddr”这个参数的,但是有 “reseived”和“rport”参数,
这个应答必须发送给“reseived”中列出的所有ip,对应的端口为“rport”中指定的,而这个应答必须经由接收请求的那个ip:port发送出去,这么做是为了穿越对称NAT
当一个server监听在多interface或者多port上时,他需要记住从哪一个上接收到了请求。对于有状态proxy,在传输期间存储这些信息也不是什么难题。然而对于无状态proxy,不会存储请求和应答的关系,所以不能记住从哪里接收到的请求。
所以,为了实现上述的要求,一个无状态的proxy就需要把请求中的目的地址和端口号编码进via 头域中,一旦这个应答来了,他就可以提取这个信息用于指导如何转发应答。
四,例子
1,A client sends an INVITE to a proxy server which looks like, in part: INVITE sip:user@example.com SIP/2.0 Via: SIP/2.0/UDP 10.1.1.1:4540;rport;branch=z9hG4bKkjshdyff
这个INVITE 请求从源地址:10.1.1.1:4540这个客户端发出来的,想要到达的目的地是user@example.com
服务proxy的地址为192.0.2.2即proxy.example.com,他会在5060和5070端口上。
于是,请求会发向proxy的5060端口
这个过程会经过nat,所以ip包上的源地值会变成192.0.2.1,而端口号变成9988
proxy接收请求再转发,但是转发之前要把"rport"参数insert到via中,于是头变成了这样:
INVITE sip:user@example.com SIP/2.0 Via: SIP/2.0/UDP proxy.example.com;branch=z9hG4bKkjsh77 ---因为经过了一个proxy嘛,所以肯定要加一个via Via: SIP/2.0/UDP 10.1.1.1:4540;received=192.0.2.1;rport=9988 ;branch=z9hG4bKkjshdyff ---因为发现接收request的源ip和via中指定的ip不同,所以这里要加上received和rport This request generates a response which arrives at the proxy:
(wxy:针对这个请求,proxy会生成一个应答,是如下这个样子的:) SIP/2.0 200 OK Via: SIP/2.0/UDP proxy.example.com;branch=z9hG4bKkjsh77 Via: SIP/2.0/UDP 10.1.1.1:4540;received=192.0.2.1;rport=9988 ;branch=z9hG4bKkjshdyff
The proxy strips its top Via header field value, and then examines the next one. It contains both a "received" parameter and an "rport" parameter. The server follows the rules specified in Section 4 and sends the response to IP address 192.0.2.1, port 9988, and sends it from port 5060 on 192.0.2.2:
proxy首先找到top via header,然后检查他的下一个,他既包含了received也包含了rport参数
于是这个服务器就会根据之前说的原则,将应答发给192.0.2.1:9988,并且是从192.0.2.2:5060上发出去的
SIP/2.0 200 OK
Via: SIP/2.0/UDP 10.1.1.1:4540;received=192.0.2.1;rport=9988
;branch=z9hG4bKkjshdyff
This packet matches the binding created by the initial request.
Therefore, the NAT rewrites the destination address of this packet
back to 10.1.1.1, and the destination port back to 4540. It forwards
this response to the client, which is listening for the response on
that address and port. The client properly receives the response.
========================================================================================
启动命令:
/usr/local/opensips/sbin/opensipsctl start
日志:
/opt/opensips/logs