diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c
index a4e0882b278c3e9ad90fd2a4cd98570db6c58a79..28550ee5b06c6dddbdfa39ad90b8e4c2c7bc234b 100644
--- a/lib/ngtcp2_conn.c
+++ b/lib/ngtcp2_conn.c
@@ -8319,8 +8319,10 @@ ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) {
   ngtcp2_tstamp t1 = ngtcp2_conn_loss_detection_expiry(conn);
   ngtcp2_tstamp t2 = ngtcp2_conn_ack_delay_expiry(conn);
   ngtcp2_tstamp t3 = ngtcp2_conn_internal_expiry(conn);
+  ngtcp2_tstamp t4 = ngtcp2_conn_lost_pkt_expiry(conn);
   ngtcp2_tstamp res = ngtcp2_min(t1, t2);
-  return ngtcp2_min(res, t3);
+  res = ngtcp2_min(res, t3);
+  return ngtcp2_min(res, t4);
 }
 
 int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
@@ -8328,6 +8330,8 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
 
   ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts);
 
+  ngtcp2_conn_remove_lost_pkt(conn, ts);
+
   if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) {
     rv = ngtcp2_conn_on_loss_detection_timer(conn, ts);
     if (rv != 0) {
@@ -8357,6 +8361,49 @@ void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn,
   acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts);
 }
 
+ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) {
+  ngtcp2_tstamp res = UINT64_MAX, ts;
+
+  if (conn->in_pktns) {
+    ts = ngtcp2_rtb_lost_pkt_ts(&conn->in_pktns->rtb);
+    if (ts != UINT64_MAX) {
+      ts += conn_compute_pto(conn, conn->in_pktns);
+      res = ngtcp2_min(res, ts);
+    }
+  }
+
+  if (conn->hs_pktns) {
+    ts = ngtcp2_rtb_lost_pkt_ts(&conn->hs_pktns->rtb);
+    if (ts != UINT64_MAX) {
+      ts += conn_compute_pto(conn, conn->hs_pktns);
+      res = ngtcp2_min(res, ts);
+    }
+  }
+
+  ts = ngtcp2_rtb_lost_pkt_ts(&conn->pktns.rtb);
+  if (ts != UINT64_MAX) {
+    ts += conn_compute_pto(conn, &conn->pktns);
+    res = ngtcp2_min(res, ts);
+  }
+
+  return res;
+}
+
+void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
+  ngtcp2_tstamp pto;
+
+  if (conn->in_pktns) {
+    pto = conn_compute_pto(conn, conn->in_pktns);
+    ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, pto, ts);
+  }
+  if (conn->hs_pktns) {
+    pto = conn_compute_pto(conn, conn->hs_pktns);
+    ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, pto, ts);
+  }
+  pto = conn_compute_pto(conn, &conn->pktns);
+  ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts);
+}
+
 /*
  * conn_client_validate_transport_params validates |params| as client.
  * |params| must be sent with Encrypted Extensions.
diff --git a/lib/ngtcp2_conn.h b/lib/ngtcp2_conn.h
index 1c0dc3ac448ecec9edac56dafa26bdb1c88b8456..fcc7860c052bdaf3d8caf65dc670a265d87f7cc8 100644
--- a/lib/ngtcp2_conn.h
+++ b/lib/ngtcp2_conn.h
@@ -638,4 +638,15 @@ ngtcp2_conn_write_single_frame_pkt(ngtcp2_conn *conn, uint8_t *dest,
  */
 int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn);
 
+/*
+ * ngtcp2_conn_lost_pkt_expiry returns the earliest expiry time of
+ * lost packet.
+ */
+ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn);
+
+/*
+ * ngtcp2_conn_remove_lost_pkt removes the expired lost packet.
+ */
+void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts);
+
 #endif /* NGTCP2_CONN_H */
diff --git a/lib/ngtcp2_rtb.c b/lib/ngtcp2_rtb.c
index f55e18d62debd75f338ee2b1cd17a0c3c9f8291f..0f1d97f27d4964e66d00f018771de4084593266d 100644
--- a/lib/ngtcp2_rtb.c
+++ b/lib/ngtcp2_rtb.c
@@ -840,33 +840,65 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
     }
   }
 
+  /* TODO Limit the number of LOST_RETRANSMITTED entries under maximum
+     reordering threshold. */
+
+  return 0;
+}
+
+void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
+                                        ngtcp2_tstamp ts) {
+  ngtcp2_ksl_it it;
+  ngtcp2_rtb_entry *ent;
+
   if (ngtcp2_ksl_len(&rtb->ents) == 0) {
-    return 0;
+    return;
   }
 
   it = ngtcp2_ksl_end(&rtb->ents);
 
   for (;;) {
+    assert(ngtcp2_ksl_it_end(&it));
+
     ngtcp2_ksl_it_prev(&it);
     ent = ngtcp2_ksl_it_get(&it);
 
     if (!(ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED) ||
         ts - ent->lost_ts < pto) {
-      return 0;
+      return;
     }
 
     ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
-                    "removing stale lost packet pkn=%" PRId64, ent->hd.pkt_num);
+                    "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
 
     ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num);
     ngtcp2_rtb_entry_del(ent, rtb->mem);
 
     if (ngtcp2_ksl_len(&rtb->ents) == 0) {
-      return 0;
+      return;
     }
   }
 }
 
+ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb) {
+  ngtcp2_ksl_it it;
+  ngtcp2_rtb_entry *ent;
+
+  if (ngtcp2_ksl_len(&rtb->ents) == 0) {
+    return UINT64_MAX;
+  }
+
+  it = ngtcp2_ksl_end(&rtb->ents);
+  ngtcp2_ksl_it_prev(&it);
+  ent = ngtcp2_ksl_it_get(&it);
+
+  if (!(ent->flags & NGTCP2_RTB_FLAG_LOST_RETRANSMITTED)) {
+    return UINT64_MAX;
+  }
+
+  return ent->lost_ts;
+}
+
 static void rtb_on_pkt_lost2(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
                              ngtcp2_rtb_entry *ent) {
   ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
diff --git a/lib/ngtcp2_rtb.h b/lib/ngtcp2_rtb.h
index c1a12d0278d2e17bc376b60340a9ed4775ba7898..b9f350fa857accb081b62610259bab3aa60b7f04 100644
--- a/lib/ngtcp2_rtb.h
+++ b/lib/ngtcp2_rtb.h
@@ -321,6 +321,19 @@ int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_frame_chain **pfrc,
                                ngtcp2_conn_stat *cstat, ngtcp2_conn *conn,
                                ngtcp2_duration pto, ngtcp2_tstamp ts);
 
+/*
+ * ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet.
+ */
+void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
+                                        ngtcp2_tstamp ts);
+
+/*
+ * ngtcp2_rtb_lost_pkt_ts returns the earliest time when the still
+ * retained packet was lost.  It returns UINT64_MAX if no such packet
+ * exists.
+ */
+ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb);
+
 /*
  * ngtcp2_rtb_remove_all removes all packets from |rtb| and prepends
  * all frames to |*pfrc|.  Even when this function fails, some frames
diff --git a/tests/main.c b/tests/main.c
index 5ffc4c7f92e67e8358fe88dd9966d7e84e8dd57c..3ac72fc1c92a86ce46adf71c233bc6ac949b00b1 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -166,6 +166,9 @@ int main() {
                    test_ngtcp2_encode_transport_params) ||
       !CU_add_test(pSuite, "rtb_add", test_ngtcp2_rtb_add) ||
       !CU_add_test(pSuite, "rtb_recv_ack", test_ngtcp2_rtb_recv_ack) ||
+      !CU_add_test(pSuite, "rtb_lost_pkt_ts", test_ngtcp2_rtb_lost_pkt_ts) ||
+      !CU_add_test(pSuite, "rtb_remove_expired_lost_pkt",
+                   test_ngtcp2_rtb_remove_expired_lost_pkt) ||
       !CU_add_test(pSuite, "idtr_open", test_ngtcp2_idtr_open) ||
       !CU_add_test(pSuite, "ringbuf_push_front",
                    test_ngtcp2_ringbuf_push_front) ||
diff --git a/tests/ngtcp2_rtb_test.c b/tests/ngtcp2_rtb_test.c
index 57e216f6007d659d0c8ea9c84dbea04dbcf8ac7e..3e303da36d37ec19f287b46af316bef9ebf8ab97 100644
--- a/tests/ngtcp2_rtb_test.c
+++ b/tests/ngtcp2_rtb_test.c
@@ -24,6 +24,8 @@
  */
 #include "ngtcp2_rtb_test.h"
 
+#include <assert.h>
+
 #include <CUnit/CUnit.h>
 
 #include "ngtcp2_rtb.h"
@@ -283,3 +285,85 @@ void test_ngtcp2_rtb_recv_ack(void) {
   ngtcp2_cc_reno_cc_free(&cc, mem);
   ngtcp2_strm_free(&crypto);
 }
+
+void test_ngtcp2_rtb_lost_pkt_ts(void) {
+  ngtcp2_rtb rtb;
+  const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_APP;
+  ngtcp2_strm crypto;
+  ngtcp2_log log;
+  const ngtcp2_mem *mem = ngtcp2_mem_default();
+  ngtcp2_cc cc;
+  ngtcp2_rst rst;
+  ngtcp2_conn_stat cstat;
+  ngtcp2_ksl_it it;
+  ngtcp2_rtb_entry *ent;
+
+  ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, mem);
+  ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+
+  conn_stat_init(&cstat);
+  ngtcp2_rst_init(&rst);
+  ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+  ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, mem);
+
+  add_rtb_entry_range(&rtb, 0, 1, &cstat, mem);
+
+  CU_ASSERT(UINT64_MAX == ngtcp2_rtb_lost_pkt_ts(&rtb));
+
+  it = ngtcp2_ksl_end(&rtb.ents);
+  ngtcp2_ksl_it_prev(&it);
+  ent = ngtcp2_ksl_it_get(&it);
+  ent->flags |= NGTCP2_RTB_FLAG_LOST_RETRANSMITTED;
+  ent->lost_ts = 16777217;
+
+  CU_ASSERT(16777217 == ngtcp2_rtb_lost_pkt_ts(&rtb));
+
+  ngtcp2_rtb_free(&rtb);
+  ngtcp2_cc_reno_cc_free(&cc, mem);
+  ngtcp2_strm_free(&crypto);
+}
+
+void test_ngtcp2_rtb_remove_expired_lost_pkt(void) {
+  ngtcp2_rtb rtb;
+  const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_APP;
+  ngtcp2_strm crypto;
+  ngtcp2_log log;
+  const ngtcp2_mem *mem = ngtcp2_mem_default();
+  ngtcp2_cc cc;
+  ngtcp2_rst rst;
+  ngtcp2_conn_stat cstat;
+  ngtcp2_ksl_it it;
+  ngtcp2_rtb_entry *ent;
+  size_t i;
+
+  ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, mem);
+  ngtcp2_log_init(&log, NULL, NULL, 0, NULL);
+
+  conn_stat_init(&cstat);
+  ngtcp2_rst_init(&rst);
+  ngtcp2_cc_reno_cc_init(&cc, &log, mem);
+  ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, mem);
+
+  add_rtb_entry_range(&rtb, 0, 7, &cstat, mem);
+
+  it = ngtcp2_ksl_end(&rtb.ents);
+
+  for (i = 0; i < 5; ++i) {
+    ngtcp2_ksl_it_prev(&it);
+    ent = ngtcp2_ksl_it_get(&it);
+    ent->flags |= NGTCP2_RTB_FLAG_LOST_RETRANSMITTED;
+    ent->lost_ts = 16777217 + i;
+  }
+
+  ngtcp2_rtb_remove_expired_lost_pkt(&rtb, 1, 16777219);
+
+  CU_ASSERT(5 == ngtcp2_ksl_len(&rtb.ents));
+
+  ngtcp2_rtb_remove_expired_lost_pkt(&rtb, 1, 16777223);
+
+  CU_ASSERT(2 == ngtcp2_ksl_len(&rtb.ents));
+
+  ngtcp2_rtb_free(&rtb);
+  ngtcp2_cc_reno_cc_free(&cc, mem);
+  ngtcp2_strm_free(&crypto);
+}
diff --git a/tests/ngtcp2_rtb_test.h b/tests/ngtcp2_rtb_test.h
index 3c890c249f5312694fb84e86c5a18c391d2f73b1..c9bf2f9671973f3fbd34939a9922809896756ac1 100644
--- a/tests/ngtcp2_rtb_test.h
+++ b/tests/ngtcp2_rtb_test.h
@@ -31,5 +31,7 @@
 
 void test_ngtcp2_rtb_add(void);
 void test_ngtcp2_rtb_recv_ack(void);
+void test_ngtcp2_rtb_lost_pkt_ts(void);
+void test_ngtcp2_rtb_remove_expired_lost_pkt(void);
 
 #endif /* NGTCP2_RTB_TEST_H */