diff --git a/examples/client.cc b/examples/client.cc
index 4a11123203d49347f557518dfc1c0a20c9aff5d0..4b6623d5a5e319282740c4b0b231891e39a4170a 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 db3b266001141c5c49b90bb705e86c7d93a0e231..457040d6da9263dce355a24b14b8bdc3a008f16a 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 02c40d3810e9057e60359690d82202cd395c9169..d1b6adbc229f7fe6efc38b9508621d9b44554f1a 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 3181e0897a2536527165bc181855f2bf7dd99c5c..b7c0cd9a6ffd740767a3d2d5f54d198253312bdf 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 684a49d533b148ae9f76a09082d51fc48eaee480..b695b52a9eb801f043d7f7695f506783607de39c 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 40e767f6ecf0db53eae78e40e8e73a926d19ec33..126df6f8e52dd5cce5614efab44c11925c0d6fa8 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 cb2e1e3c7c703a520e6bab1aa54cee7651a62e0d..a74c26f8d856d311927ddf033cce30ef9cd67324 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 44049e0a97ef834f18fe399d178a2a01e63d5c2d..6b5f752a88639bf11ef4cd41af412736402490be 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 ec42bd0c7939f458cf03f91d2ee0eb5b6f672e20..3427292502ef5b7b3fa4ddf2bb358972c6d591ff 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 9125fce8a5e667c34978ff62334d78d02fd510a3..8a15b21263823d70a4236e938ca11440e5c07be5 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 11f496598569d5707037e41b66ad94ad2a4ac03e..bd9d95cf8b273eff961976bfa454dff791ab3df2 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 6855544e00c68cb1126a12419f582c6218bf34be..474c5abf4f7adb37f14725c296983d64bee060b9 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 d7782b1114a53d13dac4160fb0195daefba2030b..391461d0aa0930ce64258317f0a12a1ea0dda4b9 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 8b7ec0def65baf720c79aeb46ec5508ff40e280b..c853221486ecbaaea57a7cdcc778994c657b0003 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 905af29a8226020ef5eec1ddb0de5fa48883dcbc..257f22e892de64c80cd450dd798d863aae3c46a4 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 7348cb9a9d01d56f0e391f0bc9ff211f2f4f5ae7..ffdd55411b3ab66875f79dd2f8d46bd024b19a38 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 580b6a6fe0fd2722d7c14cb31b2006ffcc6f0cc4..ca41c4d77e4ba7c36771f202666ba80f327ac344 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 d58a6b2ea1adb79689331d0f5a3bf4c5ad1563a6..da2061488e30f1ddf34157207425a3299cc69001 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 0d0b738f396a2393511bc21b56813058002829df..04f016d51ff701cbe46fc5e043768a5ae6dadec9 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 b6c46b8da8c3b14c77c938c4e03058ca549be99a..16576022e403fab3a78b256f6b0db75ddb7d9f34 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 1fa0b9e1b117a7ec9438095772c781ea211a92bf..31d21b4ef731742c8d2d59534b694f147f921aa4 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 5be0d3acac0a52f03401d14bae6677ab4855b6d2..de7937d16cfcf8ed7576ad987c1ac7c77904bc24 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 c1c304df002e9e8da6cfb819fe0ff9764d1cbfff..0a8b8a4f32e9073b8dec3fceacd47023c5d78b2e 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 c7a86a8615a75b22134429bfcbf0c8c29ea42b05..ffc151957d8b6899d7a938a218729fad09849689 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 3f6c4f132d86128018c9d0769d28bcdd2a80181a..3a145b1073a95354548e280b188fbb1d35b67e07 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);