From 3252779e37c0ce6262ae29398f6f4efa3774b113 Mon Sep 17 00:00:00 2001
From: Daniel Salzman <daniel.salzman@nic.cz>
Date: Mon, 23 May 2022 17:19:55 +0200
Subject: [PATCH] contrib: upgrade embedded library ngtcp2 to v0.5.0

---
 src/contrib/Makefile.inc                      |   10 +
 src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c  |   11 +-
 src/contrib/libngtcp2/ngtcp2/crypto/shared.c  |  370 ++-
 src/contrib/libngtcp2/ngtcp2/crypto/shared.h  |  125 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_acktr.c       |   56 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_acktr.h       |   48 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_addr.c        |   52 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_balloc.c      |   90 +
 .../libngtcp2/ngtcp2/lib/ngtcp2_balloc.h      |   91 +
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c |    6 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h |    1 +
 .../libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c        |   16 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h        |    1 +
 .../libngtcp2/ngtcp2/lib/ngtcp2_conn.c        | 2125 ++++++++++++-----
 .../libngtcp2/ngtcp2/lib/ngtcp2_conn.h        |  251 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_conv.c        |    9 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_conv.h        |  107 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_crypto.c      |   50 +
 .../libngtcp2/ngtcp2/lib/ngtcp2_crypto.h      |    3 +
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c |    8 +
 .../libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c       |   84 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h       |   17 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_idtr.c        |   11 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_idtr.h        |    8 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c |  137 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h |   52 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c |  107 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c |   60 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h |    8 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c |   70 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h |   31 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h |  136 ++
 .../libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c    |   40 +
 .../libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h    |  140 ++
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c |   46 +
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h |   65 +
 .../libngtcp2/ngtcp2/lib/ngtcp2_path.c        |   16 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c |  166 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h |   49 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c       |  160 ++
 .../libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h       |  123 +
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c |    4 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c  |   30 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h  |    4 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_qlog.c        |  240 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_qlog.h        |   19 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c     |   20 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h     |   22 +
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c |   16 +-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c |  335 ++-
 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h |  181 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_strm.c        |   99 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_strm.h        |  141 +-
 .../libngtcp2/ngtcp2/lib/ngtcp2_version.c     |   39 +
 src/contrib/libngtcp2/ngtcp2/ngtcp2.h         |  995 ++++++--
 src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h  |  119 +-
 src/contrib/libngtcp2/ngtcp2/version.h        |    4 +-
 57 files changed, 5347 insertions(+), 1877 deletions(-)
 create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c
 create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h
 create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h
 create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c
 create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h
 create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c
 create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h
 create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c
 create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h
 create mode 100644 src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c

diff --git a/src/contrib/Makefile.inc b/src/contrib/Makefile.inc
index f107c4e66b..c0dc0fc7d4 100644
--- a/src/contrib/Makefile.inc
+++ b/src/contrib/Makefile.inc
@@ -178,6 +178,8 @@ libembngtcp2_la_SOURCES = \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h \
+	contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c \
+	contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c \
@@ -209,10 +211,17 @@ libembngtcp2_la_SOURCES = \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h \
+	contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h \
+	contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c \
+	contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h \
+	contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c \
+	contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h \
+	contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c \
+	contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.h \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_pq.c \
@@ -238,6 +247,7 @@ libembngtcp2_la_SOURCES = \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h \
+	contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.c \
 	contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h \
 	contrib/libngtcp2/ngtcp2/crypto/gnutls.c \
diff --git a/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c b/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c
index bc7c946b64..1f331b2c52 100644
--- a/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c
+++ b/src/contrib/libngtcp2/ngtcp2/crypto/gnutls.c
@@ -452,9 +452,14 @@ int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
   int rv;
 
   if (datalen > 0) {
-    if (gnutls_handshake_write(
-            session, ngtcp2_crypto_gnutls_from_ngtcp2_level(crypto_level), data,
-            datalen) != 0) {
+    rv = gnutls_handshake_write(
+        session, ngtcp2_crypto_gnutls_from_ngtcp2_level(crypto_level), data,
+        datalen);
+    if (rv != 0) {
+      if (!gnutls_error_is_fatal(rv)) {
+        return 0;
+      }
+      gnutls_alert_send_appropriate(session, rv);
       return -1;
     }
   }
diff --git a/src/contrib/libngtcp2/ngtcp2/crypto/shared.c b/src/contrib/libngtcp2/ngtcp2/crypto/shared.c
index 882e497802..56459f090c 100644
--- a/src/contrib/libngtcp2/ngtcp2/crypto/shared.c
+++ b/src/contrib/libngtcp2/ngtcp2/crypto/shared.c
@@ -35,6 +35,7 @@
 #include <assert.h>
 
 #include "ngtcp2_macro.h"
+#include "ngtcp2_net.h"
 
 ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md,
                                         void *md_native_handle) {
@@ -85,10 +86,16 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
 
   ngtcp2_crypto_ctx_initial(&ctx);
 
-  if (version == NGTCP2_PROTO_VER_V1) {
+  switch (version) {
+  case NGTCP2_PROTO_VER_V1:
     salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1;
     saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1;
-  } else {
+    break;
+  case NGTCP2_PROTO_VER_V2_DRAFT:
+    salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V2_DRAFT;
+    saltlen = sizeof(NGTCP2_INITIAL_SALT_V2_DRAFT) - 1;
+    break;
+  default:
     salt = (const uint8_t *)NGTCP2_INITIAL_SALT_DRAFT;
     saltlen = sizeof(NGTCP2_INITIAL_SALT_DRAFT) - 1;
   }
@@ -126,27 +133,55 @@ size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) {
 }
 
 int ngtcp2_crypto_derive_packet_protection_key(
-    uint8_t *key, uint8_t *iv, uint8_t *hp_key, const ngtcp2_crypto_aead *aead,
-    const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen) {
-  static const uint8_t KEY_LABEL[] = "quic key";
-  static const uint8_t IV_LABEL[] = "quic iv";
-  static const uint8_t HP_KEY_LABEL[] = "quic hp";
+    uint8_t *key, uint8_t *iv, uint8_t *hp_key, uint32_t version,
+    const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_md *md,
+    const uint8_t *secret, size_t secretlen) {
+  static const uint8_t KEY_LABEL_V1[] = "quic key";
+  static const uint8_t IV_LABEL_V1[] = "quic iv";
+  static const uint8_t HP_KEY_LABEL_V1[] = "quic hp";
+  static const uint8_t KEY_LABEL_V2_DRAFT[] = "quicv2 key";
+  static const uint8_t IV_LABEL_V2_DRAFT[] = "quicv2 iv";
+  static const uint8_t HP_KEY_LABEL_V2_DRAFT[] = "quicv2 hp";
   size_t keylen = ngtcp2_crypto_aead_keylen(aead);
   size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+  const uint8_t *key_label;
+  size_t key_labellen;
+  const uint8_t *iv_label;
+  size_t iv_labellen;
+  const uint8_t *hp_key_label;
+  size_t hp_key_labellen;
+
+  switch (version) {
+  case NGTCP2_PROTO_VER_V2_DRAFT:
+    key_label = KEY_LABEL_V2_DRAFT;
+    key_labellen = sizeof(KEY_LABEL_V2_DRAFT) - 1;
+    iv_label = IV_LABEL_V2_DRAFT;
+    iv_labellen = sizeof(IV_LABEL_V2_DRAFT) - 1;
+    hp_key_label = HP_KEY_LABEL_V2_DRAFT;
+    hp_key_labellen = sizeof(HP_KEY_LABEL_V2_DRAFT) - 1;
+    break;
+  default:
+    key_label = KEY_LABEL_V1;
+    key_labellen = sizeof(KEY_LABEL_V1) - 1;
+    iv_label = IV_LABEL_V1;
+    iv_labellen = sizeof(IV_LABEL_V1) - 1;
+    hp_key_label = HP_KEY_LABEL_V1;
+    hp_key_labellen = sizeof(HP_KEY_LABEL_V1) - 1;
+  }
 
   if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen,
-                                      KEY_LABEL, sizeof(KEY_LABEL) - 1) != 0) {
+                                      key_label, key_labellen) != 0) {
     return -1;
   }
 
   if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen,
-                                      IV_LABEL, sizeof(IV_LABEL) - 1) != 0) {
+                                      iv_label, iv_labellen) != 0) {
     return -1;
   }
 
-  if (hp_key != NULL && ngtcp2_crypto_hkdf_expand_label(
-                            hp_key, keylen, md, secret, secretlen, HP_KEY_LABEL,
-                            sizeof(HP_KEY_LABEL) - 1) != 0) {
+  if (hp_key != NULL &&
+      ngtcp2_crypto_hkdf_expand_label(hp_key, keylen, md, secret, secretlen,
+                                      hp_key_label, hp_key_labellen) != 0) {
     return -1;
   }
 
@@ -198,11 +233,22 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
     hp_key = hp_keybuf;
   }
 
-  if (level == NGTCP2_CRYPTO_LEVEL_EARLY) {
+  switch (level) {
+  case NGTCP2_CRYPTO_LEVEL_EARLY:
     ngtcp2_crypto_ctx_tls_early(&cctx, tls);
     ngtcp2_conn_set_early_crypto_ctx(conn, &cctx);
     ctx = ngtcp2_conn_get_early_crypto_ctx(conn);
-  } else {
+    break;
+  case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+    if (ngtcp2_conn_is_server(conn) &&
+        !ngtcp2_conn_get_negotiated_version(conn)) {
+      rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+      if (rv != 0) {
+        return -1;
+      }
+    }
+    /* fall through */
+  default:
     ctx = ngtcp2_conn_get_crypto_ctx(conn);
 
     if (!ctx->aead.native_handle) {
@@ -217,8 +263,9 @@ int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
   hp = &ctx->hp;
   ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
 
-  if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md,
-                                                 secret, secretlen) != 0) {
+  if (ngtcp2_crypto_derive_packet_protection_key(
+          key, iv, hp_key, ngtcp2_conn_get_negotiated_version(conn), aead, md,
+          secret, secretlen) != 0) {
     return -1;
   }
 
@@ -332,11 +379,22 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
     hp_key = hp_keybuf;
   }
 
-  if (level == NGTCP2_CRYPTO_LEVEL_EARLY) {
+  switch (level) {
+  case NGTCP2_CRYPTO_LEVEL_EARLY:
     ngtcp2_crypto_ctx_tls_early(&cctx, tls);
     ngtcp2_conn_set_early_crypto_ctx(conn, &cctx);
     ctx = ngtcp2_conn_get_early_crypto_ctx(conn);
-  } else {
+    break;
+  case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+    if (ngtcp2_conn_is_server(conn) &&
+        !ngtcp2_conn_get_negotiated_version(conn)) {
+      rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+      if (rv != 0) {
+        return -1;
+      }
+    }
+    /* fall through */
+  default:
     ctx = ngtcp2_conn_get_crypto_ctx(conn);
 
     if (!ctx->aead.native_handle) {
@@ -351,8 +409,9 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
   hp = &ctx->hp;
   ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
 
-  if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md,
-                                                 secret, secretlen) != 0) {
+  if (ngtcp2_crypto_derive_packet_protection_key(
+          key, iv, hp_key, ngtcp2_conn_get_negotiated_version(conn), aead, md,
+          secret, secretlen) != 0) {
     return -1;
   }
 
@@ -378,15 +437,9 @@ int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
       goto fail;
     }
 
-    if (ngtcp2_conn_is_server(conn)) {
-      rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
-      if (rv != 0) {
-        return rv;
-      }
-
-      if (crypto_set_local_transport_params(conn, tls) != 0) {
-        return rv;
-      }
+    if (ngtcp2_conn_is_server(conn) &&
+        crypto_set_local_transport_params(conn, tls) != 0) {
+      goto fail;
     }
 
     break;
@@ -415,7 +468,7 @@ int ngtcp2_crypto_derive_and_install_initial_key(
     ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
     uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv,
     uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key,
-    const ngtcp2_cid *client_dcid) {
+    uint32_t version, const ngtcp2_cid *client_dcid) {
   uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
   uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
   uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
@@ -434,7 +487,6 @@ int ngtcp2_crypto_derive_and_install_initial_key(
   ngtcp2_crypto_aead_ctx retry_aead_ctx = {0};
   int rv;
   int server = ngtcp2_conn_is_server(conn);
-  uint32_t version = ngtcp2_conn_get_negotiated_version(conn);
   const uint8_t *retry_key;
   size_t retry_noncelen;
 
@@ -479,13 +531,13 @@ int ngtcp2_crypto_derive_and_install_initial_key(
   }
 
   if (ngtcp2_crypto_derive_packet_protection_key(
-          rx_key, rx_iv, rx_hp_key, &ctx.aead, &ctx.md, rx_secret,
+          rx_key, rx_iv, rx_hp_key, version, &ctx.aead, &ctx.md, rx_secret,
           NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
     return -1;
   }
 
   if (ngtcp2_crypto_derive_packet_protection_key(
-          tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret,
+          tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret,
           NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
     return -1;
   }
@@ -513,10 +565,16 @@ int ngtcp2_crypto_derive_and_install_initial_key(
   if (!server && !ngtcp2_conn_after_retry(conn)) {
     ngtcp2_crypto_aead_retry(&retry_aead);
 
-    if (version == NGTCP2_PROTO_VER_V1) {
+    switch (version) {
+    case NGTCP2_PROTO_VER_V1:
       retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
       retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
-    } else {
+      break;
+    case NGTCP2_PROTO_VER_V2_DRAFT:
+      retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT;
+      retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
+      break;
+    default:
       retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
       retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
     }
@@ -550,6 +608,114 @@ fail:
   return -1;
 }
 
+int ngtcp2_crypto_derive_and_install_vneg_initial_key(
+    ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+    uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv,
+    uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key,
+    uint32_t version, const ngtcp2_cid *client_dcid) {
+  uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+  uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+  uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+  uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+  uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+  uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+  uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+  uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+  uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+  const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_initial_crypto_ctx(conn);
+  ngtcp2_crypto_aead_ctx rx_aead_ctx = {0};
+  ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0};
+  ngtcp2_crypto_aead_ctx tx_aead_ctx = {0};
+  ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0};
+  int rv;
+  int server = ngtcp2_conn_is_server(conn);
+
+  if (!rx_secret) {
+    rx_secret = rx_secretbuf;
+  }
+  if (!tx_secret) {
+    tx_secret = tx_secretbuf;
+  }
+  if (!initial_secret) {
+    initial_secret = initial_secretbuf;
+  }
+
+  if (!rx_key) {
+    rx_key = rx_keybuf;
+  }
+  if (!rx_iv) {
+    rx_iv = rx_ivbuf;
+  }
+  if (!rx_hp_key) {
+    rx_hp_key = rx_hp_keybuf;
+  }
+  if (!tx_key) {
+    tx_key = tx_keybuf;
+  }
+  if (!tx_iv) {
+    tx_iv = tx_ivbuf;
+  }
+  if (!tx_hp_key) {
+    tx_hp_key = tx_hp_keybuf;
+  }
+
+  if (ngtcp2_crypto_derive_initial_secrets(
+          version, rx_secret, tx_secret, initial_secret, client_dcid,
+          server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) !=
+      0) {
+    return -1;
+  }
+
+  if (ngtcp2_crypto_derive_packet_protection_key(
+          rx_key, rx_iv, rx_hp_key, version, &ctx->aead, &ctx->md, rx_secret,
+          NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+    return -1;
+  }
+
+  if (ngtcp2_crypto_derive_packet_protection_key(
+          tx_key, tx_iv, tx_hp_key, version, &ctx->aead, &ctx->md, tx_secret,
+          NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+    return -1;
+  }
+
+  if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx->aead, rx_key,
+                                          NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+    goto fail;
+  }
+
+  if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx->hp, rx_hp_key) !=
+      0) {
+    goto fail;
+  }
+
+  if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx->aead, tx_key,
+                                          NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+    goto fail;
+  }
+
+  if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx->hp, tx_hp_key) !=
+      0) {
+    goto fail;
+  }
+
+  rv = ngtcp2_conn_install_vneg_initial_key(
+      conn, version, &rx_aead_ctx, rx_iv, &rx_hp_ctx, &tx_aead_ctx, tx_iv,
+      &tx_hp_ctx, NGTCP2_CRYPTO_INITIAL_IVLEN);
+  if (rv != 0) {
+    goto fail;
+  }
+
+  return 0;
+
+fail:
+  ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx);
+  ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx);
+  ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx);
+  ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx);
+
+  return -1;
+}
+
 int ngtcp2_crypto_update_key(
     ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
     ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv,
@@ -560,14 +726,15 @@ int ngtcp2_crypto_update_key(
   const ngtcp2_crypto_aead *aead = &ctx->aead;
   const ngtcp2_crypto_md *md = &ctx->md;
   size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+  uint32_t version = ngtcp2_conn_get_negotiated_version(conn);
 
   if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret,
                                           secretlen) != 0) {
     return -1;
   }
 
-  if (ngtcp2_crypto_derive_packet_protection_key(rx_key, rx_iv, NULL, aead, md,
-                                                 rx_secret, secretlen) != 0) {
+  if (ngtcp2_crypto_derive_packet_protection_key(
+          rx_key, rx_iv, NULL, version, aead, md, rx_secret, secretlen) != 0) {
     return -1;
   }
 
@@ -576,8 +743,8 @@ int ngtcp2_crypto_update_key(
     return -1;
   }
 
-  if (ngtcp2_crypto_derive_packet_protection_key(tx_key, tx_iv, NULL, aead, md,
-                                                 tx_secret, secretlen) != 0) {
+  if (ngtcp2_crypto_derive_packet_protection_key(
+          tx_key, tx_iv, NULL, version, aead, md, tx_secret, secretlen) != 0) {
     return -1;
   }
 
@@ -711,12 +878,14 @@ static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv,
   return 0;
 }
 
-static size_t crypto_generate_retry_token_aad(uint8_t *dest,
-                                              const struct sockaddr *sa,
-                                              size_t salen,
+static size_t crypto_generate_retry_token_aad(uint8_t *dest, uint32_t version,
+                                              const ngtcp2_sockaddr *sa,
+                                              ngtcp2_socklen salen,
                                               const ngtcp2_cid *retry_scid) {
   uint8_t *p = dest;
 
+  version = ngtcp2_htonl(version);
+  memcpy(p, &version, sizeof(version));
   memcpy(p, sa, salen);
   p += salen;
   memcpy(p, retry_scid->data, retry_scid->datalen);
@@ -728,8 +897,8 @@ static size_t crypto_generate_retry_token_aad(uint8_t *dest,
 static const uint8_t retry_token_info_prefix[] = "retry_token";
 
 ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
-    uint8_t *token, const uint8_t *secret, size_t secretlen,
-    const struct sockaddr *remote_addr, size_t remote_addrlen,
+    uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version,
+    const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
     const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts) {
   uint8_t plaintext[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN];
   uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
@@ -741,9 +910,11 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
   ngtcp2_crypto_md md;
   ngtcp2_crypto_aead_ctx aead_ctx;
   size_t plaintextlen;
-  uint8_t aad[sizeof(struct sockaddr_storage) + NGTCP2_MAX_CIDLEN];
+  uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) +
+              NGTCP2_MAX_CIDLEN];
   size_t aadlen;
   uint8_t *p = plaintext;
+  ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts);
   int rv;
 
   memset(plaintext, 0, sizeof(plaintext));
@@ -751,9 +922,8 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
   *p++ = (uint8_t)odcid->datalen;
   memcpy(p, odcid->data, odcid->datalen);
   p += NGTCP2_MAX_CIDLEN;
-  /* Host byte order */
-  memcpy(p, &ts, sizeof(ts));
-  p += sizeof(ts);
+  memcpy(p, &ts_be, sizeof(ts_be));
+  p += sizeof(ts_be);
 
   plaintextlen = (size_t)(p - plaintext);
 
@@ -777,8 +947,8 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
     return -1;
   }
 
-  aadlen = crypto_generate_retry_token_aad(aad, remote_addr, remote_addrlen,
-                                           retry_scid);
+  aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr,
+                                           remote_addrlen, retry_scid);
 
   p = token;
   *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY;
@@ -805,9 +975,9 @@ ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
 
 int ngtcp2_crypto_verify_retry_token(
     ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
-    const uint8_t *secret, size_t secretlen, const struct sockaddr *remote_addr,
-    socklen_t remote_addrlen, const ngtcp2_cid *dcid, ngtcp2_duration timeout,
-    ngtcp2_tstamp ts) {
+    const uint8_t *secret, size_t secretlen, uint32_t version,
+    const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+    const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts) {
   uint8_t
       plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)];
   uint8_t key[32];
@@ -817,7 +987,8 @@ int ngtcp2_crypto_verify_retry_token(
   ngtcp2_crypto_aead_ctx aead_ctx;
   ngtcp2_crypto_aead aead;
   ngtcp2_crypto_md md;
-  uint8_t aad[sizeof(struct sockaddr_storage) + NGTCP2_MAX_CIDLEN];
+  uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) +
+              NGTCP2_MAX_CIDLEN];
   size_t aadlen;
   const uint8_t *rand_data;
   const uint8_t *ciphertext;
@@ -848,8 +1019,8 @@ int ngtcp2_crypto_verify_retry_token(
     return -1;
   }
 
-  aadlen =
-      crypto_generate_retry_token_aad(aad, remote_addr, remote_addrlen, dcid);
+  aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr,
+                                           remote_addrlen, dcid);
 
   if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
     return -1;
@@ -868,10 +1039,10 @@ int ngtcp2_crypto_verify_retry_token(
 
   assert(cil == 0 || (cil >= NGTCP2_MIN_CIDLEN && cil <= NGTCP2_MAX_CIDLEN));
 
-  /* Host byte order */
   memcpy(&gen_ts, plaintext + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN,
          sizeof(gen_ts));
 
+  gen_ts = ngtcp2_ntohl64(gen_ts);
   if (gen_ts + timeout <= ts) {
     return -1;
   }
@@ -882,19 +1053,19 @@ int ngtcp2_crypto_verify_retry_token(
 }
 
 static size_t crypto_generate_regular_token_aad(uint8_t *dest,
-                                                const struct sockaddr *sa) {
+                                                const ngtcp2_sockaddr *sa) {
   const uint8_t *addr;
   size_t addrlen;
 
   switch (sa->sa_family) {
   case AF_INET:
-    addr = (const uint8_t *)&((const struct sockaddr_in *)(void *)sa)->sin_addr;
-    addrlen = sizeof(((const struct sockaddr_in *)(void *)sa)->sin_addr);
+    addr = (const uint8_t *)&((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr;
+    addrlen = sizeof(((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr);
     break;
   case AF_INET6:
     addr =
-        (const uint8_t *)&((const struct sockaddr_in6 *)(void *)sa)->sin6_addr;
-    addrlen = sizeof(((const struct sockaddr_in6 *)(void *)sa)->sin6_addr);
+        (const uint8_t *)&((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr;
+    addrlen = sizeof(((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr);
     break;
   default:
     assert(0);
@@ -908,11 +1079,10 @@ static size_t crypto_generate_regular_token_aad(uint8_t *dest,
 
 static const uint8_t regular_token_info_prefix[] = "regular_token";
 
-ngtcp2_ssize
-ngtcp2_crypto_generate_regular_token(uint8_t *token, const uint8_t *secret,
-                                     size_t secretlen,
-                                     const struct sockaddr *remote_addr,
-                                     size_t remote_addrlen, ngtcp2_tstamp ts) {
+ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
+    uint8_t *token, const uint8_t *secret, size_t secretlen,
+    const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+    ngtcp2_tstamp ts) {
   uint8_t plaintext[sizeof(ngtcp2_tstamp)];
   uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
   uint8_t key[32];
@@ -923,15 +1093,15 @@ ngtcp2_crypto_generate_regular_token(uint8_t *token, const uint8_t *secret,
   ngtcp2_crypto_md md;
   ngtcp2_crypto_aead_ctx aead_ctx;
   size_t plaintextlen;
-  uint8_t aad[sizeof(struct sockaddr_in6)];
+  uint8_t aad[sizeof(ngtcp2_sockaddr_in6)];
   size_t aadlen;
   uint8_t *p = plaintext;
+  ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts);
   int rv;
   (void)remote_addrlen;
 
-  /* Host byte order */
-  memcpy(p, &ts, sizeof(ts));
-  p += sizeof(ts);
+  memcpy(p, &ts_be, sizeof(ts_be));
+  p += sizeof(ts_be);
 
   plaintextlen = (size_t)(p - plaintext);
 
@@ -982,8 +1152,8 @@ ngtcp2_crypto_generate_regular_token(uint8_t *token, const uint8_t *secret,
 
 int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
                                        const uint8_t *secret, size_t secretlen,
-                                       const struct sockaddr *remote_addr,
-                                       socklen_t remote_addrlen,
+                                       const ngtcp2_sockaddr *remote_addr,
+                                       ngtcp2_socklen remote_addrlen,
                                        ngtcp2_duration timeout,
                                        ngtcp2_tstamp ts) {
   uint8_t plaintext[sizeof(ngtcp2_tstamp)];
@@ -994,7 +1164,7 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
   ngtcp2_crypto_aead_ctx aead_ctx;
   ngtcp2_crypto_aead aead;
   ngtcp2_crypto_md md;
-  uint8_t aad[sizeof(struct sockaddr_in6)];
+  uint8_t aad[sizeof(ngtcp2_sockaddr_in6)];
   size_t aadlen;
   const uint8_t *rand_data;
   const uint8_t *ciphertext;
@@ -1040,9 +1210,9 @@ int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
     return -1;
   }
 
-  /* Host byte order */
   memcpy(&gen_ts, plaintext, sizeof(gen_ts));
 
+  gen_ts = ngtcp2_ntohl64(gen_ts);
   if (gen_ts + timeout <= ts) {
     return -1;
   }
@@ -1074,7 +1244,7 @@ ngtcp2_ssize ngtcp2_crypto_write_connection_close(
   }
 
   if (ngtcp2_crypto_derive_packet_protection_key(
-          tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret,
+          tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret,
           NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
     return -1;
   }
@@ -1118,10 +1288,16 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
 
   ngtcp2_crypto_aead_retry(&aead);
 
-  if (version == NGTCP2_PROTO_VER_V1) {
+  switch (version) {
+  case NGTCP2_PROTO_VER_V1:
     key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
     noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
-  } else {
+    break;
+  case NGTCP2_PROTO_VER_V2_DRAFT:
+    key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT;
+    noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
+    break;
+  default:
     key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
     noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
   }
@@ -1143,22 +1319,14 @@ ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
   return spktlen;
 }
 
-/*
- * crypto_setup_initial_crypto establishes the initial secrets and
- * encryption keys, and prepares local QUIC transport parameters.
- */
-static int crypto_setup_initial_crypto(ngtcp2_conn *conn,
-                                       const ngtcp2_cid *dcid) {
-  return ngtcp2_crypto_derive_and_install_initial_key(
-      conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid);
-}
-
 int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) {
   const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn);
   void *tls = ngtcp2_conn_get_tls_native_handle(conn);
   (void)user_data;
 
-  if (crypto_setup_initial_crypto(conn, dcid) != 0) {
+  if (ngtcp2_crypto_derive_and_install_initial_key(
+          conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+          ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
 
@@ -1178,9 +1346,9 @@ int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
                                 void *user_data) {
   (void)user_data;
 
-  if (ngtcp2_crypto_derive_and_install_initial_key(conn, NULL, NULL, NULL, NULL,
-                                                   NULL, NULL, NULL, NULL, NULL,
-                                                   &hd->scid) != 0) {
+  if (ngtcp2_crypto_derive_and_install_initial_key(
+          conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+          ngtcp2_conn_get_client_chosen_version(conn), &hd->scid) != 0) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
 
@@ -1192,7 +1360,23 @@ int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
                                          void *user_data) {
   (void)user_data;
 
-  if (crypto_setup_initial_crypto(conn, dcid) != 0) {
+  if (ngtcp2_crypto_derive_and_install_initial_key(
+          conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+          ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
+  return 0;
+}
+
+int ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version,
+                                         const ngtcp2_cid *client_dcid,
+                                         void *user_data) {
+  (void)user_data;
+
+  if (ngtcp2_crypto_derive_and_install_vneg_initial_key(
+          conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, version,
+          client_dcid) != 0) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
 
diff --git a/src/contrib/libngtcp2/ngtcp2/crypto/shared.h b/src/contrib/libngtcp2/ngtcp2/crypto/shared.h
index d282fd0057..02b948901a 100644
--- a/src/contrib/libngtcp2/ngtcp2/crypto/shared.h
+++ b/src/contrib/libngtcp2/ngtcp2/crypto/shared.h
@@ -51,6 +51,16 @@
   "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb"   \
   "\x7f\x0a"
 
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INITIAL_SALT_V2_DRAFT` is a salt value which is used to
+ * derive initial secret.  It is used for QUIC v2 draft.
+ */
+#define NGTCP2_INITIAL_SALT_V2_DRAFT                                           \
+  "\xa7\x07\xc2\x03\xa5\x9b\x47\x18\x4a\x1d\x62\xca\x57\x04\x06\xea\x7a\xe3"   \
+  "\xe5\xd3"
+
 /* Maximum key usage (encryption) limits */
 #define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23)
 #define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62)
@@ -62,6 +72,40 @@
 #define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36)
 #define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (2965820ULL)
 
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet
+ * encryption and decryption.
+ */
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_init` initializes |aead| with the provided
+ * |aead_native_handle| which is an underlying AEAD object.
+ *
+ * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be
+ * a pointer to EVP_CIPHER.
+ *
+ * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be
+ * gnutls_cipher_algorithm_t casted to ``void *``.
+ *
+ * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must
+ * be a pointer to EVP_AEAD.
+ */
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+                                            void *aead_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher
+ * AEAD_AES_128_GCM for Retry packet integrity protection.
+ */
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead);
+
 /**
  * @function
  *
@@ -84,6 +128,34 @@ int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
                                          const ngtcp2_cid *client_dcid,
                                          ngtcp2_crypto_side side);
 
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_packet_protection_key` derives packet
+ * protection key.  This function writes packet protection key into
+ * the buffer pointed by |key|.  The length of derived key is
+ * `ngtcp2_crypto_aead_keylen(aead) <ngtcp2_crypto_aead_keylen>`
+ * bytes.  |key| must have enough capacity to store the key.  This
+ * function writes packet protection IV into |iv|.  The length of
+ * derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` bytes.  |iv| must have
+ * enough capacity to store the IV.
+ *
+ * If |hp| is not NULL, this function also derives packet header
+ * protection key and writes the key into the buffer pointed by |hp|.
+ * The length of derived key is `ngtcp2_crypto_aead_keylen(aead)
+ * <ngtcp2_crypto_aead_keylen>` bytes.  |hp|, if not NULL, must have
+ * enough capacity to store the key.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_packet_protection_key(uint8_t *key, uint8_t *iv,
+                                               uint8_t *hp, uint32_t version,
+                                               const ngtcp2_crypto_aead *aead,
+                                               const ngtcp2_crypto_md *md,
+                                               const uint8_t *secret,
+                                               size_t secretlen);
+
 /**
  * @function
  *
@@ -183,7 +255,58 @@ int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls);
 int ngtcp2_crypto_derive_and_install_initial_key(
     ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
     uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp,
-    uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp,
+    uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version,
+    const ngtcp2_cid *client_dcid);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_vneg_initial_key` derives initial
+ * keying materials and installs keys to |conn|.  This function is
+ * dedicated to install keys for |version| which is negotiated, or
+ * being negotiated.
+ *
+ * If |rx_secret| is not NULL, the secret for decryption is written to
+ * the buffer pointed by |rx_secret|.  The length of secret is 32
+ * bytes, and |rx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |tx_secret| is not NULL, the secret for encryption is written to
+ * the buffer pointed by |tx_secret|.  The length of secret is 32
+ * bytes, and |tx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |initial_secret| is not NULL, the initial secret is written to
+ * the buffer pointed by |initial_secret|.  The length of secret is 32
+ * bytes, and |initial_secret| must point to the buffer which has
+ * enough capacity.
+ *
+ * |client_dcid| is the destination connection ID in first Initial
+ * packet of client.
+ *
+ * If |rx_key| is not NULL, the derived packet protection key for
+ * decryption is written to the buffer pointed by |rx_key|.  If
+ * |rx_iv| is not NULL, the derived packet protection IV for
+ * decryption is written to the buffer pointed by |rx_iv|.  If |rx_hp|
+ * is not NULL, the derived header protection key for decryption is
+ * written to the buffer pointed by |rx_hp|.
+ *
+ * If |tx_key| is not NULL, the derived packet protection key for
+ * encryption is written to the buffer pointed by |tx_key|.  If
+ * |tx_iv| is not NULL, the derived packet protection IV for
+ * encryption is written to the buffer pointed by |tx_iv|.  If |tx_hp|
+ * is not NULL, the derived header protection key for encryption is
+ * written to the buffer pointed by |tx_hp|.
+ *
+ * The length of packet protection key and header protection key is 16
+ * bytes long.  The length of packet protection IV is 12 bytes long.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_and_install_vneg_initial_key(
+    ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+    uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp,
+    uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version,
     const ngtcp2_cid *client_dcid);
 
 /**
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c
index 6d646517a8..02c45de90d 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c
@@ -26,22 +26,31 @@
 
 #include <assert.h>
 
-int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
-                           ngtcp2_tstamp tstamp, const ngtcp2_mem *mem) {
-  *ent = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_acktr_entry));
+#include "ngtcp2_macro.h"
+
+static void acktr_entry_init(ngtcp2_acktr_entry *ent, int64_t pkt_num,
+                             ngtcp2_tstamp tstamp) {
+  ent->pkt_num = pkt_num;
+  ent->len = 1;
+  ent->tstamp = tstamp;
+}
+
+int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
+                                    ngtcp2_tstamp tstamp,
+                                    ngtcp2_objalloc *objalloc) {
+  *ent = ngtcp2_objalloc_acktr_entry_get(objalloc);
   if (*ent == NULL) {
     return NGTCP2_ERR_NOMEM;
   }
 
-  (*ent)->pkt_num = pkt_num;
-  (*ent)->len = 1;
-  (*ent)->tstamp = tstamp;
+  acktr_entry_init(*ent, pkt_num, tstamp);
 
   return 0;
 }
 
-void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) {
-  ngtcp2_mem_free(mem, ent);
+void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent,
+                                     ngtcp2_objalloc *objalloc) {
+  ngtcp2_objalloc_acktr_entry_release(objalloc, ent);
 }
 
 static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
@@ -52,17 +61,15 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
                       const ngtcp2_mem *mem) {
   int rv;
 
-  rv = ngtcp2_ringbuf_init(&acktr->acks, 128, sizeof(ngtcp2_acktr_ack_entry),
+  ngtcp2_objalloc_acktr_entry_init(&acktr->objalloc, 32, mem);
+
+  rv = ngtcp2_ringbuf_init(&acktr->acks, 32, sizeof(ngtcp2_acktr_ack_entry),
                            mem);
   if (rv != 0) {
     return rv;
   }
 
-  rv = ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem);
-  if (rv != 0) {
-    ngtcp2_ringbuf_free(&acktr->acks);
-    return rv;
-  }
+  ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem);
 
   acktr->log = log;
   acktr->mem = mem;
@@ -74,19 +81,26 @@ int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log,
 }
 
 void ngtcp2_acktr_free(ngtcp2_acktr *acktr) {
+#ifdef NOMEMPOOL
   ngtcp2_ksl_it it;
+#endif /* NOMEMPOOL */
 
   if (acktr == NULL) {
     return;
   }
 
+#ifdef NOMEMPOOL
   for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it);
        ngtcp2_ksl_it_next(&it)) {
-    ngtcp2_acktr_entry_del(ngtcp2_ksl_it_get(&it), acktr->mem);
+    ngtcp2_acktr_entry_objalloc_del(ngtcp2_ksl_it_get(&it), &acktr->objalloc);
   }
+#endif /* NOMEMPOOL */
+
   ngtcp2_ksl_free(&acktr->ents);
 
   ngtcp2_ringbuf_free(&acktr->acks);
+
+  ngtcp2_objalloc_free(&acktr->objalloc);
 }
 
 int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
@@ -132,7 +146,7 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
           if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) {
             prev_ent->len += ent->len + 1;
             ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &ent->pkt_num);
-            ngtcp2_acktr_entry_del(ent, acktr->mem);
+            ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
             added = 1;
           } else {
             ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num);
@@ -150,13 +164,13 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
   }
 
   if (!added) {
-    rv = ngtcp2_acktr_entry_new(&ent, pkt_num, ts, acktr->mem);
+    rv = ngtcp2_acktr_entry_objalloc_new(&ent, pkt_num, ts, &acktr->objalloc);
     if (rv != 0) {
       return rv;
     }
     rv = ngtcp2_ksl_insert(&acktr->ents, NULL, &ent->pkt_num, ent);
     if (rv != 0) {
-      ngtcp2_acktr_entry_del(ent, acktr->mem);
+      ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
       return rv;
     }
   }
@@ -173,7 +187,7 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack,
     ngtcp2_ksl_it_prev(&it);
     delent = ngtcp2_ksl_it_get(&it);
     ngtcp2_ksl_remove_hint(&acktr->ents, NULL, &it, &delent->pkt_num);
-    ngtcp2_acktr_entry_del(delent, acktr->mem);
+    ngtcp2_acktr_entry_objalloc_del(delent, &acktr->objalloc);
   }
 
   return 0;
@@ -188,7 +202,7 @@ void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) {
   for (; !ngtcp2_ksl_it_end(&it);) {
     ent = ngtcp2_ksl_it_get(&it);
     ngtcp2_ksl_remove_hint(&acktr->ents, &it, &it, &ent->pkt_num);
-    ngtcp2_acktr_entry_del(ent, acktr->mem);
+    ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
   }
 }
 
@@ -220,7 +234,7 @@ ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr,
 static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it,
                          ngtcp2_acktr_entry *ent) {
   ngtcp2_ksl_remove_hint(&acktr->ents, it, it, &ent->pkt_num);
-  ngtcp2_acktr_entry_del(ent, acktr->mem);
+  ngtcp2_acktr_entry_objalloc_del(ent, &acktr->objalloc);
 }
 
 static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb,
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h
index 40a4a4e25e..2fe9dad530 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h
@@ -35,6 +35,7 @@
 #include "ngtcp2_ringbuf.h"
 #include "ngtcp2_ksl.h"
 #include "ngtcp2_pkt.h"
+#include "ngtcp2_objalloc.h"
 
 /* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry
    which ngtcp2_acktr stores. */
@@ -46,22 +47,30 @@ typedef struct ngtcp2_log ngtcp2_log;
  * ngtcp2_acktr_entry is a range of packets which need to be acked.
  */
 typedef struct ngtcp2_acktr_entry {
-  /* pkt_num is the largest packet number to acknowledge in this
-     range. */
-  int64_t pkt_num;
-  /* len is the consecutive packets started from pkt_num which
-     includes pkt_num itself counting in decreasing order.  So pkt_num
-     = 987 and len = 2, this entry includes packet 987 and 986. */
-  size_t len;
-  /* tstamp is the timestamp when a packet denoted by pkt_num is
-     received. */
-  ngtcp2_tstamp tstamp;
+  union {
+    struct {
+      /* pkt_num is the largest packet number to acknowledge in this
+         range. */
+      int64_t pkt_num;
+      /* len is the consecutive packets started from pkt_num which
+         includes pkt_num itself counting in decreasing order.  So pkt_num
+         = 987 and len = 2, this entry includes packet 987 and 986. */
+      size_t len;
+      /* tstamp is the timestamp when a packet denoted by pkt_num is
+         received. */
+      ngtcp2_tstamp tstamp;
+    };
+
+    ngtcp2_opl_entry oplent;
+  };
 } ngtcp2_acktr_entry;
 
+ngtcp2_objalloc_def(acktr_entry, ngtcp2_acktr_entry, oplent);
+
 /*
- * ngtcp2_acktr_entry_new allocates memory for ent, and initializes it
- * with the given parameters.  The pointer to the allocated object is
- * stored to |*ent|.
+ * ngtcp2_acktr_entry_objalloc_new allocates memory for ent, and
+ * initializes it with the given parameters.  The pointer to the
+ * allocated object is stored to |*ent|.
  *
  * This function returns 0 if it succeeds, or one of the following
  * negative error codes:
@@ -69,14 +78,16 @@ typedef struct ngtcp2_acktr_entry {
  * NGTCP2_ERR_NOMEM
  *     Out of memory.
  */
-int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
-                           ngtcp2_tstamp tstamp, const ngtcp2_mem *mem);
+int ngtcp2_acktr_entry_objalloc_new(ngtcp2_acktr_entry **ent, int64_t pkt_num,
+                                    ngtcp2_tstamp tstamp,
+                                    ngtcp2_objalloc *objalloc);
 
 /*
- * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|.  It
- * deallocates memory pointed by |ent|.
+ * ngtcp2_acktr_entry_objalloc_del deallocates memory allocated for
+ * |ent|.
  */
-void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem);
+void ngtcp2_acktr_entry_objalloc_del(ngtcp2_acktr_entry *ent,
+                                     ngtcp2_objalloc *objalloc);
 
 typedef struct ngtcp2_acktr_ack_entry {
   /* largest_ack is the largest packet number in outgoing ACK frame */
@@ -101,6 +112,7 @@ typedef struct ngtcp2_acktr_ack_entry {
  * ngtcp2_acktr tracks received packets which we have to send ack.
  */
 typedef struct ngtcp2_acktr {
+  ngtcp2_objalloc objalloc;
   ngtcp2_ringbuf acks;
   /* ents includes ngtcp2_acktr_entry sorted by decreasing order of
      packet number. */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c
index 27197a694d..e052a9036c 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c
@@ -27,22 +27,10 @@
 #include <string.h>
 #include <assert.h>
 
-#ifdef WIN32
-#  include <winsock2.h>
-#  include <ws2tcpip.h>
-#else
-#  include <sys/types.h>
-#  include <sys/socket.h>
-#  include <netinet/in.h>
-#  include <netinet/ip.h>
-#  include <netinet/tcp.h>
-#  include <arpa/inet.h>
-#endif
-
-ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const struct sockaddr *addr,
-                              size_t addrlen) {
+ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr,
+                              ngtcp2_socklen addrlen) {
   dest->addrlen = addrlen;
-  dest->addr = (struct sockaddr *)addr;
+  dest->addr = (ngtcp2_sockaddr *)addr;
   return dest;
 }
 
@@ -53,27 +41,27 @@ void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src) {
   }
 }
 
-void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr,
-                           size_t addrlen) {
+void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr,
+                           ngtcp2_socklen addrlen) {
   dest->addrlen = addrlen;
   if (addrlen) {
     memcpy(dest->addr, addr, addrlen);
   }
 }
 
-static int sockaddr_eq(const struct sockaddr *a, const struct sockaddr *b) {
+static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) {
   assert(a->sa_family == b->sa_family);
 
   switch (a->sa_family) {
-  case AF_INET: {
-    const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a,
-                             *bi = (const struct sockaddr_in *)(void *)b;
+  case NGTCP2_AF_INET: {
+    const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
+                             *bi = (const ngtcp2_sockaddr_in *)(void *)b;
     return ai->sin_port == bi->sin_port &&
            memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0;
   }
-  case AF_INET6: {
-    const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a,
-                              *bi = (const struct sockaddr_in6 *)(void *)b;
+  case NGTCP2_AF_INET6: {
+    const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
+                              *bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
     return ai->sin6_port == bi->sin6_port &&
            memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0;
   }
@@ -90,17 +78,17 @@ int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) {
 
 uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
   uint32_t flags = NGTCP2_ADDR_COMPARE_FLAG_NONE;
-  const struct sockaddr *a = aa->addr;
-  const struct sockaddr *b = bb->addr;
+  const ngtcp2_sockaddr *a = aa->addr;
+  const ngtcp2_sockaddr *b = bb->addr;
 
   if (a->sa_family != b->sa_family) {
     return NGTCP2_ADDR_COMPARE_FLAG_FAMILY;
   }
 
   switch (a->sa_family) {
-  case AF_INET: {
-    const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a,
-                             *bi = (const struct sockaddr_in *)(void *)b;
+  case NGTCP2_AF_INET: {
+    const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
+                             *bi = (const ngtcp2_sockaddr_in *)(void *)b;
     if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) {
       flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR;
     }
@@ -109,9 +97,9 @@ uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
     }
     return flags;
   }
-  case AF_INET6: {
-    const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a,
-                              *bi = (const struct sockaddr_in6 *)(void *)b;
+  case NGTCP2_AF_INET6: {
+    const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
+                              *bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
     if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) {
       flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR;
     }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c
new file mode 100644
index 0000000000..5623e56566
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c
@@ -0,0 +1,90 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_balloc.h"
+
+#include <assert.h>
+
+#include "ngtcp2_mem.h"
+
+void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen,
+                        const ngtcp2_mem *mem) {
+  assert(((uintptr_t)blklen & 0xfllu) == 0);
+
+  balloc->mem = mem;
+  balloc->blklen = blklen;
+  balloc->head = NULL;
+  ngtcp2_buf_init(&balloc->buf, (void *)"", 0);
+}
+
+void ngtcp2_balloc_free(ngtcp2_balloc *balloc) {
+  if (balloc == NULL) {
+    return;
+  }
+
+  ngtcp2_balloc_clear(balloc);
+}
+
+void ngtcp2_balloc_clear(ngtcp2_balloc *balloc) {
+  ngtcp2_memblock_hd *p, *next;
+
+  for (p = balloc->head; p; p = next) {
+    next = p->next;
+    ngtcp2_mem_free(balloc->mem, p);
+  }
+
+  balloc->head = NULL;
+  ngtcp2_buf_init(&balloc->buf, (void *)"", 0);
+}
+
+int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) {
+  uint8_t *p;
+  ngtcp2_memblock_hd *hd;
+
+  assert(n <= balloc->blklen);
+
+  if (ngtcp2_buf_left(&balloc->buf) < n) {
+    p = ngtcp2_mem_malloc(balloc->mem, sizeof(ngtcp2_memblock_hd) + 0x10llu +
+                                           balloc->blklen);
+    if (p == NULL) {
+      return NGTCP2_ERR_NOMEM;
+    }
+
+    hd = (ngtcp2_memblock_hd *)(void *)p;
+    hd->next = balloc->head;
+    balloc->head = hd;
+    ngtcp2_buf_init(
+        &balloc->buf,
+        (uint8_t *)(((uintptr_t)p + sizeof(ngtcp2_memblock_hd) + 0xfllu) &
+                    ~0xfllu),
+        balloc->blklen);
+  }
+
+  assert(((uintptr_t)balloc->buf.last & 0xfllu) == 0);
+
+  *pbuf = balloc->buf.last;
+  balloc->buf.last += (n + 0xfllu) & ~0xfllu;
+
+  return 0;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h
new file mode 100644
index 0000000000..1fb16325d9
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h
@@ -0,0 +1,91 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_BALLOC_H
+#define NGTCP2_BALLOC_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_buf.h"
+
+typedef struct ngtcp2_memblock_hd ngtcp2_memblock_hd;
+
+/*
+ * ngtcp2_memblock_hd is the header of memory block.
+ */
+struct ngtcp2_memblock_hd {
+  ngtcp2_memblock_hd *next;
+};
+
+/*
+ * ngtcp2_balloc is a custom memory allocator.  It allocates |blklen|
+ * bytes of memory at once on demand, and returns its slice when the
+ * allocation is requested.
+ */
+typedef struct ngtcp2_balloc {
+  /* mem is the underlying memory allocator. */
+  const ngtcp2_mem *mem;
+  /* blklen is the size of memory block. */
+  size_t blklen;
+  /* head points to the list of memory block allocated so far. */
+  ngtcp2_memblock_hd *head;
+  /* buf wraps the current memory block for allocation requests. */
+  ngtcp2_buf buf;
+} ngtcp2_balloc;
+
+/*
+ * ngtcp2_balloc_init initializes |balloc| with |blklen| which is the
+ * size of memory block.
+ */
+void ngtcp2_balloc_init(ngtcp2_balloc *balloc, size_t blklen,
+                        const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_balloc_free releases all allocated memory blocks.
+ */
+void ngtcp2_balloc_free(ngtcp2_balloc *balloc);
+
+/*
+ * ngtcp2_balloc_get allocates |n| bytes of memory and assigns its
+ * pointer to |*pbuf|.
+ *
+ * It returns 0 if it succeeds, or one of the following negative error
+ * codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ *     Out of memory.
+ */
+int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n);
+
+/*
+ * ngtcp2_balloc_clear releases all allocated memory blocks and
+ * initializes its state.
+ */
+void ngtcp2_balloc_clear(ngtcp2_balloc *balloc);
+
+#endif /* NGTCP2_BALLOC_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c
index 082fb740e7..0816d69b81 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c
@@ -291,7 +291,7 @@ static void bbr_update_round(ngtcp2_bbr_cc *cc, const ngtcp2_cc_ack *ack) {
 static void bbr_handle_recovery(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
                                 const ngtcp2_cc_ack *ack) {
   if (cc->in_loss_recovery) {
-    if (cc->round_start) {
+    if (ack->pkt_delivered >= cc->congestion_recovery_next_round_delivered) {
       cc->packet_conservation = 0;
     }
 
@@ -313,6 +313,7 @@ static void bbr_handle_recovery(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
     cstat->congestion_recovery_start_ts = cc->congestion_recovery_start_ts;
     cc->congestion_recovery_start_ts = UINT64_MAX;
     cc->packet_conservation = 1;
+    cc->congestion_recovery_next_round_delivered = cc->rst->delivered;
   }
 }
 
@@ -412,7 +413,7 @@ static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr_cc *cc,
   if (ack->bytes_lost > 0) {
     if (cstat->cwnd > ack->bytes_lost) {
       cstat->cwnd -= ack->bytes_lost;
-      cstat->cwnd = ngtcp2_max(cstat->cwnd, cstat->max_udp_payload_size);
+      cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size);
     } else {
       cstat->cwnd = cstat->max_udp_payload_size;
     }
@@ -487,6 +488,7 @@ static void bbr_init(ngtcp2_bbr_cc *cc, ngtcp2_conn_stat *cstat,
   cc->probe_rtt_round_done = 0;
 
   cc->congestion_recovery_start_ts = UINT64_MAX;
+  cc->congestion_recovery_next_round_delivered = 0;
   cc->in_loss_recovery = 0;
 
   cstat->send_quantum = cstat->max_udp_payload_size * 10;
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h
index 2de9acc824..7311f051e1 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h
@@ -90,6 +90,7 @@ typedef struct ngtcp2_bbr_cc {
   /* congestion_recovery_start_ts is the time when congestion recovery
      period started.*/
   ngtcp2_tstamp congestion_recovery_start_ts;
+  uint64_t congestion_recovery_next_round_delivered;
   size_t full_bw_count;
   size_t cycle_index;
   ngtcp2_bbr_state state;
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c
index 6d09b81997..616e944354 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.c
@@ -336,6 +336,7 @@ static void bbr_on_init(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
   bbr->max_inflight = 0;
 
   bbr->congestion_recovery_start_ts = UINT64_MAX;
+  bbr->congestion_recovery_next_round_delivered = 0;
 
   bbr->prior_inflight_lo = 0;
   bbr->prior_inflight_hi = 0;
@@ -530,7 +531,7 @@ static void bbr_update_congestion_signals(ngtcp2_bbr2_cc *bbr,
 
 static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_bbr2_cc *bbr,
                                                    ngtcp2_conn_stat *cstat) {
-  if (bbr_is_in_probe_bw_state(bbr)) {
+  if (!bbr->filled_pipe || bbr_is_in_probe_bw_state(bbr)) {
     return;
   }
 
@@ -672,7 +673,7 @@ static void bbr_start_probe_bw_down(ngtcp2_bbr2_cc *bbr, ngtcp2_tstamp ts) {
   bbr_start_round(bbr);
 
   bbr->state = NGTCP2_BBR2_STATE_PROBE_BW_DOWN;
-  bbr->pacing_gain = 0.75;
+  bbr->pacing_gain = 0.9;
   bbr->cwnd_gain = 2;
 }
 
@@ -770,6 +771,8 @@ static void bbr_update_probe_bw_cycle_phase(ngtcp2_bbr2_cc *bbr,
 
 static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr,
                                     ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
+  (void)ts;
+
   if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) {
     return 0;
   }
@@ -778,7 +781,7 @@ static int bbr_check_time_to_cruise(ngtcp2_bbr2_cc *bbr,
     return 1;
   }
 
-  return bbr_has_elapsed_in_phase(bbr, bbr->min_rtt, ts);
+  return 0;
 }
 
 static int bbr_has_elapsed_in_phase(ngtcp2_bbr2_cc *bbr,
@@ -1192,7 +1195,7 @@ static void bbr_modulate_cwnd_for_recovery(ngtcp2_bbr2_cc *bbr,
   if (ack->bytes_lost > 0) {
     if (cstat->cwnd > ack->bytes_lost) {
       cstat->cwnd -= ack->bytes_lost;
-      cstat->cwnd = ngtcp2_max(cstat->cwnd, cstat->max_udp_payload_size);
+      cstat->cwnd = ngtcp2_max(cstat->cwnd, 2 * cstat->max_udp_payload_size);
     } else {
       cstat->cwnd = cstat->max_udp_payload_size;
     }
@@ -1301,7 +1304,7 @@ static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
 static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
                                 const ngtcp2_cc_ack *ack) {
   if (bbr->in_loss_recovery) {
-    if (bbr->round_start) {
+    if (ack->pkt_delivered >= bbr->congestion_recovery_next_round_delivered) {
       bbr->packet_conservation = 0;
     }
 
@@ -1323,6 +1326,7 @@ static void bbr_handle_recovery(ngtcp2_bbr2_cc *bbr, ngtcp2_conn_stat *cstat,
     cstat->congestion_recovery_start_ts = bbr->congestion_recovery_start_ts;
     bbr->congestion_recovery_start_ts = UINT64_MAX;
     bbr->packet_conservation = 1;
+    bbr->congestion_recovery_next_round_delivered = bbr->rst->delivered;
     bbr->prior_inflight_lo = bbr->inflight_lo;
     bbr->prior_bw_lo = bbr->bw_lo;
   }
@@ -1362,7 +1366,7 @@ static void bbr2_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat,
                                      ngtcp2_tstamp sent_ts, ngtcp2_tstamp ts) {
   ngtcp2_bbr2_cc *bbr = ngtcp2_struct_of(ccx->ccb, ngtcp2_bbr2_cc, ccb);
 
-  if (bbr->in_loss_recovery ||
+  if (!bbr->filled_pipe || bbr->in_loss_recovery ||
       bbr->congestion_recovery_start_ts != UINT64_MAX ||
       in_congestion_recovery(cstat, sent_ts)) {
     return;
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h
index ebc3cef1bd..50dc05a5f2 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr2.h
@@ -131,6 +131,7 @@ typedef struct ngtcp2_bbr2_cc {
   int packet_conservation;
   uint64_t max_inflight;
   ngtcp2_tstamp congestion_recovery_start_ts;
+  uint64_t congestion_recovery_next_round_delivered;
 
   uint64_t prior_inflight_lo;
   uint64_t prior_inflight_hi;
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c
index 15c565f2f8..aa959fab60 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c
@@ -60,6 +60,15 @@ static int conn_local_stream(ngtcp2_conn *conn, int64_t stream_id) {
  */
 static int bidi_stream(int64_t stream_id) { return (stream_id & 0x2) == 0; }
 
+/*
+ * conn_is_handshake_completed returns nonzero if QUIC handshake has
+ * completed.
+ */
+static int conn_is_handshake_completed(ngtcp2_conn *conn) {
+  return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) &&
+         conn->pktns.crypto.rx.ckm && conn->pktns.crypto.tx.ckm;
+}
+
 static int conn_call_recv_client_initial(ngtcp2_conn *conn,
                                          const ngtcp2_cid *dcid) {
   int rv;
@@ -125,6 +134,8 @@ static int conn_call_recv_crypto_data(ngtcp2_conn *conn,
   case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
   case NGTCP2_ERR_TRANSPORT_PARAM:
   case NGTCP2_ERR_PROTO:
+  case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+  case NGTCP2_ERR_NOMEM:
   case NGTCP2_ERR_CALLBACK_FAILURE:
     return rv;
   default:
@@ -545,7 +556,7 @@ static int conn_call_recv_datagram(ngtcp2_conn *conn,
     datalen = 0;
   }
 
-  if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+  if (!conn_is_handshake_completed(conn)) {
     flags |= NGTCP2_DATAGRAM_FLAG_EARLY;
   }
 
@@ -578,6 +589,21 @@ conn_call_update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
   return 0;
 }
 
+static int conn_call_version_negotiation(ngtcp2_conn *conn, uint32_t version,
+                                         const ngtcp2_cid *dcid) {
+  int rv;
+
+  assert(conn->callbacks.version_negotiation);
+
+  rv =
+      conn->callbacks.version_negotiation(conn, version, dcid, conn->user_data);
+  if (rv != 0) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
+  return 0;
+}
+
 static int crypto_offset_less(const ngtcp2_ksl_key *lhs,
                               const ngtcp2_ksl_key *rhs) {
   return *(int64_t *)lhs < *(int64_t *)rhs;
@@ -585,15 +611,13 @@ static int crypto_offset_less(const ngtcp2_ksl_key *lhs,
 
 static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id,
                       ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log,
-                      ngtcp2_qlog *qlog, const ngtcp2_mem *mem) {
+                      ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc,
+                      ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
   int rv;
 
   memset(pktns, 0, sizeof(*pktns));
 
-  rv = ngtcp2_gaptr_init(&pktns->rx.pngap, mem);
-  if (rv != 0) {
-    return rv;
-  }
+  ngtcp2_gaptr_init(&pktns->rx.pngap, mem);
 
   pktns->tx.last_pkt_num = -1;
   pktns->rx.max_pkt_num = -1;
@@ -604,27 +628,17 @@ static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id,
     goto fail_acktr_init;
   }
 
-  rv = ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0,
-                        NULL, mem);
-  if (rv != 0) {
-    goto fail_crypto_init;
-  }
+  ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL,
+                   NULL, mem);
 
-  rv = ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less,
-                       sizeof(uint64_t), mem);
-  if (rv != 0) {
-    goto fail_tx_frq_init;
-  }
+  ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, sizeof(uint64_t),
+                  mem);
 
   ngtcp2_rtb_init(&pktns->rtb, pktns_id, &pktns->crypto.strm, rst, cc, log,
-                  qlog, mem);
+                  qlog, rtb_entry_objalloc, frc_objalloc, mem);
 
   return 0;
 
-fail_tx_frq_init:
-  ngtcp2_strm_free(&pktns->crypto.strm);
-fail_crypto_init:
-  ngtcp2_acktr_free(&pktns->acktr);
 fail_acktr_init:
   ngtcp2_gaptr_free(&pktns->rx.pngap);
 
@@ -633,7 +647,8 @@ fail_acktr_init:
 
 static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id,
                      ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log,
-                     ngtcp2_qlog *qlog, const ngtcp2_mem *mem) {
+                     ngtcp2_qlog *qlog, ngtcp2_objalloc *rtb_entry_objalloc,
+                     ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
   int rv;
 
   *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns));
@@ -641,7 +656,8 @@ static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id,
     return NGTCP2_ERR_NOMEM;
   }
 
-  rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, mem);
+  rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, rtb_entry_objalloc,
+                  frc_objalloc, mem);
   if (rv != 0) {
     ngtcp2_mem_free(mem, *ppktns);
   }
@@ -689,7 +705,8 @@ static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) {
 
   delete_buffed_pkts(pktns->rx.buffed_pkts, mem);
 
-  ngtcp2_frame_chain_list_del(pktns->tx.frq, mem);
+  ngtcp2_frame_chain_list_objalloc_del(pktns->tx.frq, pktns->rtb.frc_objalloc,
+                                       mem);
 
   ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem);
   ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, mem);
@@ -697,7 +714,7 @@ static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) {
   for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);
        ngtcp2_ksl_it_next(&it)) {
     frc = ngtcp2_ksl_it_get(&it);
-    ngtcp2_frame_chain_del(frc, mem);
+    ngtcp2_frame_chain_objalloc_del(frc, pktns->rtb.frc_objalloc, mem);
   }
 
   ngtcp2_ksl_free(&pktns->crypto.tx.frq);
@@ -841,7 +858,7 @@ ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn,
 }
 
 static void conn_handle_tx_ecn(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
-                               uint8_t *prtb_entry_flags, ngtcp2_pktns *pktns,
+                               uint16_t *prtb_entry_flags, ngtcp2_pktns *pktns,
                                const ngtcp2_pkt_hd *hd, ngtcp2_tstamp ts) {
   assert(pi);
 
@@ -933,9 +950,44 @@ static void conn_reset_ecn_validation_state(ngtcp2_conn *conn) {
   pktns->tx.ecn.validation_pkt_lost = 0;
 }
 
+/* server_default_other_versions is the default other_versions field
+   sent by server. */
+static uint8_t server_default_other_versions[] = {0, 0, 0, 1};
+
+/*
+ * other_versions_new allocates new buffer, and writes |versions| of
+ * length |versionslen| in network byte order, suitable for sending in
+ * other_versions field of version_information QUIC transport
+ * parameter.  The pointer to the allocated buffer is assigned to
+ * |*pbuf|.
+ *
+ * This function returns 0 if it succeeds, or one of the negative
+ * error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ *     Out of memory.
+ */
+static int other_versions_new(uint8_t **pbuf, const uint32_t *versions,
+                              size_t versionslen, const ngtcp2_mem *mem) {
+  size_t i;
+  uint8_t *buf = ngtcp2_mem_malloc(mem, sizeof(uint32_t) * versionslen);
+
+  if (buf == NULL) {
+    return NGTCP2_ERR_NOMEM;
+  }
+
+  *pbuf = buf;
+
+  for (i = 0; i < versionslen; ++i) {
+    buf = ngtcp2_put_uint32be(buf, versions[i]);
+  }
+
+  return 0;
+}
+
 static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
                     const ngtcp2_cid *scid, const ngtcp2_path *path,
-                    uint32_t version, int callbacks_version,
+                    uint32_t client_chosen_version, int callbacks_version,
                     const ngtcp2_callbacks *callbacks, int settings_version,
                     const ngtcp2_settings *settings,
                     int transport_params_version,
@@ -945,6 +997,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
   ngtcp2_scid *scident;
   uint8_t *buf;
   uint8_t fixed_bit_byte;
+  size_t i;
+  uint32_t *preferred_versions;
   (void)callbacks_version;
   (void)settings_version;
   (void)transport_params_version;
@@ -952,6 +1006,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
   assert(settings->max_window <= NGTCP2_MAX_VARINT);
   assert(settings->max_stream_window <= NGTCP2_MAX_VARINT);
   assert(settings->max_udp_payload_size);
+  assert(settings->max_udp_payload_size <= NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE);
   assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE);
   assert(params->initial_max_data <= NGTCP2_MAX_VARINT);
   assert(params->initial_max_stream_data_bidi_local <= NGTCP2_MAX_VARINT);
@@ -970,6 +1025,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
   assert(callbacks->delete_crypto_aead_ctx);
   assert(callbacks->delete_crypto_cipher_ctx);
   assert(callbacks->get_path_challenge_data);
+  assert(!server || !ngtcp2_is_reserved_version(client_chosen_version));
 
   if (mem == NULL) {
     mem = ngtcp2_mem_default();
@@ -981,60 +1037,31 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
     goto fail_conn;
   }
 
-  rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.bound,
-                           NGTCP2_MAX_BOUND_DCID_POOL_SIZE, sizeof(ngtcp2_dcid),
-                           mem);
-  if (rv != 0) {
-    goto fail_dcid_bound_init;
-  }
+  ngtcp2_objalloc_frame_chain_init(&(*pconn)->frc_objalloc, 64, mem);
+  ngtcp2_objalloc_rtb_entry_init(&(*pconn)->rtb_entry_objalloc, 64, mem);
+  ngtcp2_objalloc_strm_init(&(*pconn)->strm_objalloc, 64, mem);
 
-  rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.unused, NGTCP2_MAX_DCID_POOL_SIZE,
-                           sizeof(ngtcp2_dcid), mem);
-  if (rv != 0) {
-    goto fail_dcid_unused_init;
-  }
+  ngtcp2_static_ringbuf_dcid_bound_init(&(*pconn)->dcid.bound);
 
-  rv =
-      ngtcp2_ringbuf_init(&(*pconn)->dcid.retired, NGTCP2_MAX_DCID_RETIRED_SIZE,
-                          sizeof(ngtcp2_dcid), mem);
-  if (rv != 0) {
-    goto fail_dcid_retired_init;
-  }
+  ngtcp2_static_ringbuf_dcid_unused_init(&(*pconn)->dcid.unused);
 
-  rv = ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem);
-  if (rv != 0) {
-    goto fail_seqgap_init;
-  }
+  ngtcp2_static_ringbuf_dcid_retired_init(&(*pconn)->dcid.retired);
 
-  rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem);
-  if (rv != 0) {
-    goto fail_scid_set_init;
-  }
+  ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem);
+
+  ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem);
 
   ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem);
 
-  rv = ngtcp2_map_init(&(*pconn)->strms, mem);
-  if (rv != 0) {
-    goto fail_strms_init;
-  }
+  ngtcp2_map_init(&(*pconn)->strms, mem);
 
   ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem);
 
-  rv = ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem);
-  if (rv != 0) {
-    goto fail_remote_bidi_idtr_init;
-  }
+  ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem);
 
-  rv = ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem);
-  if (rv != 0) {
-    goto fail_remote_uni_idtr_init;
-  }
+  ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem);
 
-  rv = ngtcp2_ringbuf_init(&(*pconn)->rx.path_challenge, 4,
-                           sizeof(ngtcp2_path_challenge_entry), mem);
-  if (rv != 0) {
-    goto fail_rx_path_challenge_init;
-  }
+  ngtcp2_static_ringbuf_path_challenge_init(&(*pconn)->rx.path_challenge);
 
   ngtcp2_log_init(&(*pconn)->log, scid, settings->log_printf,
                   settings->initial_ts, user_data);
@@ -1043,6 +1070,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
   if ((*pconn)->qlog.write) {
     buf = ngtcp2_mem_malloc(mem, NGTCP2_QLOG_BUFLEN);
     if (buf == NULL) {
+      rv = NGTCP2_ERR_NOMEM;
       goto fail_qlog_buf;
     }
     ngtcp2_buf_init(&(*pconn)->qlog.buf, buf, NGTCP2_QLOG_BUFLEN);
@@ -1057,6 +1085,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
   if (settings->token.len) {
     buf = ngtcp2_mem_malloc(mem, settings->token.len);
     if (buf == NULL) {
+      rv = NGTCP2_ERR_NOMEM;
       goto fail_token;
     }
     memcpy(buf, settings->token.base, settings->token.len);
@@ -1066,6 +1095,10 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
     (*pconn)->local.settings.token.len = 0;
   }
 
+  if (!(*pconn)->local.settings.original_version) {
+    (*pconn)->local.settings.original_version = client_chosen_version;
+  }
+
   conn_reset_conn_stat(*pconn, &(*pconn)->cstat);
   (*pconn)->cstat.initial_rtt = settings->initial_rtt;
   (*pconn)->cstat.max_udp_payload_size =
@@ -1109,19 +1142,22 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
   }
 
   rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst,
-                 &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem);
+                 &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog,
+                 &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem);
   if (rv != 0) {
     goto fail_in_pktns_init;
   }
 
   rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_PKTNS_ID_HANDSHAKE, &(*pconn)->rst,
-                 &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem);
+                 &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog,
+                 &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem);
   if (rv != 0) {
     goto fail_hs_pktns_init;
   }
 
   rv = pktns_init(&(*pconn)->pktns, NGTCP2_PKTNS_ID_APPLICATION, &(*pconn)->rst,
-                  &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem);
+                  &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog,
+                  &(*pconn)->rtb_entry_objalloc, &(*pconn)->frc_objalloc, mem);
   if (rv != 0) {
     goto fail_pktns_init;
   }
@@ -1151,6 +1187,84 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
     goto fail_seqgap_push;
   }
 
+  if (settings->preferred_versionslen) {
+    if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) {
+      for (i = 0; i < settings->preferred_versionslen; ++i) {
+        if (settings->preferred_versions[i] == client_chosen_version) {
+          break;
+        }
+      }
+
+      assert(i < settings->preferred_versionslen);
+    }
+
+    preferred_versions = ngtcp2_mem_malloc(
+        mem, sizeof(uint32_t) * settings->preferred_versionslen);
+    if (preferred_versions == NULL) {
+      rv = NGTCP2_ERR_NOMEM;
+      goto fail_preferred_versions;
+    }
+
+    for (i = 0; i < settings->preferred_versionslen; ++i) {
+      assert(ngtcp2_is_supported_version(settings->preferred_versions[i]));
+
+      preferred_versions[i] = settings->preferred_versions[i];
+    }
+
+    (*pconn)->vneg.preferred_versions = preferred_versions;
+    (*pconn)->vneg.preferred_versionslen = settings->preferred_versionslen;
+  }
+
+  if (settings->other_versionslen) {
+    if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) {
+      for (i = 0; i < settings->other_versionslen; ++i) {
+        if (settings->other_versions[i] == client_chosen_version) {
+          break;
+        }
+      }
+
+      assert(i < settings->other_versionslen);
+    }
+
+    for (i = 0; i < settings->other_versionslen; ++i) {
+      assert(ngtcp2_is_reserved_version(settings->other_versions[i]) ||
+             ngtcp2_is_supported_version(settings->other_versions[i]));
+    }
+
+    rv = other_versions_new(&buf, settings->other_versions,
+                            settings->other_versionslen, mem);
+    if (rv != 0) {
+      goto fail_other_versions;
+    }
+
+    (*pconn)->vneg.other_versions = buf;
+    (*pconn)->vneg.other_versionslen =
+        sizeof(uint32_t) * settings->other_versionslen;
+  } else if (server) {
+    if (settings->preferred_versionslen) {
+      rv = other_versions_new(&buf, settings->preferred_versions,
+                              settings->preferred_versionslen, mem);
+      if (rv != 0) {
+        goto fail_other_versions;
+      }
+
+      (*pconn)->vneg.other_versions = buf;
+      (*pconn)->vneg.other_versionslen =
+          sizeof(uint32_t) * settings->preferred_versionslen;
+    } else {
+      (*pconn)->vneg.other_versions = server_default_other_versions;
+      (*pconn)->vneg.other_versionslen = sizeof(server_default_other_versions);
+    }
+  } else if (!server && !ngtcp2_is_reserved_version(client_chosen_version)) {
+    rv = other_versions_new(&buf, &client_chosen_version, 1, mem);
+    if (rv != 0) {
+      goto fail_other_versions;
+    }
+
+    (*pconn)->vneg.other_versions = buf;
+    (*pconn)->vneg.other_versionslen = sizeof(uint32_t);
+  }
+
   callbacks->rand(&fixed_bit_byte, 1, &settings->rand_ctx);
   if (fixed_bit_byte & 1) {
     (*pconn)->flags |= NGTCP2_CONN_FLAG_CLEAR_FIXED_BIT;
@@ -1161,7 +1275,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
   (*pconn)->server = server;
   (*pconn)->oscid = *scid;
   (*pconn)->callbacks = *callbacks;
-  (*pconn)->version = version;
+  (*pconn)->client_chosen_version = client_chosen_version;
   (*pconn)->mem = mem;
   (*pconn)->user_data = user_data;
   (*pconn)->idle_ts = settings->initial_ts;
@@ -1177,12 +1291,13 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
 
   return 0;
 
+fail_other_versions:
+  ngtcp2_mem_free(mem, (*pconn)->vneg.preferred_versions);
+fail_preferred_versions:
 fail_seqgap_push:
 fail_scid_set_insert:
   ngtcp2_mem_free(mem, scident);
 fail_scident:
-  ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base);
-fail_token:
   pktns_free(&(*pconn)->pktns, mem);
 fail_pktns_init:
   pktns_del((*pconn)->hs_pktns, mem);
@@ -1191,27 +1306,19 @@ fail_hs_pktns_init:
 fail_in_pktns_init:
   cc_del(&(*pconn)->cc, settings->cc_algo, mem);
 fail_cc_init:
+  ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base);
+fail_token:
   ngtcp2_mem_free(mem, (*pconn)->qlog.buf.begin);
 fail_qlog_buf:
-  ngtcp2_ringbuf_free(&(*pconn)->rx.path_challenge);
-fail_rx_path_challenge_init:
   ngtcp2_idtr_free(&(*pconn)->remote.uni.idtr);
-fail_remote_uni_idtr_init:
   ngtcp2_idtr_free(&(*pconn)->remote.bidi.idtr);
-fail_remote_bidi_idtr_init:
   ngtcp2_map_free(&(*pconn)->strms);
-fail_strms_init:
   delete_scid(&(*pconn)->scid.set, mem);
   ngtcp2_ksl_free(&(*pconn)->scid.set);
-fail_scid_set_init:
   ngtcp2_gaptr_free(&(*pconn)->dcid.seqgap);
-fail_seqgap_init:
-  ngtcp2_ringbuf_free(&(*pconn)->dcid.retired);
-fail_dcid_retired_init:
-  ngtcp2_ringbuf_free(&(*pconn)->dcid.unused);
-fail_dcid_unused_init:
-  ngtcp2_ringbuf_free(&(*pconn)->dcid.bound);
-fail_dcid_bound_init:
+  ngtcp2_objalloc_free(&(*pconn)->strm_objalloc);
+  ngtcp2_objalloc_free(&(*pconn)->rtb_entry_objalloc);
+  ngtcp2_objalloc_free(&(*pconn)->frc_objalloc);
   ngtcp2_mem_free(mem, *pconn);
 fail_conn:
   return rv;
@@ -1219,15 +1326,16 @@ fail_conn:
 
 int ngtcp2_conn_client_new_versioned(
     ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
-    const ngtcp2_path *path, uint32_t version, int callbacks_version,
-    const ngtcp2_callbacks *callbacks, int settings_version,
-    const ngtcp2_settings *settings, int transport_params_version,
-    const ngtcp2_transport_params *params, const ngtcp2_mem *mem,
-    void *user_data) {
+    const ngtcp2_path *path, uint32_t client_chosen_version,
+    int callbacks_version, const ngtcp2_callbacks *callbacks,
+    int settings_version, const ngtcp2_settings *settings,
+    int transport_params_version, const ngtcp2_transport_params *params,
+    const ngtcp2_mem *mem, void *user_data) {
   int rv;
-  rv = conn_new(pconn, dcid, scid, path, version, callbacks_version, callbacks,
-                settings_version, settings, transport_params_version, params,
-                mem, user_data, 0);
+
+  rv = conn_new(pconn, dcid, scid, path, client_chosen_version,
+                callbacks_version, callbacks, settings_version, settings,
+                transport_params_version, params, mem, user_data, 0);
   if (rv != 0) {
     return rv;
   }
@@ -1247,18 +1355,20 @@ int ngtcp2_conn_client_new_versioned(
 
 int ngtcp2_conn_server_new_versioned(
     ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
-    const ngtcp2_path *path, uint32_t version, int callbacks_version,
-    const ngtcp2_callbacks *callbacks, int settings_version,
-    const ngtcp2_settings *settings, int transport_params_version,
-    const ngtcp2_transport_params *params, const ngtcp2_mem *mem,
-    void *user_data) {
+    const ngtcp2_path *path, uint32_t client_chosen_version,
+    int callbacks_version, const ngtcp2_callbacks *callbacks,
+    int settings_version, const ngtcp2_settings *settings,
+    int transport_params_version, const ngtcp2_transport_params *params,
+    const ngtcp2_mem *mem, void *user_data) {
   int rv;
-  rv = conn_new(pconn, dcid, scid, path, version, callbacks_version, callbacks,
-                settings_version, settings, transport_params_version, params,
-                mem, user_data, 1);
+
+  rv = conn_new(pconn, dcid, scid, path, client_chosen_version,
+                callbacks_version, callbacks, settings_version, settings,
+                transport_params_version, params, mem, user_data, 1);
   if (rv != 0) {
     return rv;
   }
+
   (*pconn)->state = NGTCP2_CS_SERVER_INITIAL;
   (*pconn)->local.bidi.next_stream_id = 1;
   (*pconn)->local.uni.next_stream_id = 3;
@@ -1293,15 +1403,30 @@ static uint64_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm,
 }
 
 static int delete_strms_each(void *data, void *ptr) {
-  const ngtcp2_mem *mem = ptr;
+  ngtcp2_conn *conn = ptr;
   ngtcp2_strm *s = data;
 
   ngtcp2_strm_free(s);
-  ngtcp2_mem_free(mem, s);
+  ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s);
 
   return 0;
 }
 
+static void conn_vneg_crypto_free(ngtcp2_conn *conn) {
+  if (conn->vneg.rx.ckm) {
+    conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx);
+  }
+  conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx);
+
+  if (conn->vneg.tx.ckm) {
+    conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx);
+  }
+  conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx);
+
+  ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem);
+  ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem);
+}
+
 void ngtcp2_conn_del(ngtcp2_conn *conn) {
   if (conn == NULL) {
     return;
@@ -1368,6 +1493,13 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) {
 
   conn_call_delete_crypto_aead_ctx(conn, &conn->crypto.retry_aead_ctx);
 
+  conn_vneg_crypto_free(conn);
+
+  ngtcp2_mem_free(conn->mem, conn->vneg.preferred_versions);
+  if (conn->vneg.other_versions != server_default_other_versions) {
+    ngtcp2_mem_free(conn->mem, conn->vneg.other_versions);
+  }
+
   ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_buf.base);
   ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_hp_buf.base);
   ngtcp2_mem_free(conn->mem, conn->local.settings.token.base);
@@ -1385,8 +1517,7 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) {
 
   ngtcp2_mem_free(conn->mem, conn->qlog.buf.begin);
 
-  ngtcp2_ringbuf_free(&conn->rx.path_challenge);
-
+  ngtcp2_pmtud_del(conn->pmtud);
   ngtcp2_pv_del(conn->pv);
 
   ngtcp2_mem_free(conn->mem, conn->rx.ccerr.reason);
@@ -1395,16 +1526,17 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) {
   ngtcp2_idtr_free(&conn->remote.bidi.idtr);
   ngtcp2_mem_free(conn->mem, conn->tx.ack);
   ngtcp2_pq_free(&conn->tx.strmq);
-  ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn->mem);
+  ngtcp2_map_each_free(&conn->strms, delete_strms_each, (void *)conn);
   ngtcp2_map_free(&conn->strms);
 
   ngtcp2_pq_free(&conn->scid.used);
   delete_scid(&conn->scid.set, conn->mem);
   ngtcp2_ksl_free(&conn->scid.set);
   ngtcp2_gaptr_free(&conn->dcid.seqgap);
-  ngtcp2_ringbuf_free(&conn->dcid.retired);
-  ngtcp2_ringbuf_free(&conn->dcid.unused);
-  ngtcp2_ringbuf_free(&conn->dcid.bound);
+
+  ngtcp2_objalloc_free(&conn->strm_objalloc);
+  ngtcp2_objalloc_free(&conn->rtb_entry_objalloc);
+  ngtcp2_objalloc_free(&conn->frc_objalloc);
 
   ngtcp2_mem_free(conn->mem, conn);
 }
@@ -1543,7 +1675,7 @@ static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr,
     ack->first_ack_blklen = 0;
   }
 
-  if (type == NGTCP2_PKT_SHORT) {
+  if (type == NGTCP2_PKT_1RTT) {
     ack->ack_delay_unscaled = ts - largest_ack_ts;
     ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS /
                      (1ULL << ack_delay_exponent);
@@ -1689,16 +1821,28 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) {
   return 1;
 }
 
+/*
+ * conn_get_cwnd returns cwnd for the current path.
+ */
+static uint64_t conn_get_cwnd(ngtcp2_conn *conn) {
+  return conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)
+             ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size)
+             : conn->cstat.cwnd;
+}
+
 /*
  * conn_cwnd_is_zero returns nonzero if the number of bytes the local
  * endpoint can sent at this time is zero.
  */
 static uint64_t conn_cwnd_is_zero(ngtcp2_conn *conn) {
   uint64_t bytes_in_flight = conn->cstat.bytes_in_flight;
-  uint64_t cwnd =
-      conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)
-          ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size)
-          : conn->cstat.cwnd;
+  uint64_t cwnd = conn_get_cwnd(conn);
+
+  if (bytes_in_flight >= cwnd) {
+    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
+                    "cwnd limited bytes_in_flight=%lu cwnd=%lu",
+                    bytes_in_flight, cwnd);
+  }
 
   return bytes_in_flight >= cwnd;
 }
@@ -1744,7 +1888,7 @@ static void conn_cryptofrq_clear(ngtcp2_conn *conn, ngtcp2_pktns *pktns) {
   for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);
        ngtcp2_ksl_it_next(&it)) {
     frc = ngtcp2_ksl_it_get(&it);
-    ngtcp2_frame_chain_del(frc, conn->mem);
+    ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
   }
   ngtcp2_ksl_clear(&pktns->crypto.tx.frq);
 }
@@ -1826,7 +1970,7 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
     }
 
     if (idx == fr->datacnt) {
-      ngtcp2_frame_chain_del(frc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
       continue;
     }
 
@@ -1865,10 +2009,10 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
       return 0;
     }
 
-    rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->datacnt - end_idx,
-                                               conn->mem);
+    rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+        &nfrc, fr->datacnt - end_idx, &conn->frc_objalloc, conn->mem);
     if (rv != 0) {
-      ngtcp2_frame_chain_del(frc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
       return rv;
     }
 
@@ -1887,8 +2031,8 @@ static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
     rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_del(nfrc, conn->mem);
-      ngtcp2_frame_chain_del(frc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
       return rv;
     }
 
@@ -1949,10 +2093,11 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
     assert(acnt > 0);
     assert(bcnt > 0);
 
-    rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, bcnt, conn->mem);
+    rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+        &nfrc, bcnt, &conn->frc_objalloc, conn->mem);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_del(frc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
       return rv;
     }
 
@@ -1965,15 +2110,16 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
     rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_del(nfrc, conn->mem);
-      ngtcp2_frame_chain_del(frc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
       return rv;
     }
 
-    rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem);
+    rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+        &nfrc, acnt, &conn->frc_objalloc, conn->mem);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_del(frc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
       return rv;
     }
 
@@ -1982,7 +2128,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
     nfr->datacnt = acnt;
     ngtcp2_vec_copy(nfr->data, a, acnt);
 
-    ngtcp2_frame_chain_del(frc, conn->mem);
+    ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
 
     *pfrc = nfrc;
 
@@ -2007,7 +2153,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
     rv = conn_cryptofrq_unacked_pop(conn, pktns, &nfrc);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_del(frc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
       return rv;
     }
     if (nfrc == NULL) {
@@ -2022,8 +2168,8 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
       rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
       if (rv != 0) {
         assert(ngtcp2_err_is_fatal(rv));
-        ngtcp2_frame_chain_del(nfrc, conn->mem);
-        ngtcp2_frame_chain_del(frc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
         return rv;
       }
       break;
@@ -2033,7 +2179,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
     left -= nmerged;
 
     if (nfr->datacnt == 0) {
-      ngtcp2_frame_chain_del(nfrc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
       continue;
     }
 
@@ -2041,8 +2187,8 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
 
     rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc);
     if (rv != 0) {
-      ngtcp2_frame_chain_del(nfrc, conn->mem);
-      ngtcp2_frame_chain_del(frc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(nfrc, &conn->frc_objalloc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
       return rv;
     }
 
@@ -2059,9 +2205,10 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
 
   assert(acnt > fr->datacnt);
 
-  rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem);
+  rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+      &nfrc, acnt, &conn->frc_objalloc, conn->mem);
   if (rv != 0) {
-    ngtcp2_frame_chain_del(frc, conn->mem);
+    ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
     return rv;
   }
 
@@ -2070,7 +2217,7 @@ static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
   nfr->datacnt = acnt;
   ngtcp2_vec_copy(nfr->data, a, acnt);
 
-  ngtcp2_frame_chain_del(frc, conn->mem);
+  ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
 
   *pfrc = nfrc;
 
@@ -2132,7 +2279,7 @@ static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used,
  * conn_should_pad_pkt returns nonzero if the packet should be padded.
  * |type| is the type of packet.  |left| is the space left in packet
  * buffer.  |write_datalen| is the number of bytes which will be sent
- * in the next, coalesced 0-RTT or Short packet.
+ * in the next, coalesced 0-RTT or 1RTT packet.
  */
 static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left,
                                uint64_t write_datalen, int ack_eliciting,
@@ -2167,7 +2314,7 @@ static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left,
                  write_datalen == 0) {
         return 1;
       } else {
-        /* If we have something to send in 0RTT or Short packet, then
+        /* If we have something to send in 0RTT or 1RTT packet, then
            add PADDING in that packet.  Take maximum in case that
            write_datalen includes DATAGRAM which cannot be split. */
         min_payloadlen =
@@ -2192,13 +2339,13 @@ static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left,
   return left <
          /* TODO Assuming that pkt_num is encoded in 1 byte. */
          NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen +
-             conn->oscid.datalen + 1 /* payloadlen bytes - 1 */ +
-             min_payloadlen + NGTCP2_MAX_AEAD_OVERHEAD;
+             conn->oscid.datalen + NGTCP2_PKT_LENGTHLEN - 1 + min_payloadlen +
+             NGTCP2_MAX_AEAD_OVERHEAD;
 }
 
 static void conn_restart_timer_on_write(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
   conn->idle_ts = ts;
-  conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE;
+  conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE;
 }
 
 static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
@@ -2253,7 +2400,7 @@ static void conn_cancel_expired_keep_alive_timer(ngtcp2_conn *conn,
 static void conn_update_keep_alive_last_ts(ngtcp2_conn *conn,
                                            ngtcp2_tstamp ts) {
   conn->keep_alive.last_ts = ts;
-  conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED;
+  conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED;
 }
 
 void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn,
@@ -2270,7 +2417,7 @@ void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn,
  * is involved (which includes other stuff, like reading packets etc
  * in a typical single threaded use case).
  */
-#define NGTCP2_PKT_PACING_OVERHEAD (500 * NGTCP2_MICROSECONDS)
+#define NGTCP2_PKT_PACING_OVERHEAD NGTCP2_MILLISECONDS
 
 static void conn_cancel_expired_pkt_tx_timer(ngtcp2_conn *conn,
                                              ngtcp2_tstamp ts) {
@@ -2310,16 +2457,6 @@ static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) {
                                                : NGTCP2_PKT_FLAG_NONE));
 }
 
-/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */
-#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00
-/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other
-   than Initial packet should be padded.  Initial packet might be
-   padded based on QUIC requirement regardless of this flag. */
-#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01
-/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come
-   and it should be encoded into the current packet. */
-#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02
-
 /*
  * conn_write_handshake_pkt writes handshake packet in the buffer
  * pointed by |dest| whose length is |destlen|.  |type| specifies long
@@ -2327,7 +2464,7 @@ static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) {
  * NGTCP2_PKT_HANDSHAKE_PKT.
  *
  * |write_datalen| is the minimum length of application data ready to
- * send in subsequent 0RTT or Short packet.
+ * send in subsequent 0RTT or 1RTT packet.
  *
  * This function returns the number of bytes written in |dest| if it
  * succeeds, or one of the following negative error codes:
@@ -2352,13 +2489,14 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
   ngtcp2_rtb_entry *rtbent;
   ngtcp2_pktns *pktns;
   size_t left;
-  uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
+  uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
   int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0;
   int pkt_empty = 1;
   int padded = 0;
   int hd_logged = 0;
   uint64_t crypto_offset;
   ngtcp2_ssize num_reclaimed;
+  uint32_t version;
 
   switch (type) {
   case NGTCP2_PKT_INITIAL:
@@ -2367,12 +2505,26 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
     }
     assert(conn->in_pktns->crypto.tx.ckm);
     pktns = conn->in_pktns;
+    version = conn->negotiated_version ? conn->negotiated_version
+                                       : conn->client_chosen_version;
+    if (version == conn->client_chosen_version) {
+      cc.ckm = pktns->crypto.tx.ckm;
+      cc.hp_ctx = pktns->crypto.tx.hp_ctx;
+    } else {
+      assert(conn->vneg.version == version);
+
+      cc.ckm = conn->vneg.tx.ckm;
+      cc.hp_ctx = conn->vneg.tx.hp_ctx;
+    }
     break;
   case NGTCP2_PKT_HANDSHAKE:
     if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) {
       return 0;
     }
     pktns = conn->hs_pktns;
+    version = conn->negotiated_version;
+    cc.ckm = pktns->crypto.tx.ckm;
+    cc.hp_ctx = pktns->crypto.tx.hp_ctx;
     break;
   default:
     assert(0);
@@ -2381,15 +2533,13 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
 
   cc.aead = pktns->crypto.ctx.aead;
   cc.hp = pktns->crypto.ctx.hp;
-  cc.ckm = pktns->crypto.tx.ckm;
-  cc.hp_ctx = pktns->crypto.tx.hp_ctx;
   cc.encrypt = conn->callbacks.encrypt;
   cc.hp_mask = conn->callbacks.hp_mask;
 
   ngtcp2_pkt_hd_init(&hd, conn_pkt_flags_long(conn), type,
                      &conn->dcid.current.cid, &conn->oscid,
                      pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns),
-                     conn->version, 0);
+                     version, 0);
 
   if (!conn->server && type == NGTCP2_PKT_INITIAL &&
       conn->local.settings.token.len) {
@@ -2412,7 +2562,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
                              /* ack_delay = */ 0,
                              NGTCP2_DEFAULT_ACK_DELAY_EXPONENT);
   if (rv != 0) {
-    ngtcp2_frame_chain_list_del(frq, conn->mem);
+    ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem);
     return rv;
   }
 
@@ -2449,7 +2599,8 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
       rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left);
       if (rv != 0) {
         assert(ngtcp2_err_is_fatal(rv));
-        ngtcp2_frame_chain_list_del(frq, conn->mem);
+        ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
+                                             conn->mem);
         return rv;
       }
 
@@ -2467,15 +2618,16 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
 
       pkt_empty = 0;
       rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+                         NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
                          NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
     }
 
     if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
         pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) {
-      num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns,
-                                                pktns->rtb.probe_pkt_left + 1);
+      num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1);
       if (num_reclaimed < 0) {
-        ngtcp2_frame_chain_list_del(frq, conn->mem);
+        ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
+                                             conn->mem);
         return rv;
       }
       if (num_reclaimed) {
@@ -2487,21 +2639,19 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
          send any probe packet.  Client needs to send probe packets
          until it knows that server has completed address validation or
          handshake has been confirmed. */
-      if (pktns->rtb.num_retransmittable == 0 &&
+      if (pktns->rtb.num_pto_eliciting == 0 &&
           (conn->server ||
            (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED |
                            NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
         pktns->rtb.probe_pkt_left = 0;
         ngtcp2_conn_set_loss_detection_timer(conn, ts);
+        /* TODO If packet is empty, we should return now if cwnd is
+           zero. */
       }
     }
 
-    /* Don't send any PING frame if client Initial has not been
-       acknowledged yet. */
     if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
-        pktns->rtb.probe_pkt_left &&
-        (type != NGTCP2_PKT_INITIAL ||
-         ngtcp2_strm_is_all_tx_data_acked(&pktns->crypto.strm))) {
+        pktns->rtb.probe_pkt_left) {
       lfr.type = NGTCP2_FRAME_PING;
 
       rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
@@ -2518,7 +2668,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
       if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
         /* The intention of smaller limit is get more chance to measure
            RTT samples in early phase. */
-        if (pktns->rtb.probe_pkt_left || pktns->tx.num_non_ack_pkt >= 1) {
+        if (pktns->tx.num_non_ack_pkt >= 1) {
           lfr.type = NGTCP2_FRAME_PING;
 
           rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr);
@@ -2563,7 +2713,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
   spktlen = ngtcp2_ppe_final(&ppe, NULL);
   if (spktlen < 0) {
     assert(ngtcp2_err_is_fatal((int)spktlen));
-    ngtcp2_frame_chain_list_del(frq, conn->mem);
+    ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem);
     return spktlen;
   }
 
@@ -2574,17 +2724,19 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
       conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts);
     }
 
-    rv = ngtcp2_rtb_entry_new(&rtbent, &hd, frq, ts, (size_t)spktlen,
-                              rtb_entry_flags, conn->mem);
+    rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, frq, ts, (size_t)spktlen,
+                                       rtb_entry_flags,
+                                       &conn->rtb_entry_objalloc);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_list_del(frq, conn->mem);
+      ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem);
       return rv;
     }
 
     rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent);
     if (rv != 0) {
-      ngtcp2_rtb_entry_del(rtbent, conn->mem);
+      ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc,
+                                    &conn->frc_objalloc, conn->mem);
       return rv;
     }
 
@@ -2651,7 +2803,7 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
     ack_delay = 0;
     ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
     break;
-  case NGTCP2_PKT_SHORT:
+  case NGTCP2_PKT_1RTT:
     pktns = &conn->pktns;
     ack_delay = conn_compute_ack_delay(conn);
     ack_delay_exponent = conn->local.transport_params.ack_delay_exponent;
@@ -2677,8 +2829,8 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
   }
 
   spktlen = ngtcp2_conn_write_single_frame_pkt(
-      conn, pi, dest, destlen, type, &conn->dcid.current.cid, ackfr,
-      NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+      conn, pi, dest, destlen, type, NGTCP2_WRITE_PKT_FLAG_NONE,
+      &conn->dcid.current.cid, ackfr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
 
   if (spktlen <= 0) {
     return spktlen;
@@ -2727,6 +2879,11 @@ static void conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
                   "discarding Initial packet number space");
 
   conn_discard_pktns(conn, &conn->in_pktns, ts);
+
+  conn_vneg_crypto_free(conn);
+
+  memset(&conn->vneg.rx, 0, sizeof(conn->vneg.rx));
+  memset(&conn->vneg.tx, 0, sizeof(conn->vneg.tx));
 }
 
 /*
@@ -3104,7 +3261,7 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) {
       return rv;
     }
 
-    rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+    rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
     if (rv != 0) {
       return rv;
     }
@@ -3166,8 +3323,8 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn,
     --conn->scid.num_retired;
   }
 
-  for (; ngtcp2_ringbuf_len(&conn->dcid.retired);) {
-    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0);
+  for (; ngtcp2_ringbuf_len(&conn->dcid.retired.rb);) {
+    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0);
     if (dcid->retired_ts + timeout >= ts) {
       break;
     }
@@ -3177,7 +3334,7 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn,
       return rv;
     }
 
-    ngtcp2_ringbuf_pop_front(&conn->dcid.retired);
+    ngtcp2_ringbuf_pop_front(&conn->dcid.retired.rb);
   }
 
   return 0;
@@ -3191,10 +3348,33 @@ static size_t conn_min_short_pktlen(ngtcp2_conn *conn) {
   return conn->dcid.current.cid.datalen + NGTCP2_MIN_PKT_EXPANDLEN;
 }
 
+/*
+ * conn_handle_unconfirmed_key_update_from_remote deals with key
+ * update which has not been confirmed yet and initiated by the remote
+ * endpoint.
+ *
+ * If key update was initiated by the remote endpoint, acknowledging a
+ * packet encrypted with the new key completes key update procedure.
+ */
+static void conn_handle_unconfirmed_key_update_from_remote(ngtcp2_conn *conn,
+                                                           int64_t largest_ack,
+                                                           ngtcp2_tstamp ts) {
+  if (!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) ||
+      (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) ||
+      largest_ack < conn->pktns.crypto.rx.ckm->pkt_num) {
+    return;
+  }
+
+  conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED;
+  conn->crypto.key_update.confirmed_ts = ts;
+
+  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed");
+}
+
 /*
  * conn_write_pkt writes a protected packet in the buffer pointed by
  * |dest| whose length if |destlen|.  |type| specifies the type of
- * packet.  It can be NGTCP2_PKT_SHORT or NGTCP2_PKT_0RTT.
+ * packet.  It can be NGTCP2_PKT_1RTT or NGTCP2_PKT_0RTT.
  *
  * This function can send new stream data.  In order to send stream
  * data, specify the underlying stream and parameters to
@@ -3242,7 +3422,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
   uint64_t datalen = 0;
   ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT];
   size_t datacnt;
-  uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
+  uint16_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE;
   int hd_logged = 0;
   ngtcp2_path_challenge_entry *pcent;
   uint8_t hd_flags;
@@ -3261,6 +3441,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
   uint64_t delta;
   const ngtcp2_cid *scid;
   int keep_alive_expired = 0;
+  uint32_t version;
 
   /* Return 0 if destlen is less than minimum packet length which can
      trigger Stateless Reset */
@@ -3291,7 +3472,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
   if (!ppe_pending) {
     switch (type) {
-    case NGTCP2_PKT_SHORT:
+    case NGTCP2_PKT_1RTT:
       hd_flags = conn_pkt_flags_short(conn);
       scid = NULL;
       cc->aead = pktns->crypto.ctx.aead;
@@ -3299,11 +3480,14 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
       cc->ckm = pktns->crypto.tx.ckm;
       cc->hp_ctx = pktns->crypto.tx.hp_ctx;
 
+      assert(conn->negotiated_version);
+
+      version = conn->negotiated_version;
+
       /* transport parameter is only valid after handshake completion
          which means we don't know how many connection ID that remote
          peer can accept before handshake completion. */
-      if (conn->oscid.datalen &&
-          (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+      if (conn->oscid.datalen && conn_is_handshake_completed(conn)) {
         rv = conn_enqueue_new_connection_id(conn);
         if (rv != 0) {
           return rv;
@@ -3322,6 +3506,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
       cc->hp = conn->early.ctx.hp;
       cc->ckm = conn->early.ckm;
       cc->hp_ctx = conn->early.hp_ctx;
+      version = conn->client_chosen_version;
       break;
     default:
       /* Unreachable */
@@ -3332,7 +3517,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
     cc->hp_mask = conn->callbacks.hp_mask;
 
     if (conn_should_send_max_data(conn)) {
-      rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+      rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
       if (rv != 0) {
         return rv;
       }
@@ -3370,7 +3555,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
     ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid,
                        pktns->tx.last_pkt_num + 1,
-                       pktns_select_pkt_numlen(pktns), conn->version, 0);
+                       pktns_select_pkt_numlen(pktns), version, 0);
 
     ngtcp2_ppe_init(ppe, dest, destlen, cc);
 
@@ -3384,8 +3569,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
       return 0;
     }
 
-    if (ngtcp2_ringbuf_len(&conn->rx.path_challenge)) {
-      pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0);
+    if (ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb)) {
+      pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0);
 
       /* PATH_RESPONSE is bound to the path that the corresponding
          PATH_CHALLENGE is received. */
@@ -3398,7 +3583,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
         if (rv != 0) {
           assert(NGTCP2_ERR_NOBUF == rv);
         } else {
-          ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge);
+          ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb);
 
           pkt_empty = 0;
           rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
@@ -3425,6 +3610,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
         ngtcp2_acktr_commit_ack(&pktns->acktr);
         ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num,
                              ackfr->ack.largest_ack);
+        if (type == NGTCP2_PKT_1RTT) {
+          conn_handle_unconfirmed_key_update_from_remote(
+              conn, ackfr->ack.largest_ack, ts);
+        }
         pkt_empty = 0;
       }
     }
@@ -3435,7 +3624,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
           ((*pfrc)->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) {
         frc = *pfrc;
         *pfrc = (*pfrc)->next;
-        ngtcp2_frame_chain_del(frc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
         continue;
       }
 
@@ -3446,16 +3635,20 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
         if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) {
           frc = *pfrc;
           *pfrc = (*pfrc)->next;
-          ngtcp2_frame_chain_del(frc, conn->mem);
+          ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
           continue;
         }
 
-        rv = conn_call_stream_stop_sending(
-            conn, (*pfrc)->fr.stop_sending.stream_id,
-            (*pfrc)->fr.stop_sending.app_error_code, strm->stream_user_data);
-        if (rv != 0) {
-          assert(ngtcp2_err_is_fatal(rv));
-          return rv;
+        if (!(strm->flags & NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED)) {
+          strm->flags |= NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED;
+
+          rv = conn_call_stream_stop_sending(
+              conn, (*pfrc)->fr.stop_sending.stream_id,
+              (*pfrc)->fr.stop_sending.app_error_code, strm->stream_user_data);
+          if (rv != 0) {
+            assert(ngtcp2_err_is_fatal(rv));
+            return rv;
+          }
         }
 
         break;
@@ -3467,7 +3660,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
             conn->remote.bidi.max_streams) {
           frc = *pfrc;
           *pfrc = (*pfrc)->next;
-          ngtcp2_frame_chain_del(frc, conn->mem);
+          ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
           continue;
         }
         break;
@@ -3476,7 +3669,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
             conn->remote.uni.max_streams) {
           frc = *pfrc;
           *pfrc = (*pfrc)->next;
-          ngtcp2_frame_chain_del(frc, conn->mem);
+          ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
           continue;
         }
         break;
@@ -3487,7 +3680,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
             (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) {
           frc = *pfrc;
           *pfrc = (*pfrc)->next;
-          ngtcp2_frame_chain_del(frc, conn->mem);
+          ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
           continue;
         }
         break;
@@ -3495,13 +3688,10 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
         if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) {
           frc = *pfrc;
           *pfrc = (*pfrc)->next;
-          ngtcp2_frame_chain_del(frc, conn->mem);
+          ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
           continue;
         }
         break;
-      case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
-        ++conn->dcid.num_retire_queued;
-        break;
       case NGTCP2_FRAME_CRYPTO:
         assert(0);
         break;
@@ -3515,6 +3705,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
       pkt_empty = 0;
       rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+                         NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
                          NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
       pfrc = &(*pfrc)->next;
     }
@@ -3555,6 +3746,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
         pkt_empty = 0;
         rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+                           NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
                            NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
       }
     }
@@ -3570,7 +3762,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
         return rv;
       }
 
-      rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+      rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
       if (rv != 0) {
         assert(ngtcp2_err_is_fatal(rv));
         return rv;
@@ -3587,6 +3779,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
       } else {
         pkt_empty = 0;
         rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+                           NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
                            NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
         pfrc = &(*pfrc)->next;
       }
@@ -3601,7 +3794,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
           return rv;
         }
 
-        rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+        rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
         if (rv != 0) {
           assert(ngtcp2_err_is_fatal(rv));
           return rv;
@@ -3619,6 +3812,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
         } else {
           pkt_empty = 0;
           rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+                             NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
                              NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
           pfrc = &(*pfrc)->next;
         }
@@ -3631,7 +3825,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
         if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
             conn_should_send_max_stream_data(conn, strm)) {
-          rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+          rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
           if (rv != 0) {
             assert(ngtcp2_err_is_fatal(rv));
             return rv;
@@ -3675,6 +3869,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
           pkt_empty = 0;
           rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+                             NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
                              NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
           pfrc = &(*pfrc)->next;
           strm->rx.max_offset = strm->rx.unsent_max_offset =
@@ -3690,6 +3885,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
         if (stream_offset == (uint64_t)-1) {
           ngtcp2_strm_streamfrq_clear(strm);
           ngtcp2_conn_tx_strmq_pop(conn);
+          assert(conn->tx.strmq_nretrans);
+          --conn->tx.strmq_nretrans;
           continue;
         }
 
@@ -3723,10 +3920,13 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
         pkt_empty = 0;
         rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+                           NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
                            NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
 
         if (ngtcp2_strm_streamfrq_empty(strm)) {
           ngtcp2_conn_tx_strmq_pop(conn);
+          assert(conn->tx.strmq_nretrans);
+          --conn->tx.strmq_nretrans;
           continue;
         }
 
@@ -3744,8 +3944,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
         !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) &&
         pktns->rtb.num_retransmittable && pktns->tx.frq == NULL &&
         pktns->rtb.probe_pkt_left) {
-      num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns,
-                                                pktns->rtb.probe_pkt_left + 1);
+      num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1);
       if (num_reclaimed < 0) {
         return rv;
       }
@@ -3757,9 +3956,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
          those packets have been acknowledged (i.e., retransmission in
          another packet).  In this case, we don't have to send any
          probe packet. */
-      if (pktns->rtb.num_retransmittable == 0) {
+      if (pktns->rtb.num_pto_eliciting == 0) {
         pktns->rtb.probe_pkt_left = 0;
         ngtcp2_conn_set_loss_detection_timer(conn, ts);
+        /* TODO If packet is empty, we should return now if cwnd is
+           zero. */
       }
     }
   } else {
@@ -3783,7 +3984,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
     assert((datacnt == 0 && datalen == 0) || (datacnt && datalen));
 
-    rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, datacnt, conn->mem);
+    rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+        &nfrc, datacnt, &conn->frc_objalloc, conn->mem);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
       return rv;
@@ -3810,6 +4012,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
     pkt_empty = 0;
     rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+                       NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
                        NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE;
 
     vmsg->stream.strm->tx.offset += ndatalen;
@@ -3829,7 +4032,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
   if (rv != NGTCP2_ERR_NOBUF && send_datagram &&
       left >= ngtcp2_pkt_datagram_framelen((size_t)datalen)) {
     if (conn->callbacks.ack_datagram || conn->callbacks.lost_datagram) {
-      rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+      rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
       if (rv != 0) {
         assert(ngtcp2_err_is_fatal(rv));
         return rv;
@@ -3917,7 +4120,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
   if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
     if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT ||
-        keep_alive_expired) {
+        keep_alive_expired || conn->pktns.rtb.probe_pkt_left) {
       lfr.type = NGTCP2_FRAME_PING;
 
       rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr);
@@ -3927,6 +4130,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
            packet is still empty. */
       } else {
         rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING;
+        if (conn->pktns.rtb.probe_pkt_left) {
+          rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_PROBE;
+        }
         pktns->tx.num_non_ack_pkt = 0;
       }
     } else {
@@ -3970,8 +4176,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
       conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, hd, ts);
     }
 
-    rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite,
-                              rtb_entry_flags, conn->mem);
+    rv = ngtcp2_rtb_entry_objalloc_new(&ent, hd, NULL, ts, (size_t)nwrite,
+                                       rtb_entry_flags,
+                                       &conn->rtb_entry_objalloc);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal((int)nwrite));
       return rv;
@@ -3992,7 +4199,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
     rv = conn_on_pkt_sent(conn, &pktns->rtb, ent);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_rtb_entry_del(ent, conn->mem);
+      ngtcp2_rtb_entry_objalloc_del(ent, &conn->rtb_entry_objalloc,
+                                    &conn->frc_objalloc, conn->mem);
       return rv;
     }
 
@@ -4013,7 +4221,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
     conn_handle_tx_ecn(conn, pi, NULL, pktns, hd, ts);
   }
 
-  conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_PPE_PENDING;
+  conn->flags &= (uint32_t)~NGTCP2_CONN_FLAG_PPE_PENDING;
 
   if (pktns->rtb.probe_pkt_left &&
       (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
@@ -4038,8 +4246,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
 ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
     ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
-    uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags,
-    const ngtcp2_path *path, ngtcp2_tstamp ts) {
+    uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr,
+    uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts) {
   int rv;
   ngtcp2_ppe ppe;
   ngtcp2_pkt_hd hd;
@@ -4047,27 +4255,44 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
   ngtcp2_ssize nwrite;
   ngtcp2_crypto_cc cc;
   ngtcp2_pktns *pktns;
-  uint8_t flags;
+  uint8_t hd_flags;
   ngtcp2_rtb_entry *rtbent;
   int padded = 0;
   const ngtcp2_cid *scid;
+  uint32_t version;
 
   switch (type) {
   case NGTCP2_PKT_INITIAL:
     pktns = conn->in_pktns;
-    flags = conn_pkt_flags_long(conn);
+    hd_flags = conn_pkt_flags_long(conn);
     scid = &conn->oscid;
+    version = conn->negotiated_version ? conn->negotiated_version
+                                       : conn->client_chosen_version;
+    if (version == conn->client_chosen_version) {
+      cc.ckm = pktns->crypto.tx.ckm;
+      cc.hp_ctx = pktns->crypto.tx.hp_ctx;
+    } else {
+      assert(version == conn->vneg.version);
+
+      cc.ckm = conn->vneg.tx.ckm;
+      cc.hp_ctx = conn->vneg.tx.hp_ctx;
+    }
     break;
   case NGTCP2_PKT_HANDSHAKE:
     pktns = conn->hs_pktns;
-    flags = conn_pkt_flags_long(conn);
+    hd_flags = conn_pkt_flags_long(conn);
     scid = &conn->oscid;
+    version = conn->negotiated_version;
+    cc.ckm = pktns->crypto.tx.ckm;
+    cc.hp_ctx = pktns->crypto.tx.hp_ctx;
     break;
-  case NGTCP2_PKT_SHORT:
-    /* 0 means Short packet. */
+  case NGTCP2_PKT_1RTT:
     pktns = &conn->pktns;
-    flags = conn_pkt_flags_short(conn);
+    hd_flags = conn_pkt_flags_short(conn);
     scid = NULL;
+    version = conn->negotiated_version;
+    cc.ckm = pktns->crypto.tx.ckm;
+    cc.hp_ctx = pktns->crypto.tx.hp_ctx;
     break;
   default:
     /* We don't support 0-RTT packet in this function. */
@@ -4077,13 +4302,12 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
 
   cc.aead = pktns->crypto.ctx.aead;
   cc.hp = pktns->crypto.ctx.hp;
-  cc.ckm = pktns->crypto.tx.ckm;
-  cc.hp_ctx = pktns->crypto.tx.hp_ctx;
   cc.encrypt = conn->callbacks.encrypt;
   cc.hp_mask = conn->callbacks.hp_mask;
 
-  ngtcp2_pkt_hd_init(&hd, flags, type, dcid, scid, pktns->tx.last_pkt_num + 1,
-                     pktns_select_pkt_numlen(pktns), conn->version, 0);
+  ngtcp2_pkt_hd_init(&hd, hd_flags, type, dcid, scid,
+                     pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns),
+                     version, 0);
 
   ngtcp2_ppe_init(&ppe, dest, destlen, &cc);
 
@@ -4107,21 +4331,25 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
   }
 
   lfr.type = NGTCP2_FRAME_PADDING;
-  switch (fr->type) {
-  case NGTCP2_FRAME_PATH_CHALLENGE:
-  case NGTCP2_FRAME_PATH_RESPONSE:
-    if (!conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
-      lfr.padding.len = ngtcp2_ppe_padding(&ppe);
-    } else {
-      lfr.padding.len = 0;
-    }
-    break;
-  default:
-    if (type == NGTCP2_PKT_SHORT) {
-      lfr.padding.len =
-          ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn));
-    } else {
-      lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe);
+  if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) {
+    lfr.padding.len = ngtcp2_ppe_padding(&ppe);
+  } else {
+    switch (fr->type) {
+    case NGTCP2_FRAME_PATH_CHALLENGE:
+    case NGTCP2_FRAME_PATH_RESPONSE:
+      if (!conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+        lfr.padding.len = ngtcp2_ppe_padding(&ppe);
+      } else {
+        lfr.padding.len = 0;
+      }
+      break;
+    default:
+      if (type == NGTCP2_PKT_1RTT) {
+        lfr.padding.len =
+            ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn));
+      } else {
+        lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe);
+      }
     }
   }
   if (lfr.padding.len) {
@@ -4135,7 +4363,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
     return nwrite;
   }
 
-  if (type == NGTCP2_PKT_SHORT) {
+  if (type == NGTCP2_PKT_1RTT) {
     ++cc.ckm->use_count;
   }
 
@@ -4147,28 +4375,34 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
   case NGTCP2_FRAME_ACK_ECN:
     ngtcp2_acktr_commit_ack(&pktns->acktr);
     ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack);
+    if (type == NGTCP2_PKT_1RTT) {
+      conn_handle_unconfirmed_key_update_from_remote(conn, fr->ack.largest_ack,
+                                                     ts);
+    }
     break;
   }
 
-  if (((rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) &&
+  if (((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) &&
       (!path || ngtcp2_path_eq(&conn->dcid.current.ps.path, path))) {
     if (pi) {
-      conn_handle_tx_ecn(conn, pi, &rtb_flags, pktns, &hd, ts);
+      conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts);
     }
 
-    rv = ngtcp2_rtb_entry_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, rtb_flags,
-                              conn->mem);
+    rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, NULL, ts, (size_t)nwrite,
+                                       rtb_entry_flags,
+                                       &conn->rtb_entry_objalloc);
     if (rv != 0) {
       return rv;
     }
 
     rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent);
     if (rv != 0) {
-      ngtcp2_rtb_entry_del(rtbent, conn->mem);
+      ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc,
+                                    &conn->frc_objalloc, conn->mem);
       return rv;
     }
 
-    if (rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
+    if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
       if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) {
         conn_restart_timer_on_write(conn, ts);
       }
@@ -4199,7 +4433,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
 }
 
 /*
- * conn_process_early_rtb makes any pending 0RTT packet Short packet.
+ * conn_process_early_rtb makes any pending 0RTT packet 1RTT packet.
  */
 static void conn_process_early_rtb(ngtcp2_conn *conn) {
   ngtcp2_rtb_entry *ent;
@@ -4215,9 +4449,9 @@ static void conn_process_early_rtb(ngtcp2_conn *conn) {
       continue;
     }
 
-    /*  0-RTT packet is retransmitted as a Short packet. */
+    /*  0-RTT packet is retransmitted as a 1RTT packet. */
     ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM;
-    ent->hd.type = NGTCP2_PKT_SHORT;
+    ent->hd.type = NGTCP2_PKT_1RTT;
   }
 }
 
@@ -4230,10 +4464,10 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) {
   ngtcp2_pktns *in_pktns = conn->in_pktns;
   ngtcp2_pktns *hs_pktns = conn->hs_pktns;
 
-  return !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) ||
-         (in_pktns && (in_pktns->rtb.num_retransmittable ||
+  return !conn_is_handshake_completed(conn) ||
+         (in_pktns && (in_pktns->rtb.num_pto_eliciting ||
                        ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) ||
-         (hs_pktns && (hs_pktns->rtb.num_retransmittable ||
+         (hs_pktns && (hs_pktns->rtb.num_pto_eliciting ||
                        ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq)));
 }
 
@@ -4246,13 +4480,20 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) {
  *
  * NGTCP2_ERR_NOMEM
  *     Out of memory.
+ * NGTCP2_ERR_CONNECTION_ID_LIMIT
+ *     The number of unacknowledged retirement exceeds the limit.
  */
 static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
   ngtcp2_pktns *pktns = &conn->pktns;
   ngtcp2_frame_chain *nfrc;
   int rv;
 
-  rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+  rv = ngtcp2_conn_track_retired_dcid_seq(conn, seq);
+  if (rv != 0) {
+    return rv;
+  }
+
+  rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
   if (rv != 0) {
     return rv;
   }
@@ -4276,7 +4517,7 @@ static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
  */
 static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
                             ngtcp2_tstamp ts) {
-  ngtcp2_ringbuf *rb = &conn->dcid.retired;
+  ngtcp2_ringbuf *rb = &conn->dcid.retired.rb;
   ngtcp2_dcid *dest, *stale_dcid;
   int rv;
 
@@ -4325,9 +4566,9 @@ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid,
   assert(!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) ||
          !ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path));
 
-  len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+  len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
   for (i = 0; i < len; ++i) {
-    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
 
     if (ngtcp2_path_eq(&dcid->ps.path, path)) {
       *pdcid = dcid;
@@ -4336,7 +4577,7 @@ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid,
   }
 
   if (conn->dcid.current.cid.datalen == 0) {
-    ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound);
+    ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb);
     ngtcp2_cid_zero(&cid);
     ngtcp2_dcid_init(ndcid, ++conn->dcid.zerolen_seq, &cid, NULL);
     ngtcp2_dcid_set_path(ndcid, path);
@@ -4346,32 +4587,113 @@ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid,
     return 0;
   }
 
-  if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+  if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
     return NGTCP2_ERR_CONN_ID_BLOCKED;
   }
 
-  if (ngtcp2_ringbuf_full(&conn->dcid.bound)) {
-    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, 0);
+  if (ngtcp2_ringbuf_full(&conn->dcid.bound.rb)) {
+    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 0);
     rv = conn_retire_dcid(conn, dcid, ts);
     if (rv != 0) {
       return rv;
     }
   }
 
-  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
-  ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound);
+  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
+  ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb);
 
   ngtcp2_dcid_copy(ndcid, dcid);
   ndcid->bound_ts = ts;
   ngtcp2_dcid_set_path(ndcid, path);
 
-  ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+  ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
 
   *pdcid = ndcid;
 
   return 0;
 }
 
+static int conn_start_pmtud(ngtcp2_conn *conn) {
+  int rv;
+  size_t hard_max_udp_payload_size;
+
+  assert(!conn->local.settings.no_pmtud);
+  assert(!conn->pmtud);
+  assert(conn_is_handshake_completed(conn));
+  assert(conn->remote.transport_params.max_udp_payload_size >=
+         NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+  hard_max_udp_payload_size =
+      ngtcp2_min(conn->remote.transport_params.max_udp_payload_size,
+                 conn->local.settings.max_udp_payload_size);
+
+  rv = ngtcp2_pmtud_new(&conn->pmtud, conn->dcid.current.max_udp_payload_size,
+                        hard_max_udp_payload_size,
+                        conn->pktns.tx.last_pkt_num + 1, conn->mem);
+  if (rv != 0) {
+    return rv;
+  }
+
+  if (ngtcp2_pmtud_finished(conn->pmtud)) {
+    ngtcp2_conn_stop_pmtud(conn);
+  }
+
+  return 0;
+}
+
+void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn) {
+  if (!conn->pmtud) {
+    return;
+  }
+
+  ngtcp2_pmtud_del(conn->pmtud);
+
+  conn->pmtud = NULL;
+}
+
+static ngtcp2_ssize conn_write_pmtud_probe(ngtcp2_conn *conn,
+                                           ngtcp2_pkt_info *pi, uint8_t *dest,
+                                           size_t destlen, ngtcp2_tstamp ts) {
+  size_t probelen;
+  ngtcp2_ssize nwrite;
+  ngtcp2_frame lfr;
+
+  assert(conn->pmtud);
+  assert(!ngtcp2_pmtud_finished(conn->pmtud));
+
+  if (!ngtcp2_pmtud_require_probe(conn->pmtud)) {
+    return 0;
+  }
+
+  probelen = ngtcp2_pmtud_probelen(conn->pmtud);
+  if (probelen > destlen) {
+    return 0;
+  }
+
+  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+                  "sending PMTUD probe packet len=%zu", probelen);
+
+  lfr.type = NGTCP2_FRAME_PING;
+
+  nwrite = ngtcp2_conn_write_single_frame_pkt(
+      conn, pi, dest, probelen, NGTCP2_PKT_1RTT,
+      NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, &conn->dcid.current.cid, &lfr,
+      NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING |
+          NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING |
+          NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE,
+      NULL, ts);
+  if (nwrite < 0) {
+    return nwrite;
+  }
+
+  assert(nwrite);
+
+  ngtcp2_pmtud_probe_sent(conn->pmtud, conn_compute_pto(conn, &conn->pktns),
+                          ts);
+
+  return nwrite;
+}
+
 /*
  * conn_stop_pv stops the path validation which is currently running.
  * This function does nothing if no path validation is currently being
@@ -4445,26 +4767,23 @@ static int conn_abort_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
   return conn_stop_pv(conn, ts);
 }
 
-static void conn_update_dcid_max_udp_payload_size(ngtcp2_conn *conn,
-                                                  ngtcp2_dcid *dcid,
-                                                  size_t payloadlen) {
-  if (!conn->local.settings.assume_symmetric_path) {
-    return;
-  }
-
-  dcid->max_udp_payload_size =
-      ngtcp2_max(dcid->max_udp_payload_size, payloadlen);
-}
-
 static size_t conn_shape_udp_payload(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
                                      size_t payloadlen) {
-  if (conn->local.settings.no_udp_payload_size_shaping) {
-    return ngtcp2_min(payloadlen, conn->local.settings.max_udp_payload_size);
+  if (conn->remote.transport_params.max_udp_payload_size) {
+    assert(conn->remote.transport_params.max_udp_payload_size >=
+           NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+
+    payloadlen = ngtcp2_min(payloadlen,
+                            conn->remote.transport_params.max_udp_payload_size);
   }
 
   payloadlen =
       ngtcp2_min(payloadlen, conn->local.settings.max_udp_payload_size);
 
+  if (conn->local.settings.no_udp_payload_size_shaping) {
+    return payloadlen;
+  }
+
   return ngtcp2_min(payloadlen, dcid->max_udp_payload_size);
 }
 
@@ -4566,7 +4885,7 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
   timeout = ngtcp2_max(timeout, 3 * conn->cstat.initial_rtt);
   expiry = ts + timeout * (1ULL << pv->round);
 
-  destlen = conn_shape_udp_payload(conn, &pv->dcid, destlen);
+  destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE);
 
   if (conn->server) {
     if (!(pv->dcid.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
@@ -4589,8 +4908,10 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
   ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry, flags, ts);
 
   nwrite = ngtcp2_conn_write_single_frame_pkt(
-      conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &pv->dcid.cid, &lfr,
-      NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pv->dcid.ps.path, ts);
+      conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
+      &pv->dcid.cid, &lfr,
+      NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING,
+      &pv->dcid.ps.path, ts);
   if (nwrite <= 0) {
     return nwrite;
   }
@@ -4632,8 +4953,8 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
   int rv;
   uint64_t tx_left;
 
-  for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge);) {
-    pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0);
+  for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb);) {
+    pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge.rb, 0);
 
     if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) {
       /* Send PATH_RESPONSE from conn_write_pkt. */
@@ -4658,7 +4979,7 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
 
     /* Client does not expect to respond to path validation against
        unknown path */
-    ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge);
+    ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb);
     pcent = NULL;
   }
 
@@ -4680,7 +5001,7 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
     }
   }
 
-  destlen = conn_shape_udp_payload(conn, dcid, destlen);
+  destlen = ngtcp2_min(destlen, NGTCP2_MAX_UDP_PAYLOAD_SIZE);
 
   if (conn->server && !(dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) {
     tx_left = conn_server_tx_left(conn, dcid);
@@ -4694,8 +5015,9 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
   memcpy(lfr.path_response.data, pcent->data, sizeof(lfr.path_response.data));
 
   nwrite = ngtcp2_conn_write_single_frame_pkt(
-      conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &dcid->cid, &lfr,
-      NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path, ts);
+      conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
+      &dcid->cid, &lfr, NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path,
+      ts);
   if (nwrite <= 0) {
     return nwrite;
   }
@@ -4704,7 +5026,7 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn,
     ngtcp2_path_copy(path, &pcent->ps.path);
   }
 
-  ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge);
+  ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge.rb);
 
   dcid->bytes_sent += (uint64_t)nwrite;
 
@@ -4753,6 +5075,12 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn,
     return NGTCP2_ERR_INVALID_ARGUMENT;
   }
 
+  /* Version Negotiation packet is ignored if client has reacted upon
+     Version Negotiation packet. */
+  if (conn->local.settings.original_version != conn->client_chosen_version) {
+    return NGTCP2_ERR_INVALID_ARGUMENT;
+  }
+
   if (payloadlen > sizeof(sv)) {
     p = ngtcp2_mem_malloc(conn->mem, payloadlen);
     if (p == NULL) {
@@ -4766,14 +5094,18 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn,
 
   ngtcp2_log_rx_vn(&conn->log, hd, p, nsv);
 
-  for (i = 0; i < nsv; ++i) {
-    if (p[i] == conn->version) {
-      ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
-                      "ignore Version Negotiation because it contains version "
-                      "selected by client");
+  ngtcp2_qlog_version_negotiation_pkt_received(&conn->qlog, hd, p, nsv);
 
-      rv = NGTCP2_ERR_INVALID_ARGUMENT;
-      goto fin;
+  if (!ngtcp2_is_reserved_version(conn->local.settings.original_version)) {
+    for (i = 0; i < nsv; ++i) {
+      if (p[i] == conn->local.settings.original_version) {
+        ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+                        "ignore Version Negotiation because it contains the "
+                        "original version");
+
+        rv = NGTCP2_ERR_INVALID_ARGUMENT;
+        goto fin;
+      }
     }
   }
 
@@ -4782,10 +5114,6 @@ static int conn_on_version_negotiation(ngtcp2_conn *conn,
     goto fin;
   }
 
-  /* TODO Just move to the terminal state for now in order not to
-     send CONNECTION_CLOSE frame. */
-  conn->state = NGTCP2_CS_DRAINING;
-
 fin:
   if (p != sv) {
     ngtcp2_mem_free(conn->mem, p);
@@ -4823,6 +5151,7 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
   ngtcp2_stream *sfr;
   ngtcp2_strm *strm;
   int rv;
+  int streamfrq_empty;
 
   if (*pfrc == NULL) {
     return 0;
@@ -4839,12 +5168,13 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
 
       strm = ngtcp2_conn_find_stream(conn, sfr->stream_id);
       if (!strm) {
-        ngtcp2_frame_chain_del(frc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
         break;
       }
+      streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm);
       rv = ngtcp2_strm_streamfrq_push(strm, frc);
       if (rv != 0) {
-        ngtcp2_frame_chain_del(frc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
         return rv;
       }
       if (!ngtcp2_strm_is_tx_queued(strm)) {
@@ -4854,6 +5184,9 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
           return rv;
         }
       }
+      if (streamfrq_empty) {
+        ++conn->tx.strmq_nretrans;
+      }
       break;
     case NGTCP2_FRAME_CRYPTO:
       frc = *pfrc;
@@ -4865,7 +5198,7 @@ int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
                              &frc->fr.crypto.offset, frc);
       if (rv != 0) {
         assert(ngtcp2_err_is_fatal(rv));
-        ngtcp2_frame_chain_del(frc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
         return rv;
       }
       break;
@@ -4923,7 +5256,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
   retry.odcid = conn->dcid.current.cid;
 
   rv = ngtcp2_pkt_verify_retry_tag(
-      conn->version, &retry, pkt, pktlen, conn->callbacks.encrypt,
+      conn->client_chosen_version, &retry, pkt, pktlen, conn->callbacks.encrypt,
       &conn->crypto.retry_aead, &conn->crypto.retry_aead_ctx);
   if (rv != 0) {
     ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
@@ -4943,7 +5276,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
     return 0;
   }
 
-  ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd);
+  ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd, &retry);
 
   /* DCID must be updated before invoking callback because client
      generates new initial keys there. */
@@ -5038,7 +5371,7 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr,
   if (num_acked < 0) {
     /* TODO assert this */
     assert(ngtcp2_err_is_fatal((int)num_acked));
-    ngtcp2_frame_chain_list_del(frc, conn->mem);
+    ngtcp2_frame_chain_list_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
     return (int)num_acked;
   }
 
@@ -5130,13 +5463,13 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn,
       return 0;
     }
 
-    strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+    strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
     if (strm == NULL) {
       return NGTCP2_ERR_NOMEM;
     }
     rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL);
     if (rv != 0) {
-      ngtcp2_mem_free(conn->mem, strm);
+      ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
       return rv;
     }
 
@@ -5316,21 +5649,17 @@ static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
  *     User-defined callback function failed; or it does not return
  *     expected result.
  */
-static ngtcp2_ssize decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest,
-                               const ngtcp2_crypto_cipher *hp,
-                               const uint8_t *pkt, size_t pktlen,
-                               size_t pkt_num_offset, ngtcp2_crypto_km *ckm,
-                               const ngtcp2_crypto_cipher_ctx *hp_ctx,
-                               ngtcp2_hp_mask hp_mask) {
+static ngtcp2_ssize
+decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+           const uint8_t *pkt, size_t pktlen, size_t pkt_num_offset,
+           const ngtcp2_crypto_cipher_ctx *hp_ctx, ngtcp2_hp_mask hp_mask) {
   size_t sample_offset;
   uint8_t *p = dest;
   uint8_t mask[NGTCP2_HP_SAMPLELEN];
   size_t i;
   int rv;
-  (void)ckm;
 
   assert(hp_mask);
-  assert(ckm);
 
   if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) {
     return NGTCP2_ERR_PROTO;
@@ -5461,7 +5790,7 @@ static void conn_recv_path_challenge(ngtcp2_conn *conn, const ngtcp2_path *path,
     return;
   }
 
-  ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge);
+  ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge.rb);
   ngtcp2_path_challenge_entry_init(ent, path, fr->data);
 }
 
@@ -5525,10 +5854,21 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
     if (rv != 0) {
       return rv;
     }
-  }
 
-  if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) {
-    pto = conn_compute_pto(conn, &conn->pktns);
+    if (!conn->local.settings.no_pmtud) {
+      ngtcp2_conn_stop_pmtud(conn);
+
+      if (!(pv->flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED)) {
+        rv = conn_start_pmtud(conn);
+        if (rv != 0) {
+          return rv;
+        }
+      }
+    }
+  }
+
+  if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) {
+    pto = conn_compute_pto(conn, &conn->pktns);
     timeout = 3 * ngtcp2_max(pto, pv->fallback_pto);
 
     if (ent_flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED) {
@@ -5658,6 +5998,33 @@ static void pktns_increase_ecn_counts(ngtcp2_pktns *pktns,
   }
 }
 
+/*
+ * vneg_other_versions_includes returns nonzero if |other_versions| of
+ * length |other_versionslen| includes |version|.  |other_versions| is
+ * the wire image of other_versions field of version_information
+ * transport parameter, and each version is encoded in network byte
+ * order.
+ */
+static int vneg_other_versions_includes(const uint8_t *other_versions,
+                                        size_t other_versionslen,
+                                        uint32_t version) {
+  size_t i;
+
+  assert(!(other_versionslen & 0x3));
+
+  if (other_versionslen == 0) {
+    return 0;
+  }
+
+  for (i = 0; i < other_versionslen; i += sizeof(uint32_t)) {
+    if (version == ngtcp2_get_uint32(&other_versions[i])) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
 static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
                             ngtcp2_strm *strm, const ngtcp2_crypto *fr);
 
@@ -5734,8 +6101,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
 
   if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) {
     if (conn->state == NGTCP2_CS_SERVER_INITIAL) {
-      /* Ignore Short packet unless server's first Handshake packet
-         has been transmitted. */
+      /* Ignore 1RTT packet unless server's first Handshake packet has
+         been transmitted. */
       return (ngtcp2_ssize)pktlen;
     }
 
@@ -5744,7 +6111,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     }
 
     ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
-                    "buffering Short packet len=%zu", pktlen);
+                    "buffering 1RTT packet len=%zu", pktlen);
 
     rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, dgramlen,
                          ts);
@@ -5760,8 +6127,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     return NGTCP2_ERR_DISCARD_PKT;
   }
 
-  switch (hd.type) {
-  case NGTCP2_PKT_VERSION_NEGOTIATION:
+  if (hd.type == NGTCP2_PKT_VERSION_NEGOTIATION) {
     hdpktlen = (size_t)nread;
 
     ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
@@ -5797,7 +6163,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
       return NGTCP2_ERR_DISCARD_PKT;
     }
     return NGTCP2_ERR_RECV_VERSION_NEGOTIATION;
-  case NGTCP2_PKT_RETRY:
+  } else if (hd.type == NGTCP2_PKT_RETRY) {
     hdpktlen = (size_t)nread;
 
     ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
@@ -5812,6 +6178,10 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
       return NGTCP2_ERR_DISCARD_PKT;
     }
 
+    if (conn->client_chosen_version != hd.version) {
+      return NGTCP2_ERR_DISCARD_PKT;
+    }
+
     rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen, ts);
     if (rv != 0) {
       if (ngtcp2_err_is_fatal(rv)) {
@@ -5828,7 +6198,18 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
 
   pktlen = (size_t)nread + hd.len;
 
-  if (conn->version != hd.version) {
+  if (!ngtcp2_is_supported_version(hd.version)) {
+    return NGTCP2_ERR_DISCARD_PKT;
+  }
+
+  if (conn->server) {
+    if (hd.version != conn->client_chosen_version &&
+        (!conn->negotiated_version || hd.version != conn->negotiated_version)) {
+      return NGTCP2_ERR_DISCARD_PKT;
+    }
+  } else if (hd.version != conn->client_chosen_version &&
+             conn->negotiated_version &&
+             hd.version != conn->negotiated_version) {
     return NGTCP2_ERR_DISCARD_PKT;
   }
 
@@ -5847,6 +6228,11 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     if (!conn->server) {
       return NGTCP2_ERR_DISCARD_PKT;
     }
+
+    if (hd.version != conn->client_chosen_version) {
+      return NGTCP2_ERR_DISCARD_PKT;
+    }
+
     if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) {
       if (conn->early.ckm) {
         ngtcp2_ssize nread2;
@@ -5885,6 +6271,14 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     assert(conn->in_pktns);
 
     if (conn->server) {
+      if (dgramlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+        ngtcp2_log_info(
+            &conn->log, NGTCP2_LOG_EVENT_PKT,
+            "Initial packet was ignored because it is included in UDP datagram "
+            "less than %zu bytes: %zu bytes",
+            NGTCP2_MAX_UDP_PAYLOAD_SIZE, dgramlen);
+        return NGTCP2_ERR_DISCARD_PKT;
+      }
       if (conn->local.settings.token.len) {
         rv = verify_token(&conn->local.settings.token, &hd);
         if (rv != 0) {
@@ -5905,18 +6299,55 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
           return rv;
         }
       }
-    } else if (hd.token.len != 0) {
-      ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
-                      "packet was ignored because token is not empty");
-      return NGTCP2_ERR_DISCARD_PKT;
+    } else {
+      if (hd.token.len != 0) {
+        ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+                        "packet was ignored because token is not empty");
+        return NGTCP2_ERR_DISCARD_PKT;
+      }
+
+      if (hd.version != conn->client_chosen_version &&
+          !conn->negotiated_version && conn->vneg.version != hd.version) {
+        if (!vneg_other_versions_includes(conn->vneg.other_versions,
+                                          conn->vneg.other_versionslen,
+                                          hd.version)) {
+          return NGTCP2_ERR_DISCARD_PKT;
+        }
+
+        /* Install new Initial keys using QUIC version = hd.version */
+        rv = conn_call_version_negotiation(
+            conn, hd.version,
+            (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)
+                ? &conn->dcid.current.cid
+                : &conn->rcid);
+        if (rv != 0) {
+          return rv;
+        }
+
+        assert(conn->vneg.version == hd.version);
+      }
     }
 
     pktns = conn->in_pktns;
     crypto = &pktns->crypto.strm;
     crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL;
 
+    if (hd.version == conn->client_chosen_version) {
+      ckm = pktns->crypto.rx.ckm;
+      hp_ctx = &pktns->crypto.rx.hp_ctx;
+    } else {
+      assert(conn->vneg.version == hd.version);
+
+      ckm = conn->vneg.rx.ckm;
+      hp_ctx = &conn->vneg.rx.hp_ctx;
+    }
+
     break;
   case NGTCP2_PKT_HANDSHAKE:
+    if (hd.version != conn->negotiated_version) {
+      return NGTCP2_ERR_DISCARD_PKT;
+    }
+
     if (!conn->hs_pktns->crypto.rx.ckm) {
       if (conn->server) {
         ngtcp2_log_info(
@@ -5939,6 +6370,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     pktns = conn->hs_pktns;
     crypto = &pktns->crypto.strm;
     crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+    ckm = pktns->crypto.rx.ckm;
+    hp_ctx = &pktns->crypto.rx.hp_ctx;
 
     break;
   default:
@@ -5952,8 +6385,6 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
   decrypt = conn->callbacks.decrypt;
   aead = &pktns->crypto.ctx.aead;
   hp = &pktns->crypto.ctx.hp;
-  ckm = pktns->crypto.rx.ckm;
-  hp_ctx = &pktns->crypto.rx.hp_ctx;
 
   assert(ckm);
   assert(hp_mask);
@@ -5965,7 +6396,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
   }
 
   nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen,
-                      (size_t)nread, ckm, hp_ctx, hp_mask);
+                      (size_t)nread, hp_ctx, hp_mask);
   if (nwrite < 0) {
     if (ngtcp2_err_is_fatal((int)nwrite)) {
       return nwrite;
@@ -6026,6 +6457,15 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     return NGTCP2_ERR_PROTO;
   }
 
+  if (!conn->server && hd.version != conn->client_chosen_version &&
+      !conn->negotiated_version) {
+    conn->negotiated_version = hd.version;
+
+    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+                    "the negotiated version is 0x%08x",
+                    conn->negotiated_version);
+  }
+
   payload = conn->crypto.decrypt_buf.base;
   payloadlen = (size_t)nwrite;
 
@@ -6110,6 +6550,15 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     case NGTCP2_FRAME_PADDING:
       break;
     case NGTCP2_FRAME_CRYPTO:
+      if (!conn->server && !conn->negotiated_version &&
+          ngtcp2_vec_len(fr->crypto.data, fr->crypto.datacnt)) {
+        conn->negotiated_version = hd.version;
+
+        ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+                        "the negotiated version is 0x%08x",
+                        conn->negotiated_version);
+      }
+
       rv = conn_recv_crypto(conn, crypto_level, crypto, &fr->crypto);
       if (rv != 0) {
         return rv;
@@ -6165,18 +6614,17 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
 
   ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
 
-  conn_update_dcid_max_udp_payload_size(conn, &conn->dcid.current, dgramlen);
-
   return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING
                                            : (ngtcp2_ssize)pktlen;
 }
 
-static int is_crypto_error(int liberr) {
+static int is_unrecoverable_error(int liberr) {
   switch (liberr) {
   case NGTCP2_ERR_CRYPTO:
   case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM:
   case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
   case NGTCP2_ERR_TRANSPORT_PARAM:
+  case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
     return 1;
   }
 
@@ -6186,7 +6634,7 @@ static int is_crypto_error(int liberr) {
 /*
  * conn_recv_handshake_cpkt processes compound packet during
  * handshake.  The buffer pointed by |pkt| might contain multiple
- * packets.  The Short packet must be the last one because it does not
+ * packets.  The 1RTT packet must be the last one because it does not
  * have payload length field.
  *
  * This function returns the same error code returned by
@@ -6200,6 +6648,7 @@ static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn,
   ngtcp2_ssize nread;
   size_t dgramlen = pktlen;
   const uint8_t *origpkt = pkt;
+  uint32_t version;
 
   if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
     conn->dcid.current.bytes_recv += dgramlen;
@@ -6217,35 +6666,36 @@ static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn,
         return NGTCP2_ERR_DRAINING;
       }
 
-      if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) &&
-          /* Not a Version Negotiation packet */
-          pktlen > 4 && ngtcp2_get_uint32(&pkt[1]) > 0 &&
-          ngtcp2_pkt_get_type_long(pkt[0]) == NGTCP2_PKT_INITIAL) {
-        if (conn->server) {
-          if (is_crypto_error((int)nread)) {
-            /* If server gets crypto error from TLS stack, it is
+      if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && pktlen > 4) {
+        /* Not a Version Negotiation packet */
+        version = ngtcp2_get_uint32(&pkt[1]);
+        if (ngtcp2_pkt_get_type_long(version, pkt[0]) == NGTCP2_PKT_INITIAL) {
+          if (conn->server) {
+            if (is_unrecoverable_error((int)nread)) {
+              /* If server gets crypto error from TLS stack, it is
+                 unrecoverable, therefore drop connection. */
+              return nread;
+            }
+
+            /* If server discards first Initial, then drop connection
+               state.  This is because SCID in packet might be corrupted
+               and the current connection state might wrongly discard
+               valid packet and prevent the handshake from
+               completing. */
+            if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) {
+              return NGTCP2_ERR_DROP_CONN;
+            }
+
+            return (ngtcp2_ssize)dgramlen;
+          }
+          /* client */
+          if (is_unrecoverable_error((int)nread)) {
+            /* If client gets crypto error from TLS stack, it is
                unrecoverable, therefore drop connection. */
             return nread;
           }
-
-          /* If server discards first Initial, then drop connection
-             state.  This is because SCID in packet might be corrupted
-             and the current connection state might wrongly discard
-             valid packet and prevent the handshake from
-             completing. */
-          if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) {
-            return NGTCP2_ERR_DROP_CONN;
-          }
-
           return (ngtcp2_ssize)dgramlen;
         }
-        /* client */
-        if (is_crypto_error((int)nread)) {
-          /* If client gets crypto error from TLS stack, it is
-             unrecoverable, therefore drop connection. */
-          return nread;
-        }
-        return (ngtcp2_ssize)dgramlen;
       }
 
       if (nread == NGTCP2_ERR_DISCARD_PKT) {
@@ -6298,11 +6748,9 @@ int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
     max_tx_offset = 0;
   }
 
-  rv = ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset,
-                        max_tx_offset, stream_user_data, conn->mem);
-  if (rv != 0) {
-    return rv;
-  }
+  ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset,
+                   max_tx_offset, stream_user_data, &conn->frc_objalloc,
+                   conn->mem);
 
   rv = ngtcp2_map_insert(&conn->strms, (ngtcp2_map_key_type)strm->stream_id,
                          strm);
@@ -6341,7 +6789,7 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm,
   int rv;
   uint64_t offset;
   uint32_t sdflags;
-  int handshake_completed = conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED;
+  int handshake_completed = conn_is_handshake_completed(conn);
 
   if (!strm->rx.rob) {
     return 0;
@@ -6573,14 +7021,14 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
       return 0;
     }
 
-    strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+    strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
     if (strm == NULL) {
       return NGTCP2_ERR_NOMEM;
     }
     /* TODO Perhaps, call new_stream callback? */
     rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL);
     if (rv != 0) {
-      ngtcp2_mem_free(conn->mem, strm);
+      ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
       return rv;
     }
 
@@ -6686,7 +7134,7 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) {
       if (fin) {
         sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN;
       }
-      if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+      if (!conn_is_handshake_completed(conn)) {
         sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY;
       }
       rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data,
@@ -6726,7 +7174,7 @@ static int conn_reset_stream(ngtcp2_conn *conn, ngtcp2_strm *strm,
   ngtcp2_frame_chain *frc;
   ngtcp2_pktns *pktns = &conn->pktns;
 
-  rv = ngtcp2_frame_chain_new(&frc, conn->mem);
+  rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc);
   if (rv != 0) {
     return rv;
   }
@@ -6759,7 +7207,7 @@ static int conn_stop_sending(ngtcp2_conn *conn, ngtcp2_strm *strm,
   ngtcp2_frame_chain *frc;
   ngtcp2_pktns *pktns = &conn->pktns;
 
-  rv = ngtcp2_frame_chain_new(&frc, conn->mem);
+  rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc);
   if (rv != 0) {
     return rv;
   }
@@ -7006,13 +7454,13 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn,
 
     /* Frame is received reset before we create ngtcp2_strm
        object. */
-    strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+    strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
     if (strm == NULL) {
       return NGTCP2_ERR_NOMEM;
     }
     rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL);
     if (rv != 0) {
-      ngtcp2_mem_free(conn->mem, strm);
+      ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
       return rv;
     }
 
@@ -7037,6 +7485,11 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn,
 
   strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST;
 
+  if (ngtcp2_strm_is_tx_queued(strm) && !ngtcp2_strm_streamfrq_empty(strm)) {
+    assert(conn->tx.strmq_nretrans);
+    --conn->tx.strmq_nretrans;
+  }
+
   ngtcp2_strm_streamfrq_clear(strm);
 
   return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm);
@@ -7088,18 +7541,18 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path,
       (!pv || (!check_stateless_reset(&pv->dcid, path, &sr) &&
                (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) ||
                 !check_stateless_reset(&pv->fallback_dcid, path, &sr))))) {
-    len = ngtcp2_ringbuf_len(&conn->dcid.retired);
+    len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
     for (i = 0; i < len; ++i) {
-      dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i);
+      dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i);
       if (check_stateless_reset(dcid, path, &sr)) {
         break;
       }
     }
 
     if (i == len) {
-      len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+      len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
       for (i = 0; i < len; ++i) {
-        dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+        dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
         if (check_stateless_reset(dcid, path, &sr)) {
           break;
         }
@@ -7115,6 +7568,8 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path,
 
   ngtcp2_log_rx_sr(&conn->log, &sr);
 
+  ngtcp2_qlog_stateless_reset_pkt_received(&conn->qlog, &sr);
+
   return conn_call_recv_stateless_reset(conn, &sr);
 }
 
@@ -7172,16 +7627,20 @@ static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb,
     if (rv != 0) {
       return rv;
     }
+
     if (i == 0) {
       ngtcp2_ringbuf_pop_front(rb);
-    } else if (i == ngtcp2_ringbuf_len(rb) - 1) {
+      continue;
+    }
+
+    if (i == ngtcp2_ringbuf_len(rb) - 1) {
       ngtcp2_ringbuf_pop_back(rb);
       break;
-    } else {
-      last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1);
-      ngtcp2_dcid_copy(dcid, last);
-      ngtcp2_ringbuf_pop_back(rb);
     }
+
+    last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1);
+    ngtcp2_dcid_copy(dcid, last);
+    ngtcp2_ringbuf_pop_back(rb);
   }
 
   return 0;
@@ -7235,10 +7694,10 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
     }
   }
 
-  len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+  len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
 
   for (i = 0; i < len; ++i) {
-    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
     rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid,
                                        fr->stateless_reset_token);
     if (rv != 0) {
@@ -7249,10 +7708,10 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
     }
   }
 
-  len = ngtcp2_ringbuf_len(&conn->dcid.unused);
+  len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb);
 
   for (i = 0; i < len; ++i) {
-    dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, i);
+    dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, i);
     rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid,
                                        fr->stateless_reset_token);
     if (rv != 0) {
@@ -7266,13 +7725,13 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
   if (conn->dcid.retire_prior_to < fr->retire_prior_to) {
     conn->dcid.retire_prior_to = fr->retire_prior_to;
 
-    rv =
-        conn_retire_dcid_prior_to(conn, &conn->dcid.bound, fr->retire_prior_to);
+    rv = conn_retire_dcid_prior_to(conn, &conn->dcid.bound.rb,
+                                   fr->retire_prior_to);
     if (rv != 0) {
       return rv;
     }
 
-    rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused,
+    rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused.rb,
                                    conn->dcid.retire_prior_to);
     if (rv != 0) {
       return rv;
@@ -7286,13 +7745,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
        For example, a peer might send seq = 50000 and retire_prior_to
        = 50000.  Then send NEW_CONNECTION_ID frames with seq <
        50000. */
-    /* TODO we might queue lots of RETIRE_CONNECTION_ID frame here
-       because conn->dcid.num_retire_queued is incremented when the
-       frame is serialized. */
-    if (conn->dcid.num_retire_queued < NGTCP2_MAX_DCID_POOL_SIZE * 2) {
-      return conn_retire_dcid_seq(conn, fr->seq);
-    }
-    return 0;
+    return conn_retire_dcid_seq(conn, fr->seq);
   }
 
   if (found) {
@@ -7312,7 +7765,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
     ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap);
   }
 
-  len = ngtcp2_ringbuf_len(&conn->dcid.unused);
+  len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb);
 
   if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) {
     ++extra_dcid;
@@ -7339,7 +7792,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn,
     return 0;
   }
 
-  dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused);
+  dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb);
   ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token);
 
   return 0;
@@ -7364,7 +7817,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
   int rv;
 
   if (conn->dcid.current.seq < conn->dcid.retire_prior_to) {
-    if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+    if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
       return 0;
     }
 
@@ -7373,7 +7826,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
       return rv;
     }
 
-    dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+    dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
     if (pv) {
       if (conn->dcid.current.seq == pv->dcid.seq) {
         ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid);
@@ -7385,7 +7838,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
     }
 
     ngtcp2_dcid_copy_cid_token(&conn->dcid.current, dcid);
-    ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+    ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
 
     rv = conn_call_activate_dcid(conn, &conn->dcid.current);
     if (rv != 0) {
@@ -7395,13 +7848,13 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
 
   if (pv) {
     if (pv->dcid.seq < conn->dcid.retire_prior_to) {
-      if (ngtcp2_ringbuf_len(&conn->dcid.unused)) {
+      if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) {
         rv = conn_retire_dcid(conn, &pv->dcid, ts);
         if (rv != 0) {
           return rv;
         }
 
-        dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+        dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
 
         if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
             pv->dcid.seq == pv->fallback_dcid.seq) {
@@ -7409,7 +7862,7 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
         }
 
         ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid);
-        ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+        ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
 
         rv = conn_call_activate_dcid(conn, &pv->dcid);
         if (rv != 0) {
@@ -7425,15 +7878,15 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn,
     }
     if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) &&
         pv->fallback_dcid.seq < conn->dcid.retire_prior_to) {
-      if (ngtcp2_ringbuf_len(&conn->dcid.unused)) {
+      if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) {
         rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts);
         if (rv != 0) {
           return rv;
         }
 
-        dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+        dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
         ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid);
-        ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+        ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
 
         rv = conn_call_activate_dcid(conn, &pv->fallback_dcid);
         if (rv != 0) {
@@ -7583,7 +8036,7 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) {
   ngtcp2_pv *pv;
   ngtcp2_dcid *dcid;
 
-  if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+  if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
     return 0;
   }
 
@@ -7602,7 +8055,7 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) {
 
   assert(conn->pv == NULL);
 
-  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
   ngtcp2_dcid_set_path(dcid, &ps.path);
 
   pto = conn_compute_pto(conn, &conn->pktns);
@@ -7616,7 +8069,7 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) {
     return rv;
   }
 
-  ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+  ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
   conn->pv = pv;
 
   return conn_call_activate_dcid(conn, &pv->dcid);
@@ -7777,9 +8230,11 @@ static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
 
 /*
  * conn_rotate_keys rotates keys.  The current key moves to old key,
- * and new key moves to the current key.
+ * and new key moves to the current key.  If the local endpoint
+ * initiated this key update, pass nonzero as |initiator|.
  */
-static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) {
+static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num,
+                             int initiator) {
   ngtcp2_pktns *pktns = &conn->pktns;
 
   assert(conn->crypto.key_update.new_rx_ckm);
@@ -7803,6 +8258,9 @@ static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) {
   pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1;
 
   conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED;
+  if (initiator) {
+    conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR;
+  }
 }
 
 /*
@@ -7857,7 +8315,16 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
     conn_reset_congestion_state(conn, ts);
     conn->dcid.current.bytes_recv += dgramlen;
     conn_reset_ecn_validation_state(conn);
-    return conn_abort_pv(conn, ts);
+
+    rv = conn_abort_pv(conn, ts);
+    if (rv != 0) {
+      return rv;
+    }
+
+    /* Run PMTUD just in case if it is prematurely aborted */
+    assert(!conn->pmtud);
+
+    return conn_start_pmtud(conn);
   }
 
   remote_addr_cmp =
@@ -7891,10 +8358,10 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
   initial_pto = conn_compute_initial_pto(conn, &conn->pktns);
   timeout = 3 * ngtcp2_max(pto, initial_pto);
 
-  len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+  len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
 
   for (i = 0; i < len; ++i) {
-    bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+    bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
     if (ngtcp2_path_eq(&bound_dcid->ps.path, path)) {
       ngtcp2_log_info(
           &conn->log, NGTCP2_LOG_EVENT_CON,
@@ -7902,13 +8369,13 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
 
       ngtcp2_dcid_copy(&dcid, bound_dcid);
       if (i == 0) {
-        ngtcp2_ringbuf_pop_front(&conn->dcid.bound);
-      } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound) - 1) {
-        ngtcp2_ringbuf_pop_back(&conn->dcid.bound);
+        ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb);
+      } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) {
+        ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
       } else {
-        last = ngtcp2_ringbuf_get(&conn->dcid.bound, len - 1);
+        last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, len - 1);
         ngtcp2_dcid_copy(bound_dcid, last);
-        ngtcp2_ringbuf_pop_back(&conn->dcid.bound);
+        ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
       }
       require_new_cid = 0;
 
@@ -7924,11 +8391,11 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
 
   if (i == len) {
     if (require_new_cid) {
-      if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+      if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
         return NGTCP2_ERR_CONN_ID_BLOCKED;
       }
-      ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused, 0));
-      ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+      ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0));
+      ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
 
       rv = conn_call_activate_dcid(conn, &dcid);
       if (rv != 0) {
@@ -7978,6 +8445,8 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
 
   conn_reset_ecn_validation_state(conn);
 
+  ngtcp2_conn_stop_pmtud(conn);
+
   if (conn->pv) {
     ngtcp2_log_info(
         &conn->log, NGTCP2_LOG_EVENT_PTV,
@@ -7994,7 +8463,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
 }
 
 /*
- * conn_recv_pkt_from_new_path is called when a Short packet is
+ * conn_recv_pkt_from_new_path is called when a 1RTT packet is
  * received from new path (not current path).  This packet would be a
  * packet which only contains probing frame, or reordered packet, or a
  * path is being validated.
@@ -8039,7 +8508,6 @@ static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn,
 
   ngtcp2_dcid_set_path(bound_dcid, path);
   bound_dcid->bytes_recv += dgramlen;
-  conn_update_dcid_max_udp_payload_size(conn, bound_dcid, dgramlen);
 
   return 0;
 }
@@ -8236,7 +8704,14 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
       return NGTCP2_ERR_DISCARD_PKT;
     }
 
-    if (pktlen < (size_t)nread + hd.len || conn->version != hd.version) {
+    if (pktlen < (size_t)nread + hd.len) {
+      return NGTCP2_ERR_DISCARD_PKT;
+    }
+
+    assert(conn->negotiated_version);
+
+    if (hd.version != conn->client_chosen_version &&
+        hd.version != conn->negotiated_version) {
       return NGTCP2_ERR_DISCARD_PKT;
     }
 
@@ -8257,6 +8732,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
                       "delayed Initial packet was discarded");
       return (ngtcp2_ssize)pktlen;
     case NGTCP2_PKT_HANDSHAKE:
+      if (hd.version != conn->negotiated_version) {
+        return NGTCP2_ERR_DISCARD_PKT;
+      }
+
       if (!conn->hs_pktns) {
         ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
                         "delayed Handshake packet was discarded");
@@ -8272,7 +8751,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
       decrypt = conn->callbacks.decrypt;
       break;
     case NGTCP2_PKT_0RTT:
-      if (!conn->server) {
+      if (!conn->server || hd.version != conn->client_chosen_version) {
         return NGTCP2_ERR_DISCARD_PKT;
       }
 
@@ -8317,7 +8796,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
   }
 
   nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen,
-                      (size_t)nread, ckm, hp_ctx, hp_mask);
+                      (size_t)nread, hp_ctx, hp_mask);
   if (nwrite < 0) {
     if (ngtcp2_err_is_fatal((int)nwrite)) {
       return nwrite;
@@ -8341,7 +8820,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
 
   ngtcp2_log_rx_pkt_hd(&conn->log, &hd);
 
-  if (hd.type == NGTCP2_PKT_SHORT) {
+  if (hd.type == NGTCP2_PKT_1RTT) {
     key_phase_bit_changed = conn_key_phase_changed(conn, &hd);
   }
 
@@ -8351,7 +8830,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
   }
 
   if (key_phase_bit_changed) {
-    assert(hd.type == NGTCP2_PKT_SHORT);
+    assert(hd.type == NGTCP2_PKT_1RTT);
 
     ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "unexpected KEY_PHASE");
 
@@ -8394,7 +8873,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
 
     assert(NGTCP2_ERR_DECRYPT == nwrite);
 
-    if (hd.type == NGTCP2_PKT_SHORT &&
+    if (hd.type == NGTCP2_PKT_1RTT &&
         ++conn->crypto.decryption_failure_count >=
             pktns->crypto.ctx.max_decryption_failure) {
       return NGTCP2_ERR_AEAD_LIMIT_REACHED;
@@ -8698,7 +9177,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     }
   }
 
-  if (conn->server && hd.type == NGTCP2_PKT_SHORT &&
+  if (conn->server && hd.type == NGTCP2_PKT_1RTT &&
       !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
     if (non_probing_pkt && pktns->rx.max_pkt_num < hd.pkt_num &&
         !conn_path_validation_in_progress(conn, path)) {
@@ -8726,10 +9205,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     }
   }
 
-  if (hd.type == NGTCP2_PKT_SHORT) {
+  if (hd.type == NGTCP2_PKT_1RTT) {
     if (ckm == conn->crypto.key_update.new_rx_ckm) {
       ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "rotate keys");
-      conn_rotate_keys(conn, hd.pkt_num);
+      conn_rotate_keys(conn, hd.pkt_num, /* initiator = */ 0);
     } else if (ckm->pkt_num > hd.pkt_num) {
       ckm->pkt_num = hd.pkt_num;
     }
@@ -8767,17 +9246,13 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
 
   ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat);
 
-  if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) {
-    conn_update_dcid_max_udp_payload_size(conn, &conn->dcid.current, dgramlen);
-  }
-
   return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING
                                            : (ngtcp2_ssize)pktlen;
 }
 
 /*
- * conn_process_buffered_protected_pkt processes buffered 0RTT or
- * Short packets.
+ * conn_process_buffered_protected_pkt processes buffered 0RTT or 1RTT
+ * packets.
  *
  * This function returns 0 if it succeeds, or the same negative error
  * codes from conn_recv_pkt.
@@ -8942,7 +9417,7 @@ static int conn_handshake_completed(ngtcp2_conn *conn) {
 
 /*
  * conn_recv_cpkt processes compound packet after handshake.  The
- * buffer pointed by |pkt| might contain multiple packets.  The Short
+ * buffer pointed by |pkt| might contain multiple packets.  The 1RTT
  * packet must be the last one because it does not have payload length
  * field.
  *
@@ -9000,11 +9475,11 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
  * retired path list.
  */
 static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) {
-  size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired);
+  size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
   ngtcp2_dcid *dcid;
 
   for (i = 0; i < len; ++i) {
-    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i);
+    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i);
     if (ngtcp2_path_eq(&dcid->ps.path, path)) {
       return 1;
     }
@@ -9024,7 +9499,7 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) {
 
   assert(conn->server);
 
-  rv = ngtcp2_frame_chain_new(&nfrc, conn->mem);
+  rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc);
   if (rv != 0) {
     return rv;
   }
@@ -9044,10 +9519,10 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) {
  * |pktlen| is the length of the buffer.  |path| is the network path.
  *
  * This function returns the number of bytes processed.  Unless the
- * last packet is Short packet and an application decryption key has
+ * last packet is 1RTT packet and an application decryption key has
  * been installed, it returns |pktlen| if it succeeds.  If it finds
- * Short packet and an application decryption key has been installed,
- * it returns the number of bytes just before Short packet begins.
+ * 1RTT packet and an application decryption key has been installed,
+ * it returns the number of bytes just before 1RTT packet begins.
  *
  * This function returns the number of bytes processed if it succeeds,
  * or one of the following negative error codes: (TBD).
@@ -9084,9 +9559,8 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn,
       }
     }
 
-    if ((conn->flags & (NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED |
-                        NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) ==
-        NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) {
+    if (conn_is_handshake_completed(conn) &&
+        !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) {
       rv = conn_handshake_completed(conn);
       if (rv != 0) {
         return rv;
@@ -9159,7 +9633,7 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn,
       conn_discard_initial_state(conn, ts);
     }
 
-    if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+    if (!conn_is_handshake_completed(conn)) {
       /* If server hits amplification limit, it cancels loss detection
          timer.  If server receives a packet from client, the limit is
          increased and server can send more.  If server has
@@ -9183,10 +9657,10 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn,
       }
 
       if ((size_t)nread < pktlen) {
-        /* We have Short packet and application rx key, but the
+        /* We have 1RTT packet and application rx key, but the
            handshake has not completed yet. */
         ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
-                        "buffering Short packet len=%zu",
+                        "buffering 1RTT packet len=%zu",
                         pktlen - (size_t)nread);
 
         rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt + nread,
@@ -9196,7 +9670,7 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn,
           return rv;
         }
 
-        return (ssize_t)pktlen;
+        return (ngtcp2_ssize)pktlen;
       }
 
       return nread;
@@ -9229,6 +9703,13 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn,
       return rv;
     }
 
+    if (!conn->local.settings.no_pmtud) {
+      rv = conn_start_pmtud(conn);
+      if (rv != 0) {
+        return rv;
+      }
+    }
+
     conn->pktns.rtb.persistent_congestion_start_ts = ts;
 
     /* Re-arm loss detection timer here after handshake has been
@@ -9252,6 +9733,7 @@ int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path,
                                    ngtcp2_tstamp ts) {
   int rv = 0;
   ngtcp2_ssize nread = 0;
+  const ngtcp2_pkt_info zero_pi = {0};
   (void)pkt_info_version;
 
   conn->log.last_ts = ts;
@@ -9273,6 +9755,10 @@ int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path,
     return 0;
   }
 
+  if (!pi) {
+    pi = &zero_pi;
+  }
+
   switch (conn->state) {
   case NGTCP2_CS_CLIENT_INITIAL:
   case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
@@ -9362,9 +9848,10 @@ static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) {
 static ngtcp2_ssize conn_retransmit_retry_early(ngtcp2_conn *conn,
                                                 ngtcp2_pkt_info *pi,
                                                 uint8_t *dest, size_t destlen,
+                                                uint8_t flags,
                                                 ngtcp2_tstamp ts) {
-  return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT,
-                        NGTCP2_WRITE_PKT_FLAG_NONE, ts);
+  return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT, flags,
+                        ts);
 }
 
 /*
@@ -9413,7 +9900,7 @@ static int conn_validate_early_transport_params_limits(ngtcp2_conn *conn) {
 /*
  * conn_write_handshake writes QUIC handshake packets to the buffer
  * pointed by |dest| of length |destlen|.  |write_datalen| specifies
- * the expected length of 0RTT or Short packet payload.  Specify 0 to
+ * the expected length of 0RTT or 1RTT packet payload.  Specify 0 to
  * |write_datalen| if there is no such data.
  *
  * This function returns the number of bytes written to the buffer, or
@@ -9467,8 +9954,11 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
     }
 
     if (pending_early_datalen) {
-      early_spktlen = conn_retransmit_retry_early(conn, pi, dest + nwrite,
-                                                  destlen - (size_t)nwrite, ts);
+      early_spktlen = conn_retransmit_retry_early(
+          conn, pi, dest + nwrite, destlen - (size_t)nwrite,
+          nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING
+                 : NGTCP2_WRITE_PKT_FLAG_NONE,
+          ts);
 
       if (early_spktlen < 0) {
         assert(ngtcp2_err_is_fatal((int)early_spktlen));
@@ -9503,9 +9993,10 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
       destlen -= (size_t)nwrite;
     }
 
-    if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+    if (!conn_is_handshake_completed(conn)) {
       if (!(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) {
-        nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen, ts);
+        nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen,
+                                             NGTCP2_WRITE_PKT_FLAG_NONE, ts);
         if (nwrite < 0) {
           return nwrite;
         }
@@ -9546,10 +10037,10 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
     conn->state = NGTCP2_CS_POST_HANDSHAKE;
 
     if (conn->remote.transport_params.preferred_address_present) {
-      assert(!ngtcp2_ringbuf_full(&conn->dcid.unused));
+      assert(!ngtcp2_ringbuf_full(&conn->dcid.unused.rb));
 
       paddr = &conn->remote.transport_params.preferred_address;
-      dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused);
+      dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb);
       ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token);
 
       rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1);
@@ -9573,6 +10064,13 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
 
     conn_process_early_rtb(conn);
 
+    if (!conn->local.settings.no_pmtud) {
+      rv = conn_start_pmtud(conn);
+      if (rv != 0) {
+        return rv;
+      }
+    }
+
     return res;
   case NGTCP2_CS_SERVER_INITIAL:
     nwrite =
@@ -9656,6 +10154,7 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn,
   uint64_t write_datalen = 0;
   uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
   int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
+  uint32_t version;
 
   assert(!conn->server);
 
@@ -9716,8 +10215,10 @@ static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn,
 
     /* If spktlen > 0, we are making a compound packet.  If Initial
        packet is written, we have to pad bytes to 0-RTT packet. */
+    version = conn->negotiated_version ? conn->negotiated_version
+                                       : conn->client_chosen_version;
     if (spktlen > 0 &&
-        ngtcp2_pkt_get_type_long(dest[0]) == NGTCP2_PKT_INITIAL) {
+        ngtcp2_pkt_get_type_long(version, dest[0]) == NGTCP2_PKT_INITIAL) {
       wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
       conn->pkt.require_padding = 1;
     } else {
@@ -9766,7 +10267,7 @@ void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) {
 }
 
 int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) {
-  return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) &&
+  return conn_is_handshake_completed(conn) &&
          (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED);
 }
 
@@ -9803,12 +10304,6 @@ int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen) {
     return (int)nread;
   }
 
-  if (p->version != NGTCP2_PROTO_VER_V1 &&
-      (p->version < NGTCP2_PROTO_VER_DRAFT_MIN ||
-       NGTCP2_PROTO_VER_DRAFT_MAX < p->version)) {
-    return NGTCP2_ERR_VERSION_NEGOTIATION;
-  }
-
   switch (p->type) {
   case NGTCP2_PKT_INITIAL:
     break;
@@ -9880,6 +10375,57 @@ int ngtcp2_conn_install_initial_key(
   return 0;
 }
 
+int ngtcp2_conn_install_vneg_initial_key(
+    ngtcp2_conn *conn, uint32_t version,
+    const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv,
+    const ngtcp2_crypto_cipher_ctx *rx_hp_ctx,
+    const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv,
+    const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) {
+  int rv;
+
+  assert(ivlen >= 8);
+
+  conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.rx.hp_ctx);
+  conn->vneg.rx.hp_ctx.native_handle = NULL;
+
+  if (conn->vneg.rx.ckm) {
+    conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.rx.ckm->aead_ctx);
+    ngtcp2_crypto_km_del(conn->vneg.rx.ckm, conn->mem);
+    conn->vneg.rx.ckm = NULL;
+  }
+
+  conn_call_delete_crypto_cipher_ctx(conn, &conn->vneg.tx.hp_ctx);
+  conn->vneg.tx.hp_ctx.native_handle = NULL;
+
+  if (conn->vneg.tx.ckm) {
+    conn_call_delete_crypto_aead_ctx(conn, &conn->vneg.tx.ckm->aead_ctx);
+    ngtcp2_crypto_km_del(conn->vneg.tx.ckm, conn->mem);
+    conn->vneg.tx.ckm = NULL;
+  }
+
+  rv = ngtcp2_crypto_km_new(&conn->vneg.rx.ckm, NULL, 0, NULL, rx_iv, ivlen,
+                            conn->mem);
+  if (rv != 0) {
+    return rv;
+  }
+
+  rv = ngtcp2_crypto_km_new(&conn->vneg.tx.ckm, NULL, 0, NULL, tx_iv, ivlen,
+                            conn->mem);
+  if (rv != 0) {
+    return rv;
+  }
+
+  /* Take owner ship after we are sure that no failure occurs, so that
+     caller can delete these contexts on failure. */
+  conn->vneg.rx.ckm->aead_ctx = *rx_aead_ctx;
+  conn->vneg.rx.hp_ctx = *rx_hp_ctx;
+  conn->vneg.tx.ckm->aead_ctx = *tx_aead_ctx;
+  conn->vneg.tx.hp_ctx = *tx_hp_ctx;
+  conn->vneg.version = version;
+
+  return 0;
+}
+
 int ngtcp2_conn_install_rx_handshake_key(
     ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx,
     const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) {
@@ -10029,7 +10575,7 @@ int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
     return NGTCP2_ERR_INVALID_STATE;
   }
 
-  conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM);
+  conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM, /* initiator = */ 1);
 
   return 0;
 }
@@ -10052,8 +10598,8 @@ static int conn_retire_stale_bound_dcid(ngtcp2_conn *conn,
   ngtcp2_dcid *dcid, *last;
   int rv;
 
-  for (i = 0; i < ngtcp2_ringbuf_len(&conn->dcid.bound);) {
-    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+  for (i = 0; i < ngtcp2_ringbuf_len(&conn->dcid.bound.rb);) {
+    dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
 
     assert(dcid->cid.datalen);
 
@@ -10068,19 +10614,19 @@ static int conn_retire_stale_bound_dcid(ngtcp2_conn *conn,
     }
 
     if (i == 0) {
-      ngtcp2_ringbuf_pop_front(&conn->dcid.bound);
+      ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb);
       continue;
     }
 
-    if (i == ngtcp2_ringbuf_len(&conn->dcid.bound) - 1) {
-      ngtcp2_ringbuf_pop_back(&conn->dcid.bound);
+    if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) {
+      ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
       break;
     }
 
-    last = ngtcp2_ringbuf_get(&conn->dcid.bound,
-                              ngtcp2_ringbuf_len(&conn->dcid.bound) - 1);
+    last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb,
+                              ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1);
     ngtcp2_dcid_copy(dcid, last);
-    ngtcp2_ringbuf_pop_back(&conn->dcid.bound);
+    ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb);
   }
 
   return 0;
@@ -10101,27 +10647,34 @@ ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) {
     res = ngtcp2_pv_next_expiry(conn->pv);
   }
 
+  if (conn->pmtud) {
+    res = ngtcp2_min(res, conn->pmtud->expiry);
+  }
+
   if (!ngtcp2_pq_empty(&conn->scid.used)) {
     scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe);
     if (scid->retired_ts != UINT64_MAX) {
-      res = ngtcp2_min(res, scid->retired_ts + pto);
+      t = scid->retired_ts + pto;
+      res = ngtcp2_min(res, t);
     }
   }
 
-  if (ngtcp2_ringbuf_len(&conn->dcid.retired)) {
-    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0);
-    res = ngtcp2_min(res, dcid->retired_ts + pto);
+  if (ngtcp2_ringbuf_len(&conn->dcid.retired.rb)) {
+    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0);
+    t = dcid->retired_ts + pto;
+    res = ngtcp2_min(res, t);
   }
 
   if (conn->dcid.current.cid.datalen) {
-    len = ngtcp2_ringbuf_len(&conn->dcid.bound);
+    len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb);
     for (i = 0; i < len; ++i) {
-      dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i);
+      dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i);
 
       assert(dcid->cid.datalen);
       assert(dcid->bound_ts != UINT64_MAX);
 
-      res = ngtcp2_min(res, dcid->bound_ts + 3 * pto);
+      t = dcid->bound_ts + 3 * pto;
+      res = ngtcp2_min(res, t);
     }
   }
 
@@ -10145,7 +10698,7 @@ ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) {
 }
 
 static ngtcp2_tstamp conn_handshake_expiry(ngtcp2_conn *conn) {
-  if ((conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) ||
+  if (conn_is_handshake_completed(conn) ||
       conn->local.settings.handshake_timeout == UINT64_MAX) {
     return UINT64_MAX;
   }
@@ -10161,11 +10714,13 @@ ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) {
   ngtcp2_tstamp t4 = ngtcp2_conn_lost_pkt_expiry(conn);
   ngtcp2_tstamp t5 = conn_keep_alive_expiry(conn);
   ngtcp2_tstamp t6 = conn_handshake_expiry(conn);
+  ngtcp2_tstamp t7 = ngtcp2_conn_get_idle_expiry(conn);
   ngtcp2_tstamp res = ngtcp2_min(t1, t2);
   res = ngtcp2_min(res, t3);
   res = ngtcp2_min(res, t4);
   res = ngtcp2_min(res, t5);
   res = ngtcp2_min(res, t6);
+  res = ngtcp2_min(res, t7);
   return ngtcp2_min(res, conn->tx.pacing.next_ts);
 }
 
@@ -10173,6 +10728,10 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
   int rv;
   ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns);
 
+  if (ngtcp2_conn_get_idle_expiry(conn) <= ts) {
+    return NGTCP2_ERR_IDLE_CLOSE;
+  }
+
   ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts);
 
   conn_cancel_expired_keep_alive_timer(conn, ts);
@@ -10185,6 +10744,13 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
     ngtcp2_pv_cancel_expired_timer(conn->pv, ts);
   }
 
+  if (conn->pmtud) {
+    ngtcp2_pmtud_handle_expiry(conn->pmtud, ts);
+    if (ngtcp2_pmtud_finished(conn->pmtud)) {
+      ngtcp2_conn_stop_pmtud(conn);
+    }
+  }
+
   if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) {
     rv = ngtcp2_conn_on_loss_detection_timer(conn, ts);
     if (rv != 0) {
@@ -10211,7 +10777,7 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
     }
   }
 
-  if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) &&
+  if (!conn_is_handshake_completed(conn) &&
       conn->local.settings.handshake_timeout != UINT64_MAX &&
       conn->local.settings.initial_ts +
               conn->local.settings.handshake_timeout <=
@@ -10288,6 +10854,42 @@ void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
   ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts);
 }
 
+/*
+ * select_preferred_version selects the most preferred version.
+ * |fallback_version| is chosen if no preference is made, or
+ * |preferred_versions| does not include any of |chosen_version| or
+ * |other_versions|.  |chosen_version| is treated as an extra other
+ * version.
+ */
+static uint32_t select_preferred_version(const uint32_t *preferred_versions,
+                                         size_t preferred_versionslen,
+                                         uint32_t chosen_version,
+                                         const uint8_t *other_versions,
+                                         size_t other_versionslen,
+                                         uint32_t fallback_version) {
+  size_t i, j;
+
+  if (!preferred_versionslen ||
+      (!other_versionslen && chosen_version == fallback_version)) {
+    return fallback_version;
+  }
+
+  for (i = 0; i < preferred_versionslen; ++i) {
+    if (preferred_versions[i] == chosen_version) {
+      return chosen_version;
+    }
+    for (j = 0; j < other_versionslen; j += sizeof(uint32_t)) {
+      if (preferred_versions[i] != ngtcp2_get_uint32(&other_versions[j])) {
+        continue;
+      }
+
+      return preferred_versions[i];
+    }
+  }
+
+  return fallback_version;
+}
+
 /*
  * conn_client_validate_transport_params validates |params| as client.
  * |params| must be sent with Encrypted Extensions.
@@ -10295,12 +10897,11 @@ void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
  * This function returns 0 if it succeeds, or one of the following
  * negative error codes:
  *
- * NGTCP2_ERR_PROTO
- *     Validation against either of original_dcid and retry_scid is
- *     failed.
  * NGTCP2_ERR_TRANSPORT_PARAM
  *     params contains preferred address but server chose zero-length
  *     connection ID.
+ * NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE
+ *     Validation against version negotiation parameters failed.
  */
 static int
 conn_client_validate_transport_params(ngtcp2_conn *conn,
@@ -10325,9 +10926,64 @@ conn_client_validate_transport_params(ngtcp2_conn *conn,
     return NGTCP2_ERR_TRANSPORT_PARAM;
   }
 
+  if (params->version_info_present) {
+    if (conn->negotiated_version != params->version_info.chosen_version) {
+      return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+    }
+
+    assert(vneg_other_versions_includes(conn->vneg.other_versions,
+                                        conn->vneg.other_versionslen,
+                                        conn->negotiated_version));
+  } else if (conn->client_chosen_version != conn->negotiated_version ||
+             conn->client_chosen_version !=
+                 conn->local.settings.original_version) {
+    return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+  }
+
+  /* When client reacted upon Version Negotiation */
+  if (conn->local.settings.original_version != conn->client_chosen_version) {
+    assert(params->version_info_present);
+
+    /* Server choose original version after Version Negotiation.
+       Draft does not say this particular case, but this smells like
+       misbehaved server because server should accept original_version
+       in the original connection. */
+    if (conn->local.settings.original_version ==
+        params->version_info.chosen_version) {
+      return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+    }
+
+    /* Check version downgrade on incompatible version negotiation. */
+    if (params->version_info.other_versionslen == 0) {
+      return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+    }
+
+    if (conn->client_chosen_version !=
+        select_preferred_version(conn->vneg.preferred_versions,
+                                 conn->vneg.preferred_versionslen,
+                                 params->version_info.chosen_version,
+                                 params->version_info.other_versions,
+                                 params->version_info.other_versionslen,
+                                 /* fallback_version = */ 0)) {
+      return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+    }
+  }
+
   return 0;
 }
 
+uint32_t
+ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn,
+                                     const ngtcp2_version_info *version_info) {
+  assert(conn->server);
+  assert(conn->client_chosen_version == version_info->chosen_version);
+
+  return select_preferred_version(
+      conn->vneg.preferred_versions, conn->vneg.preferred_versionslen,
+      version_info->chosen_version, version_info->other_versions,
+      version_info->other_versionslen, version_info->chosen_version);
+}
+
 int ngtcp2_conn_set_remote_transport_params_versioned(
     ngtcp2_conn *conn, int transport_params_version,
     const ngtcp2_transport_params *params) {
@@ -10354,7 +11010,28 @@ int ngtcp2_conn_set_remote_transport_params_versioned(
     return NGTCP2_ERR_TRANSPORT_PARAM;
   }
 
-  if (!conn->server) {
+  if (conn->server) {
+    if (params->version_info_present) {
+      if (params->version_info.chosen_version != conn->client_chosen_version) {
+        return NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE;
+      }
+
+      conn->negotiated_version =
+          ngtcp2_conn_server_negotiate_version(conn, &params->version_info);
+      if (conn->negotiated_version != conn->client_chosen_version) {
+        rv = conn_call_version_negotiation(conn, conn->negotiated_version,
+                                           &conn->rcid);
+        if (rv != 0) {
+          return rv;
+        }
+      }
+    } else {
+      conn->negotiated_version = conn->client_chosen_version;
+    }
+
+    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+                    "the negotiated version is %08x", conn->negotiated_version);
+  } else {
     rv = conn_client_validate_transport_params(conn, params);
     if (rv != 0) {
       return rv;
@@ -10414,9 +11091,16 @@ void ngtcp2_conn_set_early_remote_transport_params_versioned(
       params->initial_max_stream_data_bidi_remote;
   p->initial_max_stream_data_uni = params->initial_max_stream_data_uni;
   p->initial_max_data = params->initial_max_data;
-  p->active_connection_id_limit = params->active_connection_id_limit;
+  p->active_connection_id_limit =
+      ngtcp2_max(NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT,
+                 params->active_connection_id_limit);
   p->max_idle_timeout = params->max_idle_timeout;
-  p->max_udp_payload_size = params->max_udp_payload_size;
+  if (!params->max_udp_payload_size) {
+    p->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
+  } else {
+    p->max_udp_payload_size =
+        ngtcp2_max(NGTCP2_MAX_UDP_PAYLOAD_SIZE, params->max_udp_payload_size);
+  }
   p->disable_active_migration = params->disable_active_migration;
   p->max_datagram_frame_size = params->max_datagram_frame_size;
 
@@ -10521,6 +11205,16 @@ void ngtcp2_conn_get_local_transport_params_versioned(
   (void)transport_params_version;
 
   *params = conn->local.transport_params;
+
+  if (conn->server) {
+    params->version_info.chosen_version = conn->negotiated_version;
+  } else {
+    params->version_info.chosen_version = conn->client_chosen_version;
+  }
+
+  params->version_info.other_versions = conn->vneg.other_versions;
+  params->version_info.other_versionslen = conn->vneg.other_versionslen;
+  params->version_info_present = 1;
 }
 
 int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id,
@@ -10532,7 +11226,7 @@ int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id,
     return NGTCP2_ERR_STREAM_ID_BLOCKED;
   }
 
-  strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+  strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
   if (strm == NULL) {
     return NGTCP2_ERR_NOMEM;
   }
@@ -10540,7 +11234,7 @@ int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id,
   rv = ngtcp2_conn_init_stream(conn, strm, conn->local.bidi.next_stream_id,
                                stream_user_data);
   if (rv != 0) {
-    ngtcp2_mem_free(conn->mem, strm);
+    ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
     return rv;
   }
 
@@ -10559,7 +11253,7 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id,
     return NGTCP2_ERR_STREAM_ID_BLOCKED;
   }
 
-  strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm));
+  strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc);
   if (strm == NULL) {
     return NGTCP2_ERR_NOMEM;
   }
@@ -10567,7 +11261,7 @@ int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id,
   rv = ngtcp2_conn_init_stream(conn, strm, conn->local.uni.next_stream_id,
                                stream_user_data);
   if (rv != 0) {
-    ngtcp2_mem_free(conn->mem, strm);
+    ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
     return rv;
   }
   ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD);
@@ -10603,14 +11297,9 @@ static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn,
                                             ngtcp2_pkt_info *pi, uint8_t *dest,
                                             size_t destlen, ngtcp2_vmsg *vmsg,
                                             ngtcp2_tstamp ts) {
-  ngtcp2_pktns *in_pktns = conn->in_pktns;
-  ngtcp2_pktns *hs_pktns = conn->hs_pktns;
-  ngtcp2_pktns *pktns = &conn->pktns;
-  int no_retrans = (!in_pktns || in_pktns->rtb.num_retransmittable == 0) &&
-                   (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) &&
-                   pktns->rtb.num_retransmittable == 0;
   ngtcp2_conn_stat *cstat = &conn->cstat;
   ngtcp2_ssize nwrite;
+  int undersized;
 
   nwrite = ngtcp2_conn_write_vmsg(conn, path, pkt_info_version, pi, dest,
                                   destlen, vmsg, ts);
@@ -10622,11 +11311,20 @@ static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn,
     conn->rst.is_cwnd_limited = 1;
   }
 
-  if (nwrite == 0 && cstat->bytes_in_flight < cstat->cwnd && no_retrans) {
-    conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight;
+  if (vmsg == NULL && cstat->bytes_in_flight < cstat->cwnd &&
+      conn->tx.strmq_nretrans == 0) {
+    if (conn->local.settings.no_udp_payload_size_shaping) {
+      undersized = (size_t)nwrite < conn->local.settings.max_udp_payload_size;
+    } else {
+      undersized = (size_t)nwrite < conn->dcid.current.max_udp_payload_size;
+    }
+
+    if (undersized) {
+      conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight;
 
-    if (conn->rst.app_limited == 0) {
-      conn->rst.app_limited = cstat->max_udp_payload_size;
+      if (conn->rst.app_limited == 0) {
+        conn->rst.app_limited = cstat->max_udp_payload_size;
+      }
     }
   }
 
@@ -10724,7 +11422,6 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
                                     uint8_t *dest, size_t destlen,
                                     ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) {
   ngtcp2_ssize nwrite;
-  ngtcp2_pktns *pktns = &conn->pktns;
   size_t origlen;
   size_t origdestlen = destlen;
   int rv;
@@ -10739,7 +11436,6 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
   ngtcp2_ksl_it it;
   ngtcp2_rtb_entry *rtbent;
   (void)pkt_info_version;
-  (void)pktns;
 
   conn->log.last_ts = ts;
   conn->qlog.last_ts = ts;
@@ -10764,7 +11460,10 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
   case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
   case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED:
     nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts);
-    if (nwrite < 0) {
+    /* We might be unable to write a packet because of depletion of
+       congestion window budget, perhaps due to packet loss that
+       shrinks the window drastically. */
+    if (nwrite <= 0) {
       return nwrite;
     }
     if (conn->state != NGTCP2_CS_POST_HANDSHAKE) {
@@ -10773,17 +11472,19 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
 
     assert(nwrite);
     assert(dest[0] & NGTCP2_HEADER_FORM_BIT);
+    assert(conn->negotiated_version);
 
-    if (ngtcp2_pkt_get_type_long(dest[0]) == NGTCP2_PKT_INITIAL) {
+    if (ngtcp2_pkt_get_type_long(conn->negotiated_version, dest[0]) ==
+        NGTCP2_PKT_INITIAL) {
       /* We have added padding already, but in that case, there is no
-         space left to write Short packet. */
+         space left to write 1RTT packet. */
       wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
     }
 
     res = nwrite;
     dest += nwrite;
     destlen -= (size_t)nwrite;
-    /* Break here so that we can coalesces Short packets. */
+    /* Break here so that we can coalesces 1RTT packet. */
     break;
   case NGTCP2_CS_SERVER_INITIAL:
   case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
@@ -10854,7 +11555,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
           if (rtbent->hd.pkt_num != prev_in_pkt_num &&
               (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
             /* We have added padding already, but in that case, there
-               is no space left to write Short packet. */
+               is no space left to write 1RTT packet. */
             wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
           }
         }
@@ -10875,7 +11576,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
     return 0;
   }
 
-  assert(pktns->crypto.tx.ckm);
+  assert(conn->pktns.crypto.tx.ckm);
 
   if (conn_check_pkt_num_exhausted(conn)) {
     return NGTCP2_ERR_PKT_NUM_EXHAUSTED;
@@ -10906,7 +11607,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
     }
     /* dest and destlen have already been adjusted in ppe in the first
        run.  They are adjusted for probe packet later. */
-    nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT,
+    nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT,
                             wflags, ts);
     goto fin;
   } else {
@@ -10937,6 +11638,15 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
             goto fin;
           }
         }
+
+        if (conn->pmtud &&
+            (!conn->hs_pktns ||
+             ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) == 0)) {
+          nwrite = conn_write_pmtud_probe(conn, pi, dest, origdestlen, ts);
+          if (nwrite) {
+            goto fin;
+          }
+        }
       }
 
       if (conn->server &&
@@ -10979,13 +11689,13 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
                     "transmit probe pkt left=%zu",
                     conn->pktns.rtb.probe_pkt_left);
 
-    nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT,
+    nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT,
                             wflags, ts);
 
     goto fin;
   }
 
-  nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT,
+  nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_1RTT,
                           wflags, ts);
   if (nwrite) {
     assert(nwrite != NGTCP2_ERR_NOBUF);
@@ -10993,7 +11703,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
   }
 
   if (res == 0) {
-    nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_SHORT, ts);
+    nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_1RTT, ts);
   }
 
 fin:
@@ -11021,6 +11731,7 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
   ngtcp2_pktns *hs_pktns = conn->hs_pktns;
   ngtcp2_ssize res = 0, nwrite;
   ngtcp2_frame fr;
+  uint8_t flags = NGTCP2_WRITE_PKT_FLAG_NONE;
 
   fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
   fr.connection_close.error_code = error_code;
@@ -11032,8 +11743,9 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
       pkt_type != NGTCP2_PKT_INITIAL) {
     if (in_pktns && conn->server) {
       nwrite = ngtcp2_conn_write_single_frame_pkt(
-          conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, &conn->dcid.current.cid,
-          &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+          conn, pi, dest, destlen, NGTCP2_PKT_INITIAL,
+          NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr,
+          NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
       if (nwrite < 0) {
         return nwrite;
       }
@@ -11047,7 +11759,8 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
         hs_pktns->crypto.tx.ckm) {
       nwrite = ngtcp2_conn_write_single_frame_pkt(
           conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE,
-          &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+          NGTCP2_WRITE_PKT_FLAG_NONE, &conn->dcid.current.cid, &fr,
+          NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
       if (nwrite < 0) {
         return nwrite;
       }
@@ -11058,8 +11771,12 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
     }
   }
 
+  if (!conn->server && pkt_type == NGTCP2_PKT_INITIAL) {
+    flags = NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING;
+  }
+
   nwrite = ngtcp2_conn_write_single_frame_pkt(
-      conn, pi, dest, destlen, pkt_type, &conn->dcid.current.cid, &fr,
+      conn, pi, dest, destlen, pkt_type, flags, &conn->dcid.current.cid, &fr,
       NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
 
   if (nwrite < 0) {
@@ -11075,15 +11792,15 @@ conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
   return res;
 }
 
-ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
-    ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
-    ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint64_t error_code,
-    const uint8_t *reason, size_t reasonlen, ngtcp2_tstamp ts) {
+ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt(
+    ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+    size_t destlen, uint64_t error_code, const uint8_t *reason,
+    size_t reasonlen, ngtcp2_tstamp ts) {
   ngtcp2_pktns *in_pktns = conn->in_pktns;
   ngtcp2_pktns *hs_pktns = conn->hs_pktns;
   uint8_t pkt_type;
   ngtcp2_ssize nwrite;
-  (void)pkt_info_version;
+  uint64_t server_tx_left;
 
   conn->log.last_ts = ts;
   conn->qlog.last_ts = ts;
@@ -11094,9 +11811,10 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
 
   switch (conn->state) {
   case NGTCP2_CS_CLIENT_INITIAL:
+    return NGTCP2_ERR_INVALID_STATE;
   case NGTCP2_CS_CLOSING:
   case NGTCP2_CS_DRAINING:
-    return NGTCP2_ERR_INVALID_STATE;
+    return 0;
   default:
     break;
   }
@@ -11105,13 +11823,20 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
     ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
   }
 
+  destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen);
+
   if (pi) {
     pi->ecn = NGTCP2_ECN_NOT_ECT;
   }
 
+  if (conn->server) {
+    server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
+    destlen = ngtcp2_min(destlen, server_tx_left);
+  }
+
   if (conn->state == NGTCP2_CS_POST_HANDSHAKE ||
       (conn->server && conn->pktns.crypto.tx.ckm)) {
-    pkt_type = NGTCP2_PKT_SHORT;
+    pkt_type = NGTCP2_PKT_1RTT;
   } else if (hs_pktns && hs_pktns->crypto.tx.ckm) {
     pkt_type = NGTCP2_PKT_HANDSHAKE;
   } else if (in_pktns && in_pktns->crypto.tx.ckm) {
@@ -11133,14 +11858,14 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
   return nwrite;
 }
 
-ngtcp2_ssize ngtcp2_conn_write_application_close_versioned(
-    ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
-    ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint64_t app_error_code,
-    const uint8_t *reason, size_t reasonlen, ngtcp2_tstamp ts) {
+ngtcp2_ssize ngtcp2_conn_write_application_close_pkt(
+    ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+    size_t destlen, uint64_t app_error_code, const uint8_t *reason,
+    size_t reasonlen, ngtcp2_tstamp ts) {
   ngtcp2_ssize nwrite;
   ngtcp2_ssize res = 0;
   ngtcp2_frame fr;
-  (void)pkt_info_version;
+  uint64_t server_tx_left;
 
   conn->log.last_ts = ts;
   conn->qlog.last_ts = ts;
@@ -11151,9 +11876,10 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_versioned(
 
   switch (conn->state) {
   case NGTCP2_CS_CLIENT_INITIAL:
+    return NGTCP2_ERR_INVALID_STATE;
   case NGTCP2_CS_CLOSING:
   case NGTCP2_CS_DRAINING:
-    return NGTCP2_ERR_INVALID_STATE;
+    return 0;
   default:
     break;
   }
@@ -11162,10 +11888,17 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_versioned(
     ngtcp2_path_copy(path, &conn->dcid.current.ps.path);
   }
 
+  destlen = conn_shape_udp_payload(conn, &conn->dcid.current, destlen);
+
   if (pi) {
     pi->ecn = NGTCP2_ECN_NOT_ECT;
   }
 
+  if (conn->server) {
+    server_tx_left = conn_server_tx_left(conn, &conn->dcid.current);
+    destlen = ngtcp2_min(destlen, server_tx_left);
+  }
+
   if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) {
     nwrite = conn_write_connection_close(conn, pi, dest, destlen,
                                          conn->hs_pktns->crypto.tx.ckm
@@ -11197,8 +11930,8 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_versioned(
   fr.connection_close.reason = (uint8_t *)reason;
 
   nwrite = ngtcp2_conn_write_single_frame_pkt(
-      conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &conn->dcid.current.cid, &fr,
-      NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
+      conn, pi, dest, destlen, NGTCP2_PKT_1RTT, NGTCP2_WRITE_PKT_FLAG_NONE,
+      &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts);
 
   if (nwrite < 0) {
     return nwrite;
@@ -11215,6 +11948,92 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_versioned(
   return res;
 }
 
+static void
+connection_close_error_init(ngtcp2_connection_close_error *ccerr,
+                            ngtcp2_connection_close_error_code_type type,
+                            uint64_t error_code, const uint8_t *reason,
+                            size_t reasonlen) {
+  ccerr->type = type;
+  ccerr->error_code = error_code;
+  ccerr->frame_type = 0;
+  ccerr->reason = (uint8_t *)reason;
+  ccerr->reasonlen = reasonlen;
+}
+
+void ngtcp2_connection_close_error_default(
+    ngtcp2_connection_close_error *ccerr) {
+  connection_close_error_init(ccerr,
+                              NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT,
+                              NGTCP2_NO_ERROR, NULL, 0);
+}
+
+void ngtcp2_connection_close_error_set_transport_error(
+    ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+    const uint8_t *reason, size_t reasonlen) {
+  connection_close_error_init(ccerr,
+                              NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT,
+                              error_code, reason, reasonlen);
+}
+
+void ngtcp2_connection_close_error_set_transport_error_liberr(
+    ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason,
+    size_t reasonlen) {
+  switch (liberr) {
+  case NGTCP2_ERR_RECV_VERSION_NEGOTIATION:
+    connection_close_error_init(
+        ccerr,
+        NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION,
+        NGTCP2_NO_ERROR, reason, reasonlen);
+
+    return;
+  case NGTCP2_ERR_IDLE_CLOSE:
+    connection_close_error_init(
+        ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE,
+        NGTCP2_NO_ERROR, reason, reasonlen);
+
+    return;
+  };
+
+  ngtcp2_connection_close_error_set_transport_error(
+      ccerr, ngtcp2_err_infer_quic_transport_error_code(liberr), reason,
+      reasonlen);
+}
+
+void ngtcp2_connection_close_error_set_transport_error_tls_alert(
+    ngtcp2_connection_close_error *ccerr, uint8_t tls_alert,
+    const uint8_t *reason, size_t reasonlen) {
+  ngtcp2_connection_close_error_set_transport_error(
+      ccerr, NGTCP2_CRYPTO_ERROR | tls_alert, reason, reasonlen);
+}
+
+void ngtcp2_connection_close_error_set_application_error(
+    ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+    const uint8_t *reason, size_t reasonlen) {
+  connection_close_error_init(
+      ccerr, NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, error_code,
+      reason, reasonlen);
+}
+
+ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
+    ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+    ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
+    const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts) {
+  (void)pkt_info_version;
+
+  switch (ccerr->type) {
+  case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT:
+    return ngtcp2_conn_write_connection_close_pkt(
+        conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason,
+        ccerr->reasonlen, ts);
+  case NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION:
+    return ngtcp2_conn_write_application_close_pkt(
+        conn, path, pi, dest, destlen, ccerr->error_code, ccerr->reason,
+        ccerr->reasonlen, ts);
+  default:
+    return 0;
+  }
+}
+
 int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) {
   return conn->state == NGTCP2_CS_CLOSING;
 }
@@ -11239,11 +12058,15 @@ int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm) {
 
   if (ngtcp2_strm_is_tx_queued(strm)) {
     ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe);
+    if (!ngtcp2_strm_streamfrq_empty(strm)) {
+      assert(conn->tx.strmq_nretrans);
+      --conn->tx.strmq_nretrans;
+    }
   }
 
 fin:
   ngtcp2_strm_free(strm);
-  ngtcp2_mem_free(conn->mem, strm);
+  ngtcp2_objalloc_strm_release(&conn->strm_objalloc, strm);
 
   return rv;
 }
@@ -11444,8 +12267,12 @@ const ngtcp2_cid *ngtcp2_conn_get_client_initial_dcid(ngtcp2_conn *conn) {
   return &conn->rcid;
 }
 
+uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn) {
+  return conn->client_chosen_version;
+}
+
 uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) {
-  return conn->version;
+  return conn->negotiated_version;
 }
 
 static int delete_strms_pq_each(void *data, void *ptr) {
@@ -11454,10 +12281,14 @@ static int delete_strms_pq_each(void *data, void *ptr) {
 
   if (ngtcp2_strm_is_tx_queued(s)) {
     ngtcp2_pq_remove(&conn->tx.strmq, &s->pe);
+    if (!ngtcp2_strm_streamfrq_empty(s)) {
+      assert(conn->tx.strmq_nretrans);
+      --conn->tx.strmq_nretrans;
+    }
   }
 
   ngtcp2_strm_free(s);
-  ngtcp2_mem_free(conn->mem, s);
+  ngtcp2_objalloc_strm_release(&conn->strm_objalloc, s);
 
   return 0;
 }
@@ -11496,11 +12327,15 @@ static void conn_discard_early_data_state(ngtcp2_conn *conn) {
   for (pfrc = &conn->pktns.tx.frq; *pfrc;) {
     frc = *pfrc;
     *pfrc = (*pfrc)->next;
-    ngtcp2_frame_chain_del(frc, conn->mem);
+    ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
   }
 }
 
 int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) {
+  if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) {
+    return 0;
+  }
+
   conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED;
 
   conn_discard_early_data_state(conn);
@@ -11508,24 +12343,22 @@ int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) {
   return 0;
 }
 
-void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
-                            ngtcp2_duration ack_delay, ngtcp2_tstamp ts) {
+int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
+                           ngtcp2_duration ack_delay, ngtcp2_tstamp ts) {
   ngtcp2_conn_stat *cstat = &conn->cstat;
-  ngtcp2_duration min_rtt;
-
-  cstat->latest_rtt = rtt;
 
   if (cstat->min_rtt == UINT64_MAX) {
+    cstat->latest_rtt = rtt;
     cstat->min_rtt = rtt;
     cstat->smoothed_rtt = rtt;
     cstat->rttvar = rtt / 2;
     cstat->first_rtt_sample_ts = ts;
   } else {
-    min_rtt = ngtcp2_min(cstat->min_rtt, rtt);
     if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) {
       ack_delay =
           ngtcp2_min(ack_delay, conn->remote.transport_params.max_ack_delay);
-    } else if (ack_delay > 0 && rtt < cstat->min_rtt + ack_delay) {
+    } else if (ack_delay > 0 && rtt >= cstat->min_rtt &&
+               rtt < cstat->min_rtt + ack_delay) {
       /* Ignore RTT sample if adjusting ack_delay causes the sample
          less than min_rtt before handshake confirmation. */
       ngtcp2_log_info(
@@ -11535,14 +12368,16 @@ void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
           (uint64_t)(rtt / NGTCP2_MILLISECONDS),
           (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS),
           (uint64_t)(ack_delay / NGTCP2_MILLISECONDS));
-      return;
+      return NGTCP2_ERR_INVALID_ARGUMENT;
     }
 
-    if (rtt > min_rtt + ack_delay) {
+    cstat->latest_rtt = rtt;
+    cstat->min_rtt = ngtcp2_min(cstat->min_rtt, rtt);
+
+    if (rtt >= cstat->min_rtt + ack_delay) {
       rtt -= ack_delay;
     }
 
-    cstat->min_rtt = min_rtt;
     cstat->rttvar = (cstat->rttvar * 3 + (cstat->smoothed_rtt < rtt
                                               ? rtt - cstat->smoothed_rtt
                                               : cstat->smoothed_rtt - rtt)) /
@@ -11559,6 +12394,8 @@ void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
                   cstat->smoothed_rtt / NGTCP2_MILLISECONDS,
                   cstat->rttvar / NGTCP2_MILLISECONDS,
                   (uint64_t)(ack_delay / NGTCP2_MILLISECONDS));
+
+  return 0;
 }
 
 void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn,
@@ -11578,7 +12415,7 @@ static ngtcp2_pktns *conn_get_earliest_pktns(ngtcp2_conn *conn,
   ngtcp2_tstamp earliest_ts = UINT64_MAX;
 
   for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) {
-    if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 0 ||
+    if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 ||
         (times[i] == UINT64_MAX ||
          (earliest_ts != UINT64_MAX && times[i] >= earliest_ts) ||
          (i == NGTCP2_PKTNS_ID_APPLICATION &&
@@ -11617,7 +12454,7 @@ static ngtcp2_tstamp conn_get_earliest_pto_expiry(ngtcp2_conn *conn,
       (1ULL << pto_count);
 
   for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) {
-    if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 0 ||
+    if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 ||
         (times[i] == UINT64_MAX ||
          (i == NGTCP2_PKTNS_ID_APPLICATION &&
           !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
@@ -11661,9 +12498,9 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
     return;
   }
 
-  if ((!in_pktns || in_pktns->rtb.num_retransmittable == 0) &&
-      (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) &&
-      (pktns->rtb.num_retransmittable == 0 ||
+  if ((!in_pktns || in_pktns->rtb.num_pto_eliciting == 0) &&
+      (!hs_pktns || hs_pktns->rtb.num_pto_eliciting == 0) &&
+      (pktns->rtb.num_pto_eliciting == 0 ||
        !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) &&
       (conn->server ||
        (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED |
@@ -11727,23 +12564,23 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
     return 0;
   }
 
-  if (!conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) {
+  if (!conn->server && !conn_is_handshake_completed(conn)) {
     if (hs_pktns->crypto.tx.ckm) {
       hs_pktns->rtb.probe_pkt_left = 1;
     } else {
       in_pktns->rtb.probe_pkt_left = 1;
     }
   } else {
-    if (in_pktns && in_pktns->rtb.num_retransmittable) {
+    if (in_pktns && in_pktns->rtb.num_pto_eliciting) {
       in_pktns->rtb.probe_pkt_left = 1;
 
       assert(hs_pktns);
 
-      if (conn->server && hs_pktns->rtb.num_retransmittable) {
+      if (conn->server && hs_pktns->rtb.num_pto_eliciting) {
         /* let server coalesce packets */
         hs_pktns->rtb.probe_pkt_left = 1;
       }
-    } else if (hs_pktns && hs_pktns->rtb.num_retransmittable) {
+    } else if (hs_pktns && hs_pktns->rtb.num_pto_eliciting) {
       hs_pktns->rtb.probe_pkt_left = 1;
     } else {
       conn->pktns.rtb.probe_pkt_left = 2;
@@ -11821,7 +12658,7 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
     return rv;
   }
 
-  rv = ngtcp2_frame_chain_new(&frc, conn->mem);
+  rv = ngtcp2_frame_chain_objalloc_new(&frc, &conn->frc_objalloc);
   if (rv != 0) {
     return rv;
   }
@@ -11836,7 +12673,7 @@ int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn,
 
   rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &fr->offset, frc);
   if (rv != 0) {
-    ngtcp2_frame_chain_del(frc, conn->mem);
+    ngtcp2_frame_chain_objalloc_del(frc, &conn->frc_objalloc, conn->mem);
     return rv;
   }
 
@@ -11856,7 +12693,8 @@ int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, const uint8_t *token,
   assert(token);
   assert(tokenlen);
 
-  rv = ngtcp2_frame_chain_new_token_new(&nfrc, &tokenv, conn->mem);
+  rv = ngtcp2_frame_chain_new_token_objalloc_new(
+      &nfrc, &tokenv, &conn->frc_objalloc, conn->mem);
   if (rv != 0) {
     return rv;
   }
@@ -11932,7 +12770,7 @@ size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) {
     }
   }
 
-  n += ngtcp2_ringbuf_len(&conn->dcid.retired);
+  n += ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
 
   return n;
 }
@@ -11974,9 +12812,9 @@ size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) {
     }
   }
 
-  len = ngtcp2_ringbuf_len(&conn->dcid.retired);
+  len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb);
   for (i = 0; i < len; ++i) {
-    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i);
+    dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i);
     copy_dcid_to_cid_token(dest, dcid);
     ++dest;
   }
@@ -11995,17 +12833,20 @@ void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn, void *path_user_data) {
   conn->dcid.current.ps.path.user_data = path_user_data;
 }
 
+const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn) {
+  return &conn->dcid.current.ps.path;
+}
+
+size_t ngtcp2_conn_get_max_udp_payload_size(ngtcp2_conn *conn) {
+  return conn->local.settings.max_udp_payload_size;
+}
+
 size_t ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn) {
   if (conn->local.settings.no_udp_payload_size_shaping) {
-    return conn->local.settings.max_udp_payload_size;
+    return ngtcp2_conn_get_max_udp_payload_size(conn);
   }
 
-  return ngtcp2_min(conn->local.settings.max_udp_payload_size,
-                    conn->dcid.current.max_udp_payload_size);
-}
-
-const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn) {
-  return &conn->dcid.current.ps.path;
+  return conn->dcid.current.max_udp_payload_size;
 }
 
 static int conn_initiate_migration_precheck(ngtcp2_conn *conn,
@@ -12017,7 +12858,7 @@ static int conn_initiate_migration_precheck(ngtcp2_conn *conn,
     return NGTCP2_ERR_INVALID_STATE;
   }
 
-  if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) {
+  if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) {
     return NGTCP2_ERR_CONN_ID_BLOCKED;
   }
 
@@ -12044,6 +12885,8 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn,
     return rv;
   }
 
+  ngtcp2_conn_stop_pmtud(conn);
+
   if (conn->pv) {
     rv = conn_abort_pv(conn, ts);
     if (rv != 0) {
@@ -12056,11 +12899,11 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn,
     return rv;
   }
 
-  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
   ngtcp2_dcid_set_path(dcid, path);
 
   ngtcp2_dcid_copy(&conn->dcid.current, dcid);
-  ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+  ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
 
   rv = conn_call_activate_dcid(conn, &conn->dcid.current);
   if (rv != 0) {
@@ -12070,6 +12913,13 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn,
   conn_reset_congestion_state(conn, ts);
   conn_reset_ecn_validation_state(conn);
 
+  if (!conn->local.settings.no_pmtud) {
+    rv = conn_start_pmtud(conn);
+    if (rv != 0) {
+      return rv;
+    }
+  }
+
   return 0;
 }
 
@@ -12097,7 +12947,7 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
     }
   }
 
-  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0);
+  dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0);
   ngtcp2_dcid_set_path(dcid, path);
 
   pto = conn_compute_pto(conn, &conn->pktns);
@@ -12110,7 +12960,7 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
     return rv;
   }
 
-  ngtcp2_ringbuf_pop_front(&conn->dcid.unused);
+  ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb);
   conn->pv = pv;
 
   return conn_call_activate_dcid(conn, &pv->dcid);
@@ -12139,6 +12989,17 @@ uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) {
                                          : conn->local.uni.max_streams - n + 1;
 }
 
+uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn) {
+  uint64_t bytes_in_flight = conn->cstat.bytes_in_flight;
+  uint64_t cwnd = conn_get_cwnd(conn);
+
+  if (cwnd > bytes_in_flight) {
+    return cwnd - bytes_in_flight;
+  }
+
+  return 0;
+}
+
 ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) {
   ngtcp2_duration trpto;
   ngtcp2_duration idle_timeout;
@@ -12146,7 +13007,7 @@ ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) {
   /* TODO Remote max_idle_timeout becomes effective after handshake
      completion. */
 
-  if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) ||
+  if (!conn_is_handshake_completed(conn) ||
       conn->remote.transport_params.max_idle_timeout == 0 ||
       (conn->local.transport_params.max_idle_timeout &&
        conn->local.transport_params.max_idle_timeout <
@@ -12160,19 +13021,16 @@ ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) {
     return UINT64_MAX;
   }
 
-  trpto = 3 * conn_compute_pto(
-                  conn, (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)
-                            ? &conn->pktns
-                            : conn->hs_pktns);
+  trpto = 3 * conn_compute_pto(conn, conn_is_handshake_completed(conn)
+                                         ? &conn->pktns
+                                         : conn->hs_pktns);
 
   return conn->idle_ts + ngtcp2_max(idle_timeout, trpto);
 }
 
 ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) {
-  return conn_compute_pto(conn,
-                          (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)
-                              ? &conn->pktns
-                              : conn->hs_pktns);
+  return conn_compute_pto(
+      conn, conn_is_handshake_completed(conn) ? &conn->pktns : conn->hs_pktns);
 }
 
 void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn,
@@ -12275,6 +13133,46 @@ size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn) {
   return conn->cstat.send_quantum;
 }
 
+int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
+  size_t i;
+
+  if (conn->dcid.retire_unacked.len >=
+      sizeof(conn->dcid.retire_unacked.seqs) /
+          sizeof(conn->dcid.retire_unacked.seqs[0])) {
+    return NGTCP2_ERR_CONNECTION_ID_LIMIT;
+  }
+
+  /* Make sure that we do not have a duplicate */
+  for (i = 0; i < conn->dcid.retire_unacked.len; ++i) {
+    if (conn->dcid.retire_unacked.seqs[i] == seq) {
+      assert(0);
+    }
+  }
+
+  conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len++] = seq;
+
+  return 0;
+}
+
+void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
+  size_t i;
+
+  for (i = 0; i < conn->dcid.retire_unacked.len; ++i) {
+    if (conn->dcid.retire_unacked.seqs[i] != seq) {
+      continue;
+    }
+
+    if (i != conn->dcid.retire_unacked.len - 1) {
+      conn->dcid.retire_unacked.seqs[i] =
+          conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len - 1];
+    }
+
+    --conn->dcid.retire_unacked.len;
+
+    return;
+  }
+}
+
 void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
                                       const ngtcp2_path *path,
                                       const uint8_t *data) {
@@ -12290,7 +13188,7 @@ void ngtcp2_settings_default_versioned(int settings_version,
   settings->cc_algo = NGTCP2_CC_ALGO_CUBIC;
   settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT;
   settings->ack_thresh = 2;
-  settings->max_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE;
+  settings->max_udp_payload_size = 1500 - 48;
   settings->handshake_timeout = NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT;
 }
 
@@ -12368,3 +13266,26 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close(
 }
 
 int ngtcp2_is_bidi_stream(int64_t stream_id) { return bidi_stream(stream_id); }
+
+uint32_t ngtcp2_select_version(const uint32_t *preferred_versions,
+                               size_t preferred_versionslen,
+                               const uint32_t *offered_versions,
+                               size_t offered_versionslen) {
+  size_t i, j;
+
+  if (!preferred_versionslen || !offered_versionslen) {
+    return 0;
+  }
+
+  for (i = 0; i < preferred_versionslen; ++i) {
+    assert(ngtcp2_is_supported_version(preferred_versions[i]));
+
+    for (j = 0; j < offered_versionslen; ++j) {
+      if (preferred_versions[i] == offered_versions[j]) {
+        return preferred_versions[i];
+      }
+    }
+  }
+
+  return 0;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h
index 6944876130..bd7e641463 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h
@@ -45,6 +45,7 @@
 #include "ngtcp2_bbr.h"
 #include "ngtcp2_bbr2.h"
 #include "ngtcp2_pv.h"
+#include "ngtcp2_pmtud.h"
 #include "ngtcp2_cid.h"
 #include "ngtcp2_buf.h"
 #include "ngtcp2_ppe.h"
@@ -123,6 +124,16 @@ typedef enum {
    longer than this value, it is truncated. */
 #define NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN 1024
 
+/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00
+/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet other
+   than Initial packet should be padded.  Initial packet might be
+   padded based on QUIC requirement regardless of this flag. */
+#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01
+/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come
+   and it should be encoded into the current packet. */
+#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02
+
 /*
  * ngtcp2_max_frame is defined so that it covers the largest ACK
  * frame.
@@ -147,8 +158,13 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
 
 /* NGTCP2_CONN_FLAG_NONE indicates that no flag is set. */
 #define NGTCP2_CONN_FLAG_NONE 0x00
-/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set if handshake
-   completed. */
+/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set when TLS stack declares
+   that TLS handshake has completed.  The condition of this
+   declaration varies between TLS implementations and this flag does
+   not indicate the completion of QUIC handshake.  Some
+   implementations declare TLS handshake completion as server when
+   they write off Server Finished and before deriving application rx
+   secret. */
 #define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01
 /* NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED is set if connection ID is
    negotiated.  This is only used for client. */
@@ -201,6 +217,9 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
    installed.  conn->early.ckm cannot be used for this purpose because
    it might be discarded when a certain condition is met. */
 #define NGTCP2_CONN_FLAG_EARLY_KEY_INSTALLED 0x8000
+/* NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR is set when the local
+   endpoint has initiated key update. */
+#define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000
 
 typedef struct ngtcp2_crypto_data {
   ngtcp2_buf buf;
@@ -325,7 +344,21 @@ typedef enum ngtcp2_ecn_state {
   NGTCP2_ECN_STATE_CAPABLE,
 } ngtcp2_ecn_state;
 
+ngtcp2_static_ringbuf_def(dcid_bound, NGTCP2_MAX_BOUND_DCID_POOL_SIZE,
+                          sizeof(ngtcp2_dcid));
+ngtcp2_static_ringbuf_def(dcid_unused, NGTCP2_MAX_DCID_POOL_SIZE,
+                          sizeof(ngtcp2_dcid));
+ngtcp2_static_ringbuf_def(dcid_retired, NGTCP2_MAX_DCID_RETIRED_SIZE,
+                          sizeof(ngtcp2_dcid));
+ngtcp2_static_ringbuf_def(path_challenge, 4,
+                          sizeof(ngtcp2_path_challenge_entry));
+
+ngtcp2_objalloc_def(strm, ngtcp2_strm, oplent);
+
 struct ngtcp2_conn {
+  ngtcp2_objalloc frc_objalloc;
+  ngtcp2_objalloc rtb_entry_objalloc;
+  ngtcp2_objalloc strm_objalloc;
   ngtcp2_conn_state state;
   ngtcp2_callbacks callbacks;
   /* rcid is a connection ID present in Initial or 0-RTT packet from
@@ -350,21 +383,25 @@ struct ngtcp2_conn {
     ngtcp2_dcid current;
     /* bound is a set of destination connection IDs which are bound to
        particular paths.  These paths are not validated yet. */
-    ngtcp2_ringbuf bound;
+    ngtcp2_static_ringbuf_dcid_bound bound;
     /* unused is a set of unused CID received from peer. */
-    ngtcp2_ringbuf unused;
+    ngtcp2_static_ringbuf_dcid_unused unused;
     /* retired is a set of CID retired by local endpoint.  Keep them
        in 3*PTO to catch packets in flight along the old path. */
-    ngtcp2_ringbuf retired;
+    ngtcp2_static_ringbuf_dcid_retired retired;
     /* seqgap tracks received sequence numbers in order to ignore
        retransmitted duplicated NEW_CONNECTION_ID frame. */
     ngtcp2_gaptr seqgap;
     /* retire_prior_to is the largest retire_prior_to received so
        far. */
     uint64_t retire_prior_to;
-    /* num_retire_queued is the number of RETIRE_CONNECTION_ID frames
-       queued for transmission. */
-    size_t num_retire_queued;
+    struct {
+      /* seqs contains sequence number of Connection ID whose
+         retirement is not acknowledged by the remote endpoint yet. */
+      uint64_t seqs[NGTCP2_MAX_DCID_POOL_SIZE * 2];
+      /* len is the number of sequence numbers that seq contains. */
+      size_t len;
+    } retire_unacked;
     /* zerolen_seq is a pseudo sequence number of zero-length
        Destination Connection ID in order to distinguish between
        them. */
@@ -389,6 +426,9 @@ struct ngtcp2_conn {
   struct {
     /* strmq contains ngtcp2_strm which has frames to send. */
     ngtcp2_pq strmq;
+    /* strmq_nretrans is the number of entries in strmq which has
+       stream data to resent. */
+    size_t strmq_nretrans;
     /* ack is ACK frame.  The underlying buffer is reused. */
     ngtcp2_frame *ack;
     /* max_ack_blks is the number of additional ngtcp2_ack_blk which
@@ -440,7 +480,7 @@ struct ngtcp2_conn {
     /* window is the connection-level flow control window size. */
     uint64_t window;
     /* path_challenge stores received PATH_CHALLENGE data. */
-    ngtcp2_ringbuf path_challenge;
+    ngtcp2_static_ringbuf_path_challenge path_challenge;
     /* ccerr is the received connection close error. */
     ngtcp2_connection_close_error ccerr;
   } rx;
@@ -573,7 +613,9 @@ struct ngtcp2_conn {
     ngtcp2_frame_chain **pfrc;
     int pkt_empty;
     int hd_logged;
-    uint8_t rtb_entry_flags;
+    /* flags is bitwise OR of zero or more of
+       NGTCP2_RTB_ENTRY_FLAG_*. */
+    uint16_t rtb_entry_flags;
     ngtcp2_ssize hs_spktlen;
     int require_padding;
   } pkt;
@@ -589,9 +631,45 @@ struct ngtcp2_conn {
     ngtcp2_duration timeout;
   } keep_alive;
 
+  struct {
+    /* Initial keys for negotiated version.  If original version ==
+       negotiated version, these fields are not used. */
+    struct {
+      ngtcp2_crypto_km *ckm;
+      ngtcp2_crypto_cipher_ctx hp_ctx;
+    } rx;
+    struct {
+      ngtcp2_crypto_km *ckm;
+      ngtcp2_crypto_cipher_ctx hp_ctx;
+    } tx;
+    /* version is QUIC version that the above Initial keys are created
+       for. */
+    uint32_t version;
+    /* preferred_versions is the array of versions that are preferred
+       by the local endpoint.  Server negotiates one of those versions
+       in this array if a client initially selects a less preferred
+       version.  Client uses this field and original_version field to
+       prevent version downgrade attack if it reacted upon Version
+       Negotiation packet. */
+    uint32_t *preferred_versions;
+    /* preferred_versionslen is the number of versions stored in the
+       array pointed by preferred_versions.  This field is only used
+       by server. */
+    size_t preferred_versionslen;
+    /* other_versions is the versions that the local endpoint sends in
+       version_information transport parameter.  This is the wire
+       image of other_versions field of version_information transport
+       parameter. */
+    uint8_t *other_versions;
+    /* other_versionslen is the length of data pointed by
+       other_versions field. */
+    size_t other_versionslen;
+  } vneg;
+
   ngtcp2_map strms;
   ngtcp2_conn_stat cstat;
   ngtcp2_pv *pv;
+  ngtcp2_pmtud *pmtud;
   ngtcp2_log log;
   ngtcp2_qlog qlog;
   ngtcp2_rst rst;
@@ -601,9 +679,10 @@ struct ngtcp2_conn {
   /* idle_ts is the time instant when idle timer started. */
   ngtcp2_tstamp idle_ts;
   void *user_data;
-  uint32_t version;
+  uint32_t client_chosen_version;
+  uint32_t negotiated_version;
   /* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */
-  uint16_t flags;
+  uint32_t flags;
   int server;
 };
 
@@ -724,9 +803,15 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm);
  * ack_delay included in ACK frame.  |ack_delay| is actually tainted
  * (sent by peer), so don't assume that |ack_delay| is always smaller
  * than, or equals to |rtt|.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_INVALID_ARGUMENT
+ *     RTT sample is ignored.
  */
-void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
-                            ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
+int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
+                           ngtcp2_duration ack_delay, ngtcp2_tstamp ts);
 
 void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts);
 
@@ -779,11 +864,12 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
                                     ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts);
 
 /*
- * ngtcp2_conn_write_single_frame_pkt writes a packet which contains |fr|
- * frame only in the buffer pointed by |dest| whose length if
+ * ngtcp2_conn_write_single_frame_pkt writes a packet which contains
+ * |fr| frame only in the buffer pointed by |dest| whose length if
  * |destlen|.  |type| is a long packet type to send.  If |type| is 0,
  * Short packet is used.  |dcid| is used as a destination connection
- * ID.
+ * ID.  |flags| is zero or more of NGTCP2_WRITE_PKT_FLAG_*.  Only
+ * NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING is recognized.
  *
  * The packet written by this function will not be retransmitted.
  *
@@ -795,8 +881,8 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
  */
 ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
     ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
-    uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags,
-    const ngtcp2_path *path, ngtcp2_tstamp ts);
+    uint8_t type, uint8_t flags, const ngtcp2_cid *dcid, ngtcp2_frame *fr,
+    uint16_t rtb_entry_flags, const ngtcp2_path *path, ngtcp2_tstamp ts);
 
 /*
  * ngtcp2_conn_commit_local_transport_params commits the local
@@ -872,6 +958,133 @@ void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
  */
 ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn);
 
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_idle_expiry` returns the time when a connection
+ * should be closed if it continues to be idle.  If idle timeout is
+ * disabled, this function returns ``UINT64_MAX``.
+ */
+ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn);
+
 ngtcp2_duration ngtcp2_conn_compute_pto(ngtcp2_conn *conn, ngtcp2_pktns *pktns);
 
+/*
+ * ngtcp2_conn_track_retired_dcid_seq tracks the sequence number |seq|
+ * of unacknowledged retiring Destination Connection ID.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_CONNECTION_ID_LIMIT
+ *     The number of unacknowledged retirement exceeds the limit.
+ */
+int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq);
+
+/*
+ * ngtcp2_conn_untrack_retired_dcid_seq deletes the sequence number
+ * |seq| of unacknowledged retiring Destination Connection ID.  It is
+ * fine if such sequence number is not found.
+ */
+void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq);
+
+/*
+ * ngtcp2_conn_server_negotiate_version negotiates QUIC version.  It
+ * is compatible version negotiation.  It returns the negotiated QUIC
+ * version.  This function must not be called by client.
+ */
+uint32_t
+ngtcp2_conn_server_negotiate_version(ngtcp2_conn *conn,
+                                     const ngtcp2_version_info *version_info);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_connection_close_pkt` writes a packet which
+ * contains a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed
+ * by |dest| whose capacity is |datalen|.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent.  Each addr field must point
+ * to the buffer which should be at least ``sizeof(struct
+ * sockaddr_storage)`` bytes long.  The assignment might not be done
+ * if nothing is written to |dest|.
+ *
+ * If |pi| is not ``NULL``, this function stores packet metadata in it
+ * if it succeeds.  The metadata includes ECN markings.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * At the moment, successful call to this function makes connection
+ * close.  We may change this behaviour in the future to allow
+ * graceful shutdown.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ *     Out of memory
+ * :macro:`NGTCP2_ERR_NOBUF`
+ *     Buffer is too small
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ *     The current state does not allow sending CONNECTION_CLOSE.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ *     Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ *     User callback failed
+ */
+ngtcp2_ssize ngtcp2_conn_write_connection_close_pkt(
+    ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+    size_t destlen, uint64_t error_code, const uint8_t *reason,
+    size_t reasonlen, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_application_close_pkt` writes a packet which
+ * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed
+ * by |dest| whose capacity is |datalen|.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent.  Each addr field must point
+ * to the buffer which should be at least ``sizeof(struct
+ * sockaddr_storage)`` bytes long.  The assignment might not be done
+ * if nothing is written to |dest|.
+ *
+ * If |pi| is not ``NULL``, this function stores packet metadata in it
+ * if it succeeds.  The metadata includes ECN markings.
+ *
+ * If handshake has not been confirmed yet, CONNECTION_CLOSE (type
+ * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written
+ * instead.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * At the moment, successful call to this function makes connection
+ * close.  We may change this behaviour in the future to allow
+ * graceful shutdown.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ *     Out of memory
+ * :macro:`NGTCP2_ERR_NOBUF`
+ *     Buffer is too small
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ *     The current state does not allow sending CONNECTION_CLOSE.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ *     Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ *     User callback failed
+ */
+ngtcp2_ssize ngtcp2_conn_write_application_close_pkt(
+    ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+    size_t destlen, uint64_t app_error_code, const uint8_t *reason,
+    size_t reasonlen, ngtcp2_tstamp ts);
+
+void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn);
+
 #endif /* NGTCP2_CONN_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c
index 9064218dac..dcf72e4d0e 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c
@@ -29,6 +29,7 @@
 
 #include "ngtcp2_str.h"
 #include "ngtcp2_pkt.h"
+#include "ngtcp2_net.h"
 
 uint64_t ngtcp2_get_uint64(const uint8_t *p) {
   uint64_t n;
@@ -155,13 +156,13 @@ uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n) {
   return rv;
 }
 
-uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) {
+uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n) {
   uint8_t *rv;
 
-  assert(n < 16384);
+  assert(n < 1073741824);
 
-  rv = ngtcp2_put_uint16be(p, n);
-  *p |= 0x40;
+  rv = ngtcp2_put_uint32be(p, n);
+  *p |= 0x80;
 
   return rv;
 }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h
index dcff0fdb8c..99746fdb4c 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h
@@ -29,107 +29,8 @@
 #  include <config.h>
 #endif /* HAVE_CONFIG_H */
 
-#ifdef HAVE_ARPA_INET_H
-#  include <arpa/inet.h>
-#endif /* HAVE_ARPA_INET_H */
-
-#ifdef HAVE_NETINET_IN_H
-#  include <netinet/in.h>
-#endif /* HAVE_NETINET_IN_H */
-
-#ifdef HAVE_BYTESWAP_H
-#  include <byteswap.h>
-#endif /* HAVE_BYTESWAP_H */
-
-#ifdef HAVE_ENDIAN_H
-#  include <endian.h>
-#endif /* HAVE_ENDIAN_H */
-
-#ifdef HAVE_SYS_ENDIAN_H
-#  include <sys/endian.h>
-#endif /* HAVE_SYS_ENDIAN_H */
-
 #include <ngtcp2/ngtcp2.h>
 
-#if defined(HAVE_BSWAP_64) ||                                                  \
-    (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0)
-#  define ngtcp2_bswap64 bswap_64
-#else /* !HAVE_BSWAP_64 */
-#  define ngtcp2_bswap64(N)                                                    \
-    ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 |                           \
-     ngtcp2_ntohl((uint32_t)((N) >> 32)))
-#endif /* !HAVE_BSWAP_64 */
-
-#if defined(HAVE_BE64TOH) ||                                                   \
-    (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0)
-#  define ngtcp2_ntohl64(N) be64toh(N)
-#  define ngtcp2_htonl64(N) htobe64(N)
-#else /* !HAVE_BE64TOH */
-#  if defined(WORDS_BIGENDIAN)
-#    define ngtcp2_ntohl64(N) (N)
-#    define ngtcp2_htonl64(N) (N)
-#  else /* !WORDS_BIGENDIAN */
-#    define ngtcp2_ntohl64(N) ngtcp2_bswap64(N)
-#    define ngtcp2_htonl64(N) ngtcp2_bswap64(N)
-#  endif /* !WORDS_BIGENDIAN */
-#endif   /* !HAVE_BE64TOH */
-
-#if defined(WIN32)
-/* Windows requires ws2_32 library for ntonl family functions.  We
-   define inline functions for those function so that we don't have
-   dependeny on that lib. */
-
-#  ifdef _MSC_VER
-#    define STIN static __inline
-#  else
-#    define STIN static inline
-#  endif
-
-STIN uint32_t ngtcp2_htonl(uint32_t hostlong) {
-  uint32_t res;
-  unsigned char *p = (unsigned char *)&res;
-  *p++ = hostlong >> 24;
-  *p++ = (hostlong >> 16) & 0xffu;
-  *p++ = (hostlong >> 8) & 0xffu;
-  *p = hostlong & 0xffu;
-  return res;
-}
-
-STIN uint16_t ngtcp2_htons(uint16_t hostshort) {
-  uint16_t res;
-  unsigned char *p = (unsigned char *)&res;
-  *p++ = hostshort >> 8;
-  *p = hostshort & 0xffu;
-  return res;
-}
-
-STIN uint32_t ngtcp2_ntohl(uint32_t netlong) {
-  uint32_t res;
-  unsigned char *p = (unsigned char *)&netlong;
-  res = *p++ << 24;
-  res += *p++ << 16;
-  res += *p++ << 8;
-  res += *p;
-  return res;
-}
-
-STIN uint16_t ngtcp2_ntohs(uint16_t netshort) {
-  uint16_t res;
-  unsigned char *p = (unsigned char *)&netshort;
-  res = *p++ << 8;
-  res += *p;
-  return res;
-}
-
-#else /* !WIN32 */
-
-#  define ngtcp2_htonl htonl
-#  define ngtcp2_htons htons
-#  define ngtcp2_ntohl ntohl
-#  define ngtcp2_ntohs ntohs
-
-#endif /* !WIN32 */
-
 /*
  * ngtcp2_get_uint64 reads 8 bytes from |p| as 64 bits unsigned
  * integer encoded as network byte order, and returns it in host byte
@@ -220,12 +121,12 @@ uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n);
 uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n);
 
 /*
- * ngtcp2_put_varint14 writes |n| in |p| using variable-length integer
- * encoding.  |n| must be strictly less than 16384.  The function
- * always encodes |n| in 2 bytes.  It returns the one beyond of the
+ * ngtcp2_put_varint30 writes |n| in |p| using variable-length integer
+ * encoding.  |n| must be strictly less than 1073741824.  The function
+ * always encodes |n| in 4 bytes.  It returns the one beyond of the
  * last written position.
  */
-uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n);
+uint8_t *ngtcp2_put_varint30(uint8_t *p, uint32_t n);
 
 /*
  * ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes.  It
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c
index 562328daa8..648b8b429b 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c
@@ -30,6 +30,7 @@
 #include "ngtcp2_str.h"
 #include "ngtcp2_conv.h"
 #include "ngtcp2_conn.h"
+#include "ngtcp2_net.h"
 
 int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret,
                          size_t secretlen,
@@ -157,6 +158,7 @@ ngtcp2_ssize ngtcp2_encode_transport_params_versioned(
   size_t len = 0;
   /* For some reason, gcc 7.3.0 requires this initialization. */
   size_t preferred_addrlen = 0;
+  size_t version_infolen = 0;
   (void)transport_params_version;
 
   switch (exttype) {
@@ -258,6 +260,12 @@ ngtcp2_ssize ngtcp2_encode_transport_params_versioned(
     len += ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) +
            ngtcp2_put_varint_len(0);
   }
+  if (params->version_info_present) {
+    version_infolen = sizeof(uint32_t) + params->version_info.other_versionslen;
+    len += ngtcp2_put_varint_len(
+               NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) +
+           ngtcp2_put_varint_len(version_infolen) + version_infolen;
+  }
 
   if (destlen < len) {
     return NGTCP2_ERR_NOBUF;
@@ -394,6 +402,16 @@ ngtcp2_ssize ngtcp2_encode_transport_params_versioned(
     p = ngtcp2_put_varint(p, 0);
   }
 
+  if (params->version_info_present) {
+    p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT);
+    p = ngtcp2_put_varint(p, version_infolen);
+    p = ngtcp2_put_uint32be(p, params->version_info.chosen_version);
+    if (params->version_info.other_versionslen) {
+      p = ngtcp2_cpymem(p, params->version_info.other_versions,
+                        params->version_info.other_versionslen);
+    }
+  }
+
   assert((size_t)(p - dest) == len);
 
   return (ngtcp2_ssize)len;
@@ -504,6 +522,7 @@ int ngtcp2_decode_transport_params_versioned(
   ngtcp2_ssize nread;
   int initial_scid_present = 0;
   int original_dcid_present = 0;
+  size_t i;
   (void)transport_params_version;
 
   if (datalen == 0) {
@@ -531,6 +550,7 @@ int ngtcp2_decode_transport_params_versioned(
   memset(&params->retry_scid, 0, sizeof(params->retry_scid));
   memset(&params->initial_scid, 0, sizeof(params->initial_scid));
   memset(&params->original_dcid, 0, sizeof(params->original_dcid));
+  params->version_info_present = 0;
 
   p = data;
   end = data + datalen;
@@ -768,6 +788,36 @@ int ngtcp2_decode_transport_params_versioned(
       p += nread;
       params->grease_quic_bit = 1;
       break;
+    case NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT:
+      nread = decode_varint(&valuelen, p, end);
+      if (nread < 0) {
+        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+      }
+      p += nread;
+      if ((size_t)(end - p) < valuelen) {
+        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+      }
+      if (valuelen < sizeof(uint32_t) || (valuelen & 0x3)) {
+        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+      }
+      params->version_info.chosen_version = ngtcp2_get_uint32(p);
+      if (params->version_info.chosen_version == 0) {
+        return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+      }
+      p += sizeof(uint32_t);
+      if (valuelen > sizeof(uint32_t)) {
+        params->version_info.other_versions = (uint8_t *)p;
+        params->version_info.other_versionslen = valuelen - sizeof(uint32_t);
+
+        for (i = sizeof(uint32_t); i < valuelen;
+             i += sizeof(uint32_t), p += sizeof(uint32_t)) {
+          if (ngtcp2_get_uint32(p) == 0) {
+            return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM;
+          }
+        }
+      }
+      params->version_info_present = 1;
+      break;
     default:
       /* Ignore unknown parameter */
       nread = decode_varint(&valuelen, p, end);
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h
index c787cf07cd..8735ff8cec 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h
@@ -61,8 +61,11 @@ typedef enum ngtcp2_transport_param_id {
   NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e,
   NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f,
   NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010,
+  /* https://datatracker.ietf.org/doc/html/rfc9221 */
   NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020,
   NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT = 0x2ab2,
+  /* https://quicwg.org/quic-v2/draft-ietf-quic-v2.html */
+  NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT = 0xff73db,
 } ngtcp2_transport_param_id;
 
 /* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c
index ca007d6b07..8f676da3ef 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c
@@ -104,6 +104,10 @@ const char *ngtcp2_strerror(int liberr) {
     return "ERR_VERSION_NEGOTIATION";
   case NGTCP2_ERR_HANDSHAKE_TIMEOUT:
     return "ERR_HANDSHAKE_TIMEOUT";
+  case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+    return "ERR_VERSION_NEGOTIATION_FAILURE";
+  case NGTCP2_ERR_IDLE_CLOSE:
+    return "ERR_IDLE_CLOSE";
   default:
     return "(unknown)";
   }
@@ -131,6 +135,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) {
   case NGTCP2_ERR_TRANSPORT_PARAM:
     return NGTCP2_TRANSPORT_PARAMETER_ERROR;
   case NGTCP2_ERR_INVALID_ARGUMENT:
+  case NGTCP2_ERR_NOMEM:
+  case NGTCP2_ERR_CALLBACK_FAILURE:
     return NGTCP2_INTERNAL_ERROR;
   case NGTCP2_ERR_STREAM_STATE:
     return NGTCP2_STREAM_STATE_ERROR;
@@ -140,6 +146,8 @@ uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) {
     return NGTCP2_AEAD_LIMIT_REACHED;
   case NGTCP2_ERR_NO_VIABLE_PATH:
     return NGTCP2_NO_VIABLE_PATH;
+  case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+    return NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT;
   default:
     return NGTCP2_PROTOCOL_VIOLATION;
   }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
index 905d247cc9..87c23898e8 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.c
@@ -23,29 +23,26 @@
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 #include "ngtcp2_gaptr.h"
-#include "ngtcp2_range.h"
 
 #include <string.h>
 #include <assert.h>
 
-int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) {
-  int rv;
-  ngtcp2_range range = {0, UINT64_MAX};
+void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) {
+  ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range),
+                  mem);
 
-  rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar,
-                       sizeof(ngtcp2_range), mem);
-  if (rv != 0) {
-    return rv;
-  }
+  gaptr->mem = mem;
+}
+
+static int gaptr_gap_init(ngtcp2_gaptr *gaptr) {
+  ngtcp2_range range = {0, UINT64_MAX};
+  int rv;
 
   rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL);
   if (rv != 0) {
-    ngtcp2_ksl_free(&gaptr->gap);
     return rv;
   }
 
-  gaptr->mem = mem;
-
   return 0;
 }
 
@@ -62,6 +59,13 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) {
   ngtcp2_range k, m, l, r, q = {offset, offset + datalen};
   ngtcp2_ksl_it it;
 
+  if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+    rv = gaptr_gap_init(gaptr);
+    if (rv != 0) {
+      return rv;
+    }
+  }
+
   it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
                                      ngtcp2_ksl_range_exclusive_compar);
 
@@ -95,32 +99,66 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) {
 }
 
 uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) {
-  ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap);
-  ngtcp2_range r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+  ngtcp2_ksl_it it;
+  ngtcp2_range r;
+
+  if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+    return 0;
+  }
+
+  it = ngtcp2_ksl_begin(&gaptr->gap);
+  r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+
   return r.begin;
 }
 
-ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
-                                               uint64_t offset) {
+ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
+                                              uint64_t offset) {
   ngtcp2_range q = {offset, offset + 1};
-  return ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
-                                       ngtcp2_ksl_range_exclusive_compar);
+  ngtcp2_ksl_it it;
+
+  if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+    ngtcp2_range r = {0, UINT64_MAX};
+    return r;
+  }
+
+  it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
+                                     ngtcp2_ksl_range_exclusive_compar);
+
+  assert(!ngtcp2_ksl_it_end(&it));
+
+  return *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
 }
 
 int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset,
                            uint64_t datalen) {
   ngtcp2_range q = {offset, offset + datalen};
-  ngtcp2_ksl_it it = ngtcp2_ksl_lower_bound_compar(
-      &gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar);
-  ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
-  ngtcp2_range m = ngtcp2_range_intersect(&q, &k);
+  ngtcp2_ksl_it it;
+  ngtcp2_range k;
+  ngtcp2_range m;
+
+  if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+    return 0;
+  }
+
+  it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q,
+                                     ngtcp2_ksl_range_exclusive_compar);
+  k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
+  m = ngtcp2_range_intersect(&q, &k);
+
   return ngtcp2_range_len(&m) == 0;
 }
 
 void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr) {
-  ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap);
+  ngtcp2_ksl_it it;
   ngtcp2_range r;
 
+  if (ngtcp2_ksl_len(&gaptr->gap) == 0) {
+    return;
+  }
+
+  it = ngtcp2_ksl_begin(&gaptr->gap);
+
   assert(!ngtcp2_ksl_it_end(&it));
 
   r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it);
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h
index 3b3190c9c3..0f100a81c4 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_gaptr.h
@@ -33,6 +33,7 @@
 
 #include "ngtcp2_mem.h"
 #include "ngtcp2_ksl.h"
+#include "ngtcp2_range.h"
 
 /*
  * ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX).
@@ -47,14 +48,8 @@ typedef struct ngtcp2_gaptr {
 
 /*
  * ngtcp2_gaptr_init initializes |gaptr|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- *     Out of memory.
  */
-int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem);
+void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem);
 
 /*
  * ngtcp2_gaptr_free frees resources allocated for |gaptr|.
@@ -80,11 +75,11 @@ int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen);
 uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr);
 
 /*
- * ngtcp2_gaptr_get_first_gap_after returns the iterator pointing to
- * the first gap which overlaps or comes after |offset|.
+ * ngtcp2_gaptr_get_first_gap_after returns the first gap which
+ * overlaps or comes after |offset|.
  */
-ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
-                                               uint64_t offset);
+ngtcp2_range ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr,
+                                              uint64_t offset);
 
 /*
  * ngtcp2_gaptr_is_pushed returns nonzero if range [offset, offset +
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c
index f04806b4a8..d988022769 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.c
@@ -26,17 +26,10 @@
 
 #include <assert.h>
 
-int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) {
-  int rv;
-
-  rv = ngtcp2_gaptr_init(&idtr->gap, mem);
-  if (rv != 0) {
-    return rv;
-  }
+void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) {
+  ngtcp2_gaptr_init(&idtr->gap, mem);
 
   idtr->server = server;
-
-  return 0;
 }
 
 void ngtcp2_idtr_free(ngtcp2_idtr *idtr) {
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h
index 1be64dc16e..edb8c68c8d 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_idtr.h
@@ -51,14 +51,8 @@ typedef struct ngtcp2_idtr {
  *
  * If this object records server initiated ID (even number), set
  * |server| to nonzero.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- *     Out of memory.
  */
-int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem);
+void ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem);
 
 /*
  * ngtcp2_idtr_free frees resources allocated for |idtr|.
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c
index caef7f762a..f4ad04cbc9 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.c
@@ -33,9 +33,11 @@
 #include "ngtcp2_mem.h"
 #include "ngtcp2_range.h"
 
+static ngtcp2_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}};
+
 static size_t ksl_nodelen(size_t keylen) {
-  return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) &
-         (size_t)~0xf;
+  return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xfllu) &
+         (size_t)~0xfllu;
 }
 
 static size_t ksl_blklen(size_t nodelen) {
@@ -51,31 +53,47 @@ static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node,
   memcpy(node->key, key, ksl->keylen);
 }
 
-int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
-                    const ngtcp2_mem *mem) {
+void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
+                     const ngtcp2_mem *mem) {
   size_t nodelen = ksl_nodelen(keylen);
-  size_t blklen = ksl_blklen(nodelen);
-  ngtcp2_ksl_blk *head;
 
-  ksl->head = ngtcp2_mem_malloc(mem, blklen);
-  if (!ksl->head) {
-    return NGTCP2_ERR_NOMEM;
-  }
-  ksl->front = ksl->back = ksl->head;
+  ngtcp2_objalloc_init(&ksl->blkalloc,
+                       ((ksl_blklen(nodelen) + 0xfllu) & ~0xfllu) * 8, mem);
+
+  ksl->head = NULL;
+  ksl->front = ksl->back = NULL;
   ksl->compar = compar;
   ksl->keylen = keylen;
   ksl->nodelen = nodelen;
   ksl->n = 0;
-  ksl->mem = mem;
+}
+
+static ngtcp2_ksl_blk *ksl_blk_objalloc_new(ngtcp2_ksl *ksl) {
+  return ngtcp2_objalloc_ksl_blk_len_get(&ksl->blkalloc,
+                                         ksl_blklen(ksl->nodelen));
+}
+
+static void ksl_blk_objalloc_del(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
+  ngtcp2_objalloc_ksl_blk_release(&ksl->blkalloc, blk);
+}
+
+static int ksl_head_init(ngtcp2_ksl *ksl) {
+  ngtcp2_ksl_blk *head = ksl_blk_objalloc_new(ksl);
+  if (!head) {
+    return NGTCP2_ERR_NOMEM;
+  }
 
-  head = ksl->head;
   head->next = head->prev = NULL;
   head->n = 0;
   head->leaf = 1;
 
+  ksl->head = head;
+  ksl->front = ksl->back = head;
+
   return 0;
 }
 
+#ifdef NOMEMPOOL
 /*
  * ksl_free_blk frees |blk| recursively.
  */
@@ -88,15 +106,20 @@ static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
     }
   }
 
-  ngtcp2_mem_free(ksl->mem, blk);
+  ksl_blk_objalloc_del(ksl, blk);
 }
+#endif /* NOMEMPOOL */
 
 void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
-  if (!ksl) {
+  if (!ksl || !ksl->head) {
     return;
   }
 
+#ifdef NOMEMPOOL
   ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
+
+  ngtcp2_objalloc_free(&ksl->blkalloc);
 }
 
 /*
@@ -110,7 +133,7 @@ void ngtcp2_ksl_free(ngtcp2_ksl *ksl) {
 static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) {
   ngtcp2_ksl_blk *rblk;
 
-  rblk = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
+  rblk = ksl_blk_objalloc_new(ksl);
   if (rblk == NULL) {
     return NULL;
   }
@@ -194,9 +217,9 @@ static int ksl_split_head(ngtcp2_ksl *ksl) {
 
   lblk = ksl->head;
 
-  nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen));
+  nhead = ksl_blk_objalloc_new(ksl);
   if (nhead == NULL) {
-    ngtcp2_mem_free(ksl->mem, rblk);
+    ksl_blk_objalloc_del(ksl, rblk);
     return NGTCP2_ERR_NOMEM;
   }
   nhead->next = nhead->prev = NULL;
@@ -253,11 +276,20 @@ static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
 
 int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
                       const ngtcp2_ksl_key *key, void *data) {
-  ngtcp2_ksl_blk *blk = ksl->head;
+  ngtcp2_ksl_blk *blk;
   ngtcp2_ksl_node *node;
   size_t i;
   int rv;
 
+  if (!ksl->head) {
+    rv = ksl_head_init(ksl);
+    if (rv != 0) {
+      return rv;
+    }
+  }
+
+  blk = ksl->head;
+
   if (blk->n == NGTCP2_KSL_MAX_NBLK) {
     rv = ksl_split_head(ksl);
     if (rv != 0) {
@@ -369,10 +401,10 @@ static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk,
     ksl->back = lblk;
   }
 
-  ngtcp2_mem_free(ksl->mem, rblk);
+  ksl_blk_objalloc_del(ksl, rblk);
 
   if (ksl->head == blk && blk->n == 2) {
-    ngtcp2_mem_free(ksl->mem, ksl->head);
+    ksl_blk_objalloc_del(ksl, ksl->head);
     ksl->head = lblk;
   } else {
     ksl_remove_node(ksl, blk, i + 1);
@@ -469,6 +501,8 @@ int ngtcp2_ksl_remove_hint(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
                            const ngtcp2_ksl_key *key) {
   ngtcp2_ksl_blk *blk = hint->blk;
 
+  assert(ksl->head);
+
   if (blk->n <= NGTCP2_KSL_MIN_NBLK) {
     return ngtcp2_ksl_remove(ksl, it, key);
   }
@@ -494,6 +528,10 @@ int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it,
   ngtcp2_ksl_node *node;
   size_t i;
 
+  if (!ksl->head) {
+    return NGTCP2_ERR_INVALID_ARGUMENT;
+  }
+
   if (!blk->leaf && blk->n == 2 &&
       ngtcp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK &&
       ngtcp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) {
@@ -569,6 +607,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl,
   ngtcp2_ksl_it it;
   size_t i;
 
+  if (!blk) {
+    ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+    return it;
+  }
+
   for (;;) {
     i = ksl_bsearch(ksl, blk, key, ksl->compar);
 
@@ -606,6 +649,11 @@ ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl,
   ngtcp2_ksl_it it;
   size_t i;
 
+  if (!blk) {
+    ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+    return it;
+  }
+
   for (;;) {
     i = ksl_bsearch(ksl, blk, key, compar);
 
@@ -642,6 +690,8 @@ void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key,
   ngtcp2_ksl_node *node;
   size_t i;
 
+  assert(ksl->head);
+
   for (;;) {
     i = ksl_bsearch(ksl, blk, old_key, ksl->compar);
 
@@ -686,36 +736,49 @@ static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) {
 size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl) { return ksl->n; }
 
 void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) {
-  size_t i;
-  ngtcp2_ksl_blk *head;
-
-  if (!ksl->head->leaf) {
-    for (i = 0; i < ksl->head->n; ++i) {
-      ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, ksl->head, i)->blk);
-    }
+  if (!ksl->head) {
+    return;
   }
 
-  ksl->front = ksl->back = ksl->head;
-  ksl->n = 0;
+#ifdef NOMEMPOOL
+  ksl_free_blk(ksl, ksl->head);
+#endif /* NOMEMPOOL */
 
-  head = ksl->head;
+  ksl->front = ksl->back = ksl->head = NULL;
+  ksl->n = 0;
 
-  head->next = head->prev = NULL;
-  head->n = 0;
-  head->leaf = 1;
+  ngtcp2_objalloc_clear(&ksl->blkalloc);
 }
 
-void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); }
+void ngtcp2_ksl_print(ngtcp2_ksl *ksl) {
+  if (!ksl->head) {
+    return;
+  }
+
+  ksl_print(ksl, ksl->head, 0);
+}
 
 ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) {
   ngtcp2_ksl_it it;
-  ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0);
+
+  if (ksl->head) {
+    ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0);
+  } else {
+    ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+  }
+
   return it;
 }
 
 ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) {
   ngtcp2_ksl_it it;
-  ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
+
+  if (ksl->head) {
+    ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n);
+  } else {
+    ngtcp2_ksl_it_init(&it, ksl, &null_blk, 0);
+  }
+
   return it;
 }
 
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h
index f3716a02b3..312a151d4a 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ksl.h
@@ -33,6 +33,8 @@
 
 #include <ngtcp2/ngtcp2.h>
 
+#include "ngtcp2_objalloc.h"
+
 /*
  * Skip List using single key instead of range.
  */
@@ -79,25 +81,33 @@ struct ngtcp2_ksl_node {
  * ngtcp2_ksl_blk contains ngtcp2_ksl_node objects.
  */
 struct ngtcp2_ksl_blk {
-  /* next points to the next block if leaf field is nonzero. */
-  ngtcp2_ksl_blk *next;
-  /* prev points to the previous block if leaf field is nonzero. */
-  ngtcp2_ksl_blk *prev;
-  /* n is the number of nodes this object contains in nodes. */
-  uint32_t n;
-  /* leaf is nonzero if this block contains leaf nodes. */
-  uint32_t leaf;
   union {
-    uint64_t align;
-    /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK
-       ngtcp2_ksl_node objects.  Because ngtcp2_ksl_node object is
-       allocated along with the additional variable length key
-       storage, the size of buffer is unknown until ngtcp2_ksl_init is
-       called. */
-    uint8_t nodes[1];
+    struct {
+      /* next points to the next block if leaf field is nonzero. */
+      ngtcp2_ksl_blk *next;
+      /* prev points to the previous block if leaf field is nonzero. */
+      ngtcp2_ksl_blk *prev;
+      /* n is the number of nodes this object contains in nodes. */
+      uint32_t n;
+      /* leaf is nonzero if this block contains leaf nodes. */
+      uint32_t leaf;
+      union {
+        uint64_t align;
+        /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK
+           ngtcp2_ksl_node objects.  Because ngtcp2_ksl_node object is
+           allocated along with the additional variable length key
+           storage, the size of buffer is unknown until ngtcp2_ksl_init is
+           called. */
+        uint8_t nodes[1];
+      };
+    };
+
+    ngtcp2_opl_entry oplent;
   };
 };
 
+ngtcp2_objalloc_def(ksl_blk, ngtcp2_ksl_blk, oplent);
+
 /*
  * ngtcp2_ksl_compar is a function type which returns nonzero if key
  * |lhs| should be placed before |rhs|.  It returns 0 otherwise.
@@ -122,6 +132,7 @@ struct ngtcp2_ksl_it {
  * ngtcp2_ksl is a deterministic paged skip list.
  */
 struct ngtcp2_ksl {
+  ngtcp2_objalloc blkalloc;
   /* head points to the root block. */
   ngtcp2_ksl_blk *head;
   /* front points to the first leaf block. */
@@ -135,21 +146,14 @@ struct ngtcp2_ksl {
   /* nodelen is the actual size of ngtcp2_ksl_node including key
      storage. */
   size_t nodelen;
-  const ngtcp2_mem *mem;
 };
 
 /*
  * ngtcp2_ksl_init initializes |ksl|.  |compar| specifies compare
  * function.  |keylen| is the length of key.
- *
- * It returns 0 if it succeeds, or one of the following negative error
- * codes:
- *
- * NGTCP2_ERR_NOMEM
- *   Out of memory.
  */
-int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
-                    const ngtcp2_mem *mem);
+void ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen,
+                     const ngtcp2_mem *mem);
 
 /*
  * ngtcp2_ksl_free frees resources allocated for |ksl|.  If |ksl| is
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c
index 269835a443..ee37ff3517 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_log.c
@@ -34,6 +34,7 @@
 #include "ngtcp2_str.h"
 #include "ngtcp2_vec.h"
 #include "ngtcp2_macro.h"
+#include "ngtcp2_conv.h"
 
 void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
                      ngtcp2_printf log_printf, ngtcp2_tstamp ts,
@@ -69,7 +70,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
  *
  * # Frame event
  *
- * <DIR> <PKN> <PKTNAME>(<PKTTYPE>) <FRAMENAME>(<FRAMETYPE>)
+ * <DIR> <PKN> <PKTNAME> <FRAMENAME>(<FRAMETYPE>)
  *
  * <DIR>:
  *   Flow direction.  tx=transmission, rx=reception
@@ -78,10 +79,7 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
  *   Packet number.
  *
  * <PKTNAME>:
- *   Packet name.  (e.g., Initial, Handshake, S01)
- *
- * <PKTTYPE>:
- *   Packet type in hex string.
+ *   Packet name.  (e.g., Initial, Handshake, 1RTT)
  *
  * <FRAMENAME>:
  *   Frame name.  (e.g., STREAM, ACK, PING)
@@ -94,16 +92,16 @@ void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid,
 
 /* TODO Split second and remaining fraction with comma */
 #define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s"
-#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s(0x%02x)"
+#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s"
 #define NGTCP2_LOG_TP NGTCP2_LOG_HD " remote transport_parameters"
 
 #define NGTCP2_LOG_FRM_HD_FIELDS(DIR)                                          \
   timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "frm",      \
-      (DIR), hd->pkt_num, strpkttype(hd), hd->type
+      (DIR), hd->pkt_num, strpkttype(hd)
 
 #define NGTCP2_LOG_PKT_HD_FIELDS(DIR)                                          \
   timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "pkt",      \
-      (DIR), hd->pkt_num, strpkttype(hd), hd->type
+      (DIR), hd->pkt_num, strpkttype(hd)
 
 #define NGTCP2_LOG_TP_HD_FIELDS                                                \
   timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry"
@@ -140,6 +138,8 @@ static const char *strerrorcode(uint64_t error_code) {
     return "CRYPTO_BUFFER_EXCEEDED";
   case NGTCP2_KEY_UPDATE_ERROR:
     return "KEY_UPDATE_ERROR";
+  case NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT:
+    return "VERSION_NEGOTIATION_ERROR";
   default:
     if (0x100u <= error_code && error_code <= 0x1ffu) {
       return "CRYPTO_ERROR";
@@ -155,10 +155,6 @@ static const char *strapperrorcode(uint64_t app_error_code) {
 
 static const char *strpkttype_long(uint8_t type) {
   switch (type) {
-  case NGTCP2_PKT_VERSION_NEGOTIATION:
-    return "VN";
-  case NGTCP2_PKT_STATELESS_RESET:
-    return "SR";
   case NGTCP2_PKT_INITIAL:
     return "Initial";
   case NGTCP2_PKT_RETRY:
@@ -176,7 +172,26 @@ static const char *strpkttype(const ngtcp2_pkt_hd *hd) {
   if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) {
     return strpkttype_long(hd->type);
   }
-  return "Short";
+
+  switch (hd->type) {
+  case NGTCP2_PKT_VERSION_NEGOTIATION:
+    return "VN";
+  case NGTCP2_PKT_STATELESS_RESET:
+    return "SR";
+  case NGTCP2_PKT_1RTT:
+    return "1RTT";
+  default:
+    return "(unknown)";
+  }
+}
+
+static const char *strpkttype_type_flags(uint8_t type, uint8_t flags) {
+  ngtcp2_pkt_hd hd = {0};
+
+  hd.type = type;
+  hd.flags = flags;
+
+  return strpkttype(&hd);
 }
 
 static const char *strevent(ngtcp2_log_event ev) {
@@ -398,17 +413,11 @@ static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
 
 static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
                           const ngtcp2_crypto *fr, const char *dir) {
-  size_t datalen = 0;
-  size_t i;
-
-  for (i = 0; i < fr->datacnt; ++i) {
-    datalen += fr->data[i].len;
-  }
-
   log->log_printf(
       log->user_data,
       (NGTCP2_LOG_PKT " CRYPTO(0x%02x) offset=%" PRIu64 " len=%" PRIu64),
-      NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, datalen);
+      NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset,
+      ngtcp2_vec_len(fr->data, fr->datacnt));
 }
 
 static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
@@ -573,7 +582,6 @@ void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) {
 
   memset(&shd, 0, sizeof(shd));
 
-  shd.flags = NGTCP2_PKT_FLAG_LONG_FORM;
   shd.type = NGTCP2_PKT_STATELESS_RESET;
 
   log->log_printf(
@@ -589,6 +597,7 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype,
   uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1];
   uint8_t addr[16 * 2 + 7 + 1];
   uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1];
+  size_t i;
 
   if (!log->log_printf) {
     return;
@@ -702,6 +711,24 @@ void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype,
                   NGTCP2_LOG_TP_HD_FIELDS, params->max_datagram_frame_size);
   log->log_printf(log->user_data, (NGTCP2_LOG_TP " grease_quic_bit=%d"),
                   NGTCP2_LOG_TP_HD_FIELDS, params->grease_quic_bit);
+
+  if (params->version_info_present) {
+    log->log_printf(
+        log->user_data,
+        (NGTCP2_LOG_TP " version_information.chosen_version=0x%08x"),
+        NGTCP2_LOG_TP_HD_FIELDS, params->version_info.chosen_version);
+
+    assert(!(params->version_info.other_versionslen & 0x3));
+
+    for (i = 0; i < params->version_info.other_versionslen;
+         i += sizeof(uint32_t)) {
+      log->log_printf(
+          log->user_data,
+          (NGTCP2_LOG_TP " version_information.other_versions[%zu]=0x%08x"),
+          NGTCP2_LOG_TP_HD_FIELDS, i >> 2,
+          ngtcp2_get_uint32(&params->version_info.other_versions[i]));
+    }
+  }
 }
 
 void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
@@ -710,11 +737,9 @@ void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type,
     return;
   }
 
-  ngtcp2_log_info(
-      log, NGTCP2_LOG_EVENT_RCV,
-      "pkn=%" PRId64 " lost type=%s(0x%02x) sent_ts=%" PRIu64, pkt_num,
-      (flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(type) : "Short",
-      type, sent_ts);
+  ngtcp2_log_info(log, NGTCP2_LOG_EVENT_RCV,
+                  "pkn=%" PRId64 " lost type=%s sent_ts=%" PRIu64, pkt_num,
+                  strpkttype_type_flags(type, flags), sent_ts);
 }
 
 static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
@@ -726,15 +751,21 @@ static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
     return;
   }
 
-  ngtcp2_log_info(
-      log, NGTCP2_LOG_EVENT_PKT,
-      "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s type=%s(0x%02x) len=%zu k=%d",
-      dir, hd->pkt_num,
-      (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
-      (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen),
-      (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(hd->type)
-                                              : "Short",
-      hd->type, hd->len, (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0);
+  if (hd->type == NGTCP2_PKT_1RTT) {
+    ngtcp2_log_info(
+        log, NGTCP2_LOG_EVENT_PKT, "%s pkn=%" PRId64 " dcid=0x%s type=%s k=%d",
+        dir, hd->pkt_num,
+        (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
+        strpkttype(hd), (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0);
+  } else {
+    ngtcp2_log_info(
+        log, NGTCP2_LOG_EVENT_PKT,
+        "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s version=0x%08x type=%s len=%zu",
+        dir, hd->pkt_num,
+        (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen),
+        (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen),
+        hd->version, strpkttype(hd), hd->len);
+  }
 }
 
 void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
@@ -770,6 +801,6 @@ void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt,
 
 void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) {
   ngtcp2_log_info(log, NGTCP2_LOG_EVENT_PKT,
-                  "cancel tx pkn=%" PRId64 " type=%s(0x%02x)", hd->pkt_num,
-                  strpkttype(hd), hd->type);
+                  "cancel tx pkn=%" PRId64 " type=%s", hd->pkt_num,
+                  strpkttype(hd));
 }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c
index 9ea0da871e..12bc6e84bd 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.c
@@ -31,20 +31,14 @@
 
 #include "ngtcp2_conv.h"
 
-#define NGTCP2_INITIAL_TABLE_LENBITS 8
+#define NGTCP2_INITIAL_TABLE_LENBITS 4
 
-int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) {
+void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) {
   map->mem = mem;
-  map->tablelen = 1 << NGTCP2_INITIAL_TABLE_LENBITS;
-  map->tablelenbits = NGTCP2_INITIAL_TABLE_LENBITS;
-  map->table = ngtcp2_mem_calloc(mem, map->tablelen, sizeof(ngtcp2_map_bucket));
-  if (map->table == NULL) {
-    return NGTCP2_ERR_NOMEM;
-  }
-
+  map->tablelen = 0;
+  map->tablelenbits = 0;
+  map->table = NULL;
   map->size = 0;
-
-  return 0;
 }
 
 void ngtcp2_map_free(ngtcp2_map *map) {
@@ -77,6 +71,10 @@ int ngtcp2_map_each(ngtcp2_map *map, int (*func)(void *data, void *ptr),
   uint32_t i;
   ngtcp2_map_bucket *bkt;
 
+  if (map->size == 0) {
+    return 0;
+  }
+
   for (i = 0; i < map->tablelen; ++i) {
     bkt = &map->table[i];
 
@@ -222,9 +220,17 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) {
 
   /* Load factor is 0.75 */
   if ((map->size + 1) * 4 > map->tablelen * 3) {
-    rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
-    if (rv != 0) {
-      return rv;
+    if (map->tablelen) {
+      rv = map_resize(map, map->tablelen * 2, map->tablelenbits + 1);
+      if (rv != 0) {
+        return rv;
+      }
+    } else {
+      rv = map_resize(map, 1 << NGTCP2_INITIAL_TABLE_LENBITS,
+                      NGTCP2_INITIAL_TABLE_LENBITS);
+      if (rv != 0) {
+        return rv;
+      }
     }
   }
 
@@ -238,11 +244,18 @@ int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) {
 }
 
 void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key) {
-  uint32_t h = hash(key);
-  size_t idx = h2idx(h, map->tablelenbits);
+  uint32_t h;
+  size_t idx;
   ngtcp2_map_bucket *bkt;
   size_t d = 0;
 
+  if (map->size == 0) {
+    return NULL;
+  }
+
+  h = hash(key);
+  idx = h2idx(h, map->tablelenbits);
+
   for (;;) {
     bkt = &map->table[idx];
 
@@ -261,11 +274,18 @@ void *ngtcp2_map_find(ngtcp2_map *map, ngtcp2_map_key_type key) {
 }
 
 int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) {
-  uint32_t h = hash(key);
-  size_t idx = h2idx(h, map->tablelenbits), didx;
+  uint32_t h;
+  size_t idx, didx;
   ngtcp2_map_bucket *bkt;
   size_t d = 0;
 
+  if (map->size == 0) {
+    return NGTCP2_ERR_INVALID_ARGUMENT;
+  }
+
+  h = hash(key);
+  idx = h2idx(h, map->tablelenbits);
+
   for (;;) {
     bkt = &map->table[idx];
 
@@ -305,6 +325,10 @@ int ngtcp2_map_remove(ngtcp2_map *map, ngtcp2_map_key_type key) {
 }
 
 void ngtcp2_map_clear(ngtcp2_map *map) {
+  if (map->tablelen == 0) {
+    return;
+  }
+
   memset(map->table, 0, sizeof(*map->table) * map->tablelen);
   map->size = 0;
 }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h
index 96275f1e97..a64344a9a3 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_map.h
@@ -54,14 +54,8 @@ typedef struct ngtcp2_map {
 
 /*
  * Initializes the map |map|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- *   Out of memory
  */
-int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem);
+void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem);
 
 /*
  * Deallocates any resources allocated for |map|. The stored entries
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c
index 2c036ad163..bcce0b5cdf 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.c
@@ -25,26 +25,28 @@
  */
 #include "ngtcp2_mem.h"
 
-static void *default_malloc(size_t size, void *mem_user_data) {
-  (void)mem_user_data;
+#include <stdio.h>
+
+static void *default_malloc(size_t size, void *user_data) {
+  (void)user_data;
 
   return malloc(size);
 }
 
-static void default_free(void *ptr, void *mem_user_data) {
-  (void)mem_user_data;
+static void default_free(void *ptr, void *user_data) {
+  (void)user_data;
 
   free(ptr);
 }
 
-static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) {
-  (void)mem_user_data;
+static void *default_calloc(size_t nmemb, size_t size, void *user_data) {
+  (void)user_data;
 
   return calloc(nmemb, size);
 }
 
-static void *default_realloc(void *ptr, size_t size, void *mem_user_data) {
-  (void)mem_user_data;
+static void *default_realloc(void *ptr, size_t size, void *user_data) {
+  (void)user_data;
 
   return realloc(ptr, size);
 }
@@ -54,22 +56,58 @@ static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free,
 
 const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; }
 
+#ifndef MEMDEBUG
 void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) {
-  return mem->malloc(size, mem->mem_user_data);
+  return mem->malloc(size, mem->user_data);
 }
 
 void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) {
-  mem->free(ptr, mem->mem_user_data);
-}
-
-void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data) {
-  free_func(ptr, mem_user_data);
+  mem->free(ptr, mem->user_data);
 }
 
 void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) {
-  return mem->calloc(nmemb, size, mem->mem_user_data);
+  return mem->calloc(nmemb, size, mem->user_data);
 }
 
 void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) {
-  return mem->realloc(ptr, size, mem->mem_user_data);
+  return mem->realloc(ptr, size, mem->user_data);
+}
+#else  /* MEMDEBUG */
+void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size,
+                              const char *func, const char *file, size_t line) {
+  void *nptr = mem->malloc(size, mem->user_data);
+
+  fprintf(stderr, "malloc %p size=%zu in %s at %s:%zu\n", nptr, size, func,
+          file, line);
+
+  return nptr;
+}
+
+void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func,
+                           const char *file, size_t line) {
+  fprintf(stderr, "free ptr=%p in %s at %s:%zu\n", ptr, func, file, line);
+
+  mem->free(ptr, mem->user_data);
+}
+
+void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size,
+                              const char *func, const char *file, size_t line) {
+  void *nptr = mem->calloc(nmemb, size, mem->user_data);
+
+  fprintf(stderr, "calloc %p nmemb=%zu size=%zu in %s at %s:%zu\n", nptr, nmemb,
+          size, func, file, line);
+
+  return nptr;
+}
+
+void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size,
+                               const char *func, const char *file,
+                               size_t line) {
+  void *nptr = mem->realloc(ptr, size, mem->user_data);
+
+  fprintf(stderr, "realloc %p ptr=%p size=%zu in %s at %s:%zu\n", nptr, ptr,
+          size, func, file, line);
+
+  return nptr;
 }
+#endif /* MEMDEBUG */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h
index cdecf8763a..c99b6c5972 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_mem.h
@@ -34,10 +34,39 @@
 
 /* Convenient wrapper functions to call allocator function in
    |mem|. */
+#ifndef MEMDEBUG
 void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size);
+
 void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr);
-void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data);
+
 void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size);
+
 void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size);
+#else /* MEMDEBUG */
+void *ngtcp2_mem_malloc_debug(const ngtcp2_mem *mem, size_t size,
+                              const char *func, const char *file, size_t line);
+
+#  define ngtcp2_mem_malloc(MEM, SIZE)                                         \
+    ngtcp2_mem_malloc_debug((MEM), (SIZE), __func__, __FILE__, __LINE__)
+
+void ngtcp2_mem_free_debug(const ngtcp2_mem *mem, void *ptr, const char *func,
+                           const char *file, size_t line);
+
+#  define ngtcp2_mem_free(MEM, PTR)                                            \
+    ngtcp2_mem_free_debug((MEM), (PTR), __func__, __FILE__, __LINE__)
+
+void *ngtcp2_mem_calloc_debug(const ngtcp2_mem *mem, size_t nmemb, size_t size,
+                              const char *func, const char *file, size_t line);
+
+#  define ngtcp2_mem_calloc(MEM, NMEMB, SIZE)                                  \
+    ngtcp2_mem_calloc_debug((MEM), (NMEMB), (SIZE), __func__, __FILE__,        \
+                            __LINE__)
+
+void *ngtcp2_mem_realloc_debug(const ngtcp2_mem *mem, void *ptr, size_t size,
+                               const char *func, const char *file, size_t line);
+
+#  define ngtcp2_mem_realloc(MEM, PTR, SIZE)                                   \
+    ngtcp2_mem_realloc_debug((MEM), (PTR), (SIZE), __func__, __FILE__, __LINE__)
+#endif /* MEMDEBUG */
 
 #endif /* NGTCP2_MEM_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h
new file mode 100644
index 0000000000..abd8cfdc0c
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_net.h
@@ -0,0 +1,136 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_NET_H
+#define NGTCP2_NET_H
+
+/* This header file is explicitly allowed to be shared with
+   ngtcp2_crypto library. */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_ARPA_INET_H
+#  include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+
+#ifdef HAVE_NETINET_IN_H
+#  include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+
+#ifdef HAVE_BYTESWAP_H
+#  include <byteswap.h>
+#endif /* HAVE_BYTESWAP_H */
+
+#ifdef HAVE_ENDIAN_H
+#  include <endian.h>
+#endif /* HAVE_ENDIAN_H */
+
+#ifdef HAVE_SYS_ENDIAN_H
+#  include <sys/endian.h>
+#endif /* HAVE_SYS_ENDIAN_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#if defined(HAVE_BSWAP_64) ||                                                  \
+    (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0)
+#  define ngtcp2_bswap64 bswap_64
+#else /* !HAVE_BSWAP_64 */
+#  define ngtcp2_bswap64(N)                                                    \
+    ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 |                           \
+     ngtcp2_ntohl((uint32_t)((N) >> 32)))
+#endif /* !HAVE_BSWAP_64 */
+
+#if defined(HAVE_BE64TOH) ||                                                   \
+    (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0)
+#  define ngtcp2_ntohl64(N) be64toh(N)
+#  define ngtcp2_htonl64(N) htobe64(N)
+#else /* !HAVE_BE64TOH */
+#  if defined(WORDS_BIGENDIAN)
+#    define ngtcp2_ntohl64(N) (N)
+#    define ngtcp2_htonl64(N) (N)
+#  else /* !WORDS_BIGENDIAN */
+#    define ngtcp2_ntohl64(N) ngtcp2_bswap64(N)
+#    define ngtcp2_htonl64(N) ngtcp2_bswap64(N)
+#  endif /* !WORDS_BIGENDIAN */
+#endif   /* !HAVE_BE64TOH */
+
+#if defined(WIN32)
+/* Windows requires ws2_32 library for ntonl family functions.  We
+   define inline functions for those function so that we don't have
+   dependeny on that lib. */
+
+#  ifdef _MSC_VER
+#    define STIN static __inline
+#  else
+#    define STIN static inline
+#  endif
+
+STIN uint32_t ngtcp2_htonl(uint32_t hostlong) {
+  uint32_t res;
+  unsigned char *p = (unsigned char *)&res;
+  *p++ = hostlong >> 24;
+  *p++ = (hostlong >> 16) & 0xffu;
+  *p++ = (hostlong >> 8) & 0xffu;
+  *p = hostlong & 0xffu;
+  return res;
+}
+
+STIN uint16_t ngtcp2_htons(uint16_t hostshort) {
+  uint16_t res;
+  unsigned char *p = (unsigned char *)&res;
+  *p++ = hostshort >> 8;
+  *p = hostshort & 0xffu;
+  return res;
+}
+
+STIN uint32_t ngtcp2_ntohl(uint32_t netlong) {
+  uint32_t res;
+  unsigned char *p = (unsigned char *)&netlong;
+  res = *p++ << 24;
+  res += *p++ << 16;
+  res += *p++ << 8;
+  res += *p;
+  return res;
+}
+
+STIN uint16_t ngtcp2_ntohs(uint16_t netshort) {
+  uint16_t res;
+  unsigned char *p = (unsigned char *)&netshort;
+  res = *p++ << 8;
+  res += *p;
+  return res;
+}
+
+#else /* !WIN32 */
+
+#  define ngtcp2_htonl htonl
+#  define ngtcp2_htons htons
+#  define ngtcp2_ntohl ntohl
+#  define ngtcp2_ntohs ntohs
+
+#endif /* !WIN32 */
+
+#endif /* NGTCP2_NET_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c
new file mode 100644
index 0000000000..8b06cdd5de
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.c
@@ -0,0 +1,40 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_objalloc.h"
+
+void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen,
+                          const ngtcp2_mem *mem) {
+  ngtcp2_balloc_init(&objalloc->balloc, blklen, mem);
+  ngtcp2_opl_init(&objalloc->opl);
+}
+
+void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc) {
+  ngtcp2_balloc_free(&objalloc->balloc);
+}
+
+void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc) {
+  ngtcp2_opl_clear(&objalloc->opl);
+  ngtcp2_balloc_clear(&objalloc->balloc);
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h
new file mode 100644
index 0000000000..fc11da531a
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_objalloc.h
@@ -0,0 +1,140 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_OBJALLOC_H
+#define NGTCP2_OBJALLOC_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "ngtcp2_balloc.h"
+#include "ngtcp2_opl.h"
+#include "ngtcp2_macro.h"
+#include "ngtcp2_mem.h"
+
+/*
+ * ngtcp2_objalloc combines ngtcp2_balloc and ngtcp2_opl, and provides
+ * an object pool with the custom allocator to reduce the allocation
+ * and deallocation overheads for small objects.
+ */
+typedef struct ngtcp2_objalloc {
+  ngtcp2_balloc balloc;
+  ngtcp2_opl opl;
+} ngtcp2_objalloc;
+
+/*
+ * ngtcp2_objalloc_init initializes |objalloc|.  |blklen| is directly
+ * passed to ngtcp2_balloc_init.
+ */
+void ngtcp2_objalloc_init(ngtcp2_objalloc *objalloc, size_t blklen,
+                          const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_objalloc_free releases all allocated resources.
+ */
+void ngtcp2_objalloc_free(ngtcp2_objalloc *objalloc);
+
+/*
+ * ngtcp2_objalloc_clear releases all allocated resources and
+ * initializes its state.
+ */
+void ngtcp2_objalloc_clear(ngtcp2_objalloc *objalloc);
+
+#ifndef NOMEMPOOL
+#  define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD)                         \
+    inline static void ngtcp2_objalloc_##NAME##_init(                          \
+        ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) {      \
+      ngtcp2_objalloc_init(objalloc,                                           \
+                           ((sizeof(TYPE) + 0xfllu) & ~0xfllu) * nmemb, mem);  \
+    }                                                                          \
+                                                                               \
+    inline static TYPE *ngtcp2_objalloc_##NAME##_get(                          \
+        ngtcp2_objalloc *objalloc) {                                           \
+      ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl);               \
+      TYPE *obj;                                                               \
+      int rv;                                                                  \
+                                                                               \
+      if (!oplent) {                                                           \
+        rv =                                                                   \
+            ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, sizeof(TYPE)); \
+        if (rv != 0) {                                                         \
+          return NULL;                                                         \
+        }                                                                      \
+                                                                               \
+        return obj;                                                            \
+      }                                                                        \
+                                                                               \
+      return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD);                      \
+    }                                                                          \
+                                                                               \
+    inline static TYPE *ngtcp2_objalloc_##NAME##_len_get(                      \
+        ngtcp2_objalloc *objalloc, size_t len) {                               \
+      ngtcp2_opl_entry *oplent = ngtcp2_opl_pop(&objalloc->opl);               \
+      TYPE *obj;                                                               \
+      int rv;                                                                  \
+                                                                               \
+      if (!oplent) {                                                           \
+        rv = ngtcp2_balloc_get(&objalloc->balloc, (void **)&obj, len);         \
+        if (rv != 0) {                                                         \
+          return NULL;                                                         \
+        }                                                                      \
+                                                                               \
+        return obj;                                                            \
+      }                                                                        \
+                                                                               \
+      return ngtcp2_struct_of(oplent, TYPE, OPLENTFIELD);                      \
+    }                                                                          \
+                                                                               \
+    inline static void ngtcp2_objalloc_##NAME##_release(                       \
+        ngtcp2_objalloc *objalloc, TYPE *obj) {                                \
+      ngtcp2_opl_push(&objalloc->opl, &obj->OPLENTFIELD);                      \
+    }
+#else /* NOMEMPOOL */
+#  define ngtcp2_objalloc_def(NAME, TYPE, OPLENTFIELD)                         \
+    inline static void ngtcp2_objalloc_##NAME##_init(                          \
+        ngtcp2_objalloc *objalloc, size_t nmemb, const ngtcp2_mem *mem) {      \
+      ngtcp2_objalloc_init(objalloc,                                           \
+                           ((sizeof(TYPE) + 0xfllu) & ~0xfllu) * nmemb, mem);  \
+    }                                                                          \
+                                                                               \
+    inline static TYPE *ngtcp2_objalloc_##NAME##_get(                          \
+        ngtcp2_objalloc *objalloc) {                                           \
+      return ngtcp2_mem_malloc(objalloc->balloc.mem, sizeof(TYPE));            \
+    }                                                                          \
+                                                                               \
+    inline static TYPE *ngtcp2_objalloc_##NAME##_len_get(                      \
+        ngtcp2_objalloc *objalloc, size_t len) {                               \
+      return ngtcp2_mem_malloc(objalloc->balloc.mem, len);                     \
+    }                                                                          \
+                                                                               \
+    inline static void ngtcp2_objalloc_##NAME##_release(                       \
+        ngtcp2_objalloc *objalloc, TYPE *obj) {                                \
+      ngtcp2_mem_free(objalloc->balloc.mem, obj);                              \
+    }
+#endif /* NOMEMPOOL */
+
+#endif /* NGTCP2_OBJALLOC_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c
new file mode 100644
index 0000000000..a29361c634
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c
@@ -0,0 +1,46 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_opl.h"
+
+void ngtcp2_opl_init(ngtcp2_opl *opl) { opl->head = NULL; }
+
+void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent) {
+  ent->next = opl->head;
+  opl->head = ent;
+}
+
+ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl) {
+  ngtcp2_opl_entry *ent = opl->head;
+
+  if (!ent) {
+    return NULL;
+  }
+
+  opl->head = ent->next;
+
+  return ent;
+}
+
+void ngtcp2_opl_clear(ngtcp2_opl *opl) { opl->head = NULL; }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h
new file mode 100644
index 0000000000..714aa36630
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h
@@ -0,0 +1,65 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_OPL_H
+#define NGTCP2_OPL_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_opl_entry ngtcp2_opl_entry;
+
+struct ngtcp2_opl_entry {
+  ngtcp2_opl_entry *next;
+};
+
+/*
+ * ngtcp2_opl is an object memory pool.
+ */
+typedef struct ngtcp2_opl {
+  ngtcp2_opl_entry *head;
+} ngtcp2_opl;
+
+/*
+ * ngtcp2_opl_init initializes |opl|.
+ */
+void ngtcp2_opl_init(ngtcp2_opl *opl);
+
+/*
+ * ngtcp2_opl_push inserts |ent| to |opl| head.
+ */
+void ngtcp2_opl_push(ngtcp2_opl *opl, ngtcp2_opl_entry *ent);
+
+/*
+ * ngtcp2_opl_pop removes the first ngtcp2_opl_entry from |opl| and
+ * returns it.  If |opl| does not have any entry, it returns NULL.
+ */
+ngtcp2_opl_entry *ngtcp2_opl_pop(ngtcp2_opl *opl);
+
+void ngtcp2_opl_clear(ngtcp2_opl *opl);
+
+#endif /* NGTCP2_OPL_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c
index 7a51e4da5c..8323873003 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c
@@ -46,14 +46,14 @@ int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) {
 }
 
 void ngtcp2_path_storage_init(ngtcp2_path_storage *ps,
-                              const struct sockaddr *local_addr,
-                              size_t local_addrlen,
-                              const struct sockaddr *remote_addr,
-                              size_t remote_addrlen, void *user_data) {
-  ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf,
+                              const ngtcp2_sockaddr *local_addr,
+                              ngtcp2_socklen local_addrlen,
+                              const ngtcp2_sockaddr *remote_addr,
+                              ngtcp2_socklen remote_addrlen, void *user_data) {
+  ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf,
                    0);
   ngtcp2_addr_init(&ps->path.remote,
-                   (const struct sockaddr *)&ps->remote_addrbuf, 0);
+                   (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0);
 
   ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen);
   ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen);
@@ -69,9 +69,9 @@ void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps,
 }
 
 void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) {
-  ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf,
+  ngtcp2_addr_init(&ps->path.local, (const ngtcp2_sockaddr *)&ps->local_addrbuf,
                    0);
   ngtcp2_addr_init(&ps->path.remote,
-                   (const struct sockaddr *)&ps->remote_addrbuf, 0);
+                   (const ngtcp2_sockaddr *)&ps->remote_addrbuf, 0);
   ps->path.user_data = NULL;
 }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c
index 2fb4e34ee0..2ec782b239 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c
@@ -26,7 +26,6 @@
 
 #include <assert.h>
 #include <string.h>
-#include <stdio.h>
 
 #include "ngtcp2_conv.h"
 #include "ngtcp2_str.h"
@@ -68,6 +67,7 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
   size_t len;
   uint32_t version;
   size_t dcidlen, scidlen;
+  int supported_version;
 
   assert(datalen);
 
@@ -95,22 +95,30 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
 
     version = ngtcp2_get_uint32(&data[1]);
 
-    if ((version == 0 || version == NGTCP2_PROTO_VER_V1 ||
-         (NGTCP2_PROTO_VER_DRAFT_MIN <= version &&
-          version <= NGTCP2_PROTO_VER_DRAFT_MAX)) &&
+    supported_version = ngtcp2_is_supported_version(version);
+
+    if (supported_version &&
         (dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) {
       return NGTCP2_ERR_INVALID_ARGUMENT;
     }
 
+    if (version && !supported_version &&
+        datalen < NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
+      return NGTCP2_ERR_INVALID_ARGUMENT;
+    }
+
     *pversion = version;
     *pdcid = &data[6];
     *pdcidlen = dcidlen;
     *pscid = &data[6 + dcidlen + 1];
     *pscidlen = scidlen;
 
-    if (version && version != NGTCP2_PROTO_VER_V1 &&
-        (version < NGTCP2_PROTO_VER_DRAFT_MIN ||
-         NGTCP2_PROTO_VER_DRAFT_MAX < version)) {
+    if (!version) {
+      /* VN */
+      return 0;
+    }
+
+    if (!supported_version) {
       return NGTCP2_ERR_VERSION_NEGOTIATION;
     }
     return 0;
@@ -184,6 +192,8 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
 
   if (version == 0) {
     type = NGTCP2_PKT_VERSION_NEGOTIATION;
+    /* Version Negotiation is not a long header packet. */
+    flags = NGTCP2_PKT_FLAG_NONE;
     /* This must be Version Negotiation packet which lacks packet
        number and payload length fields. */
     len = 5 + 2;
@@ -192,8 +202,10 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
       flags |= NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR;
     }
 
-    type = ngtcp2_pkt_get_type_long(pkt[0]);
+    type = ngtcp2_pkt_get_type_long(version, pkt[0]);
     switch (type) {
+    case 0:
+      return NGTCP2_ERR_INVALID_ARGUMENT;
     case NGTCP2_PKT_INITIAL:
       len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN -
             1; /* Cut packet number field */
@@ -268,10 +280,15 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
   }
 
   switch (type) {
-  case NGTCP2_PKT_VERSION_NEGOTIATION:
   case NGTCP2_PKT_RETRY:
     break;
   default:
+    if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) {
+      assert(type == NGTCP2_PKT_VERSION_NEGOTIATION);
+      /* Version Negotiation is not a long header packet. */
+      break;
+    }
+
     /* Length */
     n = ngtcp2_get_varint_len(p);
     len += n - 1;
@@ -298,11 +315,17 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
   p += ntokenlen + tokenlen;
 
   switch (type) {
-  case NGTCP2_PKT_VERSION_NEGOTIATION:
   case NGTCP2_PKT_RETRY:
     dest->len = 0;
     break;
   default:
+    if (!(flags & NGTCP2_PKT_FLAG_LONG_FORM)) {
+      assert(type == NGTCP2_PKT_VERSION_NEGOTIATION);
+      /* Version Negotiation is not a long header packet. */
+      dest->len = 0;
+      break;
+    }
+
     vi = ngtcp2_get_varint(&n, p);
     if (vi > SIZE_MAX) {
       return NGTCP2_ERR_INVALID_ARGUMENT;
@@ -338,7 +361,7 @@ ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
 
   p = &pkt[1];
 
-  dest->type = NGTCP2_PKT_SHORT;
+  dest->type = NGTCP2_PKT_1RTT;
 
   ngtcp2_cid_init(&dest->dcid, p, dcidlen);
   p += dcidlen;
@@ -366,7 +389,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
                      len and 1 byte for packet number. */
 
   if (hd->type != NGTCP2_PKT_RETRY) {
-    len += 2 /* Length */ + hd->pkt_numlen;
+    len += NGTCP2_PKT_LENGTHLEN /* Length */ + hd->pkt_numlen;
   }
 
   if (hd->type == NGTCP2_PKT_INITIAL) {
@@ -379,7 +402,8 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
 
   p = out;
 
-  *p = (uint8_t)(NGTCP2_HEADER_FORM_BIT | (hd->type << 4) |
+  *p = (uint8_t)(NGTCP2_HEADER_FORM_BIT |
+                 (ngtcp2_pkt_versioned_type(hd->version, hd->type) << 4) |
                  (uint8_t)(hd->pkt_numlen - 1));
   if (!(hd->flags & NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR)) {
     *p |= NGTCP2_FIXED_BIT_MASK;
@@ -405,7 +429,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen,
   }
 
   if (hd->type != NGTCP2_PKT_RETRY) {
-    p = ngtcp2_put_varint14(p, (uint16_t)hd->len);
+    p = ngtcp2_put_varint30(p, (uint32_t)hd->len);
     p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen);
   }
 
@@ -2259,10 +2283,16 @@ ngtcp2_ssize ngtcp2_pkt_write_retry(
     return pseudo_retrylen;
   }
 
-  if (version == NGTCP2_PROTO_VER_V1) {
+  switch (version) {
+  case NGTCP2_PROTO_VER_V1:
     nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
     noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
-  } else {
+    break;
+  case NGTCP2_PROTO_VER_V2_DRAFT:
+    nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT;
+    noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
+    break;
+  default:
     nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT;
     noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
   }
@@ -2345,10 +2375,16 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
 
   pseudo_retrylen = (size_t)(p - pseudo_retry);
 
-  if (version == NGTCP2_PROTO_VER_V1) {
+  switch (version) {
+  case NGTCP2_PROTO_VER_V1:
     nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1;
     noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
-  } else {
+    break;
+  case NGTCP2_PROTO_VER_V2_DRAFT:
+    nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V2_DRAFT;
+    noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
+    break;
+  default:
     nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT;
     noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
   }
@@ -2435,8 +2471,98 @@ size_t ngtcp2_pkt_datagram_framelen(size_t len) {
   return 1 /* type */ + ngtcp2_put_varint_len(len) + len;
 }
 
-uint8_t ngtcp2_pkt_get_type_long(uint8_t c) {
-  return (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4);
+int ngtcp2_is_supported_version(uint32_t version) {
+  switch (version) {
+  case NGTCP2_PROTO_VER_V1:
+  case NGTCP2_PROTO_VER_V2_DRAFT:
+    return 1;
+  default:
+    return NGTCP2_PROTO_VER_DRAFT_MIN <= version &&
+           version <= NGTCP2_PROTO_VER_DRAFT_MAX;
+  }
+}
+
+int ngtcp2_is_reserved_version(uint32_t version) {
+  return (version & NGTCP2_RESERVED_VERSION_MASK) ==
+         NGTCP2_RESERVED_VERSION_MASK;
+}
+
+uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c) {
+  uint8_t pkt_type = (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4);
+
+  switch (version) {
+  case NGTCP2_PROTO_VER_V2_DRAFT:
+    switch (pkt_type) {
+    case NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT:
+      return NGTCP2_PKT_INITIAL;
+    case NGTCP2_PKT_TYPE_0RTT_V2_DRAFT:
+      return NGTCP2_PKT_0RTT;
+    case NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT:
+      return NGTCP2_PKT_HANDSHAKE;
+    case NGTCP2_PKT_TYPE_RETRY_V2_DRAFT:
+      return NGTCP2_PKT_RETRY;
+    default:
+      return 0;
+    }
+  default:
+    if (!ngtcp2_is_supported_version(version)) {
+      return 0;
+    }
+
+    /* QUIC v1 and draft versions share the same numeric packet
+       types. */
+    switch (pkt_type) {
+    case NGTCP2_PKT_TYPE_INITIAL_V1:
+      return NGTCP2_PKT_INITIAL;
+    case NGTCP2_PKT_TYPE_0RTT_V1:
+      return NGTCP2_PKT_0RTT;
+    case NGTCP2_PKT_TYPE_HANDSHAKE_V1:
+      return NGTCP2_PKT_HANDSHAKE;
+    case NGTCP2_PKT_TYPE_RETRY_V1:
+      return NGTCP2_PKT_RETRY;
+    default:
+      return 0;
+    }
+  }
+}
+
+uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type) {
+  switch (version) {
+  case NGTCP2_PROTO_VER_V2_DRAFT:
+    switch (pkt_type) {
+    case NGTCP2_PKT_INITIAL:
+      return NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT;
+    case NGTCP2_PKT_0RTT:
+      return NGTCP2_PKT_TYPE_0RTT_V2_DRAFT;
+    case NGTCP2_PKT_HANDSHAKE:
+      return NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT;
+    case NGTCP2_PKT_RETRY:
+      return NGTCP2_PKT_TYPE_RETRY_V2_DRAFT;
+    default:
+      assert(0);
+      abort();
+    }
+  default:
+    /* Assume that unsupported versions share the numeric long packet
+       types with QUIC v1 in order to send a packet to elicit Version
+       Negotiation packet. */
+
+    /* QUIC v1 and draft versions share the same numeric packet
+       types. */
+    switch (pkt_type) {
+    case NGTCP2_PKT_INITIAL:
+      return NGTCP2_PKT_TYPE_INITIAL_V1;
+    case NGTCP2_PKT_0RTT:
+      return NGTCP2_PKT_TYPE_0RTT_V1;
+    case NGTCP2_PKT_HANDSHAKE:
+      return NGTCP2_PKT_TYPE_HANDSHAKE_V1;
+    case NGTCP2_PKT_RETRY:
+      return NGTCP2_PKT_TYPE_RETRY_V1;
+    default:
+      assert(0);
+      abort();
+    }
+  }
 }
 
 int ngtcp2_pkt_verify_reserved_bits(uint8_t c) {
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h
index 021f334591..2f7838a08a 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h
@@ -103,6 +103,40 @@
 /* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */
 #define NGTCP2_RETRY_TAGLEN 16
 
+/* NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE is the maximum UDP payload size
+   that this library can write. */
+#define NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE ((1 << 24) - 1)
+
+/* NGTCP2_PKT_LENGTHLEN is the number of bytes that is occupied by
+   Length field in Long packet header. */
+#define NGTCP2_PKT_LENGTHLEN 4
+
+/* NGTCP2_PKT_TYPE_INITIAL_V1 is Initial long header packet type for
+   QUIC v1. */
+#define NGTCP2_PKT_TYPE_INITIAL_V1 0x0
+/* NGTCP2_PKT_TYPE_0RTT_V1 is 0RTT long header packet type for QUIC
+   v1. */
+#define NGTCP2_PKT_TYPE_0RTT_V1 0x1
+/* NGTCP2_PKT_TYPE_HANDSHAKE_V1 is Handshake long header packet type
+   for QUIC v1. */
+#define NGTCP2_PKT_TYPE_HANDSHAKE_V1 0x2
+/* NGTCP2_PKT_TYPE_RETRY_V1 is Retry long header packet type for QUIC
+   v1. */
+#define NGTCP2_PKT_TYPE_RETRY_V1 0x3
+
+/* NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT is Initial long header packet type
+   for QUIC v2 draft. */
+#define NGTCP2_PKT_TYPE_INITIAL_V2_DRAFT 0x1
+/* NGTCP2_PKT_TYPE_0RTT_V2_DRAFT is 0RTT long header packet type for
+   QUIC v2 draft. */
+#define NGTCP2_PKT_TYPE_0RTT_V2_DRAFT 0x2
+/* NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT is Handshake long header packet
+   type for QUIC v2 draft. */
+#define NGTCP2_PKT_TYPE_HANDSHAKE_V2_DRAFT 0x3
+/* NGTCP2_PKT_TYPE_RETRY_V2_DRAFT is Retry long header packet type for
+   QUIC v2 draft. */
+#define NGTCP2_PKT_TYPE_RETRY_V2_DRAFT 0x0
+
 typedef struct ngtcp2_pkt_retry {
   ngtcp2_cid odcid;
   ngtcp2_vec token;
@@ -1186,12 +1220,21 @@ int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry,
                                 const ngtcp2_crypto_aead *aead,
                                 const ngtcp2_crypto_aead_ctx *aead_ctx);
 
+/*
+ * ngtcp2_pkt_versioned_type returns versioned packet type for a
+ * version |version| that corresponds to the version-independent
+ * |pkt_type|.
+ */
+uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type);
+
 /**
  * @function
  *
- * `ngtcp2_pkt_get_type_long` returns the long packet type.  |c| is
- * the first byte of Long packet header.
+ * `ngtcp2_pkt_get_type_long` returns the version-independent long
+ * packet type.  |version| is the QUIC version.  |c| is the first byte
+ * of Long packet header.  If |version| is not supported by the
+ * library, it returns 0.
  */
-uint8_t ngtcp2_pkt_get_type_long(uint8_t c);
+uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c);
 
 #endif /* NGTCP2_PKT_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c
new file mode 100644
index 0000000000..26318bb1c8
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.c
@@ -0,0 +1,160 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_pmtud.h"
+
+#include <assert.h>
+
+#include "ngtcp2_mem.h"
+#include "ngtcp2_macro.h"
+
+/* NGTCP2_PMTUD_PROBE_NUM_MAX is the maximum number of packets sent
+   for each probe. */
+#define NGTCP2_PMTUD_PROBE_NUM_MAX 3
+
+static size_t mtu_probes[] = {
+    1454 - 48, /* The well known MTU used by a domestic optic fiber
+                  service in Japan. */
+    1390 - 48, /* Typical Tunneled MTU */
+    1280 - 48, /* IPv6 minimum MTU */
+    1492 - 48, /* PPPoE */
+};
+
+static size_t mtu_probeslen = sizeof(mtu_probes) / sizeof(mtu_probes[0]);
+
+int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size,
+                     size_t hard_max_udp_payload_size, int64_t tx_pkt_num,
+                     const ngtcp2_mem *mem) {
+  ngtcp2_pmtud *pmtud = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pmtud));
+
+  if (pmtud == NULL) {
+    return NGTCP2_ERR_NOMEM;
+  }
+
+  pmtud->mem = mem;
+  pmtud->mtu_idx = 0;
+  pmtud->num_pkts_sent = 0;
+  pmtud->expiry = UINT64_MAX;
+  pmtud->tx_pkt_num = tx_pkt_num;
+  pmtud->max_udp_payload_size = max_udp_payload_size;
+  pmtud->hard_max_udp_payload_size = hard_max_udp_payload_size;
+  pmtud->min_fail_udp_payload_size = SIZE_MAX;
+
+  for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) {
+    if (mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) {
+      continue;
+    }
+    if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) {
+      break;
+    }
+  }
+
+  *ppmtud = pmtud;
+
+  return 0;
+}
+
+void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud) {
+  if (!pmtud) {
+    return;
+  }
+
+  ngtcp2_mem_free(pmtud->mem, pmtud);
+}
+
+size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud) {
+  assert(pmtud->mtu_idx < mtu_probeslen);
+
+  return mtu_probes[pmtud->mtu_idx];
+}
+
+void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto,
+                             ngtcp2_tstamp ts) {
+  ngtcp2_tstamp timeout;
+
+  if (++pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) {
+    timeout = pto;
+  } else {
+    timeout = 3 * pto;
+  }
+
+  pmtud->expiry = ts + timeout;
+}
+
+int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud) {
+  return pmtud->expiry == UINT64_MAX;
+}
+
+static void pmtud_next_probe(ngtcp2_pmtud *pmtud) {
+  assert(pmtud->mtu_idx < mtu_probeslen);
+
+  ++pmtud->mtu_idx;
+  pmtud->num_pkts_sent = 0;
+  pmtud->expiry = UINT64_MAX;
+
+  for (; pmtud->mtu_idx < mtu_probeslen; ++pmtud->mtu_idx) {
+    if (mtu_probes[pmtud->mtu_idx] <= pmtud->max_udp_payload_size ||
+        mtu_probes[pmtud->mtu_idx] > pmtud->hard_max_udp_payload_size) {
+      continue;
+    }
+
+    if (mtu_probes[pmtud->mtu_idx] < pmtud->min_fail_udp_payload_size) {
+      break;
+    }
+  }
+}
+
+void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen) {
+  pmtud->max_udp_payload_size =
+      ngtcp2_max(pmtud->max_udp_payload_size, payloadlen);
+
+  assert(pmtud->mtu_idx < mtu_probeslen);
+
+  if (mtu_probes[pmtud->mtu_idx] > pmtud->max_udp_payload_size) {
+    return;
+  }
+
+  pmtud_next_probe(pmtud);
+}
+
+void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts) {
+  if (ts < pmtud->expiry) {
+    return;
+  }
+
+  pmtud->expiry = UINT64_MAX;
+
+  if (pmtud->num_pkts_sent < NGTCP2_PMTUD_PROBE_NUM_MAX) {
+    return;
+  }
+
+  pmtud->min_fail_udp_payload_size =
+      ngtcp2_min(pmtud->min_fail_udp_payload_size, mtu_probes[pmtud->mtu_idx]);
+
+  pmtud_next_probe(pmtud);
+}
+
+int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud) {
+  return pmtud->mtu_idx >= mtu_probeslen;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h
new file mode 100644
index 0000000000..6b2e691cfc
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pmtud.h
@@ -0,0 +1,123 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_PMTUD_H
+#define NGTCP2_PMTUD_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+typedef struct ngtcp2_pmtud {
+  const ngtcp2_mem *mem;
+  /* mtu_idx is the index of UDP payload size candidates to try
+     out. */
+  size_t mtu_idx;
+  /* num_pkts_sent is the number of mtu_idx sized UDP datagram payload
+     sent */
+  size_t num_pkts_sent;
+  /* expiry is the expired, if it is reached, send out the next UDP
+     datagram.  UINT64_MAX means no expiry, or expiration is canceled.
+     In either case, new probe packet should be sent unless we have
+     done all attempts. */
+  ngtcp2_tstamp expiry;
+  /* tx_pkt_num is the smallest outgoing packet number where the
+     current discovery is performed.  In other words, acknowledging
+     packet whose packet number lower than that does not indicate the
+     success of Path MTU Discovery. */
+  int64_t tx_pkt_num;
+  /* max_udp_payload_size is the maximum UDP payload size which is
+     known to work. */
+  size_t max_udp_payload_size;
+  /* hard_max_udp_payload_size is the maximum UDP payload size that is
+     going to be probed. */
+  size_t hard_max_udp_payload_size;
+  /* min_fail_udp_payload_size is the minimum UDP payload size that is
+     known to fail. */
+  size_t min_fail_udp_payload_size;
+} ngtcp2_pmtud;
+
+/*
+ * ngtcp2_pmtud_new creates new ngtcp2_pmtud object, and assigns its
+ * pointer to |*ppmtud|.  |max_udp_payload_size| is the maximum UDP
+ * payload size that is known to work for the current path.
+ * |tx_pkt_num| should be the next packet number to send, which is
+ * used to differentiate the PMTUD probe packet sent by the previous
+ * PMTUD.  PMTUD might finish immediately if |max_udp_payload_size| is
+ * larger than or equal to all UDP payload probe candidates.
+ * Therefore, call ngtcp2_pmtud_finished to check this situation.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * NGTCP2_ERR_NOMEM
+ *     Out of memory.
+ */
+int ngtcp2_pmtud_new(ngtcp2_pmtud **ppmtud, size_t max_udp_payload_size,
+                     size_t hard_max_udp_payload_size, int64_t tx_pkt_num,
+                     const ngtcp2_mem *mem);
+
+/*
+ * ngtcp2_pmtud_del deletes |pmtud|.
+ */
+void ngtcp2_pmtud_del(ngtcp2_pmtud *pmtud);
+
+/*
+ * ngtcp2_pmtud_probelen returns the length of UDP payload size for a
+ * PMTUD probe packet.
+ */
+size_t ngtcp2_pmtud_probelen(ngtcp2_pmtud *pmtud);
+
+/*
+ * ngtcp2_pmtud_probe_sent should be invoked when a PMTUD probe packet is
+ * sent.
+ */
+void ngtcp2_pmtud_probe_sent(ngtcp2_pmtud *pmtud, ngtcp2_duration pto,
+                             ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pmtud_require_probe returns nonzero if a PMTUD probe packet
+ * should be sent.
+ */
+int ngtcp2_pmtud_require_probe(ngtcp2_pmtud *pmtud);
+
+/*
+ * ngtcp2_pmtud_probe_success should be invoked when a PMTUD probe
+ * UDP datagram sized |payloadlen| is acknowledged.
+ */
+void ngtcp2_pmtud_probe_success(ngtcp2_pmtud *pmtud, size_t payloadlen);
+
+/*
+ * ngtcp2_pmtud_handle_expiry handles expiry.
+ */
+void ngtcp2_pmtud_handle_expiry(ngtcp2_pmtud *pmtud, ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_pmtud_finished returns nonzero if PMTUD has finished.
+ */
+int ngtcp2_pmtud_finished(ngtcp2_pmtud *pmtud);
+
+#endif /* NGTCP2_PMTUD_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c
index 47f4f10a29..5376246bd4 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ppe.c
@@ -57,7 +57,7 @@ int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) {
     if (hd->type == NGTCP2_PKT_INITIAL) {
       ppe->len_offset += ngtcp2_put_varint_len(hd->token.len) + hd->token.len;
     }
-    ppe->pkt_num_offset = ppe->len_offset + 2;
+    ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN;
     rv = ngtcp2_pkt_encode_hd_long(
         buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd);
   } else {
@@ -115,7 +115,7 @@ ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) {
   assert(cc->hp_mask);
 
   if (ppe->len_offset) {
-    ngtcp2_put_varint14(
+    ngtcp2_put_varint30(
         buf->begin + ppe->len_offset,
         (uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead.max_overhead));
   }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c
index d5f7759d6c..314e005293 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.c
@@ -42,19 +42,12 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data,
 int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid,
                   ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log,
                   const ngtcp2_mem *mem) {
-  int rv;
-
   (*ppv) = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pv));
   if (*ppv == NULL) {
     return NGTCP2_ERR_NOMEM;
   }
 
-  rv = ngtcp2_ringbuf_init(&(*ppv)->ents, NGTCP2_PV_MAX_ENTRIES,
-                           sizeof(ngtcp2_pv_entry), mem);
-  if (rv != 0) {
-    ngtcp2_mem_free(mem, *ppv);
-    return 0;
-  }
+  ngtcp2_static_ringbuf_pv_ents_init(&(*ppv)->ents);
 
   ngtcp2_dcid_copy(&(*ppv)->dcid, dcid);
 
@@ -74,7 +67,6 @@ void ngtcp2_pv_del(ngtcp2_pv *pv) {
   if (pv == NULL) {
     return;
   }
-  ngtcp2_ringbuf_free(&pv->ents);
   ngtcp2_mem_free(pv->mem, pv);
 }
 
@@ -85,11 +77,11 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
 
   assert(pv->probe_pkt_left);
 
-  if (ngtcp2_ringbuf_len(&pv->ents) == 0) {
+  if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) {
     pv->started_ts = ts;
   }
 
-  ent = ngtcp2_ringbuf_push_back(&pv->ents);
+  ent = ngtcp2_ringbuf_push_back(&pv->ents.rb);
   ngtcp2_pv_entry_init(ent, data, expiry, flags);
 
   pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_CANCEL_TIMER;
@@ -97,7 +89,7 @@ void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data,
 }
 
 int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) {
-  size_t len = ngtcp2_ringbuf_len(&pv->ents);
+  size_t len = ngtcp2_ringbuf_len(&pv->ents.rb);
   size_t i;
   ngtcp2_pv_entry *ent;
 
@@ -106,7 +98,7 @@ int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) {
   }
 
   for (i = 0; i < len; ++i) {
-    ent = ngtcp2_ringbuf_get(&pv->ents, i);
+    ent = ngtcp2_ringbuf_get(&pv->ents.rb, i);
     if (memcmp(ent->data, data, sizeof(ent->data)) == 0) {
       *pflags = ent->flags;
       ngtcp2_log_info(pv->log, NGTCP2_LOG_EVENT_PTV, "path has been validated");
@@ -120,11 +112,11 @@ int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) {
 void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts) {
   ngtcp2_pv_entry *ent;
 
-  if (ngtcp2_ringbuf_len(&pv->ents) == 0) {
+  if (ngtcp2_ringbuf_len(&pv->ents.rb) == 0) {
     return;
   }
 
-  ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1);
+  ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1);
 
   if (ent->expiry > ts) {
     return;
@@ -146,9 +138,9 @@ int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) {
     return 0;
   }
 
-  assert(ngtcp2_ringbuf_len(&pv->ents));
+  assert(ngtcp2_ringbuf_len(&pv->ents.rb));
 
-  ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1);
+  ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1);
 
   t = pv->started_ts + pv->timeout;
   t = ngtcp2_max(t, ent->expiry);
@@ -160,11 +152,11 @@ ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv) {
   ngtcp2_pv_entry *ent;
 
   if ((pv->flags & NGTCP2_PV_FLAG_CANCEL_TIMER) ||
-      ngtcp2_ringbuf_len(&pv->ents) == 0) {
+      ngtcp2_ringbuf_len(&pv->ents.rb) == 0) {
     return UINT64_MAX;
   }
 
-  ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1);
+  ent = ngtcp2_ringbuf_get(&pv->ents.rb, ngtcp2_ringbuf_len(&pv->ents.rb) - 1);
 
   return ent->expiry;
 }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h
index effbc4daee..4c31562847 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pv.h
@@ -86,6 +86,8 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data,
 
 typedef struct ngtcp2_pv ngtcp2_pv;
 
+ngtcp2_static_ringbuf_def(pv_ents, NGTCP2_PV_MAX_ENTRIES,
+                          sizeof(ngtcp2_pv_entry));
 /*
  * ngtcp2_pv is the context of a single path validation.
  */
@@ -98,7 +100,7 @@ struct ngtcp2_pv {
      fallback if this path validation fails. */
   ngtcp2_dcid fallback_dcid;
   /* ents is the ring buffer of ngtcp2_pv_entry */
-  ngtcp2_ringbuf ents;
+  ngtcp2_static_ringbuf_pv_ents ents;
   /* timeout is the duration within which this path validation should
      succeed. */
   ngtcp2_duration timeout;
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c
index 5bfe62d60c..69eaeb7367 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c
@@ -28,6 +28,8 @@
 
 #include "ngtcp2_str.h"
 #include "ngtcp2_vec.h"
+#include "ngtcp2_conv.h"
+#include "ngtcp2_net.h"
 
 void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write,
                       ngtcp2_tstamp ts, void *user_data) {
@@ -238,6 +240,10 @@ static ngtcp2_vec vec_pkt_type_handshake = ngtcp2_make_vec_lit("handshake");
 static ngtcp2_vec vec_pkt_type_0rtt = ngtcp2_make_vec_lit("0RTT");
 static ngtcp2_vec vec_pkt_type_1rtt = ngtcp2_make_vec_lit("1RTT");
 static ngtcp2_vec vec_pkt_type_retry = ngtcp2_make_vec_lit("retry");
+static ngtcp2_vec vec_pkt_type_version_negotiation =
+    ngtcp2_make_vec_lit("version_negotiation");
+static ngtcp2_vec vec_pkt_type_stateless_reset =
+    ngtcp2_make_vec_lit("stateless_reset");
 static ngtcp2_vec vec_pkt_type_unknown = ngtcp2_make_vec_lit("unknown");
 
 static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) {
@@ -256,19 +262,33 @@ static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) {
     }
   }
 
-  return &vec_pkt_type_1rtt;
+  switch (hd->type) {
+  case NGTCP2_PKT_VERSION_NEGOTIATION:
+    return &vec_pkt_type_version_negotiation;
+  case NGTCP2_PKT_STATELESS_RESET:
+    return &vec_pkt_type_stateless_reset;
+  case NGTCP2_PKT_1RTT:
+    return &vec_pkt_type_1rtt;
+  default:
+    return &vec_pkt_type_unknown;
+  }
 }
 
 static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd) {
   /*
-   * {"packet_type":"version_negotiation","packet_number":"0000000000000000000"}
+   * {"packet_type":"version_negotiation","packet_number":"0000000000000000000","token":{"data":""}}
    */
-#define NGTCP2_QLOG_PKT_HD_OVERHEAD 75
+#define NGTCP2_QLOG_PKT_HD_OVERHEAD 95
 
   *p++ = '{';
   p = write_pair(p, "packet_type", qlog_pkt_type(hd));
   *p++ = ',';
   p = write_pair_number(p, "packet_number", (uint64_t)hd->pkt_num);
+  if (hd->type == NGTCP2_PKT_INITIAL && hd->token.len) {
+    p = write_verbatim(p, ",\"token\":{");
+    p = write_pair_hex(p, "data", hd->token.base, hd->token.len);
+    *p++ = '}';
+  }
   /* TODO Write DCIL and DCID */
   /* TODO Write SCIL and SCID */
   *p++ = '}';
@@ -313,10 +333,7 @@ static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) {
 
   p = write_verbatim(p, "{\"frame_type\":\"ack\",");
   p = write_pair_duration(p, "ack_delay", fr->ack_delay_unscaled);
-  *p++ = ',';
-  p = write_string(p, "acked_ranges");
-  *p++ = ':';
-  *p++ = '[';
+  p = write_verbatim(p, ",\"acked_ranges\":[");
 
   largest_ack = fr->largest_ack;
   min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen;
@@ -410,14 +427,15 @@ static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_crypto *fr) {
 
 static uint8_t *write_new_token_frame(uint8_t *p, const ngtcp2_new_token *fr) {
   /*
-   * {"frame_type":"new_token","length":0000000000000000000,"token":""}
+   * {"frame_type":"new_token","length":0000000000000000000,"token":{"data":""}}
    */
-#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 66
+#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 75
 
   p = write_verbatim(p, "{\"frame_type\":\"new_token\",");
   p = write_pair_number(p, "length", fr->token.len);
-  *p++ = ',';
-  p = write_pair_hex(p, "token", fr->token.base, fr->token.len);
+  p = write_verbatim(p, ",\"token\":{");
+  p = write_pair_hex(p, "data", fr->token.base, fr->token.len);
+  *p++ = '}';
   *p++ = '}';
 
   return p;
@@ -480,9 +498,7 @@ static uint8_t *write_max_streams_frame(uint8_t *p,
    */
 #define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 89
 
-  p = write_verbatim(p, "{\"frame_type\":\"max_streams\",");
-  p = write_string(p, "stream_type");
-  *p++ = ':';
+  p = write_verbatim(p, "{\"frame_type\":\"max_streams\",\"stream_type\":");
   if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) {
     p = write_string(p, "bidirectional");
   } else {
@@ -541,9 +557,9 @@ static uint8_t *write_streams_blocked_frame(uint8_t *p,
 static uint8_t *
 write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) {
   /*
-   * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
+   * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":{"data":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}}
    */
-#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 271
+#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 280
 
   p = write_verbatim(p, "{\"frame_type\":\"new_connection_id\",");
   p = write_pair_number(p, "sequence_number", fr->seq);
@@ -553,10 +569,11 @@ write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) {
   p = write_pair_number(p, "connection_id_length", fr->cid.datalen);
   *p++ = ',';
   p = write_pair_cid(p, "connection_id", &fr->cid);
-  *p++ = ',';
-  p = write_pair_hex(p, "stateless_reset_token", fr->stateless_reset_token,
+  p = write_verbatim(p, ",\"stateless_reset_token\":{");
+  p = write_pair_hex(p, "data", fr->stateless_reset_token,
                      sizeof(fr->stateless_reset_token));
   *p++ = '}';
+  *p++ = '}';
 
   return p;
 }
@@ -611,9 +628,8 @@ write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) {
    */
 #define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 131
 
-  p = write_verbatim(p, "{\"frame_type\":\"connection_close\",");
-  p = write_string(p, "error_space");
-  *p++ = ':';
+  p = write_verbatim(p,
+                     "{\"frame_type\":\"connection_close\",\"error_space\":");
   if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) {
     p = write_string(p, "transport");
   } else {
@@ -698,7 +714,11 @@ static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
 #define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD                                     \
   (1 + 50 + NGTCP2_QLOG_PKT_HD_OVERHEAD)
 
-  assert(ngtcp2_buf_left(&qlog->buf) >= NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD);
+  if (ngtcp2_buf_left(&qlog->buf) <
+      NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD + hd->token.len * 2) {
+    return;
+  }
+
   assert(ngtcp2_buf_len(&qlog->buf));
 
   /* Eat last ',' */
@@ -727,15 +747,13 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
 
   switch (fr->type) {
   case NGTCP2_FRAME_PADDING:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_padding_frame(p, &fr->padding);
     break;
   case NGTCP2_FRAME_PING:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_ping_frame(p, &fr->ping);
@@ -747,86 +765,75 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
             (size_t)(fr->type == NGTCP2_FRAME_ACK_ECN
                          ? NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD
                          : 0) +
-            NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1 +
-            NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+            NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1) {
       return;
     }
     p = write_ack_frame(p, &fr->ack);
     break;
   case NGTCP2_FRAME_RESET_STREAM:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD +
-                                          1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) <
+        NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_reset_stream_frame(p, &fr->reset_stream);
     break;
   case NGTCP2_FRAME_STOP_SENDING:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD +
-                                          1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) <
+        NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_stop_sending_frame(p, &fr->stop_sending);
     break;
   case NGTCP2_FRAME_CRYPTO:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_crypto_frame(p, &fr->crypto);
     break;
   case NGTCP2_FRAME_NEW_TOKEN:
     if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD +
-                                          fr->new_token.token.len * 2 + 1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+                                          fr->new_token.token.len * 2 + 1) {
       return;
     }
     p = write_new_token_frame(p, &fr->new_token);
     break;
   case NGTCP2_FRAME_STREAM:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_stream_frame(p, &fr->stream);
     break;
   case NGTCP2_FRAME_MAX_DATA:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_max_data_frame(p, &fr->max_data);
     break;
   case NGTCP2_FRAME_MAX_STREAM_DATA:
     if (ngtcp2_buf_left(&qlog->buf) <
-        NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1 +
-            NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+        NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_max_stream_data_frame(p, &fr->max_stream_data);
     break;
   case NGTCP2_FRAME_MAX_STREAMS_BIDI:
   case NGTCP2_FRAME_MAX_STREAMS_UNI:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD +
-                                          1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) <
+        NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_max_streams_frame(p, &fr->max_streams);
     break;
   case NGTCP2_FRAME_DATA_BLOCKED:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD +
-                                          1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) <
+        NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_data_blocked_frame(p, &fr->data_blocked);
     break;
   case NGTCP2_FRAME_STREAM_DATA_BLOCKED:
     if (ngtcp2_buf_left(&qlog->buf) <
-        NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1 +
-            NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+        NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_stream_data_blocked_frame(p, &fr->stream_data_blocked);
@@ -834,40 +841,35 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
   case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI:
   case NGTCP2_FRAME_STREAMS_BLOCKED_UNI:
     if (ngtcp2_buf_left(&qlog->buf) <
-        NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1 +
-            NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+        NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_streams_blocked_frame(p, &fr->streams_blocked);
     break;
   case NGTCP2_FRAME_NEW_CONNECTION_ID:
     if (ngtcp2_buf_left(&qlog->buf) <
-        NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1 +
-            NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+        NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_new_connection_id_frame(p, &fr->new_connection_id);
     break;
   case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
     if (ngtcp2_buf_left(&qlog->buf) <
-        NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1 +
-            NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+        NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_retire_connection_id_frame(p, &fr->retire_connection_id);
     break;
   case NGTCP2_FRAME_PATH_CHALLENGE:
     if (ngtcp2_buf_left(&qlog->buf) <
-        NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1 +
-            NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+        NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_path_challenge_frame(p, &fr->path_challenge);
     break;
   case NGTCP2_FRAME_PATH_RESPONSE:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD +
-                                          1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) <
+        NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_path_response_frame(p, &fr->path_response);
@@ -875,24 +877,21 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
   case NGTCP2_FRAME_CONNECTION_CLOSE:
   case NGTCP2_FRAME_CONNECTION_CLOSE_APP:
     if (ngtcp2_buf_left(&qlog->buf) <
-        NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1 +
-            NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+        NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_connection_close_frame(p, &fr->connection_close);
     break;
   case NGTCP2_FRAME_HANDSHAKE_DONE:
     if (ngtcp2_buf_left(&qlog->buf) <
-        NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1 +
-            NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+        NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_handshake_done_frame(p, &fr->handshake_done);
     break;
   case NGTCP2_FRAME_DATAGRAM:
   case NGTCP2_FRAME_DATAGRAM_LEN:
-    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1 +
-                                          NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) {
+    if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1) {
       return;
     }
     p = write_datagram_frame(p, &fr->datagram);
@@ -960,9 +959,10 @@ void ngtcp2_qlog_parameters_set_transport_params(
     *p++ = ',';
   }
   if (params->stateless_reset_token_present) {
-    p = write_pair_hex(p, "stateless_reset_token",
-                       params->stateless_reset_token,
+    p = write_verbatim(p, "\"stateless_reset_token\":{");
+    p = write_pair_hex(p, "data", params->stateless_reset_token,
                        sizeof(params->stateless_reset_token));
+    *p++ = '}';
     *p++ = ',';
   }
   p = write_pair_bool(p, "disable_active_migration",
@@ -1011,10 +1011,11 @@ void ngtcp2_qlog_parameters_set_transport_params(
     p = write_pair_number(p, "port_v6", paddr->ipv6_port);
     *p++ = ',';
     p = write_pair_cid(p, "connection_id", &paddr->cid);
-    *p++ = ',';
-    p = write_pair_hex(p, "stateless_reset_token", paddr->stateless_reset_token,
+    p = write_verbatim(p, ",\"stateless_reset_token\":{");
+    p = write_pair_hex(p, "data", paddr->stateless_reset_token,
                        sizeof(paddr->stateless_reset_token));
     *p++ = '}';
+    *p++ = '}';
   }
   *p++ = ',';
   p = write_pair_number(p, "max_datagram_frame_size",
@@ -1093,23 +1094,110 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) {
               (size_t)(p - buf));
 }
 
-void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog,
-                                    const ngtcp2_pkt_hd *hd) {
+void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+                                    const ngtcp2_pkt_retry *retry) {
+  uint8_t rawbuf[1024];
+  ngtcp2_buf buf;
+
+  if (!qlog->write) {
+    return;
+  }
+
+  ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf));
+
+  *buf.last++ = '\x1e';
+  *buf.last++ = '{';
+  buf.last = qlog_write_time(qlog, buf.last);
+  buf.last = write_verbatim(
+      buf.last,
+      ",\"name\":\"transport:packet_received\",\"data\":{\"header\":");
+
+  if (ngtcp2_buf_left(&buf) <
+      NGTCP2_QLOG_PKT_HD_OVERHEAD + hd->token.len * 2 +
+          sizeof(",\"retry_token\":{\"data\":\"\"}}}\n") - 1 +
+          retry->token.len * 2) {
+    return;
+  }
+
+  buf.last = write_pkt_hd(buf.last, hd);
+  buf.last = write_verbatim(buf.last, ",\"retry_token\":{");
+  buf.last =
+      write_pair_hex(buf.last, "data", retry->token.base, retry->token.len);
+  buf.last = write_verbatim(buf.last, "}}}\n");
+
+  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos,
+              ngtcp2_buf_len(&buf));
+}
+
+void ngtcp2_qlog_stateless_reset_pkt_received(
+    ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr) {
   uint8_t buf[256];
   uint8_t *p = buf;
+  ngtcp2_pkt_hd hd = {0};
 
   if (!qlog->write) {
     return;
   }
 
+  hd.type = NGTCP2_PKT_STATELESS_RESET;
+
   *p++ = '\x1e';
   *p++ = '{';
   p = qlog_write_time(qlog, p);
   p = write_verbatim(
       p, ",\"name\":\"transport:packet_received\",\"data\":{\"header\":");
-  p = write_pkt_hd(p, hd);
+  p = write_pkt_hd(p, &hd);
+  *p++ = ',';
+  p = write_pair_hex(p, "stateless_reset_token", sr->stateless_reset_token,
+                     NGTCP2_STATELESS_RESET_TOKENLEN);
   p = write_verbatim(p, "}}\n");
 
   qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf,
               (size_t)(p - buf));
 }
+
+void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog,
+                                                  const ngtcp2_pkt_hd *hd,
+                                                  const uint32_t *sv,
+                                                  size_t nsv) {
+  uint8_t rawbuf[512];
+  ngtcp2_buf buf;
+  size_t i;
+  uint32_t v;
+
+  if (!qlog->write) {
+    return;
+  }
+
+  ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf));
+
+  *buf.last++ = '\x1e';
+  *buf.last++ = '{';
+  buf.last = qlog_write_time(qlog, buf.last);
+  buf.last = write_verbatim(
+      buf.last,
+      ",\"name\":\"transport:packet_received\",\"data\":{\"header\":");
+  buf.last = write_pkt_hd(buf.last, hd);
+  buf.last = write_verbatim(buf.last, ",\"supported_versions\":[");
+
+  if (nsv) {
+    if (ngtcp2_buf_left(&buf) <
+        (sizeof("\"xxxxxxxx\",") - 1) * nsv - 1 + sizeof("]}}\n") - 1) {
+      return;
+    }
+
+    v = ngtcp2_htonl(sv[0]);
+    buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v));
+
+    for (i = 1; i < nsv; ++i) {
+      *buf.last++ = ',';
+      v = ngtcp2_htonl(sv[i]);
+      buf.last = write_hex(buf.last, (const uint8_t *)&v, sizeof(v));
+    }
+  }
+
+  buf.last = write_verbatim(buf.last, "]}}\n");
+
+  qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf.pos,
+              ngtcp2_buf_len(&buf));
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h
index cb3c2063f7..b9107c0e5c 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h
@@ -139,6 +139,23 @@ void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent);
  * ngtcp2_qlog_retry_pkt_received writes packet_received event for a
  * received Retry packet.
  */
-void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd);
+void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd,
+                                    const ngtcp2_pkt_retry *retry);
+
+/*
+ * ngtcp2_qlog_stateless_reset_pkt_received writes packet_received
+ * event for a received Stateless Reset packet.
+ */
+void ngtcp2_qlog_stateless_reset_pkt_received(
+    ngtcp2_qlog *qlog, const ngtcp2_pkt_stateless_reset *sr);
+
+/*
+ * ngtcp2_qlog_version_negotiation_pkt_received writes packet_received
+ * event for a received Version Negotiation packet.
+ */
+void ngtcp2_qlog_version_negotiation_pkt_received(ngtcp2_qlog *qlog,
+                                                  const ngtcp2_pkt_hd *hd,
+                                                  const uint32_t *sv,
+                                                  size_t nsv);
 
 #endif /* NGTCP2_QLOG_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
index e4deab1ff7..a6b3f73e73 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
@@ -43,24 +43,30 @@ unsigned int __popcnt(unsigned int x) {
 
 int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
                         const ngtcp2_mem *mem) {
+  uint8_t *buf = ngtcp2_mem_malloc(mem, nmemb * size);
+  if (buf == NULL) {
+    return NGTCP2_ERR_NOMEM;
+  }
+
+  ngtcp2_ringbuf_buf_init(rb, nmemb, size, buf, mem);
+
+  return 0;
+}
+
+void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
+                             uint8_t *buf, const ngtcp2_mem *mem) {
 #ifdef WIN32
   assert(1 == __popcnt((unsigned int)nmemb));
 #else
   assert(1 == __builtin_popcount((unsigned int)nmemb));
 #endif
 
-  rb->buf = ngtcp2_mem_malloc(mem, nmemb * size);
-  if (rb->buf == NULL) {
-    return NGTCP2_ERR_NOMEM;
-  }
-
+  rb->buf = buf;
   rb->mem = mem;
   rb->nmemb = nmemb;
   rb->size = size;
   rb->first = 0;
   rb->len = 0;
-
-  return 0;
 }
 
 void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb) {
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h
index c6e1421518..16635c9410 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h
@@ -62,6 +62,13 @@ typedef struct ngtcp2_ringbuf {
 int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
                         const ngtcp2_mem *mem);
 
+/*
+ * ngtcp2_ringbuf_buf_init initializes |rb| with given buffer and
+ * size.
+ */
+void ngtcp2_ringbuf_buf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
+                             uint8_t *buf, const ngtcp2_mem *mem);
+
 /*
  * ngtcp2_ringbuf_free frees resources allocated for |rb|.  This
  * function does not free the memory pointed by |rb|.
@@ -107,4 +114,19 @@ void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset);
 /* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */
 int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb);
 
+/* ngtcp2_static_ringbuf_def defines ngtcp2_ringbuf struct wrapper
+   which uses a statically allocated buffer that is suitable for a
+   usage that does not change buffer size with ngtcp2_ringbuf_resize.
+   ngtcp2_ringbuf_free should never be called for rb field. */
+#define ngtcp2_static_ringbuf_def(NAME, NMEMB, SIZE)                           \
+  typedef struct ngtcp2_static_ringbuf_##NAME {                                \
+    ngtcp2_ringbuf rb;                                                         \
+    uint8_t buf[(NMEMB) * (SIZE)];                                             \
+  } ngtcp2_static_ringbuf_##NAME;                                              \
+                                                                               \
+  static inline void ngtcp2_static_ringbuf_##NAME##_init(                      \
+      ngtcp2_static_ringbuf_##NAME *srb) {                                     \
+    ngtcp2_ringbuf_buf_init(&srb->rb, (NMEMB), (SIZE), srb->buf, NULL);        \
+  }
+
 #endif /* NGTCP2_RINGBUF_H */
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c
index 94028d148b..9c3d75dc33 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c
@@ -69,11 +69,8 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) {
   int rv;
   ngtcp2_rob_gap *g;
 
-  rv = ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar,
-                       sizeof(ngtcp2_range), mem);
-  if (rv != 0) {
-    goto fail_gapksl_ksl_init;
-  }
+  ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range),
+                  mem);
 
   rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem);
   if (rv != 0) {
@@ -85,23 +82,18 @@ int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) {
     goto fail_gapksl_ksl_insert;
   }
 
-  rv = ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar,
-                       sizeof(ngtcp2_range), mem);
-  if (rv != 0) {
-    goto fail_dataksl_ksl_init;
-  }
+  ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range),
+                  mem);
 
   rob->chunk = chunk;
   rob->mem = mem;
 
   return 0;
 
-fail_dataksl_ksl_init:
 fail_gapksl_ksl_insert:
   ngtcp2_rob_gap_del(g, mem);
 fail_rob_gap_new:
   ngtcp2_ksl_free(&rob->gapksl);
-fail_gapksl_ksl_init:
   return rv;
 }
 
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c
index 4c8af11322..76beb46921 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c
@@ -46,6 +46,18 @@ int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem) {
   return 0;
 }
 
+int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc,
+                                    ngtcp2_objalloc *objalloc) {
+  *pfrc = ngtcp2_objalloc_frame_chain_get(objalloc);
+  if (*pfrc == NULL) {
+    return NGTCP2_ERR_NOMEM;
+  }
+
+  ngtcp2_frame_chain_init(*pfrc);
+
+  return 0;
+}
+
 int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
                                     const ngtcp2_mem *mem) {
   *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain) + extralen);
@@ -58,9 +70,10 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
   return 0;
 }
 
-int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc,
-                                          size_t datacnt,
-                                          const ngtcp2_mem *mem) {
+int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+                                                   size_t datacnt,
+                                                   ngtcp2_objalloc *objalloc,
+                                                   const ngtcp2_mem *mem) {
   size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream);
 
   if (datacnt > 1) {
@@ -71,12 +84,13 @@ int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc,
     }
   }
 
-  return ngtcp2_frame_chain_new(pfrc, mem);
+  return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
 }
 
-int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc,
-                                          size_t datacnt,
-                                          const ngtcp2_mem *mem) {
+int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+                                                   size_t datacnt,
+                                                   ngtcp2_objalloc *objalloc,
+                                                   const ngtcp2_mem *mem) {
   size_t need, avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto);
 
   if (datacnt > 1) {
@@ -87,12 +101,13 @@ int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc,
     }
   }
 
-  return ngtcp2_frame_chain_new(pfrc, mem);
+  return ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
 }
 
-int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
-                                     const ngtcp2_vec *token,
-                                     const ngtcp2_mem *mem) {
+int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
+                                              const ngtcp2_vec *token,
+                                              ngtcp2_objalloc *objalloc,
+                                              const ngtcp2_mem *mem) {
   size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token);
   int rv;
   uint8_t *p;
@@ -101,7 +116,7 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
   if (token->len > avail) {
     rv = ngtcp2_frame_chain_extralen_new(pfrc, token->len - avail, mem);
   } else {
-    rv = ngtcp2_frame_chain_new(pfrc, mem);
+    rv = ngtcp2_frame_chain_objalloc_new(pfrc, objalloc);
   }
   if (rv != 0) {
     return rv;
@@ -133,19 +148,71 @@ void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) {
   ngtcp2_mem_free(mem, frc);
 }
 
+void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc,
+                                     ngtcp2_objalloc *objalloc,
+                                     const ngtcp2_mem *mem) {
+  ngtcp2_frame_chain_binder *binder;
+
+  if (frc == NULL) {
+    return;
+  }
+
+  switch (frc->fr.type) {
+  case NGTCP2_FRAME_STREAM:
+    if (frc->fr.stream.datacnt &&
+        sizeof(ngtcp2_vec) * (frc->fr.stream.datacnt - 1) >
+            sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream)) {
+      ngtcp2_frame_chain_del(frc, mem);
+
+      return;
+    }
+
+    break;
+  case NGTCP2_FRAME_CRYPTO:
+    if (frc->fr.crypto.datacnt &&
+        sizeof(ngtcp2_vec) * (frc->fr.crypto.datacnt - 1) >
+            sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto)) {
+      ngtcp2_frame_chain_del(frc, mem);
+
+      return;
+    }
+
+    break;
+  case NGTCP2_FRAME_NEW_TOKEN:
+    if (frc->fr.new_token.token.len >
+        sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token)) {
+      ngtcp2_frame_chain_del(frc, mem);
+
+      return;
+    }
+
+    break;
+  }
+
+  binder = frc->binder;
+  if (binder && --binder->refcount == 0) {
+    ngtcp2_mem_free(mem, binder);
+  }
+
+  frc->binder = NULL;
+
+  ngtcp2_objalloc_frame_chain_release(objalloc, frc);
+}
+
 void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) {
   frc->next = NULL;
   frc->binder = NULL;
 }
 
-void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc,
-                                 const ngtcp2_mem *mem) {
+void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc,
+                                          ngtcp2_objalloc *objalloc,
+                                          const ngtcp2_mem *mem) {
   ngtcp2_frame_chain *next;
 
-  for (; frc;) {
+  for (; frc; frc = next) {
     next = frc->next;
-    ngtcp2_frame_chain_del(frc, mem);
-    frc = next;
+
+    ngtcp2_frame_chain_objalloc_del(frc, objalloc, mem);
   }
 }
 
@@ -182,35 +249,46 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b,
   return 0;
 }
 
-int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd,
-                         ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
-                         size_t pktlen, uint8_t flags, const ngtcp2_mem *mem) {
-  (*pent) = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_rtb_entry));
+static void rtb_entry_init(ngtcp2_rtb_entry *ent, const ngtcp2_pkt_hd *hd,
+                           ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
+                           size_t pktlen, uint16_t flags) {
+  memset(ent, 0, sizeof(*ent));
+
+  ent->hd.pkt_num = hd->pkt_num;
+  ent->hd.type = hd->type;
+  ent->hd.flags = hd->flags;
+  ent->frc = frc;
+  ent->ts = ts;
+  ent->lost_ts = UINT64_MAX;
+  ent->pktlen = pktlen;
+  ent->flags = flags;
+  ent->next = NULL;
+}
+
+int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent,
+                                  const ngtcp2_pkt_hd *hd,
+                                  ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
+                                  size_t pktlen, uint16_t flags,
+                                  ngtcp2_objalloc *objalloc) {
+  *pent = ngtcp2_objalloc_rtb_entry_get(objalloc);
   if (*pent == NULL) {
     return NGTCP2_ERR_NOMEM;
   }
 
-  (*pent)->hd.pkt_num = hd->pkt_num;
-  (*pent)->hd.type = hd->type;
-  (*pent)->hd.flags = hd->flags;
-  (*pent)->frc = frc;
-  (*pent)->ts = ts;
-  (*pent)->lost_ts = UINT64_MAX;
-  (*pent)->pktlen = pktlen;
-  (*pent)->flags = flags;
-  (*pent)->next = NULL;
+  rtb_entry_init(*pent, hd, frc, ts, pktlen, flags);
 
   return 0;
 }
 
-void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem) {
-  if (ent == NULL) {
-    return;
-  }
+void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent,
+                                   ngtcp2_objalloc *objalloc,
+                                   ngtcp2_objalloc *frc_objalloc,
+                                   const ngtcp2_mem *mem) {
+  ngtcp2_frame_chain_list_objalloc_del(ent->frc, frc_objalloc, mem);
 
-  ngtcp2_frame_chain_list_del(ent->frc, mem);
+  ent->frc = NULL;
 
-  ngtcp2_mem_free(mem, ent);
+  ngtcp2_objalloc_rtb_entry_release(objalloc, ent);
 }
 
 static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
@@ -220,7 +298,10 @@ static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
 void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id,
                      ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc,
                      ngtcp2_log *log, ngtcp2_qlog *qlog,
-                     const ngtcp2_mem *mem) {
+                     ngtcp2_objalloc *rtb_entry_objalloc,
+                     ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
+  rtb->rtb_entry_objalloc = rtb_entry_objalloc;
+  rtb->frc_objalloc = frc_objalloc;
   ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem);
   rtb->crypto = crypto;
   rtb->rst = rst;
@@ -231,6 +312,7 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id,
   rtb->largest_acked_tx_pkt_num = -1;
   rtb->num_ack_eliciting = 0;
   rtb->num_retransmittable = 0;
+  rtb->num_pto_eliciting = 0;
   rtb->probe_pkt_left = 0;
   rtb->pktns_id = pktns_id;
   rtb->cc_pkt_num = 0;
@@ -249,7 +331,9 @@ void ngtcp2_rtb_free(ngtcp2_rtb *rtb) {
   it = ngtcp2_ksl_begin(&rtb->ents);
 
   for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
-    ngtcp2_rtb_entry_del(ngtcp2_ksl_it_get(&it), rtb->mem);
+    ngtcp2_rtb_entry_objalloc_del(ngtcp2_ksl_it_get(&it),
+                                  rtb->rtb_entry_objalloc, rtb->frc_objalloc,
+                                  rtb->mem);
   }
 
   ngtcp2_ksl_free(&rtb->ents);
@@ -272,6 +356,9 @@ static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
   if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) {
     ++rtb->num_retransmittable;
   }
+  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) {
+    ++rtb->num_pto_eliciting;
+  }
 }
 
 static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
@@ -293,6 +380,11 @@ static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
     --rtb->num_retransmittable;
   }
 
+  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) {
+    assert(rtb->num_pto_eliciting);
+    --rtb->num_pto_eliciting;
+  }
+
   if (rtb->cc_pkt_num <= ent->hd.pkt_num) {
     assert(cstat->bytes_in_flight >= ent->pktlen);
     cstat->bytes_in_flight -= ent->pktlen;
@@ -316,6 +408,7 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
   ngtcp2_range gap, range;
   size_t num_reclaimed = 0;
   int rv;
+  int streamfrq_empty;
 
   assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE);
 
@@ -354,8 +447,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
         }
       }
 
-      rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->stream.datacnt,
-                                                 rtb->mem);
+      rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+          &nfrc, fr->stream.datacnt, rtb->frc_objalloc, rtb->mem);
       if (rv != 0) {
         return rv;
       }
@@ -364,9 +457,10 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
       ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data,
                       fr->stream.datacnt);
 
+      streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm);
       rv = ngtcp2_strm_streamfrq_push(strm, nfrc);
       if (rv != 0) {
-        ngtcp2_frame_chain_del(nfrc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem);
         return rv;
       }
       if (!ngtcp2_strm_is_tx_queued(strm)) {
@@ -376,6 +470,9 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
           return rv;
         }
       }
+      if (streamfrq_empty) {
+        ++conn->tx.strmq_nretrans;
+      }
 
       ++num_reclaimed;
 
@@ -393,8 +490,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
         continue;
       }
 
-      rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->crypto.datacnt,
-                                                 rtb->mem);
+      rv = ngtcp2_frame_chain_crypto_datacnt_objalloc_new(
+          &nfrc, fr->crypto.datacnt, rtb->frc_objalloc, rtb->mem);
       if (rv != 0) {
         return rv;
       }
@@ -407,7 +504,7 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
                              &nfrc->fr.crypto.offset, nfrc);
       if (rv != 0) {
         assert(ngtcp2_err_is_fatal(rv));
-        ngtcp2_frame_chain_del(nfrc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(nfrc, rtb->frc_objalloc, rtb->mem);
         return rv;
       }
 
@@ -415,8 +512,8 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
 
       continue;
     case NGTCP2_FRAME_NEW_TOKEN:
-      rv = ngtcp2_frame_chain_new_token_new(&nfrc, &fr->new_token.token,
-                                            rtb->mem);
+      rv = ngtcp2_frame_chain_new_token_objalloc_new(
+          &nfrc, &fr->new_token.token, rtb->frc_objalloc, rtb->mem);
       if (rv != 0) {
         return rv;
       }
@@ -431,7 +528,7 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
     case NGTCP2_FRAME_DATAGRAM_LEN:
       continue;
     default:
-      rv = ngtcp2_frame_chain_new(&nfrc, rtb->mem);
+      rv = ngtcp2_frame_chain_objalloc_new(&nfrc, rtb->frc_objalloc);
       if (rv != 0) {
         return rv;
       }
@@ -508,42 +605,12 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
                     ts);
   }
 
-  if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE)) {
-    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
-      ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
-                      "pkn=%" PRId64 " has already been reclaimed on PTO",
-                      ent->hd.pkt_num);
-      assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
-      assert(UINT64_MAX == ent->lost_ts);
-
-      ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
-      ent->lost_ts = ts;
-
-      ++rtb->num_lost_pkts;
-
-      ngtcp2_ksl_it_next(it);
-
-      return 0;
-    }
-
-    if (conn->callbacks.lost_datagram &&
-        (ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM)) {
-      rv = conn_process_lost_datagram(conn, ent);
-      if (rv != 0) {
-        return rv;
-      }
-    }
-
-    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) {
-      assert(ent->frc);
-      assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
-      assert(UINT64_MAX == ent->lost_ts);
-
-      reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent);
-      if (reclaimed < 0) {
-        return (int)reclaimed;
-      }
-    }
+  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
+    ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+                    "pkn=%" PRId64 " has already been reclaimed on PTO",
+                    ent->hd.pkt_num);
+    assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
+    assert(UINT64_MAX == ent->lost_ts);
 
     ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
     ent->lost_ts = ts;
@@ -555,15 +622,31 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
     return 0;
   }
 
-  ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
-                  "pkn=%" PRId64
-                  " is a probe packet, no retransmission is necessary",
-                  ent->hd.pkt_num);
+  if (conn->callbacks.lost_datagram &&
+      (ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM)) {
+    rv = conn_process_lost_datagram(conn, ent);
+    if (rv != 0) {
+      return rv;
+    }
+  }
+
+  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) {
+    assert(ent->frc);
+    assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
+    assert(UINT64_MAX == ent->lost_ts);
 
-  rv = ngtcp2_ksl_remove_hint(&rtb->ents, it, it, &ent->hd.pkt_num);
-  assert(0 == rv);
+    reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent);
+    if (reclaimed < 0) {
+      return (int)reclaimed;
+    }
+  }
+
+  ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED;
+  ent->lost_ts = ts;
 
-  ngtcp2_rtb_entry_del(ent, rtb->mem);
+  ++rtb->num_lost_pkts;
+
+  ngtcp2_ksl_it_next(it);
 
   return 0;
 }
@@ -640,6 +723,18 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
   ngtcp2_strm *crypto = rtb->crypto;
   ngtcp2_pktns *pktns;
 
+  if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) && conn->pmtud &&
+      conn->pmtud->tx_pkt_num <= ent->hd.pkt_num) {
+    ngtcp2_pmtud_probe_success(conn->pmtud, ent->pktlen);
+
+    conn->dcid.current.max_udp_payload_size =
+        ngtcp2_max(conn->dcid.current.max_udp_payload_size, ent->pktlen);
+
+    if (ngtcp2_pmtud_finished(conn->pmtud)) {
+      ngtcp2_conn_stop_pmtud(conn);
+    }
+  }
+
   for (frc = ent->frc; frc; frc = frc->next) {
     if (frc->binder) {
       frc->binder->flags |= NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK;
@@ -730,8 +825,8 @@ static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
       }
       break;
     case NGTCP2_FRAME_RETIRE_CONNECTION_ID:
-      assert(conn->dcid.num_retire_queued);
-      --conn->dcid.num_retire_queued;
+      ngtcp2_conn_untrack_retired_dcid_seq(conn,
+                                           frc->fr.retire_connection_id.seq);
       break;
     case NGTCP2_FRAME_DATAGRAM:
     case NGTCP2_FRAME_DATAGRAM_LEN:
@@ -840,8 +935,10 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
   cc_ack.rtt = UINT64_MAX;
 
   if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) &&
+      (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) &&
       largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) {
-    conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED;
+    conn->flags &= (uint32_t) ~(NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED |
+                                NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR);
     conn->crypto.key_update.confirmed_ts = ts;
 
     ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed");
@@ -917,8 +1014,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
   if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) {
     cc_ack.rtt = pkt_ts - largest_pkt_sent_ts;
 
-    ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts);
-    if (cc->new_rtt_sample) {
+    rv = ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts);
+    if (rv == 0 && cc->new_rtt_sample) {
       cc->new_rtt_sample(cc, cstat, ts);
     }
   }
@@ -949,7 +1046,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
 
       rtb_on_pkt_acked(rtb, ent, cstat, ts);
       acked_ent = ent->next;
-      ngtcp2_rtb_entry_del(ent, rtb->mem);
+      ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+                                    rtb->frc_objalloc, rtb->mem);
     }
 
     if (verify_ecn) {
@@ -961,7 +1059,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
     for (ent = acked_ent; ent; ent = acked_ent) {
       rtb_on_pkt_acked(rtb, ent, cstat, ts);
       acked_ent = ent->next;
-      ngtcp2_rtb_entry_del(ent, rtb->mem);
+      ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+                                    rtb->frc_objalloc, rtb->mem);
     }
   }
 
@@ -989,7 +1088,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
 fail:
   for (ent = acked_ent; ent; ent = acked_ent) {
     acked_ent = ent->next;
-    ngtcp2_rtb_entry_del(ent, rtb->mem);
+    ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+                                  rtb->frc_objalloc, rtb->mem);
   }
 
   return rv;
@@ -1217,7 +1317,8 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) {
     --rtb->num_lost_pkts;
     rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
     assert(0 == rv);
-    ngtcp2_rtb_entry_del(ent, rtb->mem);
+    ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+                                  rtb->frc_objalloc, rtb->mem);
   }
 }
 
@@ -1251,7 +1352,8 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
     --rtb->num_lost_pkts;
     rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
     assert(0 == rv);
-    ngtcp2_rtb_entry_del(ent, rtb->mem);
+    ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+                                  rtb->frc_objalloc, rtb->mem);
 
     if (ngtcp2_ksl_len(&rtb->ents) == 0) {
       return;
@@ -1285,6 +1387,7 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
   ngtcp2_stream *sfr;
   ngtcp2_strm *strm;
   int rv;
+  int streamfrq_empty;
 
   ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
                       ent->ts);
@@ -1301,6 +1404,14 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
     return 0;
   }
 
+  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+    ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
+                    "pkn=%" PRId64
+                    " is a PMTUD probe packet, no retransmission is necessary",
+                    ent->hd.pkt_num);
+    return 0;
+  }
+
   if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
     --rtb->num_lost_pkts;
 
@@ -1339,12 +1450,13 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
 
       strm = ngtcp2_conn_find_stream(conn, sfr->stream_id);
       if (!strm) {
-        ngtcp2_frame_chain_del(frc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
         break;
       }
+      streamfrq_empty = ngtcp2_strm_streamfrq_empty(strm);
       rv = ngtcp2_strm_streamfrq_push(strm, frc);
       if (rv != 0) {
-        ngtcp2_frame_chain_del(frc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
         return rv;
       }
       if (!ngtcp2_strm_is_tx_queued(strm)) {
@@ -1354,6 +1466,9 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
           return rv;
         }
       }
+      if (streamfrq_empty) {
+        ++conn->tx.strmq_nretrans;
+      }
       break;
     case NGTCP2_FRAME_CRYPTO:
       frc = *pfrc;
@@ -1365,7 +1480,7 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
                              &frc->fr.crypto.offset, frc);
       if (rv != 0) {
         assert(ngtcp2_err_is_fatal(rv));
-        ngtcp2_frame_chain_del(frc, conn->mem);
+        ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
         return rv;
       }
       break;
@@ -1383,7 +1498,7 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
 
       *pfrc = (*pfrc)->next;
 
-      ngtcp2_frame_chain_del(frc, conn->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, rtb->frc_objalloc, rtb->mem);
       break;
     default:
       pfrc = &(*pfrc)->next;
@@ -1413,7 +1528,8 @@ int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
     assert(0 == rv);
 
     rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent);
-    ngtcp2_rtb_entry_del(ent, rtb->mem);
+    ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+                                  rtb->frc_objalloc, rtb->mem);
     if (rv != 0) {
       return rv;
     }
@@ -1442,7 +1558,8 @@ void ngtcp2_rtb_remove_early_data(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat) {
     rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
     assert(0 == rv);
 
-    ngtcp2_rtb_entry_del(ent, rtb->mem);
+    ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
+                                  rtb->frc_objalloc, rtb->mem);
   }
 }
 
@@ -1487,6 +1604,12 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
     assert(rtb->num_retransmittable);
     --rtb->num_retransmittable;
 
+    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING) {
+      ent->flags &= (uint16_t)~NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING;
+      assert(rtb->num_pto_eliciting);
+      --rtb->num_pto_eliciting;
+    }
+
     if (reclaimed) {
       --num_pkts;
     }
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h
index 25cf5211ab..d02a01bb1c 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h
@@ -34,6 +34,7 @@
 #include "ngtcp2_pkt.h"
 #include "ngtcp2_ksl.h"
 #include "ngtcp2_pq.h"
+#include "ngtcp2_objalloc.h"
 
 typedef struct ngtcp2_conn ngtcp2_conn;
 typedef struct ngtcp2_pktns ngtcp2_pktns;
@@ -72,11 +73,19 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
  * ngtcp2_frame_chain chains frames in a single packet.
  */
 struct ngtcp2_frame_chain {
-  ngtcp2_frame_chain *next;
-  ngtcp2_frame_chain_binder *binder;
-  ngtcp2_frame fr;
+  union {
+    struct {
+      ngtcp2_frame_chain *next;
+      ngtcp2_frame_chain_binder *binder;
+      ngtcp2_frame fr;
+    };
+
+    ngtcp2_opl_entry oplent;
+  };
 };
 
+ngtcp2_objalloc_def(frame_chain, ngtcp2_frame_chain, oplent);
+
 /*
  * ngtcp2_bind_frame_chains binds two frame chains |a| and |b| using
  * new or existing ngtcp2_frame_chain_binder.  |a| might have non-NULL
@@ -111,6 +120,13 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b,
  */
 int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem);
 
+/*
+ * ngtcp2_frame_chain_objalloc_new behaves like
+ * ngtcp2_frame_chain_new, but it uses |objalloc| to allocate the object.
+ */
+int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc,
+                                    ngtcp2_objalloc *objalloc);
+
 /*
  * ngtcp2_frame_chain_extralen_new works like ngtcp2_frame_chain_new,
  * but it allocates extra memory |extralen| in order to extend
@@ -120,30 +136,33 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen,
                                     const ngtcp2_mem *mem);
 
 /*
- * ngtcp2_frame_chain_stream_datacnt_new works like
+ * ngtcp2_frame_chain_stream_datacnt_objalloc_new works like
  * ngtcp2_frame_chain_new, but it allocates enough data to store
  * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_stream
- * object.  If |datacnt| equals to 1, ngtcp2_frame_chain_new is called
- * internally.
+ * object.  If no additional space is required,
+ * ngtcp2_frame_chain_objalloc_new is called internally.
  */
-int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc,
-                                          size_t datacnt,
-                                          const ngtcp2_mem *mem);
+int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+                                                   size_t datacnt,
+                                                   ngtcp2_objalloc *objalloc,
+                                                   const ngtcp2_mem *mem);
 
 /*
- * ngtcp2_frame_chain_crypto_datacnt_new works like
+ * ngtcp2_frame_chain_crypto_datacnt_objalloc_new works like
  * ngtcp2_frame_chain_new, but it allocates enough data to store
  * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_crypto
- * object.  If |datacnt| equals to 1, ngtcp2_frame_chain_new is called
- * internally.
+ * object.  If no additional space is required,
+ * ngtcp2_frame_chain_objalloc_new is called internally.
  */
-int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc,
-                                          size_t datacnt,
-                                          const ngtcp2_mem *mem);
+int ngtcp2_frame_chain_crypto_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc,
+                                                   size_t datacnt,
+                                                   ngtcp2_objalloc *objalloc,
+                                                   const ngtcp2_mem *mem);
 
-int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
-                                     const ngtcp2_vec *token,
-                                     const ngtcp2_mem *mem);
+int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc,
+                                              const ngtcp2_vec *token,
+                                              ngtcp2_objalloc *objalloc,
+                                              const ngtcp2_mem *mem);
 
 /*
  * ngtcp2_frame_chain_del deallocates |frc|.  It also deallocates the
@@ -151,17 +170,28 @@ int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc,
  */
 void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem);
 
+/*
+ * ngtcp2_frame_chain_objalloc_del adds |frc| to |objalloc| for reuse.
+ * It might just delete |frc| depending on the frame type and the size
+ * of |frc|.
+ */
+void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc,
+                                     ngtcp2_objalloc *objalloc,
+                                     const ngtcp2_mem *mem);
+
 /*
  * ngtcp2_frame_chain_init initializes |frc|.
  */
 void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc);
 
 /*
- * ngtcp2_frame_chain_list_del deletes |frc|, and all objects
- * connected by next field.
+ * ngtcp2_frame_chain_list_objalloc_del adds all ngtcp2_frame_chain
+ * linked from |frc| to |objalloc| for reuse.  Depending on the frame type
+ * and its size, ngtcp2_frame_chain might be deleted instead.
  */
-void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc,
-                                 const ngtcp2_mem *mem);
+void ngtcp2_frame_chain_list_objalloc_del(ngtcp2_frame_chain *frc,
+                                          ngtcp2_objalloc *objalloc,
+                                          const ngtcp2_mem *mem);
 
 /* NGTCP2_RTB_ENTRY_FLAG_NONE indicates that no flag is set. */
 #define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00
@@ -171,9 +201,8 @@ void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc,
 /* NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE indicates that the entry
    includes a frame which must be retransmitted until it is
    acknowledged.  In most cases, this flag is used along with
-   NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING.  We have these 2 flags because
-   NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE triggers PTO, but just
-   NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING does not. */
+   NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING and
+   NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING. */
 #define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02
 /* NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING indicates that the entry
    elicits acknowledgement. */
@@ -191,6 +220,12 @@ void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc,
 /* NGTCP2_RTB_ENTRY_FLAG_DATAGRAM indicates that the entry includes
    DATAGRAM frame. */
 #define NGTCP2_RTB_ENTRY_FLAG_DATAGRAM 0x40
+/* NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE indicates that the entry includes
+   a PMTUD probe packet. */
+#define NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE 0x80
+/* NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING indicates that the entry
+   includes a packet which elicits PTO probe packets. */
+#define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100
 
 typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
 
@@ -199,60 +234,69 @@ typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
  * to the one packet which is waiting for its ACK.
  */
 struct ngtcp2_rtb_entry {
-  ngtcp2_rtb_entry *next;
-
-  struct {
-    int64_t pkt_num;
-    uint8_t type;
-    uint8_t flags;
-  } hd;
-  ngtcp2_frame_chain *frc;
-  /* ts is the time point when a packet included in this entry is sent
-     to a peer. */
-  ngtcp2_tstamp ts;
-  /* lost_ts is the time when this entry is marked lost. */
-  ngtcp2_tstamp lost_ts;
-  /* pktlen is the length of QUIC packet */
-  size_t pktlen;
-  struct {
-    uint64_t delivered;
-    ngtcp2_tstamp delivered_ts;
-    ngtcp2_tstamp first_sent_ts;
-    uint64_t tx_in_flight;
-    uint64_t lost;
-    int is_app_limited;
-  } rst;
-  /* flags is bitwise-OR of zero or more of
-     NGTCP2_RTB_ENTRY_FLAG_*. */
-  uint8_t flags;
+  union {
+    struct {
+      ngtcp2_rtb_entry *next;
+
+      struct {
+        int64_t pkt_num;
+        uint8_t type;
+        uint8_t flags;
+      } hd;
+      ngtcp2_frame_chain *frc;
+      /* ts is the time point when a packet included in this entry is sent
+         to a peer. */
+      ngtcp2_tstamp ts;
+      /* lost_ts is the time when this entry is marked lost. */
+      ngtcp2_tstamp lost_ts;
+      /* pktlen is the length of QUIC packet */
+      size_t pktlen;
+      struct {
+        uint64_t delivered;
+        ngtcp2_tstamp delivered_ts;
+        ngtcp2_tstamp first_sent_ts;
+        uint64_t tx_in_flight;
+        uint64_t lost;
+        int is_app_limited;
+      } rst;
+      /* flags is bitwise-OR of zero or more of
+         NGTCP2_RTB_ENTRY_FLAG_*. */
+      uint16_t flags;
+    };
+
+    ngtcp2_opl_entry oplent;
+  };
 };
 
+ngtcp2_objalloc_def(rtb_entry, ngtcp2_rtb_entry, oplent);
+
 /*
  * ngtcp2_rtb_entry_new allocates ngtcp2_rtb_entry object, and assigns
- * its pointer to |*pent|.  On success, |*pent| takes ownership of
- * |frc|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- *     Out of memory.
+ * its pointer to |*pent|.
  */
-int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd,
-                         ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
-                         size_t pktlen, uint8_t flags, const ngtcp2_mem *mem);
+int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent,
+                                  const ngtcp2_pkt_hd *hd,
+                                  ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
+                                  size_t pktlen, uint16_t flags,
+                                  ngtcp2_objalloc *objalloc);
 
 /*
- * ngtcp2_rtb_entry_del deallocates |ent|.  It also frees memory
- * pointed by |ent|.
+ * ngtcp2_rtb_entry_objalloc_del adds |ent| to |objalloc| for reuse.
+ * ngtcp2_frame_chain linked from ent->frc are also added to
+ * |frc_objalloc| depending on their frame type and size.
  */
-void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem);
+void ngtcp2_rtb_entry_objalloc_del(ngtcp2_rtb_entry *ent,
+                                   ngtcp2_objalloc *objalloc,
+                                   ngtcp2_objalloc *frc_objalloc,
+                                   const ngtcp2_mem *mem);
 
 /*
  * ngtcp2_rtb tracks sent packets, and its ACK timeout for
  * retransmission.
  */
 typedef struct ngtcp2_rtb {
+  ngtcp2_objalloc *frc_objalloc;
+  ngtcp2_objalloc *rtb_entry_objalloc;
   /* ents includes ngtcp2_rtb_entry sorted by decreasing order of
      packet number. */
   ngtcp2_ksl ents;
@@ -271,6 +315,9 @@ typedef struct ngtcp2_rtb {
   /* num_retransmittable is the number of packets which contain frames
      that must be retransmitted on loss. */
   size_t num_retransmittable;
+  /* num_pto_eliciting is the number of packets that elicit PTO probe
+     packets. */
+  size_t num_pto_eliciting;
   /* probe_pkt_left is the number of probe packet to send */
   size_t probe_pkt_left;
   /* pktns_id is the identifier of packet number space. */
@@ -296,7 +343,9 @@ typedef struct ngtcp2_rtb {
  */
 void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id,
                      ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc,
-                     ngtcp2_log *log, ngtcp2_qlog *qlog, const ngtcp2_mem *mem);
+                     ngtcp2_log *log, ngtcp2_qlog *qlog,
+                     ngtcp2_objalloc *rtb_entry_objalloc,
+                     ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem);
 
 /*
  * ngtcp2_rtb_free deallocates resources allocated for |rtb|.
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c
index b601fbcc90..6d4a95196a 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c
@@ -35,9 +35,11 @@ static int offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) {
   return *(int64_t *)lhs < *(int64_t *)rhs;
 }
 
-int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
-                     uint64_t max_rx_offset, uint64_t max_tx_offset,
-                     void *stream_user_data, const ngtcp2_mem *mem) {
+void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
+                      uint64_t max_rx_offset, uint64_t max_tx_offset,
+                      void *stream_user_data, ngtcp2_objalloc *frc_objalloc,
+                      const ngtcp2_mem *mem) {
+  strm->frc_objalloc = frc_objalloc;
   strm->cycle = 0;
   strm->tx.acked_offset = NULL;
   strm->tx.cont_acked_offset = 0;
@@ -56,8 +58,6 @@ int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
   strm->pe.index = NGTCP2_PQ_BAD_INDEX;
   strm->mem = mem;
   strm->app_error_code = 0;
-
-  return 0;
 }
 
 void ngtcp2_strm_free(ngtcp2_strm *strm) {
@@ -70,17 +70,23 @@ void ngtcp2_strm_free(ngtcp2_strm *strm) {
   if (strm->tx.streamfrq) {
     for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);
          ngtcp2_ksl_it_next(&it)) {
-      ngtcp2_frame_chain_del(ngtcp2_ksl_it_get(&it), strm->mem);
+      ngtcp2_frame_chain_objalloc_del(ngtcp2_ksl_it_get(&it),
+                                      strm->frc_objalloc, strm->mem);
     }
 
     ngtcp2_ksl_free(strm->tx.streamfrq);
     ngtcp2_mem_free(strm->mem, strm->tx.streamfrq);
   }
 
-  ngtcp2_rob_free(strm->rx.rob);
-  ngtcp2_mem_free(strm->mem, strm->rx.rob);
-  ngtcp2_gaptr_free(strm->tx.acked_offset);
-  ngtcp2_mem_free(strm->mem, strm->tx.acked_offset);
+  if (strm->rx.rob) {
+    ngtcp2_rob_free(strm->rx.rob);
+    ngtcp2_mem_free(strm->mem, strm->rx.rob);
+  }
+
+  if (strm->tx.acked_offset) {
+    ngtcp2_gaptr_free(strm->tx.acked_offset);
+    ngtcp2_mem_free(strm->mem, strm->tx.acked_offset);
+  }
 }
 
 static int strm_rob_init(ngtcp2_strm *strm) {
@@ -154,17 +160,12 @@ void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) {
 }
 
 static int strm_streamfrq_init(ngtcp2_strm *strm) {
-  int rv;
   ngtcp2_ksl *streamfrq = ngtcp2_mem_malloc(strm->mem, sizeof(*streamfrq));
   if (streamfrq == NULL) {
     return NGTCP2_ERR_NOMEM;
   }
 
-  rv = ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem);
-  if (rv != 0) {
-    ngtcp2_mem_free(strm->mem, streamfrq);
-    return rv;
-  }
+  ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem);
 
   strm->tx.streamfrq = streamfrq;
 
@@ -233,7 +234,7 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
     if (idx == fr->datacnt) {
       if (fr->fin) {
         if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) {
-          ngtcp2_frame_chain_del(frc, strm->mem);
+          ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
           assert(ngtcp2_ksl_len(strm->tx.streamfrq) == 0);
           return 0;
         }
@@ -253,7 +254,7 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
         return 0;
       }
 
-      ngtcp2_frame_chain_del(frc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
       continue;
     }
 
@@ -292,10 +293,10 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
       return 0;
     }
 
-    rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->datacnt - end_idx,
-                                               strm->mem);
+    rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+        &nfrc, fr->datacnt - end_idx, strm->frc_objalloc, strm->mem);
     if (rv != 0) {
-      ngtcp2_frame_chain_del(frc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
       return rv;
     }
 
@@ -317,8 +318,8 @@ static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm,
     rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_del(nfrc, strm->mem);
-      ngtcp2_frame_chain_del(frc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
       return rv;
     }
 
@@ -382,7 +383,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
       rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc);
       if (rv != 0) {
         assert(ngtcp2_err_is_fatal(rv));
-        ngtcp2_frame_chain_del(frc, strm->mem);
+        ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
         return rv;
       }
       *pfrc = NULL;
@@ -400,10 +401,11 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
     assert(acnt > 0);
     assert(bcnt > 0);
 
-    rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, bcnt, strm->mem);
+    rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+        &nfrc, bcnt, strm->frc_objalloc, strm->mem);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_del(frc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
       return rv;
     }
 
@@ -419,15 +421,16 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
     rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_del(nfrc, strm->mem);
-      ngtcp2_frame_chain_del(frc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
       return rv;
     }
 
-    rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem);
+    rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+        &nfrc, acnt, strm->frc_objalloc, strm->mem);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_del(frc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
       return rv;
     }
 
@@ -437,7 +440,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
     nfr->datacnt = acnt;
     ngtcp2_vec_copy(nfr->data, a, acnt);
 
-    ngtcp2_frame_chain_del(frc, strm->mem);
+    ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
 
     *pfrc = nfrc;
 
@@ -459,7 +462,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
     rv = strm_streamfrq_unacked_pop(strm, &nfrc);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
-      ngtcp2_frame_chain_del(frc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
       return rv;
     }
     if (nfrc == NULL) {
@@ -470,7 +473,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
 
     if (nfr->fin && nfr->datacnt == 0) {
       fr->fin = 1;
-      ngtcp2_frame_chain_del(nfrc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
       break;
     }
 
@@ -480,8 +483,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
       rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
       if (rv != 0) {
         assert(ngtcp2_err_is_fatal(rv));
-        ngtcp2_frame_chain_del(nfrc, strm->mem);
-        ngtcp2_frame_chain_del(frc, strm->mem);
+        ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+        ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
         return rv;
       }
       break;
@@ -492,7 +495,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
 
     if (nfr->datacnt == 0) {
       fr->fin = nfr->fin;
-      ngtcp2_frame_chain_del(nfrc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
       continue;
     }
 
@@ -500,8 +503,8 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
 
     rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc);
     if (rv != 0) {
-      ngtcp2_frame_chain_del(nfrc, strm->mem);
-      ngtcp2_frame_chain_del(frc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(nfrc, strm->frc_objalloc, strm->mem);
+      ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
       return rv;
     }
 
@@ -519,9 +522,10 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
 
   assert(acnt > fr->datacnt);
 
-  rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem);
+  rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
+      &nfrc, acnt, strm->frc_objalloc, strm->mem);
   if (rv != 0) {
-    ngtcp2_frame_chain_del(frc, strm->mem);
+    ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
     return rv;
   }
 
@@ -530,7 +534,7 @@ int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc,
   nfr->datacnt = acnt;
   ngtcp2_vec_copy(nfr->data, a, acnt);
 
-  ngtcp2_frame_chain_del(frc, strm->mem);
+  ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
 
   *pfrc = nfrc;
 
@@ -596,7 +600,7 @@ void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) {
   for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);
        ngtcp2_ksl_it_next(&it)) {
     frc = ngtcp2_ksl_it_get(&it);
-    ngtcp2_frame_chain_del(frc, strm->mem);
+    ngtcp2_frame_chain_objalloc_del(frc, strm->frc_objalloc, strm->mem);
   }
   ngtcp2_ksl_clear(strm->tx.streamfrq);
 }
@@ -616,7 +620,6 @@ int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm) {
 
 ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm,
                                                  uint64_t offset) {
-  ngtcp2_ksl_it gapit;
   ngtcp2_range gap;
 
   if (strm->tx.acked_offset == NULL) {
@@ -625,8 +628,7 @@ ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm,
     return gap;
   }
 
-  gapit = ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset);
-  return *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit);
+  return ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset);
 }
 
 uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) {
@@ -638,7 +640,6 @@ uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) {
 }
 
 static int strm_acked_offset_init(ngtcp2_strm *strm) {
-  int rv;
   ngtcp2_gaptr *acked_offset =
       ngtcp2_mem_malloc(strm->mem, sizeof(*acked_offset));
 
@@ -646,11 +647,7 @@ static int strm_acked_offset_init(ngtcp2_strm *strm) {
     return NGTCP2_ERR_NOMEM;
   }
 
-  rv = ngtcp2_gaptr_init(acked_offset, strm->mem);
-  if (rv != 0) {
-    ngtcp2_mem_free(strm->mem, acked_offset);
-    return rv;
-  }
+  ngtcp2_gaptr_init(acked_offset, strm->mem);
 
   strm->tx.acked_offset = acked_offset;
 
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h
index 0fb3cc84ea..c9319268e5 100644
--- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h
@@ -75,86 +75,91 @@ typedef struct ngtcp2_frame_chain ngtcp2_frame_chain;
    In this case, without this flag, we are unable to distinguish
    assigned value from unassigned one.  */
 #define NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET 0x100
+/* NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED is set when
+   stream_stop_sending callback is called. */
+#define NGTCP2_STRM_FLAG_STREAM_STOP_SENDING_CALLED 0x200
 
 typedef struct ngtcp2_strm ngtcp2_strm;
 
 struct ngtcp2_strm {
-  ngtcp2_pq_entry pe;
-  uint64_t cycle;
+  union {
+    struct {
+      ngtcp2_pq_entry pe;
+      uint64_t cycle;
+      ngtcp2_objalloc *frc_objalloc;
 
-  struct {
-    /* acked_offset tracks acknowledged outgoing data. */
-    ngtcp2_gaptr *acked_offset;
-    /* cont_acked_offset is the offset that all data up to this offset
-       is acknowledged by a remote endpoint.  It is used until the
-       remote endpoint acknowledges data in out-of-order.  After that,
-       acked_offset is used instead. */
-    uint64_t cont_acked_offset;
-    /* streamfrq contains STREAM frame for retransmission.  The flow
-       control credits have been paid when they are transmitted first
-       time.  There are no restriction regarding flow control for
-       retransmission. */
-    ngtcp2_ksl *streamfrq;
-    /* offset is the next offset of outgoing data.  In other words, it
-       is the number of bytes sent in this stream without
-       duplication. */
-    uint64_t offset;
-    /* max_tx_offset is the maximum offset that local endpoint can
-       send for this stream. */
-    uint64_t max_offset;
-    /* last_max_stream_data_ts is the timestamp when last
-       MAX_STREAM_DATA frame is sent. */
-    ngtcp2_tstamp last_max_stream_data_ts;
-  } tx;
+      struct {
+        /* acked_offset tracks acknowledged outgoing data. */
+        ngtcp2_gaptr *acked_offset;
+        /* cont_acked_offset is the offset that all data up to this offset
+           is acknowledged by a remote endpoint.  It is used until the
+           remote endpoint acknowledges data in out-of-order.  After that,
+           acked_offset is used instead. */
+        uint64_t cont_acked_offset;
+        /* streamfrq contains STREAM frame for retransmission.  The flow
+           control credits have been paid when they are transmitted first
+           time.  There are no restriction regarding flow control for
+           retransmission. */
+        ngtcp2_ksl *streamfrq;
+        /* offset is the next offset of outgoing data.  In other words, it
+           is the number of bytes sent in this stream without
+           duplication. */
+        uint64_t offset;
+        /* max_tx_offset is the maximum offset that local endpoint can
+           send for this stream. */
+        uint64_t max_offset;
+        /* last_max_stream_data_ts is the timestamp when last
+           MAX_STREAM_DATA frame is sent. */
+        ngtcp2_tstamp last_max_stream_data_ts;
+      } tx;
 
-  struct {
-    /* rob is the reorder buffer for incoming stream data.  The data
-       received in out of order is buffered and sorted by its offset
-       in this object. */
-    ngtcp2_rob *rob;
-    /* cont_offset is the largest offset of consecutive data.  It is
-       used until the endpoint receives out-of-order data.  After
-       that, rob is used to track the offset and data. */
-    uint64_t cont_offset;
-    /* last_offset is the largest offset of stream data received for
-       this stream. */
-    uint64_t last_offset;
-    /* max_offset is the maximum offset that remote endpoint can send
-       to this stream. */
-    uint64_t max_offset;
-    /* unsent_max_offset is the maximum offset that remote endpoint
-       can send to this stream, and it is not notified to the remote
-       endpoint.  unsent_max_offset >= max_offset must be hold. */
-    uint64_t unsent_max_offset;
-    /* window is the stream-level flow control window size. */
-    uint64_t window;
-  } rx;
+      struct {
+        /* rob is the reorder buffer for incoming stream data.  The data
+           received in out of order is buffered and sorted by its offset
+           in this object. */
+        ngtcp2_rob *rob;
+        /* cont_offset is the largest offset of consecutive data.  It is
+           used until the endpoint receives out-of-order data.  After
+           that, rob is used to track the offset and data. */
+        uint64_t cont_offset;
+        /* last_offset is the largest offset of stream data received for
+           this stream. */
+        uint64_t last_offset;
+        /* max_offset is the maximum offset that remote endpoint can send
+           to this stream. */
+        uint64_t max_offset;
+        /* unsent_max_offset is the maximum offset that remote endpoint
+           can send to this stream, and it is not notified to the remote
+           endpoint.  unsent_max_offset >= max_offset must be hold. */
+        uint64_t unsent_max_offset;
+        /* window is the stream-level flow control window size. */
+        uint64_t window;
+      } rx;
 
-  const ngtcp2_mem *mem;
-  int64_t stream_id;
-  void *stream_user_data;
-  /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */
-  uint32_t flags;
-  /* app_error_code is an error code the local endpoint sent in
-     RESET_STREAM or STOP_SENDING, or received from a remote endpoint
-     in RESET_STREAM or STOP_SENDING.  First application error code is
-     chosen and when set, NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag is
-     set in flags field. */
-  uint64_t app_error_code;
+      const ngtcp2_mem *mem;
+      int64_t stream_id;
+      void *stream_user_data;
+      /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */
+      uint32_t flags;
+      /* app_error_code is an error code the local endpoint sent in
+         RESET_STREAM or STOP_SENDING, or received from a remote endpoint
+         in RESET_STREAM or STOP_SENDING.  First application error code is
+         chosen and when set, NGTCP2_STRM_FLAG_APP_ERROR_CODE_SET flag is
+         set in flags field. */
+      uint64_t app_error_code;
+    };
+
+    ngtcp2_opl_entry oplent;
+  };
 };
 
 /*
  * ngtcp2_strm_init initializes |strm|.
- *
- * This function returns 0 if it succeeds, or one of the following
- * negative error codes:
- *
- * NGTCP2_ERR_NOMEM
- *     Out of memory
  */
-int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
-                     uint64_t max_rx_offset, uint64_t max_tx_offset,
-                     void *stream_user_data, const ngtcp2_mem *mem);
+void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
+                      uint64_t max_rx_offset, uint64_t max_tx_offset,
+                      void *stream_user_data, ngtcp2_objalloc *frc_objalloc,
+                      const ngtcp2_mem *mem);
 
 /*
  * ngtcp2_strm_free deallocates memory allocated for |strm|.  This
diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c
new file mode 100644
index 0000000000..b31162c3eb
--- /dev/null
+++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_version.c
@@ -0,0 +1,39 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM,
+                              NGTCP2_VERSION};
+
+const ngtcp2_info *ngtcp2_version(int least_version) {
+  if (least_version > NGTCP2_VERSION_NUM) {
+    return NULL;
+  }
+  return &version;
+}
diff --git a/src/contrib/libngtcp2/ngtcp2/ngtcp2.h b/src/contrib/libngtcp2/ngtcp2/ngtcp2.h
index 4012fed065..5f5946d7fc 100644
--- a/src/contrib/libngtcp2/ngtcp2/ngtcp2.h
+++ b/src/contrib/libngtcp2/ngtcp2/ngtcp2.h
@@ -32,6 +32,11 @@
 #  define WIN32
 #endif
 
+#ifdef WIN32
+#  pragma warning(push)
+#  pragma warning(disable : 4324)
+#endif
+
 #include <stdlib.h>
 #if defined(_MSC_VER) && (_MSC_VER < 1800)
 /* MSVC < 2013 does not have inttypes.h because it is not C99
@@ -45,10 +50,26 @@
 #include <stdarg.h>
 #include <stddef.h>
 
-#ifdef WIN32
-#  include <winsock2.h>
+#ifndef NGTCP2_USE_GENERIC_SOCKADDR
+#  ifdef WIN32
+#    include <ws2tcpip.h>
+#  else
+#    include <sys/socket.h>
+#    include <netinet/in.h>
+#  endif
+#endif
+
+#ifdef AF_INET
+#  define NGTCP2_AF_INET AF_INET
 #else
-#  include <sys/socket.h>
+#  define NGTCP2_AF_INET 2
+#endif
+
+#ifdef AF_INET6
+#  define NGTCP2_AF_INET6 AF_INET6
+#else
+#  define NGTCP2_AF_INET6 23
+#  define NGTCP2_USE_GENERIC_IPV6_SOCKADDR
 #endif
 
 #include <ngtcp2/version.h>
@@ -70,11 +91,9 @@
 #endif   /* !defined(WIN32) */
 
 #ifdef WIN32
-#  define NGTCP2_ALIGN_BEFORE(N) __declspec(align(N))
-#  define NGTCP2_ALIGN_AFTER(N)
+#  define NGTCP2_ALIGN(N) __declspec(align(N))
 #else /* !WIN32 */
-#  define NGTCP2_ALIGN_BEFORE(N)
-#  define NGTCP2_ALIGN_AFTER(N) __attribute__((aligned(N)))
+#  define NGTCP2_ALIGN(N) __attribute__((aligned(N)))
 #endif /* !WIN32 */
 
 #ifdef __cplusplus
@@ -92,61 +111,68 @@ typedef ptrdiff_t ngtcp2_ssize;
  * @functypedef
  *
  * :type:`ngtcp2_malloc` is a custom memory allocator to replace
- * :manpage:`malloc(3)`.  The |mem_user_data| is the mem_user_data
- * member of :type:`ngtcp2_mem` structure.
+ * :manpage:`malloc(3)`.  The |user_data| is
+ * :member:`ngtcp2_mem.user_data`.
  */
-typedef void *(*ngtcp2_malloc)(size_t size, void *mem_user_data);
+typedef void *(*ngtcp2_malloc)(size_t size, void *user_data);
 
 /**
  * @functypedef
  *
  * :type:`ngtcp2_free` is a custom memory allocator to replace
- * :manpage:`free(3)`.  The |mem_user_data| is the mem_user_data
- * member of :type:`ngtcp2_mem` structure.
+ * :manpage:`free(3)`.  The |user_data| is
+ * :member:`ngtcp2_mem.user_data`.
  */
-typedef void (*ngtcp2_free)(void *ptr, void *mem_user_data);
+typedef void (*ngtcp2_free)(void *ptr, void *user_data);
 
 /**
  * @functypedef
  *
  * :type:`ngtcp2_calloc` is a custom memory allocator to replace
- * :manpage:`calloc(3)`.  The |mem_user_data| is the mem_user_data
- * member of :type:`ngtcp2_mem` structure.
+ * :manpage:`calloc(3)`.  The |user_data| is the
+ * :member:`ngtcp2_mem.user_data`.
  */
-typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *mem_user_data);
+typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *user_data);
 
 /**
  * @functypedef
  *
  * :type:`ngtcp2_realloc` is a custom memory allocator to replace
- * :manpage:`realloc(3)`.  The |mem_user_data| is the mem_user_data
- * member of :type:`ngtcp2_mem` structure.
+ * :manpage:`realloc(3)`.  The |user_data| is the
+ * :member:`ngtcp2_mem.user_data`.
  */
-typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data);
+typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *user_data);
 
 /**
  * @struct
  *
- * Custom memory allocator functions and user defined pointer.  The
- * |mem_user_data| member is passed to each allocator function.  This
- * can be used, for example, to achieve per-connection memory pool.
+ * :type:`ngtcp2_mem` is a custom memory allocator.  The
+ * :member:`user_data` field is passed to each allocator function.
+ * This can be used, for example, to achieve per-connection memory
+ * pool.
  *
  * In the following example code, ``my_malloc``, ``my_free``,
  * ``my_calloc`` and ``my_realloc`` are the replacement of the
  * standard allocators :manpage:`malloc(3)`, :manpage:`free(3)`,
  * :manpage:`calloc(3)` and :manpage:`realloc(3)` respectively::
  *
- *     void *my_malloc_cb(size_t size, void *mem_user_data) {
+ *     void *my_malloc_cb(size_t size, void *user_data) {
+ *       (void)user_data;
  *       return my_malloc(size);
  *     }
  *
- *     void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); }
+ *     void my_free_cb(void *ptr, void *user_data) {
+ *       (void)user_data;
+ *       my_free(ptr);
+ *     }
  *
- *     void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) {
+ *     void *my_calloc_cb(size_t nmemb, size_t size, void *user_data) {
+ *       (void)user_data;
  *       return my_calloc(nmemb, size);
  *     }
  *
- *     void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) {
+ *     void *my_realloc_cb(void *ptr, size_t size, void *user_data) {
+ *       (void)user_data;
  *       return my_realloc(ptr, size);
  *     }
  *
@@ -159,10 +185,10 @@ typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data);
  */
 typedef struct ngtcp2_mem {
   /**
-   * :member:`mem_user_data` is an arbitrary user supplied data.  This
+   * :member:`user_data` is an arbitrary user supplied data.  This
    * is passed to each allocator function.
    */
-  void *mem_user_data;
+  void *user_data;
   /**
    * :member:`malloc` is a custom allocator function to replace
    * :manpage:`malloc(3)`.
@@ -233,7 +259,17 @@ typedef struct ngtcp2_mem {
  *
  * :macro:`NGTCP2_PROTO_VER_V1` is the QUIC version 1.
  */
-#define NGTCP2_PROTO_VER_V1 0x00000001u
+#define NGTCP2_PROTO_VER_V1 ((uint32_t)0x00000001u)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_PROTO_VER_V2_DRAFT` is the provisional version
+ * number for QUIC version 2 draft.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_PROTO_VER_V2_DRAFT ((uint32_t)0x709a50c4u)
 
 /**
  * @macro
@@ -267,6 +303,14 @@ typedef struct ngtcp2_mem {
  */
 #define NGTCP2_PROTO_VER_MIN NGTCP2_PROTO_VER_DRAFT_MIN
 
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RESERVED_VERSION_MASK` is the bit mask of reserved
+ * version.
+ */
+#define NGTCP2_RESERVED_VERSION_MASK 0x0a0a0a0au
+
 /**
  * @macrosection
  *
@@ -354,6 +398,28 @@ typedef struct ngtcp2_mem {
  */
 #define NGTCP2_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"
 
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_KEY_V2_DRAFT` is an encryption key to create
+ * integrity tag of Retry packet.  It is used for QUIC v2 draft.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_RETRY_KEY_V2_DRAFT                                              \
+  "\xba\x85\x8d\xc7\xb4\x3d\xe5\xdb\xf8\x76\x17\xff\x4a\xb2\x53\xdb"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_RETRY_NONCE_V2_DRAFT` is nonce used when generating
+ * integrity tag of Retry packet.  It is used for QUIC v2 draft.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_RETRY_NONCE_V2_DRAFT                                            \
+  "\x14\x1b\x99\xc2\x39\xb0\x3e\x78\x5d\x6a\x2e\x9f"
+
 /**
  * @macro
  *
@@ -456,7 +522,7 @@ typedef struct ngtcp2_mem {
  *
  * :type:`ngtcp2_pkt_info` is a packet metadata.
  */
-typedef NGTCP2_ALIGN_BEFORE(8) struct NGTCP2_ALIGN_AFTER(8) ngtcp2_pkt_info {
+typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info {
   /**
    * :member:`ecn` is ECN marking and when passing
    * `ngtcp2_conn_read_pkt()`, and it should be either
@@ -721,6 +787,20 @@ typedef NGTCP2_ALIGN_BEFORE(8) struct NGTCP2_ALIGN_AFTER(8) ngtcp2_pkt_info {
  * connection is not established before the specified deadline.
  */
 #define NGTCP2_ERR_HANDSHAKE_TIMEOUT -246
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE` indicates the
+ * version negotiation failed.
+ */
+#define NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE -247
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_ERR_IDLE_CLOSE` indicates the connection should be
+ * closed silently because of idle timeout.
+ */
+#define NGTCP2_ERR_IDLE_CLOSE -248
 /**
  * @macro
  *
@@ -782,7 +862,8 @@ typedef NGTCP2_ALIGN_BEFORE(8) struct NGTCP2_ALIGN_AFTER(8) ngtcp2_pkt_info {
 /**
  * @enum
  *
- * :type:`ngtcp2_pkt_type` defines QUIC packet types.
+ * :type:`ngtcp2_pkt_type` defines QUIC version-independent QUIC
+ * packet types.
  */
 typedef enum ngtcp2_pkt_type {
   /**
@@ -798,23 +879,23 @@ typedef enum ngtcp2_pkt_type {
   /**
    * :enum:`NGTCP2_PKT_INITIAL` indicates Initial packet.
    */
-  NGTCP2_PKT_INITIAL = 0x0,
+  NGTCP2_PKT_INITIAL = 0x10,
   /**
    * :enum:`NGTCP2_PKT_0RTT` indicates 0RTT packet.
    */
-  NGTCP2_PKT_0RTT = 0x1,
+  NGTCP2_PKT_0RTT = 0x11,
   /**
    * :enum:`NGTCP2_PKT_HANDSHAKE` indicates Handshake packet.
    */
-  NGTCP2_PKT_HANDSHAKE = 0x2,
+  NGTCP2_PKT_HANDSHAKE = 0x12,
   /**
    * :enum:`NGTCP2_PKT_RETRY` indicates Retry packet.
    */
-  NGTCP2_PKT_RETRY = 0x3,
+  NGTCP2_PKT_RETRY = 0x13,
   /**
-   * :enum:`NGTCP2_PKT_SHORT` is defined by libngtcp2 for convenience.
+   * :enum:`NGTCP2_PKT_1RTT` is defined by libngtcp2 for convenience.
    */
-  NGTCP2_PKT_SHORT = 0x40
+  NGTCP2_PKT_1RTT = 0x40
 } ngtcp2_pkt_type;
 
 /**
@@ -966,6 +1047,16 @@ typedef enum ngtcp2_pkt_type {
  */
 #define NGTCP2_CRYPTO_ERROR 0x100u
 
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT` is QUIC transport
+ * error code ``VERSION_NEGOTIATION_ERROR``.
+ *
+ * https://quicwg.org/quic-v2/draft-ietf-quic-v2.html
+ */
+#define NGTCP2_VERSION_NEGOTIATION_ERROR_DRAFT 0x53f8u
+
 /**
  * @enum
  *
@@ -1100,8 +1191,8 @@ typedef struct ngtcp2_pkt_hd {
    */
   uint8_t type;
   /**
-   * :member:`flags` is zero or more of NGTCP2_PKT_FLAG_*.  See
-   * :macro:`NGTCP2_PKT_FLAG_NONE`.
+   * :member:`flags` is zero or more of :macro:`NGTCP2_PKT_FLAG_*
+   * <NGTCP2_PKT_FLAG_NONE>`.
    */
   uint8_t flags;
 } ngtcp2_pkt_hd;
@@ -1246,6 +1337,29 @@ typedef struct ngtcp2_preferred_addr {
   uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN];
 } ngtcp2_preferred_addr;
 
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_version_info` represents version_information
+ * structure.
+ */
+typedef struct ngtcp2_version_info {
+  /**
+   * :member:`chosen_version` is the version chosen by the sender.
+   */
+  uint32_t chosen_version;
+  /**
+   * :member:`other_versions` points the wire image of other_versions
+   * field.  The each version is therefore in network byte order.
+   */
+  uint8_t *other_versions;
+  /**
+   * :member:`other_versionslen` is the number of bytes pointed by
+   * :member:`other_versions`, not the number of versions included.
+   */
+  size_t other_versionslen;
+} ngtcp2_version_info;
+
 #define NGTCP2_TRANSPORT_PARAMS_VERSION_V1 1
 #define NGTCP2_TRANSPORT_PARAMS_VERSION NGTCP2_TRANSPORT_PARAMS_VERSION_V1
 
@@ -1353,7 +1467,7 @@ typedef struct ngtcp2_transport_params {
    * :member:`max_datagram_frame_size` is the maximum size of DATAGRAM
    * frame that this endpoint willingly receives.  Specifying 0
    * disables DATAGRAM support.  See
-   * https://tools.ietf.org/html/draft-ietf-quic-datagram-01
+   * https://datatracker.ietf.org/doc/html/rfc9221
    */
   uint64_t max_datagram_frame_size;
   /**
@@ -1388,6 +1502,18 @@ typedef struct ngtcp2_transport_params {
    * regardless of this field value.
    */
   uint8_t grease_quic_bit;
+  /**
+   * :member:`version_info` contains version_information field if
+   * :member:`version_info_present` is nonzero.  Application should
+   * not specify this field.
+   */
+  ngtcp2_version_info version_info;
+  /**
+   * :member:`version_info_present` is nonzero if
+   * :member:`version_info` is set.  Application should not specify
+   * this field.
+   */
+  uint8_t version_info_present;
 } ngtcp2_transport_params;
 
 /**
@@ -1468,11 +1594,11 @@ typedef struct ngtcp2_conn_stat {
   ngtcp2_tstamp loss_detection_timer;
   /**
    * :member:`last_tx_pkt_ts` corresponds to
-   * time_of_last_ack_eliciting_packet in RFC 9002.
+   * time_of_last_ack_eliciting_packet in :rfc:`9002`.
    */
   ngtcp2_tstamp last_tx_pkt_ts[NGTCP2_PKTNS_ID_MAX];
   /**
-   * :member:`loss_time` corresponds to loss_time in RFC 9002.
+   * :member:`loss_time` corresponds to loss_time in :rfc:`9002`.
    */
   ngtcp2_tstamp loss_time[NGTCP2_PKTNS_ID_MAX];
   /**
@@ -1592,8 +1718,8 @@ typedef struct ngtcp2_rand_ctx {
  *
  * :type:`ngtcp2_qlog_write` is a callback function which is called to
  * write qlog |data| of length |datalen| bytes.  |flags| is bitwise OR
- * of zero or more of NGTCP2_QLOG_WRITE_FLAG_*.  See
- * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE`.  If
+ * of zero or more of :macro:`NGTCP2_QLOG_WRITE_FLAG_*
+ * <NGTCP2_QLOG_WRITE_FLAG_NONE>`.  If
  * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` is set, |datalen| may be 0.
  */
 typedef void (*ngtcp2_qlog_write)(void *user_data, uint32_t flags,
@@ -1702,21 +1828,13 @@ typedef struct ngtcp2_settings {
    * immediate acknowledgement.
    */
   size_t ack_thresh;
-  /**
-   * :member:`assume_symmetric_path`, if set to nonzero, assumes that
-   * a network path is symmetric and extends the UDP payload size up to
-   * the incoming UDP payload size.  The size is still capped by
-   * :member:`max_udp_payload_size`.  This field is ignored if
-   * :member:`no_udp_payload_size_shaping` is set to nonzero.
-   */
-  int assume_symmetric_path;
   /**
    * :member:`no_udp_payload_size_shaping`, if set to nonzero,
    * instructs the library not to limit the UDP payload size to
-   * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` (which can be extended, see
-   * :member:`assume_symmetric_path`) and instead fully utilize the
-   * given buffer size or :member:`max_udp_payload_size` which is
-   * smaller.
+   * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` (which can be extended by
+   * Path MTU Discovery) and instead use the mininum size among the
+   * given buffer size, :member:`max_udp_payload_size`, and the
+   * received max_udp_payload QUIC transport parameter.
    */
   int no_udp_payload_size_shaping;
   /**
@@ -1728,8 +1846,155 @@ typedef struct ngtcp2_settings {
    * field is set to ``UINT64_MAX``, no handshake timeout is set.
    */
   ngtcp2_duration handshake_timeout;
+  /**
+   * :member:`preferred_versions` is the array of versions that are
+   * preferred by the local endpoint.  All versions set in this array
+   * must be supported by the library, and compatible to QUIC v1.  The
+   * reserved versions are not allowed.  They are sorted in the order
+   * of preference.
+   *
+   * On compatible version negotiation, server will negotiate one of
+   * those versions contained in this array if a client initially
+   * chooses a less preferred version.  This version set corresponds
+   * to Offered Versions in QUIC Version Negotiation draft, and it should
+   * be sent in Version Negotiation packet.
+   *
+   * Client uses this field and :member:`original_version` to prevent
+   * version downgrade attack if it reacted upon Version Negotiation
+   * packet.  If this field is specified, client must include
+   * |client_chosen_version| passed to `ngtcp2_conn_client_new` unless
+   * |client_chosen_version| is a reserved version.
+   */
+  uint32_t *preferred_versions;
+  /**
+   * :member:`preferred_versionslen` is the number of versions that
+   * are contained in the array pointed by
+   * :member:`preferred_versions`.
+   */
+  size_t preferred_versionslen;
+  /**
+   * :member:`other_versions` is the array of versions that are set in
+   * :member:`other_versions <ngtcp2_version_info.other_versions>`
+   * field of outgoing version_information QUIC transport parameter.
+   *
+   * For server, this corresponds to Fully-Deployed Versions in QUIC
+   * Version Negotiation draft.  If this field is set not, it is set
+   * to :member:`preferred_versions` internally if
+   * :member:`preferred_versionslen` is not zero.  If this field is
+   * not set, and :member:`preferred_versionslen` is zero, this field
+   * is set to :macro:`NGTCP2_PROTO_VER_V1` internally.
+   *
+   * Client must include |client_chosen_version| passed to
+   * `ngtcp2_conn_client_new` in this array if this field is set and
+   * |client_chosen_version| is not a reserved version.  If this field
+   * is not set, |client_chosen_version| passed to
+   * `ngtcp2_conn_client_new` will be set in this field internally
+   * unless |client_chosen_version| is a reserved version.
+   */
+  uint32_t *other_versions;
+  /**
+   * :member:`other_versionslen` is the number of versions that are
+   * contained in the array pointed by :member:`other_versions`.
+   */
+  size_t other_versionslen;
+  /**
+   * :member:`original_version` is the original version that client
+   * initially used to make a connection attempt.  If it is set, and
+   * it differs from |client_chosen_version| passed to
+   * `ngtcp2_conn_client_new`, the library assumes that client reacted
+   * upon Version Negotiation packet.  Server does not use this field.
+   */
+  uint32_t original_version;
+  /**
+   * :member:`no_pmtud`, if set to nonzero, disables Path MTU
+   * Discovery.
+   */
+  int no_pmtud;
 } ngtcp2_settings;
 
+#ifdef NGTCP2_USE_GENERIC_SOCKADDR
+typedef struct ngtcp2_sockaddr {
+  uint16_t sa_family;
+  uint8_t sa_data[14];
+} ngtcp2_sockaddr;
+
+typedef struct ngtcp2_in_addr {
+  uint32_t s_addr;
+} ngtcp2_in_addr;
+
+typedef struct ngtcp2_sockaddr_in {
+  uint16_t sin_family;
+  uint16_t sin_port;
+  ngtcp2_in_addr sin_addr;
+  uint8_t sin_zero[8];
+} ngtcp2_sockaddr_in;
+
+#  define NGTCP2_SS_MAXSIZE 128
+#  define NGTCP2_SS_ALIGNSIZE (sizeof(uint64_t))
+#  define NGTCP2_SS_PAD1SIZE (NGTCP2_SS_ALIGNSIZE - sizeof(uint16_t))
+#  define NGTCP2_SS_PAD2SIZE                                                   \
+    (NGTCP2_SS_MAXSIZE -                                                       \
+     (sizeof(uint16_t) + NGTCP2_SS_PAD1SIZE + NGTCP2_SS_ALIGNSIZE))
+
+typedef struct ngtcp2_sockaddr_storage {
+  uint16_t ss_family;
+  uint8_t _ss_pad1[NGTCP2_SS_PAD1SIZE];
+  uint64_t _ss_align;
+  uint8_t _ss_pad2[NGTCP2_SS_PAD2SIZE];
+} ngtcp2_sockaddr_storage;
+
+#  undef NGTCP2_SS_PAD2SIZE
+#  undef NGTCP2_SS_PAD1SIZE
+#  undef NGTCP2_SS_ALIGNSIZE
+#  undef NGTCP2_SS_MAXSIZE
+
+typedef uint32_t ngtcp2_socklen;
+#else
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_sockaddr` is typedefed to struct sockaddr.  If
+ * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to
+ * the generic struct sockaddr defined in ngtcp2.h.
+ */
+typedef struct sockaddr ngtcp2_sockaddr;
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_sockaddr_storage` is typedefed to struct
+ * sockaddr_storage.  If :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is
+ * defined, it is typedefed to the generic struct sockaddr_storage
+ * defined in ngtcp2.h.
+ */
+typedef struct sockaddr_storage ngtcp2_sockaddr_storage;
+typedef struct sockaddr_in ngtcp2_sockaddr_in;
+/**
+ * @typedef
+ *
+ * :type:`ngtcp2_socklen` is typedefed to socklen_t.  If
+ * :macro:`NGTCP2_USE_GENERIC_SOCKADDR` is defined, it is typedefed to
+ * uint32_t.
+ */
+typedef socklen_t ngtcp2_socklen;
+#endif
+
+#if defined(NGTCP2_USE_GENERIC_SOCKADDR) ||                                    \
+    defined(NGTCP2_USE_GENERIC_IPV6_SOCKADDR)
+typedef struct ngtcp2_in6_addr {
+  uint8_t in6_addr[16];
+} ngtcp2_in6_addr;
+
+typedef struct ngtcp2_sockaddr_in6 {
+  uint16_t sin6_family;
+  uint16_t sin6_port;
+  uint32_t sin6_flowinfo;
+  ngtcp2_in6_addr sin6_addr;
+  uint32_t sin6_scope_id;
+} ngtcp2_sockaddr_in6;
+#else
+typedef struct sockaddr_in6 ngtcp2_sockaddr_in6;
+#endif
+
 /**
  * @struct
  *
@@ -1740,11 +2005,11 @@ typedef struct ngtcp2_addr {
    * :member:`addr` points to the buffer which contains endpoint
    * address.  It must not be ``NULL``.
    */
-  struct sockaddr *addr;
+  ngtcp2_sockaddr *addr;
   /**
    * :member:`addrlen` is the length of addr.
    */
-  size_t addrlen;
+  ngtcp2_socklen addrlen;
 } ngtcp2_addr;
 
 /**
@@ -1793,11 +2058,11 @@ typedef struct ngtcp2_path_storage {
   /**
    * :member:`local_addrbuf` is a buffer to store local address.
    */
-  struct sockaddr_storage local_addrbuf;
+  ngtcp2_sockaddr_storage local_addrbuf;
   /**
    * :member:`remote_addrbuf` is a buffer to store remote address.
    */
-  struct sockaddr_storage remote_addrbuf;
+  ngtcp2_sockaddr_storage remote_addrbuf;
 } ngtcp2_path_storage;
 
 /**
@@ -1884,7 +2149,7 @@ typedef struct ngtcp2_crypto_cipher_ctx {
  * :type:`ngtcp2_crypto_ctx` is a convenient structure to bind all
  * crypto related objects in one place.  Use
  * `ngtcp2_crypto_ctx_initial` to initialize this struct for Initial
- * packet encryption.  For Handshake and Short packets, use
+ * packet encryption.  For Handshake and 1RTT packets, use
  * `ngtcp2_crypto_ctx_tls`.
  */
 typedef struct ngtcp2_crypto_ctx {
@@ -2019,9 +2284,11 @@ ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
  * Negotiation packet has random type in wire format.  For
  * convenience, this function sets
  * :enum:`ngtcp2_pkt_type.NGTCP2_PKT_VERSION_NEGOTIATION` to
- * :member:`dest->type <ngtcp2_pkt_hd.type>`, and sets 0 to
- * :member:`dest->len <ngtcp2_pkt_hd.len>`.  Version Negotiation
- * packet occupies a single packet.
+ * :member:`dest->type <ngtcp2_pkt_hd.type>`, clears
+ * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` flag from :member:`dest->flags
+ * <ngtcp2_pkt_hd.flags>`, and sets 0 to :member:`dest->len
+ * <ngtcp2_pkt_hd.len>`.  Version Negotiation packet occupies a single
+ * packet.
  *
  * It stores the result in the object pointed by |dest|, and returns
  * the number of bytes decoded to read the packet header if it
@@ -2127,8 +2394,8 @@ typedef struct ngtcp2_conn ngtcp2_conn;
  * `ngtcp2_conn_submit_crypto_data` function.  Make sure that before
  * calling `ngtcp2_conn_submit_crypto_data` function, client
  * application must create initial packet protection keys and IVs, and
- * provide them to ngtcp2 library using `ngtcp2_conn_set_initial_key`
- * and
+ * provide them to ngtcp2 library using
+ * `ngtcp2_conn_install_initial_key`.
  *
  * This callback function must return 0 if it succeeds, or
  * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
@@ -2146,7 +2413,7 @@ typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data);
  * Initial packet from client.  An server application must implement
  * this callback, and generate initial keys and IVs for both
  * transmission and reception.  Install them using
- * `ngtcp2_conn_set_initial_key`.  |dcid| is the destination
+ * `ngtcp2_conn_install_initial_key`.  |dcid| is the destination
  * connection ID which client generated randomly.  It is used to
  * derive initial packet protection keys.
  *
@@ -2206,12 +2473,24 @@ typedef enum ngtcp2_crypto_level {
  *
  * The application should provide the given data to TLS stack.
  *
- * The callback function must return 0 if it succeeds.  If TLS stack
- * reported error, return :macro:`NGTCP2_ERR_CRYPTO`.  If application
- * encounters fatal error, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
- * which makes the library call return immediately.  If the other
- * value is returned, it is treated as
+ * The callback function must return 0 if it succeeds, or one of the
+ * following negative error codes:
+ *
+ * - :macro:`NGTCP2_ERR_CRYPTO`
+ * - :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM`
+ * - :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`
+ * - :macro:`NGTCP2_ERR_TRANSPORT_PARAM`
+ * - :macro:`NGTCP2_ERR_PROTO`
+ * - :macro:`NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE`
+ * - :macro:`NGTCP2_ERR_NOMEM`
+ * - :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ *
+ * If the other value is returned, it is treated as
  * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ *
+ * If application encounters fatal error, return
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
  */
 typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn,
                                        ngtcp2_crypto_level crypto_level,
@@ -2358,8 +2637,8 @@ typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead,
  * :macro:`NGTCP2_HP_SAMPLELEN` bytes available for convenience.
  *
  * The callback function must return 0 if it succeeds, or
- *  :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
- *  return immediately.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
+ * return immediately.
  */
 typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
                               const ngtcp2_crypto_cipher_ctx *hp_ctx,
@@ -2400,8 +2679,8 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
  *
  * :type:`ngtcp2_recv_stream_data` is invoked when stream data is
  * received.  The stream is specified by |stream_id|.  |flags| is the
- * bitwise-OR of zero or more of NGTCP2_STREAM_DATA_FLAG_*.  See
- * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE`.  If |flags| &
+ * bitwise-OR of zero or more of :macro:`NGTCP2_STREAM_DATA_FLAG_*
+ * <NGTCP2_STREAM_DATA_FLAG_NONE>`.  If |flags| &
  * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` is nonzero, this portion of
  * the data is the last data in this stream.  |offset| is the offset
  * where this data begins.  The library ensures that data is passed to
@@ -2464,9 +2743,9 @@ typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id,
  * :type:`ngtcp2_stream_close` is invoked when a stream is closed.
  * This callback is not called when QUIC connection is closed before
  * existing streams are closed.  |flags| is the bitwise-OR of zero or
- * more of NGTCP2_STREAM_CLOSE_FLAG_*.  See
- * :macro:`NGTCP2_STREAM_CLOSE_FLAG_NONE`.  |app_error_code| indicates
- * the error code of this closure if
+ * more of :macro:`NGTCP2_STREAM_CLOSE_FLAG_*
+ * <NGTCP2_STREAM_CLOSE_FLAG_NONE>`.  |app_error_code| indicates the
+ * error code of this closure if
  * :macro:`NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET` is set in
  * |flags|.  If it is not set, the stream was closed without any error
  * code, which generally means success.
@@ -2665,7 +2944,8 @@ typedef int (*ngtcp2_update_key)(
  * @macro
  *
  * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR` indicates the
- * validation involving server preferred address.
+ * validation involving server preferred address.  This flag is only
+ * set for client.
  */
 #define NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR 0x01
 
@@ -2674,9 +2954,9 @@ typedef int (*ngtcp2_update_key)(
  *
  * :type:`ngtcp2_path_validation` is a callback function which tells
  * the application the outcome of path validation.  |flags| is zero or
- * more of NGTCP2_PATH_VALIDATION_FLAG_*.  See
- * :macro:`NGTCP2_PATH_VALIDATION_FLAG_NONE`.  |path| is the path that
- * was validated.  If |res| is
+ * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_*
+ * <NGTCP2_PATH_VALIDATION_FLAG_NONE>`.  |path| is the path that was
+ * validated.  If |res| is
  * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`,
  * the path validation succeeded.  If |res| is
  * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_FAILURE`,
@@ -2829,7 +3109,7 @@ typedef void (*ngtcp2_delete_crypto_cipher_ctx)(
  *
  * :type:`ngtcp2_recv_datagram` is invoked when DATAGRAM frame is
  * received.  |flags| is bitwise-OR of zero or more of
- * NGTCP2_DATAGRAM_FLAG_*.  See :macro:`NGTCP2_DATAGRAM_FLAG_NONE`.
+ * :macro:`NGTCP2_DATAGRAM_FLAG_* <NGTCP2_DATAGRAM_FLAG_NONE>`.
  *
  * If :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` is set in |flags|, it
  * indicates that DATAGRAM frame was received in 0RTT packet and a
@@ -2906,6 +3186,27 @@ typedef int (*ngtcp2_stream_stop_sending)(ngtcp2_conn *conn, int64_t stream_id,
                                           void *user_data,
                                           void *stream_user_data);
 
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_version_negotiation` is invoked when the compatible
+ * version negotiation takes place.  For client, it is called when it
+ * sees a change in version field of a long header packet.  This
+ * callback function might be called multiple times for client.  For
+ * server, it is called once when the version is negotiated.
+ *
+ * The implementation of this callback must install new Initial keys
+ * for |version|.  Use `ngtcp2_conn_install_vneg_initial_key` to
+ * install keys.
+ *
+ * The callback function must return 0 if it succeeds.  Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_version_negotiation)(ngtcp2_conn *conn, uint32_t version,
+                                          const ngtcp2_cid *client_dcid,
+                                          void *user_data);
+
 #define NGTCP2_CALLBACKS_VERSION_V1 1
 #define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_VERSION_V1
 
@@ -3144,6 +3445,12 @@ typedef struct ngtcp2_callbacks {
    * optional.
    */
   ngtcp2_stream_stop_sending stream_stop_sending;
+  /**
+   * :member:`version_negotiation` is a callback function which is
+   * invoked when the compatible version negotiation takes place.
+   * This callback function must be specified.
+   */
+  ngtcp2_version_negotiation version_negotiation;
 } ngtcp2_callbacks;
 
 /**
@@ -3209,18 +3516,15 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry(
  * whether packet |pkt| of length |pktlen| from client is acceptable
  * for the very initial packet to a connection.
  *
- * If |dest| is not ``NULL`` and the function returns 0,
- * :macro:`NGTCP2_ERR_RETRY`, or
- * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`, the decoded packet header
- * is stored to the object pointed by |dest|.
+ * If |dest| is not ``NULL`` and the function returns 0, or
+ * :macro:`NGTCP2_ERR_RETRY`, the decoded packet header is stored to
+ * the object pointed by |dest|.
  *
  * This function returns 0 if it succeeds, or one of the following
  * negative error codes:
  *
  * :macro:`NGTCP2_ERR_RETRY`
  *     Retry packet should be sent.
- * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`
- *     Version Negotiation packet should be sent.
  * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
  *     The packet is not acceptable for the very first packet to a new
  *     connection; or it failed to parse the packet header.
@@ -3233,15 +3537,16 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
  *
  * `ngtcp2_conn_client_new` creates new :type:`ngtcp2_conn`, and
  * initializes it as client.  |dcid| is randomized destination
- * connection ID.  |scid| is source connection ID.  |version| is a
- * QUIC version to use.  |path| is the network path where this QUIC
- * connection is being established and must not be ``NULL``.
- * |callbacks|, |settings|, and |params| must not be ``NULL``, and the
- * function make a copy of each of them.  |params| is local QUIC
- * transport parameters and sent to a remote endpoint during
- * handshake.  |user_data| is the arbitrary pointer which is passed to
- * the user-defined callback functions.  If |mem| is ``NULL``, the
- * memory allocator returned by `ngtcp2_mem_default()` is used.
+ * connection ID.  |scid| is source connection ID.
+ * |client_chosen_version| is a QUIC version that a cilent chooses.
+ * |path| is the network path where this QUIC connection is being
+ * established and must not be ``NULL``.  |callbacks|, |settings|, and
+ * |params| must not be ``NULL``, and the function make a copy of each
+ * of them.  |params| is local QUIC transport parameters and sent to a
+ * remote endpoint during handshake.  |user_data| is the arbitrary
+ * pointer which is passed to the user-defined callback functions.  If
+ * |mem| is ``NULL``, the memory allocator returned by
+ * `ngtcp2_mem_default()` is used.
  *
  * This function returns 0 if it succeeds, or one of the following
  * negative error codes:
@@ -3251,11 +3556,11 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
  */
 NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned(
     ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
-    const ngtcp2_path *path, uint32_t version, int callbacks_version,
-    const ngtcp2_callbacks *callbacks, int settings_version,
-    const ngtcp2_settings *settings, int transport_params_version,
-    const ngtcp2_transport_params *params, const ngtcp2_mem *mem,
-    void *user_data);
+    const ngtcp2_path *path, uint32_t client_chosen_version,
+    int callbacks_version, const ngtcp2_callbacks *callbacks,
+    int settings_version, const ngtcp2_settings *settings,
+    int transport_params_version, const ngtcp2_transport_params *params,
+    const ngtcp2_mem *mem, void *user_data);
 
 /**
  * @function
@@ -3264,13 +3569,13 @@ NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned(
  * initializes it as server.  |dcid| is a destination connection ID.
  * |scid| is a source connection ID.  |path| is the network path where
  * this QUIC connection is being established and must not be ``NULL``.
- * |version| is a QUIC version to use.  |callbacks|, |settings|, and
- * |params| must not be ``NULL``, and the function make a copy of each
- * of them.  |params| is local QUIC transport parameters and sent to a
- * remote endpoint during handshake.  |user_data| is the arbitrary
- * pointer which is passed to the user-defined callback functions.  If
- * |mem| is ``NULL``, the memory allocator returned by
- * `ngtcp2_mem_default()` is used.
+ * |client_chosen_version| is a QUIC version that a client chooses.
+ * |callbacks|, |settings|, and |params| must not be ``NULL``, and the
+ * function make a copy of each of them.  |params| is local QUIC
+ * transport parameters and sent to a remote endpoint during
+ * handshake.  |user_data| is the arbitrary pointer which is passed to
+ * the user-defined callback functions.  If |mem| is ``NULL``, the
+ * memory allocator returned by `ngtcp2_mem_default()` is used.
  *
  * This function returns 0 if it succeeds, or one of the following
  * negative error codes:
@@ -3280,11 +3585,11 @@ NGTCP2_EXTERN int ngtcp2_conn_client_new_versioned(
  */
 NGTCP2_EXTERN int ngtcp2_conn_server_new_versioned(
     ngtcp2_conn **pconn, const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
-    const ngtcp2_path *path, uint32_t version, int callbacks_version,
-    const ngtcp2_callbacks *callbacks, int settings_version,
-    const ngtcp2_settings *settings, int transport_params_version,
-    const ngtcp2_transport_params *params, const ngtcp2_mem *mem,
-    void *user_data);
+    const ngtcp2_path *path, uint32_t client_chosen_version,
+    int callbacks_version, const ngtcp2_callbacks *callbacks,
+    int settings_version, const ngtcp2_settings *settings,
+    int transport_params_version, const ngtcp2_transport_params *params,
+    const ngtcp2_mem *mem, void *user_data);
 
 /**
  * @function
@@ -3300,8 +3605,8 @@ NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn);
  * `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of
  * length |pktlen| and processes it.  |path| is the network path the
  * packet is delivered and must not be ``NULL``.  |pi| is packet
- * metadata and must not be ``NULL``. This function performs QUIC
- * handshake as well.
+ * metadata and may be ``NULL``. This function performs QUIC handshake
+ * as well.
  *
  * This function must not be called from inside the callback
  * functions.
@@ -3339,15 +3644,17 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt_versioned(
 /**
  * @function
  *
- * `ngtcp2_conn_handshake_completed` tells |conn| that the QUIC
- * handshake has completed.
+ * `ngtcp2_conn_handshake_completed` tells |conn| that the TLS stack
+ * declares TLS handshake completion.  This does not mean QUIC
+ * handshake has completed.  The library needs extra conditions to be
+ * met.
  */
 NGTCP2_EXTERN void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn);
 
 /**
  * @function
  *
- * `ngtcp2_conn_get_handshake_completed` returns nonzero if handshake
+ * `ngtcp2_conn_get_handshake_completed` returns nonzero if QUIC handshake
  * has completed.
  */
 NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn);
@@ -3391,6 +3698,42 @@ NGTCP2_EXTERN int ngtcp2_conn_install_initial_key(
     const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv,
     const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen);
 
+/**
+ * @function
+ *
+ * `ngtcp2_conn_install_vneg_initial_key` installs packet protection
+ * keying materials for Initial packets on compatible version
+ * negotiation for |version|.  |rx_aead_ctx| is AEAD cipher context
+ * object and must be initialized with a decryption key.  |rx_iv| is
+ * IV of length |rx_ivlen| for decryption.  |rx_hp_ctx| is a packet
+ * header protection cipher context object for decryption.  Similarly,
+ * |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for encrypting outgoing
+ * packets and are the same length with the decryption counterpart .
+ * If they have already been set, they are overwritten.
+ *
+ * |ivlen| must be the minimum length of AEAD nonce, or 8 bytes if
+ * that is larger.
+ *
+ * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|,
+ * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|.
+ * :type:`ngtcp2_delete_crypto_aead_ctx` and
+ * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete
+ * these objects when they are no longer used.  If this function
+ * fails, the caller is responsible to delete them.
+ *
+ * This function returns 0 if it succeeds, or one of the following
+ * negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ *     Out of memory.
+ */
+NGTCP2_EXTERN int ngtcp2_conn_install_vneg_initial_key(
+    ngtcp2_conn *conn, uint32_t version,
+    const ngtcp2_crypto_aead_ctx *rx_aead_ctx, const uint8_t *rx_iv,
+    const ngtcp2_crypto_cipher_ctx *rx_hp_ctx,
+    const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv,
+    const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen);
+
 /**
  * @function
  *
@@ -3605,15 +3948,6 @@ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
 NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn,
                                             ngtcp2_tstamp ts);
 
-/**
- * @function
- *
- * `ngtcp2_conn_get_idle_expiry` returns the time when a connection
- * should be closed if it continues to be idle.  If idle timeout is
- * disabled, this function returns ``UINT64_MAX``.
- */
-NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn);
-
 /**
  * @function
  *
@@ -3887,6 +4221,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream_versioned(
  * pointed by |dest| of length |destlen|.  This function performs QUIC
  * handshake as well.
  *
+ * |destlen| should be at least
+ * :member:`ngtcp2_settings.max_udp_payload_size`.
+ *
  * Specifying -1 to |stream_id| means no new stream data to send.
  *
  * If |path| is not ``NULL``, this function stores the network path
@@ -3955,8 +4292,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream_versioned(
  *
  * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not
  * call other ngtcp2 API functions (application can still call
- * `ngtcp2_conn_write_connection_close` or
- * `ngtcp2_conn_write_application_close` to handle error from this
+ * `ngtcp2_conn_write_connection_close` to handle error from this
  * function).  Just keep calling `ngtcp2_conn_writev_stream`,
  * `ngtcp2_conn_write_pkt`, or `ngtcp2_conn_writev_datagram` until it
  * returns a positive number (which indicates a complete packet is
@@ -4040,6 +4376,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned(
  * |dest| of length |destlen|.  This function performs QUIC handshake
  * as well.
  *
+ * |destlen| should be at least
+ * :member:`ngtcp2_settings.max_udp_payload_size`.
+ *
  * For |path| and |pi| parameters, refer to
  * `ngtcp2_conn_writev_stream`.
  *
@@ -4086,8 +4425,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream_versioned(
  *
  * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not
  * call other ngtcp2 API functions (application can still call
- * `ngtcp2_conn_write_connection_close` or
- * `ngtcp2_conn_write_application_close` to handle error from this
+ * `ngtcp2_conn_write_connection_close` to handle error from this
  * function).  Just keep calling `ngtcp2_conn_writev_datagram`,
  * `ngtcp2_conn_writev_stream` or `ngtcp2_conn_write_pkt` until it
  * returns a positive number (which indicates a complete packet is
@@ -4125,88 +4463,6 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned(
     uint32_t flags, uint64_t dgram_id, const ngtcp2_vec *datav, size_t datavcnt,
     ngtcp2_tstamp ts);
 
-/**
- * @function
- *
- * `ngtcp2_conn_write_connection_close` writes a packet which contains
- * a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed by
- * |dest| whose capacity is |datalen|.
- *
- * If |path| is not ``NULL``, this function stores the network path
- * with which the packet should be sent.  Each addr field must point
- * to the buffer which should be at least ``sizeof(struct
- * sockaddr_storage)`` bytes long.  The assignment might not be done
- * if nothing is written to |dest|.
- *
- * If |pi| is not ``NULL``, this function stores packet metadata in it
- * if it succeeds.  The metadata includes ECN markings.
- *
- * This function must not be called from inside the callback
- * functions.
- *
- * At the moment, successful call to this function makes connection
- * close.  We may change this behaviour in the future to allow
- * graceful shutdown.
- *
- * :macro:`NGTCP2_ERR_NOMEM`
- *     Out of memory
- * :macro:`NGTCP2_ERR_NOBUF`
- *     Buffer is too small
- * :macro:`NGTCP2_ERR_INVALID_STATE`
- *     The current state does not allow sending CONNECTION_CLOSE.
- * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
- *     Packet number is exhausted, and cannot send any more packet.
- * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
- *     User callback failed
- */
-NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
-    ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
-    ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint64_t error_code,
-    const uint8_t *reason, size_t reasonlen, ngtcp2_tstamp ts);
-
-/**
- * @function
- *
- * `ngtcp2_conn_write_application_close` writes a packet which
- * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed
- * by |dest| whose capacity is |datalen|.
- *
- * If |path| is not ``NULL``, this function stores the network path
- * with which the packet should be sent.  Each addr field must point
- * to the buffer which should be at least ``sizeof(struct
- * sockaddr_storage)`` bytes long.  The assignment might not be done
- * if nothing is written to |dest|.
- *
- * If |pi| is not ``NULL``, this function stores packet metadata in it
- * if it succeeds.  The metadata includes ECN markings.
- *
- * If handshake has not been confirmed yet, CONNECTION_CLOSE (type
- * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written
- * instead.
- *
- * This function must not be called from inside the callback
- * functions.
- *
- * At the moment, successful call to this function makes connection
- * close.  We may change this behaviour in the future to allow
- * graceful shutdown.
- *
- * :macro:`NGTCP2_ERR_NOMEM`
- *     Out of memory
- * :macro:`NGTCP2_ERR_NOBUF`
- *     Buffer is too small
- * :macro:`NGTCP2_ERR_INVALID_STATE`
- *     The current state does not allow sending CONNECTION_CLOSE.
- * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
- *     Packet number is exhausted, and cannot send any more packet.
- * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
- *     User callback failed
- */
-NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_application_close_versioned(
-    ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
-    ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint64_t app_error_code,
-    const uint8_t *reason, size_t reasonlen, ngtcp2_tstamp ts);
-
 /**
  * @function
  *
@@ -4367,10 +4623,20 @@ typedef struct ngtcp2_cid_token {
 NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn,
                                                  ngtcp2_cid_token *dest);
 
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_client_chosen_version` returns the client chosen
+ * version.
+ */
+NGTCP2_EXTERN uint32_t ngtcp2_conn_get_client_chosen_version(ngtcp2_conn *conn);
+
 /**
  * @function
  *
  * `ngtcp2_conn_get_negotiated_version` returns the negotiated version.
+ *
+ * Until the version is negotiated, this function returns 0.
  */
 NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn);
 
@@ -4458,18 +4724,32 @@ NGTCP2_EXTERN void ngtcp2_conn_set_path_user_data(ngtcp2_conn *conn,
 /**
  * @function
  *
- * `ngtcp2_conn_get_path_max_udp_payload_size` returns the maximum
- * outgoing UDP payload size for the current path.
+ * `ngtcp2_conn_get_path` returns the current path.
  */
-NGTCP2_EXTERN size_t
-ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn);
+NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn);
 
 /**
  * @function
  *
- * `ngtcp2_conn_get_path` returns the current path.
+ * `ngtcp2_conn_get_max_udp_payload_size` returns the maximum UDP
+ * payload size that this local endpoint would send.  This is the
+ * value of :member:`ngtcp2_settings.max_udp_payload_size` that is
+ * passed to `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`.
  */
-NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn);
+NGTCP2_EXTERN size_t ngtcp2_conn_get_max_udp_payload_size(ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_path_max_udp_payload_size` returns the maximum UDP
+ * payload size for the current path.  If
+ * :member:`ngtcp2_settings.no_udp_payload_size_shaping` is set to
+ * nonzero, this function is equivalent to
+ * `ngtcp2_conn_get_max_udp_payload_size`.  Otherwise, it returns the
+ * maximum UDP payload size that is probed for the current path.
+ */
+NGTCP2_EXTERN size_t
+ngtcp2_conn_get_path_max_udp_payload_size(ngtcp2_conn *conn);
 
 /**
  * @function
@@ -4556,6 +4836,15 @@ NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn);
  */
 NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn);
 
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_cwnd_left` returns the cwnd minus the number of
+ * bytes in flight on the current path.  If the former is smaller than
+ * the latter, this function returns 0.
+ */
+NGTCP2_EXTERN uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn);
+
 /**
  * @function
  *
@@ -4580,7 +4869,7 @@ ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn);
 /**
  * @function
  *
- * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/Short packet
+ * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/1RTT packet
  * encryption.  The passed data will be passed to
  * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and
  * :type:`ngtcp2_hp_mask` callbacks.
@@ -4629,7 +4918,7 @@ ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, const ngtcp2_crypto_aead *aead,
  * @function
  *
  * `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx`
- * object for Handshake/Short packet encryption.
+ * object for Handshake/1RTT packet encryption.
  */
 NGTCP2_EXTERN const ngtcp2_crypto_ctx *
 ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn);
@@ -4672,6 +4961,18 @@ typedef enum ngtcp2_connection_close_error_code_type {
    * indicates the error code is application error code.
    */
   NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION,
+  /**
+   * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION`
+   * is a special case of QUIC transport error, and it indicates that
+   * client receives Version Negotiation packet.
+   */
+  NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION,
+  /**
+   * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE`
+   * is a special case of QUIC transport error, and it indicates that
+   * connection is closed because of idle timeout.
+   */
+  NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE
 } ngtcp2_connection_close_error_code_type;
 
 /**
@@ -4681,14 +4982,14 @@ typedef enum ngtcp2_connection_close_error_code_type {
  * error code, its type, and the optional reason phrase.
  */
 typedef struct ngtcp2_connection_close_error {
-  /**
-   * :member:`error_code` is the error code for connection closure.
-   */
-  uint64_t error_code;
   /**
    * :member:`type` is the type of :member:`error_code`.
    */
   ngtcp2_connection_close_error_code_type type;
+  /**
+   * :member:`error_code` is the error code for connection closure.
+   */
+  uint64_t error_code;
   /**
    * :member:`frame_type` is the type of QUIC frame which triggers
    * this connection error.  This field is set to 0 if the frame type
@@ -4697,8 +4998,9 @@ typedef struct ngtcp2_connection_close_error {
   uint64_t frame_type;
   /**
    * :member:`reason` points to the buffer which contains a reason
-   * phrase.  It may be NULL if there is no reason phrase.  It is
-   * truncated to 1024 bytes.
+   * phrase.  It may be NULL if there is no reason phrase.  If it is
+   * received from a remote endpoint, it is truncated to at most 1024
+   * bytes.
    */
   uint8_t *reason;
   /**
@@ -4708,6 +5010,155 @@ typedef struct ngtcp2_connection_close_error {
   size_t reasonlen;
 } ngtcp2_connection_close_error;
 
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_default` initializes |ccerr| with
+ * the default values.  It sets the following fields:
+ *
+ * - :member:`type <ngtcp2_connection_close_error.type>` =
+ *   :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`
+ * - :member:`error_code <ngtcp2_connection_close_error.error_code>` =
+ *   :macro:`NGTCP2_NO_ERROR`.
+ * - :member:`frame_type <ngtcp2_connection_close_error.frame_type>` =
+ *   0
+ * - :member:`reason <ngtcp2_connection_close_error.reason>` = NULL
+ * - :member:`reasonlen <ngtcp2_connection_close_error.reasonlen>` = 0
+ */
+NGTCP2_EXTERN void
+ngtcp2_connection_close_error_default(ngtcp2_connection_close_error *ccerr);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_transport_error` sets
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to |error_code|.
+ * |reason| is the reason phrase of length |reasonlen|.  This function
+ * does not make a copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error(
+    ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+    const uint8_t *reason, size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_transport_error_liberr` sets
+ * type and error_code based on |liberr|.
+ *
+ * If |liberr| is :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION`,
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` is set
+ * to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_VERSION_NEGOTIATION`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to
+ * :macro:`NGTCP2_NO_ERROR`.  If |liberr| is
+ * :macro:`NGTCP2_ERR_IDLE_CLOSE`, :member:`ccerr->type
+ * <ngtcp2_connection_close_error.type>` is set to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT_IDLE_CLOSE`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to
+ * :macro:`NGTCP2_NO_ERROR`.  Otherwise, :member:`ccerr->type
+ * <ngtcp2_connection_close_error.type>` is set to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` is set to an error code
+ * inferred by |liberr| (see
+ * `ngtcp2_err_infer_quic_transport_error_code`).  |reason| is the
+ * reason phrase of length |reasonlen|.  This function does not make a
+ * copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_liberr(
+    ngtcp2_connection_close_error *ccerr, int liberr, const uint8_t *reason,
+    size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_transport_error_tls_alert` sets
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to bitwise-OR of
+ * :macro:`NGTCP2_CRYPTO_ERROR` and |tls_alert|.  |reason| is the
+ * reason phrase of length |reasonlen|.  This function does not make a
+ * copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_transport_error_tls_alert(
+    ngtcp2_connection_close_error *ccerr, uint8_t tls_alert,
+    const uint8_t *reason, size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_connection_close_error_set_application_error` sets
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` to
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`,
+ * and :member:`ccerr->error_code
+ * <ngtcp2_connection_close_error.error_code>` to |error_code|.
+ * |reason| is the reason phrase of length |reasonlen|.  This function
+ * does not make a copy of the reason phrase.
+ */
+NGTCP2_EXTERN void ngtcp2_connection_close_error_set_application_error(
+    ngtcp2_connection_close_error *ccerr, uint64_t error_code,
+    const uint8_t *reason, size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_connection_close` writes a packet which contains
+ * CONNECTION_CLOSE frame(s) (type 0x1c or 0x1d) in the buffer pointed
+ * by |dest| whose capacity is |destlen|.
+ *
+ * For client, |destlen| should be at least
+ * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`.
+ *
+ * If |path| is not ``NULL``, this function stores the network path
+ * with which the packet should be sent.  Each addr field must point
+ * to the buffer which should be at least ``sizeof(struct
+ * sockaddr_storage)`` bytes long.  The assignment might not be done
+ * if nothing is written to |dest|.
+ *
+ * If |pi| is not ``NULL``, this function stores packet metadata in it
+ * if it succeeds.  The metadata includes ECN markings.
+ *
+ * If :member:`ccerr->type <ngtcp2_connection_close_error.type>` ==
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT`,
+ * this function sends CONNECTION_CLOSE (type 0x1c) frame.  If
+ * :member:`ccerr->type <ngtcp2_connection_close_error.type>` ==
+ * :enum:`ngtcp2_connection_close_error_code_type.NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION`,
+ * it sends CONNECTION_CLOSE (type 0x1d) frame.  Otherwise, it does
+ * not produce any data, and returns 0.
+ *
+ * This function must not be called from inside the callback
+ * functions.
+ *
+ * At the moment, successful call to this function makes connection
+ * close.  We may change this behaviour in the future to allow
+ * graceful shutdown.
+ *
+ * This function returns the number of bytes written in |dest| if it
+ * succeeds, or one of the following negative error codes:
+ *
+ * :macro:`NGTCP2_ERR_NOMEM`
+ *     Out of memory
+ * :macro:`NGTCP2_ERR_NOBUF`
+ *     Buffer is too small
+ * :macro:`NGTCP2_ERR_INVALID_STATE`
+ *     The current state does not allow sending CONNECTION_CLOSE.
+ * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED`
+ *     Packet number is exhausted, and cannot send any more packet.
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
+ *     User callback failed
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close_versioned(
+    ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+    ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen,
+    const ngtcp2_connection_close_error *ccerr, ngtcp2_tstamp ts);
+
 /**
  * @function
  *
@@ -4819,8 +5270,8 @@ NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr);
  * returns |dest|.
  */
 NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest,
-                                            const struct sockaddr *addr,
-                                            size_t addrlen);
+                                            const ngtcp2_sockaddr *addr,
+                                            ngtcp2_socklen addrlen);
 
 /**
  * @function
@@ -4833,8 +5284,8 @@ NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest,
  * capacity to store the copy.
  */
 NGTCP2_EXTERN void ngtcp2_addr_copy_byte(ngtcp2_addr *dest,
-                                         const struct sockaddr *addr,
-                                         size_t addrlen);
+                                         const ngtcp2_sockaddr *addr,
+                                         ngtcp2_socklen addrlen);
 
 /**
  * @function
@@ -4843,10 +5294,10 @@ NGTCP2_EXTERN void ngtcp2_addr_copy_byte(ngtcp2_addr *dest,
  * arguments.  This function copies |local_addr| and |remote_addr|.
  */
 NGTCP2_EXTERN void ngtcp2_path_storage_init(ngtcp2_path_storage *ps,
-                                            const struct sockaddr *local_addr,
-                                            size_t local_addrlen,
-                                            const struct sockaddr *remote_addr,
-                                            size_t remote_addrlen,
+                                            const ngtcp2_sockaddr *local_addr,
+                                            ngtcp2_socklen local_addrlen,
+                                            const ngtcp2_sockaddr *remote_addr,
+                                            ngtcp2_socklen remote_addrlen,
                                             void *user_data);
 
 /**
@@ -4870,8 +5321,7 @@ NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps);
  *   :macro:`NGTCP2_DEFAULT_INITIAL_RTT`
  * * :type:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2
  * * :type:`max_udp_payload_size
- *   <ngtcp2_settings.max_udp_payload_size>` =
- *   :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`
+ *   <ngtcp2_settings.max_udp_payload_size>` = 1452
  * * :type:`handshake_timeout <ngtcp2_settings.handshake_timeout>` =
  *   :macro:`NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT`.
  */
@@ -4951,11 +5401,12 @@ typedef struct ngtcp2_info {
 /**
  * @function
  *
- * Returns a pointer to a ngtcp2_info struct with version information
- * about the run-time library in use.  The |least_version| argument
- * can be set to a 24 bit numerical value for the least accepted
- * version number and if the condition is not met, this function will
- * return a ``NULL``.  Pass in 0 to skip the version checking.
+ * `ngtcp2_version` returns a pointer to a ngtcp2_info struct with
+ * version information about the run-time library in use.  The
+ * |least_version| argument can be set to a 24 bit numerical value for
+ * the least accepted version number and if the condition is not met,
+ * this function will return a ``NULL``.  Pass in 0 to skip the
+ * version checking.
  */
 NGTCP2_EXTERN const ngtcp2_info *ngtcp2_version(int least_version);
 
@@ -4985,6 +5436,41 @@ NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src);
  */
 NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b);
 
+/**
+ * @function
+ *
+ * `ngtcp2_is_supported_version` returns nonzero if the library supports
+ * QUIC version |version|.
+ */
+NGTCP2_EXTERN int ngtcp2_is_supported_version(uint32_t version);
+
+/*
+ * @function
+ *
+ * `ngtcp2_is_reserved_version` returns nonzero if |version| is a
+ * reserved version.
+ */
+NGTCP2_EXTERN int ngtcp2_is_reserved_version(uint32_t version);
+
+/**
+ * @function
+ *
+ * `ngtcp2_select_version` selects and returns a version from the
+ * version set |offered_versions| of |offered_versionslen| elements.
+ * |preferred_versions| of |preferred_versionslen| elements specifies
+ * the preference of versions, which is sorted in the order of
+ * preference.  All versions included in |preferred_versions| must be
+ * supported by the library, that is, passing a version to
+ * `ngtcp2_is_supported_version` must return nonzero.  This function
+ * is intended to be used by client when it receives Version
+ * Negotiation packet.  If no version is selected, this function
+ * returns 0.
+ */
+NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions,
+                                             size_t preferred_versionslen,
+                                             const uint32_t *offered_versions,
+                                             size_t offered_versionslen);
+
 /*
  * Versioned function wrappers
  */
@@ -5045,21 +5531,10 @@ NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b);
  * struct version.
  */
 #define ngtcp2_conn_write_connection_close(CONN, PATH, PI, DEST, DESTLEN,      \
-                                           ERROR_CODE, REASON, REASONLEN, TS)  \
+                                           CCERR, TS)                          \
   ngtcp2_conn_write_connection_close_versioned(                                \
       (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN),        \
-      (ERROR_CODE), (REASON), (REASONLEN), (TS))
-
-/*
- * `ngtcp2_conn_write_application_close` is a wrapper around
- * `ngtcp2_conn_write_application_close_versioned` to set the correct
- * struct version.
- */
-#define ngtcp2_conn_write_application_close(                                   \
-    CONN, PATH, PI, DEST, DESTLEN, APP_ERROR_CODE, REASON, REASONLEN, TS)      \
-  ngtcp2_conn_write_application_close_versioned(                               \
-      (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (DEST), (DESTLEN),        \
-      (APP_ERROR_CODE), (REASON), (REASONLEN), (TS))
+      (CCERR), (TS))
 
 /*
  * `ngtcp2_encode_transport_params` is a wrapper around
@@ -5173,6 +5648,10 @@ NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b);
 #define ngtcp2_settings_default(SETTINGS)                                      \
   ngtcp2_settings_default_versioned(NGTCP2_SETTINGS_VERSION, (SETTINGS))
 
+#ifdef WIN32
+#  pragma warning(pop)
+#endif
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h b/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h
index f364bceabd..e21165048d 100644
--- a/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h
+++ b/src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h
@@ -59,15 +59,6 @@ extern "C" {
  */
 #define NGTCP2_CRYPTO_INITIAL_IVLEN 12
 
-/**
- * @function
- *
- * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet
- * encryption and decryption.
- */
-NGTCP2_EXTERN ngtcp2_crypto_ctx *
-ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx);
-
 /**
  * @function
  *
@@ -96,33 +87,6 @@ NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
 NGTCP2_EXTERN ngtcp2_crypto_ctx *
 ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, void *tls_native_handle);
 
-/**
- * @function
- *
- * `ngtcp2_crypto_aead_init` initializes |aead| with the provided
- * |aead_native_handle| which is an underlying AEAD object.
- *
- * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be
- * a pointer to EVP_CIPHER.
- *
- * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be
- * gnutls_cipher_algorithm_t casted to ``void *``.
- *
- * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must
- * be a pointer to EVP_AEAD.
- */
-NGTCP2_EXTERN ngtcp2_crypto_aead *
-ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, void *aead_native_handle);
-
-/**
- * @function
- *
- * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher
- * AEAD_AES_128_GCM for Retry packet integrity protection.
- */
-NGTCP2_EXTERN ngtcp2_crypto_aead *
-ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead);
-
 /**
  * @function
  *
@@ -253,31 +217,6 @@ typedef enum ngtcp2_crypto_side {
 NGTCP2_EXTERN size_t
 ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead);
 
-/**
- * @function
- *
- * `ngtcp2_crypto_derive_packet_protection_key` derives packet
- * protection key.  This function writes packet protection key into
- * the buffer pointed by |key|.  The length of derived key is
- * `ngtcp2_crypto_aead_keylen(aead) <ngtcp2_crypto_aead_keylen>`
- * bytes.  |key| must have enough capacity to store the key.  This
- * function writes packet protection IV into |iv|.  The length of
- * derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead)
- * <ngtcp2_crypto_packet_protection_ivlen>` bytes.  |iv| must have
- * enough capacity to store the IV.
- *
- * If |hp| is not NULL, this function also derives packet header
- * protection key and writes the key into the buffer pointed by |hp|.
- * The length of derived key is `ngtcp2_crypto_aead_keylen(aead)
- * <ngtcp2_crypto_aead_keylen>` bytes.  |hp|, if not NULL, must have
- * enough capacity to store the key.
- *
- * This function returns 0 if it succeeds, or -1.
- */
-NGTCP2_EXTERN int ngtcp2_crypto_derive_packet_protection_key(
-    uint8_t *key, uint8_t *iv, uint8_t *hp, const ngtcp2_crypto_aead *aead,
-    const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen);
-
 /**
  * @function
  *
@@ -703,18 +642,18 @@ NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
  * successfully generated token starts with
  * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY`.  |secret| of length
  * |secretlen| is an initial keying material to generate keys to
- * encrypt the token.  |remote_addr| of length |remote_addrlen| is an
- * address of client.  |retry_scid| is a Source Connection ID chosen
- * by server and set in Retry packet.  |odcid| is a Destination
- * Connection ID in Initial packet sent by client.  |ts| is the
- * timestamp when the token is generated.
+ * encrypt the token.  |version| is QUIC version.  |remote_addr| of
+ * length |remote_addrlen| is an address of client.  |retry_scid| is a
+ * Source Connection ID chosen by server and set in Retry packet.
+ * |odcid| is a Destination Connection ID in Initial packet sent by
+ * client.  |ts| is the timestamp when the token is generated.
  *
  * This function returns the length of generated token if it succeeds,
  * or -1.
  */
 NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
-    uint8_t *token, const uint8_t *secret, size_t secretlen,
-    const struct sockaddr *remote_addr, size_t remote_addrlen,
+    uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version,
+    const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
     const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts);
 
 /**
@@ -723,22 +662,23 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
  * `ngtcp2_crypto_verify_retry_token` verifies Retry token stored in
  * the buffer pointed by |token| of length |tokenlen|.  |secret| of
  * length |secretlen| is an initial keying material to generate keys
- * to decrypt the token.  |remote_addr| of length |remote_addrlen| is
- * an address of client.  |dcid| is a Destination Connection ID in
- * Initial packet sent by client.  |timeout| is the period during
- * which the token is valid.  |ts| is the current timestamp.  When
- * validation succeeds, the extracted Destination Connection ID (which
- * is the Destination Connection ID in Initial packet sent by client
- * that triggered Retry packet) is stored to the buffer pointed by
- * |odcid|.
+ * to decrypt the token.  |version| is QUIC version of the Initial
+ * packet that contains this token.  |remote_addr| of length
+ * |remote_addrlen| is an address of client.  |dcid| is a Destination
+ * Connection ID in Initial packet sent by client.  |timeout| is the
+ * period during which the token is valid.  |ts| is the current
+ * timestamp.  When validation succeeds, the extracted Destination
+ * Connection ID (which is the Destination Connection ID in Initial
+ * packet sent by client that triggered Retry packet) is stored to the
+ * buffer pointed by |odcid|.
  *
  * This function returns 0 if it succeeds, or -1.
  */
 NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token(
     ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
-    const uint8_t *secret, size_t secretlen, const struct sockaddr *remote_addr,
-    socklen_t remote_addrlen, const ngtcp2_cid *dcid, ngtcp2_duration timeout,
-    ngtcp2_tstamp ts);
+    const uint8_t *secret, size_t secretlen, uint32_t version,
+    const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+    const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts);
 
 /**
  * @function
@@ -759,7 +699,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token(
  */
 NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
     uint8_t *token, const uint8_t *secret, size_t secretlen,
-    const struct sockaddr *remote_addr, size_t remote_addrlen,
+    const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
     ngtcp2_tstamp ts);
 
 /**
@@ -776,8 +716,8 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
  */
 NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token(
     const uint8_t *token, size_t tokenlen, const uint8_t *secret,
-    size_t secretlen, const struct sockaddr *remote_addr,
-    socklen_t remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts);
+    size_t secretlen, const ngtcp2_sockaddr *remote_addr,
+    ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts);
 
 /**
  * @function
@@ -897,6 +837,21 @@ NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn,
                                                            uint8_t *data,
                                                            void *user_data);
 
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_version_negotiation_cb` installs Initial keys for
+ * |version| which is negotiated or being negotiated.  |client_dcid|
+ * is the destination connection ID in first Initial packet of client.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.version_negotiation` field.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version,
+                                     const ngtcp2_cid *client_dcid,
+                                     void *user_data);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/contrib/libngtcp2/ngtcp2/version.h b/src/contrib/libngtcp2/ngtcp2/version.h
index 7821bd0fb5..c6c1d26c70 100644
--- a/src/contrib/libngtcp2/ngtcp2/version.h
+++ b/src/contrib/libngtcp2/ngtcp2/version.h
@@ -36,7 +36,7 @@
  *
  * Version number of the ngtcp2 library release.
  */
-#define NGTCP2_VERSION "0.1.0"
+#define NGTCP2_VERSION "0.5.0"
 
 /**
  * @macro
@@ -46,6 +46,6 @@
  * number, 8 bits for minor and 8 bits for patch. Version 1.2.3
  * becomes 0x010203.
  */
-#define NGTCP2_VERSION_NUM 0x000100
+#define NGTCP2_VERSION_NUM 0x000500
 
 #endif /* VERSION_H */
-- 
GitLab