From 6ec417c022ee2e0cfeac96090ac2778fc68fa23d Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Tue, 25 Aug 2020 19:12:04 +0900
Subject: [PATCH] Add ECN

---
 examples/client.cc           |  72 ++-
 examples/client.h            |   7 +-
 examples/h09client.cc        |  71 ++-
 examples/h09client.h         |   7 +-
 examples/h09server.cc        | 140 ++++--
 examples/h09server.h         |  13 +-
 examples/server.cc           | 141 ++++--
 examples/server.h            |  13 +-
 examples/shared.cc           |  65 +++
 examples/shared.h            |  12 +
 lib/includes/ngtcp2/ngtcp2.h |  36 +-
 lib/ngtcp2_conn.c            | 285 ++++++++---
 lib/ngtcp2_conn.h            |  54 ++-
 lib/ngtcp2_log.c             |   8 +
 lib/ngtcp2_pkt.c             |  32 +-
 lib/ngtcp2_pkt.h             |   9 +-
 lib/ngtcp2_qlog.c            |  17 +
 lib/ngtcp2_rtb.c             |  73 ++-
 lib/ngtcp2_rtb.h             |   6 +-
 tests/main.c                 |   4 +
 tests/ngtcp2_conn_test.c     | 907 +++++++++++++++++++++++------------
 tests/ngtcp2_conn_test.h     |   1 +
 tests/ngtcp2_pkt_test.c      |  79 +++
 tests/ngtcp2_pkt_test.h      |   1 +
 tests/ngtcp2_rtb_test.c      |  10 +-
 25 files changed, 1529 insertions(+), 534 deletions(-)

diff --git a/examples/client.cc b/examples/client.cc
index 4a111232..4b6623d5 100644
--- a/examples/client.cc
+++ b/examples/client.cc
@@ -371,6 +371,7 @@ void siginthandler(struct ev_loop *loop, ev_signal *w, int revents) {
 Client::Client(struct ev_loop *loop, SSL_CTX *ssl_ctx)
     : local_addr_{},
       remote_addr_{},
+      ecn_(0),
       max_pktlen_(0),
       loop_(loop),
       ssl_ctx_(ssl_ctx),
@@ -1015,12 +1016,13 @@ int Client::init(int fd, const Address &local_addr, const Address &remote_addr,
   return 0;
 }
 
-int Client::feed_data(const sockaddr *sa, socklen_t salen, uint8_t *data,
+int Client::feed_data(const sockaddr *sa, socklen_t salen,
+                      const ngtcp2_pkt_info *pi, uint8_t *data,
                       size_t datalen) {
   auto path =
       ngtcp2_path{{local_addr_.len, const_cast<sockaddr *>(&local_addr_.su.sa)},
                   {salen, const_cast<sockaddr *>(sa)}};
-  if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, data, datalen,
+  if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, pi, data, datalen,
                                      util::timestamp(loop_));
       rv != 0) {
     std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
@@ -1048,13 +1050,26 @@ int Client::feed_data(const sockaddr *sa, socklen_t salen, uint8_t *data,
 int Client::on_read() {
   std::array<uint8_t, 65536> buf;
   sockaddr_union su;
-  socklen_t addrlen;
   size_t pktcnt = 0;
+  ngtcp2_pkt_info pi;
+
+  iovec msg_iov;
+  msg_iov.iov_base = buf.data();
+  msg_iov.iov_len = buf.size();
+
+  msghdr msg{};
+  msg.msg_name = &su;
+  msg.msg_iov = &msg_iov;
+  msg.msg_iovlen = 1;
+
+  std::array<uint8_t, CMSG_SPACE(sizeof(uint8_t))> msg_ctrl;
+  msg.msg_control = msg_ctrl.data();
 
   for (;;) {
-    addrlen = sizeof(su);
-    auto nread =
-        recvfrom(fd_, buf.data(), buf.size(), MSG_DONTWAIT, &su.sa, &addrlen);
+    msg.msg_namelen = sizeof(su);
+    msg.msg_controllen = msg_ctrl.size();
+
+    auto nread = recvmsg(fd_, &msg, MSG_DONTWAIT);
 
     if (nread == -1) {
       if (errno != EAGAIN && errno != EWOULDBLOCK) {
@@ -1063,10 +1078,13 @@ int Client::on_read() {
       break;
     }
 
+    pi.ecn = msghdr_get_ecn(&msg, su.storage.ss_family);
+
     if (!config.quiet) {
       std::cerr << "Received packet: local="
                 << util::straddr(&local_addr_.su.sa, local_addr_.len)
-                << " remote=" << util::straddr(&su.sa, addrlen) << " " << nread
+                << " remote=" << util::straddr(&su.sa, msg.msg_namelen)
+                << " ecn=0x" << std::hex << pi.ecn << std::dec << " " << nread
                 << " bytes" << std::endl;
     }
 
@@ -1077,7 +1095,7 @@ int Client::on_read() {
       break;
     }
 
-    if (feed_data(&su.sa, addrlen, buf.data(), nread) != 0) {
+    if (feed_data(&su.sa, msg.msg_namelen, &pi, buf.data(), nread) != 0) {
       return -1;
     }
 
@@ -1187,8 +1205,10 @@ int Client::write_streams() {
       flags |= NGTCP2_WRITE_STREAM_FLAG_FIN;
     }
 
+    ngtcp2_pkt_info pi;
+
     auto nwrite = ngtcp2_conn_writev_stream(
-        conn_, &path.path, sendbuf_.wpos(), max_pktlen_, &ndatalen, flags,
+        conn_, &path.path, &pi, sendbuf_.wpos(), max_pktlen_, &ndatalen, flags,
         stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt,
         util::timestamp(loop_));
     if (nwrite < 0) {
@@ -1237,7 +1257,7 @@ int Client::write_streams() {
 
     sendbuf_.push(nwrite);
 
-    update_remote_addr(&path.path.remote);
+    update_remote_addr(&path.path.remote, &pi);
     reset_idle_timer();
 
     if (auto rv = send_packet(); rv != NETWORK_ERR_OK) {
@@ -1356,6 +1376,8 @@ int create_sock(Address &remote_addr, const char *addr, const char *port) {
     return -1;
   }
 
+  fd_set_recv_ecn(fd, rp->ai_family);
+
   remote_addr.len = rp->ai_addrlen;
   memcpy(&remote_addr.su, rp->ai_addr, rp->ai_addrlen);
 
@@ -1478,9 +1500,15 @@ void Client::start_delay_stream_timer() {
   ev_timer_start(loop_, &delay_stream_timer_);
 }
 
-void Client::update_remote_addr(const ngtcp2_addr *addr) {
+void Client::update_remote_addr(const ngtcp2_addr *addr,
+                                const ngtcp2_pkt_info *pi) {
   remote_addr_.len = addr->addrlen;
   memcpy(&remote_addr_.su, addr->addr, addr->addrlen);
+  if (pi) {
+    ecn_ = pi->ecn;
+  } else {
+    ecn_ = 0;
+  }
 }
 
 int Client::send_packet() {
@@ -1492,11 +1520,22 @@ int Client::send_packet() {
     return NETWORK_ERR_OK;
   }
 
+  iovec msg_iov;
+  msg_iov.iov_base = const_cast<uint8_t *>(sendbuf_.rpos());
+  msg_iov.iov_len = sendbuf_.size();
+
+  msghdr msg{};
+  msg.msg_name = const_cast<sockaddr *>(&remote_addr_.su.sa);
+  msg.msg_namelen = remote_addr_.len;
+  msg.msg_iov = &msg_iov;
+  msg.msg_iovlen = 1;
+
+  fd_set_ecn(fd_, remote_addr_.su.storage.ss_family, ecn_);
+
   ssize_t nwrite = 0;
 
   do {
-    nwrite = sendto(fd_, sendbuf_.rpos(), sendbuf_.size(), MSG_DONTWAIT,
-                    &remote_addr_.su.sa, remote_addr_.len);
+    nwrite = sendmsg(fd_, &msg, MSG_DONTWAIT);
   } while (nwrite == -1 && errno == EINTR);
 
   if (nwrite == -1) {
@@ -1514,8 +1553,9 @@ int Client::send_packet() {
     std::cerr << "Sent packet: local="
               << util::straddr(&local_addr_.su.sa, local_addr_.len)
               << " remote="
-              << util::straddr(&remote_addr_.su.sa, remote_addr_.len) << " "
-              << nwrite << " bytes" << std::endl;
+              << util::straddr(&remote_addr_.su.sa, remote_addr_.len)
+              << " ecn=0x" << std::hex << ecn_ << std::dec << " " << nwrite
+              << " bytes" << std::endl;
   }
 
   return NETWORK_ERR_OK;
@@ -1557,7 +1597,7 @@ int Client::handle_error() {
     sendbuf_.push(n);
   }
 
-  update_remote_addr(&path.path.remote);
+  update_remote_addr(&path.path.remote, nullptr);
 
   return send_packet();
 }
diff --git a/examples/client.h b/examples/client.h
index db3b2660..457040d6 100644
--- a/examples/client.h
+++ b/examples/client.h
@@ -213,8 +213,8 @@ public:
   int on_read();
   int on_write();
   int write_streams();
-  int feed_data(const sockaddr *sa, socklen_t salen, uint8_t *data,
-                size_t datalen);
+  int feed_data(const sockaddr *sa, socklen_t salen, const ngtcp2_pkt_info *pi,
+                uint8_t *data, size_t datalen);
   int handle_expiry();
   void schedule_retransmit();
   int handshake_completed();
@@ -226,7 +226,7 @@ public:
                        size_t datalen);
 
   ngtcp2_conn *conn() const;
-  void update_remote_addr(const ngtcp2_addr *addr);
+  void update_remote_addr(const ngtcp2_addr *addr, const ngtcp2_pkt_info *pi);
   int send_packet();
   void remove_tx_crypto_data(ngtcp2_crypto_level crypto_level, uint64_t offset,
                              uint64_t datalen);
@@ -275,6 +275,7 @@ public:
 private:
   Address local_addr_;
   Address remote_addr_;
+  unsigned int ecn_;
   size_t max_pktlen_;
   ev_io wev_;
   ev_io rev_;
diff --git a/examples/h09client.cc b/examples/h09client.cc
index 02c40d38..d1b6adbc 100644
--- a/examples/h09client.cc
+++ b/examples/h09client.cc
@@ -367,6 +367,7 @@ void siginthandler(struct ev_loop *loop, ev_signal *w, int revents) {
 Client::Client(struct ev_loop *loop, SSL_CTX *ssl_ctx)
     : local_addr_{},
       remote_addr_{},
+      ecn_(0),
       max_pktlen_(0),
       loop_(loop),
       ssl_ctx_(ssl_ctx),
@@ -994,12 +995,13 @@ int Client::init(int fd, const Address &local_addr, const Address &remote_addr,
   return 0;
 }
 
-int Client::feed_data(const sockaddr *sa, socklen_t salen, uint8_t *data,
+int Client::feed_data(const sockaddr *sa, socklen_t salen,
+                      const ngtcp2_pkt_info *pi, uint8_t *data,
                       size_t datalen) {
   auto path =
       ngtcp2_path{{local_addr_.len, const_cast<sockaddr *>(&local_addr_.su.sa)},
                   {salen, const_cast<sockaddr *>(sa)}};
-  if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, data, datalen,
+  if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, pi, data, datalen,
                                      util::timestamp(loop_));
       rv != 0) {
     std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
@@ -1027,13 +1029,26 @@ int Client::feed_data(const sockaddr *sa, socklen_t salen, uint8_t *data,
 int Client::on_read() {
   std::array<uint8_t, 65536> buf;
   sockaddr_union su;
-  socklen_t addrlen;
   size_t pktcnt = 0;
+  ngtcp2_pkt_info pi;
+
+  iovec msg_iov;
+  msg_iov.iov_base = buf.data();
+  msg_iov.iov_len = buf.size();
+
+  msghdr msg{};
+  msg.msg_name = &su;
+  msg.msg_iov = &msg_iov;
+  msg.msg_iovlen = 1;
+
+  std::array<uint8_t, CMSG_SPACE(sizeof(uint8_t))> msg_ctrl;
+  msg.msg_control = msg_ctrl.data();
 
   for (;;) {
-    addrlen = sizeof(su);
-    auto nread =
-        recvfrom(fd_, buf.data(), buf.size(), MSG_DONTWAIT, &su.sa, &addrlen);
+    msg.msg_namelen = sizeof(su);
+    msg.msg_controllen = msg_ctrl.size();
+
+    auto nread = recvmsg(fd_, &msg, MSG_DONTWAIT);
 
     if (nread == -1) {
       if (errno != EAGAIN && errno != EWOULDBLOCK) {
@@ -1042,10 +1057,13 @@ int Client::on_read() {
       break;
     }
 
+    pi.ecn = msghdr_get_ecn(&msg, su.storage.ss_family);
+
     if (!config.quiet) {
       std::cerr << "Received packet: local="
                 << util::straddr(&local_addr_.su.sa, local_addr_.len)
-                << " remote=" << util::straddr(&su.sa, addrlen) << " " << nread
+                << " remote=" << util::straddr(&su.sa, msg.msg_namelen)
+                << " ecn=0x" << std::hex << pi.ecn << std::dec << " " << nread
                 << " bytes" << std::endl;
     }
 
@@ -1056,7 +1074,7 @@ int Client::on_read() {
       break;
     }
 
-    if (feed_data(&su.sa, addrlen, buf.data(), nread) != 0) {
+    if (feed_data(&su.sa, msg.msg_namelen, &pi, buf.data(), nread) != 0) {
       return -1;
     }
 
@@ -1157,9 +1175,10 @@ int Client::write_streams() {
     }
 
     ngtcp2_ssize ndatalen;
+    ngtcp2_pkt_info pi;
 
     auto nwrite = ngtcp2_conn_writev_stream(
-        conn_, &path.path, sendbuf_.wpos(), max_pktlen_, &ndatalen, flags,
+        conn_, &path.path, &pi, sendbuf_.wpos(), max_pktlen_, &ndatalen, flags,
         stream_id, &vec, vcnt, util::timestamp(loop_));
     if (nwrite < 0) {
       switch (nwrite) {
@@ -1195,7 +1214,7 @@ int Client::write_streams() {
 
     sendbuf_.push(nwrite);
 
-    update_remote_addr(&path.path.remote);
+    update_remote_addr(&path.path.remote, &pi);
     reset_idle_timer();
 
     if (auto rv = send_packet(); rv != NETWORK_ERR_OK) {
@@ -1314,6 +1333,8 @@ int create_sock(Address &remote_addr, const char *addr, const char *port) {
     return -1;
   }
 
+  fd_set_recv_ecn(fd, rp->ai_family);
+
   remote_addr.len = rp->ai_addrlen;
   memcpy(&remote_addr.su, rp->ai_addr, rp->ai_addrlen);
 
@@ -1436,9 +1457,15 @@ void Client::start_delay_stream_timer() {
   ev_timer_start(loop_, &delay_stream_timer_);
 }
 
-void Client::update_remote_addr(const ngtcp2_addr *addr) {
+void Client::update_remote_addr(const ngtcp2_addr *addr,
+                                const ngtcp2_pkt_info *pi) {
   remote_addr_.len = addr->addrlen;
   memcpy(&remote_addr_.su, addr->addr, addr->addrlen);
+  if (pi) {
+    ecn_ = pi->ecn;
+  } else {
+    ecn_ = 0;
+  }
 }
 
 int Client::send_packet() {
@@ -1450,11 +1477,22 @@ int Client::send_packet() {
     return NETWORK_ERR_OK;
   }
 
+  iovec msg_iov;
+  msg_iov.iov_base = const_cast<uint8_t *>(sendbuf_.rpos());
+  msg_iov.iov_len = sendbuf_.size();
+
+  msghdr msg{};
+  msg.msg_name = const_cast<sockaddr *>(&remote_addr_.su.sa);
+  msg.msg_namelen = remote_addr_.len;
+  msg.msg_iov = &msg_iov;
+  msg.msg_iovlen = 1;
+
+  fd_set_ecn(fd_, remote_addr_.su.storage.ss_family, ecn_);
+
   ssize_t nwrite = 0;
 
   do {
-    nwrite = sendto(fd_, sendbuf_.rpos(), sendbuf_.size(), MSG_DONTWAIT,
-                    &remote_addr_.su.sa, remote_addr_.len);
+    nwrite = sendmsg(fd_, &msg, MSG_DONTWAIT);
   } while (nwrite == -1 && errno == EINTR);
 
   if (nwrite == -1) {
@@ -1472,8 +1510,9 @@ int Client::send_packet() {
     std::cerr << "Sent packet: local="
               << util::straddr(&local_addr_.su.sa, local_addr_.len)
               << " remote="
-              << util::straddr(&remote_addr_.su.sa, remote_addr_.len) << " "
-              << nwrite << " bytes" << std::endl;
+              << util::straddr(&remote_addr_.su.sa, remote_addr_.len)
+              << " ecn=0x" << std::hex << ecn_ << std::dec << " " << nwrite
+              << " bytes" << std::endl;
   }
 
   return NETWORK_ERR_OK;
@@ -1515,7 +1554,7 @@ int Client::handle_error() {
     sendbuf_.push(n);
   }
 
-  update_remote_addr(&path.path.remote);
+  update_remote_addr(&path.path.remote, nullptr);
 
   return send_packet();
 }
diff --git a/examples/h09client.h b/examples/h09client.h
index 3181e089..b7c0cd9a 100644
--- a/examples/h09client.h
+++ b/examples/h09client.h
@@ -223,8 +223,8 @@ public:
   int on_read();
   int on_write();
   int write_streams();
-  int feed_data(const sockaddr *sa, socklen_t salen, uint8_t *data,
-                size_t datalen);
+  int feed_data(const sockaddr *sa, socklen_t salen, const ngtcp2_pkt_info *pi,
+                uint8_t *data, size_t datalen);
   int handle_expiry();
   void schedule_retransmit();
   int handshake_completed();
@@ -236,7 +236,7 @@ public:
                        size_t datalen);
 
   ngtcp2_conn *conn() const;
-  void update_remote_addr(const ngtcp2_addr *addr);
+  void update_remote_addr(const ngtcp2_addr *addr, const ngtcp2_pkt_info *pi);
   int send_packet();
   void remove_tx_crypto_data(ngtcp2_crypto_level crypto_level, uint64_t offset,
                              uint64_t datalen);
@@ -279,6 +279,7 @@ public:
 private:
   Address local_addr_;
   Address remote_addr_;
+  unsigned int ecn_;
   size_t max_pktlen_;
   ev_io wev_;
   ev_io rev_;
diff --git a/examples/h09server.cc b/examples/h09server.cc
index 684a49d5..b695b52a 100644
--- a/examples/h09server.cc
+++ b/examples/h09server.cc
@@ -990,18 +990,26 @@ void Handler::update_endpoint(const ngtcp2_addr *addr) {
   assert(endpoint_);
 }
 
-void Handler::update_remote_addr(const ngtcp2_addr *addr) {
+void Handler::update_remote_addr(const ngtcp2_addr *addr,
+                                 const ngtcp2_pkt_info *pi) {
   remote_addr_.len = addr->addrlen;
   memcpy(&remote_addr_.su, addr->addr, addr->addrlen);
+
+  if (pi) {
+    ecn_ = pi->ecn;
+  } else {
+    ecn_ = 0;
+  }
 }
 
 int Handler::feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
-                       uint8_t *data, size_t datalen) {
+                       const ngtcp2_pkt_info *pi, uint8_t *data,
+                       size_t datalen) {
   auto path = ngtcp2_path{{ep.addr.len, const_cast<sockaddr *>(&ep.addr.su.sa),
                            const_cast<Endpoint *>(&ep)},
                           {salen, const_cast<sockaddr *>(sa)}};
 
-  if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, data, datalen,
+  if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, pi, data, datalen,
                                      util::timestamp(loop_));
       rv != 0) {
     std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
@@ -1033,8 +1041,8 @@ int Handler::feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
 }
 
 int Handler::on_read(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
-                     uint8_t *data, size_t datalen) {
-  if (auto rv = feed_data(ep, sa, salen, data, datalen); rv != 0) {
+                     const ngtcp2_pkt_info *pi, uint8_t *data, size_t datalen) {
+  if (auto rv = feed_data(ep, sa, salen, pi, data, datalen); rv != 0) {
     return rv;
   }
 
@@ -1106,6 +1114,7 @@ int Handler::write_streams() {
                                static_cast<size_t>(64_k / max_pktlen_));
   std::array<uint8_t, 64_k> buf;
   uint8_t *bufpos = buf.data();
+  ngtcp2_pkt_info pi;
 
   for (;;) {
     int64_t stream_id = -1;
@@ -1126,8 +1135,8 @@ int Handler::write_streams() {
     ngtcp2_ssize ndatalen;
 
     auto nwrite = ngtcp2_conn_writev_stream(
-        conn_, &path.path, bufpos, max_pktlen_, &ndatalen, flags, stream_id,
-        &vec, vcnt, util::timestamp(loop_));
+        conn_, &path.path, &pi, bufpos, max_pktlen_, &ndatalen, flags,
+        stream_id, &vec, vcnt, util::timestamp(loop_));
     if (nwrite < 0) {
       switch (nwrite) {
       case NGTCP2_ERR_STREAM_DATA_BLOCKED:
@@ -1156,7 +1165,7 @@ int Handler::write_streams() {
 
     if (nwrite == 0) {
       if (bufpos - buf.data()) {
-        server_->send_packet(*endpoint_, remote_addr_, buf.data(),
+        server_->send_packet(*endpoint_, remote_addr_, ecn_, buf.data(),
                              bufpos - buf.data(), max_pktlen_);
         reset_idle_timer();
       }
@@ -1169,24 +1178,26 @@ int Handler::write_streams() {
 #if NGTCP2_ENABLE_UDP_GSO
     if (pktcnt == 0) {
       update_endpoint(&path.path.local);
-      update_remote_addr(&path.path.remote);
+      update_remote_addr(&path.path.remote, &pi);
     } else if (remote_addr_.len != path.path.remote.addrlen ||
                0 != memcmp(&remote_addr_.su, path.path.remote.addr,
-                           path.path.remote.addrlen)) {
-      server_->send_packet(*endpoint_, remote_addr_, buf.data(),
+                           path.path.remote.addrlen) ||
+               endpoint_ != path.path.local.user_data || ecn_ != pi.ecn) {
+      server_->send_packet(*endpoint_, remote_addr_, ecn_, buf.data(),
                            bufpos - buf.data() - nwrite, max_pktlen_);
 
-      update_remote_addr(&path.path.remote);
+      update_endpoint(&path.path.local);
+      update_remote_addr(&path.path.remote, &pi);
 
-      server_->send_packet(*endpoint_, remote_addr_, bufpos - nwrite, nwrite,
-                           max_pktlen_);
+      server_->send_packet(*endpoint_, remote_addr_, ecn_, bufpos - nwrite,
+                           nwrite, max_pktlen_);
       reset_idle_timer();
       ev_io_start(loop_, &wev_);
       return 0;
     }
 
     if (++pktcnt == max_pktcnt || static_cast<size_t>(nwrite) < max_pktlen_) {
-      server_->send_packet(*endpoint_, remote_addr_, buf.data(),
+      server_->send_packet(*endpoint_, remote_addr_, ecn_, buf.data(),
                            bufpos - buf.data(), max_pktlen_);
       reset_idle_timer();
       ev_io_start(loop_, &wev_);
@@ -1194,10 +1205,10 @@ int Handler::write_streams() {
     }
 #else  // !NGTCP2_ENABLE_UDP_GSO
     update_endpoint(&path.path.local);
-    update_remote_addr(&path.path.remote);
+    update_remote_addr(&path.path.remote, &pi);
     reset_idle_timer();
 
-    server_->send_packet(*endpoint_, remote_addr_, buf.data(),
+    server_->send_packet(*endpoint_, remote_addr_, ecn_, buf.data(),
                          bufpos - buf.data(), 0);
     if (++pktcnt == max_pktcnt) {
       ev_io_start(loop_, &wev_);
@@ -1270,7 +1281,7 @@ int Handler::start_closing_period() {
   }
 
   update_endpoint(&path.path.local);
-  update_remote_addr(&path.path.remote);
+  update_remote_addr(&path.path.remote, nullptr);
 
   return 0;
 }
@@ -1294,8 +1305,9 @@ int Handler::send_conn_close() {
 
   assert(conn_closebuf_ && conn_closebuf_->size());
 
-  return server_->send_packet(*endpoint_, remote_addr_, conn_closebuf_->rpos(),
-                              conn_closebuf_->size(), 0);
+  return server_->send_packet(*endpoint_, remote_addr_, 0,
+                              conn_closebuf_->rpos(), conn_closebuf_->size(),
+                              0);
 }
 
 void Handler::schedule_retransmit() {
@@ -1593,6 +1605,8 @@ int create_sock(Address &local_addr, const char *addr, const char *port,
     return -1;
   }
 
+  fd_set_recv_ecn(fd, rp->ai_family);
+
   socklen_t len = sizeof(local_addr.su.storage);
   if (getsockname(fd, &local_addr.su.sa, &len) == -1) {
     std::cerr << "getsockname: " << strerror(errno) << std::endl;
@@ -1648,6 +1662,14 @@ int add_endpoint(std::vector<Endpoint> &endpoints, const Address &addr) {
     return -1;
   }
 
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
+                 static_cast<socklen_t>(sizeof(val))) == -1) {
+    close(fd);
+    return -1;
+  }
+
+  fd_set_recv_ecn(fd, addr.su.sa.sa_family);
+
   endpoints.emplace_back(Endpoint{});
   auto &ep = endpoints.back();
   ep.addr = addr;
@@ -1699,15 +1721,28 @@ int Server::init(const char *addr, const char *port) {
 
 int Server::on_read(Endpoint &ep) {
   sockaddr_union su;
-  socklen_t addrlen;
   std::array<uint8_t, 64_k> buf;
   ngtcp2_pkt_hd hd;
   size_t pktcnt = 0;
+  ngtcp2_pkt_info pi;
+
+  iovec msg_iov;
+  msg_iov.iov_base = buf.data();
+  msg_iov.iov_len = buf.size();
+
+  msghdr msg{};
+  msg.msg_name = &su;
+  msg.msg_iov = &msg_iov;
+  msg.msg_iovlen = 1;
+
+  std::array<uint8_t, CMSG_SPACE(sizeof(uint8_t))> msg_ctrl;
+  msg.msg_control = msg_ctrl.data();
 
   for (; pktcnt < 10;) {
-    addrlen = sizeof(su);
-    auto nread =
-        recvfrom(ep.fd, buf.data(), buf.size(), MSG_DONTWAIT, &su.sa, &addrlen);
+    msg.msg_namelen = sizeof(su);
+    msg.msg_controllen = msg_ctrl.size();
+
+    auto nread = recvmsg(ep.fd, &msg, MSG_DONTWAIT);
     if (nread == -1) {
       if (!(errno == EAGAIN || errno == ENOTCONN)) {
         std::cerr << "recvfrom: " << strerror(errno) << std::endl;
@@ -1717,10 +1752,13 @@ int Server::on_read(Endpoint &ep) {
 
     ++pktcnt;
 
+    pi.ecn = msghdr_get_ecn(&msg, su.storage.ss_family);
+
     if (!config.quiet) {
       std::cerr << "Received packet: local="
                 << util::straddr(&ep.addr.su.sa, ep.addr.len)
-                << " remote=" << util::straddr(&su.sa, addrlen) << " " << nread
+                << " remote=" << util::straddr(&su.sa, msg.msg_namelen)
+                << " ecn=0x" << std::hex << pi.ecn << std::dec << " " << nread
                 << " bytes" << std::endl;
     }
 
@@ -1745,7 +1783,7 @@ int Server::on_read(Endpoint &ep) {
         rv != 0) {
       if (rv == 1) {
         send_version_negotiation(version, scid, scidlen, dcid, dcidlen, ep,
-                                 &su.sa, addrlen);
+                                 &su.sa, msg.msg_namelen);
         continue;
       }
       std::cerr << "Could not decode version and CID from QUIC packet header: "
@@ -1772,7 +1810,7 @@ int Server::on_read(Endpoint &ep) {
           }
           send_version_negotiation(hd.version, hd.scid.data, hd.scid.datalen,
                                    hd.dcid.data, hd.dcid.datalen, ep, &su.sa,
-                                   addrlen);
+                                   msg.msg_namelen);
           continue;
         }
 
@@ -1783,28 +1821,30 @@ int Server::on_read(Endpoint &ep) {
           if (config.validate_addr || hd.token.len) {
             std::cerr << "Perform stateless address validation" << std::endl;
             if (hd.token.len == 0) {
-              send_retry(&hd, ep, &su.sa, addrlen);
+              send_retry(&hd, ep, &su.sa, msg.msg_namelen);
               continue;
             }
 
             if (hd.token.base[0] != RETRY_TOKEN_MAGIC &&
                 hd.dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN) {
-              send_stateless_connection_close(&hd, ep, &su.sa, addrlen);
+              send_stateless_connection_close(&hd, ep, &su.sa, msg.msg_namelen);
               continue;
             }
 
             switch (hd.token.base[0]) {
             case RETRY_TOKEN_MAGIC:
-              if (verify_retry_token(&ocid, &hd, &su.sa, addrlen) != 0) {
-                send_stateless_connection_close(&hd, ep, &su.sa, addrlen);
+              if (verify_retry_token(&ocid, &hd, &su.sa, msg.msg_namelen) !=
+                  0) {
+                send_stateless_connection_close(&hd, ep, &su.sa,
+                                                msg.msg_namelen);
                 continue;
               }
               pocid = &ocid;
               break;
             case TOKEN_MAGIC:
-              if (verify_token(&hd, &su.sa, addrlen) != 0) {
+              if (verify_token(&hd, &su.sa, msg.msg_namelen) != 0) {
                 if (config.validate_addr) {
-                  send_retry(&hd, ep, &su.sa, addrlen);
+                  send_retry(&hd, ep, &su.sa, msg.msg_namelen);
                   continue;
                 }
 
@@ -1817,7 +1857,7 @@ int Server::on_read(Endpoint &ep) {
                 std::cerr << "Ignore unrecognized token" << std::endl;
               }
               if (config.validate_addr) {
-                send_retry(&hd, ep, &su.sa, addrlen);
+                send_retry(&hd, ep, &su.sa, msg.msg_namelen);
                 continue;
               }
 
@@ -1828,21 +1868,22 @@ int Server::on_read(Endpoint &ep) {
           }
           break;
         case NGTCP2_PKT_0RTT:
-          send_retry(&hd, ep, &su.sa, addrlen);
+          send_retry(&hd, ep, &su.sa, msg.msg_namelen);
           continue;
         }
 
         auto h = std::make_unique<Handler>(loop_, ssl_ctx_, this, &hd.dcid);
-        if (h->init(ep, &su.sa, addrlen, &hd.scid, &hd.dcid, pocid,
+        if (h->init(ep, &su.sa, msg.msg_namelen, &hd.scid, &hd.dcid, pocid,
                     hd.token.base, hd.token.len, hd.version) != 0) {
           continue;
         }
 
-        switch (h->on_read(ep, &su.sa, addrlen, buf.data(), nread)) {
+        switch (
+            h->on_read(ep, &su.sa, msg.msg_namelen, &pi, buf.data(), nread)) {
         case 0:
           break;
         case NETWORK_ERR_RETRY:
-          send_retry(&hd, ep, &su.sa, addrlen);
+          send_retry(&hd, ep, &su.sa, msg.msg_namelen);
           continue;
         default:
           continue;
@@ -1892,7 +1933,9 @@ int Server::on_read(Endpoint &ep) {
       continue;
     }
 
-    if (auto rv = h->on_read(ep, &su.sa, addrlen, buf.data(), nread); rv != 0) {
+    if (auto rv =
+            h->on_read(ep, &su.sa, msg.msg_namelen, &pi, buf.data(), nread);
+        rv != 0) {
       if (rv != NETWORK_ERR_CLOSE_WAIT) {
         remove(h);
       }
@@ -1955,7 +1998,7 @@ int Server::send_version_negotiation(uint32_t version, const uint8_t *dcid,
   remote_addr.len = salen;
   memcpy(&remote_addr.su.sa, sa, salen);
 
-  if (send_packet(ep, remote_addr, buf.rpos(), buf.size(), 0) !=
+  if (send_packet(ep, remote_addr, 0, buf.rpos(), buf.size(), 0) !=
       NETWORK_ERR_OK) {
     return -1;
   }
@@ -2016,7 +2059,7 @@ int Server::send_retry(const ngtcp2_pkt_hd *chd, Endpoint &ep,
   remote_addr.len = salen;
   memcpy(&remote_addr.su.sa, sa, salen);
 
-  if (send_packet(ep, remote_addr, buf.rpos(), buf.size(), 0) !=
+  if (send_packet(ep, remote_addr, 0, buf.rpos(), buf.size(), 0) !=
       NETWORK_ERR_OK) {
     return -1;
   }
@@ -2042,7 +2085,7 @@ int Server::send_stateless_connection_close(const ngtcp2_pkt_hd *chd,
   remote_addr.len = salen;
   memcpy(&remote_addr.su.sa, sa, salen);
 
-  if (send_packet(ep, remote_addr, buf.rpos(), buf.size(), 0) !=
+  if (send_packet(ep, remote_addr, 0, buf.rpos(), buf.size(), 0) !=
       NETWORK_ERR_OK) {
     return -1;
   }
@@ -2450,7 +2493,8 @@ int Server::verify_token(const ngtcp2_pkt_hd *hd, const sockaddr *sa,
 }
 
 int Server::send_packet(Endpoint &ep, const Address &remote_addr,
-                        const uint8_t *data, size_t datalen, size_t gso_size) {
+                        unsigned int ecn, const uint8_t *data, size_t datalen,
+                        size_t gso_size) {
   if (debug::packet_lost(config.tx_loss_prob)) {
     if (!config.quiet) {
       std::cerr << "** Simulated outgoing packet loss **" << std::endl;
@@ -2482,6 +2526,11 @@ int Server::send_packet(Endpoint &ep, const Address &remote_addr,
   }
 #endif // NGTCP2_ENABLE_UDP_GSO
 
+  if (ep.ecn != ecn) {
+    ep.ecn = ecn;
+    fd_set_ecn(ep.fd, ep.addr.su.storage.ss_family, ecn);
+  }
+
   ssize_t nwrite = 0;
 
   do {
@@ -2498,8 +2547,9 @@ int Server::send_packet(Endpoint &ep, const Address &remote_addr,
   if (!config.quiet) {
     std::cerr << "Sent packet: local="
               << util::straddr(&ep.addr.su.sa, ep.addr.len) << " remote="
-              << util::straddr(&remote_addr.su.sa, remote_addr.len) << " "
-              << nwrite << " bytes" << std::endl;
+              << util::straddr(&remote_addr.su.sa, remote_addr.len) << " ecn=0x"
+              << std::hex << ecn << std::dec << " " << nwrite << " bytes"
+              << std::endl;
   }
 
   return NETWORK_ERR_OK;
diff --git a/examples/h09server.h b/examples/h09server.h
index 40e767f6..126df6f8 100644
--- a/examples/h09server.h
+++ b/examples/h09server.h
@@ -198,6 +198,8 @@ struct Endpoint {
   ev_io rev;
   Server *server;
   int fd;
+  // ecn is the last ECN bits set to fd.
+  unsigned int ecn;
 };
 
 struct Crypto {
@@ -220,11 +222,11 @@ public:
            uint32_t version);
 
   int on_read(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
-              uint8_t *data, size_t datalen);
+              const ngtcp2_pkt_info *pi, uint8_t *data, size_t datalen);
   int on_write();
   int write_streams();
   int feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
-                uint8_t *data, size_t datalen);
+                const ngtcp2_pkt_info *pi, uint8_t *data, size_t datalen);
   void schedule_retransmit();
   int handle_expiry();
   void signal_write();
@@ -257,7 +259,7 @@ public:
   int handle_error();
   int send_conn_close();
   void update_endpoint(const ngtcp2_addr *addr);
-  void update_remote_addr(const ngtcp2_addr *addr);
+  void update_remote_addr(const ngtcp2_addr *addr, const ngtcp2_pkt_info *pi);
 
   int on_key(ngtcp2_crypto_level level, const uint8_t *rsecret,
              const uint8_t *wsecret, size_t secretlen);
@@ -283,6 +285,7 @@ public:
 private:
   Endpoint *endpoint_;
   Address remote_addr_;
+  unsigned int ecn_;
   size_t max_pktlen_;
   struct ev_loop *loop_;
   SSL_CTX *ssl_ctx_;
@@ -336,8 +339,8 @@ public:
   int generate_token(uint8_t *token, size_t &tokenlen, const sockaddr *sa);
   int verify_token(const ngtcp2_pkt_hd *hd, const sockaddr *sa,
                    socklen_t salen);
-  int send_packet(Endpoint &ep, const Address &remote_addr, const uint8_t *data,
-                  size_t datalen, size_t gso_size);
+  int send_packet(Endpoint &ep, const Address &remote_addr, unsigned int ecn,
+                  const uint8_t *data, size_t datalen, size_t gso_size);
   void remove(const Handler *h);
 
   int derive_token_key(uint8_t *key, size_t &keylen, uint8_t *iv, size_t &ivlen,
diff --git a/examples/server.cc b/examples/server.cc
index cb2e1e3c..a74c26f8 100644
--- a/examples/server.cc
+++ b/examples/server.cc
@@ -1651,18 +1651,26 @@ void Handler::update_endpoint(const ngtcp2_addr *addr) {
   assert(endpoint_);
 }
 
-void Handler::update_remote_addr(const ngtcp2_addr *addr) {
+void Handler::update_remote_addr(const ngtcp2_addr *addr,
+                                 const ngtcp2_pkt_info *pi) {
   remote_addr_.len = addr->addrlen;
   memcpy(&remote_addr_.su, addr->addr, addr->addrlen);
+
+  if (pi) {
+    ecn_ = pi->ecn;
+  } else {
+    ecn_ = 0;
+  }
 }
 
 int Handler::feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
-                       uint8_t *data, size_t datalen) {
+                       const ngtcp2_pkt_info *pi, uint8_t *data,
+                       size_t datalen) {
   auto path = ngtcp2_path{{ep.addr.len, const_cast<sockaddr *>(&ep.addr.su.sa),
                            const_cast<Endpoint *>(&ep)},
                           {salen, const_cast<sockaddr *>(sa)}};
 
-  if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, data, datalen,
+  if (auto rv = ngtcp2_conn_read_pkt(conn_, &path, pi, data, datalen,
                                      util::timestamp(loop_));
       rv != 0) {
     std::cerr << "ngtcp2_conn_read_pkt: " << ngtcp2_strerror(rv) << std::endl;
@@ -1694,8 +1702,8 @@ int Handler::feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
 }
 
 int Handler::on_read(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
-                     uint8_t *data, size_t datalen) {
-  if (auto rv = feed_data(ep, sa, salen, data, datalen); rv != 0) {
+                     const ngtcp2_pkt_info *pi, uint8_t *data, size_t datalen) {
+  if (auto rv = feed_data(ep, sa, salen, pi, data, datalen); rv != 0) {
     return rv;
   }
 
@@ -1767,6 +1775,7 @@ int Handler::write_streams() {
                                static_cast<size_t>(64_k / max_pktlen_));
   std::array<uint8_t, 64_k> buf;
   uint8_t *bufpos = buf.data();
+  ngtcp2_pkt_info pi;
 
   for (;;) {
     int64_t stream_id = -1;
@@ -1794,8 +1803,9 @@ int Handler::write_streams() {
     }
 
     auto nwrite = ngtcp2_conn_writev_stream(
-        conn_, &path.path, bufpos, max_pktlen_, &ndatalen, flags, stream_id,
-        reinterpret_cast<const ngtcp2_vec *>(v), vcnt, util::timestamp(loop_));
+        conn_, &path.path, &pi, bufpos, max_pktlen_, &ndatalen, flags,
+        stream_id, reinterpret_cast<const ngtcp2_vec *>(v), vcnt,
+        util::timestamp(loop_));
     if (nwrite < 0) {
       switch (nwrite) {
       case NGTCP2_ERR_STREAM_DATA_BLOCKED:
@@ -1834,7 +1844,7 @@ int Handler::write_streams() {
 
     if (nwrite == 0) {
       if (bufpos - buf.data()) {
-        server_->send_packet(*endpoint_, remote_addr_, buf.data(),
+        server_->send_packet(*endpoint_, remote_addr_, ecn_, buf.data(),
                              bufpos - buf.data(), max_pktlen_);
         reset_idle_timer();
       }
@@ -1847,24 +1857,26 @@ int Handler::write_streams() {
 #if NGTCP2_ENABLE_UDP_GSO
     if (pktcnt == 0) {
       update_endpoint(&path.path.local);
-      update_remote_addr(&path.path.remote);
+      update_remote_addr(&path.path.remote, &pi);
     } else if (remote_addr_.len != path.path.remote.addrlen ||
                0 != memcmp(&remote_addr_.su, path.path.remote.addr,
-                           path.path.remote.addrlen)) {
-      server_->send_packet(*endpoint_, remote_addr_, buf.data(),
+                           path.path.remote.addrlen) ||
+               endpoint_ != path.path.local.user_data || ecn_ != pi.ecn) {
+      server_->send_packet(*endpoint_, remote_addr_, ecn_, buf.data(),
                            bufpos - buf.data() - nwrite, max_pktlen_);
 
-      update_remote_addr(&path.path.remote);
+      update_endpoint(&path.path.local);
+      update_remote_addr(&path.path.remote, &pi);
 
-      server_->send_packet(*endpoint_, remote_addr_, bufpos - nwrite, nwrite,
-                           max_pktlen_);
+      server_->send_packet(*endpoint_, remote_addr_, ecn_, bufpos - nwrite,
+                           nwrite, max_pktlen_);
       reset_idle_timer();
       ev_io_start(loop_, &wev_);
       return 0;
     }
 
     if (++pktcnt == max_pktcnt || static_cast<size_t>(nwrite) < max_pktlen_) {
-      server_->send_packet(*endpoint_, remote_addr_, buf.data(),
+      server_->send_packet(*endpoint_, remote_addr_, ecn_, buf.data(),
                            bufpos - buf.data(), max_pktlen_);
       reset_idle_timer();
       ev_io_start(loop_, &wev_);
@@ -1872,10 +1884,10 @@ int Handler::write_streams() {
     }
 #else  // !NGTCP2_ENABLE_UDP_GSO
     update_endpoint(&path.path.local);
-    update_remote_addr(&path.path.remote);
+    update_remote_addr(&path.path.remote, &pi);
     reset_idle_timer();
 
-    server_->send_packet(*endpoint_, remote_addr_, buf.data(),
+    server_->send_packet(*endpoint_, remote_addr_, ecn_, buf.data(),
                          bufpos - buf.data(), 0);
     if (++pktcnt == max_pktcnt) {
       ev_io_start(loop_, &wev_);
@@ -1948,7 +1960,7 @@ int Handler::start_closing_period() {
   }
 
   update_endpoint(&path.path.local);
-  update_remote_addr(&path.path.remote);
+  update_remote_addr(&path.path.remote, nullptr);
 
   return 0;
 }
@@ -1972,8 +1984,9 @@ int Handler::send_conn_close() {
 
   assert(conn_closebuf_ && conn_closebuf_->size());
 
-  return server_->send_packet(*endpoint_, remote_addr_, conn_closebuf_->rpos(),
-                              conn_closebuf_->size(), 0);
+  return server_->send_packet(*endpoint_, remote_addr_, 0,
+                              conn_closebuf_->rpos(), conn_closebuf_->size(),
+                              0);
 }
 
 void Handler::schedule_retransmit() {
@@ -2224,6 +2237,8 @@ int create_sock(Address &local_addr, const char *addr, const char *port,
     return -1;
   }
 
+  fd_set_recv_ecn(fd, rp->ai_family);
+
   socklen_t len = sizeof(local_addr.su.storage);
   if (getsockname(fd, &local_addr.su.sa, &len) == -1) {
     std::cerr << "getsockname: " << strerror(errno) << std::endl;
@@ -2279,6 +2294,14 @@ int add_endpoint(std::vector<Endpoint> &endpoints, const Address &addr) {
     return -1;
   }
 
+  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
+                 static_cast<socklen_t>(sizeof(val))) == -1) {
+    close(fd);
+    return -1;
+  }
+
+  fd_set_recv_ecn(fd, addr.su.sa.sa_family);
+
   endpoints.emplace_back(Endpoint{});
   auto &ep = endpoints.back();
   ep.addr = addr;
@@ -2330,15 +2353,28 @@ int Server::init(const char *addr, const char *port) {
 
 int Server::on_read(Endpoint &ep) {
   sockaddr_union su;
-  socklen_t addrlen;
   std::array<uint8_t, 64_k> buf;
   ngtcp2_pkt_hd hd;
   size_t pktcnt = 0;
+  ngtcp2_pkt_info pi;
+
+  iovec msg_iov;
+  msg_iov.iov_base = buf.data();
+  msg_iov.iov_len = buf.size();
+
+  msghdr msg{};
+  msg.msg_name = &su;
+  msg.msg_iov = &msg_iov;
+  msg.msg_iovlen = 1;
+
+  std::array<uint8_t, CMSG_SPACE(sizeof(uint8_t))> msg_ctrl;
+  msg.msg_control = msg_ctrl.data();
 
   for (; pktcnt < 10;) {
-    addrlen = sizeof(su);
-    auto nread =
-        recvfrom(ep.fd, buf.data(), buf.size(), MSG_DONTWAIT, &su.sa, &addrlen);
+    msg.msg_namelen = sizeof(su);
+    msg.msg_controllen = msg_ctrl.size();
+
+    auto nread = recvmsg(ep.fd, &msg, MSG_DONTWAIT);
     if (nread == -1) {
       if (!(errno == EAGAIN || errno == ENOTCONN)) {
         std::cerr << "recvfrom: " << strerror(errno) << std::endl;
@@ -2348,10 +2384,13 @@ int Server::on_read(Endpoint &ep) {
 
     ++pktcnt;
 
+    pi.ecn = msghdr_get_ecn(&msg, su.storage.ss_family);
+
     if (!config.quiet) {
       std::cerr << "Received packet: local="
                 << util::straddr(&ep.addr.su.sa, ep.addr.len)
-                << " remote=" << util::straddr(&su.sa, addrlen) << " " << nread
+                << " remote=" << util::straddr(&su.sa, msg.msg_namelen)
+                << " ecn=0x" << std::hex << pi.ecn << std::dec << " " << nread
                 << " bytes" << std::endl;
     }
 
@@ -2376,7 +2415,7 @@ int Server::on_read(Endpoint &ep) {
         rv != 0) {
       if (rv == 1) {
         send_version_negotiation(version, scid, scidlen, dcid, dcidlen, ep,
-                                 &su.sa, addrlen);
+                                 &su.sa, msg.msg_namelen);
         continue;
       }
       std::cerr << "Could not decode version and CID from QUIC packet header: "
@@ -2403,7 +2442,7 @@ int Server::on_read(Endpoint &ep) {
           }
           send_version_negotiation(hd.version, hd.scid.data, hd.scid.datalen,
                                    hd.dcid.data, hd.dcid.datalen, ep, &su.sa,
-                                   addrlen);
+                                   msg.msg_namelen);
           continue;
         }
 
@@ -2414,28 +2453,30 @@ int Server::on_read(Endpoint &ep) {
           if (config.validate_addr || hd.token.len) {
             std::cerr << "Perform stateless address validation" << std::endl;
             if (hd.token.len == 0) {
-              send_retry(&hd, ep, &su.sa, addrlen);
+              send_retry(&hd, ep, &su.sa, msg.msg_namelen);
               continue;
             }
 
             if (hd.token.base[0] != RETRY_TOKEN_MAGIC &&
                 hd.dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN) {
-              send_stateless_connection_close(&hd, ep, &su.sa, addrlen);
+              send_stateless_connection_close(&hd, ep, &su.sa, msg.msg_namelen);
               continue;
             }
 
             switch (hd.token.base[0]) {
             case RETRY_TOKEN_MAGIC:
-              if (verify_retry_token(&ocid, &hd, &su.sa, addrlen) != 0) {
-                send_stateless_connection_close(&hd, ep, &su.sa, addrlen);
+              if (verify_retry_token(&ocid, &hd, &su.sa, msg.msg_namelen) !=
+                  0) {
+                send_stateless_connection_close(&hd, ep, &su.sa,
+                                                msg.msg_namelen);
                 continue;
               }
               pocid = &ocid;
               break;
             case TOKEN_MAGIC:
-              if (verify_token(&hd, &su.sa, addrlen) != 0) {
+              if (verify_token(&hd, &su.sa, msg.msg_namelen) != 0) {
                 if (config.validate_addr) {
-                  send_retry(&hd, ep, &su.sa, addrlen);
+                  send_retry(&hd, ep, &su.sa, msg.msg_namelen);
                   continue;
                 }
 
@@ -2448,7 +2489,7 @@ int Server::on_read(Endpoint &ep) {
                 std::cerr << "Ignore unrecognized token" << std::endl;
               }
               if (config.validate_addr) {
-                send_retry(&hd, ep, &su.sa, addrlen);
+                send_retry(&hd, ep, &su.sa, msg.msg_namelen);
                 continue;
               }
 
@@ -2459,21 +2500,22 @@ int Server::on_read(Endpoint &ep) {
           }
           break;
         case NGTCP2_PKT_0RTT:
-          send_retry(&hd, ep, &su.sa, addrlen);
+          send_retry(&hd, ep, &su.sa, msg.msg_namelen);
           continue;
         }
 
         auto h = std::make_unique<Handler>(loop_, ssl_ctx_, this, &hd.dcid);
-        if (h->init(ep, &su.sa, addrlen, &hd.scid, &hd.dcid, pocid,
+        if (h->init(ep, &su.sa, msg.msg_namelen, &hd.scid, &hd.dcid, pocid,
                     hd.token.base, hd.token.len, hd.version) != 0) {
           continue;
         }
 
-        switch (h->on_read(ep, &su.sa, addrlen, buf.data(), nread)) {
+        switch (
+            h->on_read(ep, &su.sa, msg.msg_namelen, &pi, buf.data(), nread)) {
         case 0:
           break;
         case NETWORK_ERR_RETRY:
-          send_retry(&hd, ep, &su.sa, addrlen);
+          send_retry(&hd, ep, &su.sa, msg.msg_namelen);
           continue;
         default:
           continue;
@@ -2523,7 +2565,9 @@ int Server::on_read(Endpoint &ep) {
       continue;
     }
 
-    if (auto rv = h->on_read(ep, &su.sa, addrlen, buf.data(), nread); rv != 0) {
+    if (auto rv =
+            h->on_read(ep, &su.sa, msg.msg_namelen, &pi, buf.data(), nread);
+        rv != 0) {
       if (rv != NETWORK_ERR_CLOSE_WAIT) {
         remove(h);
       }
@@ -2586,7 +2630,7 @@ int Server::send_version_negotiation(uint32_t version, const uint8_t *dcid,
   remote_addr.len = salen;
   memcpy(&remote_addr.su.sa, sa, salen);
 
-  if (send_packet(ep, remote_addr, buf.rpos(), buf.size(), 0) !=
+  if (send_packet(ep, remote_addr, 0, buf.rpos(), buf.size(), 0) !=
       NETWORK_ERR_OK) {
     return -1;
   }
@@ -2647,7 +2691,7 @@ int Server::send_retry(const ngtcp2_pkt_hd *chd, Endpoint &ep,
   remote_addr.len = salen;
   memcpy(&remote_addr.su.sa, sa, salen);
 
-  if (send_packet(ep, remote_addr, buf.rpos(), buf.size(), 0) !=
+  if (send_packet(ep, remote_addr, 0, buf.rpos(), buf.size(), 0) !=
       NETWORK_ERR_OK) {
     return -1;
   }
@@ -2673,7 +2717,7 @@ int Server::send_stateless_connection_close(const ngtcp2_pkt_hd *chd,
   remote_addr.len = salen;
   memcpy(&remote_addr.su.sa, sa, salen);
 
-  if (send_packet(ep, remote_addr, buf.rpos(), buf.size(), 0) !=
+  if (send_packet(ep, remote_addr, 0, buf.rpos(), buf.size(), 0) !=
       NETWORK_ERR_OK) {
     return -1;
   }
@@ -3081,7 +3125,8 @@ int Server::verify_token(const ngtcp2_pkt_hd *hd, const sockaddr *sa,
 }
 
 int Server::send_packet(Endpoint &ep, const Address &remote_addr,
-                        const uint8_t *data, size_t datalen, size_t gso_size) {
+                        unsigned int ecn, const uint8_t *data, size_t datalen,
+                        size_t gso_size) {
   if (debug::packet_lost(config.tx_loss_prob)) {
     if (!config.quiet) {
       std::cerr << "** Simulated outgoing packet loss **" << std::endl;
@@ -3113,6 +3158,11 @@ int Server::send_packet(Endpoint &ep, const Address &remote_addr,
   }
 #endif // NGTCP2_ENABLE_UDP_GSO
 
+  if (ep.ecn != ecn) {
+    ep.ecn = ecn;
+    fd_set_ecn(ep.fd, ep.addr.su.storage.ss_family, ecn);
+  }
+
   ssize_t nwrite = 0;
 
   do {
@@ -3129,8 +3179,9 @@ int Server::send_packet(Endpoint &ep, const Address &remote_addr,
   if (!config.quiet) {
     std::cerr << "Sent packet: local="
               << util::straddr(&ep.addr.su.sa, ep.addr.len) << " remote="
-              << util::straddr(&remote_addr.su.sa, remote_addr.len) << " "
-              << nwrite << " bytes" << std::endl;
+              << util::straddr(&remote_addr.su.sa, remote_addr.len) << " ecn=0x"
+              << std::hex << ecn << std::dec << " " << nwrite << " bytes"
+              << std::endl;
   }
 
   return NETWORK_ERR_OK;
diff --git a/examples/server.h b/examples/server.h
index 44049e0a..6b5f752a 100644
--- a/examples/server.h
+++ b/examples/server.h
@@ -204,6 +204,8 @@ struct Endpoint {
   ev_io rev;
   Server *server;
   int fd;
+  // ecn is the last ECN bits set to fd.
+  unsigned int ecn;
 };
 
 struct Crypto {
@@ -226,11 +228,11 @@ public:
            uint32_t version);
 
   int on_read(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
-              uint8_t *data, size_t datalen);
+              const ngtcp2_pkt_info *pi, uint8_t *data, size_t datalen);
   int on_write();
   int write_streams();
   int feed_data(const Endpoint &ep, const sockaddr *sa, socklen_t salen,
-                uint8_t *data, size_t datalen);
+                const ngtcp2_pkt_info *pi, uint8_t *data, size_t datalen);
   void schedule_retransmit();
   int handle_expiry();
   void signal_write();
@@ -262,7 +264,7 @@ public:
   int handle_error();
   int send_conn_close();
   void update_endpoint(const ngtcp2_addr *addr);
-  void update_remote_addr(const ngtcp2_addr *addr);
+  void update_remote_addr(const ngtcp2_addr *addr, const ngtcp2_pkt_info *pi);
 
   int on_key(ngtcp2_crypto_level level, const uint8_t *rsecret,
              const uint8_t *wsecret, size_t secretlen);
@@ -301,6 +303,7 @@ public:
 private:
   Endpoint *endpoint_;
   Address remote_addr_;
+  unsigned int ecn_;
   size_t max_pktlen_;
   struct ev_loop *loop_;
   SSL_CTX *ssl_ctx_;
@@ -354,8 +357,8 @@ public:
   int generate_token(uint8_t *token, size_t &tokenlen, const sockaddr *sa);
   int verify_token(const ngtcp2_pkt_hd *hd, const sockaddr *sa,
                    socklen_t salen);
-  int send_packet(Endpoint &ep, const Address &remote_addr, const uint8_t *data,
-                  size_t datalen, size_t gso_size);
+  int send_packet(Endpoint &ep, const Address &remote_addr, unsigned int ecn,
+                  const uint8_t *data, size_t datalen, size_t gso_size);
   void remove(const Handler *h);
 
   int derive_token_key(uint8_t *key, size_t &keylen, uint8_t *iv, size_t &ivlen,
diff --git a/examples/shared.cc b/examples/shared.cc
index ec42bd0c..34272925 100644
--- a/examples/shared.cc
+++ b/examples/shared.cc
@@ -26,6 +26,13 @@
 
 #include <nghttp3/nghttp3.h>
 
+#include <cstring>
+#include <iostream>
+
+#ifdef HAVE_NETINET_IN_H
+#  include <netinet/in.h>
+#endif // HAVE_NETINET_IN_H
+
 namespace ngtcp2 {
 
 QUICError quic_err_transport(int liberr) {
@@ -50,4 +57,62 @@ QUICError quic_err_app(int liberr) {
           nghttp3_err_infer_quic_app_error_code(liberr)};
 }
 
+unsigned int msghdr_get_ecn(msghdr *msg, int family) {
+  switch (family) {
+  case AF_INET:
+    for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+      if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TOS &&
+          cmsg->cmsg_len) {
+        return *reinterpret_cast<uint8_t *>(CMSG_DATA(cmsg));
+      }
+    }
+    break;
+  case AF_INET6:
+    for (auto cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+      if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_TCLASS &&
+          cmsg->cmsg_len) {
+        return *reinterpret_cast<uint8_t *>(CMSG_DATA(cmsg));
+      }
+    }
+    break;
+  }
+
+  return 0;
+}
+
+void fd_set_ecn(int fd, int family, unsigned int ecn) {
+  switch (family) {
+  case AF_INET:
+    if (setsockopt(fd, IPPROTO_IP, IP_TOS, &ecn,
+                   static_cast<socklen_t>(sizeof(ecn))) == -1) {
+      std::cerr << "setsockopt: " << strerror(errno) << std::endl;
+    }
+    break;
+  case AF_INET6:
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &ecn,
+                   static_cast<socklen_t>(sizeof(ecn))) == -1) {
+      std::cerr << "setsockopt: " << strerror(errno) << std::endl;
+    }
+    break;
+  }
+}
+
+void fd_set_recv_ecn(int fd, int family) {
+  unsigned int tos = 1;
+  switch (family) {
+  case AF_INET:
+    if (setsockopt(fd, IPPROTO_IP, IP_RECVTOS, &tos,
+                   static_cast<socklen_t>(sizeof(tos))) == -1) {
+      std::cerr << "setsockopt: " << strerror(errno) << std::endl;
+    }
+    break;
+  case AF_INET6:
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVTCLASS, &tos,
+                   static_cast<socklen_t>(sizeof(tos))) == -1) {
+      std::cerr << "setsockopt: " << strerror(errno) << std::endl;
+    }
+    break;
+  }
+}
+
 } // namespace ngtcp2
diff --git a/examples/shared.h b/examples/shared.h
index 9125fce8..8a15b212 100644
--- a/examples/shared.h
+++ b/examples/shared.h
@@ -57,6 +57,18 @@ QUICError quic_err_tls(int alert);
 
 QUICError quic_err_app(int liberr);
 
+// msghdr_get_ecn gets ECN bits from |msg|.  |family| is the address
+// family from which packet is received.
+unsigned int msghdr_get_ecn(msghdr *msg, int family);
+
+// fd_set_ecn sets ECN bits |ecn| to |fd|.  |family| is the address
+// family of |fd|.
+void fd_set_ecn(int fd, int family, unsigned int ecn);
+
+// fd_set_recv_ecn sets socket option to |fd| so that it can receive
+// ECN bits.
+void fd_set_recv_ecn(int fd, int family);
+
 } // namespace ngtcp2
 
 #endif // SHARED_H
diff --git a/lib/includes/ngtcp2/ngtcp2.h b/lib/includes/ngtcp2/ngtcp2.h
index 11f49659..bd9d95cf 100644
--- a/lib/includes/ngtcp2/ngtcp2.h
+++ b/lib/includes/ngtcp2/ngtcp2.h
@@ -230,6 +230,24 @@ typedef struct ngtcp2_mem {
 /* NGTCP2_DEFAULT_INITIAL_RTT is a default initial RTT. */
 #define NGTCP2_DEFAULT_INITIAL_RTT (333 * NGTCP2_MILLISECONDS)
 
+typedef enum ngtcp2_ecn {
+  NGTCP2_ECN_NOT_ECT = 0x0,
+  NGTCP2_ECN_ECT_1 = 0x1,
+  NGTCP2_ECN_ECT_0 = 0x2,
+  NGTCP2_ECN_CE = 0x3,
+  NGTCP2_ECN_MASK = 0x3
+} ngtcp2_ecn;
+
+typedef struct ngtcp2_pkt_info {
+  /**
+   * ecn is ECN marking and when passing `ngtcp2_conn_read_pkt()`, it
+   * should be either :enum:`NGTCP2_ECN_NOT_ECT`,
+   * :enum:`NGTCP2_ECN_ECT_1`, :enum:`NGTCP2_ECN_ECT_0`, or
+   * :enum:`NGTCP2_ECN_CE`.
+   */
+  uint32_t ecn;
+} ngtcp2_pkt_info;
+
 #if defined(__cplusplus) && __cplusplus >= 201103L
 typedef enum ngtcp2_lib_error : int {
 #else
@@ -2054,8 +2072,9 @@ 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.  This function performs
- * QUIC handshake as well.
+ * packet is delivered and must not be NULL.  |pi| is packet metadata
+ * and must not be NULL. This function performs QUIC handshake as
+ * well.
  *
  * This function must not be called from inside the callback
  * functions.
@@ -2075,6 +2094,7 @@ NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn);
  */
 NGTCP2_EXTERN int ngtcp2_conn_read_pkt(ngtcp2_conn *conn,
                                        const ngtcp2_path *path,
+                                       const ngtcp2_pkt_info *pi,
                                        const uint8_t *pkt, size_t pktlen,
                                        ngtcp2_tstamp ts);
 
@@ -2087,6 +2107,7 @@ NGTCP2_EXTERN int ngtcp2_conn_read_pkt(ngtcp2_conn *conn,
  */
 NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn,
                                                  ngtcp2_path *path,
+                                                 ngtcp2_pkt_info *pi,
                                                  uint8_t *dest, size_t destlen,
                                                  ngtcp2_tstamp ts);
 
@@ -2614,8 +2635,8 @@ typedef enum ngtcp2_write_stream_flag {
  * conveniently accepts a single buffer.
  */
 NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream(
-    ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen,
-    ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id,
+    ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+    size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id,
     const uint8_t *data, size_t datalen, ngtcp2_tstamp ts);
 
 /**
@@ -2634,6 +2655,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream(
  * sockaddr_storage)`` is enough.  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 the all given data is encoded as STREAM frame in |dest|, and if
  * |flags| & NGTCP2_WRITE_STREAM_FLAG_FIN is nonzero, fin flag is set
  * to outgoing STREAM frame.  Otherwise, fin flag in STREAM frame is
@@ -2727,8 +2751,8 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream(
  * other library functions.
  */
 NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream(
-    ngtcp2_conn *conn, ngtcp2_path *path, uint8_t *dest, size_t destlen,
-    ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id,
+    ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest,
+    size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id,
     const ngtcp2_vec *datav, size_t datavcnt, ngtcp2_tstamp ts);
 
 /**
diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c
index 6855544e..474c5abf 100644
--- a/lib/ngtcp2_conn.c
+++ b/lib/ngtcp2_conn.c
@@ -633,6 +633,84 @@ static void delete_scid(ngtcp2_ksl *scids, const ngtcp2_mem *mem) {
   }
 }
 
+static void conn_set_ecn(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+                         ngtcp2_tstamp ts) {
+  if (pi == NULL) {
+    return;
+  }
+
+  switch (conn->tx.ecn.state) {
+  case NGTCP2_ECN_STATE_TESTING:
+    if (conn->tx.ecn.validation_start_ts == UINT64_MAX) {
+      assert(0 == conn->tx.ecn.dgram_sent);
+      assert(0 == conn->tx.ecn.dgram_lost);
+
+      conn->tx.ecn.validation_start_ts = ts;
+    } else if (ts - conn->tx.ecn.validation_start_ts >=
+               3 * conn->cstat.smoothed_rtt) {
+      conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN;
+      pi->ecn = NGTCP2_ECN_NOT_ECT;
+      return;
+    }
+
+    if (++conn->tx.ecn.dgram_sent == NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS) {
+      conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN;
+    }
+    /* fall through */
+  case NGTCP2_ECN_STATE_CAPABLE:
+    pi->ecn = NGTCP2_ECN_ECT_0;
+    break;
+  case NGTCP2_ECN_STATE_FAILED:
+  case NGTCP2_ECN_STATE_UNKNOWN:
+    pi->ecn = NGTCP2_ECN_NOT_ECT;
+    break;
+  default:
+    assert(0);
+  }
+}
+
+/*
+ * conn_should_send_ecn returns nonzero if UDP datagram should be sent
+ * with ECN marking.
+ */
+static int conn_should_send_ecn(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+  switch (conn->tx.ecn.state) {
+  case NGTCP2_ECN_STATE_TESTING:
+    if (conn->tx.ecn.validation_start_ts == UINT64_MAX) {
+      assert(0 == conn->tx.ecn.dgram_sent);
+      assert(0 == conn->tx.ecn.dgram_lost);
+
+      return 1;
+    }
+
+    if (ts - conn->tx.ecn.validation_start_ts < 3 * conn->cstat.smoothed_rtt) {
+      return 1;
+    }
+
+    return 0;
+  case NGTCP2_ECN_STATE_CAPABLE:
+    return 1;
+  default:
+    return 0;
+  }
+}
+
+static void conn_reset_ecn_validation_state(ngtcp2_conn *conn) {
+  conn->tx.ecn.state = NGTCP2_ECN_STATE_TESTING;
+  conn->tx.ecn.validation_start_ts = UINT64_MAX;
+  conn->tx.ecn.start_pkt_num = INT64_MAX;
+  conn->tx.ecn.dgram_sent = 0;
+  conn->tx.ecn.dgram_lost = 0;
+}
+
+static void conn_record_tx_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+                               const ngtcp2_pkt_hd *hd) {
+  ++pktns->tx.ecn.ect0;
+  if (conn->tx.ecn.start_pkt_num == INT64_MAX) {
+    conn->tx.ecn.start_pkt_num = hd->pkt_num;
+  }
+}
+
 static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
                     const ngtcp2_cid *scid, const ngtcp2_path *path,
                     uint32_t version, const ngtcp2_conn_callbacks *callbacks,
@@ -818,6 +896,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
   (*pconn)->idle_ts = settings->initial_ts;
   (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX;
 
+  conn_reset_ecn_validation_state(*pconn);
+
   ngtcp2_qlog_start(&(*pconn)->qlog, server ? &settings->qlog.odcid : dcid,
                     server);
 
@@ -1155,7 +1235,14 @@ static int conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr,
 
   ack = &conn->tx.ack->ack;
 
-  ack->type = NGTCP2_FRAME_ACK;
+  if (pktns->rx.ecn.ect0 || pktns->rx.ecn.ect1 || pktns->rx.ecn.ce) {
+    ack->type = NGTCP2_FRAME_ACK_ECN;
+    ack->ecn.ect0 = pktns->rx.ecn.ect0;
+    ack->ecn.ect1 = pktns->rx.ecn.ect1;
+    ack->ecn.ce = pktns->rx.ecn.ce;
+  } else {
+    ack->type = NGTCP2_FRAME_ACK;
+  }
   ack->num_blks = 0;
 
   rpkt = ngtcp2_ksl_it_get(&it);
@@ -2039,6 +2126,11 @@ build_pkt:
   ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)spktlen);
 
   if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) {
+    if (conn_should_send_ecn(conn, ts)) {
+      rtb_entry_flags |= NGTCP2_RTB_FLAG_ECN;
+      conn_record_tx_ecn(conn, pktns, &hd);
+    }
+
     rv = ngtcp2_rtb_entry_new(&rtbent, &hd, frq, ts, (size_t)spktlen,
                               rtb_entry_flags, conn->mem);
     if (rv != 0) {
@@ -2132,9 +2224,11 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, uint8_t *dest,
     return 0;
   }
 
-  return ngtcp2_conn_write_single_frame_pkt(conn, dest, destlen, type,
-                                            &conn->dcid.current.cid, ackfr,
-                                            NGTCP2_RTB_FLAG_NONE, ts);
+  return ngtcp2_conn_write_single_frame_pkt(
+      conn, dest, destlen, type, &conn->dcid.current.cid, ackfr,
+      conn_should_send_ecn(conn, ts) ? NGTCP2_RTB_FLAG_ECN
+                                     : NGTCP2_RTB_FLAG_NONE,
+      ts);
 }
 
 static void conn_discard_pktns(ngtcp2_conn *conn, ngtcp2_pktns **ppktns,
@@ -3218,6 +3312,11 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, uint8_t *dest,
   /* TODO ack-eliciting vs needs-tracking */
   /* probe packet needs tracking but it does not need ACK, could be lost. */
   if ((rtb_entry_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) {
+    if (conn_should_send_ecn(conn, ts)) {
+      rtb_entry_flags |= NGTCP2_RTB_FLAG_ECN;
+      conn_record_tx_ecn(conn, pktns, hd);
+    }
+
     rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite,
                               rtb_entry_flags, conn->mem);
     if (rv != 0) {
@@ -3376,6 +3475,10 @@ ngtcp2_conn_write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *dest,
   }
 
   if ((rtb_flags & NGTCP2_RTB_FLAG_ACK_ELICITING) || padded) {
+    if (rtb_flags & NGTCP2_RTB_FLAG_ECN) {
+      conn_record_tx_ecn(conn, pktns, &hd);
+    }
+
     rv = ngtcp2_rtb_entry_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, rtb_flags,
                               conn->mem);
     if (rv != 0) {
@@ -3637,9 +3740,9 @@ static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn,
 }
 
 ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, ngtcp2_path *path,
-                                   uint8_t *dest, size_t destlen,
-                                   ngtcp2_tstamp ts) {
-  return ngtcp2_conn_writev_stream(conn, path, dest, destlen,
+                                   ngtcp2_pkt_info *pi, uint8_t *dest,
+                                   size_t destlen, ngtcp2_tstamp ts) {
+  return ngtcp2_conn_writev_stream(conn, path, pi, dest, destlen,
                                    /* pdatalen = */ NULL,
                                    NGTCP2_WRITE_STREAM_FLAG_NONE,
                                    /* stream_id = */ -1,
@@ -3914,6 +4017,7 @@ static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
 
   reset_conn_stat_recovery(&conn->cstat);
   conn_reset_congestion_state(conn);
+  conn_reset_ecn_validation_state(conn);
 
   return 0;
 }
@@ -3967,8 +4071,8 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr,
 
   ngtcp2_acktr_recv_ack(&pktns->acktr, fr);
 
-  num_acked =
-      ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pkt_ts, ts);
+  num_acked = ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pktns,
+                                  pkt_ts, ts);
   if (num_acked < 0) {
     /* TODO assert this */
     assert(ngtcp2_err_is_fatal((int)num_acked));
@@ -3976,6 +4080,12 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr,
     return (int)num_acked;
   }
 
+  if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+    pktns->rx.ecn.ack.ect0 = fr->ecn.ect0;
+    pktns->rx.ecn.ack.ect1 = fr->ecn.ect1;
+    pktns->rx.ecn.ack.ce = fr->ecn.ce;
+  }
+
   if (num_acked == 0) {
     return 0;
   }
@@ -4116,8 +4226,9 @@ static void conn_recv_max_data(ngtcp2_conn *conn, const ngtcp2_max_data *fr) {
  *     Out of memory.
  */
 static int conn_buffer_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
-                           const ngtcp2_path *path, const uint8_t *pkt,
-                           size_t pktlen, ngtcp2_tstamp ts) {
+                           const ngtcp2_path *path, const ngtcp2_pkt_info *pi,
+                           const uint8_t *pkt, size_t pktlen,
+                           ngtcp2_tstamp ts) {
   int rv;
   ngtcp2_pkt_chain **ppc = &pktns->rx.buffed_pkts, *pc;
   size_t i;
@@ -4129,7 +4240,7 @@ static int conn_buffer_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
     return 0;
   }
 
-  rv = ngtcp2_pkt_chain_new(&pc, path, pkt, pktlen, ts, conn->mem);
+  rv = ngtcp2_pkt_chain_new(&pc, path, pi, pkt, pktlen, ts, conn->mem);
   if (rv != 0) {
     return rv;
   }
@@ -4388,6 +4499,7 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr,
     }
     ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid);
     conn_reset_congestion_state(conn);
+    conn_reset_ecn_validation_state(conn);
   }
 
   rv = conn_call_path_validation(conn, &pv->dcid.ps.path,
@@ -4467,12 +4579,28 @@ static int verify_token(const ngtcp2_vec *token, const ngtcp2_pkt_hd *hd) {
   return NGTCP2_ERR_PROTO;
 }
 
+static void pktns_increase_ecn_counts(ngtcp2_pktns *pktns,
+                                      const ngtcp2_pkt_info *pi) {
+  switch (pi->ecn & NGTCP2_ECN_MASK) {
+  case NGTCP2_ECN_ECT_0:
+    ++pktns->rx.ecn.ect0;
+    break;
+  case NGTCP2_ECN_ECT_1:
+    ++pktns->rx.ecn.ect1;
+    break;
+  case NGTCP2_ECN_CE:
+    ++pktns->rx.ecn.ce;
+    break;
+  }
+}
+
 static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level,
                             ngtcp2_strm *strm, const ngtcp2_crypto *fr);
 
 static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
-                                  const uint8_t *pkt, size_t pktlen,
-                                  ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts);
+                                  const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+                                  size_t pktlen, ngtcp2_tstamp pkt_ts,
+                                  ngtcp2_tstamp ts);
 
 static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn,
                                                ngtcp2_pktns *pktns,
@@ -4510,11 +4638,10 @@ static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn,
  * In addition to the above error codes, error codes returned from
  * conn_recv_pkt are also returned.
  */
-static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn,
-                                            const ngtcp2_path *path,
-                                            const uint8_t *pkt, size_t pktlen,
-                                            ngtcp2_tstamp pkt_ts,
-                                            ngtcp2_tstamp ts) {
+static ngtcp2_ssize
+conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
+                        const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+                        size_t pktlen, ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) {
   ngtcp2_ssize nread;
   ngtcp2_pkt_hd hd;
   ngtcp2_max_frame mfr;
@@ -4552,7 +4679,7 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn,
     ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
                     "buffering Short packet len=%zu", pktlen);
 
-    rv = conn_buffer_pkt(conn, &conn->pktns, path, pkt, pktlen, ts);
+    rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, ts);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
       return rv;
@@ -4656,8 +4783,9 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn,
       if (conn->early.ckm) {
         ngtcp2_ssize nread2;
         /* TODO Avoid to parse header twice. */
-        nread2 = conn_recv_pkt(conn, &conn->dcid.current.ps.path, pkt, pktlen,
-                               pkt_ts, ts);
+        /* TODO Why we use conn->dcid.current.ps.path here? */
+        nread2 = conn_recv_pkt(conn, &conn->dcid.current.ps.path, pi, pkt,
+                               pktlen, pkt_ts, ts);
         if (nread2 < 0) {
           return nread2;
         }
@@ -4671,7 +4799,7 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn,
     ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
                     "buffering 0-RTT packet len=%zu", pktlen);
 
-    rv = conn_buffer_pkt(conn, conn->in_pktns, path, pkt, pktlen, ts);
+    rv = conn_buffer_pkt(conn, conn->in_pktns, path, pi, pkt, pktlen, ts);
     if (rv != 0) {
       assert(ngtcp2_err_is_fatal(rv));
       return rv;
@@ -4732,7 +4860,7 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn,
       ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
                       "buffering Handshake packet len=%zu", pktlen);
 
-      rv = conn_buffer_pkt(conn, conn->hs_pktns, path, pkt, pktlen, ts);
+      rv = conn_buffer_pkt(conn, conn->hs_pktns, path, pi, pkt, pktlen, ts);
       if (rv != 0) {
         assert(ngtcp2_err_is_fatal(rv));
         return rv;
@@ -4936,7 +5064,12 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn,
     return rv;
   }
 
-  if (require_ack && ++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT) {
+  pktns_increase_ecn_counts(pktns, pi);
+
+  /* TODO Initial and Handshake are always acknowledged without
+     delay. */
+  if (require_ack && (++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT ||
+                      (pi->ecn & NGTCP2_ECN_MASK) == NGTCP2_ECN_CE)) {
     ngtcp2_acktr_immediate_ack(&pktns->acktr);
   }
 
@@ -4964,6 +5097,7 @@ static ngtcp2_ssize conn_recv_handshake_pkt(ngtcp2_conn *conn,
  * conn_recv_handshake_pkt.
  */
 static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
+                                    const ngtcp2_pkt_info *pi,
                                     const uint8_t *pkt, size_t pktlen,
                                     ngtcp2_tstamp ts) {
   ngtcp2_ssize nread;
@@ -4971,7 +5105,7 @@ static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
   conn->cstat.bytes_recv += pktlen;
 
   while (pktlen) {
-    nread = conn_recv_handshake_pkt(conn, path, pkt, pktlen, ts, ts);
+    nread = conn_recv_handshake_pkt(conn, path, pi, pkt, pktlen, ts, ts);
     if (nread < 0) {
       if (ngtcp2_err_is_fatal((int)nread)) {
         return (int)nread;
@@ -6531,6 +6665,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
                     "path is migrated back to the original path");
     ngtcp2_dcid_copy(&conn->dcid.current, &conn->pv->fallback_dcid);
     conn_reset_congestion_state(conn);
+    conn_reset_ecn_validation_state(conn);
     rv = conn_stop_pv(conn, ts);
     if (rv != 0) {
       return rv;
@@ -6595,6 +6730,7 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn,
   ngtcp2_dcid_copy(&conn->dcid.current, &dcid);
 
   conn_reset_congestion_state(conn);
+  conn_reset_ecn_validation_state(conn);
 
   if (conn->pv) {
     ngtcp2_log_info(
@@ -6759,8 +6895,9 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
  *     Frame has strictly larger end offset than it is permitted.
  */
 static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
-                                  const uint8_t *pkt, size_t pktlen,
-                                  ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) {
+                                  const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+                                  size_t pktlen, ngtcp2_tstamp pkt_ts,
+                                  ngtcp2_tstamp ts) {
   ngtcp2_pkt_hd hd;
   int rv = 0;
   size_t hdpktlen;
@@ -7242,7 +7379,10 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     return rv;
   }
 
-  if (require_ack && ++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT) {
+  pktns_increase_ecn_counts(pktns, pi);
+
+  if (require_ack && (++pktns->acktr.rx_npkt >= NGTCP2_NUM_IMMEDIATE_ACK_PKT ||
+                      (pi->ecn & NGTCP2_ECN_MASK) == NGTCP2_ECN_CE)) {
     ngtcp2_acktr_immediate_ack(&pktns->acktr);
   }
 
@@ -7279,8 +7419,8 @@ static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn,
 
   for (ppc = &pktns->rx.buffed_pkts; *ppc;) {
     next = (*ppc)->next;
-    nread = conn_recv_pkt(conn, &(*ppc)->path.path, (*ppc)->pkt, (*ppc)->pktlen,
-                          (*ppc)->ts, ts);
+    nread = conn_recv_pkt(conn, &(*ppc)->path.path, &(*ppc)->pi, (*ppc)->pkt,
+                          (*ppc)->pktlen, (*ppc)->ts, ts);
     if (nread < 0 && !ngtcp2_err_is_fatal((int)nread) &&
         nread != NGTCP2_ERR_DRAINING) {
       /* TODO We don't know this is the first QUIC packet in a
@@ -7325,8 +7465,9 @@ static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn,
 
   for (ppc = &pktns->rx.buffed_pkts; *ppc;) {
     next = (*ppc)->next;
-    nread = conn_recv_handshake_pkt(conn, &(*ppc)->path.path, (*ppc)->pkt,
-                                    (*ppc)->pktlen, (*ppc)->ts, ts);
+    nread =
+        conn_recv_handshake_pkt(conn, &(*ppc)->path.path, &(*ppc)->pi,
+                                (*ppc)->pkt, (*ppc)->pktlen, (*ppc)->ts, ts);
     ngtcp2_pkt_chain_del(*ppc, conn->mem);
     *ppc = next;
     if (nread < 0) {
@@ -7382,6 +7523,20 @@ static int conn_handshake_completed(ngtcp2_conn *conn) {
     }
   }
 
+  switch (conn->tx.ecn.state) {
+  case NGTCP2_ECN_STATE_TESTING:
+  case NGTCP2_ECN_STATE_UNKNOWN:
+    /* We might not have sufficient ECN marked packet in 1RTT packets.
+       In this case, we might wrongly conclude that the path is not
+       ECN capable.  Validate it again. */
+    if (conn->pktns.tx.ecn.ect0 < 3) {
+      conn_reset_ecn_validation_state(conn);
+    }
+    break;
+  default:
+    break;
+  }
+
   return 0;
 }
 
@@ -7395,7 +7550,8 @@ static int conn_handshake_completed(ngtcp2_conn *conn) {
  * codes from conn_recv_pkt except for NGTCP2_ERR_DISCARD_PKT.
  */
 static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
-                          const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts) {
+                          const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+                          size_t pktlen, ngtcp2_tstamp ts) {
   ngtcp2_ssize nread;
   int rv;
   const uint8_t *origpkt = pkt;
@@ -7404,7 +7560,7 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path,
   conn->cstat.bytes_recv += pktlen;
 
   while (pktlen) {
-    nread = conn_recv_pkt(conn, path, pkt, pktlen, ts, ts);
+    nread = conn_recv_pkt(conn, path, pi, pkt, pktlen, ts, ts);
     if (nread < 0) {
       if (ngtcp2_err_is_fatal((int)nread)) {
         return (int)nread;
@@ -7489,8 +7645,8 @@ static int conn_enqueue_handshake_done(ngtcp2_conn *conn) {
  * negative error codes: (TBD).
  */
 static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
-                               const uint8_t *pkt, size_t pktlen,
-                               ngtcp2_tstamp ts) {
+                               const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+                               size_t pktlen, ngtcp2_tstamp ts) {
   int rv;
 
   switch (conn->state) {
@@ -7498,7 +7654,7 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
     /* TODO Better to log something when we ignore input */
     return 0;
   case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE:
-    rv = conn_recv_handshake_cpkt(conn, path, pkt, pktlen, ts);
+    rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
     if (rv < 0) {
       return rv;
     }
@@ -7519,7 +7675,7 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
 
     return 0;
   case NGTCP2_CS_SERVER_INITIAL:
-    rv = conn_recv_handshake_cpkt(conn, path, pkt, pktlen, ts);
+    rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
     if (rv < 0) {
       return rv;
     }
@@ -7562,7 +7718,7 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
 
     return 0;
   case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
-    rv = conn_recv_handshake_cpkt(conn, path, pkt, pktlen, ts);
+    rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts);
     if (rv < 0) {
       return rv;
     }
@@ -7648,7 +7804,8 @@ static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path,
 }
 
 int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
-                         const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts) {
+                         const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+                         size_t pktlen, ngtcp2_tstamp ts) {
   int rv = 0;
 
   conn->log.last_ts = ts;
@@ -7677,7 +7834,7 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
   case NGTCP2_CS_SERVER_INITIAL:
   case NGTCP2_CS_SERVER_WAIT_HANDSHAKE:
   case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED:
-    return conn_read_handshake(conn, path, pkt, pktlen, ts);
+    return conn_read_handshake(conn, path, pi, pkt, pktlen, ts);
   case NGTCP2_CS_CLOSING:
     return NGTCP2_ERR_CLOSING;
   case NGTCP2_CS_DRAINING:
@@ -7687,7 +7844,7 @@ int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path,
     if (rv != 0) {
       return rv;
     }
-    return conn_recv_cpkt(conn, path, pkt, pktlen, ts);
+    return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts);
   default:
     assert(0);
   }
@@ -8746,23 +8903,24 @@ ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id) {
 }
 
 ngtcp2_ssize ngtcp2_conn_write_stream(ngtcp2_conn *conn, ngtcp2_path *path,
-                                      uint8_t *dest, size_t destlen,
-                                      ngtcp2_ssize *pdatalen, uint32_t flags,
-                                      int64_t stream_id, const uint8_t *data,
-                                      size_t datalen, ngtcp2_tstamp ts) {
+                                      ngtcp2_pkt_info *pi, uint8_t *dest,
+                                      size_t destlen, ngtcp2_ssize *pdatalen,
+                                      uint32_t flags, int64_t stream_id,
+                                      const uint8_t *data, size_t datalen,
+                                      ngtcp2_tstamp ts) {
   ngtcp2_vec datav;
 
   datav.len = datalen;
   datav.base = (uint8_t *)data;
 
-  return ngtcp2_conn_writev_stream(conn, path, dest, destlen, pdatalen, flags,
-                                   stream_id, &datav, 1, ts);
+  return ngtcp2_conn_writev_stream(conn, path, pi, dest, destlen, pdatalen,
+                                   flags, stream_id, &datav, 1, ts);
 }
 
 ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path,
-                                       uint8_t *dest, size_t destlen,
-                                       ngtcp2_ssize *pdatalen, uint32_t flags,
-                                       int64_t stream_id,
+                                       ngtcp2_pkt_info *pi, uint8_t *dest,
+                                       size_t destlen, ngtcp2_ssize *pdatalen,
+                                       uint32_t flags, int64_t stream_id,
                                        const ngtcp2_vec *datav, size_t datavcnt,
                                        ngtcp2_tstamp ts) {
   ngtcp2_vmsg vmsg, *pvmsg;
@@ -8794,12 +8952,13 @@ ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path,
     pvmsg = NULL;
   }
 
-  return ngtcp2_conn_write_vmsg(conn, path, dest, destlen, pvmsg, ts);
+  return ngtcp2_conn_write_vmsg(conn, path, pi, dest, destlen, pvmsg, ts);
 }
 
 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_pkt_info *pi, uint8_t *dest,
+                                    size_t destlen, ngtcp2_vmsg *vmsg,
+                                    ngtcp2_tstamp ts) {
   ngtcp2_ssize nwrite;
   ngtcp2_pktns *pktns = &conn->pktns;
   size_t origlen = destlen;
@@ -8821,7 +8980,11 @@ 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, dest, destlen, vmsg, ts);
-    if (nwrite < 0 || conn->state != NGTCP2_CS_POST_HANDSHAKE) {
+    if (nwrite < 0) {
+      return nwrite;
+    }
+    if (conn->state != NGTCP2_CS_POST_HANDSHAKE) {
+      conn_set_ecn(conn, pi, ts);
       return nwrite;
     }
     res = nwrite;
@@ -8853,6 +9016,9 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
     }
     if (conn->state != NGTCP2_CS_POST_HANDSHAKE &&
         conn->pktns.crypto.tx.ckm == NULL) {
+      if (res) {
+        conn_set_ecn(conn, pi, ts);
+      }
       return res;
     }
     break;
@@ -8959,7 +9125,11 @@ fin:
 
   if (nwrite >= 0) {
     conn->cstat.bytes_sent += (size_t)nwrite;
-    return res + nwrite;
+    res += nwrite;
+    if (res) {
+      conn_set_ecn(conn, pi, ts);
+    }
+    return res;
   }
   /* NGTCP2_CONN_FLAG_PPE_PENDING is set in conn_write_pkt above.
      ppe_pending cannot be used here. */
@@ -9881,6 +10051,7 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
   }
 
   conn_reset_congestion_state(conn);
+  conn_reset_ecn_validation_state(conn);
 
   return 0;
 }
diff --git a/lib/ngtcp2_conn.h b/lib/ngtcp2_conn.h
index d7782b11..391461d0 100644
--- a/lib/ngtcp2_conn.h
+++ b/lib/ngtcp2_conn.h
@@ -108,6 +108,10 @@ typedef enum {
    ACK-eliciting packets. */
 #define NGTCP2_MAX_NON_ACK_TX_PKT 3
 
+/* NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS is the maximum number of ECN marked
+   packets sent in NGTCP2_ECN_STATE_TESTING period. */
+#define NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS 10
+
 /*
  * ngtcp2_max_frame is defined so that it covers the largest ACK
  * frame.
@@ -195,6 +199,12 @@ typedef struct ngtcp2_pktns {
     /* num_non_ack_pkt is the number of continuous non ACK-eliciting
        packets. */
     size_t num_non_ack_pkt;
+
+    struct {
+      /* ect0 is the number of QUIC packets, not UDP datagram, which
+         are sent in UDP datagram with ECT0 marking. */
+      size_t ect0;
+    } ecn;
   } tx;
 
   struct {
@@ -227,6 +237,21 @@ typedef struct ngtcp2_pktns {
      *   ngtcp2_pktns.
      */
     ngtcp2_pkt_chain *buffed_pkts;
+
+    struct {
+      /* ect0, ect1, and ce are the number of QUIC packets received
+         with those markings. */
+      size_t ect0;
+      size_t ect1;
+      size_t ce;
+      struct {
+        /* ect0, ect1, ce are the ECN counts received in the latest
+           ACK frame. */
+        size_t ect0;
+        size_t ect1;
+        size_t ce;
+      } ack;
+    } ecn;
   } rx;
 
   struct {
@@ -259,6 +284,13 @@ typedef struct ngtcp2_pktns {
   ngtcp2_rtb rtb;
 } ngtcp2_pktns;
 
+typedef enum ngtcp2_ecn_state {
+  NGTCP2_ECN_STATE_TESTING,
+  NGTCP2_ECN_STATE_UNKNOWN,
+  NGTCP2_ECN_STATE_FAILED,
+  NGTCP2_ECN_STATE_CAPABLE,
+} ngtcp2_ecn_state;
+
 struct ngtcp2_conn {
   ngtcp2_conn_state state;
   ngtcp2_conn_callbacks callbacks;
@@ -329,6 +361,23 @@ struct ngtcp2_conn {
     /* max_offset is the maximum offset that local endpoint can
        send. */
     uint64_t max_offset;
+
+    struct {
+      /* state is the state of ECN validation */
+      ngtcp2_ecn_state state;
+      /* validation_start_ts is the timestamp when ECN validation is
+         started.  It is UINT64_MAX if it has not started yet. */
+      ngtcp2_tstamp validation_start_ts;
+      /* start_pkt_num is the lowest packet number that are sent
+         during ECN validation period. */
+      int64_t start_pkt_num;
+      /* dgram_sent is the number of UDP datagram sent during ECN
+         validation period. */
+      size_t dgram_sent;
+      /* dgram_lost is the number of UDP datagram lost during ECN
+         validation period. */
+      size_t dgram_lost;
+    } ecn;
   } tx;
 
   struct {
@@ -629,8 +678,9 @@ int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm);
 ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn);
 
 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_pkt_info *pi, uint8_t *dest,
+                                    size_t destlen, ngtcp2_vmsg *vmsg,
+                                    ngtcp2_tstamp ts);
 
 /*
  * ngtcp2_conn_write_single_frame_pkt writes a packet which contains |fr|
diff --git a/lib/ngtcp2_log.c b/lib/ngtcp2_log.c
index 8b7ec0de..c8532214 100644
--- a/lib/ngtcp2_log.c
+++ b/lib/ngtcp2_log.c
@@ -242,6 +242,14 @@ static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
                     NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack,
                     min_ack, blk->gap, blk->blklen);
   }
+
+  if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+    log->log_printf(log->user_data,
+                    (NGTCP2_LOG_PKT " ACK(0x%02x) ect0=%" PRIu64
+                                    " ect1=%" PRIu64 " ce=%" PRIu64),
+                    NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->ecn.ect0,
+                    fr->ecn.ect1, fr->ecn.ce);
+  }
 }
 
 static void log_fr_padding(ngtcp2_log *log, const ngtcp2_pkt_hd *hd,
diff --git a/lib/ngtcp2_pkt.c b/lib/ngtcp2_pkt.c
index 905af29a..257f22e8 100644
--- a/lib/ngtcp2_pkt.c
+++ b/lib/ngtcp2_pkt.c
@@ -35,7 +35,8 @@
 #include "ngtcp2_mem.h"
 
 int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path,
-                         const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts,
+                         const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+                         size_t pktlen, ngtcp2_tstamp ts,
                          const ngtcp2_mem *mem) {
   *ppc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pkt_chain) + pktlen);
   if (*ppc == NULL) {
@@ -43,6 +44,7 @@ int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path,
   }
 
   ngtcp2_path_storage_init2(&(*ppc)->path, path);
+  (*ppc)->pi = *pi;
   (*ppc)->next = NULL;
   (*ppc)->pkt = (uint8_t *)(*ppc) + sizeof(ngtcp2_pkt_chain);
   (*ppc)->pktlen = pktlen;
@@ -734,11 +736,14 @@ ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest,
   }
 
   if (type == NGTCP2_FRAME_ACK_ECN) {
-    /* Just parse ECN section for now */
-    for (i = 0; i < 3; ++i) {
-      ngtcp2_get_varint(&n, p);
-      p += n;
-    }
+    dest->ecn.ect0 = ngtcp2_get_varint(&n, p);
+    p += n;
+
+    dest->ecn.ect1 = ngtcp2_get_varint(&n, p);
+    p += n;
+
+    dest->ecn.ce = ngtcp2_get_varint(&n, p);
+    p += n;
   }
 
   assert((size_t)(p - payload) == len);
@@ -1381,6 +1386,7 @@ ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen,
   case NGTCP2_FRAME_STREAM:
     return ngtcp2_pkt_encode_stream_frame(out, outlen, &fr->stream);
   case NGTCP2_FRAME_ACK:
+  case NGTCP2_FRAME_ACK_ECN:
     return ngtcp2_pkt_encode_ack_frame(out, outlen, &fr->ack);
   case NGTCP2_FRAME_PADDING:
     return ngtcp2_pkt_encode_padding_frame(out, outlen, &fr->padding);
@@ -1506,13 +1512,19 @@ ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen,
     len += ngtcp2_put_varint_len(blk->blklen);
   }
 
+  if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+    len += ngtcp2_put_varint_len(fr->ecn.ect0) +
+           ngtcp2_put_varint_len(fr->ecn.ect1) +
+           ngtcp2_put_varint_len(fr->ecn.ce);
+  }
+
   if (outlen < len) {
     return NGTCP2_ERR_NOBUF;
   }
 
   p = out;
 
-  *p++ = NGTCP2_FRAME_ACK;
+  *p++ = fr->type;
   p = ngtcp2_put_varint(p, (uint64_t)fr->largest_ack);
   p = ngtcp2_put_varint(p, fr->ack_delay);
   p = ngtcp2_put_varint(p, fr->num_blks);
@@ -1524,6 +1536,12 @@ ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen,
     p = ngtcp2_put_varint(p, blk->blklen);
   }
 
+  if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+    p = ngtcp2_put_varint(p, fr->ecn.ect0);
+    p = ngtcp2_put_varint(p, fr->ecn.ect1);
+    p = ngtcp2_put_varint(p, fr->ecn.ce);
+  }
+
   assert((size_t)(p - out) == len);
 
   return (ngtcp2_ssize)len;
diff --git a/lib/ngtcp2_pkt.h b/lib/ngtcp2_pkt.h
index 7348cb9a..ffdd5541 100644
--- a/lib/ngtcp2_pkt.h
+++ b/lib/ngtcp2_pkt.h
@@ -167,6 +167,11 @@ typedef struct {
    * 2**ack_delay_component * NGTCP2_MICROSECONDS.
    */
   ngtcp2_duration ack_delay_unscaled;
+  struct {
+    uint64_t ect0;
+    uint64_t ect1;
+    uint64_t ce;
+  } ecn;
   uint64_t first_ack_blklen;
   size_t num_blks;
   ngtcp2_ack_blk blks[1];
@@ -315,6 +320,7 @@ typedef struct ngtcp2_pkt_chain ngtcp2_pkt_chain;
  */
 struct ngtcp2_pkt_chain {
   ngtcp2_path_storage path;
+  ngtcp2_pkt_info pi;
   ngtcp2_pkt_chain *next;
   uint8_t *pkt;
   size_t pktlen;
@@ -335,7 +341,8 @@ struct ngtcp2_pkt_chain {
  *     Out of memory.
  */
 int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path,
-                         const uint8_t *pkt, size_t pktlen, ngtcp2_tstamp ts,
+                         const ngtcp2_pkt_info *pi, const uint8_t *pkt,
+                         size_t pktlen, ngtcp2_tstamp ts,
                          const ngtcp2_mem *mem);
 
 /*
diff --git a/lib/ngtcp2_qlog.c b/lib/ngtcp2_qlog.c
index 580b6a6f..ca41c4d7 100644
--- a/lib/ngtcp2_qlog.c
+++ b/lib/ngtcp2_qlog.c
@@ -328,9 +328,13 @@ static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) {
    *
    * each range:
    * ["0000000000000000000","0000000000000000000"],
+   *
+   * ecn:
+   * ,"ect1":"0000000000000000000","ect0":"0000000000000000000","ce":"0000000000000000000"
    */
 #define NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD 70
 #define NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD 46
+#define NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD 85
 
   *p++ = '{';
   /* TODO Handle ACK ECN */
@@ -370,6 +374,16 @@ static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) {
   }
 
   *p++ = ']';
+
+  if (fr->type == NGTCP2_FRAME_ACK_ECN) {
+    *p++ = ',';
+    p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "ect1"), fr->ecn.ect1);
+    *p++ = ',';
+    p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "ect0"), fr->ecn.ect0);
+    *p++ = ',';
+    p = write_pair_numstr(p, ngtcp2_vec_lit(&name, "ce"), fr->ecn.ce);
+  }
+
   *p++ = '}';
 
   return p;
@@ -846,6 +860,9 @@ void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) {
   case NGTCP2_FRAME_ACK_ECN:
     if (ngtcp2_buf_left(&qlog->buf) <
         NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD +
+            (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) {
       return;
diff --git a/lib/ngtcp2_rtb.c b/lib/ngtcp2_rtb.c
index d58a6b2e..da206148 100644
--- a/lib/ngtcp2_rtb.c
+++ b/lib/ngtcp2_rtb.c
@@ -662,9 +662,35 @@ static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
   }
 }
 
+static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+                            const ngtcp2_ack *fr, size_t ecn_acked) {
+  if (conn->tx.ecn.state == NGTCP2_ECN_STATE_FAILED) {
+    return;
+  }
+
+  if ((ecn_acked && fr->type == NGTCP2_FRAME_ACK) ||
+      pktns->rx.ecn.ack.ect0 > fr->ecn.ect0 ||
+      pktns->rx.ecn.ack.ect1 > fr->ecn.ect1 ||
+      pktns->rx.ecn.ack.ce > fr->ecn.ce ||
+      (fr->ecn.ect0 - pktns->rx.ecn.ack.ect0) +
+              (fr->ecn.ce - pktns->rx.ecn.ack.ce) <
+          ecn_acked) {
+    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
+                    "path is not ECN capable");
+    conn->tx.ecn.state = NGTCP2_ECN_STATE_FAILED;
+    return;
+  }
+
+  if (conn->tx.ecn.state != NGTCP2_ECN_STATE_CAPABLE && ecn_acked) {
+    ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "path is ECN capable");
+    conn->tx.ecn.state = NGTCP2_ECN_STATE_CAPABLE;
+  }
+}
+
 ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
                                  ngtcp2_conn_stat *cstat, ngtcp2_conn *conn,
-                                 ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) {
+                                 ngtcp2_pktns *pktns, ngtcp2_tstamp pkt_ts,
+                                 ngtcp2_tstamp ts) {
   ngtcp2_rtb_entry *ent;
   int64_t largest_ack = fr->largest_ack, min_ack;
   size_t i;
@@ -676,6 +702,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
   ngtcp2_cc *cc = rtb->cc;
   ngtcp2_rtb_entry *acked_ent = NULL;
   int ack_eliciting_pkt_acked = 0;
+  size_t ecn_acked = 0;
+  int verify_ecn = 0;
 
   if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) &&
       largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) {
@@ -685,8 +713,10 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
     ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed");
   }
 
-  rtb->largest_acked_tx_pkt_num =
-      ngtcp2_max(rtb->largest_acked_tx_pkt_num, largest_ack);
+  if (rtb->largest_acked_tx_pkt_num < largest_ack) {
+    rtb->largest_acked_tx_pkt_num = largest_ack;
+    verify_ecn = 1;
+  }
 
   /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
   it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack);
@@ -758,6 +788,11 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
 
   if (conn) {
     for (ent = acked_ent; ent; ent = acked_ent) {
+      if (ent->hd.pkt_num >= conn->tx.ecn.start_pkt_num &&
+          (ent->flags & NGTCP2_RTB_FLAG_ECN)) {
+        ++ecn_acked;
+      }
+
       rv = rtb_process_acked_pkt(rtb, ent, conn);
       if (rv != 0) {
         goto fail;
@@ -767,6 +802,10 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
       acked_ent = ent->next;
       ngtcp2_rtb_entry_del(ent, rtb->mem);
     }
+
+    if (verify_ecn) {
+      conn_verify_ecn(conn, pktns, fr, ecn_acked);
+    }
   } else {
     /* For unit tests */
     for (ent = acked_ent; ent; ent = acked_ent) {
@@ -836,6 +875,7 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
   int rv;
   uint64_t pkt_thres =
       rtb->cc_bytes_in_flight / cstat->max_udp_payload_size / 2;
+  size_t ecn_dgram_lost = 0;
 
   pkt_thres = ngtcp2_max(pkt_thres, NGTCP2_PKT_THRESHOLD);
   cstat->loss_time[rtb->pktns_id] = UINT64_MAX;
@@ -877,6 +917,11 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
           continue;
         }
 
+        if (ent->hd.pkt_num >= conn->tx.ecn.start_pkt_num &&
+            (ent->flags & NGTCP2_RTB_FLAG_ECN)) {
+          ++ecn_dgram_lost;
+        }
+
         rtb_on_remove(rtb, ent, cstat);
         rv = rtb_on_pkt_lost(rtb, &it, ent, conn, pktns, ts);
         if (rv != 0) {
@@ -884,6 +929,28 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
         }
       }
 
+      switch (conn->tx.ecn.state) {
+      case NGTCP2_ECN_STATE_TESTING:
+        if (conn->tx.ecn.validation_start_ts == UINT64_MAX) {
+          break;
+        }
+        if (ts - conn->tx.ecn.validation_start_ts < 3 * cstat->smoothed_rtt) {
+          conn->tx.ecn.dgram_lost += ecn_dgram_lost;
+          break;
+        }
+        conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN;
+        /* fall through */
+      case NGTCP2_ECN_STATE_UNKNOWN:
+        conn->tx.ecn.dgram_lost += ecn_dgram_lost;
+        assert(conn->tx.ecn.dgram_sent >= conn->tx.ecn.dgram_lost);
+        if (conn->tx.ecn.dgram_sent == conn->tx.ecn.dgram_lost) {
+          conn->tx.ecn.state = NGTCP2_ECN_STATE_FAILED;
+        }
+        break;
+      default:
+        break;
+      }
+
       cc->congestion_event(cc, cstat, latest_ts, ts);
 
       loss_window = latest_ts - oldest_ts;
diff --git a/lib/ngtcp2_rtb.h b/lib/ngtcp2_rtb.h
index 0d0b738f..04f016d5 100644
--- a/lib/ngtcp2_rtb.h
+++ b/lib/ngtcp2_rtb.h
@@ -193,6 +193,9 @@ typedef enum {
   /* NGTCP2_RTB_FLAG_LOST_RETRANSMITTED indicates that the entry has
      been marked lost and scheduled to retransmit. */
   NGTCP2_RTB_FLAG_LOST_RETRANSMITTED = 0x10,
+  /* NGTCP2_RTB_FLAG_ECN indicates that the entry is included in a UDP
+     datagram with ECN marking. */
+  NGTCP2_RTB_FLAG_ECN = 0x20,
 } ngtcp2_rtb_flag;
 
 struct ngtcp2_rtb_entry;
@@ -340,7 +343,8 @@ ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb);
  */
 ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
                                  ngtcp2_conn_stat *cstat, ngtcp2_conn *conn,
-                                 ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts);
+                                 ngtcp2_pktns *pktns, ngtcp2_tstamp pkt_ts,
+                                 ngtcp2_tstamp ts);
 
 /*
  * ngtcp2_rtb_detect_lost_pkt detects lost packets and prepends the
diff --git a/tests/main.c b/tests/main.c
index b6c46b8d..16576022 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -87,6 +87,8 @@ int main() {
                    test_ngtcp2_pkt_encode_stream_frame) ||
       !CU_add_test(pSuite, "pkt_encode_ack_frame",
                    test_ngtcp2_pkt_encode_ack_frame) ||
+      !CU_add_test(pSuite, "pkt_encode_ack_ecn_frame",
+                   test_ngtcp2_pkt_encode_ack_ecn_frame) ||
       !CU_add_test(pSuite, "pkt_encode_reset_stream_frame",
                    test_ngtcp2_pkt_encode_reset_stream_frame) ||
       !CU_add_test(pSuite, "pkt_encode_connection_close_frame",
@@ -264,6 +266,8 @@ int main() {
                    test_ngtcp2_conn_write_application_close) ||
       !CU_add_test(pSuite, "conn_rtb_reclaim_on_pto",
                    test_ngtcp2_conn_rtb_reclaim_on_pto) ||
+      !CU_add_test(pSuite, "conn_validate_ecn",
+                   test_ngtcp2_conn_validate_ecn) ||
       !CU_add_test(pSuite, "pkt_write_connection_close",
                    test_ngtcp2_pkt_write_connection_close) ||
       !CU_add_test(pSuite, "map", test_ngtcp2_map) ||
diff --git a/tests/ngtcp2_conn_test.c b/tests/ngtcp2_conn_test.c
index 1fa0b9e1..31d21b4e 100644
--- a/tests/ngtcp2_conn_test.c
+++ b/tests/ngtcp2_conn_test.c
@@ -123,6 +123,8 @@ static uint8_t null_data[4096];
 static ngtcp2_path_storage null_path;
 static ngtcp2_path_storage new_path;
 
+static ngtcp2_pkt_info null_pi;
+
 void init_static_path(void) {
   path_init(&null_path, 0, 0, 0, 0);
   path_init(&new_path, 1, 0, 2, 0);
@@ -657,7 +659,7 @@ void test_ngtcp2_conn_stream_open_close(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
@@ -671,7 +673,7 @@ void test_ngtcp2_conn_stream_open_close(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NGTCP2_STRM_FLAG_SHUT_RD == strm->flags);
@@ -679,7 +681,7 @@ void test_ngtcp2_conn_stream_open_close(void) {
   CU_ASSERT(fr.stream.offset == ngtcp2_strm_rx_offset(strm));
 
   spktlen =
-      ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+      ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                NGTCP2_WRITE_STREAM_FLAG_FIN, 4, NULL, 0, 3);
 
   CU_ASSERT(spktlen > 0);
@@ -700,7 +702,7 @@ void test_ngtcp2_conn_stream_open_close(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 3, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 3);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
 
   CU_ASSERT(0 == rv);
 
@@ -752,7 +754,7 @@ void test_ngtcp2_conn_stream_rx_flow_control(void) {
 
     pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                     (int64_t)i, &fr);
-    rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+    rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
     CU_ASSERT(0 == rv);
 
@@ -780,7 +782,7 @@ void test_ngtcp2_conn_stream_rx_flow_control(void) {
 
   CU_ASSERT(ngtcp2_strm_is_tx_queued(strm));
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 2);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 2);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(ngtcp2_pq_empty(&conn->tx.strmq));
@@ -817,7 +819,7 @@ void test_ngtcp2_conn_stream_rx_flow_control_error(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
 
@@ -844,32 +846,32 @@ void test_ngtcp2_conn_stream_tx_flow_control(void) {
   CU_ASSERT(0 == rv);
 
   strm = ngtcp2_conn_find_stream(conn, stream_id);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 1024, 1);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 1024, 1);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(1024 == nwrite);
   CU_ASSERT(1024 == strm->tx.offset);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 1024, 2);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 1024, 2);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(1023 == nwrite);
   CU_ASSERT(2047 == strm->tx.offset);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 1024, 3);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 1024, 3);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_DATA_BLOCKED == spktlen);
 
   /* We can write 0 length STREAM frame */
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 0, 3);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 0, 3);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(0 == nwrite);
@@ -881,14 +883,14 @@ void test_ngtcp2_conn_stream_tx_flow_control(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 4);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(2048 == strm->tx.max_offset);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 1024, 5);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 1024, 5);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(1 == nwrite);
@@ -905,9 +907,9 @@ void test_ngtcp2_conn_stream_tx_flow_control(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id,
-                                     null_data, 1024, 1);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_FIN,
+                                     stream_id, null_data, 1024, 1);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(1024 == nwrite);
@@ -939,7 +941,7 @@ void test_ngtcp2_conn_rx_flow_control(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
@@ -958,7 +960,7 @@ void test_ngtcp2_conn_rx_flow_control(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(0 == rv);
 
@@ -967,7 +969,7 @@ void test_ngtcp2_conn_rx_flow_control(void) {
   CU_ASSERT(2048 == conn->rx.unsent_max_offset);
   CU_ASSERT(1024 == conn->rx.max_offset);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 3);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(2048 == conn->rx.max_offset);
@@ -998,7 +1000,7 @@ void test_ngtcp2_conn_rx_flow_control_error(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
 
@@ -1024,33 +1026,33 @@ void test_ngtcp2_conn_tx_flow_control(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 1024, 1);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 1024, 1);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(1024 == nwrite);
   CU_ASSERT(1024 == conn->tx.offset);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 1023, 2);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 1023, 2);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(1023 == nwrite);
   CU_ASSERT(1024 + 1023 == conn->tx.offset);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 1024, 3);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 1024, 3);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(1 == nwrite);
   CU_ASSERT(2048 == conn->tx.offset);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 1024, 4);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 1024, 4);
 
   CU_ASSERT(spktlen == 0);
   CU_ASSERT(-1 == nwrite);
@@ -1060,14 +1062,14 @@ void test_ngtcp2_conn_tx_flow_control(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 5);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 5);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(3072 == conn->tx.max_offset);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 1024, 4);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 1024, 4);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(1024 == nwrite);
@@ -1100,7 +1102,7 @@ void test_ngtcp2_conn_shutdown_stream_write(void) {
   setup_default_client(&conn);
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                            NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, null_data,
                            1239, 1);
   rv = ngtcp2_conn_shutdown_stream_write(conn, stream_id, NGTCP2_APP_ERR01);
@@ -1123,7 +1125,7 @@ void test_ngtcp2_conn_shutdown_stream_write(void) {
   CU_ASSERT(NULL != strm);
   CU_ASSERT(NGTCP2_APP_ERR01 == strm->app_error_code);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 2);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 2);
 
   CU_ASSERT(spktlen > 0);
 
@@ -1135,7 +1137,7 @@ void test_ngtcp2_conn_shutdown_stream_write(void) {
   pktlen =
       write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 890, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id));
@@ -1148,7 +1150,7 @@ void test_ngtcp2_conn_shutdown_stream_write(void) {
 
   pktlen =
       write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 899, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, stream_id));
@@ -1169,7 +1171,7 @@ void test_ngtcp2_conn_shutdown_stream_write(void) {
   pktlen =
       write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 119, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id));
@@ -1179,7 +1181,7 @@ void test_ngtcp2_conn_shutdown_stream_write(void) {
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id));
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 2);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 2);
 
   CU_ASSERT(spktlen > 0);
 
@@ -1191,7 +1193,7 @@ void test_ngtcp2_conn_shutdown_stream_write(void) {
 
   pktlen =
       write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 121, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id));
@@ -1205,7 +1207,7 @@ void test_ngtcp2_conn_shutdown_stream_write(void) {
   pktlen =
       write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 332, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 3);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, stream_id));
@@ -1236,11 +1238,11 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
-  ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                            NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2);
 
   fr.type = NGTCP2_FRAME_RESET_STREAM;
@@ -1249,7 +1251,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 955;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 3);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
 
   CU_ASSERT(0 == rv);
 
@@ -1273,14 +1275,14 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
-  ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                            NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2);
   ngtcp2_conn_shutdown_stream_read(conn, 4, NGTCP2_APP_ERR01);
-  ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 3);
+  ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3);
 
   fr.type = NGTCP2_FRAME_RESET_STREAM;
   fr.reset_stream.stream_id = 4;
@@ -1288,7 +1290,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 955;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 4);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
@@ -1308,14 +1310,14 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
-  ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                            NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2);
   ngtcp2_conn_shutdown_stream_write(conn, 4, NGTCP2_APP_ERR01);
-  ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 3);
+  ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3);
 
   fr.type = NGTCP2_FRAME_RESET_STREAM;
   fr.reset_stream.stream_id = 4;
@@ -1323,7 +1325,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 955;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 4);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
@@ -1335,7 +1337,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.ack.num_blks = 0;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 3, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 5);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 5);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 4));
@@ -1355,11 +1357,11 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
-  ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                            NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2);
 
   fr.type = NGTCP2_FRAME_STOP_SENDING;
@@ -1367,12 +1369,12 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 3);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 4);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 4);
 
   CU_ASSERT(spktlen > 0);
 
@@ -1382,7 +1384,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 955;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 3, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 4);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
@@ -1394,7 +1396,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.ack.num_blks = 0;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 4, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 5);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 5);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 4));
@@ -1414,7 +1416,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
@@ -1424,7 +1426,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 954;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(NGTCP2_ERR_FINAL_SIZE == rv);
 
@@ -1444,7 +1446,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
@@ -1454,7 +1456,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 956;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(NGTCP2_ERR_FINAL_SIZE == rv);
 
@@ -1469,7 +1471,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 0;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
 
@@ -1484,7 +1486,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 1999;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 0));
@@ -1502,7 +1504,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 0;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_LIMIT == rv);
 
@@ -1518,7 +1520,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 0;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(
@@ -1540,7 +1542,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 1 << 20;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
 
@@ -1559,7 +1561,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 1 << 20;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
 
@@ -1582,7 +1584,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
@@ -1592,7 +1594,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 1024 * 1024;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
 
@@ -1613,7 +1615,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
@@ -1623,7 +1625,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 1024 * 1024;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv);
 
@@ -1643,7 +1645,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 0;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_PROTO == rv);
 
@@ -1662,7 +1664,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
@@ -1672,7 +1674,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 1024;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(128 * 1024 + 1024 == conn->rx.unsent_max_offset);
@@ -1693,14 +1695,14 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.stream.data[0].base = null_data;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
-  ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                            NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2);
   ngtcp2_conn_shutdown_stream_read(conn, 4, NGTCP2_APP_ERR01);
-  ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 3);
+  ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3);
 
   CU_ASSERT(128 * 1024 + 956 == conn->rx.unsent_max_offset);
 
@@ -1710,7 +1712,7 @@ void test_ngtcp2_conn_recv_reset_stream(void) {
   fr.reset_stream.final_size = 957;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 4);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
@@ -1736,7 +1738,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
   setup_default_client(&conn);
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                            NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, null_data,
                            333, ++t);
 
@@ -1746,7 +1748,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -1771,7 +1773,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
   setup_default_client(&conn);
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                            NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, null_data,
                            333, ++t);
 
@@ -1782,7 +1784,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -1792,7 +1794,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id));
@@ -1807,7 +1809,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
   CU_ASSERT(NGTCP2_APP_ERR01 == frc->fr.reset_stream.app_error_code);
   CU_ASSERT(333 == frc->fr.reset_stream.final_size);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -1819,7 +1821,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, stream_id));
@@ -1835,7 +1837,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
   fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
 
@@ -1855,7 +1857,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
   fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
 
@@ -1873,7 +1875,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
   fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NGTCP2_FRAME_RESET_STREAM == conn->pktns.tx.frq->fr.type);
@@ -1889,7 +1891,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
   fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
 
@@ -1912,7 +1914,7 @@ void test_ngtcp2_conn_recv_stop_sending(void) {
   fr.stop_sending.app_error_code = NGTCP2_APP_ERR01;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL == conn->pktns.tx.frq);
@@ -1944,7 +1946,7 @@ void test_ngtcp2_conn_recv_conn_id_omitted(void) {
 
   pktlen =
       write_single_frame_pkt_without_conn_id(conn, buf, sizeof(buf), 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   /* packet is just ignored */
   CU_ASSERT(0 == rv);
@@ -1962,7 +1964,7 @@ void test_ngtcp2_conn_recv_conn_id_omitted(void) {
 
   pktlen =
       write_single_frame_pkt_without_conn_id(conn, buf, sizeof(buf), 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
@@ -1981,7 +1983,7 @@ void test_ngtcp2_conn_short_pkt_type(void) {
   setup_default_client(&conn);
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 19, 1);
 
@@ -1998,7 +2000,7 @@ void test_ngtcp2_conn_short_pkt_type(void) {
   conn->pktns.tx.last_pkt_num = 0x6afd78;
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 19, 1);
 
@@ -2015,7 +2017,7 @@ void test_ngtcp2_conn_short_pkt_type(void) {
   conn->pktns.tx.last_pkt_num = 0x6bc106;
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 19, 1);
 
@@ -2032,7 +2034,7 @@ void test_ngtcp2_conn_short_pkt_type(void) {
   conn->pktns.tx.last_pkt_num = 127;
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 19, 1);
 
@@ -2049,7 +2051,7 @@ void test_ngtcp2_conn_short_pkt_type(void) {
   conn->pktns.tx.last_pkt_num = 128;
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 19, 1);
 
@@ -2066,7 +2068,7 @@ void test_ngtcp2_conn_short_pkt_type(void) {
   conn->pktns.tx.last_pkt_num = 32767;
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 19, 1);
 
@@ -2083,7 +2085,7 @@ void test_ngtcp2_conn_short_pkt_type(void) {
   conn->pktns.tx.last_pkt_num = 32768;
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 19, 1);
 
@@ -2100,7 +2102,7 @@ void test_ngtcp2_conn_short_pkt_type(void) {
   conn->pktns.tx.last_pkt_num = 8388607;
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 19, 1);
 
@@ -2117,7 +2119,7 @@ void test_ngtcp2_conn_short_pkt_type(void) {
   conn->pktns.tx.last_pkt_num = 8388608;
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 19, 1);
 
@@ -2134,7 +2136,7 @@ void test_ngtcp2_conn_short_pkt_type(void) {
   conn->pktns.tx.last_pkt_num = NGTCP2_MAX_PKT_NUM - 1;
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 19, 1);
 
@@ -2170,7 +2172,8 @@ void test_ngtcp2_conn_recv_stateless_reset(void) {
 
   CU_ASSERT(spktlen > 0);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, (size_t)spktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+                            (size_t)spktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_DRAINING == rv);
   CU_ASSERT(NGTCP2_CS_DRAINING == conn->state);
@@ -2189,7 +2192,8 @@ void test_ngtcp2_conn_recv_stateless_reset(void) {
 
   CU_ASSERT(spktlen > 0);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, (size_t)spktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+                            (size_t)spktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_DRAINING == rv);
   CU_ASSERT(NGTCP2_CS_DRAINING == conn->state);
@@ -2211,7 +2215,8 @@ void test_ngtcp2_conn_recv_stateless_reset(void) {
   /* long packet */
   buf[0] |= NGTCP2_HEADER_FORM_BIT;
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, (size_t)spktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+                            (size_t)spktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_DRAINING == rv);
   CU_ASSERT(NGTCP2_CS_DRAINING == conn->state);
@@ -2239,7 +2244,8 @@ void test_ngtcp2_conn_recv_stateless_reset(void) {
   /* Make largest CID so that ngtcp2_pkt_decode_hd_long fails */
   buf[5] = 0xff;
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, (size_t)spktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+                            (size_t)spktlen, 1);
 
   CU_ASSERT(NGTCP2_ERR_DRAINING == rv);
   CU_ASSERT(NGTCP2_CS_DRAINING == conn->state);
@@ -2256,7 +2262,8 @@ void test_ngtcp2_conn_recv_stateless_reset(void) {
 
   CU_ASSERT(spktlen > 0);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, (size_t)spktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+                            (size_t)spktlen, 1);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NGTCP2_CS_DRAINING != conn->state);
@@ -2284,7 +2291,7 @@ void test_ngtcp2_conn_recv_retry(void) {
   setup_handshake_client(&conn);
   conn->callbacks.recv_retry = recv_retry;
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -2295,11 +2302,12 @@ void test_ngtcp2_conn_recv_retry(void) {
   CU_ASSERT(spktlen > 0);
 
   for (i = 0; i < 2; ++i) {
-    rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, (size_t)spktlen, ++t);
+    rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+                              (size_t)spktlen, ++t);
 
     CU_ASSERT(0 == rv);
 
-    spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+    spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
     if (i == 1) {
       /* Retry packet was ignored */
@@ -2318,7 +2326,7 @@ void test_ngtcp2_conn_recv_retry(void) {
   setup_handshake_client(&conn);
   conn->callbacks.recv_retry = recv_retry;
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -2331,11 +2339,12 @@ void test_ngtcp2_conn_recv_retry(void) {
   /* Change tag */
   buf[spktlen - 1] = 1;
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, (size_t)spktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+                            (size_t)spktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(0 == spktlen);
 
@@ -2349,16 +2358,18 @@ void test_ngtcp2_conn_recv_retry(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen,
-                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                      null_datav(&datav, 219), 1, ++t);
+  spktlen =
+      ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), &datalen,
+                                NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+                                null_datav(&datav, 219), 1, ++t);
 
   CU_ASSERT(sizeof(buf) == spktlen);
   CU_ASSERT(219 == datalen);
 
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen,
-                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                      null_datav(&datav, 119), 1, ++t);
+  spktlen =
+      ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), &datalen,
+                                NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+                                null_datav(&datav, 119), 1, ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(119 == datalen);
@@ -2369,11 +2380,12 @@ void test_ngtcp2_conn_recv_retry(void) {
 
   CU_ASSERT(spktlen > 0);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, (size_t)spktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+                            (size_t)spktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 219 + 119);
   CU_ASSERT(2 == conn->pktns.tx.last_pkt_num);
@@ -2383,9 +2395,9 @@ void test_ngtcp2_conn_recv_retry(void) {
   CU_ASSERT(0 == ngtcp2_ksl_len(strm->tx.streamfrq));
 
   /* ngtcp2_conn_write_stream sends new 0RTT packet. */
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &datalen,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 120, ++t);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &datalen, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 120, ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(3 == conn->pktns.tx.last_pkt_num);
@@ -2414,7 +2426,7 @@ void test_ngtcp2_conn_recv_delayed_handshake_pkt(void) {
   pktlen = write_single_frame_handshake_pkt(
       conn, buf, sizeof(buf), NGTCP2_PKT_HANDSHAKE, &conn->oscid,
       ngtcp2_conn_get_dcid(conn), 1, NGTCP2_PROTO_VER_MAX, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(1 == ngtcp2_ksl_len(&conn->hs_pktns->acktr.ents));
@@ -2435,7 +2447,7 @@ void test_ngtcp2_conn_recv_max_streams(void) {
   fr.max_streams.max_streams = 999;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 1);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(999 == conn->local.uni.max_streams);
@@ -2444,7 +2456,7 @@ void test_ngtcp2_conn_recv_max_streams(void) {
   fr.max_streams.max_streams = 997;
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 2, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, 2);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(997 == conn->local.bidi.max_streams);
@@ -2476,11 +2488,11 @@ void test_ngtcp2_conn_handshake(void) {
       conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid,
       ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -2503,7 +2515,7 @@ void test_ngtcp2_conn_handshake_error(void) {
   /* client side */
   setup_handshake_client(&conn);
   conn->callbacks.recv_crypto_data = recv_crypto_handshake_error;
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -2517,7 +2529,7 @@ void test_ngtcp2_conn_handshake_error(void) {
       conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &conn->oscid,
       ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_CRYPTO == rv);
 
@@ -2537,7 +2549,7 @@ void test_ngtcp2_conn_handshake_error(void) {
       conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid,
       ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_CRYPTO == rv);
 
@@ -2557,7 +2569,7 @@ void test_ngtcp2_conn_handshake_error(void) {
       conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid,
       ngtcp2_conn_get_dcid(conn), ++pkt_num, 0xffff, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv);
 
@@ -2576,7 +2588,7 @@ void test_ngtcp2_conn_retransmit_protected(void) {
   setup_default_client(&conn);
 
   ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), NULL,
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL,
                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                      null_data, 126, ++t);
 
@@ -2588,7 +2600,7 @@ void test_ngtcp2_conn_retransmit_protected(void) {
   conn->pktns.rtb.largest_acked_tx_pkt_num = 1000000007;
   it = ngtcp2_rtb_head(&conn->pktns.rtb);
   ngtcp2_conn_detect_lost_pkt(conn, &conn->pktns, &conn->cstat, ++t);
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(NULL == conn->pktns.tx.frq);
@@ -2609,7 +2621,7 @@ void test_ngtcp2_conn_retransmit_protected(void) {
   ngtcp2_conn_shutdown_stream_write(conn, stream_id_a, NGTCP2_APP_ERR01);
   ngtcp2_conn_shutdown_stream_write(conn, stream_id_b, NGTCP2_APP_ERR01);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -2619,7 +2631,8 @@ void test_ngtcp2_conn_retransmit_protected(void) {
   conn->pktns.rtb.largest_acked_tx_pkt_num = 1000000007;
   it = ngtcp2_rtb_head(&conn->pktns.rtb);
   ngtcp2_conn_detect_lost_pkt(conn, &conn->pktns, &conn->cstat, ++t);
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, (size_t)(spktlen - 1), ++t);
+  spktlen =
+      ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, (size_t)(spktlen - 1), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -2658,7 +2671,7 @@ void test_ngtcp2_conn_send_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -2688,7 +2701,7 @@ void test_ngtcp2_conn_send_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -2719,7 +2732,7 @@ void test_ngtcp2_conn_send_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -2754,7 +2767,7 @@ void test_ngtcp2_conn_send_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -2766,7 +2779,7 @@ void test_ngtcp2_conn_send_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -2807,7 +2820,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(4 == ud.stream_data.stream_id);
@@ -2827,7 +2840,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(4 == ud.stream_data.stream_id);
@@ -2854,7 +2867,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(4 == ud.stream_data.stream_id);
@@ -2871,7 +2884,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(4 == ud.stream_data.stream_id);
@@ -2899,7 +2912,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(4 == ud.stream_data.stream_id);
@@ -2910,7 +2923,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(0 == ud.stream_data.stream_id);
@@ -2935,7 +2948,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(0 == ud.stream_data.stream_id);
@@ -2952,7 +2965,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(4 == ud.stream_data.stream_id);
@@ -2978,7 +2991,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(0 == ud.stream_data.stream_id);
@@ -2995,7 +3008,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(4 == ud.stream_data.stream_id);
@@ -3021,7 +3034,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(3 == ud.stream_data.stream_id);
@@ -3048,7 +3061,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_LIMIT == rv);
 
@@ -3073,7 +3086,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
 
@@ -3092,7 +3105,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_CRYPTO == rv);
 
@@ -3110,7 +3123,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
@@ -3132,7 +3145,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4));
@@ -3154,7 +3167,8 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                     ++pkt_num, &fr);
 
     ud.stream_data.stream_id = 0;
-    rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+    rv =
+        ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
     CU_ASSERT(0 == rv);
     CU_ASSERT(0 == ud.stream_data.stream_id);
@@ -3181,7 +3195,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 0));
@@ -3193,7 +3207,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 0));
@@ -3211,7 +3225,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
   ud.stream_data.stream_id = -1;
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(-1 == ud.stream_data.stream_id);
@@ -3229,7 +3243,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
   ud.stream_data.stream_id = -1;
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(-1 == ud.stream_data.stream_id);
@@ -3257,7 +3271,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(0 == ud.stream_data.stream_id);
@@ -3274,7 +3288,7 @@ void test_ngtcp2_conn_recv_stream_data(void) {
                                   ++pkt_num, &fr);
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(4 == ud.stream_data.stream_id);
@@ -3299,7 +3313,7 @@ void test_ngtcp2_conn_recv_ping(void) {
 
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL == conn->pktns.tx.frq);
@@ -3328,7 +3342,7 @@ void test_ngtcp2_conn_recv_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
 
@@ -3345,7 +3359,7 @@ void test_ngtcp2_conn_recv_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
 
@@ -3362,7 +3376,7 @@ void test_ngtcp2_conn_recv_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_LIMIT == rv);
 
@@ -3379,7 +3393,7 @@ void test_ngtcp2_conn_recv_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -3401,7 +3415,7 @@ void test_ngtcp2_conn_recv_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv);
 
@@ -3419,7 +3433,7 @@ void test_ngtcp2_conn_recv_max_stream_data(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(1000000009 == strm->tx.max_offset);
@@ -3443,9 +3457,9 @@ void test_ngtcp2_conn_send_early_data(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &datalen,
-                                     NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id,
-                                     null_data, 1024, ++t);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN,
+                                     stream_id, null_data, 1024, ++t);
 
   CU_ASSERT((ngtcp2_ssize)sizeof(buf) == spktlen);
   CU_ASSERT(674 == datalen);
@@ -3460,9 +3474,10 @@ void test_ngtcp2_conn_send_early_data(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen,
-                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                      null_datav(&datav, 199), 1, ++t);
+  spktlen =
+      ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), &datalen,
+                                NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+                                null_datav(&datav, 199), 1, ++t);
 
   CU_ASSERT(sizeof(buf) == spktlen);
   CU_ASSERT(199 == datalen);
@@ -3476,9 +3491,9 @@ void test_ngtcp2_conn_send_early_data(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen,
-                                      NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id,
-                                      NULL, 0, ++t);
+  spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                      &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN,
+                                      stream_id, NULL, 0, ++t);
 
   CU_ASSERT(sizeof(buf) == spktlen);
   CU_ASSERT(0 == datalen);
@@ -3492,17 +3507,17 @@ void test_ngtcp2_conn_send_early_data(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen,
-                                      NGTCP2_WRITE_STREAM_FLAG_NONE, -1, NULL,
-                                      0, ++t);
+  spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                      &datalen, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                      -1, NULL, 0, ++t);
 
   CU_ASSERT(spktlen > 0);
 
   /* We have written Initial.  Now check that STREAM frame is
      written. */
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, sizeof(buf), &datalen,
-                                      NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                      NULL, 0, ++t);
+  spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                      &datalen, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                      stream_id, NULL, 0, ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -3516,7 +3531,7 @@ void test_ngtcp2_conn_send_early_data(void) {
   CU_ASSERT(0 == rv);
 
   spktlen = ngtcp2_conn_writev_stream(
-      conn, NULL, buf,
+      conn, NULL, NULL, buf,
       NGTCP2_MIN_LONG_HEADERLEN + 1 + ngtcp2_conn_get_dcid(conn)->datalen +
           conn->oscid.datalen + 300,
       &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id, NULL, 0, ++t);
@@ -3556,11 +3571,11 @@ void test_ngtcp2_conn_recv_early_data(void) {
       conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid,
       ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -3577,13 +3592,13 @@ void test_ngtcp2_conn_recv_early_data(void) {
       conn->version, &fr, null_iv, sizeof(null_iv));
 
   memset(&ud, 0, sizeof(ud));
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(4 == ud.stream_data.stream_id);
   CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_0RTT);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -3609,7 +3624,7 @@ void test_ngtcp2_conn_recv_early_data(void) {
       conn, buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
       conn->version, &fr, null_iv, sizeof(null_iv));
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_RETRY == rv);
 
@@ -3641,11 +3656,11 @@ void test_ngtcp2_conn_recv_early_data(void) {
                                   &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
                                   conn->version, &fr, null_iv, sizeof(null_iv));
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -3686,11 +3701,11 @@ void test_ngtcp2_conn_recv_compound_pkt(void) {
       conn, buf + pktlen, sizeof(buf) - pktlen, NGTCP2_PKT_INITIAL,
       &conn->oscid, ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -3727,7 +3742,7 @@ void test_ngtcp2_conn_recv_compound_pkt(void) {
   pktlen += write_single_frame_pkt(conn, buf + pktlen, sizeof(buf) - pktlen,
                                    &conn->oscid, ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -3778,12 +3793,12 @@ void test_ngtcp2_conn_pkt_payloadlen(void) {
   /* This first packet which does not increase initial packet number
      space CRYPTO offset or it does not get buffered as 0RTT is an
      error.  But it is unsecured Initial, so we just ignore it. */
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv);
   CU_ASSERT(NGTCP2_CS_SERVER_INITIAL == conn->state);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen == 0);
   CU_ASSERT(0 == ngtcp2_ksl_len(&conn->in_pktns->acktr.ents));
@@ -3807,7 +3822,7 @@ void test_ngtcp2_conn_writev_stream(void) {
   setup_default_client(&conn);
 
   /* This will sends NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -3820,7 +3835,7 @@ void test_ngtcp2_conn_writev_stream(void) {
    * STREAM overhead (+3)
    * AEAD overhead (16)
    */
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 39, &datalen,
+  spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 39, &datalen,
                                       NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                       &datav, 1, ++t);
 
@@ -3833,7 +3848,7 @@ void test_ngtcp2_conn_writev_stream(void) {
   setup_default_client(&conn);
 
   /* This will sends NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -3841,7 +3856,7 @@ void test_ngtcp2_conn_writev_stream(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 40, &datalen,
+  spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 40, &datalen,
                                       NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
                                       &datav, 1, ++t);
 
@@ -3858,7 +3873,7 @@ void test_ngtcp2_conn_writev_stream(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 1200, &datalen,
+  spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 1200, &datalen,
                                       NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id,
                                       &datav, 1, ++t);
 
@@ -3871,7 +3886,7 @@ void test_ngtcp2_conn_writev_stream(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 1200, &datalen,
+  spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 1200, &datalen,
                                       NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id,
                                       &datav, 1, ++t);
 
@@ -3879,7 +3894,7 @@ void test_ngtcp2_conn_writev_stream(void) {
   CU_ASSERT(10 == datalen);
   CU_ASSERT(ngtcp2_ppe_left(&conn->pkt.ppe) < left);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -3893,7 +3908,7 @@ void test_ngtcp2_conn_writev_stream(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 1200, &datalen,
+  spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 1200, &datalen,
                                       NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id,
                                       &datav, 1, ++t);
 
@@ -3906,7 +3921,7 @@ void test_ngtcp2_conn_writev_stream(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_writev_stream(conn, NULL, buf, 1200, &datalen,
+  spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 1200, &datalen,
                                       NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id,
                                       &datav, 1, ++t);
 
@@ -3914,7 +3929,7 @@ void test_ngtcp2_conn_writev_stream(void) {
   CU_ASSERT(10 == datalen);
   CU_ASSERT(ngtcp2_ppe_left(&conn->pkt.ppe) < left);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   /* Make sure that packet is padded */
   CU_ASSERT(1200 == spktlen);
@@ -3944,7 +3959,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   setup_default_client(&conn);
 
   /* This will send NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -3957,7 +3972,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->dcid.unused));
@@ -3978,7 +3993,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused));
@@ -3996,7 +4011,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   CU_ASSERT(NULL == frc->next);
 
   /* This will send RETIRE_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4007,7 +4022,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   setup_default_client(&conn);
 
   /* This will send NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4020,7 +4035,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused));
@@ -4033,7 +4048,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   CU_ASSERT(NULL == frc->next);
 
   /* This will send RETIRE_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4046,7 +4061,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused));
@@ -4064,7 +4079,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   setup_default_server(&conn);
 
   /* This will send NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4090,7 +4105,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
          sizeof(token3));
 
   pktlen = write_pkt(conn, buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 4);
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -4110,7 +4125,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused));
@@ -4135,7 +4150,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   setup_default_server(&conn);
 
   /* This will send NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4149,7 +4164,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   memcpy(frs[1].new_connection_id.stateless_reset_token, token, sizeof(token));
 
   pktlen = write_pkt(conn, buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2);
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -4169,7 +4184,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(2 == conn->dcid.current.seq);
@@ -4194,7 +4209,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   setup_default_server(&conn);
 
   /* This will send NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4208,7 +4223,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   memcpy(frs[1].new_connection_id.stateless_reset_token, token, sizeof(token));
 
   pktlen = write_pkt(conn, buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2);
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -4234,7 +4249,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(3 == conn->dcid.current.seq);
@@ -4259,7 +4274,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   conn->local.settings.transport_params.active_connection_id_limit = 2;
 
   /* This will send NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4284,7 +4299,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
          sizeof(token3));
 
   pktlen = write_pkt(conn, buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 3);
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_CONNECTION_ID_LIMIT == rv);
 
@@ -4294,7 +4309,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   setup_default_server(&conn);
 
   /* This will send NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4315,7 +4330,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
 
   pktlen = write_pkt(conn, buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 3);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused));
@@ -4325,7 +4340,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
                           &conn->pv->fallback_dcid.cid));
 
   /* This will send PATH_CHALLENGE frame */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4335,7 +4350,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL == conn->pv);
@@ -4343,7 +4358,7 @@ void test_ngtcp2_conn_recv_new_connection_id(void) {
   /* Receive NEW_CONNECTION_ID seq=1 again, which should be ignored. */
   pktlen = write_pkt(conn, buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused));
@@ -4369,7 +4384,7 @@ void test_ngtcp2_conn_recv_retire_connection_id(void) {
   conn->remote.transport_params.active_connection_id_limit = 7;
 
   /* This will send NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4387,7 +4402,7 @@ void test_ngtcp2_conn_recv_retire_connection_id(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NGTCP2_SCID_FLAG_RETIRED == scid->flags);
@@ -4397,14 +4412,14 @@ void test_ngtcp2_conn_recv_retire_connection_id(void) {
   CU_ASSERT(1 == conn->scid.num_retired);
 
   /* One NEW_CONNECTION_ID frame is setn as a replacement. */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(8 == ngtcp2_ksl_len(&conn->scid.set));
   CU_ASSERT(1 == conn->scid.num_retired);
 
   /* No NEW_CONNECTION_ID frames should be sent. */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen == 0);
   CU_ASSERT(8 == ngtcp2_ksl_len(&conn->scid.set));
@@ -4413,7 +4428,7 @@ void test_ngtcp2_conn_recv_retire_connection_id(void) {
   /* Now time passed and still no NEW_CONNECTION_ID frames should be
      sent */
   t += 7 * NGTCP2_DEFAULT_INITIAL_RTT;
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), t);
 
   CU_ASSERT(spktlen == 0);
   CU_ASSERT(7 == ngtcp2_ksl_len(&conn->scid.set));
@@ -4431,7 +4446,7 @@ void test_ngtcp2_conn_recv_retire_connection_id(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_PROTO == rv);
 
@@ -4461,7 +4476,7 @@ void test_ngtcp2_conn_server_path_validation(void) {
   setup_default_server(&conn);
 
   /* This will send NEW_CONNECTION_ID frames */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(ngtcp2_ksl_len(&conn->scid.set) > 1);
@@ -4475,7 +4490,7 @@ void test_ngtcp2_conn_server_path_validation(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -4484,12 +4499,12 @@ void test_ngtcp2_conn_server_path_validation(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path1.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path1.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != conn->pv);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(ngtcp2_ringbuf_len(&conn->pv->ents) > 0);
@@ -4500,7 +4515,7 @@ void test_ngtcp2_conn_server_path_validation(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path1.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path1.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(ngtcp2_path_eq(&new_path1.path, &conn->dcid.current.ps.path));
@@ -4520,12 +4535,12 @@ void test_ngtcp2_conn_server_path_validation(void) {
   pktlen =
       write_single_frame_pkt(conn, buf, sizeof(buf), new_cid, ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path2.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path2.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != conn->pv);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(ngtcp2_ringbuf_len(&conn->pv->ents) > 0);
@@ -4536,7 +4551,7 @@ void test_ngtcp2_conn_server_path_validation(void) {
   pktlen =
       write_single_frame_pkt(conn, buf, sizeof(buf), new_cid, ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path2.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path2.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(ngtcp2_path_eq(&new_path2.path, &conn->dcid.current.ps.path));
@@ -4570,7 +4585,7 @@ void test_ngtcp2_conn_client_connection_migration(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -4612,7 +4627,7 @@ void test_ngtcp2_conn_recv_path_challenge(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -4622,14 +4637,14 @@ void test_ngtcp2_conn_recv_path_challenge(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge) > 0);
 
   ngtcp2_path_storage_zero(&ps);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, &ps.path, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, &ps.path, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(ngtcp2_path_eq(&conn->dcid.current.ps.path, &ps.path));
@@ -4659,7 +4674,7 @@ void test_ngtcp2_conn_key_update(void) {
                                         NGTCP2_PKT_FLAG_KEY_PHASE, &conn->oscid,
                                         ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(NULL != conn->crypto.key_update.old_rx_ckm);
@@ -4669,7 +4684,7 @@ void test_ngtcp2_conn_key_update(void) {
   CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED);
 
   t += NGTCP2_SECONDS;
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -4683,7 +4698,7 @@ void test_ngtcp2_conn_key_update(void) {
                                         NGTCP2_PKT_FLAG_KEY_PHASE, &conn->oscid,
                                         ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(t == conn->crypto.key_update.confirmed_ts);
@@ -4691,7 +4706,7 @@ void test_ngtcp2_conn_key_update(void) {
 
   t += ngtcp2_conn_get_pto(conn) + 1;
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), t);
 
   CU_ASSERT(0 == spktlen);
   CU_ASSERT(NULL == conn->crypto.key_update.old_rx_ckm);
@@ -4713,9 +4728,9 @@ void test_ngtcp2_conn_key_update(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                     null_data, 1024, ++t);
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                     &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                     stream_id, null_data, 1024, ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED);
@@ -4730,7 +4745,7 @@ void test_ngtcp2_conn_key_update(void) {
                                         NGTCP2_PKT_FLAG_KEY_PHASE, &conn->oscid,
                                         ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(t == conn->crypto.key_update.confirmed_ts);
@@ -4759,7 +4774,7 @@ void test_ngtcp2_conn_crypto_buffer_exceeded(void) {
   pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid,
                                   ++pkt_num, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED == rv);
 
@@ -4782,7 +4797,7 @@ void test_ngtcp2_conn_handshake_probe(void) {
   /* Retransmit first Initial on PTO timer */
   setup_handshake_client(&conn);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting);
@@ -4792,7 +4807,7 @@ void test_ngtcp2_conn_handshake_probe(void) {
   CU_ASSERT(0 == rv);
   CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(1 == conn->in_pktns->rtb.num_retransmittable);
@@ -4808,7 +4823,7 @@ void test_ngtcp2_conn_handshake_probe(void) {
   pktlen = write_single_frame_handshake_pkt(
       conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &conn->oscid,
       ngtcp2_conn_get_dcid(conn), 0, NGTCP2_PROTO_VER_MAX, &fr);
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting);
@@ -4822,7 +4837,7 @@ void test_ngtcp2_conn_handshake_probe(void) {
 
   /* This sends anti-deadlock padded Initial packet even if we have
      nothing to send. */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(0 == conn->in_pktns->rtb.num_retransmittable);
@@ -4849,7 +4864,7 @@ void test_ngtcp2_conn_handshake_probe(void) {
 
   /* This sends anti-deadlock Handshake packet even if we have nothing
      to send. */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
   CU_ASSERT(0 == conn->hs_pktns->rtb.num_retransmittable);
@@ -4910,7 +4925,7 @@ void test_ngtcp2_conn_handshake_loss(void) {
       conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid,
       ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, cfr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -4930,11 +4945,11 @@ void test_ngtcp2_conn_handshake_loss(void) {
 
   /* Initial and first Handshake are coalesced into 1 packet. */
   for (i = 0; i < 3; ++i) {
-    spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+    spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
     CU_ASSERT(spktlen > 0);
   }
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(0 == spktlen);
 
@@ -4946,12 +4961,12 @@ void test_ngtcp2_conn_handshake_loss(void) {
   CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left);
 
   for (i = 0; i < 2; ++i) {
-    spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+    spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
     CU_ASSERT(spktlen > 0);
   }
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(0 == spktlen);
 
@@ -4973,11 +4988,11 @@ void test_ngtcp2_conn_handshake_loss(void) {
       ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr);
 
   t += 8;
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, t);
 
   CU_ASSERT(0 == rv);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(0 == spktlen);
 
@@ -4985,7 +5000,7 @@ void test_ngtcp2_conn_handshake_loss(void) {
 
   ngtcp2_conn_on_loss_detection_timer(conn, t);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -5015,7 +5030,7 @@ void test_ngtcp2_conn_handshake_loss(void) {
       conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid,
       ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, cfr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -5029,11 +5044,11 @@ void test_ngtcp2_conn_handshake_loss(void) {
 
   /* Initial and first Handshake are coalesced into 1 packet. */
   for (i = 0; i < 3; ++i) {
-    spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+    spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
     CU_ASSERT(spktlen > 0);
   }
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(0 == spktlen);
 
@@ -5053,11 +5068,11 @@ void test_ngtcp2_conn_handshake_loss(void) {
      bytes of data because Initial packet does not contain ACK
      frame. */
   for (i = 0; i < 2; ++i) {
-    spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+    spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
     CU_ASSERT(spktlen > 0);
   }
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(0 == spktlen);
 
@@ -5081,7 +5096,7 @@ void test_ngtcp2_conn_handshake_loss(void) {
       ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr);
 
   t += 8;
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, t);
 
   CU_ASSERT(0 == rv);
 
@@ -5095,11 +5110,11 @@ void test_ngtcp2_conn_handshake_loss(void) {
      into account this ACK.  When calculating the available space for
      CRYPTO data to send, we must consider the acknowledged data and
      compute correct CRYPTO offset. */
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
   CU_ASSERT(0 == spktlen);
 
   it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents);
@@ -5143,7 +5158,7 @@ void test_ngtcp2_conn_recv_client_initial_retry(void) {
       conn, buf, sizeof(buf), NGTCP2_PKT_INITIAL, &rcid,
       ngtcp2_conn_get_dcid(conn), ++pkt_num, conn->version, &fr);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_RETRY == rv);
 
@@ -5184,7 +5199,7 @@ void test_ngtcp2_conn_recv_client_initial_token(void) {
       conn, buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
       conn->version, &fr, raw_token, sizeof(raw_token));
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(0 == rv);
   CU_ASSERT(45 == ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm));
@@ -5211,7 +5226,7 @@ void test_ngtcp2_conn_recv_client_initial_token(void) {
       conn, buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num,
       conn->version, &fr, raw_token, sizeof(raw_token));
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, pktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv);
   CU_ASSERT(0 == ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm));
@@ -5252,7 +5267,7 @@ void test_ngtcp2_conn_recv_version_negotiation(void) {
 
   setup_handshake_client(&conn);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -5266,7 +5281,8 @@ void test_ngtcp2_conn_recv_version_negotiation(void) {
 
   CU_ASSERT(spktlen > 0);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, (size_t)spktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+                            (size_t)spktlen, ++t);
 
   CU_ASSERT(NGTCP2_ERR_RECV_VERSION_NEGOTIATION == rv);
 
@@ -5276,7 +5292,7 @@ void test_ngtcp2_conn_recv_version_negotiation(void) {
      client */
   setup_handshake_client(&conn);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -5291,7 +5307,8 @@ void test_ngtcp2_conn_recv_version_negotiation(void) {
 
   CU_ASSERT(spktlen > 0);
 
-  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, buf, (size_t)spktlen, ++t);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf,
+                            (size_t)spktlen, ++t);
 
   CU_ASSERT(0 == rv);
 
@@ -5334,7 +5351,7 @@ void test_ngtcp2_conn_send_initial_token(void) {
                                   null_iv, &hp_ctx, sizeof(null_iv));
   ngtcp2_conn_set_retry_aead(conn, &retry_aead, &aead_ctx);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), ++t);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t);
 
   CU_ASSERT(spktlen > 0);
 
@@ -5459,7 +5476,7 @@ void test_ngtcp2_conn_write_connection_close(void) {
   /* Client only Initial key */
   setup_handshake_client(&conn);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 0);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0);
 
   CU_ASSERT(spktlen > 0);
 
@@ -5479,7 +5496,7 @@ void test_ngtcp2_conn_write_connection_close(void) {
   /* Client has Initial and Handshake keys */
   setup_handshake_client(&conn);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 0);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0);
 
   CU_ASSERT(spktlen > 0);
 
@@ -5644,7 +5661,7 @@ void test_ngtcp2_conn_write_application_close(void) {
   /* Client only Initial key */
   setup_handshake_client(&conn);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 0);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0);
 
   CU_ASSERT(spktlen > 0);
 
@@ -5664,7 +5681,7 @@ void test_ngtcp2_conn_write_application_close(void) {
   /* Client has Initial and Handshake keys */
   setup_handshake_client(&conn);
 
-  spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 0);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0);
 
   CU_ASSERT(spktlen > 0);
 
@@ -5833,9 +5850,9 @@ void test_ngtcp2_conn_rtb_reclaim_on_pto(void) {
   CU_ASSERT(0 == rv);
 
   for (i = 0; i < 5; ++i) {
-    spktlen = ngtcp2_conn_write_stream(conn, NULL, buf, sizeof(buf), &nwrite,
-                                       NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
-                                       null_data, 1024, 1);
+    spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf),
+                                       &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                       stream_id, null_data, 1024, 1);
 
     CU_ASSERT(0 < spktlen);
   }
@@ -5846,8 +5863,8 @@ void test_ngtcp2_conn_rtb_reclaim_on_pto(void) {
 
   CU_ASSERT(0 == rv);
 
-  spktlen =
-      ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 3 * NGTCP2_SECONDS);
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf),
+                                  3 * NGTCP2_SECONDS);
 
   CU_ASSERT(spktlen > 0);
 
@@ -5865,6 +5882,268 @@ void test_ngtcp2_conn_rtb_reclaim_on_pto(void) {
   ngtcp2_conn_del(conn);
 }
 
+void test_ngtcp2_conn_validate_ecn(void) {
+  ngtcp2_conn *conn;
+  uint8_t buf[2048];
+  ngtcp2_ssize spktlen;
+  ngtcp2_pkt_info pi;
+  size_t pktlen;
+  int rv;
+  ngtcp2_frame fr;
+  int64_t stream_id;
+  ngtcp2_ssize nwrite;
+  size_t i;
+
+  setup_default_client(&conn);
+
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1);
+
+  CU_ASSERT(0 < spktlen);
+  CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+  CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+  CU_ASSERT(1 == conn->tx.ecn.validation_start_ts);
+  CU_ASSERT(0 == conn->tx.ecn.start_pkt_num);
+
+  fr.type = NGTCP2_FRAME_ACK_ECN;
+  fr.ack.largest_ack = 0;
+  fr.ack.ack_delay = 0;
+  fr.ack.first_ack_blklen = 0;
+  fr.ack.num_blks = 0;
+  fr.ack.ecn.ect0 = 1;
+  fr.ack.ecn.ect1 = 0;
+  fr.ack.ecn.ce = 0;
+
+  pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 0, &fr);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(NGTCP2_ECN_STATE_CAPABLE == conn->tx.ecn.state);
+
+  rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+  CU_ASSERT(0 == rv);
+
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite,
+                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+                                     null_data, 1024, 2);
+
+  CU_ASSERT(0 < spktlen);
+
+  /* Receiving ACK frame containing less ECN counts fails
+     validation */
+  fr.type = NGTCP2_FRAME_ACK_ECN;
+  fr.ack.largest_ack = 1;
+  fr.ack.ack_delay = 0;
+  fr.ack.first_ack_blklen = 0;
+  fr.ack.num_blks = 0;
+  fr.ack.ecn.ect0 = 0;
+  fr.ack.ecn.ect1 = 0;
+  fr.ack.ecn.ce = 0;
+
+  pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 1, &fr);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite,
+                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+                                     null_data, 1024, 2);
+
+  CU_ASSERT(0 < spktlen);
+  CU_ASSERT(NGTCP2_ECN_NOT_ECT == pi.ecn);
+
+  ngtcp2_conn_del(conn);
+
+  /* Receiving ACK frame without ECN counts invalidates ECN
+     capability */
+  setup_default_server(&conn);
+
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1);
+
+  CU_ASSERT(0 < spktlen);
+  CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+  CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+  CU_ASSERT(1 == conn->tx.ecn.validation_start_ts);
+  CU_ASSERT(0 == conn->tx.ecn.start_pkt_num);
+
+  fr.type = NGTCP2_FRAME_ACK;
+  fr.ack.largest_ack = 0;
+  fr.ack.ack_delay = 0;
+  fr.ack.first_ack_blklen = 0;
+  fr.ack.num_blks = 0;
+
+  pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 0, &fr);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+
+  ngtcp2_conn_del(conn);
+
+  /* CE counts must be considered */
+  setup_default_client(&conn);
+
+  rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+  CU_ASSERT(0 == rv);
+
+  for (i = 0; i < 2; ++i) {
+    spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf),
+                                       &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                       stream_id, null_data, 1024, 2);
+
+    CU_ASSERT(0 < spktlen);
+    CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+  }
+
+  CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+  CU_ASSERT(2 == conn->tx.ecn.validation_start_ts);
+  CU_ASSERT(0 == conn->tx.ecn.start_pkt_num);
+
+  fr.type = NGTCP2_FRAME_ACK_ECN;
+  fr.ack.largest_ack = 1;
+  fr.ack.ack_delay = 0;
+  fr.ack.first_ack_blklen = 1;
+  fr.ack.num_blks = 0;
+  fr.ack.ecn.ect0 = 1;
+  fr.ack.ecn.ect1 = 0;
+  fr.ack.ecn.ce = 1;
+
+  pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 0, &fr);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(NGTCP2_ECN_STATE_CAPABLE == conn->tx.ecn.state);
+  CU_ASSERT(0 == ngtcp2_ksl_len(&conn->pktns.rtb.ents));
+
+  ngtcp2_conn_del(conn);
+
+  /* If increments of ECN counts is less than the number of
+     acknowledged ECN entries, ECN validation fails. */
+  setup_default_client(&conn);
+
+  spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1);
+
+  CU_ASSERT(0 < spktlen);
+  CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+  CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+  CU_ASSERT(1 == conn->tx.ecn.validation_start_ts);
+  CU_ASSERT(0 == conn->tx.ecn.start_pkt_num);
+
+  fr.type = NGTCP2_FRAME_ACK_ECN;
+  fr.ack.largest_ack = 0;
+  fr.ack.ack_delay = 0;
+  fr.ack.first_ack_blklen = 0;
+  fr.ack.num_blks = 0;
+  fr.ack.ecn.ect0 = 0;
+  fr.ack.ecn.ect1 = 1;
+  fr.ack.ecn.ce = 0;
+
+  pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 0, &fr);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+
+  ngtcp2_conn_del(conn);
+
+  /* ECN validation fails if all ECN marked packets are lost */
+  setup_default_client(&conn);
+
+  rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+  CU_ASSERT(0 == rv);
+
+  for (i = 0; i < NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS; ++i) {
+    spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf),
+                                       &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                       stream_id, null_data, 25, 0);
+
+    CU_ASSERT(0 < spktlen);
+    CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+  }
+
+  CU_ASSERT(NGTCP2_ECN_STATE_UNKNOWN == conn->tx.ecn.state);
+  CU_ASSERT(NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS == conn->tx.ecn.dgram_sent);
+
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite,
+                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+                                     null_data, 25, 0);
+
+  CU_ASSERT(0 < spktlen);
+  CU_ASSERT(NGTCP2_ECN_NOT_ECT == pi.ecn);
+  CU_ASSERT(NGTCP2_ECN_STATE_UNKNOWN == conn->tx.ecn.state);
+  CU_ASSERT(0 == conn->tx.ecn.validation_start_ts);
+  CU_ASSERT(0 == conn->tx.ecn.start_pkt_num);
+  CU_ASSERT(NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS == conn->tx.ecn.dgram_sent);
+  CU_ASSERT(0 == conn->tx.ecn.dgram_lost);
+
+  fr.type = NGTCP2_FRAME_ACK;
+  fr.ack.largest_ack = NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS;
+  fr.ack.ack_delay = 0;
+  fr.ack.first_ack_blklen = 0;
+  fr.ack.num_blks = 0;
+
+  pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 0, &fr);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen,
+                            NGTCP2_SECONDS);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+  CU_ASSERT(NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS == conn->tx.ecn.dgram_lost);
+
+  ngtcp2_conn_del(conn);
+
+  /* ECN validation fails if all ECN marked packets sent in last 3 *
+     RTT are lost */
+  setup_default_client(&conn);
+
+  rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL);
+
+  CU_ASSERT(0 == rv);
+
+  for (i = 0; i < 2; ++i) {
+    spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf),
+                                       &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE,
+                                       stream_id, null_data, 25, 0);
+
+    CU_ASSERT(0 < spktlen);
+    CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn);
+  }
+
+  CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state);
+  CU_ASSERT(2 == conn->tx.ecn.dgram_sent);
+
+  spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite,
+                                     NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id,
+                                     null_data, 25, 3 * NGTCP2_SECONDS);
+
+  CU_ASSERT(0 < spktlen);
+  CU_ASSERT(NGTCP2_ECN_NOT_ECT == pi.ecn);
+  CU_ASSERT(NGTCP2_ECN_STATE_UNKNOWN == conn->tx.ecn.state);
+  CU_ASSERT(0 == conn->tx.ecn.validation_start_ts);
+  CU_ASSERT(0 == conn->tx.ecn.start_pkt_num);
+  CU_ASSERT(2 == conn->tx.ecn.dgram_sent);
+  CU_ASSERT(0 == conn->tx.ecn.dgram_lost);
+
+  fr.type = NGTCP2_FRAME_ACK;
+  fr.ack.largest_ack = 2;
+  fr.ack.ack_delay = 0;
+  fr.ack.first_ack_blklen = 0;
+  fr.ack.num_blks = 0;
+
+  pktlen = write_single_frame_pkt(conn, buf, sizeof(buf), &conn->oscid, 0, &fr);
+  rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen,
+                            4 * NGTCP2_SECONDS);
+
+  CU_ASSERT(0 == rv);
+  CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state);
+  CU_ASSERT(2 == conn->tx.ecn.dgram_lost);
+
+  ngtcp2_conn_del(conn);
+}
+
 void test_ngtcp2_pkt_write_connection_close(void) {
   ngtcp2_ssize spktlen;
   uint8_t buf[1200];
diff --git a/tests/ngtcp2_conn_test.h b/tests/ngtcp2_conn_test.h
index 5be0d3ac..de7937d1 100644
--- a/tests/ngtcp2_conn_test.h
+++ b/tests/ngtcp2_conn_test.h
@@ -77,6 +77,7 @@ void test_ngtcp2_conn_set_remote_transport_params(void);
 void test_ngtcp2_conn_write_connection_close(void);
 void test_ngtcp2_conn_write_application_close(void);
 void test_ngtcp2_conn_rtb_reclaim_on_pto(void);
+void test_ngtcp2_conn_validate_ecn(void);
 void test_ngtcp2_pkt_write_connection_close(void);
 
 #endif /* NGTCP2_CONN_TEST_H */
diff --git a/tests/ngtcp2_pkt_test.c b/tests/ngtcp2_pkt_test.c
index c1c304df..0a8b8a4f 100644
--- a/tests/ngtcp2_pkt_test.c
+++ b/tests/ngtcp2_pkt_test.c
@@ -694,6 +694,85 @@ void test_ngtcp2_pkt_encode_ack_frame(void) {
   memset(&nmfr, 0, sizeof(nmfr));
 }
 
+void test_ngtcp2_pkt_encode_ack_ecn_frame(void) {
+  uint8_t buf[256];
+  ngtcp2_max_frame mfr, nmfr;
+  ngtcp2_frame *fr = &mfr.fr, *nfr = &nmfr.fr;
+  ngtcp2_ssize rv;
+  size_t framelen;
+  size_t i;
+  ngtcp2_ack_blk *blks;
+
+  /* 0 Num Blocks */
+  fr->type = NGTCP2_FRAME_ACK_ECN;
+  fr->ack.largest_ack = 0xf1f2f3f4llu;
+  fr->ack.first_ack_blklen = 0;
+  fr->ack.ack_delay = 0;
+  fr->ack.num_blks = 0;
+  fr->ack.ecn.ect0 = 64;
+  fr->ack.ecn.ect1 = 16384;
+  fr->ack.ecn.ce = 1073741824;
+
+  framelen = 1 + 8 + 1 + 1 + 1 + 2 + 4 + 8;
+
+  rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr->ack);
+
+  CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+  rv = ngtcp2_pkt_decode_ack_frame(&nfr->ack, buf, framelen);
+
+  CU_ASSERT((ngtcp2_ssize)framelen == rv);
+  CU_ASSERT(fr->type == nfr->type);
+  CU_ASSERT(fr->ack.largest_ack == nfr->ack.largest_ack);
+  CU_ASSERT(fr->ack.ack_delay == nfr->ack.ack_delay);
+  CU_ASSERT(fr->ack.num_blks == nfr->ack.num_blks);
+  CU_ASSERT(fr->ack.ecn.ect0 == nfr->ack.ecn.ect0);
+  CU_ASSERT(fr->ack.ecn.ect1 == nfr->ack.ecn.ect1);
+  CU_ASSERT(fr->ack.ecn.ce == nfr->ack.ecn.ce);
+
+  memset(&nmfr, 0, sizeof(nmfr));
+
+  /* 2 Num Blocks */
+  fr->type = NGTCP2_FRAME_ACK_ECN;
+  fr->ack.largest_ack = 0xf1f2f3f4llu;
+  fr->ack.first_ack_blklen = 0xe1e2e3e4llu;
+  fr->ack.ack_delay = 0xf1f2;
+  fr->ack.num_blks = 2;
+  blks = fr->ack.blks;
+  blks[0].gap = 255;
+  blks[0].blklen = 0xd1d2d3d4llu;
+  blks[1].gap = 1;
+  blks[1].blklen = 0xd1d2d3d4llu;
+  fr->ack.ecn.ect0 = 0;
+  fr->ack.ecn.ect1 = 64;
+  fr->ack.ecn.ce = 16384;
+
+  framelen = 1 + 8 + 4 + 1 + 8 + (2 + 8) + (1 + 8) + 1 + 2 + 4;
+
+  rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr->ack);
+
+  CU_ASSERT((ngtcp2_ssize)framelen == rv);
+
+  rv = ngtcp2_pkt_decode_ack_frame(&nfr->ack, buf, framelen);
+
+  CU_ASSERT((ngtcp2_ssize)framelen == rv);
+  CU_ASSERT(fr->type == nfr->type);
+  CU_ASSERT(fr->ack.largest_ack == nfr->ack.largest_ack);
+  CU_ASSERT(fr->ack.ack_delay == nfr->ack.ack_delay);
+  CU_ASSERT(fr->ack.num_blks == nfr->ack.num_blks);
+
+  for (i = 0; i < fr->ack.num_blks; ++i) {
+    CU_ASSERT(fr->ack.blks[i].gap == nfr->ack.blks[i].gap);
+    CU_ASSERT(fr->ack.blks[i].blklen == nfr->ack.blks[i].blklen);
+  }
+
+  CU_ASSERT(fr->ack.ecn.ect0 == nfr->ack.ecn.ect0);
+  CU_ASSERT(fr->ack.ecn.ect1 == nfr->ack.ecn.ect1);
+  CU_ASSERT(fr->ack.ecn.ce == nfr->ack.ecn.ce);
+
+  memset(&nmfr, 0, sizeof(nmfr));
+}
+
 void test_ngtcp2_pkt_encode_reset_stream_frame(void) {
   uint8_t buf[32];
   ngtcp2_reset_stream fr, nfr;
diff --git a/tests/ngtcp2_pkt_test.h b/tests/ngtcp2_pkt_test.h
index c7a86a86..ffc15195 100644
--- a/tests/ngtcp2_pkt_test.h
+++ b/tests/ngtcp2_pkt_test.h
@@ -37,6 +37,7 @@ void test_ngtcp2_pkt_decode_ack_frame(void);
 void test_ngtcp2_pkt_decode_padding_frame(void);
 void test_ngtcp2_pkt_encode_stream_frame(void);
 void test_ngtcp2_pkt_encode_ack_frame(void);
+void test_ngtcp2_pkt_encode_ack_ecn_frame(void);
 void test_ngtcp2_pkt_encode_reset_stream_frame(void);
 void test_ngtcp2_pkt_encode_connection_close_frame(void);
 void test_ngtcp2_pkt_encode_connection_close_app_frame(void);
diff --git a/tests/ngtcp2_rtb_test.c b/tests/ngtcp2_rtb_test.c
index 3f6c4f13..3a145b10 100644
--- a/tests/ngtcp2_rtb_test.c
+++ b/tests/ngtcp2_rtb_test.c
@@ -184,7 +184,7 @@ void test_ngtcp2_rtb_recv_ack(void) {
   fr->num_blks = 0;
 
   num_acked =
-      ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, 1000000009, 1000000009);
+      ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009);
 
   CU_ASSERT(2 == num_acked);
   CU_ASSERT(65 == ngtcp2_ksl_len(&rtb.ents));
@@ -210,7 +210,7 @@ void test_ngtcp2_rtb_recv_ack(void) {
   blks[1].blklen = 1; /* (180), 179 */
 
   num_acked =
-      ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, 1000000009, 1000000009);
+      ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009);
 
   CU_ASSERT(4 == num_acked);
   CU_ASSERT(63 == ngtcp2_ksl_len(&rtb.ents));
@@ -236,7 +236,7 @@ void test_ngtcp2_rtb_recv_ack(void) {
   fr->blks[0].blklen = 0;
 
   num_acked =
-      ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, 1000000009, 1000000009);
+      ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009);
 
   CU_ASSERT(1 == num_acked);
   assert_rtb_entry_not_found(&rtb, 0);
@@ -255,7 +255,7 @@ void test_ngtcp2_rtb_recv_ack(void) {
   fr->num_blks = 0;
 
   num_acked =
-      ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, 1000000009, 1000000009);
+      ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009);
 
   CU_ASSERT(1 == num_acked);
   assert_rtb_entry_not_found(&rtb, 0);
@@ -276,7 +276,7 @@ void test_ngtcp2_rtb_recv_ack(void) {
   fr->blks[0].blklen = 0;
 
   num_acked =
-      ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, 1000000009, 1000000009);
+      ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009);
 
   CU_ASSERT(1 == num_acked);
   assert_rtb_entry_not_found(&rtb, 0);
-- 
GitLab