丢包时慢速 DTLS 握手

Slow DTLS handshake in case of packet loss

我使用 openssl 实现了一个 DTLS 服务器。 (我有一个 udp 套接字,我正在使用内存 bio 与 openssl 通信。)但是,如果有数据包丢失,DTLS 握手可能需要 1-2 秒,这对我来说很长。

无丢包时的正常流量:(几毫秒)

Client Hello ------------------------->
             <------------------------- Server Hello
Rest of the handshake ---------------->
                      <---------------- Rest of the handshake

我遇到的流程:(几秒)

Client Hello ------------------------->
             <-------(lost)------------ Server Hello
Client Hello ------------------------->
Client Hello ------------------------->
Client Hello ------------------------->
             <------------------------- Server Hello
Rest of the handshake ---------------->
                      <---------------- Rest of the handshake

即使在本地环境中,我也可以轻松地重现它,直接删除第一个 Server Hello。

我很好奇为什么服务器不响应一些即将到来的客户端问候。 如果它会回答,握手可能会在 1 秒内完成,但这种方式需要 1-2 秒,当它最终再次回答稍后的客户端问候时。

如何使 DTLS 握手更快完成? (例如,通过为所有 Client Hello 启用答案,有办法吗?)

在 OpenSSL 中,默认的 DTLS 重新传输计时器从 1 秒开始,如果未收到响应,则定期加倍。在你的情况下,服务器已经发送了它的 ServerHello(可能还有 ServerHelloDone 和其他消息 - 但你没有显示)并且现在正在等待 ClientKeyExchange 消息。从同一对等方收到的任何后续 ClientHellos 都被假定为陈旧的重传并被忽略。

您可以使用 DTLSv1_listen() 让服务器无状态运行,这意味着服务器将侦听来自任何对等点的连接,并假定任何传入的 ClientHello 来自新对等点并立即响应它 -尽管在这种情况下它会增加一个额外的往返行程,因为所有 ClientHello 都需要在其中包含 "cookie"。它也不会解决握手后期数据包丢失的任何问题 - 所以它对你的问题没有真正的帮助。

从 OpenSSL 1.1.1 开始,您可以使用自定义 DTLS 计时器回调通过 DTLS_set_timer_cb():

控制计时器的超时

https://www.openssl.org/docs/man1.1.1/man3/DTLS_set_timer_cb.html

这是一个将初始计时器设置为 50 毫秒并定期将其加倍的示例(对于基于网络的实现,50 毫秒可能太短了 - 但这个特定示例是 "in memory" 测试):

https://github.com/openssl/openssl/blob/20c98cd45399423f760dbd75d8912769c6b7b10e/test/dtlstest.c#L45-L53

注意不要将超时设置得太低,因为您可能会开始用大量虚假重新传输淹没您的网络。