diff --git a/ChangeLog b/ChangeLog
index b05c962fa..975b3bac0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,9 +5,11 @@ mbed TLS ChangeLog (Sorted per branch, date)
 Features
    * Add support for fragmentation of outgoing DTLS handshake messages. This
      is controlled by the maximum fragment length as set locally or negotiated
-     with the peer, as well as new per-connection MTU option, set using
+     with the peer, as well as by a new per-connection MTU option, set using
      mbedtls_ssl_set_mtu().
-   * Add support for fragmentation of outoing DTLS handshake messages.
+   * 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).
    * Add support for packing multiple records within a single datagram,
      enabled by default.
 
diff --git a/include/mbedtls/ssl.h b/include/mbedtls/ssl.h
index c1fad6ab4..b6700fc90 100644
--- a/include/mbedtls/ssl.h
+++ b/include/mbedtls/ssl.h
@@ -1400,23 +1400,28 @@ void mbedtls_ssl_set_bio( mbedtls_ssl_context *ssl,
  *                 the maximum size datagram the DTLS layer will pass to the
  *                 \c f_send() callback set using \c mbedtls_ssl_set_bio().
  *
+ * \note           The limit on datagram size is converted to a limit on
+ *                 record payload by subtracting the current overhead of
+ *                 encapsulation and encryption/authentication if any.
+ *
  * \note           This can be called at any point during the connection, for
  *                 example when a PMTU estimate becomes available from other
  *                 sources, such as lower (or higher) protocol layers.
  *
- * \note           This only controls the size of the packets we send.
- *                 Client-side, you can request the server to use smaller
- *                 records with \c mbedtls_ssl_conf_max_frag_len().
+ * \note           This setting only controls the size of the packets we send,
+ *                 and does not restrict the size of the datagrams we're
+ *                 willing to receive. Client-side, you can request the
+ *                 server to use smaller records with \c
+ *                 mbedtls_ssl_conf_max_frag_len().
  *
  * \note           If both a MTU and a maximum fragment length have been
  *                 configured (or negotiated with the peer), the resulting
- *                 lower limit (after translating the MTU setting to a limit
- *                 on the record content length) is used.
+ *                 lower limit on record payload (see first note) is used.
  *
  * \note           This can only be used to decrease the maximum size
- *                 of datagrams sent. It cannot be used to increase the
- *                 maximum size of records over the limit set by
- *                 #MBEDTLS_SSL_OUT_CONTENT_LEN.
+ *                 of datagrams (hence records, see first note) sent. It
+ *                 cannot be used to increase the maximum size of records over
+ *                 the limit set by #MBEDTLS_SSL_OUT_CONTENT_LEN.
  *
  * \note           Values lower than the current record layer expansion will
  *                 result in an error when trying to send data.
diff --git a/include/mbedtls/ssl_internal.h b/include/mbedtls/ssl_internal.h
index 2c0684f3d..3f595a322 100644
--- a/include/mbedtls/ssl_internal.h
+++ b/include/mbedtls/ssl_internal.h
@@ -334,6 +334,8 @@ struct mbedtls_ssl_handshake_params
         } future_record;
 
     } buffering;
+
+    uint16_t mtu;                       /*!<  Handshake mtu, used to fragment outgoing messages */
 #endif /* MBEDTLS_SSL_PROTO_DTLS */
 
     /*
diff --git a/library/ssl_tls.c b/library/ssl_tls.c
index eceac913d..5fccedffe 100644
--- a/library/ssl_tls.c
+++ b/library/ssl_tls.c
@@ -109,9 +109,10 @@ static void ssl_update_in_pointers( mbedtls_ssl_context *ssl,
 
 #if defined(MBEDTLS_SSL_PROTO_DTLS)
 
+static size_t ssl_get_current_mtu( const mbedtls_ssl_context *ssl );
 static uint16_t ssl_get_maximum_datagram_size( mbedtls_ssl_context const *ssl )
 {
-    uint16_t mtu = ssl->mtu;
+    uint16_t mtu = ssl_get_current_mtu( ssl );
 
     if( mtu != 0 && mtu < MBEDTLS_SSL_OUT_BUFFER_LEN )
         return( (int) mtu );
@@ -184,6 +185,15 @@ static int ssl_double_retransmit_timeout( mbedtls_ssl_context *ssl )
     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 */
@@ -7832,10 +7842,29 @@ size_t mbedtls_ssl_get_max_frag_len( const mbedtls_ssl_context *ssl )
 }
 #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;
 
+#if !defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) && \
+    !defined(MBEDTLS_SSL_PROTO_DTLS)
+    (void) ssl;
+#endif
+
 #if defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH)
     const size_t mfl = mbedtls_ssl_get_max_frag_len( ssl );
 
@@ -7844,9 +7873,9 @@ int mbedtls_ssl_get_max_out_record_payload( const mbedtls_ssl_context *ssl )
 #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;
 
@@ -7862,7 +7891,7 @@ int mbedtls_ssl_get_max_out_record_payload( const mbedtls_ssl_context *ssl )
         if( max_len > mtu - overhead )
             max_len = mtu - overhead;
     }
-#endif
+#endif /* MBEDTLS_SSL_PROTO_DTLS */
 
 #if !defined(MBEDTLS_SSL_MAX_FRAGMENT_LENGTH) &&        \
     !defined(MBEDTLS_SSL_PROTO_DTLS)
diff --git a/tests/ssl-opt.sh b/tests/ssl-opt.sh
index 657aa8a7c..b77c096fb 100755
--- a/tests/ssl-opt.sh
+++ b/tests/ssl-opt.sh
@@ -5143,7 +5143,28 @@ run_test    "DTLS fragmenting: both (MTU)" \
             -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
 not_with_valgrind # spurious resend due to timeout
 requires_config_enabled MBEDTLS_SSL_PROTO_DTLS
 requires_config_enabled MBEDTLS_RSA_C
@@ -5160,7 +5181,26 @@ run_test    "DTLS fragmenting: proxy MTU, simple handshake" \
              mtu=512" \
             0 \
             -S "resend" \
-            -C "resend" \
+            -s "found fragmented DTLS handshake message" \
+            -c "found fragmented DTLS handshake message" \
+            -C "error"
+
+not_with_valgrind # spurious resend due to timeout
+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, simple handshake, nbio" \
+            -p "$P_PXY mtu=512" \
+            "$P_SRV dtls=1 debug_level=2 auth_mode=required \
+             crt_file=data_files/server7_int-ca.crt \
+             key_file=data_files/server7.key \
+             mtu=512 nbio=2" \
+            "$P_CLI dtls=1 debug_level=2 \
+             crt_file=data_files/server8_int-ca2.crt \
+             key_file=data_files/server8.key \
+             mtu=512 nbio=2" \
+            0 \
+            -S "resend" \
             -s "found fragmented DTLS handshake message" \
             -c "found fragmented DTLS handshake message" \
             -C "error"
@@ -5171,9 +5211,10 @@ run_test    "DTLS fragmenting: proxy MTU, simple handshake" \
 # Since we don't support reading fragmented ClientHello yet,
 # up the MTU to 1450 (larger than ClientHello with session ticket,
 # but still smaller than client's Certificate to ensure fragmentation).
-#
 # A resend on the client-side might happen if the server is
 # slow to reset, therefore omitting '-C "resend"' below.
+# reco_delay avoids races where the client reconnects before the server has
+# resumed listening, which would result in a spurious resend.
 not_with_valgrind # spurious resend due to timeout
 requires_config_enabled MBEDTLS_SSL_PROTO_DTLS
 requires_config_enabled MBEDTLS_RSA_C
@@ -5187,7 +5228,7 @@ run_test    "DTLS fragmenting: proxy MTU, resumed handshake" \
             "$P_CLI dtls=1 debug_level=2 \
              crt_file=data_files/server8_int-ca2.crt \
              key_file=data_files/server8.key \
-             mtu=1450 reconnect=1" \
+             mtu=1450 reconnect=1 reco_delay=1" \
             0 \
             -S "resend" \
             -s "found fragmented DTLS handshake message" \
@@ -5363,6 +5404,25 @@ run_test    "DTLS fragmenting: proxy MTU + 3d" \
             -c "found fragmented DTLS handshake message" \
             -C "error"
 
+requires_config_enabled MBEDTLS_SSL_PROTO_DTLS
+requires_config_enabled MBEDTLS_RSA_C
+requires_config_enabled MBEDTLS_ECDSA_C
+client_needs_more_time 2
+run_test    "DTLS fragmenting: proxy MTU + 3d, nbio" \
+            -p "$P_PXY mtu=512 drop=8 delay=8 duplicate=8" \
+            "$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=250-10000 mtu=512 nbio=2" \
+            "$P_CLI dtls=1 debug_level=2 \
+             crt_file=data_files/server8_int-ca2.crt \
+             key_file=data_files/server8.key \
+             hs_timeout=250-10000 mtu=512 nbio=2" \
+            0 \
+            -s "found fragmented DTLS handshake message" \
+            -c "found fragmented DTLS handshake message" \
+            -C "error"
+
 # interop tests for DTLS fragmentating with reliable connection
 #
 # here and below we just want to test that the we fragment in a way that
@@ -5372,6 +5432,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS
 requires_config_enabled MBEDTLS_RSA_C
 requires_config_enabled MBEDTLS_ECDSA_C
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+requires_gnutls
 run_test    "DTLS fragmenting: gnutls server, DTLS 1.2" \
             "$G_SRV -u" \
             "$P_CLI dtls=1 debug_level=2 \
@@ -5387,6 +5448,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS
 requires_config_enabled MBEDTLS_RSA_C
 requires_config_enabled MBEDTLS_ECDSA_C
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_1
+requires_gnutls
 run_test    "DTLS fragmenting: gnutls server, DTLS 1.0" \
             "$G_SRV -u" \
             "$P_CLI dtls=1 debug_level=2 \
@@ -5403,6 +5465,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS
 requires_config_enabled MBEDTLS_RSA_C
 requires_config_enabled MBEDTLS_ECDSA_C
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_2
+requires_gnutls
 run_test    "DTLS fragmenting: gnutls client, DTLS 1.2" \
             "$P_SRV dtls=1 debug_level=2 server_addr=::1 \
              crt_file=data_files/server7_int-ca.crt \
@@ -5418,6 +5481,7 @@ requires_config_enabled MBEDTLS_SSL_PROTO_DTLS
 requires_config_enabled MBEDTLS_RSA_C
 requires_config_enabled MBEDTLS_ECDSA_C
 requires_config_enabled MBEDTLS_SSL_PROTO_TLS1_1
+requires_gnutls
 run_test    "DTLS fragmenting: gnutls client, DTLS 1.0" \
             "$P_SRV dtls=1 debug_level=2 server_addr=::1 \
              crt_file=data_files/server7_int-ca.crt \
@@ -5527,6 +5591,7 @@ run_test    "DTLS fragmenting: 3d, gnutls server, DTLS 1.0" \
 ##
 ## # gnutls-cli always tries IPv6 first, and doesn't fall back to IPv4 with DTLS
 ## requires_ipv6
+## requires_gnutls
 ## requires_config_enabled MBEDTLS_SSL_PROTO_DTLS
 ## requires_config_enabled MBEDTLS_RSA_C
 ## requires_config_enabled MBEDTLS_ECDSA_C
@@ -5544,6 +5609,7 @@ run_test    "DTLS fragmenting: 3d, gnutls server, DTLS 1.0" \
 ##
 ## # gnutls-cli always tries IPv6 first, and doesn't fall back to IPv4 with DTLS
 ## requires_ipv6
+## requires_gnutls
 ## requires_config_enabled MBEDTLS_SSL_PROTO_DTLS
 ## requires_config_enabled MBEDTLS_RSA_C
 ## requires_config_enabled MBEDTLS_ECDSA_C