Adding a method to change the initial DTLS retransmission timer value.

This allows an application to override the default of 1 second, which
is what's instructed in RFC 6347 but is not an absolute requirement.

Change-Id: I0bbb16e31990fbcab44a29325b6ec7757d5789e5
Reviewed-on: https://boringssl-review.googlesource.com/7930
Reviewed-by: David Benjamin <davidben@google.com>
This commit is contained in:
Taylor Brandstetter 2016-05-10 19:30:28 -07:00 committed by David Benjamin
parent 0e01eb534c
commit 376a0fed24
8 changed files with 85 additions and 9 deletions

View File

@ -487,6 +487,16 @@ OPENSSL_EXPORT int SSL_get_error(const SSL *ssl, int ret_code);
* and zero on failure. */
OPENSSL_EXPORT int SSL_set_mtu(SSL *ssl, unsigned mtu);
/* DTLSv1_set_initial_timeout_duration sets the initial duration for a DTLS
* handshake timeout.
*
* This duration overrides the default of 1 second, which is the strong
* recommendation of RFC 6347 (see section 4.2.4.1). However, there may exist
* situations where a shorter timeout would be beneficial, such as for
* time-sensitive applications. */
OPENSSL_EXPORT void DTLSv1_set_initial_timeout_duration(SSL *ssl,
unsigned duration_ms);
/* DTLSv1_get_timeout queries the next DTLS handshake timeout. If there is a
* timeout in progress, it sets |*out| to the time remaining and returns one.
* Otherwise, it returns zero.
@ -3882,6 +3892,10 @@ struct ssl_st {
struct ssl3_state_st *s3; /* SSLv3 variables */
struct dtls1_state_st *d1; /* DTLSv1 variables */
/* initial_timeout_duration_ms is the default DTLS timeout duration in
* milliseconds. It's used to initialize the timer any time it's restarted. */
unsigned initial_timeout_duration_ms;
/* callback that allows applications to peek at protocol messages */
void (*msg_callback)(int write_p, int version, int content_type,
const void *buf, size_t len, SSL *ssl, void *arg);

View File

@ -157,17 +157,26 @@ int dtls1_supports_cipher(const SSL_CIPHER *cipher) {
return cipher->algorithm_enc != SSL_RC4 && cipher->algorithm_enc != SSL_eNULL;
}
void DTLSv1_set_initial_timeout_duration(SSL *ssl, unsigned int duration_ms) {
ssl->initial_timeout_duration_ms = duration_ms;
}
void dtls1_start_timer(SSL *ssl) {
/* If timer is not set, initialize duration with 1 second */
/* If timer is not set, initialize duration (by default, 1 second) */
if (ssl->d1->next_timeout.tv_sec == 0 && ssl->d1->next_timeout.tv_usec == 0) {
ssl->d1->timeout_duration = 1;
ssl->d1->timeout_duration_ms = ssl->initial_timeout_duration_ms;
}
/* Set timeout to current time */
get_current_time(ssl, &ssl->d1->next_timeout);
/* Add duration to current time */
ssl->d1->next_timeout.tv_sec += ssl->d1->timeout_duration;
ssl->d1->next_timeout.tv_sec += ssl->d1->timeout_duration_ms / 1000;
ssl->d1->next_timeout.tv_usec += (ssl->d1->timeout_duration_ms % 1000) * 1000;
if (ssl->d1->next_timeout.tv_usec >= 1000000) {
ssl->d1->next_timeout.tv_sec++;
ssl->d1->next_timeout.tv_usec -= 1000000;
}
BIO_ctrl(SSL_get_rbio(ssl), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
&ssl->d1->next_timeout);
}
@ -230,9 +239,9 @@ int dtls1_is_timer_expired(SSL *ssl) {
}
void dtls1_double_timeout(SSL *ssl) {
ssl->d1->timeout_duration *= 2;
if (ssl->d1->timeout_duration > 60) {
ssl->d1->timeout_duration = 60;
ssl->d1->timeout_duration_ms *= 2;
if (ssl->d1->timeout_duration_ms > 60000) {
ssl->d1->timeout_duration_ms = 60000;
}
dtls1_start_timer(ssl);
}
@ -241,7 +250,7 @@ void dtls1_stop_timer(SSL *ssl) {
/* Reset everything */
ssl->d1->num_timeouts = 0;
memset(&ssl->d1->next_timeout, 0, sizeof(struct timeval));
ssl->d1->timeout_duration = 1;
ssl->d1->timeout_duration_ms = ssl->initial_timeout_duration_ms;
BIO_ctrl(SSL_get_rbio(ssl), BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
&ssl->d1->next_timeout);
/* Clear retransmission buffer */

View File

@ -956,8 +956,8 @@ typedef struct dtls1_state_st {
* timeout. */
struct timeval next_timeout;
/* Timeout duration */
unsigned short timeout_duration;
/* timeout_duration_ms is the timeout duration in milliseconds. */
unsigned timeout_duration_ms;
} DTLS1_STATE;
extern const SSL3_ENC_METHOD TLSv1_enc_data;

View File

@ -369,6 +369,10 @@ SSL *SSL_new(SSL_CTX *ctx) {
ssl->min_version = ctx->min_version;
ssl->max_version = ctx->max_version;
/* RFC 6347 states that implementations SHOULD use an initial timer value of
* 1 second. */
ssl->initial_timeout_duration_ms = 1000;
ssl->options = ctx->options;
ssl->mode = ctx->mode;
ssl->max_cert_list = ctx->max_cert_list;

View File

@ -1317,6 +1317,10 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
return false;
}
}
if (config->initial_timeout_duration_ms > 0) {
DTLSv1_set_initial_timeout_duration(ssl.get(),
config->initial_timeout_duration_ms);
}
int sock = Connect(config->port);
if (sock == -1) {

View File

@ -4583,6 +4583,24 @@ var timeouts = []time.Duration{
60 * time.Second,
}
// shortTimeouts is an alternate set of timeouts which would occur if the
// initial timeout duration was set to 250ms.
var shortTimeouts = []time.Duration{
250 * time.Millisecond,
500 * time.Millisecond,
1 * time.Second,
2 * time.Second,
4 * time.Second,
8 * time.Second,
16 * time.Second,
32 * time.Second,
60 * time.Second,
60 * time.Second,
60 * time.Second,
60 * time.Second,
60 * time.Second,
}
func addDTLSRetransmitTests() {
// Test that this is indeed the timeout schedule. Stress all
// four patterns of handshake.
@ -4659,6 +4677,31 @@ func addDTLSRetransmitTests() {
},
flags: []string{"-async"},
})
// Test the timeout schedule when a shorter initial timeout duration is set.
testCases = append(testCases, testCase{
protocol: dtls,
name: "DTLS-Retransmit-Short-Client",
config: Config{
Bugs: ProtocolBugs{
TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1],
},
},
resumeSession: true,
flags: []string{"-async", "-initial-timeout-duration-ms", "250"},
})
testCases = append(testCases, testCase{
protocol: dtls,
testType: serverTest,
name: "DTLS-Retransmit-Short-Server",
config: Config{
Bugs: ProtocolBugs{
TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1],
},
},
resumeSession: true,
flags: []string{"-async", "-initial-timeout-duration-ms", "250"},
})
}
func addExportKeyingMaterialTests() {

View File

@ -148,6 +148,7 @@ const Flag<int> kIntFlags[] = {
&TestConfig::expect_server_key_exchange_hash },
{ "-expect-key-exchange-info",
&TestConfig::expect_key_exchange_info },
{ "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms },
};
} // namespace

View File

@ -104,6 +104,7 @@ struct TestConfig {
bool use_sparse_dh_prime = false;
int expect_key_exchange_info = 0;
bool use_old_client_cert_callback = false;
int initial_timeout_duration_ms = 0;
};
bool ParseConfig(int argc, char **argv, TestConfig *out_config);