客户端发送绑定请求后,对端agent必须准备好接收绑定请求,这个绑定请求是发送到了每个候选地址的基准地址。绑定请求包含在自己最近的offer或者answer消息中。因此,即使agent是一个轻量级部署场景的agent,它也要求部署方案维持一个状态来处理服务器端的流程。
接收绑定请求时,agent必须使用短期安全机制对请求进行认证和消息完整性检查。Agent必须考虑用户名称的有效性,如果用户名称含有两个用户名称构成的(通过冒号:分割),其中第一个用户名称等于一个用户名称,这个用户在此会话中,由agent在offer或者answer生成的名称。关于名称构成的讨论,读者可以查阅上一篇文章。有时也存在其他的可能性,例如,提供方offerer在收到对端peer的answer之前一个收到一个绑定请求。如果这样的情况发生,agent必须马上生成一个响应消息,响应消息中包含映射地址的计算数据。在这一时间点,agent已经具备了足够的信息生成响应消息。因此,agent就不需要对端peer的密码。一旦agent收到answer以后,这个answer消息就会马上通过几个步骤进行处理,包括检查修复角色冲突的流程,计算映射地址的流程,通过学习获得peer反射候选地址,Triggered Checks和更新推荐标识。提醒读者,以上这些流程都是针对全部署场景agent来说的。还有一些场景中,在收到answer之前,收到了多个STTUN请求,这样就会导致在triggered check 队列中生成多个配对队列。
Agent一定不能使用ALTERNATE-SERVER机制,也一定不能支持RFC3489规范的向后兼容机制,它必须使用FINGERPRINT机制。
如果agent在媒体数据中(例如在QoS中)使用了区分服务-Diffserv(遵从RFC2475),agent也应该使用同样的标识方式在绑定请求的响应中。因为区分服务不在我们讨论的范围,具体关于Diffserv,读者查阅RFC2475,这里不做进一步讨论。以下几个步骤的讨论将仅涉及全部署场景的agent处理流程。
1、检测修复角色冲突
正常情况下,通过agent的主控/被控方选择,双方可以选择自己的成功的角色。但是,在一些非正常的环境中,例如使用了第三方的呼叫控制,双方都成为同样的角色。下面,笔者将讨论如何检查同一角色冲突问题,以及如何修复同一角色冲突问题。
Agent必须检查绑定请求,其属性可能是ICE- CONTROLLING或ICE-CONTROLLED 属性。如果要检查以上两种属性,agent需要按照以下流程进行:
- 如果在请求中,其属性既不是ICE-CONTROLLING,也不是ICE-CONTROLLED属性的话,说明agent没有遵从RFC5245规范,这里有角色冲突的问题,但是agent目前执行不了检测。
- 如果agent是一个被控方角色,并且请求中出现了ICE-CONTROLLING属性,接下来根据具体属性内容大小进行判断:
- 如果agent的tie-breaker大于或等于ICE-CONTROLLING的内容,此agent生成一个绑定错误响应,错误响应中包含一个ERROR-CODE属性(487-角色冲突),这里,agent角色仍然保持不变。
- 如果agent的tie-breaker小于ICE-CONTROLLING的内容,agent切换到主控方agent角色。
- 如果agent是一个主控方agent角色,并且请求中出现了ICE-CONTROLLED属性,接下来根据具体属性内容进行判断:
- 如果agent的tie-breaker大于或等于ICE-CONTROLLED的内容,agent切换到被控方角色。
- 如果agent的tie-breaker小于ICE-CONTROLLED的内容, 此agent生成一个绑定错误响应,错误响应中包含一个ERROR-CODE属性(487-角色冲突),这里,agent角色仍然保持不变。
- 如果agent是主控方角色agent,并且ICE-CONTROLLING属性出现在了请求中,或者agent是被控方角色agent,并且ICE-CONTROLLED属性出现在了请求中,双方角色无冲突。
修改了agent角色以后,需要对角色的状态进行修复。因为各自角色的优先级是主控方和被控方的主要执行功能,因此agent修改属性不是简单切换角色就完成了工作流程。修改角色需要agent重新计算配对优先级。这个计算方式我们在前面的历史文章中有非常详细地介绍,读者可查阅此文章。agent角色切换或者修改同样也会直接影响agent其他后续的功能执行,例如,它是否负责选择推荐配对,是否基于ICE结果生成更新offer的消息。
假设STUN服务器端对绑定请求生成了成功的响应的话,甚至于agent角色也发生了变化,agent可以继续执行其余服务器端的步骤:计算映射地址,通过学习获得peer反射候选地址等流程。
2、计算映射地址
对于在转发候选地址收到的请求来说,被STUN流程(即XOR-MAPPED-ADDRESS属性生成)使用的源传输地址是一个传输地址,这个传输地址是被STUN认为的传输地址。读者在计算映射地址是一定要注意,这里涉及了两种绑定请求的传输方式(Data Indication和ChannelData)。如果绑定请求通过Data Indication传输的话,这个源传输地址会出现在Data Indication消息的XOR-MAPPED-ADDRESS属性中。如果绑定请求通过ChannelData传输的话,这个源传输地址是绑定此channel 通道的地址。关于Data Indication消息和ChannelData的定义细节,读者查阅以下两个规范草案:
- Data Indication:https://tools.ietf.org/id/draft-rosenberg-midcom-turn-08.txt
- ChannelData:https://tools.ietf.org/html/rfc5766-10
3、学习Peer Reflexive Candidates
如果请求中的源传输地址不能匹配任何现存远端候选地址的话,这说明对端的是一个新的peer反射候选地址。这个候选地址需要通过以下方式进行构建:
- 此候选地址的优先级设置为请求中获得的PRIORITY 属性值。
- 候选地址类型设置为peer reflexive。
- 候选地址的foundation设置为一个任意值,这个foundation必须区别于所有其他远端候选地址的foundation。如果在SDP的后续的offer/answer交互中包含了peer reflexive候选地址,这将表示对此候选地址来说,这是一个真实的foundation。
- 此候选地址的component ID设置为一个component ID,这个ID是为本地候选地址到远端地址(发送请求的地址)。
- 构建候选地址完成后,这个候选地址将被添加到远端候选地址列表中。但是,agent还不会使用本地候选地址和这个远端候选地址进行配对处理。在triggered check流程会进行本地候选地址和其配对的处理。
4、Triggered Checks处理
下一步,agent将会构建配对,在此配对中,本地候选地址等同于STUN请求接收的传输地址,远端候选地址等同于源传输地址(这是请求发出的地址),这个地址可能是通过学习获得的一个peer reflexive 远端候选地址。本地候选地址将是主机候选地址(这种情况下,请求可能不是通过转发候选地址获得),或者本地候选地址是一个转发候选地址(这种情况下,请求通过转发地址获得)。此本地候选地址从来不会是服务器端反射地址。因为这两个候选地址对agent来说都是可知的,所以,agent可以获得它们的配对,然后计算其候选配对优先级。接下来,这个配对会查询检查列表。通过查询检查列表可能获得其中一个结果:
- 如果配对已在检查列表中,继续执行以下四种检查:
- 如果配对在等待或封冻状态,如果检查没有出现在triggered check队列中,把此配对的检查流程加入到triggered check队列中。
- 如果配对在In-Progress 状态,agent将会取消这个事务处理。这里取消的意思是agent将不在重传请求,将不会认为缺乏响应是失败的,但是会在事务超时时间内对此响应执行等待流程。另外,通过对agent进行队列排序,此agent将会被加入到triggered check队列中,因此会让agent创建一个新的连接检查,然后,agent必须对此配对创建一个新的连接检查(重新作为一个新的STUN绑定请求事务)。最后,这个配对的状态切换为等待状态。
- 如果配对状态是失败状态,此状态必须切换为等待状态,并且通过把agent加入triggered check队列,由此触发agent创建一个新的连接检查。接下来,agent必须为这一对配对创建一个新的检查连接(重新作为一个新的STUN绑定请求事务)。
- 如果配对状态是成功状态,agent则无需执行进一步处理步骤。
- 当双方agent在NAT背后时,通过以上步骤处理来支持ICE的快速处理。
如果配对不在检查列表时,需要进行执行以下流程:
- 基于配对优先级,配对会插入到检查列表中
- 配对状态设置为等待状态
- 配对会加入到triggered check队列中
加入到triggered check队列以后,triggered check队列将会被发送出去。当triggered check队列发送以后,其构建和处理的流程按照前面讨论的发送请求来处理。具体关于发送请求的详情,读者可参与笔者上一篇文章,或者查阅RFC5245-7.1.2,这里不再重复。这些流程要求agent获得一个传输地址,部分用户名(username fragment),对端peer密码。这里读者还要再次注意用户名称和密码的设置。远端候选地址的用户名称(username fragment)等于收到的绑定请求中,冒号后面的USERNAME名称,agent使用此用户名称检查从peer收到的SIP消息(可能从多个分叉中检查),然后找到此用户名称(username fragment),然后通过此用户名称选择相应的密码。关于用户名称和密码构成的处理方式,读者可以参考上一篇文章中的介绍。
5、更新推荐Flag标识
- 如果必要,配对的推荐方式标识也是需要更新的。如果agent收到了一个绑定请求,绑定请求中已有一个USE-CANDIDATE设置属性,并且这个agent是一个主控方agent,agent将会查看配对状态(根据上一个讨论中的计算方式),并且继续进行判断处理:
- 如果这个配对状态是成功状态,这表示由这对配对生成的检查流程生成了一个成功的响应。这会引起agent的一个更新处理。当成功响应收到以后,agent会构建一对有效配对。关于构建有效配对的流程,读者可以参考笔者的客户端流程处理的文章。构建配对后,agent会将配对的推荐标识设置为true值。这样的设置方式可能会结束此媒体流的ICE处理流程。
- 如果配对状态是In-Progress状态的话,如果agent的检查流程生成了成功的结果,当响应抵达时,随之生成的有效配对已确定了的推荐标识设置。当响应抵达时,这样的设置方式可能会结束此媒体流的ICE处理流程。关于ICE结束处理流程的讨论,笔者将在下一篇文章做做详细说明。
6、针对Lite部署的额外流程处理讨论
本文章中以上讨论的都是基于全场景部署的agent的讨论。轻量级的部署场景agent的处理相对比较简单,使用的场景也不是非常普遍,因此没有太多的约定条件(没有优先级,foundation等计算设置,没有队列检查等流程)。这里,笔者针对轻量级部署场景的agent做一些简单说明,希望读者引起注意。如果收到的check消息中包含了USE-CANDIDATE设置属性,agent将需要构建一个候选配对。其中候选配对中,本地候选地址等于传输地址(收到请求的地址),远端候选地址等于一个源传输地址(注意,这个地址是收到请求的源传输地址)。构建成的候选配对设定一个任意优先级,然后推送到有效候选列表中(valid list)。Agent然后设置这对候选配对的推荐标识为true。针对媒体流的每个构件模块,如果这个有效候选列表包含了一对候选配对,媒体流的ICE处理流程将被视作完成状态。
到此为止,结合上一篇文章中关于STUN客户端处理流程介绍,笔者已经完成了关于ICE连接检查的讨论(客户端处理流程和本章节的服务器端处理流程)。在接下来的章节中,笔者将重点讨论关于ICE结束处理的流程。
参考资料:
https://tools.ietf.org/html/rfc5766
https://www.rfc-editor.org/rfc/rfc8445
![](http://www.ctiforum.com/uploadfile/2019/1224/20191224014316600.jpg)
关注微信公众号:asterisk-cn,获得有价值的Asterisk行业分享
Asterisk freepbx FreeSBC技术文档: www.freepbx.org.cn
融合通信/IPPBX商业解决方案:www.hiastar.com
如何使用FreeSBC,qq技术分享群:334023047, www.freesbc.cn