]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
contrib: upgrade embedded library libngtcp2 to v0.8.0
authorDaniel Salzman <daniel.salzman@nic.cz>
Mon, 22 Aug 2022 10:42:48 +0000 (12:42 +0200)
committerDaniel Salzman <daniel.salzman@nic.cz>
Wed, 24 Aug 2022 14:58:43 +0000 (16:58 +0200)
12 files changed:
configure.ac
doc/requirements.rst
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.h
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h
src/contrib/libngtcp2/ngtcp2/ngtcp2.h
src/contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h
src/contrib/libngtcp2/ngtcp2/version.h

index fdc3294671c7e47b95f04224ea7cfa159ea63a15..ff7273a0d5a81ed43c555ce6bf05ab0d96f8360d 100644 (file)
@@ -556,12 +556,12 @@ AS_IF([test "$enable_daemon" = "yes" -o "$enable_utilities" = "yes"], [
 
 # QUIC support
 AC_ARG_ENABLE([quic],
-   AS_HELP_STRING([--enable-quic=auto|yes|no], [Support DoQ (needs libngtcp2 = 0.7.0, gnutls >= 3.7.2) [default=auto]]),
+   AS_HELP_STRING([--enable-quic=auto|yes|no], [Support DoQ (needs libngtcp2 = 0.8.0, gnutls >= 3.7.2) [default=auto]]),
    [], [enable_quic=auto])
 
 AS_CASE([$enable_quic],
-   [auto], [PKG_CHECK_MODULES([libngtcp2], [libngtcp2 = 0.7.0 libngtcp2_crypto_gnutls], [enable_quic=yes], [enable_quic=no])],
-   [yes], [PKG_CHECK_MODULES([libngtcp2], [libngtcp2 = 0.7.0 libngtcp2_crypto_gnutls], [enable_quic=yes],
+   [auto], [PKG_CHECK_MODULES([libngtcp2], [libngtcp2 = 0.8.0 libngtcp2_crypto_gnutls], [enable_quic=yes], [enable_quic=no])],
+   [yes], [PKG_CHECK_MODULES([libngtcp2], [libngtcp2 = 0.8.0 libngtcp2_crypto_gnutls], [enable_quic=yes],
              AS_IF([test "$gnutls_quic" = "yes"],
                 [enable_quic=embedded
                  embedded_libngtcp2_CFLAGS="-I\$(top_srcdir)/src/contrib/libngtcp2 -I\$(top_srcdir)/src/contrib/libngtcp2/ngtcp2/lib"
index 08a9a19ff7277a50801df9bf635e9bcf8268e3f7..aca5a457b78a113020d6071cb2e71c94657f63a3 100644 (file)
@@ -109,6 +109,6 @@ recommendations.
 DNS-over-QUIC (DoQ) support in :doc:`knotd<man_knotd>`, :doc:`kxdpgun<man_kxdpgun>`,
 and :doc:`kdig<man_kdig>`:
 
-* libngtcp2 == 0.7.0 (or embedded one via `--enable-quic`)
+* libngtcp2 == 0.8.0 (or embedded one via `--enable-quic`)
 * gnutls >= 3.7.2
 * :ref:`Mode XDP` (for knotd and kxdpgun)
index 6d81c311112f19b3ccccaf4bd5419e17037cd1e6..a3135680ca160fe61628993374ef7b22b28c083b 100644 (file)
@@ -4448,7 +4448,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
 
   if (((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) &&
       (!path || ngtcp2_path_eq(&conn->dcid.current.ps.path, path))) {
-    if (pi) {
+    if (pi && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE)) {
       conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts);
     }
 
@@ -4479,7 +4479,8 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
                         nwrite);
       }
     }
-  } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
+  } else if (pi && !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) &&
+             conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) {
     conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts);
   }
 
@@ -4706,6 +4707,10 @@ static int conn_start_pmtud(ngtcp2_conn *conn) {
   return 0;
 }
 
+int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn) {
+  return conn_start_pmtud(conn);
+}
+
 void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn) {
   if (!conn->pmtud) {
     return;
@@ -6868,7 +6873,7 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm,
     /* Stop calling callback if application has called
        ngtcp2_conn_shutdown_stream_read() inside the callback.
        Because it doubly counts connection window. */
-    if (strm->flags & (NGTCP2_STRM_FLAG_STOP_SENDING)) {
+    if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) {
       return 0;
     }
 
@@ -11131,7 +11136,14 @@ int ngtcp2_conn_set_remote_transport_params(
     ngtcp2_conn *conn, const ngtcp2_transport_params *params) {
   int rv;
 
-  assert(!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED));
+  /* We expect this function is called once per QUIC connection, but
+     GnuTLS server seems to call TLS extension callback twice if it
+     sends HelloRetryRequest.  In practice, same QUIC transport
+     parameters are sent in the 2nd client flight, just returning 0
+     would cause no harm. */
+  if (conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED) {
+    return 0;
+  }
 
   /* Assume that ngtcp2_decode_transport_params sets default value if
      active_connection_id_limit is omitted. */
@@ -11174,7 +11186,8 @@ int ngtcp2_conn_set_remote_transport_params(
         conn->negotiated_version;
 
     ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON,
-                    "the negotiated version is %08x", conn->negotiated_version);
+                    "the negotiated version is 0x%08x",
+                    conn->negotiated_version);
   } else {
     rv = conn_client_validate_transport_params(conn, params);
     if (rv != 0) {
@@ -12271,6 +12284,8 @@ int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn,
  */
 static int conn_shutdown_stream_write(ngtcp2_conn *conn, ngtcp2_strm *strm,
                                       uint64_t app_error_code) {
+  ngtcp2_strm_set_app_error_code(strm, app_error_code);
+
   if ((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) ||
       ngtcp2_strm_is_all_tx_data_fin_acked(strm)) {
     return 0;
@@ -12279,7 +12294,6 @@ static int conn_shutdown_stream_write(ngtcp2_conn *conn, ngtcp2_strm *strm,
   /* Set this flag so that we don't accidentally send DATA to this
      stream. */
   strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST;
-  ngtcp2_strm_set_app_error_code(strm, app_error_code);
 
   ngtcp2_strm_streamfrq_clear(strm);
 
@@ -12327,7 +12341,7 @@ int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id,
 
   strm = ngtcp2_conn_find_stream(conn, stream_id);
   if (strm == NULL) {
-    return NGTCP2_ERR_STREAM_NOT_FOUND;
+    return 0;
   }
 
   rv = conn_shutdown_stream_read(conn, strm, app_error_code);
@@ -12349,7 +12363,7 @@ int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id,
 
   strm = ngtcp2_conn_find_stream(conn, stream_id);
   if (strm == NULL) {
-    return NGTCP2_ERR_STREAM_NOT_FOUND;
+    return 0;
   }
 
   return conn_shutdown_stream_write(conn, strm, app_error_code);
@@ -12361,7 +12375,7 @@ int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id,
 
   strm = ngtcp2_conn_find_stream(conn, stream_id);
   if (strm == NULL) {
-    return NGTCP2_ERR_STREAM_NOT_FOUND;
+    return 0;
   }
 
   return conn_shutdown_stream_read(conn, strm, app_error_code);
@@ -12409,7 +12423,7 @@ int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id,
 
   strm = ngtcp2_conn_find_stream(conn, stream_id);
   if (strm == NULL) {
-    return NGTCP2_ERR_STREAM_NOT_FOUND;
+    return 0;
   }
 
   return conn_extend_max_stream_offset(conn, strm, datalen);
@@ -12539,9 +12553,8 @@ int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
           &conn->log, NGTCP2_LOG_EVENT_RCV,
           "ignore rtt sample because ack_delay is too large latest_rtt=%" PRIu64
           " min_rtt=%" PRIu64 " ack_delay=%" PRIu64,
-          (uint64_t)(rtt / NGTCP2_MILLISECONDS),
-          (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS),
-          (uint64_t)(ack_delay / NGTCP2_MILLISECONDS));
+          rtt / NGTCP2_MILLISECONDS, cstat->min_rtt / NGTCP2_MILLISECONDS,
+          ack_delay / NGTCP2_MILLISECONDS);
       return NGTCP2_ERR_INVALID_ARGUMENT;
     }
 
@@ -12559,15 +12572,14 @@ int ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt,
     cstat->smoothed_rtt = (cstat->smoothed_rtt * 7 + rtt) / 8;
   }
 
-  ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
-                  "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64
-                  " smoothed_rtt=%" PRIu64 " rttvar=%" PRIu64
-                  " ack_delay=%" PRIu64,
-                  (uint64_t)(cstat->latest_rtt / NGTCP2_MILLISECONDS),
-                  (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS),
-                  cstat->smoothed_rtt / NGTCP2_MILLISECONDS,
-                  cstat->rttvar / NGTCP2_MILLISECONDS,
-                  (uint64_t)(ack_delay / NGTCP2_MILLISECONDS));
+  ngtcp2_log_info(
+      &conn->log, NGTCP2_LOG_EVENT_RCV,
+      "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 " smoothed_rtt=%" PRIu64
+      " rttvar=%" PRIu64 " ack_delay=%" PRIu64,
+      cstat->latest_rtt / NGTCP2_MILLISECONDS,
+      cstat->min_rtt / NGTCP2_MILLISECONDS,
+      cstat->smoothed_rtt / NGTCP2_MILLISECONDS,
+      cstat->rttvar / NGTCP2_MILLISECONDS, ack_delay / NGTCP2_MILLISECONDS);
 
   return 0;
 }
@@ -12580,52 +12592,44 @@ void ngtcp2_conn_get_conn_stat_versioned(ngtcp2_conn *conn,
   *cstat = conn->cstat;
 }
 
-static ngtcp2_pktns *conn_get_earliest_pktns(ngtcp2_conn *conn,
-                                             ngtcp2_tstamp *pts,
-                                             const ngtcp2_tstamp *times) {
-  ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns};
-  ngtcp2_pktns *res = NULL;
+static void conn_get_loss_time_and_pktns(ngtcp2_conn *conn,
+                                         ngtcp2_tstamp *ploss_time,
+                                         ngtcp2_pktns **ppktns) {
+  ngtcp2_pktns *const ns[] = {conn->hs_pktns, &conn->pktns};
+  ngtcp2_conn_stat *cstat = &conn->cstat;
+  ngtcp2_duration *loss_time = cstat->loss_time;
+  ngtcp2_tstamp earliest_loss_time = loss_time[NGTCP2_PKTNS_ID_INITIAL];
+  ngtcp2_pktns *pktns = conn->in_pktns;
   size_t i;
-  ngtcp2_tstamp earliest_ts = UINT64_MAX;
 
-  for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) {
+  for (i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
     if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 ||
-        (times[i] == UINT64_MAX ||
-         (earliest_ts != UINT64_MAX && times[i] >= earliest_ts) ||
-         (i == NGTCP2_PKTNS_ID_APPLICATION &&
-          !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
+        loss_time[i] >= earliest_loss_time) {
       continue;
     }
 
-    earliest_ts = times[i];
-    res = ns[i];
+    earliest_loss_time = loss_time[i];
+    pktns = ns[i];
   }
 
-  if (res == NULL && !conn->server) {
-    if (conn->hs_pktns && conn->hs_pktns->crypto.tx.ckm) {
-      res = conn->hs_pktns;
-    } else {
-      res = conn->in_pktns;
-    }
+  if (ploss_time) {
+    *ploss_time = earliest_loss_time;
   }
-
-  if (pts) {
-    *pts = earliest_ts;
+  if (ppktns) {
+    *ppktns = pktns;
   }
-  return res;
 }
 
 static ngtcp2_tstamp conn_get_earliest_pto_expiry(ngtcp2_conn *conn,
-                                                  const ngtcp2_tstamp *times,
-                                                  size_t pto_count,
                                                   ngtcp2_tstamp ts) {
   ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns};
   size_t i;
   ngtcp2_tstamp earliest_ts = UINT64_MAX, t;
   ngtcp2_conn_stat *cstat = &conn->cstat;
+  ngtcp2_tstamp *times = cstat->last_tx_pkt_ts;
   ngtcp2_duration duration =
       compute_pto(cstat->smoothed_rtt, cstat->rttvar, /* max_ack_delay = */ 0) *
-      (1ULL << pto_count);
+      (1ULL << cstat->pto_count);
 
   for (i = NGTCP2_PKTNS_ID_INITIAL; i < NGTCP2_PKTNS_ID_MAX; ++i) {
     if (ns[i] == NULL || ns[i]->rtb.num_pto_eliciting == 0 ||
@@ -12639,7 +12643,8 @@ static ngtcp2_tstamp conn_get_earliest_pto_expiry(ngtcp2_conn *conn,
 
     if (i == NGTCP2_PKTNS_ID_APPLICATION) {
       assert(conn->remote.transport_params);
-      t += conn->remote.transport_params->max_ack_delay * (1ULL << pto_count);
+      t += conn->remote.transport_params->max_ack_delay *
+           (1ULL << cstat->pto_count);
     }
 
     if (t < earliest_ts) {
@@ -12662,7 +12667,7 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
   ngtcp2_pktns *pktns = &conn->pktns;
   ngtcp2_tstamp earliest_loss_time;
 
-  conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time);
+  conn_get_loss_time_and_pktns(conn, &earliest_loss_time, NULL);
 
   if (earliest_loss_time != UINT64_MAX) {
     cstat->loss_detection_timer = earliest_loss_time;
@@ -12689,16 +12694,14 @@ void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
     return;
   }
 
-  cstat->loss_detection_timer = conn_get_earliest_pto_expiry(
-      conn, cstat->last_tx_pkt_ts, cstat->pto_count, ts);
+  cstat->loss_detection_timer = conn_get_earliest_pto_expiry(conn, ts);
 
   timeout =
       cstat->loss_detection_timer > ts ? cstat->loss_detection_timer - ts : 0;
 
   ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
                   "loss_detection_timer=%" PRIu64 " timeout=%" PRIu64,
-                  cstat->loss_detection_timer,
-                  (uint64_t)(timeout / NGTCP2_MILLISECONDS));
+                  cstat->loss_detection_timer, timeout / NGTCP2_MILLISECONDS);
 }
 
 int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
@@ -12707,8 +12710,7 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
   ngtcp2_pktns *in_pktns = conn->in_pktns;
   ngtcp2_pktns *hs_pktns = conn->hs_pktns;
   ngtcp2_tstamp earliest_loss_time;
-  ngtcp2_pktns *loss_pktns =
-      conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time);
+  ngtcp2_pktns *loss_pktns = NULL;
 
   conn->log.last_ts = ts;
   conn->qlog.last_ts = ts;
@@ -12727,10 +12729,14 @@ int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
     return 0;
   }
 
+  conn_get_loss_time_and_pktns(conn, &earliest_loss_time, &loss_pktns);
+
   ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV,
                   "loss detection timer fired");
 
   if (earliest_loss_time != UINT64_MAX) {
+    assert(loss_pktns);
+
     rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, cstat, ts);
     if (rv != 0) {
       return rv;
@@ -13368,6 +13374,16 @@ void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) {
   }
 }
 
+size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, int64_t stream_id) {
+  ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+  if (strm == NULL) {
+    return 0;
+  }
+
+  return strm->tx.loss_count;
+}
+
 void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
                                       const ngtcp2_path *path,
                                       const uint8_t *data) {
index 79bf44c9c4cf2eeddf0dd5fc2d8ae456527db949..b1c6564175d482f63b8619ee08d1a4ceba1dc6ab 100644 (file)
@@ -1087,6 +1087,8 @@ ngtcp2_ssize ngtcp2_conn_write_application_close_pkt(
     size_t destlen, uint64_t app_error_code, const uint8_t *reason,
     size_t reasonlen, ngtcp2_tstamp ts);
 
+int ngtcp2_conn_start_pmtud(ngtcp2_conn *conn);
+
 void ngtcp2_conn_stop_pmtud(ngtcp2_conn *conn);
 
 /**
index 2ec782b239ef6cc916ffb5deeb9b3b62decd1edf..afc940139e22743c1d6e2ac69ca351ac8954a01f 100644 (file)
@@ -60,9 +60,7 @@ void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) {
   ngtcp2_mem_free(mem, pc);
 }
 
-int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
-                                  size_t *pdcidlen, const uint8_t **pscid,
-                                  size_t *pscidlen, const uint8_t *data,
+int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest, const uint8_t *data,
                                   size_t datalen, size_t short_dcidlen) {
   size_t len;
   uint32_t version;
@@ -107,11 +105,11 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
       return NGTCP2_ERR_INVALID_ARGUMENT;
     }
 
-    *pversion = version;
-    *pdcid = &data[6];
-    *pdcidlen = dcidlen;
-    *pscid = &data[6 + dcidlen + 1];
-    *pscidlen = scidlen;
+    dest->version = version;
+    dest->dcid = &data[6];
+    dest->dcidlen = dcidlen;
+    dest->scid = &data[6 + dcidlen + 1];
+    dest->scidlen = scidlen;
 
     if (!version) {
       /* VN */
@@ -131,11 +129,11 @@ int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
     return NGTCP2_ERR_INVALID_ARGUMENT;
   }
 
-  *pversion = 0;
-  *pdcid = &data[1];
-  *pdcidlen = short_dcidlen;
-  *pscid = NULL;
-  *pscidlen = 0;
+  dest->version = 0;
+  dest->dcid = &data[1];
+  dest->dcidlen = short_dcidlen;
+  dest->scid = NULL;
+  dest->scidlen = 0;
 
   return 0;
 }
@@ -2267,8 +2265,8 @@ ngtcp2_ssize ngtcp2_pkt_write_retry(
 
   /* Retry packet is sent at most once per one connection attempt.  In
      the first connection attempt, client has to send random DCID
-     which is at least 8 bytes long. */
-  if (odcid->datalen < 8) {
+     which is at least NGTCP2_MIN_INITIAL_DCIDLEN bytes long. */
+  if (odcid->datalen < NGTCP2_MIN_INITIAL_DCIDLEN) {
     return NGTCP2_ERR_INVALID_ARGUMENT;
   }
 
index c2f97bf1808262631e138026ee848957d59cb432..644071400a6eb7d0e212ef1b8a151086b98fd13c 100644 (file)
@@ -319,6 +319,7 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id,
   rtb->cc_bytes_in_flight = 0;
   rtb->persistent_congestion_start_ts = UINT64_MAX;
   rtb->num_lost_pkts = 0;
+  rtb->num_lost_pmtud_pkts = 0;
 }
 
 void ngtcp2_rtb_free(ngtcp2_rtb *rtb) {
@@ -361,12 +362,18 @@ static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
   }
 }
 
-static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
-                          ngtcp2_conn_stat *cstat) {
+static size_t rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
+                            ngtcp2_conn_stat *cstat) {
   if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
     assert(rtb->num_lost_pkts);
     --rtb->num_lost_pkts;
-    return;
+
+    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+      assert(rtb->num_lost_pmtud_pkts);
+      --rtb->num_lost_pmtud_pkts;
+    }
+
+    return 0;
   }
 
   if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
@@ -391,16 +398,33 @@ static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
 
     assert(rtb->cc_bytes_in_flight >= ent->pktlen);
     rtb->cc_bytes_in_flight -= ent->pktlen;
+
+    /* If PMTUD packet is lost, we do not report the lost bytes to the
+       caller in order to ignore loss of PMTUD packet. */
+    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+      return 0;
+    }
+
+    return ent->pktlen;
   }
+
+  return 0;
 }
 
+/* NGTCP2_RECLAIM_FLAG_NONE indicates that no flag is set. */
+#define NGTCP2_RECLAIM_FLAG_NONE 0x00u
+/* NGTCP2_RECLAIM_FLAG_ON_LOSS indicates that frames are reclaimed
+   because of the packet loss.*/
+#define NGTCP2_RECLAIM_FLAG_ON_LOSS 0x01u
+
 /*
  * rtb_reclaim_frame queues unacknowledged frames included in |ent|
  * for retransmission.  The re-queued frames are not deleted from
- * |ent|.  It returns the number of frames queued.
+ * |ent|.  It returns the number of frames queued.  |flags| is bitwise
+ * OR of 0 or more of NGTCP2_RECLAIM_FLAG_*.
  */
-static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
-                                      ngtcp2_pktns *pktns,
+static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, uint8_t flags,
+                                      ngtcp2_conn *conn, ngtcp2_pktns *pktns,
                                       ngtcp2_rtb_entry *ent) {
   ngtcp2_frame_chain *frc, *nfrc, **pfrc = &pktns->tx.frq;
   ngtcp2_frame *fr;
@@ -447,6 +471,12 @@ static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
         }
       }
 
+      if ((flags & NGTCP2_RECLAIM_FLAG_ON_LOSS) &&
+          ent->hd.pkt_num != strm->tx.last_lost_pkt_num) {
+        strm->tx.last_lost_pkt_num = ent->hd.pkt_num;
+        ++strm->tx.loss_count;
+      }
+
       rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new(
           &nfrc, fr->stream.datacnt, rtb->frc_objalloc, rtb->mem);
       if (rv != 0) {
@@ -596,7 +626,9 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
     ngtcp2_qlog_pkt_lost(rtb->qlog, ent);
   }
 
-  if (rtb->cc->on_pkt_lost) {
+  if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+    ++rtb->num_lost_pmtud_pkts;
+  } else if (rtb->cc->on_pkt_lost) {
     cc->on_pkt_lost(cc, cstat,
                     ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen,
                                        rtb->pktns_id, ent->ts, ent->rst.lost,
@@ -635,7 +667,8 @@ static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it,
     assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
     assert(UINT64_MAX == ent->lost_ts);
 
-    reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent);
+    reclaimed =
+        rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_ON_LOSS, conn, pktns, ent);
     if (reclaimed < 0) {
       return (int)reclaimed;
     }
@@ -929,7 +962,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
   size_t ecn_acked = 0;
   int verify_ecn = 0;
   ngtcp2_cc_ack cc_ack = {0};
-  size_t num_lost_pkts = rtb->num_lost_pkts;
+  size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts;
 
   cc_ack.prior_bytes_in_flight = cstat->bytes_in_flight;
   cc_ack.rtt = UINT64_MAX;
@@ -1065,7 +1098,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
   }
 
   if (rtb->cc->on_spurious_congestion && num_lost_pkts &&
-      rtb->num_lost_pkts == 0) {
+      rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts == 0) {
     rtb->cc->on_spurious_congestion(cc, cstat, ts);
   }
 
@@ -1096,11 +1129,11 @@ fail:
 }
 
 static int rtb_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat,
-                        const ngtcp2_rtb_entry *ent, uint64_t loss_delay,
-                        ngtcp2_tstamp lost_send_time, size_t pkt_thres) {
+                        const ngtcp2_rtb_entry *ent, ngtcp2_duration loss_delay,
+                        size_t pkt_thres, ngtcp2_tstamp ts) {
   ngtcp2_tstamp loss_time;
 
-  if (ent->ts <= lost_send_time ||
+  if (ent->ts + loss_delay <= ts ||
       rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + (int64_t)pkt_thres) {
     return 1;
   }
@@ -1149,7 +1182,6 @@ static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
                                ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
   ngtcp2_rtb_entry *ent;
   ngtcp2_duration loss_delay;
-  ngtcp2_tstamp lost_send_time;
   ngtcp2_ksl_it it;
   ngtcp2_tstamp latest_ts, oldest_ts;
   int64_t last_lost_pkt_num;
@@ -1161,14 +1193,13 @@ static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
   size_t ecn_pkt_lost = 0;
   ngtcp2_tstamp start_ts;
   ngtcp2_duration pto = ngtcp2_conn_compute_pto(conn, pktns);
-  uint64_t prior_bytes_in_flight = cstat->bytes_in_flight;
+  uint64_t bytes_lost = 0;
   ngtcp2_duration max_ack_delay;
 
   pkt_thres = ngtcp2_max(pkt_thres, NGTCP2_PKT_THRESHOLD);
   pkt_thres = ngtcp2_min(pkt_thres, 256);
   cstat->loss_time[rtb->pktns_id] = UINT64_MAX;
   loss_delay = compute_pkt_loss_delay(cstat);
-  lost_send_time = ts - loss_delay;
 
   it = ngtcp2_ksl_lower_bound(&rtb->ents, &rtb->largest_acked_tx_pkt_num);
   for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) {
@@ -1178,8 +1209,7 @@ static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
       break;
     }
 
-    if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, lost_send_time,
-                     (size_t)pkt_thres)) {
+    if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, (size_t)pkt_thres, ts)) {
       /* All entries from ent are considered to be lost. */
       latest_ts = oldest_ts = ent->ts;
       last_lost_pkt_num = ent->hd.pkt_num;
@@ -1220,13 +1250,19 @@ static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
           ++ecn_pkt_lost;
         }
 
-        rtb_on_remove(rtb, ent, cstat);
+        bytes_lost += rtb_on_remove(rtb, ent, cstat);
         rv = rtb_on_pkt_lost(rtb, &it, ent, cstat, conn, pktns, ts);
         if (rv != 0) {
           return rv;
         }
       }
 
+      /* If only PMTUD packets are lost, do not trigger congestion
+         event. */
+      if (bytes_lost == 0) {
+        break;
+      }
+
       switch (conn->tx.ecn.state) {
       case NGTCP2_ECN_STATE_TESTING:
         if (conn->tx.ecn.validation_start_ts == UINT64_MAX) {
@@ -1287,9 +1323,7 @@ static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
   ngtcp2_rtb_remove_excessive_lost_pkt(rtb, (size_t)pkt_thres);
 
   if (ppkt_lost) {
-    assert(prior_bytes_in_flight >= cstat->bytes_in_flight);
-
-    *ppkt_lost = prior_bytes_in_flight - cstat->bytes_in_flight;
+    *ppkt_lost = bytes_lost;
   }
 
   return 0;
@@ -1319,6 +1353,11 @@ void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) {
                     "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
 
     --rtb->num_lost_pkts;
+
+    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+      --rtb->num_lost_pmtud_pkts;
+    }
+
     rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
     assert(0 == rv);
     ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
@@ -1354,6 +1393,11 @@ void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
                     "removing stale lost pkn=%" PRId64, ent->hd.pkt_num);
 
     --rtb->num_lost_pkts;
+
+    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+      --rtb->num_lost_pmtud_pkts;
+    }
+
     rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
     assert(0 == rv);
     ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
@@ -1419,6 +1463,10 @@ static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
   if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
     --rtb->num_lost_pkts;
 
+    if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
+      --rtb->num_lost_pmtud_pkts;
+    }
+
     ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV,
                     "pkn=%" PRId64
                     " was declared lost and has already been retransmitted",
@@ -1596,7 +1644,8 @@ ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
 
     assert(ent->frc);
 
-    reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent);
+    reclaimed =
+        rtb_reclaim_frame(rtb, NGTCP2_RECLAIM_FLAG_NONE, conn, pktns, ent);
     if (reclaimed < 0) {
       return reclaimed;
     }
index 895092ac3322828c0922aa74f91be9b3914d99fc..a97805dbaf3bc391f482c722945794878ecc9496 100644 (file)
@@ -336,6 +336,10 @@ typedef struct ngtcp2_rtb {
   /* num_lost_pkts is the number entries in ents which has
      NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */
   size_t num_lost_pkts;
+  /* num_lost_pmtud_pkts is the number of entries in ents which have
+     both NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED and
+     NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE flags set. */
+  size_t num_lost_pmtud_pkts;
 } ngtcp2_rtb;
 
 /*
index deca1a86b10cbfea34cd5e66bd1ebec7e6faffb1..6f20e866ad51c0cc78e01520ac1f44aa658570f8 100644 (file)
@@ -47,6 +47,8 @@ void ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags,
   strm->tx.offset = 0;
   strm->tx.max_offset = max_tx_offset;
   strm->tx.last_max_stream_data_ts = UINT64_MAX;
+  strm->tx.loss_count = 0;
+  strm->tx.last_lost_pkt_num = -1;
   strm->rx.rob = NULL;
   strm->rx.cont_offset = 0;
   strm->rx.last_offset = 0;
index d4dc1b2b4c5262373bb43ed4490a72bee18a3e65..8e3cfe83543509f4e228fb1c35b7f45002602afc 100644 (file)
@@ -111,6 +111,18 @@ struct ngtcp2_strm {
         /* last_max_stream_data_ts is the timestamp when last
            MAX_STREAM_DATA frame is sent. */
         ngtcp2_tstamp last_max_stream_data_ts;
+        /* loss_count is the number of packets that contain STREAM
+           frame for this stream and are declared to be lost.  It may
+           include the spurious losses.  It does not include a packet
+           whose contents have been reclaimed for PTO and which is
+           later declared to be lost.  Those data are not blocked by
+           the flow control and will be sent immediately if no other
+           restrictions are applied. */
+        size_t loss_count;
+        /* last_lost_pkt_num is the packet number of the packet that
+           is counted to loss_count.  It is used to avoid to count
+           multiple STREAM frames in one lost packet. */
+        int64_t last_lost_pkt_num;
       } tx;
 
       struct {
index afac27893afb98e34bc8068cc552444fbb56f4df..ed71cb3ea0cb3715e6f61a9707b6133527c565c1 100644 (file)
@@ -225,7 +225,7 @@ typedef struct ngtcp2_mem {
  *
  * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 second.
  */
-#define NGTCP2_SECONDS ((uint64_t)1000000000ULL)
+#define NGTCP2_SECONDS ((ngtcp2_duration)1000000000ULL)
 
 /**
  * @macro
@@ -233,7 +233,7 @@ typedef struct ngtcp2_mem {
  * :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds
  * to 1 millisecond.
  */
-#define NGTCP2_MILLISECONDS ((uint64_t)1000000ULL)
+#define NGTCP2_MILLISECONDS ((ngtcp2_duration)1000000ULL)
 
 /**
  * @macro
@@ -241,7 +241,7 @@ typedef struct ngtcp2_mem {
  * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds
  * to 1 microsecond.
  */
-#define NGTCP2_MICROSECONDS ((uint64_t)1000ULL)
+#define NGTCP2_MICROSECONDS ((ngtcp2_duration)1000ULL)
 
 /**
  * @macro
@@ -249,7 +249,7 @@ typedef struct ngtcp2_mem {
  * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to
  * 1 nanosecond.
  */
-#define NGTCP2_NANOSECONDS ((uint64_t)1ULL)
+#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL)
 
 /**
  * @macrosection
@@ -850,7 +850,7 @@ typedef struct NGTCP2_ALIGN(8) ngtcp2_pkt_info {
 /**
  * @macro
  *
- * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long packet
+ * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long header packet
  * header.
  */
 #define NGTCP2_PKT_FLAG_LONG_FORM 0x01u
@@ -2287,6 +2287,37 @@ NGTCP2_EXTERN int ngtcp2_decode_transport_params_new(
 NGTCP2_EXTERN void ngtcp2_transport_params_del(ngtcp2_transport_params *params,
                                                const ngtcp2_mem *mem);
 
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_version_cid` is a convenient struct to store the
+ * result of `ngtcp2_pkt_decode_version_cid`.
+ */
+typedef struct ngtcp2_version_cid {
+  /**
+   * :member:`version` stores QUIC version.
+   */
+  uint32_t version;
+  /**
+   * :member:`dcid` points to the Destination Connection ID.
+   */
+  const uint8_t *dcid;
+  /**
+   * :member:`dcidlen` is the length of the Destination Connection ID
+   * pointed by :member:`dcid`.
+   */
+  size_t dcidlen;
+  /**
+   * :member:`scid` points to the Source Connection ID.
+   */
+  const uint8_t *scid;
+  /**
+   * :member:`scidlen` is the length of the Source Connection ID
+   * pointed by :member:`scid`.
+   */
+  size_t scidlen;
+} ngtcp2_version_cid;
+
 /**
  * @function
  *
@@ -2299,25 +2330,31 @@ NGTCP2_EXTERN void ngtcp2_transport_params_del(ngtcp2_transport_params *params,
  * Longer Connection ID is only valid if the version is unsupported
  * QUIC version.
  *
- * If the given packet is Long packet, this function extracts the
- * version from the packet and assigns it to |*pversion|.  It also
+ * If the given packet is Long header packet, this function extracts
+ * the version from the packet and assigns it to
+ * :member:`dest->version <ngtcp2_version_cid.version>`.  It also
  * extracts the pointer to the Destination Connection ID and its
- * length and assigns them to |*pdcid| and |*pdcidlen| respectively.
- * Similarly, it extracts the pointer to the Source Connection ID and
- * its length and assigns them to |*pscid| and |*pscidlen|
- * respectively.
- *
- * If the given packet is Short packet, |*pversion| will be 0,
- * |*pscid| will be ``NULL``, and |*pscidlen| will be 0.  Because the
- * Short packet does not have the length of Destination Connection ID,
- * the caller has to pass the length in |short_dcidlen|.  This
- * function extracts the pointer to the Destination Connection ID and
- * assigns it to |*pdcid|.  |short_dcidlen| is assigned to
- * |*pdcidlen|.
+ * length and assigns them to :member:`dest->dcid
+ * <ngtcp2_version_cid.dcid>` and :member:`dest->dcidlen
+ * <ngtcp2_version_cid.dcidlen>` respectively.  Similarly, it extracts
+ * the pointer to the Source Connection ID and its length and assigns
+ * them to :member:`dest->scid <ngtcp2_version_cid.scid>` and
+ * :member:`dest->scidlen <ngtcp2_version_cid.scidlen>` respectively.
+ *
+ * If the given packet is Short header packet, :member:`dest->version
+ * <ngtcp2_version_cid.version>` will be 0, :member:`dest->scid
+ * <ngtcp2_version_cid.scid>` will be ``NULL``, and
+ * :member:`dest->scidlen <ngtcp2_version_cid.scidlen>` will be 0.
+ * Because the Short header packet does not have the length of
+ * Destination Connection ID, the caller has to pass the length in
+ * |short_dcidlen|.  This function extracts the pointer to the
+ * Destination Connection ID and assigns it to :member:`dest->dcid
+ * <ngtcp2_version_cid.dcid>`.  |short_dcidlen| is assigned to
+ * :member:`dest->dcidlen <ngtcp2_version_cid.dcidlen>`.
  *
  * If Version Negotiation is required, this function returns
  * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`.  Unlike the other error
- * cases, all output parameters are assigned as described above.
+ * cases, all fields of |dest| are assigned as described above.
  *
  * This function returns 0 if it succeeds.  Otherwise, one of the
  * following negative error code:
@@ -2327,11 +2364,10 @@ NGTCP2_EXTERN void ngtcp2_transport_params_del(ngtcp2_transport_params *params,
  * :macro:`NGTCP2_ERR_VERSION_NEGOTIATION`
  *     Version Negotiation packet should be sent.
  */
-NGTCP2_EXTERN int
-ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid,
-                              size_t *pdcidlen, const uint8_t **pscid,
-                              size_t *pscidlen, const uint8_t *data,
-                              size_t datalen, size_t short_dcidlen);
+NGTCP2_EXTERN int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest,
+                                                const uint8_t *data,
+                                                size_t datalen,
+                                                size_t short_dcidlen);
 
 /**
  * @function
@@ -2372,12 +2408,12 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest,
 /**
  * @function
  *
- * `ngtcp2_pkt_decode_hd_short` decodes QUIC short packet header in
- * |pkt| of length |pktlen|.  |dcidlen| is the length of DCID in
- * packet header.  Short packet does not encode the length of
- * connection ID, thus we need the input from the outside.  This
- * function only parses the input just before packet number field.
- * This function can handle Connection ID up to
+ * `ngtcp2_pkt_decode_hd_short` decodes QUIC short header packet
+ * header in |pkt| of length |pktlen|.  |dcidlen| is the length of
+ * DCID in packet header.  Short header packet does not encode the
+ * length of connection ID, thus we need the input from the outside.
+ * This function only parses the input just before packet number
+ * field.  This function can handle Connection ID up to
  * :macro:`NGTCP2_MAX_CIDLEN`.  Consider to use
  * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID.  It
  * stores the result in the object pointed by |dest|, and returns the
@@ -2468,9 +2504,6 @@ typedef struct ngtcp2_conn ngtcp2_conn;
  * This callback function must return 0 if it succeeds, or
  * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
  * return immediately.
- *
- * TODO: Define error code for TLS stack failure.  Suggestion:
- * NGTCP2_ERR_CRYPTO.
  */
 typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data);
 
@@ -2488,9 +2521,6 @@ typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data);
  * The callback function must return 0 if it succeeds.  If an error
  * occurs, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the
  * library call return immediately.
- *
- * TODO: Define error code for TLS stack failure.  Suggestion:
- * NGTCP2_ERR_CRYPTO.
  */
 typedef int (*ngtcp2_recv_client_initial)(ngtcp2_conn *conn,
                                           const ngtcp2_cid *dcid,
@@ -2612,15 +2642,15 @@ typedef int (*ngtcp2_recv_version_negotiation)(ngtcp2_conn *conn,
  * @functypedef
  *
  * :type:`ngtcp2_recv_retry` is invoked when Retry packet is received.
- * This callback is client only.
+ * This callback is client use only.
  *
  * Application must regenerate packet protection key, IV, and header
  * protection key for Initial packets using the destination connection
- * ID obtained by `ngtcp2_conn_get_dcid()` and install them by calling
- * `ngtcp2_conn_install_initial_key()`.
+ * ID obtained by :member:`hd->scid <ngtcp2_pkt_hd.scid>` and install
+ * them by calling `ngtcp2_conn_install_initial_key()`.
  *
- * 0-RTT data accepted by the ngtcp2 library will be retransmitted by
- * the library automatically.
+ * 0-RTT data accepted by the ngtcp2 library will be automatically
+ * retransmitted as 0-RTT data by the library.
  *
  * The callback function must return 0 if it succeeds.  Returning
  * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
@@ -2643,7 +2673,7 @@ typedef int (*ngtcp2_recv_retry)(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
  * The implementation of this callback must encrypt |plaintext| using
  * the negotiated cipher suite and write the ciphertext into the
  * buffer pointed by |dest|.  |dest| has enough capacity to store the
- * ciphertext.
+ * ciphertext and any additional AEAD tag data.
  *
  * |dest| and |plaintext| may point to the same buffer.
  *
@@ -2690,7 +2720,7 @@ typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead,
  * @functypedef
  *
  * :type:`ngtcp2_hp_mask` is invoked when the ngtcp2 library asks the
- * application to produce mask to encrypt or decrypt packet header.
+ * application to produce mask to encrypt or decrypt packet header.
  * The encryption cipher is |hp|.  |hp_ctx| is the cipher context
  * object which is initialized with header protection key.  The sample
  * is passed as |sample| which is :macro:`NGTCP2_HP_SAMPLELEN` bytes
@@ -2699,10 +2729,11 @@ typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead,
  * The implementation of this callback must produce a mask using the
  * header protection cipher suite specified by QUIC specification and
  * write the result into the buffer pointed by |dest|.  The length of
- * mask must be at least :macro:`NGTCP2_HP_MASKLEN`.  The library only
- * uses the first :macro:`NGTCP2_HP_MASKLEN` bytes of the produced
- * mask.  The buffer pointed by |dest| is guaranteed to have at least
- * :macro:`NGTCP2_HP_SAMPLELEN` bytes available for convenience.
+ * the mask must be at least :macro:`NGTCP2_HP_MASKLEN`.  The library
+ * only uses the first :macro:`NGTCP2_HP_MASKLEN` bytes of the
+ * produced mask.  The buffer pointed by |dest| is guaranteed to have
+ * at least :macro:`NGTCP2_HP_SAMPLELEN` bytes available for
+ * convenience.
  *
  * The callback function must return 0 if it succeeds, or
  * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call
@@ -2752,9 +2783,9 @@ typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
  * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` is nonzero, this portion of
  * the data is the last data in this stream.  |offset| is the offset
  * where this data begins.  The library ensures that data is passed to
- * the application in the non-decreasing order of |offset|.  The data
- * is passed as |data| of length |datalen|.  |datalen| may be 0 if and
- * only if |fin| is nonzero.
+ * the application in the non-decreasing order of |offset| without any
+ * overlap.  The data is passed as |data| of length |datalen|.
+ * |datalen| may be 0 if and only if |fin| is nonzero.
  *
  * If :macro:`NGTCP2_STREAM_DATA_FLAG_EARLY` is set in |flags|, it
  * indicates that a part of or whole data was received in 0RTT packet
@@ -2855,10 +2886,10 @@ typedef int (*ngtcp2_stream_reset)(ngtcp2_conn *conn, int64_t stream_id,
  * which is called when stream data is acked, and application can free
  * the data.  The acked range of data is [offset, offset + datalen).
  * For a given stream_id, this callback is called sequentially in
- * increasing order of |offset|.  |datalen| is normally strictly
- * greater than 0.  One exception is that when a packet which includes
- * STREAM frame which has fin flag set, and 0 length data, this
- * callback is invoked with 0 passed as |datalen|.
+ * increasing order of |offset| without any overlap.  |datalen| is
+ * normally strictly greater than 0.  One exception is that when a
+ * packet which includes STREAM frame which has fin flag set, and 0
+ * length data, this callback is invoked with 0 passed as |datalen|.
  *
  * If a stream is closed prematurely and stream data is still
  * in-flight, this callback function is not called for those data.
@@ -3211,7 +3242,8 @@ typedef int (*ngtcp2_ack_datagram)(ngtcp2_conn *conn, uint64_t dgram_id,
  * :type:`ngtcp2_lost_datagram` is invoked when a packet which
  * contains DATAGRAM frame which is identified by |dgram_id| is
  * declared lost.  |dgram_id| is the valued passed to
- * `ngtcp2_conn_writev_datagram`.
+ * `ngtcp2_conn_writev_datagram`.  Note that the loss might be
+ * spurious, and DATAGRAM frame might be acknowledged later.
  *
  * The callback function must return 0 if it succeeds, or
  * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return
@@ -3597,6 +3629,9 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close(
  *     Buffer is too small.
  * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`
  *     Callback function failed.
+ * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
+ *     :member:`odcid->datalen <ngtcp2_cid.datalen>` is less than
+ *     :macro:`NGTCP2_MIN_INITIAL_DCIDLEN`.
  */
 NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry(
     uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
@@ -3622,7 +3657,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry(
  *     Retry packet should be sent.
  * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`
  *     The packet is not acceptable for the very first packet to a new
- *     connection; or it failed to parse the packet header.
+ *     connection; or the function failed to parse the packet header.
  */
 NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
                                 size_t pktlen);
@@ -3633,7 +3668,7 @@ NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt,
  * `ngtcp2_conn_client_new` creates new :type:`ngtcp2_conn`, and
  * initializes it as client.  |dcid| is randomized destination
  * connection ID.  |scid| is source connection ID.
- * |client_chosen_version| is a QUIC version that a cilent chooses.
+ * |client_chosen_version| is a QUIC version that a client chooses.
  * |path| is the network path where this QUIC connection is being
  * established and must not be ``NULL``.  |callbacks|, |settings|, and
  * |params| must not be ``NULL``, and the function make a copy of each
@@ -3707,17 +3742,20 @@ NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn);
  * functions.
  *
  * This function returns 0 if it succeeds, or negative error codes.
- * In general, if the error code which satisfies
- * `ngtcp2_err_is_fatal(err) <ngtcp2_err_is_fatal>` != 0 is returned,
- * the application should just close the connection by calling
- * `ngtcp2_conn_write_connection_close` or just delete the QUIC
- * connection using `ngtcp2_conn_del`.  It is undefined to call the
- * other library functions.  If :macro:`NGTCP2_ERR_RETRY` is returned,
- * application must be a server and it must perform address validation
- * by sending Retry packet and close the connection.  If
+ * If :macro:`NGTCP2_ERR_RETRY` is returned, application must be a
+ * server and it must perform address validation by sending Retry
+ * packet and discard the connection state.  If
  * :macro:`NGTCP2_ERR_DROP_CONN` is returned, server application must
  * drop the connection silently (without sending any CONNECTION_CLOSE
- * frame) and discard connection state.
+ * frame) and discard connection state.  If
+ * :macro:`NGTCP2_ERR_DRAINING` is returned, a connection has entered
+ * the draining state, and no further packet transmission is allowed.
+ * If :macro:`NGTCP2_ERR_CRYPTO` is returned, the error happened in
+ * TLS stack and `ngtcp2_conn_get_tls_alert` returns TLS alert if set.
+ *
+ * If any other negative errors are returned, call
+ * `ngtcp2_conn_write_connection_close` to get terminal packet, and
+ * sending it makes QUIC connection enter the closing state.
  */
 NGTCP2_EXTERN int
 ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path,
@@ -3916,7 +3954,7 @@ NGTCP2_EXTERN int ngtcp2_conn_install_early_key(
  * @function
  *
  * `ngtcp2_conn_install_rx_key` installs packet protection keying
- * materials for decrypting Short packets.  |secret| of length
+ * materials for decrypting Short header packets.  |secret| of length
  * |secretlen| is the decryption secret which is used to derive keying
  * materials passed to this function.  |aead_ctx| is AEAD cipher
  * context object which must be initialized with a decryption key.
@@ -3947,7 +3985,7 @@ NGTCP2_EXTERN int ngtcp2_conn_install_rx_key(
  * @function
  *
  * `ngtcp2_conn_install_tx_key` installs packet protection keying
- * materials for encrypting Short packets.  |secret| of length
+ * materials for encrypting Short header packets.  |secret| of length
  * |secretlen| is the encryption secret which is used to derive keying
  * materials passed to this function.  |aead_ctx| is AEAD cipher
  * context object which must be initialized with an encryption key.
@@ -4256,8 +4294,6 @@ NGTCP2_EXTERN int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn,
  *
  * :macro:`NGTCP2_ERR_NOMEM`
  *     Out of memory
- * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
- *     Stream does not exist
  */
 NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn,
                                               int64_t stream_id,
@@ -4278,8 +4314,6 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn,
  *
  * :macro:`NGTCP2_ERR_NOMEM`
  *     Out of memory
- * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
- *     Stream does not exist
  */
 NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn,
                                                     int64_t stream_id,
@@ -4299,8 +4333,6 @@ NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn,
  *
  * :macro:`NGTCP2_ERR_NOMEM`
  *     Out of memory
- * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
- *     Stream does not exist
  */
 NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn,
                                                    int64_t stream_id,
@@ -4602,7 +4634,7 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned(
  * @function
  *
  * `ngtcp2_conn_is_in_closing_period` returns nonzero if |conn| is in
- * closing period.
+ * the closing period.
  */
 NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn);
 
@@ -4610,7 +4642,7 @@ NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn);
  * @function
  *
  * `ngtcp2_conn_is_in_draining_period` returns nonzero if |conn| is in
- * draining period.
+ * the draining period.
  */
 NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn);
 
@@ -4623,8 +4655,8 @@ NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn);
  * This function returns 0 if it succeeds, or one of the following
  * negative error codes:
  *
- * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND`
- *     Stream was not found
+ * :macro:`NGTCP2_ERR_NOMEM`
+ *     Out of memory.
  */
 NGTCP2_EXTERN int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn,
                                                        int64_t stream_id,
@@ -5378,6 +5410,18 @@ NGTCP2_EXTERN void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn,
  */
 NGTCP2_EXTERN size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn);
 
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_stream_loss_count` returns the number of packets
+ * that contain STREAM frame for a stream identified by |stream_id|
+ * and are declared to be lost.  The number may include the spurious
+ * losses.  If no stream identified by |stream_id| is found, this
+ * function returns 0.
+ */
+NGTCP2_EXTERN size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn,
+                                                       int64_t stream_id);
+
 /**
  * @function
  *
index 7ddba40d2a7e288ab882635b43a331254f4073b0..4736b51c3cb48dcde9f7cd2c0bdee2440e647e9d 100644 (file)
@@ -68,7 +68,7 @@ extern "C" {
  * `ngtcp2_crypto_ctx_tls` initializes |ctx| by extracting negotiated
  * ciphers and message digests from native TLS session
  * |tls_native_handle|.  This is used for encrypting/decrypting
- * Handshake and Short packets.
+ * Handshake and Short header packets.
  *
  * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be
  * a pointer to SSL object.
index 0bde99d5b7d9ddbd66ba82e06d04a9b2f9b87bff..4172dd43463f21c1c519d1a7a895a012d6f655bd 100644 (file)
@@ -36,7 +36,7 @@
  *
  * Version number of the ngtcp2 library release.
  */
-#define NGTCP2_VERSION "0.7.0"
+#define NGTCP2_VERSION "0.8.0"
 
 /**
  * @macro
@@ -46,6 +46,6 @@
  * number, 8 bits for minor and 8 bits for patch. Version 1.2.3
  * becomes 0x010203.
  */
-#define NGTCP2_VERSION_NUM 0x000700
+#define NGTCP2_VERSION_NUM 0x000800
 
 #endif /* VERSION_H */