Implement PMTU auto-reduction in handshake
diff --git a/ChangeLog b/ChangeLog
index 3f144a7..fc47441 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,9 @@
is controlled by the maximum fragment length as set locally or negotiated
with the peer, as well as by a new per-connection MTU option, set using
mbedtls_ssl_set_mtu().
+ * Add support for auto-adjustment of MTU to a safe value during the
+ handshake when flights do not get through (RFC 6347, section 4.1.1.1,
+ last paragraph).
Bugfix
* Fixes an issue with MBEDTLS_CHACHAPOLY_C which would not compile if
diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h
index 18982f8..6be684e 100644
--- a/include/mbedtls/ssl_internal.h
+++ b/include/mbedtls/ssl_internal.h
@@ -307,6 +307,7 @@
resending messages */
unsigned char alt_out_ctr[8]; /*!< Alternative record epoch/counter
for resending messages */
+ uint16_t mtu; /*!< Handshake mtu, used to fragment outoing messages */
#endif /* MBEDTLS_SSL_PROTO_DTLS */
/*
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index faa9467..30c1a78 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -108,6 +108,15 @@
if( ssl->handshake->retransmit_timeout >= ssl->conf->hs_timeout_max )
return( -1 );
+ /* Implement the final paragraph of RFC 6347 section 4.1.1.1
+ * in the following way: after the initial transmission and a first
+ * retransmission, back off to a temporary estimated MTU of 508 bytes.
+ * This value is guaranteed to be deliverable (if not guaranteed to be
+ * delivered) of any compliant IPv4 (and IPv6) network, and should work
+ * on most non-IP stacks too. */
+ if( ssl->handshake->retransmit_timeout != ssl->conf->hs_timeout_min )
+ ssl->handshake->mtu = 508;
+
new_timeout = 2 * ssl->handshake->retransmit_timeout;
/* Avoid arithmetic overflow and range overflow */
@@ -7088,6 +7097,20 @@
}
#endif /* MBEDTLS_SSL_MAX_FRAGMENT_LENGTH */
+#if defined(MBEDTLS_SSL_PROTO_DTLS)
+static size_t ssl_get_current_mtu( const mbedtls_ssl_context *ssl )
+{
+ if( ssl->handshake == NULL || ssl->handshake->mtu == 0 )
+ return( ssl->mtu );
+
+ if( ssl->mtu == 0 )
+ return( ssl->handshake->mtu );
+
+ return( ssl->mtu < ssl->handshake->mtu ?
+ ssl->mtu : ssl->handshake->mtu );
+}
+#endif /* MBEDTLS_SSL_PROTO_DTLS */
+
int mbedtls_ssl_get_max_out_record_payload( const mbedtls_ssl_context *ssl )
{
size_t max_len = MBEDTLS_SSL_OUT_CONTENT_LEN;
@@ -7105,9 +7128,9 @@
#endif
#if defined(MBEDTLS_SSL_PROTO_DTLS)
- if( ssl->mtu != 0 )
+ if( ssl_get_current_mtu( ssl ) != 0 )
{
- const size_t mtu = ssl->mtu;
+ const size_t mtu = ssl_get_current_mtu( ssl );
const int ret = mbedtls_ssl_get_record_expansion( ssl );
const size_t overhead = (size_t) ret;
@@ -7123,7 +7146,7 @@
if( max_len > mtu - overhead )
max_len = mtu - overhead;
}
-#endif
+#endif /* MBEDTLS_SSL_PROTO_DTLS */
return( (int) max_len );
}
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index ec2717a..9fc16bf 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -5111,6 +5111,25 @@
-c "found fragmented DTLS handshake message" \
-C "error"
+# Test for automatic MTU reduction on repeated resend
+requires_config_enabled MBEDTLS_SSL_PROTO_DTLS
+requires_config_enabled MBEDTLS_RSA_C
+requires_config_enabled MBEDTLS_ECDSA_C
+run_test "DTLS fragmenting: proxy MTU: auto-reduction" \
+ -p "$P_PXY mtu=508" \
+ "$P_SRV dtls=1 debug_level=2 auth_mode=required \
+ crt_file=data_files/server7_int-ca.crt \
+ key_file=data_files/server7.key\
+ hs_timeout=100-400" \
+ "$P_CLI dtls=1 debug_level=2 \
+ crt_file=data_files/server8_int-ca2.crt \
+ key_file=data_files/server8.key \
+ hs_timeout=100-400" \
+ 0 \
+ -s "found fragmented DTLS handshake message" \
+ -c "found fragmented DTLS handshake message" \
+ -C "error"
+
# the proxy shouldn't drop or mess up anything, so we shouldn't need to resend
# OTOH the client might resend if the server is to slow to reset after sending
# a HelloVerifyRequest, so only check for no retransmission server-side