You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by ma...@apache.org on 2018/03/16 02:03:00 UTC

[trafficserver] branch quic-latest updated (e86ea38 -> 147b19b)

This is an automated email from the ASF dual-hosted git repository.

masaori pushed a change to branch quic-latest
in repository https://gitbox.apache.org/repos/asf/trafficserver.git.


    from e86ea38  Move ats_unique_buf to ink_memory
     new e10160e  Add Stateless Retry Support
     new 89f9562  Generate client address validation token
     new 147b19b  clang-format

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 iocore/net/P_QUICNetVConnection.h        |  3 ++
 iocore/net/QUICNetVConnection.cc         | 48 +++++++++++++++++++++++++--
 iocore/net/QUICPacketHandler.cc          |  6 ++++
 iocore/net/quic/QUICConfig.cc            |  9 ++++-
 iocore/net/quic/QUICConfig.h             |  2 ++
 iocore/net/quic/QUICGlobals.cc           | 57 ++++++++++++++++++++++++++++++--
 iocore/net/quic/QUICGlobals.h            | 15 ++++-----
 iocore/net/quic/QUICHandshake.cc         | 44 ++++++++++++++++++------
 iocore/net/quic/QUICHandshake.h          | 16 +++++----
 iocore/net/quic/QUICHandshakeProtocol.cc |  9 +++++
 iocore/net/quic/QUICHandshakeProtocol.h  |  4 +++
 iocore/net/quic/QUICPacket.cc            |  5 ++-
 iocore/net/quic/QUICPacket.h             |  2 +-
 iocore/net/quic/QUICTLS.cc               | 19 ++++-------
 iocore/net/quic/QUICTLS.h                |  1 -
 iocore/net/quic/QUICTypes.h              |  8 +++++
 mgmt/RecordsConfig.cc                    |  2 ++
 17 files changed, 203 insertions(+), 47 deletions(-)

-- 
To stop receiving notification emails like this one, please contact
masaori@apache.org.

[trafficserver] 02/03: Generate client address validation token

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

masaori pushed a commit to branch quic-latest
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 89f9562844399648d28184662b60fe1072ca000f
Author: Masaori Koshiba <ma...@apache.org>
AuthorDate: Fri Mar 16 10:57:44 2018 +0900

    Generate client address validation token
---
 iocore/net/QUICPacketHandler.cc | 31 +++-------------------
 iocore/net/quic/QUICGlobals.cc  | 57 +++++++++++++++++++++++++++++++++++++++--
 iocore/net/quic/QUICGlobals.h   | 15 +++++------
 3 files changed, 64 insertions(+), 39 deletions(-)

diff --git a/iocore/net/QUICPacketHandler.cc b/iocore/net/QUICPacketHandler.cc
index da98693..f5a3bbe 100644
--- a/iocore/net/QUICPacketHandler.cc
+++ b/iocore/net/QUICPacketHandler.cc
@@ -27,6 +27,7 @@
 #include "QUICPacket.h"
 #include "QUICDebugNames.h"
 #include "QUICEvents.h"
+#include "QUICGlobals.h"
 
 //
 // QUICPacketHandler
@@ -85,32 +86,6 @@ QUICPacketHandler::_read_connection_id(IOBufferBlock *block)
   return QUICPacket::connection_id(buf);
 }
 
-// TODO: ramdomize token and verify it
-// dummy token to simplify test
-static uint8_t token[] = {0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef,
-                          0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef,
-                          0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef,
-                          0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef};
-
-static int
-generate_cookie_callback(SSL * /* ssl */, unsigned char *cookie, size_t *cookie_len)
-{
-  memcpy(cookie, token, sizeof(token));
-  *cookie_len = sizeof(token);
-
-  return 1;
-}
-
-static int
-verify_cookie_callback(SSL *ssl, const unsigned char *cookie, size_t cookie_len)
-{
-  if (memcmp(token, cookie, sizeof(token)) == 0) {
-    return 1;
-  } else {
-    return 0;
-  }
-}
-
 //
 // QUICPacketHandlerIn
 //
@@ -123,8 +98,8 @@ QUICPacketHandlerIn::QUICPacketHandlerIn(const NetProcessor::AcceptOptions &opt,
 
   // callbacks for cookie ext
   // Requires OpenSSL-1.1.1-pre3+ : https://github.com/openssl/openssl/pull/5463
-  SSL_CTX_set_stateless_cookie_generate_cb(this->_ssl_ctx, generate_cookie_callback);
-  SSL_CTX_set_stateless_cookie_verify_cb(this->_ssl_ctx, verify_cookie_callback);
+  SSL_CTX_set_stateless_cookie_generate_cb(this->_ssl_ctx, QUIC::ssl_generate_stateless_cookie);
+  SSL_CTX_set_stateless_cookie_verify_cb(this->_ssl_ctx, QUIC::ssl_verify_stateless_cookie);
 }
 
 QUICPacketHandlerIn::~QUICPacketHandlerIn()
diff --git a/iocore/net/quic/QUICGlobals.cc b/iocore/net/quic/QUICGlobals.cc
index 7627a2e..c843e9b 100644
--- a/iocore/net/quic/QUICGlobals.cc
+++ b/iocore/net/quic/QUICGlobals.cc
@@ -21,17 +21,37 @@
  *  limitations under the License.
  */
 
-#include <cstring>
 #include "QUICGlobals.h"
+
+#include <cstring>
+
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+
+#include "P_SSLNextProtocolSet.h"
+#include "P_QUICNetVConnection.h"
 #include "QUICStats.h"
 #include "QUICConnection.h"
-#include "P_SSLNextProtocolSet.h"
 
 RecRawStatBlock *quic_rsb;
 
 int QUIC::ssl_quic_qc_index = -1;
 int QUIC::ssl_quic_hs_index = -1;
 
+static constexpr size_t STATELESS_COOKIE_SECRET_LENGTH                 = 16;
+static uint8_t stateless_cookie_secret[STATELESS_COOKIE_SECRET_LENGTH] = {0};
+
+void
+QUIC::init()
+{
+  QUIC::_register_stats();
+  ssl_quic_qc_index = SSL_get_ex_new_index(0, (void *)"QUICConnection index", nullptr, nullptr, nullptr);
+  ssl_quic_hs_index = SSL_get_ex_new_index(0, (void *)"QUICHandshake index", nullptr, nullptr, nullptr);
+
+  // TODO: read cookie secret from file like SSLTicketKeyConfig
+  RAND_bytes(stateless_cookie_secret, STATELESS_COOKIE_SECRET_LENGTH);
+}
+
 int
 QUIC::ssl_select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned inlen,
                                void *)
@@ -50,6 +70,39 @@ QUIC::ssl_select_next_protocol(SSL *ssl, const unsigned char **out, unsigned cha
   return SSL_TLSEXT_ERR_NOACK;
 }
 
+int
+QUIC::ssl_generate_stateless_cookie(SSL *ssl, unsigned char *cookie, size_t *cookie_len)
+{
+  // Call UnixNetVConnection::get_remote_addr() safely
+  // TODO: add APIs to getting client addr in QUICConnection
+  QUICConnection *qc      = static_cast<QUICConnection *>(SSL_get_ex_data(ssl, QUIC::ssl_quic_qc_index));
+  QUICNetVConnection *qvc = dynamic_cast<QUICNetVConnection *>(qc);
+
+  uint8_t key[INET6_ADDRPORTSTRLEN] = {0};
+  size_t key_len                    = INET6_ADDRPORTSTRLEN;
+  ats_ip_nptop(qvc->get_remote_addr(), reinterpret_cast<char *>(key), key_len);
+
+  unsigned int dst_len = 0;
+  HMAC(EVP_sha1(), stateless_cookie_secret, STATELESS_COOKIE_SECRET_LENGTH, key, key_len, cookie, &dst_len);
+  *cookie_len = dst_len;
+
+  return 1;
+}
+
+int
+QUIC::ssl_verify_stateless_cookie(SSL *ssl, const unsigned char *cookie, size_t cookie_len)
+{
+  uint8_t token[EVP_MAX_MD_SIZE];
+  size_t token_len;
+
+  if (QUIC::ssl_generate_stateless_cookie(ssl, token, &token_len) && cookie_len == token_len &&
+      memcmp(token, cookie, cookie_len) == 0) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
 void
 QUIC::_register_stats()
 {
diff --git a/iocore/net/quic/QUICGlobals.h b/iocore/net/quic/QUICGlobals.h
index c4b5268..26f2e1b 100644
--- a/iocore/net/quic/QUICGlobals.h
+++ b/iocore/net/quic/QUICGlobals.h
@@ -28,19 +28,16 @@
 class QUIC
 {
 public:
-  static void
-  init()
-  {
-    QUIC::_register_stats();
-    ssl_quic_qc_index = SSL_get_ex_new_index(0, (void *)"QUICConnection index", nullptr, nullptr, nullptr);
-    ssl_quic_hs_index = SSL_get_ex_new_index(0, (void *)"QUICHandshake index", nullptr, nullptr, nullptr);
-  }
-  static int ssl_quic_qc_index;
-  static int ssl_quic_hs_index;
+  static void init();
 
   // SSL callbacks
   static int ssl_select_next_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in,
                                       unsigned inlen, void *);
+  static int ssl_generate_stateless_cookie(SSL *ssl, unsigned char *cookie, size_t *cookie_len);
+  static int ssl_verify_stateless_cookie(SSL *ssl, const unsigned char *cookie, size_t cookie_len);
+
+  static int ssl_quic_qc_index;
+  static int ssl_quic_hs_index;
 
 private:
   static void _register_stats();

-- 
To stop receiving notification emails like this one, please contact
masaori@apache.org.

[trafficserver] 03/03: clang-format

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

masaori pushed a commit to branch quic-latest
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit 147b19b16d760e692df7e5b267f6324f390478ef
Author: Masaori Koshiba <ma...@apache.org>
AuthorDate: Fri Mar 16 11:00:03 2018 +0900

    clang-format
---
 iocore/net/quic/QUICHandshake.cc | 4 ++--
 iocore/net/quic/QUICHandshake.h  | 2 +-
 iocore/net/quic/QUICTLS.cc       | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/iocore/net/quic/QUICHandshake.cc b/iocore/net/quic/QUICHandshake.cc
index 4276715..42b79f1 100644
--- a/iocore/net/quic/QUICHandshake.cc
+++ b/iocore/net/quic/QUICHandshake.cc
@@ -162,7 +162,6 @@ QUICHandshake::is_stateless_retry_enabled() const
   return this->_stateless_retry;
 }
 
-
 QUICHandshakeProtocol *
 QUICHandshake::protocol()
 {
@@ -403,7 +402,8 @@ QUICHandshake::state_closed(int event, void *data)
 }
 
 QUICHandshakeMsgType
-QUICHandshake::msg_type() const {
+QUICHandshake::msg_type() const
+{
   if (this->_hs_protocol) {
     return this->_hs_protocol->msg_type();
   } else {
diff --git a/iocore/net/quic/QUICHandshake.h b/iocore/net/quic/QUICHandshake.h
index d53171f..6ddae16 100644
--- a/iocore/net/quic/QUICHandshake.h
+++ b/iocore/net/quic/QUICHandshake.h
@@ -97,7 +97,7 @@ private:
   std::shared_ptr<QUICTransportParameters> _remote_transport_parameters = nullptr;
 
   QUICVersionNegotiator *_version_negotiator = nullptr;
-  NetVConnectionContext_t _netvc_context = NET_VCONNECTION_UNSET;
+  NetVConnectionContext_t _netvc_context     = NET_VCONNECTION_UNSET;
   QUICStatelessResetToken _reset_token;
   bool _stateless_retry = false;
 
diff --git a/iocore/net/quic/QUICTLS.cc b/iocore/net/quic/QUICTLS.cc
index 50b2e73..46ac529 100644
--- a/iocore/net/quic/QUICTLS.cc
+++ b/iocore/net/quic/QUICTLS.cc
@@ -108,12 +108,12 @@ QUICTLS::handshake(uint8_t *out, size_t &out_len, size_t max_out_len, const uint
         ret = SSL_stateless(this->_ssl);
         if (ret > 0) {
           this->_stateless = false;
-          this->_msg_type = QUICHandshakeMsgType::SH;
+          this->_msg_type  = QUICHandshakeMsgType::SH;
         } else if (ret == 0) {
           this->_msg_type = QUICHandshakeMsgType::HRR;
         }
       } else {
-        ret = SSL_accept(this->_ssl);
+        ret             = SSL_accept(this->_ssl);
         this->_msg_type = QUICHandshakeMsgType::SH;
       }
     } else {

-- 
To stop receiving notification emails like this one, please contact
masaori@apache.org.

[trafficserver] 01/03: Add Stateless Retry Support

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

masaori pushed a commit to branch quic-latest
in repository https://gitbox.apache.org/repos/asf/trafficserver.git

commit e10160e6b505b9a285ee85d641f15158409357f9
Author: Masaori Koshiba <ma...@apache.org>
AuthorDate: Wed Mar 14 12:08:57 2018 +0900

    Add Stateless Retry Support
    
    To enable this feature set `proxy.config.quic.stateless_retry` 1.
    Currently Address Validation Token in cookie ext is dummy.
---
 iocore/net/P_QUICNetVConnection.h        |  3 ++
 iocore/net/QUICNetVConnection.cc         | 48 ++++++++++++++++++++++++++++++--
 iocore/net/QUICPacketHandler.cc          | 31 +++++++++++++++++++++
 iocore/net/quic/QUICConfig.cc            |  9 +++++-
 iocore/net/quic/QUICConfig.h             |  2 ++
 iocore/net/quic/QUICHandshake.cc         | 44 ++++++++++++++++++++++-------
 iocore/net/quic/QUICHandshake.h          | 16 +++++++----
 iocore/net/quic/QUICHandshakeProtocol.cc |  9 ++++++
 iocore/net/quic/QUICHandshakeProtocol.h  |  4 +++
 iocore/net/quic/QUICPacket.cc            |  5 ++--
 iocore/net/quic/QUICPacket.h             |  2 +-
 iocore/net/quic/QUICTLS.cc               | 17 ++++-------
 iocore/net/quic/QUICTLS.h                |  1 -
 iocore/net/quic/QUICTypes.h              |  8 ++++++
 mgmt/RecordsConfig.cc                    |  2 ++
 15 files changed, 166 insertions(+), 35 deletions(-)

diff --git a/iocore/net/P_QUICNetVConnection.h b/iocore/net/P_QUICNetVConnection.h
index 781ee3c..03a1c42 100644
--- a/iocore/net/P_QUICNetVConnection.h
+++ b/iocore/net/P_QUICNetVConnection.h
@@ -112,6 +112,8 @@ class SSLNextProtocolSet;
  *  |   _state_handshake_process_zero_rtt_protected_packet()
  *  | WRITE:
  *  |   _state_common_send_packet()
+ *  |   or
+ *  |   _state_handshake_send_retry_packet()
  *  v
  * state_connection_established()
  *  | READ:
@@ -295,6 +297,7 @@ private:
   QUICErrorUPtr _state_common_receive_packet();
   QUICErrorUPtr _state_connection_closing_and_draining_receive_packet();
   QUICErrorUPtr _state_common_send_packet();
+  QUICErrorUPtr _state_handshake_send_retry_packet();
   QUICErrorUPtr _state_closing_send_packet();
 
   Ptr<ProxyMutex> _packet_transmitter_mutex;
diff --git a/iocore/net/QUICNetVConnection.cc b/iocore/net/QUICNetVConnection.cc
index a6859da..42d2197 100644
--- a/iocore/net/QUICNetVConnection.cc
+++ b/iocore/net/QUICNetVConnection.cc
@@ -176,7 +176,7 @@ QUICNetVConnection::start(SSL_CTX *ssl_ctx)
   if (this->direction() == NET_VCONNECTION_IN) {
     QUICConfig::scoped_config params;
     this->_reset_token.generate(this->_quic_connection_id, params->server_id());
-    this->_handshake_handler = new QUICHandshake(this, ssl_ctx, this->_reset_token);
+    this->_handshake_handler = new QUICHandshake(this, ssl_ctx, this->_reset_token, params->stateless_retry());
   } else {
     this->_handshake_handler = new QUICHandshake(this, ssl_ctx);
     this->_handshake_handler->start(&this->_packet_factory);
@@ -549,7 +549,16 @@ QUICNetVConnection::state_handshake(int event, Event *data)
   }
   case QUIC_EVENT_PACKET_WRITE_READY: {
     this->_close_packet_write_ready(data);
-    error = this->_state_common_send_packet();
+
+    if (this->_handshake_handler && this->_handshake_handler->msg_type() == QUICHandshakeMsgType::HRR) {
+      error = this->_state_handshake_send_retry_packet();
+      if (this->_handshake_handler->is_stateless_retry_enabled()) {
+        this->_switch_to_close_state();
+      }
+    } else {
+      error = this->_state_common_send_packet();
+    }
+
     break;
   }
   case EVENT_IMMEDIATE:
@@ -919,6 +928,37 @@ QUICNetVConnection::_state_common_send_packet()
   return QUICErrorUPtr(new QUICNoError());
 }
 
+// RETRY packet contains ONLY a single STREAM frame
+QUICErrorUPtr
+QUICNetVConnection::_state_handshake_send_retry_packet()
+{
+  size_t len = 0;
+  ats_unique_buf buf(nullptr, [](void *p) { ats_free(p); });
+  QUICPacketType current_packet_type = QUICPacketType::UNINITIALIZED;
+
+  QUICFrameUPtr frame(nullptr, nullptr);
+  bool retransmittable = this->_handshake_handler->is_stateless_retry_enabled() ? false : true;
+
+  SCOPED_MUTEX_LOCK(packet_transmitter_lock, this->_packet_transmitter_mutex, this_ethread());
+  SCOPED_MUTEX_LOCK(frame_transmitter_lock, this->_frame_transmitter_mutex, this_ethread());
+
+  ink_assert(this->_frame_send_queue.size() == 1);
+  frame = std::move(this->_frame_send_queue.front());
+  this->_frame_send_queue.pop();
+  this->_store_frame(buf, len, retransmittable, current_packet_type, std::move(frame));
+  if (len == 0) {
+    return QUICErrorUPtr(new QUICConnectionError(QUICTransErrorCode::INTERNAL_ERROR));
+  }
+
+  QUICPacketUPtr packet = this->_build_packet(std::move(buf), len, retransmittable, QUICPacketType::RETRY);
+  this->_packet_handler->send_packet(*packet, this);
+  this->_loss_detector->on_packet_sent(std::move(packet));
+
+  QUIC_INCREMENT_DYN_STAT_EX(QUICStats::total_packets_sent_stat, 1);
+
+  return QUICErrorUPtr(new QUICNoError());
+}
+
 QUICErrorUPtr
 QUICNetVConnection::_state_closing_send_packet()
 {
@@ -1089,6 +1129,10 @@ QUICNetVConnection::_build_packet(ats_unique_buf buf, size_t len, bool retransmi
     packet = this->_packet_factory.create_initial_packet(this->_original_quic_connection_id, this->largest_acked_packet_number(),
                                                          QUIC_SUPPORTED_VERSIONS[0], std::move(buf), len);
     break;
+  case QUICPacketType::RETRY:
+    packet = this->_packet_factory.create_retry_packet(this->_quic_connection_id, this->largest_acked_packet_number(),
+                                                       std::move(buf), len, retransmittable);
+    break;
   case QUICPacketType::HANDSHAKE:
     packet = this->_packet_factory.create_handshake_packet(this->_quic_connection_id, this->largest_acked_packet_number(),
                                                            std::move(buf), len, retransmittable);
diff --git a/iocore/net/QUICPacketHandler.cc b/iocore/net/QUICPacketHandler.cc
index 2561005..da98693 100644
--- a/iocore/net/QUICPacketHandler.cc
+++ b/iocore/net/QUICPacketHandler.cc
@@ -85,6 +85,32 @@ QUICPacketHandler::_read_connection_id(IOBufferBlock *block)
   return QUICPacket::connection_id(buf);
 }
 
+// TODO: ramdomize token and verify it
+// dummy token to simplify test
+static uint8_t token[] = {0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef,
+                          0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef,
+                          0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef,
+                          0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef};
+
+static int
+generate_cookie_callback(SSL * /* ssl */, unsigned char *cookie, size_t *cookie_len)
+{
+  memcpy(cookie, token, sizeof(token));
+  *cookie_len = sizeof(token);
+
+  return 1;
+}
+
+static int
+verify_cookie_callback(SSL *ssl, const unsigned char *cookie, size_t cookie_len)
+{
+  if (memcmp(token, cookie, sizeof(token)) == 0) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
 //
 // QUICPacketHandlerIn
 //
@@ -94,6 +120,11 @@ QUICPacketHandlerIn::QUICPacketHandlerIn(const NetProcessor::AcceptOptions &opt,
   // create Connection Table
   QUICConfig::scoped_config params;
   _ctable = new QUICConnectionTable(params->connection_table_size());
+
+  // callbacks for cookie ext
+  // Requires OpenSSL-1.1.1-pre3+ : https://github.com/openssl/openssl/pull/5463
+  SSL_CTX_set_stateless_cookie_generate_cb(this->_ssl_ctx, generate_cookie_callback);
+  SSL_CTX_set_stateless_cookie_verify_cb(this->_ssl_ctx, verify_cookie_callback);
 }
 
 QUICPacketHandlerIn::~QUICPacketHandlerIn()
diff --git a/iocore/net/quic/QUICConfig.cc b/iocore/net/quic/QUICConfig.cc
index 8bcf9c9..0b8c00c 100644
--- a/iocore/net/quic/QUICConfig.cc
+++ b/iocore/net/quic/QUICConfig.cc
@@ -39,7 +39,8 @@ QUICConfigParams::initialize()
   REC_EstablishStaticConfigInt32U(this->_initial_max_data, "proxy.config.quic.initial_max_data");
   REC_EstablishStaticConfigInt32U(this->_initial_max_stream_data, "proxy.config.quic.initial_max_stream_data");
   REC_EstablishStaticConfigInt32U(this->_server_id, "proxy.config.quic.server_id");
-  REC_EstablishStaticConfigInt32(_connection_table_size, "proxy.config.quic.connection_table.size");
+  REC_EstablishStaticConfigInt32(this->_connection_table_size, "proxy.config.quic.connection_table.size");
+  REC_EstablishStaticConfigInt32U(this->_stateless_retry, "proxy.config.quic.stateless_retry");
 }
 
 uint32_t
@@ -67,6 +68,12 @@ QUICConfigParams::connection_table_size()
 }
 
 uint32_t
+QUICConfigParams::stateless_retry() const
+{
+  return this->_stateless_retry;
+}
+
+uint32_t
 QUICConfigParams::initial_max_data() const
 {
   return this->_initial_max_data;
diff --git a/iocore/net/quic/QUICConfig.h b/iocore/net/quic/QUICConfig.h
index a7a4f60..4ae7721 100644
--- a/iocore/net/quic/QUICConfig.h
+++ b/iocore/net/quic/QUICConfig.h
@@ -39,6 +39,7 @@ public:
   uint32_t initial_max_stream_id_uni_out() const;
   uint32_t server_id() const;
   static int connection_table_size();
+  uint32_t stateless_retry() const;
 
 private:
   // FIXME Fill appropriate default values in RecordsConfig.cc
@@ -48,6 +49,7 @@ private:
   uint32_t _initial_max_stream_data = 0;
   uint32_t _server_id               = 0;
   static int _connection_table_size;
+  uint32_t _stateless_retry = 0;
 
   uint32_t _initial_max_stream_id_bidi_in  = 100;
   uint32_t _initial_max_stream_id_bidi_out = 101;
diff --git a/iocore/net/quic/QUICHandshake.cc b/iocore/net/quic/QUICHandshake.cc
index 428106a..4276715 100644
--- a/iocore/net/quic/QUICHandshake.cc
+++ b/iocore/net/quic/QUICHandshake.cc
@@ -86,17 +86,18 @@ static constexpr int UDP_MAXIMUM_PAYLOAD_SIZE = 65527;
 // TODO: fix size
 static constexpr int MAX_HANDSHAKE_MSG_LEN = 65527;
 
-QUICHandshake::QUICHandshake(QUICConnection *qc, SSL_CTX *ssl_ctx) : QUICHandshake(qc, ssl_ctx, {})
+QUICHandshake::QUICHandshake(QUICConnection *qc, SSL_CTX *ssl_ctx) : QUICHandshake(qc, ssl_ctx, {}, false)
 {
 }
 
-QUICHandshake::QUICHandshake(QUICConnection *qc, SSL_CTX *ssl_ctx, QUICStatelessResetToken token)
+QUICHandshake::QUICHandshake(QUICConnection *qc, SSL_CTX *ssl_ctx, QUICStatelessResetToken token, bool stateless_retry)
   : QUICApplication(qc),
     _ssl(SSL_new(ssl_ctx)),
-    _hs_protocol(new QUICTLS(this->_ssl, qc->direction())),
+    _hs_protocol(new QUICTLS(this->_ssl, qc->direction(), stateless_retry)),
     _version_negotiator(new QUICVersionNegotiator()),
     _netvc_context(qc->direction()),
-    _reset_token(token)
+    _reset_token(token),
+    _stateless_retry(stateless_retry)
 {
   SSL_set_ex_data(this->_ssl, QUIC::ssl_quic_qc_index, qc);
   SSL_set_ex_data(this->_ssl, QUIC::ssl_quic_hs_index, this);
@@ -144,17 +145,24 @@ QUICHandshake::start(const QUICPacket *initial_packet, QUICPacketFactory *packet
 }
 
 bool
-QUICHandshake::is_version_negotiated()
+QUICHandshake::is_version_negotiated() const
 {
   return (this->_version_negotiator->status() == QUICVersionNegotiationStatus::NEGOTIATED);
 }
 
 bool
-QUICHandshake::is_completed()
+QUICHandshake::is_completed() const
 {
   return this->handler == &QUICHandshake::state_complete;
 }
 
+bool
+QUICHandshake::is_stateless_retry_enabled() const
+{
+  return this->_stateless_retry;
+}
+
+
 QUICHandshakeProtocol *
 QUICHandshake::protocol()
 {
@@ -393,6 +401,16 @@ QUICHandshake::state_closed(int event, void *data)
 {
   return EVENT_DONE;
 }
+
+QUICHandshakeMsgType
+QUICHandshake::msg_type() const {
+  if (this->_hs_protocol) {
+    return this->_hs_protocol->msg_type();
+  } else {
+    return QUICHandshakeMsgType::NONE;
+  }
+}
+
 void
 QUICHandshake::_load_local_server_transport_parameters(QUICVersion negotiated_version)
 {
@@ -505,12 +523,18 @@ QUICHandshake::_process_client_hello()
   QUICErrorUPtr error     = QUICErrorUPtr(new QUICNoError());
 
   switch (result) {
+  case SSL_ERROR_NONE:
   case SSL_ERROR_WANT_READ: {
-    QUICHSDebug("Enter state_auth");
-    SET_HANDLER(&QUICHandshake::state_auth);
+    if (this->_hs_protocol->msg_type() == QUICHandshakeMsgType::HRR) {
+      // TODO: Send HRR on Retry Packet directly
+      stream_io->write_reenable();
+    } else {
+      QUICHSDebug("Enter state_auth");
+      SET_HANDLER(&QUICHandshake::state_auth);
 
-    stream_io->write_reenable();
-    stream_io->read_reenable();
+      stream_io->write_reenable();
+      stream_io->read_reenable();
+    }
 
     break;
   }
diff --git a/iocore/net/quic/QUICHandshake.h b/iocore/net/quic/QUICHandshake.h
index 3fd0dfe..d53171f 100644
--- a/iocore/net/quic/QUICHandshake.h
+++ b/iocore/net/quic/QUICHandshake.h
@@ -55,7 +55,7 @@ public:
   // Constructor for client side
   QUICHandshake(QUICConnection *qc, SSL_CTX *ssl_ctx);
   // Constructor for server side
-  QUICHandshake(QUICConnection *qc, SSL_CTX *ssl_ctx, QUICStatelessResetToken token);
+  QUICHandshake(QUICConnection *qc, SSL_CTX *ssl_ctx, QUICStatelessResetToken token, bool stateless_retry);
   ~QUICHandshake();
 
   // for client side
@@ -79,13 +79,17 @@ public:
   std::shared_ptr<const QUICTransportParameters> local_transport_parameters();
   std::shared_ptr<const QUICTransportParameters> remote_transport_parameters();
 
-  bool is_version_negotiated();
-  bool is_completed();
+  bool is_version_negotiated() const;
+  bool is_completed() const;
+  bool is_stateless_retry_enabled() const;
 
   void set_transport_parameters(std::shared_ptr<QUICTransportParametersInClientHello> tp);
   void set_transport_parameters(std::shared_ptr<QUICTransportParametersInEncryptedExtensions> tp);
   void set_transport_parameters(std::shared_ptr<QUICTransportParametersInNewSessionTicket> tp);
 
+  // A workaround API to indicate handshake msg type to QUICNetVConnection
+  QUICHandshakeMsgType msg_type() const;
+
 private:
   SSL *_ssl                                                             = nullptr;
   QUICHandshakeProtocol *_hs_protocol                                   = nullptr;
@@ -93,6 +97,9 @@ private:
   std::shared_ptr<QUICTransportParameters> _remote_transport_parameters = nullptr;
 
   QUICVersionNegotiator *_version_negotiator = nullptr;
+  NetVConnectionContext_t _netvc_context = NET_VCONNECTION_UNSET;
+  QUICStatelessResetToken _reset_token;
+  bool _stateless_retry = false;
 
   void _load_local_server_transport_parameters(QUICVersion negotiated_version);
   void _load_local_client_transport_parameters(QUICVersion initial_version);
@@ -106,7 +113,4 @@ private:
 
   int _complete_handshake();
   void _abort_handshake(QUICTransErrorCode code);
-
-  NetVConnectionContext_t _netvc_context = NET_VCONNECTION_UNSET;
-  QUICStatelessResetToken _reset_token;
 };
diff --git a/iocore/net/quic/QUICHandshakeProtocol.cc b/iocore/net/quic/QUICHandshakeProtocol.cc
index 4cb7a10..d73826a 100644
--- a/iocore/net/quic/QUICHandshakeProtocol.cc
+++ b/iocore/net/quic/QUICHandshakeProtocol.cc
@@ -79,3 +79,12 @@ QUICPacketProtection::key_phase() const
 {
   return this->_key_phase;
 }
+
+//
+// QUICHandshakeProtocol
+//
+QUICHandshakeMsgType
+QUICHandshakeProtocol::msg_type() const
+{
+  return this->_msg_type;
+}
diff --git a/iocore/net/quic/QUICHandshakeProtocol.h b/iocore/net/quic/QUICHandshakeProtocol.h
index 86412f4..3e4649d 100644
--- a/iocore/net/quic/QUICHandshakeProtocol.h
+++ b/iocore/net/quic/QUICHandshakeProtocol.h
@@ -59,4 +59,8 @@ public:
                        uint64_t pkt_num, const uint8_t *ad, size_t ad_len, QUICKeyPhase phase) const = 0;
   virtual bool decrypt(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, size_t cipher_len,
                        uint64_t pkt_num, const uint8_t *ad, size_t ad_len, QUICKeyPhase phase) const = 0;
+  virtual QUICHandshakeMsgType msg_type() const;
+
+protected:
+  QUICHandshakeMsgType _msg_type = QUICHandshakeMsgType::NONE;
 };
diff --git a/iocore/net/quic/QUICPacket.cc b/iocore/net/quic/QUICPacket.cc
index 35e8749..610a67b 100644
--- a/iocore/net/quic/QUICPacket.cc
+++ b/iocore/net/quic/QUICPacket.cc
@@ -782,14 +782,13 @@ QUICPacketFactory::create_initial_packet(QUICConnectionId connection_id, QUICPac
   return this->_create_encrypted_packet(std::move(header), true);
 }
 
-// retransmittable? depends on stateless?
 QUICPacketUPtr
 QUICPacketFactory::create_retry_packet(QUICConnectionId connection_id, QUICPacketNumber base_packet_number, ats_unique_buf payload,
-                                       size_t len)
+                                       size_t len, bool retransmittable)
 {
   QUICPacketHeaderUPtr header = QUICPacketHeader::build(QUICPacketType::RETRY, connection_id, this->_packet_number_generator.next(),
                                                         base_packet_number, this->_version, std::move(payload), len);
-  return this->_create_encrypted_packet(std::move(header), false);
+  return this->_create_encrypted_packet(std::move(header), retransmittable);
 }
 
 QUICPacketUPtr
diff --git a/iocore/net/quic/QUICPacket.h b/iocore/net/quic/QUICPacket.h
index c4b13cb..669cbea 100644
--- a/iocore/net/quic/QUICPacket.h
+++ b/iocore/net/quic/QUICPacket.h
@@ -343,7 +343,7 @@ public:
   QUICPacketUPtr create_initial_packet(QUICConnectionId connection_id, QUICPacketNumber base_packet_number, QUICVersion version,
                                        ats_unique_buf payload, size_t len);
   QUICPacketUPtr create_retry_packet(QUICConnectionId connection_id, QUICPacketNumber base_packet_number, ats_unique_buf payload,
-                                     size_t len);
+                                     size_t len, bool retransmittable);
   QUICPacketUPtr create_handshake_packet(QUICConnectionId connection_id, QUICPacketNumber base_packet_number,
                                          ats_unique_buf payload, size_t len, bool retransmittable);
   QUICPacketUPtr create_server_protected_packet(QUICConnectionId connection_id, QUICPacketNumber base_packet_number,
diff --git a/iocore/net/quic/QUICTLS.cc b/iocore/net/quic/QUICTLS.cc
index 74e8c46..50b2e73 100644
--- a/iocore/net/quic/QUICTLS.cc
+++ b/iocore/net/quic/QUICTLS.cc
@@ -79,7 +79,7 @@ QUICTLS::handshake(uint8_t *out, size_t &out_len, size_t max_out_len, const uint
     ERR_clear_error();
     int ret = 0;
     if (this->_netvc_context == NET_VCONNECTION_IN) {
-      // // process early data
+      // process early data
       if (!this->_early_data_processed) {
         if (this->_read_early_data()) {
           this->_early_data_processed = true;
@@ -106,14 +106,15 @@ QUICTLS::handshake(uint8_t *out, size_t &out_len, size_t max_out_len, const uint
         SSL_set_bio(this->_ssl, rbio, wbio);
 
         ret = SSL_stateless(this->_ssl);
-        if (ret >= 0) {
-          Debug(tag, "Sending HRR");
+        if (ret > 0) {
           this->_stateless = false;
-        } else {
-          Debug(tag, "SSL_stateless error");
+          this->_msg_type = QUICHandshakeMsgType::SH;
+        } else if (ret == 0) {
+          this->_msg_type = QUICHandshakeMsgType::HRR;
         }
       } else {
         ret = SSL_accept(this->_ssl);
+        this->_msg_type = QUICHandshakeMsgType::SH;
       }
     } else {
       ret = SSL_connect(this->_ssl);
@@ -165,12 +166,6 @@ QUICTLS::is_key_derived(QUICKeyPhase key_phase) const
   }
 }
 
-bool
-QUICTLS::is_stateless()
-{
-  return this->_stateless;
-}
-
 int
 QUICTLS::initialize_key_materials(QUICConnectionId cid)
 {
diff --git a/iocore/net/quic/QUICTLS.h b/iocore/net/quic/QUICTLS.h
index 20d1c61..4c6ebef 100644
--- a/iocore/net/quic/QUICTLS.h
+++ b/iocore/net/quic/QUICTLS.h
@@ -56,7 +56,6 @@ public:
 
   // FIXME SSL handle should not be exported
   SSL *ssl_handle();
-  bool is_stateless();
 
 private:
   QUICKeyGenerator _keygen_for_client = QUICKeyGenerator(QUICKeyGenerator::Context::CLIENT);
diff --git a/iocore/net/quic/QUICTypes.h b/iocore/net/quic/QUICTypes.h
index 25c1aab..2b4814d 100644
--- a/iocore/net/quic/QUICTypes.h
+++ b/iocore/net/quic/QUICTypes.h
@@ -136,6 +136,14 @@ enum class QUICTransErrorCode : uint16_t {
   TLS_FATAL_ALERT_RECEIVED,
 };
 
+enum class QUICHandshakeMsgType {
+  NONE = 0,
+  CH,
+  SH,
+  HRR,
+  FN,
+};
+
 // Application Protocol Error Codes defined in application
 using QUICAppErrorCode                          = uint16_t;
 constexpr uint16_t QUIC_APP_ERROR_CODE_STOPPING = 0;
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index 86e6af2..87531c7 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -1328,6 +1328,8 @@ static const RecordElement RecordsConfig[] =
   ,
   {RECT_CONFIG, "proxy.config.quic.connection_table.size", RECD_INT, "65521", RECU_RESTART_TS, RR_NULL, RECC_INT, "[1-536870909]", RECA_NULL}
   ,
+  {RECT_CONFIG, "proxy.config.quic.stateless_retry", RECD_INT, "0", RECU_RESTART_TS, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
+  ,
 
   //# Add LOCAL Records Here
   {RECT_LOCAL, "proxy.local.incoming_ip_to_bind", RECD_STRING, nullptr, RECU_NULL, RR_NULL, RECC_NULL, nullptr, RECA_NULL}

-- 
To stop receiving notification emails like this one, please contact
masaori@apache.org.