From 0ac247fd88c8238ab780f6937e622c1def05e6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?= Date: Tue, 30 Sep 2014 22:21:31 +0200 Subject: [PATCH] Implement timeout back-off (fixed range for now) --- include/polarssl/ssl.h | 8 +++++ library/ssl_tls.c | 76 ++++++++++++++++++++++++++++++++---------- 2 files changed, 67 insertions(+), 17 deletions(-) diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index 202a2004e..60f32d778 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h @@ -261,6 +261,13 @@ #define SSL_RETRANS_WAITING 2 #define SSL_RETRANS_FINISHED 3 +/* + * Default range for DTLS retransmission timer value, in milliseconds. + * RFC 6347 4.2.4.1 says from 1 second to 60 seconds. + */ +#define SSL_DTLS_TIMEOUT_DFL_MIN 1000 +#define SSL_DTLS_TIMEOUT_DFL_MAX 60000 + /** * \name SECTION: Module settings * @@ -652,6 +659,7 @@ struct _ssl_handshake_params unsigned char *hs_msg; /*!< Reassembled handshake message */ + uint32_t retransmit_timeout; /*!< Current value of timeout */ unsigned char retransmit_state; /*!< Retransmission state */ ssl_flight_item *flight; /*!< Current outgoing flight */ ssl_flight_item *cur_msg; /*!< Current message in flight */ diff --git a/library/ssl_tls.c b/library/ssl_tls.c index f6626c995..308d87714 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c @@ -88,7 +88,7 @@ static inline size_t ssl_ep_len( const ssl_context *ssl ) * Passing millisecs = 0 cancels a running timer. * The timer is already running iff time_limit != 0. */ -void ssl_set_timer( ssl_context *ssl, unsigned long millisecs ) +void ssl_set_timer( ssl_context *ssl, uint32_t millisecs ) { ssl->time_limit = millisecs; get_timer( &ssl->time_info, 1 ); @@ -109,6 +109,40 @@ int ssl_check_timer( ssl_context *ssl ) } #endif +/* + * Double the retransmit timeout value, within the allowed range, + * returning -1 if the maximum value has already been reached. + */ +static int ssl_double_retransmit_timeout( ssl_context *ssl ) +{ + uint32_t new_timeout; + + if( ssl->handshake->retransmit_timeout >= SSL_DTLS_TIMEOUT_DFL_MAX ) + return( -1 ); + + new_timeout = 2 * ssl->handshake->retransmit_timeout; + + /* Avoid arithmetic overflow and range overflow */ + if( new_timeout < ssl->handshake->retransmit_timeout || + new_timeout > SSL_DTLS_TIMEOUT_DFL_MAX ) + { + new_timeout = SSL_DTLS_TIMEOUT_DFL_MAX; + } + + ssl->handshake->retransmit_timeout = new_timeout; + SSL_DEBUG_MSG( 3, ( "update timeout value to %d millisecs", + ssl->handshake->retransmit_timeout ) ); + + return( 0 ); +} + +static void ssl_reset_retransmit_timeout( ssl_context *ssl ) +{ + ssl->handshake->retransmit_timeout = SSL_DTLS_TIMEOUT_DFL_MIN; + SSL_DEBUG_MSG( 3, ( "update timeout value to %d millisecs", + ssl->handshake->retransmit_timeout ) ); +} + #if defined(POLARSSL_SSL_MAX_FRAGMENT_LENGTH) /* * Convert max_fragment_length codes to length. @@ -1951,12 +1985,12 @@ int ssl_fetch_input( ssl_context *ssl, size_t nb_want ) return( POLARSSL_ERR_SSL_INTERNAL_ERROR ); } - // TODO-DTLS: for now, use constant timeout = 1 sec/datagram len = SSL_BUFFER_LEN - ( ssl->in_hdr - ssl->in_buf ); if( ssl->f_recv_timeout != NULL && - ssl->handshake != NULL ) /* No resend outside handshake */ + ssl->handshake != NULL ) /* No timeout outside handshake */ { - ret = ssl->f_recv_timeout( ssl->p_bio, ssl->in_hdr, len, 1 ); + ret = ssl->f_recv_timeout( ssl->p_bio, ssl->in_hdr, len, + ssl->handshake->retransmit_timeout / 1000 ); } else ret = ssl->f_recv( ssl->p_bio, ssl->in_hdr, len ); @@ -1972,6 +2006,12 @@ int ssl_fetch_input( ssl_context *ssl, size_t nb_want ) { SSL_DEBUG_MSG( 2, ( "recv timeout" ) ); + if( ssl_double_retransmit_timeout( ssl ) != 0 ) + { + SSL_DEBUG_MSG( 1, ( "handshake timeout" ) ); + return( POLARSSL_ERR_NET_TIMEOUT ); + } + if( ( ret = ssl_resend( ssl ) ) != 0 ) { SSL_DEBUG_RET( 1, "ssl_resend", ret ); @@ -2247,8 +2287,7 @@ int ssl_resend( ssl_context *ssl ) else ssl->handshake->retransmit_state = SSL_RETRANS_WAITING; - /* WIP: hardcoded 1 sec will be replaced */ - ssl_set_timer( ssl, 1000 ); + ssl_set_timer( ssl, ssl->handshake->retransmit_timeout ); SSL_DEBUG_MSG( 2, ( "<= ssl_resend" ) ); @@ -2268,8 +2307,9 @@ void ssl_recv_flight_completed( ssl_context *ssl ) /* The next incoming flight will start with this msg_seq */ ssl->handshake->in_flight_start_seq = ssl->handshake->in_msg_seq; - /* Cancel timer */ + /* Cancel timer and reset timeout value */ ssl_set_timer( ssl, 0 ); + ssl_reset_retransmit_timeout( ssl ); if( ssl->in_msgtype == SSL_MSG_HANDSHAKE && ssl->in_msg[0] == SSL_HS_FINISHED ) @@ -2285,8 +2325,7 @@ void ssl_recv_flight_completed( ssl_context *ssl ) */ void ssl_send_flight_completed( ssl_context *ssl ) { - /* WIP: hardcoded 1 sec is temporary */ - ssl_set_timer( ssl, 1000 ); + ssl_set_timer( ssl, ssl->handshake->retransmit_timeout ); if( ssl->in_msgtype == SSL_MSG_HANDSHAKE && ssl->in_msg[0] == SSL_HS_FINISHED ) @@ -4489,16 +4528,19 @@ static int ssl_handshake_init( ssl_context *ssl ) ssl->handshake->key_cert = ssl->key_cert; #endif + /* + * We may not know yet if we're using DTLS, + * so always initiliase DTLS-specific fields. + */ #if defined(POLARSSL_SSL_PROTO_DTLS) - if( ssl->transport == SSL_TRANSPORT_DATAGRAM ) - { - ssl->handshake->alt_transform_out = ssl->transform_out; + ssl->handshake->alt_transform_out = ssl->transform_out; - if( ssl->endpoint == SSL_IS_CLIENT ) - ssl->handshake->retransmit_state = SSL_RETRANS_PREPARING; - else - ssl->handshake->retransmit_state = SSL_RETRANS_WAITING; - } + ssl->handshake->retransmit_timeout = SSL_DTLS_TIMEOUT_DFL_MIN; + + if( ssl->endpoint == SSL_IS_CLIENT ) + ssl->handshake->retransmit_state = SSL_RETRANS_PREPARING; + else + ssl->handshake->retransmit_state = SSL_RETRANS_WAITING; #endif return( 0 );