]> git.ipfire.org Git - thirdparty/knot-dns.git/commitdiff
libngtcp2: update embedded library to v1.17.0
authorJan Doskočil <jan.doskocil@nic.cz>
Mon, 20 Oct 2025 08:36:48 +0000 (10:36 +0200)
committerDaniel Salzman <daniel.salzman@nic.cz>
Mon, 20 Oct 2025 08:39:58 +0000 (10:39 +0200)
14 files changed:
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.c
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rst.h
src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c
src/contrib/libngtcp2/ngtcp2/ngtcp2.h
src/contrib/libngtcp2/ngtcp2/version.h

index 1fb273d494e8e2ab77daa2d5784661fb89e3a408..a028a8a8f4045fd7529265fb51019a09104a1378 100644 (file)
@@ -58,14 +58,12 @@ int ngtcp2_sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) {
 
   switch (a->sa_family) {
   case NGTCP2_AF_INET: {
-    const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
-                             *bi = (const ngtcp2_sockaddr_in *)(void *)b;
+    const ngtcp2_sockaddr_in *ai = (void *)a, *bi = (void *)b;
     return ai->sin_port == bi->sin_port &&
            memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0;
   }
   case NGTCP2_AF_INET6: {
-    const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
-                              *bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
+    const ngtcp2_sockaddr_in6 *ai = (void *)a, *bi = (void *)b;
     return ai->sin6_port == bi->sin6_port &&
            memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0;
   }
@@ -89,8 +87,7 @@ uint32_t ngtcp2_addr_cmp(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
 
   switch (a->sa_family) {
   case NGTCP2_AF_INET: {
-    const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a,
-                             *bi = (const ngtcp2_sockaddr_in *)(void *)b;
+    const ngtcp2_sockaddr_in *ai = (void *)a, *bi = (void *)b;
     if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) {
       flags |= NGTCP2_ADDR_CMP_FLAG_ADDR;
     }
@@ -100,8 +97,7 @@ uint32_t ngtcp2_addr_cmp(const ngtcp2_addr *aa, const ngtcp2_addr *bb) {
     return flags;
   }
   case NGTCP2_AF_INET6: {
-    const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a,
-                              *bi = (const ngtcp2_sockaddr_in6 *)(void *)b;
+    const ngtcp2_sockaddr_in6 *ai = (void *)a, *bi = (void *)b;
     if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) {
       flags |= NGTCP2_ADDR_CMP_FLAG_ADDR;
     }
index 4a6797689fcc01257b54b0fab5b23664d7522044..cb865c5cefdf4c1a0111f90e631c702204ffb1b8 100644 (file)
@@ -71,7 +71,7 @@ int ngtcp2_balloc_get(ngtcp2_balloc *balloc, void **pbuf, size_t n) {
       return NGTCP2_ERR_NOMEM;
     }
 
-    hd = (ngtcp2_memblock_hd *)(void *)p;
+    hd = (void *)p;
     hd->next = balloc->head;
     balloc->head = hd;
     ngtcp2_buf_init(
index 2aa5d6fcadb7ad6f18ce48a189fed9dcda038705..62e6b2f125aafe9939f64b44b61cd8eab7d39a75 100644 (file)
@@ -190,20 +190,20 @@ static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr,
 static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr,
                                     ngtcp2_conn_stat *cstat);
 
-static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr);
+static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr, const ngtcp2_rs *rs);
 
 static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr,
                                          ngtcp2_conn_stat *cstat,
-                                         ngtcp2_tstamp ts);
+                                         const ngtcp2_rs *rs, ngtcp2_tstamp ts);
 
 static void bbr_note_loss(ngtcp2_cc_bbr *bbr);
 
 static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
                                    const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
 
-static uint64_t
-bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr,
-                                       const ngtcp2_cc_pkt *pkt);
+static uint64_t bbr_inflight_at_loss(ngtcp2_cc_bbr *bbr,
+                                     const ngtcp2_cc_pkt *pkt,
+                                     const ngtcp2_rs *rs);
 
 static void bbr_update_min_rtt(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack,
                                ngtcp2_tstamp ts);
@@ -334,8 +334,6 @@ static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
 
   bbr->max_inflight = 0;
 
-  bbr->congestion_recovery_start_ts = UINT64_MAX;
-
   bbr->bdp = 0;
 }
 
@@ -393,7 +391,7 @@ static void bbr_check_startup_high_loss(ngtcp2_cc_bbr *bbr) {
   if (bbr->full_bw_reached || bbr->loss_events_in_round <= 6 ||
       (bbr->in_loss_recovery &&
        bbr->round_count <= bbr->round_count_at_recovery) ||
-      !bbr_is_inflight_too_high(bbr)) {
+      !bbr_is_inflight_too_high(bbr, &bbr->rst->rs)) {
     return;
   }
 
@@ -403,11 +401,12 @@ static void bbr_check_startup_high_loss(ngtcp2_cc_bbr *bbr) {
 }
 
 static void bbr_init_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
-  cstat->pacing_interval_m =
+  cstat->pacing_interval_m = ngtcp2_max_uint64(
     ((cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS
                                                : cstat->smoothed_rtt)
      << 10) *
-    100 / NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd;
+      100 / NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd,
+    1);
 }
 
 static void bbr_set_pacing_rate_with_gain(ngtcp2_cc_bbr *bbr,
@@ -568,7 +567,8 @@ static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
                               const ngtcp2_cc_ack *ack) {
   bbr_update_round(bbr, ack);
 
-  if (cstat->delivery_rate_sec >= bbr->max_bw || !bbr->rst->rs.is_app_limited) {
+  if (cstat->delivery_rate_sec && (cstat->delivery_rate_sec >= bbr->max_bw ||
+                                   !bbr->rst->rs.is_app_limited)) {
     ngtcp2_window_filter_update(&bbr->max_bw_filter, cstat->delivery_rate_sec,
                                 bbr->cycle_count);
 
@@ -880,7 +880,7 @@ static void bbr_adapt_longterm_model(ngtcp2_cc_bbr *bbr,
     }
   }
 
-  if (!bbr_is_inflight_too_high(bbr)) {
+  if (!bbr_is_inflight_too_high(bbr, &bbr->rst->rs)) {
     if (bbr->inflight_longterm == UINT64_MAX) {
       return;
     }
@@ -926,17 +926,16 @@ static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr,
   return ngtcp2_min_uint64(bbr->bdp, cstat->cwnd);
 }
 
-static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr) {
-  const ngtcp2_rs *rs = &bbr->rst->rs;
+static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr, const ngtcp2_rs *rs) {
+  (void)bbr;
   return rs->lost * NGTCP2_BBR_LOSS_THRESH_DENOM >
          rs->tx_in_flight * NGTCP2_BBR_LOSS_THRESH_NUMER;
 }
 
 static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr,
                                          ngtcp2_conn_stat *cstat,
+                                         const ngtcp2_rs *rs,
                                          ngtcp2_tstamp ts) {
-  const ngtcp2_rs *rs = &bbr->rst->rs;
-
   bbr->bw_probe_samples = 0;
 
   if (!rs->is_app_limited) {
@@ -960,7 +959,7 @@ static void bbr_note_loss(ngtcp2_cc_bbr *bbr) {
 
 static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
                                    const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) {
-  ngtcp2_rs *rs = &bbr->rst->rs;
+  ngtcp2_rs rs = {0};
 
   bbr_note_loss(bbr);
 
@@ -968,22 +967,21 @@ static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
     return;
   }
 
-  rs->tx_in_flight = pkt->tx_in_flight;
+  rs.tx_in_flight = pkt->tx_in_flight;
   assert(bbr->rst->lost >= pkt->lost);
-  rs->lost = bbr->rst->lost - pkt->lost;
-  rs->is_app_limited = pkt->is_app_limited;
+  rs.lost = bbr->rst->lost - pkt->lost;
+  rs.is_app_limited = pkt->is_app_limited;
 
-  if (bbr_is_inflight_too_high(bbr)) {
-    rs->tx_in_flight = bbr_inflight_longterm_from_lost_packet(bbr, pkt);
+  if (bbr_is_inflight_too_high(bbr, &rs)) {
+    rs.tx_in_flight = bbr_inflight_at_loss(bbr, pkt, &rs);
 
-    bbr_handle_inflight_too_high(bbr, cstat, ts);
+    bbr_handle_inflight_too_high(bbr, cstat, &rs, ts);
   }
 }
 
-static uint64_t
-bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr,
-                                       const ngtcp2_cc_pkt *pkt) {
-  ngtcp2_rs *rs = &bbr->rst->rs;
+static uint64_t bbr_inflight_at_loss(ngtcp2_cc_bbr *bbr,
+                                     const ngtcp2_cc_pkt *pkt,
+                                     const ngtcp2_rs *rs) {
   uint64_t inflight_prev, lost_prev, lost_prefix;
   (void)bbr;
 
@@ -1280,28 +1278,15 @@ static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
 
 static void bbr_handle_recovery(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
                                 const ngtcp2_cc_ack *ack) {
-  if (bbr->in_loss_recovery) {
-    if (ack->largest_pkt_sent_ts != UINT64_MAX &&
-        !in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
-      bbr->in_loss_recovery = 0;
-      bbr->round_count_at_recovery = UINT64_MAX;
-      bbr_restore_cwnd(bbr, cstat);
-    }
-
+  if (!bbr->in_loss_recovery) {
     return;
   }
 
-  if (bbr->congestion_recovery_start_ts != UINT64_MAX) {
-    bbr->in_loss_recovery = 1;
-    bbr->round_count_at_recovery =
-      bbr->round_start ? bbr->round_count : bbr->round_count + 1;
-    bbr_save_cwnd(bbr, cstat);
-    cstat->cwnd =
-      cstat->bytes_in_flight +
-      ngtcp2_max_uint64(ack->bytes_delivered, cstat->max_tx_udp_payload_size);
-
-    cstat->congestion_recovery_start_ts = bbr->congestion_recovery_start_ts;
-    bbr->congestion_recovery_start_ts = UINT64_MAX;
+  if (ack->largest_pkt_sent_ts != UINT64_MAX &&
+      !in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
+    bbr->in_loss_recovery = 0;
+    bbr->round_count_at_recovery = UINT64_MAX;
+    bbr_restore_cwnd(bbr, cstat);
   }
 }
 
@@ -1313,18 +1298,24 @@ static void bbr_cc_on_pkt_lost(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
 }
 
 static void bbr_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
-                                    ngtcp2_tstamp sent_ts, uint64_t bytes_lost,
+                                    ngtcp2_tstamp sent_ts,
+                                    const ngtcp2_cc_ack *ack,
                                     ngtcp2_tstamp ts) {
   ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc);
-  (void)bytes_lost;
 
-  if (bbr->in_loss_recovery ||
-      bbr->congestion_recovery_start_ts != UINT64_MAX ||
-      in_congestion_recovery(cstat, sent_ts)) {
+  if (bbr->in_loss_recovery || in_congestion_recovery(cstat, sent_ts)) {
     return;
   }
 
-  bbr->congestion_recovery_start_ts = ts;
+  bbr->in_loss_recovery = 1;
+  bbr->round_count_at_recovery =
+    bbr->round_start ? bbr->round_count : bbr->round_count + 1;
+  bbr_save_cwnd(bbr, cstat);
+  cstat->cwnd =
+    cstat->bytes_in_flight +
+    ngtcp2_max_uint64(ack->bytes_delivered, cstat->max_tx_udp_payload_size);
+
+  cstat->congestion_recovery_start_ts = ts;
 }
 
 static void bbr_cc_on_spurious_congestion(ngtcp2_cc *cc,
@@ -1333,7 +1324,6 @@ static void bbr_cc_on_spurious_congestion(ngtcp2_cc *cc,
   ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc);
   (void)ts;
 
-  bbr->congestion_recovery_start_ts = UINT64_MAX;
   cstat->congestion_recovery_start_ts = UINT64_MAX;
 
   if (bbr->in_loss_recovery) {
@@ -1350,7 +1340,6 @@ static void bbr_cc_on_persistent_congestion(ngtcp2_cc *cc,
   (void)ts;
 
   cstat->congestion_recovery_start_ts = UINT64_MAX;
-  bbr->congestion_recovery_start_ts = UINT64_MAX;
   bbr->in_loss_recovery = 0;
   bbr->round_count_at_recovery = UINT64_MAX;
 
@@ -1365,6 +1354,11 @@ static void bbr_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
   ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc);
 
   bbr_handle_recovery(bbr, cstat, ack);
+
+  if (ack->bytes_delivered == 0) {
+    return;
+  }
+
   bbr_update_on_ack(bbr, cstat, ack, ts);
 }
 
index 0499924f58264f950bedd37ae831bf18d61d6b91..f0d529639ce5bea79f6f8c06ea2d788689fb39d8 100644 (file)
@@ -130,7 +130,6 @@ typedef struct ngtcp2_cc_bbr {
   int in_loss_recovery;
   uint64_t round_count_at_recovery;
   uint64_t max_inflight;
-  ngtcp2_tstamp congestion_recovery_start_ts;
   uint64_t bdp;
 } ngtcp2_cc_bbr;
 
index 3fffef54484b80979fd16e9fa6db4ee6cd561feb..f8e420ed7d8ea6b28387bdadb41159e08f8e634d 100644 (file)
@@ -41,6 +41,40 @@ uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) {
   return ngtcp2_min_uint64(10 * max_udp_payload_size, n);
 }
 
+/* 1.25 is the under-utilization avoidance factor described in
+   https://datatracker.ietf.org/doc/html/rfc9002#section-7.7 */
+#define NGTCP2_CC_PACING_GAIN_H 125
+
+static void init_pacing_rate(ngtcp2_conn_stat *cstat) {
+  assert(cstat->cwnd);
+
+  cstat->pacing_interval_m = ngtcp2_max_uint64(
+    (NGTCP2_MILLISECONDS << 10) * 100 / NGTCP2_CC_PACING_GAIN_H / cstat->cwnd,
+    1);
+  cstat->send_quantum = 10 * cstat->max_tx_udp_payload_size;
+}
+
+static void set_pacing_rate(ngtcp2_conn_stat *cstat) {
+  size_t send_quantum = 64 * 1024;
+
+  assert(cstat->cwnd);
+
+  cstat->pacing_interval_m =
+    ((cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS
+                                               : cstat->smoothed_rtt)
+     << 10) *
+    100 / NGTCP2_CC_PACING_GAIN_H / cstat->cwnd;
+
+  cstat->pacing_interval_m = ngtcp2_max_uint64(cstat->pacing_interval_m, 1);
+
+  send_quantum =
+    ngtcp2_min_size(send_quantum, (size_t)((NGTCP2_MILLISECONDS << 10) /
+                                           cstat->pacing_interval_m));
+
+  cstat->send_quantum =
+    ngtcp2_max_size(send_quantum, 10 * cstat->max_tx_udp_payload_size);
+}
+
 ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
                                   size_t pktlen, ngtcp2_pktns_id pktns_id,
                                   ngtcp2_tstamp sent_ts, uint64_t lost,
@@ -56,9 +90,14 @@ ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num,
   return pkt;
 }
 
-static void reno_cc_reset(ngtcp2_cc_reno *reno) { reno->pending_add = 0; }
+static void reno_cc_reset(ngtcp2_cc_reno *reno, ngtcp2_conn_stat *cstat) {
+  reno->pending_add = 0;
 
-void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log) {
+  init_pacing_rate(cstat);
+}
+
+void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log,
+                         ngtcp2_conn_stat *cstat) {
   *reno = (ngtcp2_cc_reno){
     .cc =
       {
@@ -70,7 +109,7 @@ void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log) {
       },
   };
 
-  reno_cc_reset(reno);
+  reno_cc_reset(reno, cstat);
 }
 
 static int in_congestion_recovery(const ngtcp2_conn_stat *cstat,
@@ -92,6 +131,9 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
 
   if (cstat->cwnd < cstat->ssthresh) {
     cstat->cwnd += pkt->pktlen;
+
+    set_pacing_rate(cstat);
+
     ngtcp2_log_infof(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
                      "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64,
                      pkt->pkt_num, cstat->cwnd);
@@ -102,14 +144,17 @@ void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
   reno->pending_add = m % cstat->cwnd;
 
   cstat->cwnd += m / cstat->cwnd;
+
+  set_pacing_rate(cstat);
 }
 
 void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
                                         ngtcp2_tstamp sent_ts,
-                                        uint64_t bytes_lost, ngtcp2_tstamp ts) {
+                                        const ngtcp2_cc_ack *ack,
+                                        ngtcp2_tstamp ts) {
   ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc);
   uint64_t min_cwnd;
-  (void)bytes_lost;
+  (void)ack;
 
   if (in_congestion_recovery(cstat, sent_ts)) {
     return;
@@ -123,6 +168,8 @@ void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
 
   reno->pending_add = 0;
 
+  set_pacing_rate(cstat);
+
   ngtcp2_log_infof(reno->cc.log, NGTCP2_LOG_EVENT_CCA,
                    "reduce cwnd because of packet loss cwnd=%" PRIu64,
                    cstat->cwnd);
@@ -136,15 +183,16 @@ void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
 
   cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
   cstat->congestion_recovery_start_ts = UINT64_MAX;
+
+  set_pacing_rate(cstat);
 }
 
 void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
                              ngtcp2_tstamp ts) {
   ngtcp2_cc_reno *reno = ngtcp2_struct_of(cc, ngtcp2_cc_reno, cc);
-  (void)cstat;
   (void)ts;
 
-  reno_cc_reset(reno);
+  reno_cc_reset(reno, cstat);
 }
 
 static void cubic_vars_reset(ngtcp2_cubic_vars *v) {
@@ -156,11 +204,11 @@ static void cubic_vars_reset(ngtcp2_cubic_vars *v) {
 
   v->app_limited_start_ts = UINT64_MAX;
   v->app_limited_duration = 0;
-  v->pending_bytes_delivered = 0;
-  v->pending_est_bytes_delivered = 0;
+  v->pending_bytes_acked = 0;
+  v->pending_est_bytes_acked = 0;
 }
 
-static void cubic_cc_reset(ngtcp2_cc_cubic *cubic) {
+static void cubic_cc_reset(ngtcp2_cc_cubic *cubic, ngtcp2_conn_stat *cstat) {
   cubic_vars_reset(&cubic->current);
   cubic_vars_reset(&cubic->undo.v);
   cubic->undo.cwnd = 0;
@@ -174,10 +222,12 @@ static void cubic_cc_reset(ngtcp2_cc_cubic *cubic) {
   cubic->hs.css_round = 0;
 
   cubic->next_round_delivered = 0;
+
+  init_pacing_rate(cstat);
 }
 
 void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cubic, ngtcp2_log *log,
-                          ngtcp2_rst *rst) {
+                          ngtcp2_conn_stat *cstat, ngtcp2_rst *rst) {
   *cubic = (ngtcp2_cc_cubic){
     .cc =
       {
@@ -191,7 +241,7 @@ void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cubic, ngtcp2_log *log,
     .rst = rst,
   };
 
-  cubic_cc_reset(cubic);
+  cubic_cc_reset(cubic, cstat);
 }
 
 uint64_t ngtcp2_cbrt(uint64_t n) {
@@ -266,12 +316,14 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
   ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
   uint64_t w_cubic, w_cubic_next;
   uint64_t target, m;
+  uint64_t bytes_acked;
   ngtcp2_duration rtt_thresh;
   int round_start;
   int is_app_limited =
     cubic->rst->rs.is_app_limited && !cubic->rst->is_cwnd_limited;
 
-  if (in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
+  if (ack->bytes_delivered == 0 ||
+      in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) {
     return;
   }
 
@@ -291,6 +343,8 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
         cstat->cwnd += ack->bytes_delivered;
       }
 
+      set_pacing_rate(cstat);
+
       ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
                        "%" PRIu64 " bytes acked, slow start cwnd=%" PRIu64,
                        ack->bytes_delivered, cstat->cwnd);
@@ -379,25 +433,36 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
     target = w_cubic_next;
   }
 
-  m = ack->bytes_delivered * cstat->max_tx_udp_payload_size +
-      cubic->current.pending_est_bytes_delivered;
-  cubic->current.pending_est_bytes_delivered = m % cstat->cwnd;
+  bytes_acked = ack->bytes_delivered * cstat->max_tx_udp_payload_size;
+  m = (bytes_acked + cubic->current.pending_est_bytes_acked) / cstat->cwnd;
+
+  cubic->current.pending_est_bytes_acked += bytes_acked;
+  cubic->current.pending_est_bytes_acked -= m * cstat->cwnd;
+
+  assert(cubic->current.pending_est_bytes_acked < cstat->cwnd);
 
   if (cubic->current.w_est < cubic->current.cwnd_prior) {
-    cubic->current.w_est += m * 9 / 17 / cstat->cwnd;
+    cubic->current.w_est += m * 9 / 17;
   } else {
-    cubic->current.w_est += m / cstat->cwnd;
+    cubic->current.w_est += m;
   }
 
   if (cubic->current.w_est > w_cubic) {
     cstat->cwnd = cubic->current.w_est;
   } else {
-    m = (target - cstat->cwnd) * cstat->max_tx_udp_payload_size +
-        cubic->current.pending_bytes_delivered;
-    cubic->current.pending_bytes_delivered = m % cstat->cwnd;
-    cstat->cwnd += m / cstat->cwnd;
+    bytes_acked = (target - cstat->cwnd) * cstat->max_tx_udp_payload_size;
+    m = (bytes_acked + cubic->current.pending_bytes_acked) / cstat->cwnd;
+
+    cubic->current.pending_bytes_acked += bytes_acked;
+    cubic->current.pending_bytes_acked -= m * cstat->cwnd;
+
+    assert(cubic->current.pending_bytes_acked < cstat->cwnd);
+
+    cstat->cwnd += m;
   }
 
+  set_pacing_rate(cstat);
+
   ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
                    "%" PRIu64 " bytes acked, cubic-ca cwnd=%" PRIu64
                    " k_m=%" PRIu64 " target=%" PRIu64 " w_est=%" PRIu64,
@@ -407,7 +472,7 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
 
 void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
                                          ngtcp2_tstamp sent_ts,
-                                         uint64_t bytes_lost,
+                                         const ngtcp2_cc_ack *ack,
                                          ngtcp2_tstamp ts) {
   ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
   uint64_t flight_size;
@@ -428,8 +493,8 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
   cubic->current.epoch_start = ts;
   cubic->current.app_limited_start_ts = UINT64_MAX;
   cubic->current.app_limited_duration = 0;
-  cubic->current.pending_bytes_delivered = 0;
-  cubic->current.pending_est_bytes_delivered = 0;
+  cubic->current.pending_bytes_acked = 0;
+  cubic->current.pending_est_bytes_acked = 0;
 
   if (cstat->cwnd < cubic->current.w_max) {
     cubic->current.w_max = cstat->cwnd * 17 / 20;
@@ -443,7 +508,7 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
   cstat->ssthresh = cstat->cwnd * 7 / 10;
 
   if (cubic->rst->rs.delivered * 2 < cstat->cwnd) {
-    flight_size = cstat->bytes_in_flight + bytes_lost;
+    flight_size = cstat->bytes_in_flight + ack->bytes_lost;
     cstat->ssthresh = ngtcp2_min_uint64(
       cstat->ssthresh,
       ngtcp2_max_uint64(cubic->rst->rs.delivered, flight_size));
@@ -464,6 +529,8 @@ void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
   cubic->current.k_m =
     ngtcp2_cbrt((cwnd_delta << 30) * 10 / 4 / cstat->max_tx_udp_payload_size);
 
+  set_pacing_rate(cstat);
+
   ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
                    "reduce cwnd because of packet loss cwnd=%" PRIu64,
                    cstat->cwnd);
@@ -482,6 +549,8 @@ void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *cc,
     cstat->cwnd = cubic->undo.cwnd;
     cstat->ssthresh = cubic->undo.ssthresh;
 
+    set_pacing_rate(cstat);
+
     ngtcp2_log_infof(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
                      "spurious congestion is detected and congestion state is "
                      "restored cwnd=%" PRIu64,
@@ -499,17 +568,18 @@ void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc,
   ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
   (void)ts;
 
-  cubic_cc_reset(cubic);
+  cubic_cc_reset(cubic, cstat);
 
   cstat->cwnd = 2 * cstat->max_tx_udp_payload_size;
   cstat->congestion_recovery_start_ts = UINT64_MAX;
+
+  set_pacing_rate(cstat);
 }
 
 void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
                               ngtcp2_tstamp ts) {
   ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
-  (void)cstat;
   (void)ts;
 
-  cubic_cc_reset(cubic);
+  cubic_cc_reset(cubic, cstat);
 }
index e4e19e1aa6b9455da4682e75a03f1b0b5da5a9da..010a1c3541d51b55c585349804848dc7086f3168 100644 (file)
@@ -88,11 +88,6 @@ typedef struct ngtcp2_cc_pkt {
  * acknowledged and lost bytes.
  */
 typedef struct ngtcp2_cc_ack {
-  /**
-   * :member:`prior_bytes_in_flight` is the in-flight bytes before
-   * processing this ACK.
-   */
-  uint64_t prior_bytes_in_flight;
   /**
    * :member:`bytes_delivered` is the number of bytes acknowledged.
    */
@@ -143,13 +138,16 @@ typedef void (*ngtcp2_cc_on_pkt_lost)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
  * @functypedef
  *
  * :type:`ngtcp2_cc_congestion_event` is a callback function which is
- * called when congestion event happens (e.g., when packet is lost).
- * |bytes_lost| is the number of bytes lost in this congestion event.
+ * called when congestion event happens (e.g., when packet is lost or
+ * due to ECN).  |ack| contains information after ACK processing.
+ * This callback may be called from non-ACK processing context.  In
+ * that case, the information only taken from |ack| processing has
+ * default values, like 0 or UINT64_MAX;
  */
 typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc,
                                            ngtcp2_conn_stat *cstat,
                                            ngtcp2_tstamp sent_ts,
-                                           uint64_t bytes_lost,
+                                           const ngtcp2_cc_ack *ack,
                                            ngtcp2_tstamp ts);
 
 /**
@@ -254,7 +252,7 @@ typedef struct ngtcp2_cc {
   ngtcp2_cc_on_pkt_lost on_pkt_lost;
   /**
    * :member:`congestion_event` is a callback function which is called
-   * when congestion event happens (.e.g, packet is lost).
+   * when congestion event happens (e.g., packet is lost).
    */
   ngtcp2_cc_congestion_event congestion_event;
   /**
@@ -310,14 +308,16 @@ typedef struct ngtcp2_cc_reno {
   uint64_t pending_add;
 } ngtcp2_cc_reno;
 
-void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log);
+void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log,
+                         ngtcp2_conn_stat *cstat);
 
 void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
                                     const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts);
 
 void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
                                         ngtcp2_tstamp sent_ts,
-                                        uint64_t bytes_lost, ngtcp2_tstamp ts);
+                                        const ngtcp2_cc_ack *ack,
+                                        ngtcp2_tstamp ts);
 
 void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc,
                                                 ngtcp2_conn_stat *cstat,
@@ -340,8 +340,8 @@ typedef struct ngtcp2_cubic_vars {
   /* app_limited_duration is the cumulative duration where a
      connection is under app limited when ACK is received. */
   ngtcp2_duration app_limited_duration;
-  uint64_t pending_bytes_delivered;
-  uint64_t pending_est_bytes_delivered;
+  uint64_t pending_bytes_acked;
+  uint64_t pending_est_bytes_acked;
 } ngtcp2_cubic_vars;
 
 /* ngtcp2_cc_cubic is CUBIC congestion controller. */
@@ -371,14 +371,15 @@ typedef struct ngtcp2_cc_cubic {
 } ngtcp2_cc_cubic;
 
 void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cc, ngtcp2_log *log,
-                          ngtcp2_rst *rst);
+                          ngtcp2_conn_stat *cstat, ngtcp2_rst *rst);
 
 void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
                                     const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts);
 
 void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
                                          ngtcp2_tstamp sent_ts,
-                                         uint64_t bytes_lost, ngtcp2_tstamp ts);
+                                         const ngtcp2_cc_ack *ack,
+                                         ngtcp2_tstamp ts);
 
 void ngtcp2_cc_cubic_cc_on_spurious_congestion(ngtcp2_cc *ccx,
                                                ngtcp2_conn_stat *cstat,
index 13938761dd88096b4e9cd184aab8cea441fc44c1..b1a9840c6e1d20fb425347ed6f41fda779298022 100644 (file)
@@ -1166,8 +1166,8 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
 
   assert(settings->max_window <= NGTCP2_MAX_VARINT);
   assert(settings->max_stream_window <= NGTCP2_MAX_VARINT);
-  assert(settings->max_tx_udp_payload_size);
-  assert(settings->max_tx_udp_payload_size <= NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE);
+  assert(settings->max_tx_udp_payload_size >= NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+  assert(settings->max_tx_udp_payload_size <= NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE);
   assert(settings->initial_pkt_num <= INT32_MAX);
   assert(settings->initial_rtt);
   assert(params->active_connection_id_limit >=
@@ -1203,6 +1203,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
 
   for (i = 0; i < settings->pmtud_probeslen; ++i) {
     assert(settings->pmtud_probes[i] > NGTCP2_MAX_UDP_PAYLOAD_SIZE);
+    assert(settings->pmtud_probes[i] <= NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE);
   }
 
   if (mem == NULL) {
@@ -1335,11 +1336,12 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid,
 
   switch (settings->cc_algo) {
   case NGTCP2_CC_ALGO_RENO:
-    ngtcp2_cc_reno_init(&(*pconn)->reno, &(*pconn)->log);
+    ngtcp2_cc_reno_init(&(*pconn)->reno, &(*pconn)->log, &(*pconn)->cstat);
 
     break;
   case NGTCP2_CC_ALGO_CUBIC:
-    ngtcp2_cc_cubic_init(&(*pconn)->cubic, &(*pconn)->log, &(*pconn)->rst);
+    ngtcp2_cc_cubic_init(&(*pconn)->cubic, &(*pconn)->log, &(*pconn)->cstat,
+                         &(*pconn)->rst);
 
     break;
   case NGTCP2_CC_ALGO_BBR:
@@ -2373,6 +2375,15 @@ conn_crumble_initial_crypto(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc,
   return (ngtcp2_ssize)datacnt;
 }
 
+static size_t conn_dgram_padding(ngtcp2_conn *conn, ngtcp2_ppe *ppe) {
+  if (conn->local.settings.no_tx_udp_payload_size_shaping) {
+    return ngtcp2_ppe_dgram_padding_size(
+      ppe, conn->local.settings.max_tx_udp_payload_size);
+  }
+
+  return ngtcp2_ppe_dgram_padding(ppe);
+}
+
 static size_t conn_min_pktlen(ngtcp2_conn *conn);
 
 /*
@@ -2669,7 +2680,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest,
         (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0,
         require_padding)) {
     lfr.type = NGTCP2_FRAME_PADDING;
-    lfr.padding.len = ngtcp2_ppe_dgram_padding(&ppe);
+    lfr.padding.len = conn_dgram_padding(conn, &ppe);
   } else if (pkt_empty) {
     return 0;
   } else {
@@ -3450,7 +3461,6 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
   uint64_t crypto_offset;
   uint64_t stream_offset;
   ngtcp2_ssize num_reclaimed;
-  int fin;
   uint64_t target_max_data;
   ngtcp2_conn_stat *cstat = &conn->cstat;
   uint64_t delta;
@@ -4145,9 +4155,8 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
     nfrc->fr.stream.datacnt = datacnt;
     ngtcp2_vec_copy(nfrc->fr.stream.data, data, datacnt);
 
-    fin = (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_FIN) &&
-          ndatalen == datalen;
-    nfrc->fr.stream.fin = (uint8_t)fin;
+    nfrc->fr.stream.fin = (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_FIN) &&
+                          ndatalen == datalen;
 
     rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr);
     if (rv != 0) {
@@ -4166,7 +4175,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
     conn->tx.offset += ndatalen;
     vmsg->stream.strm->flags |= NGTCP2_STRM_FLAG_ANY_SENT;
 
-    if (fin) {
+    if (nfrc->fr.stream.fin) {
       ngtcp2_strm_shutdown(vmsg->stream.strm, NGTCP2_STRM_FLAG_SHUT_WR);
     }
 
@@ -4396,7 +4405,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
       (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) {
     lfr.padding.len = ngtcp2_ppe_padding_size(ppe, destlen);
   } else if (require_padding) {
-    lfr.padding.len = ngtcp2_ppe_dgram_padding(ppe);
+    lfr.padding.len = conn_dgram_padding(conn, ppe);
   } else {
     lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen);
     min_padded = 1;
@@ -4595,13 +4604,13 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt(
   if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL) {
     lfr.padding.len = ngtcp2_ppe_dgram_padding_size(&ppe, destlen);
   } else if (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) {
-    lfr.padding.len = ngtcp2_ppe_dgram_padding(&ppe);
+    lfr.padding.len = conn_dgram_padding(conn, &ppe);
   } else {
     switch (fr->type) {
     case NGTCP2_FRAME_PATH_CHALLENGE:
     case NGTCP2_FRAME_PATH_RESPONSE:
       if (!conn->server || destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) {
-        lfr.padding.len = ngtcp2_ppe_dgram_padding(&ppe);
+        lfr.padding.len = conn_dgram_padding(conn, &ppe);
       } else {
         lfr.padding.len = 0;
       }
@@ -12639,7 +12648,7 @@ void ngtcp2_ccerr_set_liberr(ngtcp2_ccerr *ccerr, int liberr,
                reasonlen);
 
     return;
-  };
+  }
 
   ngtcp2_ccerr_set_transport_error(
     ccerr, ngtcp2_err_infer_quic_transport_error_code(liberr), reason,
@@ -13848,8 +13857,17 @@ int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id,
   return 0;
 }
 
+void *ngtcp2_conn_get_stream_user_data(ngtcp2_conn *conn, int64_t stream_id) {
+  ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id);
+
+  if (strm == NULL) {
+    return NULL;
+  }
+
+  return strm->stream_user_data;
+}
+
 void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
-  uint64_t pacing_interval_m;
   ngtcp2_duration wait, d;
 
   conn_update_timestamp(conn, ts);
@@ -13858,20 +13876,9 @@ void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
     return;
   }
 
-  if (conn->cstat.pacing_interval_m) {
-    pacing_interval_m = conn->cstat.pacing_interval_m;
-  } else {
-    /* 1.25 is the under-utilization avoidance factor described in
-       https://datatracker.ietf.org/doc/html/rfc9002#section-7.7 */
-    pacing_interval_m = ((conn->cstat.first_rtt_sample_ts == UINT64_MAX
-                            ? NGTCP2_MILLISECONDS
-                            : conn->cstat.smoothed_rtt)
-                         << 10) *
-                        100 / 125 / conn->cstat.cwnd;
-    pacing_interval_m = ngtcp2_max_uint64(pacing_interval_m, 1);
-  }
-
-  wait = (ngtcp2_duration)((conn->tx.pacing.pktlen * pacing_interval_m) >> 10);
+  wait = (ngtcp2_duration)((conn->tx.pacing.pktlen *
+                            conn->cstat.pacing_interval_m) >>
+                           10);
 
   d = ngtcp2_min_uint64(wait / 2, conn->tx.pacing.compensation);
   wait -= d;
@@ -13909,6 +13916,25 @@ ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned(
   ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
   ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen,
   ngtcp2_write_pkt write_pkt, ngtcp2_tstamp ts) {
+  ngtcp2_ssize nwrite;
+
+  buflen = ngtcp2_min_size(buflen, ngtcp2_conn_get_send_quantum(conn));
+
+  nwrite = ngtcp2_conn_write_aggregate_pkt2_versioned(
+    conn, path, pkt_info_version, pi, buf, buflen, pgsolen, write_pkt, 0, ts);
+  if (nwrite < 0) {
+    return nwrite;
+  }
+
+  ngtcp2_conn_update_pkt_tx_time(conn, ts);
+
+  return nwrite;
+}
+
+ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt2_versioned(
+  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+  ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen,
+  ngtcp2_write_pkt write_pkt, size_t num_pkts, ngtcp2_tstamp ts) {
   size_t max_udp_payloadlen = ngtcp2_conn_get_max_tx_udp_payload_size(conn);
   size_t path_max_udp_payloadlen =
     ngtcp2_conn_get_path_max_tx_udp_payload_size(conn);
@@ -13923,9 +13949,9 @@ ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned(
 
   assert(buflen >= path_max_udp_payloadlen);
 
-  buflen =
-    ngtcp2_min_size(buflen, ngtcp2_max_size(ngtcp2_conn_get_send_quantum(conn),
-                                            path_max_udp_payloadlen));
+  if (num_pkts == 0) {
+    num_pkts = SIZE_MAX;
+  }
 
   for (;;) {
     ecn_state = conn->tx.ecn.state;
@@ -13947,13 +13973,16 @@ ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned(
     wbuf += nwrite;
     buflen -= (size_t)nwrite;
 
+    --num_pkts;
+
     if (first_pkt) {
       assert(!(conn->flags & NGTCP2_CONN_FLAG_AGGREGATE_PKTS));
 
       *pgsolen = (size_t)nwrite;
 
       if ((size_t)nwrite != path_max_udp_payloadlen ||
-          buflen < path_max_udp_payloadlen || ecn_state != conn->tx.ecn.state) {
+          buflen < path_max_udp_payloadlen || ecn_state != conn->tx.ecn.state ||
+          num_pkts == 0) {
         nwrite = wbuf - buf;
         break;
       }
@@ -13977,7 +14006,7 @@ ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned(
     }
 
     if (buflen < path_max_udp_payloadlen || (size_t)nwrite < *pgsolen ||
-        ecn_state != conn->tx.ecn.state) {
+        ecn_state != conn->tx.ecn.state || num_pkts == 0) {
       nwrite = wbuf - buf;
       break;
     }
@@ -13985,8 +14014,6 @@ ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned(
 
   conn->flags &= ~NGTCP2_CONN_FLAG_AGGREGATE_PKTS;
 
-  ngtcp2_conn_update_pkt_tx_time(conn, ts);
-
   return nwrite;
 }
 
index ed358dc48d5caeb1d4bac010901b32c26d4a96ff..b3cdb1d64a6d8427531fdc3d2218215591461c19 100644 (file)
 /* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */
 #define NGTCP2_RETRY_TAGLEN 16
 
-/* NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE is the maximum UDP datagram
-   payload size that this library can write. */
-#define NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE ((1 << 24) - 1)
-
 /* NGTCP2_PKT_LENGTHLEN is the number of bytes that is occupied by
    Length field in Long packet header. */
 #define NGTCP2_PKT_LENGTHLEN 4
@@ -195,7 +191,7 @@ typedef struct ngtcp2_stream {
   uint8_t flags;
   /* CRYPTO frame does not include this field, and must set it to
      0. */
-  uint8_t fin;
+  int fin;
   /* CRYPTO frame does not include this field, and must set it to
      0. */
   int64_t stream_id;
index 353afca4d48fb574eb127251eaf7070731cf0af2..40c25f2a3d1cbcb643c747941560349d1142e97d 100644 (file)
 #include "ngtcp2_macro.h"
 
 #ifndef NDEBUG
-static int ispow2(size_t n) {
-#  if defined(_MSC_VER) && !defined(__clang__) &&                              \
-    (defined(_M_ARM) || (defined(_M_ARM64) && _MSC_VER < 1941))
-  return n && !(n & (n - 1));
-#  elif defined(WIN32)
-  return 1 == __popcnt((unsigned int)n);
-#  else  /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \
-            (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */
-  return 1 == __builtin_popcount((unsigned int)n);
-#  endif /* !((defined(_MSC_VER) && !defined(__clang__) && (defined(_M_ARM) || \
-            (defined(_M_ARM64) && _MSC_VER < 1941))) || defined(WIN32)) */
-}
+/* Power-of-two test; simple portable bit trick. */
+static int ispow2(size_t n) { return n && !(n & (n - 1)); }
 #endif /* !defined(NDEBUG) */
 
 int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size,
index 6f44c8ac8de673932560b89ccdde056a61c1d716..77247108ec469ff7e1cadc5a4c3027fec5d0d3a7 100644 (file)
@@ -59,16 +59,27 @@ void ngtcp2_rst_reset(ngtcp2_rst *rst) {
   rst->lost = 0;
 }
 
+void ngtcp2_rst_reset_rate_sample(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) {
+  ngtcp2_rs *rs = &rst->rs;
+
+  rs->interval = UINT64_MAX;
+  rs->prior_ts = UINT64_MAX;
+
+  cstat->delivery_rate_sec = 0;
+}
+
 void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
                             const ngtcp2_conn_stat *cstat) {
-  if (cstat->bytes_in_flight == 0) {
+  /* cstat->bytes_in_flight includes ent->pktlen.  If they are the
+     same, there is no in-flight packets. */
+  if (cstat->bytes_in_flight == ent->pktlen) {
     rst->first_sent_ts = rst->delivered_ts = ent->ts;
   }
   ent->rst.first_sent_ts = rst->first_sent_ts;
   ent->rst.delivered_ts = rst->delivered_ts;
   ent->rst.delivered = rst->delivered;
   ent->rst.is_app_limited = rst->app_limited != 0;
-  ent->rst.tx_in_flight = cstat->bytes_in_flight + ent->pktlen;
+  ent->rst.tx_in_flight = cstat->bytes_in_flight;
   ent->rst.lost = rst->lost;
   ent->rst.end_seq = ++rst->last_seq;
 }
@@ -89,7 +100,6 @@ void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) {
   rs->delivered = rst->delivered - rs->prior_delivered;
 
   if (rs->interval < cstat->min_rtt) {
-    rs->interval = UINT64_MAX;
     return;
   }
 
index 1bb624de79b53f885fee71ee5a78bdd15b58e387..b0bc2d06c9ca50ea1fb981cac898180c704dc4df 100644 (file)
@@ -79,6 +79,8 @@ void ngtcp2_rst_init(ngtcp2_rst *rst);
 
 void ngtcp2_rst_reset(ngtcp2_rst *rst);
 
+void ngtcp2_rst_reset_rate_sample(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat);
+
 void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
                             const ngtcp2_conn_stat *cstat);
 void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat);
index f51c0f14ef97337bd38540a50dc48fc0f03ad2f5..5908fcc5c5c39e268c6bf254eafe443ac85e92f7 100644 (file)
@@ -128,13 +128,12 @@ void ngtcp2_rtb_free(ngtcp2_rtb *rtb) {
 
 static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
                        ngtcp2_conn_stat *cstat) {
-  ngtcp2_rst_on_pkt_sent(rtb->rst, ent, cstat);
-
   assert(rtb->cc_pkt_num <= ent->hd.pkt_num);
 
   cstat->bytes_in_flight += ent->pktlen;
   rtb->cc_bytes_in_flight += ent->pktlen;
 
+  ngtcp2_rst_on_pkt_sent(rtb->rst, ent, cstat);
   ngtcp2_rst_update_app_limited(rtb->rst, cstat);
 
   if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
@@ -738,8 +737,7 @@ static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent,
 static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
                             ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
                             const ngtcp2_ack *fr, size_t ecn_acked,
-                            ngtcp2_tstamp largest_pkt_sent_ts,
-                            ngtcp2_tstamp ts) {
+                            const ngtcp2_cc_ack *cc_ack, ngtcp2_tstamp ts) {
   if (conn->tx.ecn.state == NGTCP2_ECN_STATE_FAILED) {
     return;
   }
@@ -766,9 +764,9 @@ static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
   }
 
   if (fr->type == NGTCP2_FRAME_ACK_ECN) {
-    if (cc->congestion_event && largest_pkt_sent_ts != UINT64_MAX &&
+    if (cc->congestion_event && cc_ack->largest_pkt_sent_ts != UINT64_MAX &&
         fr->ecn.ce > pktns->acktr.ecn.ack.ce) {
-      cc->congestion_event(cc, cstat, largest_pkt_sent_ts, 0, ts);
+      cc->congestion_event(cc, cstat, cc_ack->largest_pkt_sent_ts, cc_ack, ts);
     }
 
     pktns->acktr.ecn.ack.ect0 = fr->ecn.ect0;
@@ -777,7 +775,7 @@ static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
   }
 }
 
-static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
+static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_cc_ack *cc_ack,
                                ngtcp2_conn *conn, ngtcp2_pktns *pktns,
                                ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts);
 
@@ -791,7 +789,6 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
   int rv;
   ngtcp2_ksl_it it;
   size_t num_acked = 0;
-  ngtcp2_tstamp largest_pkt_sent_ts = UINT64_MAX;
   int64_t pkt_num;
   ngtcp2_cc *cc = rtb->cc;
   ngtcp2_rtb_entry *acked_ent = NULL;
@@ -799,7 +796,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 = {
-    .prior_bytes_in_flight = cstat->bytes_in_flight,
+    .largest_pkt_sent_ts = UINT64_MAX,
     .rtt = UINT64_MAX,
   };
   size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_ignore_pkts;
@@ -819,12 +816,17 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
     verify_ecn = 1;
   }
 
+  ngtcp2_rst_reset_rate_sample(rtb->rst, cstat);
+
   /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */
   it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack);
   if (ngtcp2_ksl_it_end(&it)) {
     if (conn && verify_ecn) {
-      conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked,
-                      largest_pkt_sent_ts, ts);
+      conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked, &cc_ack, ts);
+    }
+
+    if (cc->on_ack_recv) {
+      cc->on_ack_recv(cc, cstat, &cc_ack, ts);
     }
 
     return 0;
@@ -849,7 +851,7 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
     }
 
     if (largest_ack == pkt_num) {
-      largest_pkt_sent_ts = ent->ts;
+      cc_ack.largest_pkt_sent_ts = ent->ts;
     }
 
     if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
@@ -889,9 +891,9 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
     }
   }
 
-  if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) {
-    cc_ack.rtt =
-      ngtcp2_max_uint64(pkt_ts - largest_pkt_sent_ts, NGTCP2_NANOSECONDS);
+  if (cc_ack.largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) {
+    cc_ack.rtt = ngtcp2_max_uint64(pkt_ts - cc_ack.largest_pkt_sent_ts,
+                                   NGTCP2_NANOSECONDS);
 
     rv = ngtcp2_conn_update_rtt(conn, cc_ack.rtt, fr->ack_delay_unscaled, ts);
     if (rv == 0 && cc->new_rtt_sample &&
@@ -927,11 +929,6 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
       ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
                                     rtb->frc_objalloc, rtb->mem);
     }
-
-    if (verify_ecn) {
-      conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked,
-                      largest_pkt_sent_ts, ts);
-    }
   } else {
     /* For unit tests */
     for (ent = acked_ent; ent; ent = acked_ent) {
@@ -952,15 +949,18 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr,
     ngtcp2_rst_on_ack_recv(rtb->rst, cstat);
 
     if (conn) {
-      rv = rtb_detect_lost_pkt(rtb, &cc_ack.bytes_lost, conn, pktns, cstat, ts);
+      rv = rtb_detect_lost_pkt(rtb, &cc_ack, conn, pktns, cstat, ts);
       if (rv != 0) {
         return rv;
       }
     }
   }
 
-  cc_ack.largest_pkt_sent_ts = largest_pkt_sent_ts;
-  if (num_acked && cc->on_ack_recv) {
+  if (conn && verify_ecn) {
+    conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked, &cc_ack, ts);
+  }
+
+  if (cc->on_ack_recv) {
     cc->on_ack_recv(cc, cstat, &cc_ack, ts);
   }
 
@@ -1026,7 +1026,11 @@ static int conn_all_ecn_pkt_lost(ngtcp2_conn *conn) {
          pktns->tx.ecn.validation_pkt_sent == pktns->tx.ecn.validation_pkt_lost;
 }
 
-static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
+/*
+ * This function assigns the number of bytes lost to
+ * |cc_ack|->bytes_lost if any.
+ */
+static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_cc_ack *cc_ack,
                                ngtcp2_conn *conn, ngtcp2_pktns *pktns,
                                ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) {
   ngtcp2_rtb_entry *ent;
@@ -1144,8 +1148,10 @@ static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost,
         break;
       }
 
+      cc_ack->bytes_lost = bytes_lost;
+
       if (cc->congestion_event) {
-        cc->congestion_event(cc, cstat, latest_ts, bytes_lost, ts);
+        cc->congestion_event(cc, cstat, latest_ts, cc_ack, ts);
       }
 
       loss_window = latest_ts - oldest_ts;
@@ -1181,18 +1187,18 @@ 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) {
-    *ppkt_lost = bytes_lost;
-  }
-
   return 0;
 }
 
 int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
                                ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat,
                                ngtcp2_tstamp ts) {
-  return rtb_detect_lost_pkt(rtb, /* ppkt_lost = */ NULL, conn, pktns, cstat,
-                             ts);
+  ngtcp2_cc_ack cc_ack = {
+    .largest_pkt_sent_ts = UINT64_MAX,
+    .rtt = UINT64_MAX,
+  };
+
+  return rtb_detect_lost_pkt(rtb, &cc_ack, conn, pktns, cstat, ts);
 }
 
 void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) {
index baecc277ee19bbbbe689136d28854cfe916a0386..5634c9c751e0522da8e9b22a66b12d3c302b411d 100644 (file)
@@ -306,15 +306,29 @@ typedef struct ngtcp2_mem {
  * @macro
  *
  * :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE` is the default maximum UDP
- * datagram payload size that the local endpoint transmits.
+ * datagram payload size that the local endpoint transmits without
+ * Path MTU Discovery (PMTUD) or the custom settings (see
+ * :member:`ngtcp2_settings.max_tx_udp_payload_size` and
+ * :member:`ngtcp2_settings.no_tx_udp_payload_size_shaping`).
  */
 #define NGTCP2_MAX_UDP_PAYLOAD_SIZE 1200
 
 /**
  * @macro
  *
- * :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` is the maximum UDP
+ * :macro:`NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE` is the maximum UDP datagram
+ * payload size that this library can output.
+ */
+#define NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE 65527
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE` was the maximum UDP
  * datagram payload size that Path MTU Discovery can discover.
+ *
+ * Deprecated since v1.17.0.  Path MTU Discovery is not capped to this
+ * value anymore.
  */
 #define NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE 1452
 
@@ -1786,7 +1800,9 @@ typedef struct ngtcp2_settings {
   ngtcp2_printf log_printf;
   /**
    * :member:`max_tx_udp_payload_size` is the maximum size of UDP
-   * datagram payload that the local endpoint transmits.
+   * datagram payload that the local endpoint transmits.  This must be
+   * larger than or equal to :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`, and
+   * less then or equal to :macro:`NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE`.
    */
   size_t max_tx_udp_payload_size;
   /**
@@ -1951,12 +1967,13 @@ typedef struct ngtcp2_settings {
   /**
    * :member:`pmtud_probes` is the array of UDP datagram payload size
    * to probe during Path MTU Discovery.  The discovery is done in the
-   * order appeared in this array.  The size must be strictly larger
-   * than 1200, otherwise the behavior is undefined.  The maximum
-   * value in this array should be set to
-   * :member:`max_tx_udp_payload_size`.  If this field is not set, the
-   * predefined PMTUD probes are made.  This field has been available
-   * since v1.4.0.
+   * order appeared in this array.  The payload size must be strictly
+   * larger than :macro:`NGTCP2_MAX_UDP_PAYLOAD_SIZE`, and less than
+   * or equal to :macro:`NGTCP2_MAX_TX_UDP_PAYLOAD_SIZE`.  Otherwise
+   * the behavior is undefined.  The maximum value in this array
+   * should be set to :member:`max_tx_udp_payload_size`.  If this
+   * field is not set, the predefined PMTUD probes are made.  This
+   * field has been available since v1.4.0.
    */
   const uint16_t *pmtud_probes;
   /**
@@ -4155,9 +4172,7 @@ NGTCP2_EXTERN void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn,
  * `ngtcp2_conn_get_expiry` returns the next expiry time.  It returns
  * ``UINT64_MAX`` if there is no next expiry.
  *
- * Call `ngtcp2_conn_handle_expiry` and then
- * `ngtcp2_conn_writev_stream` (or `ngtcp2_conn_writev_datagram`) when
- * the expiry time has passed.
+ * Call `ngtcp2_conn_handle_expiry` when the expiry time has passed.
  */
 NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
 
@@ -4165,6 +4180,20 @@ NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn);
  * @function
  *
  * `ngtcp2_conn_handle_expiry` handles expired timer.
+ *
+ * If it returns :macro:`NGTCP2_ERR_IDLE_CLOSE`, it means that an idle
+ * timer has fired for this particular connection.  In this case, drop
+ * the connection without calling
+ * `ngtcp2_conn_write_connection_close`.  If it returns any of the
+ * other negative error codes, close the connection by sending the
+ * terminal packet produced by `ngtcp2_conn_write_connection_close`.
+ * Otherwise, schedule `ngtcp2_conn_writev_stream` call.  An
+ * application may call any number of additional
+ * `ngtcp2_conn_read_pkt` and `ngtcp2_conn_handle_expiry` before
+ * calling `ngtcp2_conn_writev_stream`.  After calling
+ * `ngtcp2_conn_writev_stream`, new expiry is set.  The application
+ * should call `ngtcp2_conn_get_expiry` to get a new deadline and set
+ * the timer.
  */
 NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn,
                                             ngtcp2_tstamp ts);
@@ -5629,6 +5658,26 @@ NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn,
                                                    int64_t stream_id,
                                                    void *stream_user_data);
 
+/**
+ * @function
+ *
+ * `ngtcp2_conn_get_stream_user_data` returns stream_user_data
+ * associated to the stream identified by |stream_id|.  If the stream
+ * is not found, or no stream data is associated to the stream, this
+ * function returns NULL.
+ *
+ * The stream_user_data can be associated to the stream by one of the
+ * following functions:
+ *
+ * - `ngtcp2_conn_open_bidi_stream`
+ * - `ngtcp2_conn_open_uni_stream`
+ * - `ngtcp2_conn_set_stream_user_data`
+ *
+ * This function has been available since v1.17.0.
+ */
+NGTCP2_EXTERN void *ngtcp2_conn_get_stream_user_data(ngtcp2_conn *conn,
+                                                     int64_t stream_id);
+
 /**
  * @function
  *
@@ -5714,14 +5763,23 @@ typedef ngtcp2_ssize (*ngtcp2_write_pkt)(ngtcp2_conn *conn, ngtcp2_path *path,
  * first packet is `ngtcp2_conn_get_path_max_tx_udp_payload_size(conn)
  * <ngtcp2_conn_get_path_max_tx_udp_payload_size>` bytes long.  The
  * application can adjust the length of the buffer to limit the number
- * of packets to aggregate.  If this function returns positive
- * integer, all packets share the same :type:`ngtcp2_path` and
- * :type:`ngtcp2_pkt_info` values, and they are assigned to the
- * objects pointed by |path| and |pi| respectively.  The length of all
- * packets other than the last packet is assigned to |*pgsolen|.  The
- * length of last packet is equal to or less than |*pgsolen|.
- * |write_pkt| must write a single packet.  After all packets are
- * written, this function calls `ngtcp2_conn_update_pkt_tx_time`.
+ * of packets to aggregate (or use `ngtcp2_conn_write_aggregate_pkt2`
+ * to control the number of packets to write directly).  If this
+ * function returns positive integer, all packets share the same
+ * :type:`ngtcp2_path` and :type:`ngtcp2_pkt_info` values, and they
+ * are assigned to the objects pointed by |path| and |pi|
+ * respectively.  The length of all packets other than the last packet
+ * is assigned to |*pgsolen|.  The length of last packet is equal to
+ * or less than |*pgsolen|.  |write_pkt| must write a single packet.
+ * After all packets are written, this function calls
+ * `ngtcp2_conn_update_pkt_tx_time`.
+ *
+ * This function is equivalent to call
+ * `ngtcp2_conn_write_aggregate_pkt2` with |buflen| = min(|buflen|,
+ * `ngtcp2_conn_get_send_quantum(conn)
+ * <ngtcp2_conn_get_send_quantum>`) and |num_pkts| = 0 followed by
+ * `ngtcp2_conn_update_pkt_tx_time(conn)
+ * <ngtcp2_conn_update_pkt_tx_time>`.
  *
  * This function returns the number of bytes written to the buffer, or
  * a negative error code returned by |write_pkt|.
@@ -5733,6 +5791,29 @@ NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned(
   ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen,
   ngtcp2_write_pkt write_pkt, ngtcp2_tstamp ts);
 
+/**
+ * @function
+ *
+ * `ngtcp2_conn_write_aggregate_pkt2` behaves like
+ * `ngtcp2_conn_write_aggregate_pkt`, but it accepts |num_pkts| to
+ * specify the maximum number of packets to write.  If |num_pkts| is
+ * 0, this function writes packets as much as possible.  The actual
+ * number of packets to write is determined by the connection state
+ * (e.g., the congestion controller, data available to send) and the
+ * length of packet produced.  It also does not clamp |buflen|, and
+ * does not call `ngtcp2_conn_update_pkt_tx_time`.
+ *
+ * This function offers more flexibility and optimization chances to
+ * an application.  It can experiment different GSO buffer size
+ * strategy and number of GSO writes per event loop.
+ *
+ * This function has been available since v1.17.0.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt2_versioned(
+  ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version,
+  ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen,
+  ngtcp2_write_pkt write_pkt, size_t num_pkts, ngtcp2_tstamp ts);
+
 /**
  * @function
  *
@@ -6140,6 +6221,17 @@ NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions,
     (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (BUF), (BUFLEN), (PGSOLEN), \
     (WRITE_PKT), (TS))
 
+/*
+ * `ngtcp2_conn_write_aggregate_pkt2` is a wrapper around
+ * `ngtcp2_conn_write_aggregate_pkt2_versioned` to set the correct
+ * struct version.
+ */
+#define ngtcp2_conn_write_aggregate_pkt2(CONN, PATH, PI, BUF, BUFLEN, PGSOLEN, \
+                                         WRITE_PKT, NUM_PKTS, TS)              \
+  ngtcp2_conn_write_aggregate_pkt2_versioned(                                  \
+    (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (BUF), (BUFLEN), (PGSOLEN), \
+    (WRITE_PKT), (NUM_PKTS), (TS))
+
 /*
  * `ngtcp2_settings_default` is a wrapper around
  * `ngtcp2_settings_default_versioned` to set the correct struct
index 9fabd7b179781f10e5d25c0c62540bb4949e734e..3ff1ebac9705ca103b2aa037e36363eeed59780d 100644 (file)
@@ -36,7 +36,7 @@
  *
  * Version number of the ngtcp2 library release.
  */
-#define NGTCP2_VERSION "1.16.0"
+#define NGTCP2_VERSION "1.17.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 0x011000
+#define NGTCP2_VERSION_NUM 0x011100
 
 #endif /* !defined(NGTCP2_VERSION_H) */