contrib/libngtcp2/ngtcp2/lib/ngtcp2_window_filter.h \
contrib/libngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c \
contrib/libngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_callbacks.c \
+ contrib/libngtcp2/ngtcp2/lib/ngtcp2_callbacks.h \
contrib/libngtcp2/ngtcp2/ngtcp2.h \
contrib/libngtcp2/ngtcp2/ngtcp2_crypto.h \
contrib/libngtcp2/ngtcp2/ngtcp2_crypto_gnutls.h \
static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr);
-static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr);
+static void bbr_reset_shortterm_model(ngtcp2_cc_bbr *bbr);
static void bbr_init_round_counting(ngtcp2_cc_bbr *bbr);
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts);
-static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts);
+static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat);
ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack);
-static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_ack *ack);
+static void bbr_adapt_longterm_model(ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack);
static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
bbr->full_bw_reached = 0;
bbr_reset_congestion_signals(bbr);
- bbr_reset_lower_bounds(bbr);
+ bbr_reset_shortterm_model(bbr);
bbr_init_round_counting(bbr);
bbr_reset_full_bw(bbr);
bbr_init_pacing_rate(bbr, cstat);
bbr->inflight_latest = 0;
}
-static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr) {
+static void bbr_reset_shortterm_model(ngtcp2_cc_bbr *bbr) {
bbr->bw_shortterm = UINT64_MAX;
bbr->inflight_shortterm = UINT64_MAX;
}
ngtcp2_log_info(bbr->cc.log, NGTCP2_LOG_EVENT_CCA,
"bbr start ProbeBW_REFILL");
- bbr_reset_lower_bounds(bbr);
+ bbr_reset_shortterm_model(bbr);
bbr->bw_probe_up_rounds = 0;
bbr->bw_probe_up_acks = 0;
return;
}
- bbr_adapt_upper_bounds(bbr, cstat, ack);
+ bbr_adapt_longterm_model(bbr, cstat, ack);
if (!bbr_is_in_probe_bw_state(bbr)) {
return;
return;
}
- if (bbr_is_time_to_cruise(bbr, cstat, ts)) {
+ if (bbr_is_time_to_cruise(bbr, cstat)) {
bbr_start_probe_bw_cruise(bbr);
}
}
}
-static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts) {
- (void)ts;
-
- if (cstat->bytes_in_flight > bbr_inflight_with_headroom(bbr, cstat)) {
- return 0;
- }
-
- if (cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, 100)) {
- return 1;
- }
+static int bbr_is_time_to_cruise(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
+ uint64_t inflight = ngtcp2_min_uint64(bbr_inflight_with_headroom(bbr, cstat),
+ bbr_inflight(bbr, cstat, 100));
- return 0;
+ return cstat->bytes_in_flight <= inflight;
}
static int bbr_is_time_to_go_down(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
if (bbr->rst->is_cwnd_limited && cstat->cwnd >= bbr->inflight_longterm) {
bbr_reset_full_bw(bbr);
bbr->full_bw = cstat->delivery_rate_sec;
- } else if (bbr->full_bw_now) {
- return 1;
+
+ return 0;
}
- return 0;
+ return bbr->full_bw_now;
}
static int bbr_has_elapsed_in_phase(ngtcp2_cc_bbr *bbr,
}
}
-static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_ack *ack) {
+static void bbr_adapt_longterm_model(ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
if (bbr->ack_phase == NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_STARTING &&
bbr->round_start) {
bbr->ack_phase = NGTCP2_BBR_ACK_PHASE_ACKS_PROBE_FEEDBACK;
bbr->rand(&rand, 1, &bbr->rand_ctx);
- bbr->rounds_since_bw_probe = (uint64_t)(rand * 2 / 256);
+ bbr->rounds_since_bw_probe = (uint64_t)(rand / 128);
bbr->rand(&rand, 1, &bbr->rand_ctx);
}
rs->tx_in_flight = pkt->tx_in_flight;
- /* bbr->rst->lost is not incremented for pkt yet */
- assert(bbr->rst->lost + pkt->pktlen >= pkt->lost);
- rs->lost = bbr->rst->lost + pkt->pktlen - pkt->lost;
+ assert(bbr->rst->lost >= pkt->lost);
+ rs->lost = bbr->rst->lost - pkt->lost;
rs->is_app_limited = pkt->is_app_limited;
if (bbr_is_inflight_too_high(bbr)) {
}
static void bbr_exit_probe_rtt(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts) {
- bbr_reset_lower_bounds(bbr);
+ bbr_reset_shortterm_model(bbr);
if (bbr->full_bw_reached) {
bbr_start_probe_bw_down(bbr, ts);
static void bbr_bound_cwnd_for_probe_rtt(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat) {
- uint64_t probe_rtt_cwnd;
-
if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT) {
- probe_rtt_cwnd = bbr_probe_rtt_cwnd(bbr, cstat);
-
- cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, probe_rtt_cwnd);
+ cstat->cwnd =
+ ngtcp2_min_uint64(cstat->cwnd, bbr_probe_rtt_cwnd(bbr, cstat));
}
}
ngtcp2_conn_stat *cstat, ngtcp2_rst *rst,
ngtcp2_tstamp initial_ts, ngtcp2_rand rand,
const ngtcp2_rand_ctx *rand_ctx) {
- memset(bbr, 0, sizeof(*bbr));
-
- bbr->cc.log = log;
- bbr->cc.on_pkt_lost = bbr_cc_on_pkt_lost;
- bbr->cc.congestion_event = bbr_cc_congestion_event;
- bbr->cc.on_spurious_congestion = bbr_cc_on_spurious_congestion;
- bbr->cc.on_persistent_congestion = bbr_cc_on_persistent_congestion;
- bbr->cc.on_ack_recv = bbr_cc_on_ack_recv;
- bbr->cc.on_pkt_sent = bbr_cc_on_pkt_sent;
- bbr->cc.reset = bbr_cc_reset;
-
- bbr->rst = rst;
- bbr->rand = rand;
- bbr->rand_ctx = *rand_ctx;
- bbr->initial_cwnd = cstat->cwnd;
+ *bbr = (ngtcp2_cc_bbr){
+ .cc =
+ {
+ .log = log,
+ .on_pkt_lost = bbr_cc_on_pkt_lost,
+ .congestion_event = bbr_cc_congestion_event,
+ .on_spurious_congestion = bbr_cc_on_spurious_congestion,
+ .on_persistent_congestion = bbr_cc_on_persistent_congestion,
+ .on_ack_recv = bbr_cc_on_ack_recv,
+ .on_pkt_sent = bbr_cc_on_pkt_sent,
+ .reset = bbr_cc_reset,
+ },
+ .rst = rst,
+ .rand = rand,
+ .rand_ctx = *rand_ctx,
+ .initial_cwnd = cstat->cwnd,
+ };
bbr_on_init(bbr, cstat, initial_ts);
}
* written to the underlying buffer. In other words, it returns
* buf->end - buf->last.
*/
-#define ngtcp2_buf_left(BUF) (size_t)((BUF)->end - (BUF)->last)
+static inline size_t ngtcp2_buf_left(const ngtcp2_buf *buf) {
+ return (size_t)(buf->end - buf->last);
+}
/*
* ngtcp2_buf_len returns the number of bytes left to read. In other
* words, it returns buf->last - buf->pos.
*/
-#define ngtcp2_buf_len(BUF) (size_t)((BUF)->last - (BUF)->pos)
+static inline size_t ngtcp2_buf_len(const ngtcp2_buf *buf) {
+ return (size_t)(buf->last - buf->pos);
+}
/*
* ngtcp2_buf_cap returns the capacity of the buffer. In other words,
--- /dev/null
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2025 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "ngtcp2_callbacks.h"
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_unreachable.h"
+
+static void callbacks_copy(ngtcp2_callbacks *dest, const ngtcp2_callbacks *src,
+ int callbacks_version) {
+ assert(callbacks_version != NGTCP2_CALLBACKS_VERSION);
+
+ memcpy(dest, src, ngtcp2_callbackslen_version(callbacks_version));
+}
+
+const ngtcp2_callbacks *ngtcp2_callbacks_convert_to_latest(
+ ngtcp2_callbacks *dest, int callbacks_version, const ngtcp2_callbacks *src) {
+ if (callbacks_version == NGTCP2_CALLBACKS_VERSION) {
+ return src;
+ }
+
+ memset(dest, 0, sizeof(*dest));
+
+ callbacks_copy(dest, src, callbacks_version);
+
+ return dest;
+}
+
+void ngtcp2_callbacks_convert_to_old(int callbacks_version,
+ ngtcp2_callbacks *dest,
+ const ngtcp2_callbacks *src) {
+ assert(callbacks_version != NGTCP2_CALLBACKS_VERSION);
+
+ callbacks_copy(dest, src, callbacks_version);
+}
+
+size_t ngtcp2_callbackslen_version(int callbacks_version) {
+ ngtcp2_callbacks callbacks;
+
+ switch (callbacks_version) {
+ case NGTCP2_CALLBACKS_VERSION:
+ return sizeof(callbacks);
+ case NGTCP2_CALLBACKS_V1:
+ return offsetof(ngtcp2_callbacks, tls_early_data_rejected) +
+ sizeof(callbacks.tls_early_data_rejected);
+ default:
+ ngtcp2_unreachable();
+ }
+}
--- /dev/null
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2025 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CALLBACKS_H
+#define NGTCP2_CALLBACKS_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* defined(HAVE_CONFIG_H) */
+
+#include <ngtcp2/ngtcp2.h>
+
+/*
+ * ngtcp2_callbacks_convert_to_latest converts |src| of version
+ * |callbacks_version| to the latest version NGTCP2_CALLBACKS_VERSION.
+ *
+ * |dest| must point to the latest version. |src| may be the older
+ * version, and if so, it may have fewer fields. Accessing those
+ * fields causes undefined behavior.
+ *
+ * If |callbacks_version| == NGTCP2_CALLBACKS_VERSION, no conversion
+ * is made, and |src| is returned. Otherwise, first |dest| is
+ * zero-initialized, and then all valid fields in |src| are copied
+ * into |dest|. Finally, |dest| is returned.
+ */
+const ngtcp2_callbacks *ngtcp2_callbacks_convert_to_latest(
+ ngtcp2_callbacks *dest, int callbacks_version, const ngtcp2_callbacks *src);
+
+/*
+ * ngtcp2_callbacks_convert_to_old converts |src| of the latest
+ * version to |dest| of version |callbacks_version|.
+ *
+ * |callbacks_version| must not be the latest version
+ * NGTCP2_CALLBACKS_VERSION.
+ *
+ * |dest| points to the older version, and it may have fewer fields.
+ * Accessing those fields causes undefined behavior.
+ *
+ * This function copies all valid fields in version
+ * |callbacks_version| from |src| to |dest|.
+ */
+void ngtcp2_callbacks_convert_to_old(int callbacks_version,
+ ngtcp2_callbacks *dest,
+ const ngtcp2_callbacks *src);
+
+/*
+ * ngtcp2_callbackslen_version returns the effective length of
+ * ngtcp2_callbacks at the version |callbacks_version|.
+ */
+size_t ngtcp2_callbackslen_version(int callbacks_version);
+
+#endif /* !defined(NGTCP2_CALLBACKS_H) */
static void reno_cc_reset(ngtcp2_cc_reno *reno) { reno->pending_add = 0; }
void ngtcp2_cc_reno_init(ngtcp2_cc_reno *reno, ngtcp2_log *log) {
- memset(reno, 0, sizeof(*reno));
-
- reno->cc.log = log;
- reno->cc.on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked;
- reno->cc.congestion_event = ngtcp2_cc_reno_cc_congestion_event;
- reno->cc.on_persistent_congestion =
- ngtcp2_cc_reno_cc_on_persistent_congestion;
- reno->cc.reset = ngtcp2_cc_reno_cc_reset;
+ *reno = (ngtcp2_cc_reno){
+ .cc =
+ {
+ .log = log,
+ .on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked,
+ .congestion_event = ngtcp2_cc_reno_cc_congestion_event,
+ .on_persistent_congestion = ngtcp2_cc_reno_cc_on_persistent_congestion,
+ .reset = ngtcp2_cc_reno_cc_reset,
+ },
+ };
reno_cc_reset(reno);
}
static void cubic_vars_reset(ngtcp2_cubic_vars *v) {
v->cwnd_prior = 0;
v->w_max = 0;
- v->k = 0;
+ v->k_m = 0;
v->epoch_start = UINT64_MAX;
v->w_est = 0;
void ngtcp2_cc_cubic_init(ngtcp2_cc_cubic *cubic, ngtcp2_log *log,
ngtcp2_rst *rst) {
- memset(cubic, 0, sizeof(*cubic));
-
- cubic->cc.log = log;
- cubic->cc.on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv;
- cubic->cc.congestion_event = ngtcp2_cc_cubic_cc_congestion_event;
- cubic->cc.on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion;
- cubic->cc.on_persistent_congestion =
- ngtcp2_cc_cubic_cc_on_persistent_congestion;
- cubic->cc.reset = ngtcp2_cc_cubic_cc_reset;
-
- cubic->rst = rst;
+ *cubic = (ngtcp2_cc_cubic){
+ .cc =
+ {
+ .log = log,
+ .on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv,
+ .congestion_event = ngtcp2_cc_cubic_cc_congestion_event,
+ .on_spurious_congestion = ngtcp2_cc_cubic_cc_on_spurious_congestion,
+ .on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion,
+ .reset = ngtcp2_cc_cubic_cc_reset,
+ },
+ .rst = rst,
+ };
cubic_cc_reset(cubic);
}
#define NGTCP2_HS_CSS_GROWTH_DIVISOR 4
#define NGTCP2_HS_CSS_ROUNDS 5
-static int64_t cubic_cc_compute_w_cubic(ngtcp2_cc_cubic *cubic,
- const ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts) {
+static uint64_t cubic_cc_compute_w_cubic(ngtcp2_cc_cubic *cubic,
+ const ngtcp2_conn_stat *cstat,
+ ngtcp2_tstamp ts) {
ngtcp2_duration t = ts - cubic->current.epoch_start;
- int64_t tx = (int64_t)((t << 10) / NGTCP2_SECONDS);
- int64_t time_delta = ngtcp2_min_int64(tx - cubic->current.k, 3600 << 10);
- int64_t delta = ((((time_delta * time_delta) >> 10) * time_delta) >> 20) *
- (int64_t)cstat->max_tx_udp_payload_size * 4 / 10;
+ uint64_t tx_m = (t << 10) / NGTCP2_SECONDS;
+ int neg = tx_m < cubic->current.k_m;
+ uint64_t time_delta_m;
+ uint64_t delta;
+
+ /* Avoid signed bit-shift */
+ if (neg) {
+ time_delta_m = cubic->current.k_m - tx_m;
+ } else {
+ time_delta_m = tx_m - cubic->current.k_m;
+ }
+
+ time_delta_m = ngtcp2_min_uint64(time_delta_m, 3600 << 10);
+
+ delta = ((((time_delta_m * time_delta_m) >> 10) * time_delta_m) >> 10) *
+ cstat->max_tx_udp_payload_size * 4 / 10;
+ delta >>= 10;
+
+ if (neg) {
+ if (cubic->current.w_max < delta) {
+ /* Negative w_cubic is not interesting. */
+ return 0;
+ }
- return (int64_t)cubic->current.w_max + delta;
+ return cubic->current.w_max - delta;
+ }
+
+ return cubic->current.w_max + delta;
}
void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat,
const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts) {
ngtcp2_cc_cubic *cubic = ngtcp2_struct_of(cc, ngtcp2_cc_cubic, cc);
- int64_t w_cubic, w_cubic_next;
+ uint64_t w_cubic, w_cubic_next;
uint64_t target, m;
ngtcp2_duration rtt_thresh;
int round_start;
cubic, cstat,
ts - cubic->current.app_limited_duration + cstat->smoothed_rtt);
- if (w_cubic_next < (int64_t)cstat->cwnd) {
+ if (w_cubic_next < cstat->cwnd) {
target = cstat->cwnd;
- } else if (2 * w_cubic_next > 3 * (int64_t)cstat->cwnd) {
+ } else if (2 * w_cubic_next > 3 * cstat->cwnd) {
target = cstat->cwnd * 3 / 2;
} else {
- assert(w_cubic_next >= 0);
- target = (uint64_t)w_cubic_next;
+ target = w_cubic_next;
}
m = ack->bytes_delivered * cstat->max_tx_udp_payload_size +
cubic->current.w_est += m / cstat->cwnd;
}
- if ((int64_t)cubic->current.w_est > w_cubic) {
+ 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;
- cstat->cwnd += m / cstat->cwnd;
cubic->current.pending_bytes_delivered = m % cstat->cwnd;
+ cstat->cwnd += m / cstat->cwnd;
}
ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
"%" PRIu64 " bytes acked, cubic-ca cwnd=%" PRIu64
- " k=%" PRIi64 " target=%" PRIu64 " w_est=%" PRIu64,
- ack->bytes_delivered, cstat->cwnd, cubic->current.k, target,
+ " k_m=%" PRIu64 " target=%" PRIu64 " w_est=%" PRIu64,
+ ack->bytes_delivered, cstat->cwnd, cubic->current.k_m, target,
cubic->current.w_est);
}
cubic->current.w_max = cstat->cwnd;
}
+ cubic->current.w_max =
+ ngtcp2_max_uint64(cubic->current.w_max, 2 * cstat->max_tx_udp_payload_size);
+
cstat->ssthresh = cstat->cwnd * 7 / 10;
if (cubic->rst->rs.delivered * 2 < cstat->cwnd) {
cubic->current.w_est = cstat->cwnd;
- if (cstat->cwnd < cubic->current.w_max) {
- cwnd_delta = cubic->current.w_max - cstat->cwnd;
- } else {
- cwnd_delta = cstat->cwnd - cubic->current.w_max;
- }
+ assert(cubic->current.w_max >= cstat->cwnd);
- cubic->current.k = (int64_t)ngtcp2_cbrt((cwnd_delta << 30) * 10 / 4 /
- cstat->max_tx_udp_payload_size);
- if (cstat->cwnd >= cubic->current.w_max) {
- cubic->current.k = -cubic->current.k;
- }
+ cwnd_delta = cubic->current.w_max - cstat->cwnd;
+
+ cubic->current.k_m =
+ ngtcp2_cbrt((cwnd_delta << 30) * 10 / 4 / cstat->max_tx_udp_payload_size);
ngtcp2_log_info(cubic->cc.log, NGTCP2_LOG_EVENT_CCA,
"reduce cwnd because of packet loss cwnd=%" PRIu64,
typedef struct ngtcp2_cubic_vars {
uint64_t cwnd_prior;
uint64_t w_max;
- int64_t k;
+ /* CUBIC K with 10 bits extra precision. */
+ uint64_t k_m;
ngtcp2_tstamp epoch_start;
uint64_t w_est;
#include "ngtcp2_net.h"
#include "ngtcp2_transport_params.h"
#include "ngtcp2_settings.h"
+#include "ngtcp2_callbacks.h"
#include "ngtcp2_tstamp.h"
#include "ngtcp2_frame_chain.h"
return 0;
}
+static int conn_call_begin_path_validation(ngtcp2_conn *conn,
+ const ngtcp2_pv *pv) {
+ int rv;
+ uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE;
+ const ngtcp2_path *old_path = NULL;
+
+ if (!pv || !conn->callbacks.begin_path_validation) {
+ return 0;
+ }
+
+ if (pv->flags & NGTCP2_PV_FLAG_PREFERRED_ADDR) {
+ flags |= NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR;
+ }
+
+ if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) {
+ old_path = &pv->fallback_dcid.ps.path;
+ }
+
+ rv = conn->callbacks.begin_path_validation(conn, flags, &pv->dcid.ps.path,
+ old_path, conn->user_data);
+ if (rv != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv,
ngtcp2_path_validation_result res) {
int rv;
return 0;
}
+// pktns_init initializes |pktns|. It assumes that the object pointed
+// by |pktns| is zero-cleared.
static void pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id,
ngtcp2_rst *rst, ngtcp2_cc *cc, int64_t initial_pkt_num,
ngtcp2_log *log, ngtcp2_qlog *qlog,
ngtcp2_objalloc *rtb_entry_objalloc,
ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
- memset(pktns, 0, sizeof(*pktns));
-
ngtcp2_gaptr_init(&pktns->rx.pngap, mem);
pktns->tx.last_pkt_num = initial_pkt_num - 1;
ngtcp2_log *log, ngtcp2_qlog *qlog,
ngtcp2_objalloc *rtb_entry_objalloc,
ngtcp2_objalloc *frc_objalloc, const ngtcp2_mem *mem) {
- *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns));
+ *ppktns = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_pktns));
if (*ppktns == NULL) {
return NGTCP2_ERR_NOMEM;
}
uint32_t *preferred_versions;
ngtcp2_settings settingsbuf;
ngtcp2_transport_params paramsbuf;
- (void)callbacks_version;
+ ngtcp2_callbacks callbacksbuf;
+ uint64_t map_seed;
(void)settings_version;
settings =
ngtcp2_settings_convert_to_latest(&settingsbuf, settings_version, settings);
params = ngtcp2_transport_params_convert_to_latest(
¶msbuf, transport_params_version, params);
+ callbacks = ngtcp2_callbacks_convert_to_latest(&callbacksbuf,
+ callbacks_version, callbacks);
assert(settings->max_window <= NGTCP2_MAX_VARINT);
assert(settings->max_stream_window <= NGTCP2_MAX_VARINT);
ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem);
- ngtcp2_map_init(&(*pconn)->strms, mem);
+ callbacks->rand((uint8_t *)&map_seed, sizeof(map_seed), &settings->rand_ctx);
+ ngtcp2_map_init(&(*pconn)->strms, map_seed, mem);
ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem);
ngtcp2_strm *strm;
int pkt_empty = 1;
uint64_t ndatalen = 0;
+ uint64_t wdatalen;
int send_stream = 0;
int stream_blocked = 0;
int send_datagram = 0;
left = ngtcp2_ppe_left(ppe);
if (*pfrc == NULL && send_stream && ngtcp2_pq_empty(&conn->tx.strmq) &&
- (ndatalen = ngtcp2_pkt_stream_max_datalen(
+ (wdatalen = ngtcp2_pkt_stream_max_datalen(
vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen,
left)) != (size_t)-1 &&
- (ndatalen || datalen == 0)) {
+ (wdatalen == ndatalen || wdatalen >= NGTCP2_MIN_STREAM_DATALEN) &&
+ (wdatalen || datalen == 0)) {
+ ndatalen = wdatalen;
datacnt = ngtcp2_vec_copy_at_most(data, NGTCP2_MAX_STREAM_DATACNT,
vmsg->stream.data, vmsg->stream.datacnt,
(size_t)ndatalen);
conn->pv = npv;
- return 0;
+ return conn_call_begin_path_validation(conn, conn->pv);
}
/*
case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM:
case NGTCP2_ERR_TRANSPORT_PARAM:
case NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE:
+ case NGTCP2_ERR_INTERNAL:
return 1;
}
fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) &&
rx_offset == strm->rx.last_offset;
- if (fin || datalen) {
- if (fin) {
- sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN;
- }
- if (!conn_is_tls_handshake_completed(conn)) {
- sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT;
- }
- rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data,
- (size_t)datalen);
- if (rv != 0) {
- return rv;
- }
+ assert(fin || datalen);
- rv = conn_emit_pending_stream_data(conn, strm, rx_offset);
- if (rv != 0) {
- return rv;
- }
+ if (fin) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN;
+ }
+ if (!conn_is_tls_handshake_completed(conn)) {
+ sdflags |= NGTCP2_STREAM_DATA_FLAG_0RTT;
+ }
+ rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data,
+ (size_t)datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
+ rv = conn_emit_pending_stream_data(conn, strm, rx_offset);
+ if (rv != 0) {
+ return rv;
}
} else if (fr->datacnt && !(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) {
rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len,
conn->pv = pv;
- return conn_call_activate_dcid(conn, &pv->dcid);
+ rv = conn_call_activate_dcid(conn, &pv->dcid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return conn_call_begin_path_validation(conn, conn->pv);
}
/*
conn->pv = pv;
- return 0;
+ return conn_call_begin_path_validation(conn, conn->pv);
}
/*
conn->pv = pv;
}
- return conn_call_activate_dcid(conn, &conn->dcid.current);
+ rv = conn_call_activate_dcid(conn, &conn->dcid.current);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return conn_call_begin_path_validation(conn, conn->pv);
}
int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path,
conn->pv = pv;
- return conn_call_activate_dcid(conn, &pv->dcid);
+ rv = conn_call_activate_dcid(conn, &pv->dcid);
+ if (rv != 0) {
+ return rv;
+ }
+
+ return conn_call_begin_path_validation(conn, conn->pv);
}
uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) {
ngtcp2_crypto_km ckm;
ngtcp2_crypto_cc cc;
ngtcp2_ppe ppe;
- ngtcp2_frame fr = {0};
+ ngtcp2_frame fr;
int rv;
ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid,
return NGTCP2_ERR_NOBUF;
}
- fr.type = NGTCP2_FRAME_CONNECTION_CLOSE;
- fr.connection_close.error_code = error_code;
- fr.connection_close.frame_type = 0;
- fr.connection_close.reasonlen = reasonlen;
- fr.connection_close.reason = (uint8_t *)reason;
+ fr.connection_close = (ngtcp2_connection_close){
+ .type = NGTCP2_FRAME_CONNECTION_CLOSE,
+ .error_code = error_code,
+ .reasonlen = reasonlen,
+ .reason = (uint8_t *)reason,
+ };
rv = ngtcp2_ppe_encode_frame(&ppe, &fr);
if (rv != 0) {
/*
* ngtcp2_ksl_nth_node returns the |n|th node under |blk|.
*/
-#define ngtcp2_ksl_nth_node(KSL, BLK, N) \
- ((ngtcp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N)))
+static inline ngtcp2_ksl_node *ngtcp2_ksl_nth_node(const ngtcp2_ksl *ksl,
+ const ngtcp2_ksl_blk *blk,
+ size_t n) {
+ return (ngtcp2_ksl_node *)(void *)(blk->nodes + ksl->nodelen * n);
+}
#ifndef WIN32
/*
* |it| points to. It is undefined to call this function when
* ngtcp2_ksl_it_end(it) returns nonzero.
*/
-#define ngtcp2_ksl_it_get(IT) \
- ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->data
+static inline void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) {
+ return ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->data;
+}
/*
* ngtcp2_ksl_it_next advances the iterator by one. It is undefined
* if this function is called when ngtcp2_ksl_it_end(it) returns
* nonzero.
*/
-#define ngtcp2_ksl_it_next(IT) \
- (++(IT)->i == (IT)->blk->n && (IT)->blk->next \
- ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \
- : 0)
+static inline void ngtcp2_ksl_it_next(ngtcp2_ksl_it *it) {
+ if (++it->i == it->blk->n && it->blk->next) {
+ it->blk = it->blk->next;
+ it->i = 0;
+ }
+}
/*
* ngtcp2_ksl_it_prev moves backward the iterator by one. It is
* ngtcp2_ksl_it_end returns nonzero if |it| points to the one beyond
* the last node.
*/
-#define ngtcp2_ksl_it_end(IT) \
- ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL)
+static inline int ngtcp2_ksl_it_end(const ngtcp2_ksl_it *it) {
+ return it->blk->n == it->i && it->blk->next == NULL;
+}
/*
* ngtcp2_ksl_it_begin returns nonzero if |it| points to the first
* It is undefined to call this function when ngtcp2_ksl_it_end(it)
* returns nonzero.
*/
-#define ngtcp2_ksl_it_key(IT) \
- ((ngtcp2_ksl_key *)ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key)
+static inline ngtcp2_ksl_key *ngtcp2_ksl_it_key(const ngtcp2_ksl_it *it) {
+ return (ngtcp2_ksl_key *)ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->key;
+}
/*
* ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar.
return;
}
- memset(&shd, 0, sizeof(shd));
-
- shd.type = NGTCP2_PKT_STATELESS_RESET;
+ shd = (ngtcp2_pkt_hd){
+ .type = NGTCP2_PKT_STATELESS_RESET,
+ };
log->log_printf(
log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"),
#define NGTCP2_INITIAL_HASHBITS 4
-void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) {
+void ngtcp2_map_init(ngtcp2_map *map, uint64_t seed, const ngtcp2_mem *mem) {
map->mem = mem;
map->hashbits = 0;
map->table = NULL;
+ map->seed = seed;
map->size = 0;
}
return 0;
}
-static size_t hash(ngtcp2_map_key_type key, size_t bits) {
- return (size_t)((key * 11400714819323198485llu) >> (64 - bits));
+static size_t map_hash(const ngtcp2_map *map, ngtcp2_map_key_type key) {
+ /* hasher from
+ https://github.com/rust-lang/rustc-hash/blob/dc5c33f1283de2da64d8d7a06401d91aded03ad4/src/lib.rs
+ We do not perform finalization here because we use top bits
+ anyway. */
+ key += map->seed;
+ key *= 0xf1357aea2e62a9c5ull;
+ return (size_t)((key * 11400714819323198485llu) >> (64 - map->hashbits));
}
static void map_bucket_swap(ngtcp2_map_bucket *a, ngtcp2_map_bucket *b) {
continue;
}
- idx = hash(bkt->key, map->hashbits);
+ idx = map_hash(map, bkt->key);
fprintf(stderr, "@%zu hash=%zu key=%" PRIu64 " base=%zu distance=%u\n", i,
- hash(bkt->key, map->hashbits), bkt->key, idx, bkt->psl);
+ map_hash(map, bkt->key), bkt->key, idx, bkt->psl);
}
}
#endif /* !defined(WIN32) */
-static int insert(ngtcp2_map_bucket *table, size_t hashbits,
- ngtcp2_map_key_type key, void *data) {
- size_t idx = hash(key, hashbits);
+static int map_insert(ngtcp2_map *map, ngtcp2_map_key_type key, void *data) {
+ size_t idx = map_hash(map, key);
ngtcp2_map_bucket b = {
.key = key,
.data = data,
};
ngtcp2_map_bucket *bkt;
- size_t mask = (1u << hashbits) - 1;
+ size_t mask = (1u << map->hashbits) - 1;
for (;;) {
- bkt = &table[idx];
+ bkt = &map->table[idx];
if (bkt->data == NULL) {
*bkt = b;
+ ++map->size;
return 0;
}
static int map_resize(ngtcp2_map *map, size_t new_hashbits) {
size_t i;
- ngtcp2_map_bucket *new_table;
ngtcp2_map_bucket *bkt;
size_t tablelen;
int rv;
+ ngtcp2_map new_map = {
+ .table = ngtcp2_mem_calloc(map->mem, 1u << new_hashbits,
+ sizeof(ngtcp2_map_bucket)),
+ .mem = map->mem,
+ .seed = map->seed,
+ .hashbits = new_hashbits,
+ };
(void)rv;
- new_table =
- ngtcp2_mem_calloc(map->mem, 1u << new_hashbits, sizeof(ngtcp2_map_bucket));
- if (new_table == NULL) {
+ if (new_map.table == NULL) {
return NGTCP2_ERR_NOMEM;
}
continue;
}
- rv = insert(new_table, new_hashbits, bkt->key, bkt->data);
+ rv = map_insert(&new_map, bkt->key, bkt->data);
assert(0 == rv);
}
}
ngtcp2_mem_free(map->mem, map->table);
+ map->table = new_map.table;
map->hashbits = new_hashbits;
- map->table = new_table;
return 0;
}
assert(data);
- /* Load factor is 0.75 */
+ /* Load factor is 7/8 */
/* Under the very initial condition, that is map->size == 0 and
- map->hashbits == 0, 4 > 3 still holds nicely. */
- if ((map->size + 1) * 4 > (1u << map->hashbits) * 3) {
+ map->hashbits == 0, 8 > 7 still holds nicely. */
+ if ((map->size + 1) * 8 > (1u << map->hashbits) * 7) {
if (map->hashbits) {
rv = map_resize(map, map->hashbits + 1);
if (rv != 0) {
}
}
- rv = insert(map->table, map->hashbits, key, data);
+ rv = map_insert(map, key, data);
if (rv != 0) {
return rv;
}
- ++map->size;
-
return 0;
}
return NULL;
}
- idx = hash(key, map->hashbits);
+ idx = map_hash(map, key);
mask = (1u << map->hashbits) - 1;
for (;;) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
- idx = hash(key, map->hashbits);
+ idx = map_hash(map, key);
mask = (1u << map->hashbits) - 1;
for (;;) {
typedef struct ngtcp2_map {
ngtcp2_map_bucket *table;
const ngtcp2_mem *mem;
+ uint64_t seed;
size_t size;
size_t hashbits;
} ngtcp2_map;
/*
* ngtcp2_map_init initializes the map |map|.
*/
-void ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem);
+void ngtcp2_map_init(ngtcp2_map *map, uint64_t seed, const ngtcp2_mem *mem);
/*
* ngtcp2_map_free deallocates any resources allocated for |map|. The
v2. */
#define NGTCP2_PKT_TYPE_RETRY_V2 0x0
+/* NGTCP2_MIN_STREAM_DATALEN is the minimum length of STREAM frame to
+ avoid too small frame. It is not always enforced for various
+ reasons. For example, due to flow control, we might have fewer
+ bytes available to send. Therefore, it is only applied when the
+ length of data to send is larger than this limit. */
+#define NGTCP2_MIN_STREAM_DATALEN 256
+
typedef struct ngtcp2_pkt_retry {
ngtcp2_cid odcid;
uint8_t *token;
void *ngtcp2_ringbuf_get(const ngtcp2_ringbuf *rb, size_t offset);
/* ngtcp2_ringbuf_len returns the number of elements stored. */
-#define ngtcp2_ringbuf_len(RB) ((RB)->len)
+static inline size_t ngtcp2_ringbuf_len(const ngtcp2_ringbuf *rb) {
+ return rb->len;
+}
/* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */
int ngtcp2_ringbuf_full(const ngtcp2_ringbuf *rb);
rs->prior_ts = UINT64_MAX;
rs->tx_in_flight = 0;
rs->lost = 0;
- rs->prior_lost = 0;
rs->send_elapsed = 0;
rs->ack_elapsed = 0;
rs->last_end_seq = -1;
rst->app_limited = 0;
rst->is_cwnd_limited = 0;
rst->lost = 0;
- rst->valid_after_seq = rst->last_seq;
}
void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent,
rs->interval = ngtcp2_max_uint64(rs->send_elapsed, rs->ack_elapsed);
rs->delivered = rst->delivered - rs->prior_delivered;
- rs->lost = rst->lost - rs->prior_lost;
if (rs->interval < cstat->min_rtt) {
rs->interval = UINT64_MAX;
cstat->delivery_rate_sec = rs->delivered * NGTCP2_SECONDS / rs->interval;
}
-static int rst_is_newest_pkt(const ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent,
- const ngtcp2_rs *rs) {
- return ent->ts > rst->first_sent_ts ||
- (ent->ts == rst->first_sent_ts && ent->rst.end_seq > rs->last_end_seq);
+static int is_newest_pkt(const ngtcp2_rtb_entry *ent, const ngtcp2_rs *rs) {
+ return ent->rst.end_seq > rs->last_end_seq;
}
void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent,
ngtcp2_tstamp ts) {
ngtcp2_rs *rs = &rst->rs;
- if (ent->rst.end_seq <= rst->valid_after_seq) {
- return;
- }
-
rst->delivered += ent->pktlen;
rst->delivered_ts = ts;
- if (rs->prior_ts == UINT64_MAX || rst_is_newest_pkt(rst, ent, rs)) {
+ if (rs->prior_ts == UINT64_MAX || is_newest_pkt(ent, rs)) {
rs->prior_delivered = ent->rst.delivered;
rs->prior_ts = ent->rst.delivered_ts;
rs->is_app_limited = ent->rst.is_app_limited;
rs->send_elapsed = ent->ts - ent->rst.first_sent_ts;
rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts;
- rs->tx_in_flight = ent->rst.tx_in_flight;
- rs->prior_lost = ent->rst.lost;
rs->last_end_seq = ent->rst.end_seq;
rst->first_sent_ts = ent->ts;
}
ngtcp2_tstamp prior_ts;
uint64_t tx_in_flight;
uint64_t lost;
- uint64_t prior_lost;
ngtcp2_duration send_elapsed;
ngtcp2_duration ack_elapsed;
int64_t last_end_seq;
across all packet number spaces, we can replace this with a
packet number. */
int64_t last_seq;
- /* valid_after_seq is the sequence number, and ignore a packet if
- the sequence number of the packet is less than or equal to this
- number. */
- int64_t valid_after_seq;
int is_cwnd_limited;
} ngtcp2_rst;
static void rtb_entry_init(ngtcp2_rtb_entry *ent, const ngtcp2_pkt_hd *hd,
ngtcp2_frame_chain *frc, ngtcp2_tstamp ts,
size_t pktlen, uint16_t flags) {
- memset(ent, 0, sizeof(*ent));
-
- ent->hd.pkt_num = hd->pkt_num;
- ent->hd.type = hd->type;
- ent->hd.flags = hd->flags;
- ent->frc = frc;
- ent->ts = ts;
- ent->lost_ts = UINT64_MAX;
- ent->pktlen = pktlen;
- ent->flags = flags;
+ *ent = (ngtcp2_rtb_entry){
+ .hd =
+ {
+ .pkt_num = hd->pkt_num,
+ .type = hd->type,
+ .flags = hd->flags,
+ },
+ .frc = frc,
+ .ts = ts,
+ .lost_ts = UINT64_MAX,
+ .pktlen = pktlen,
+ .flags = flags,
+ };
}
int ngtcp2_rtb_entry_objalloc_new(ngtcp2_rtb_entry **pent,
if (ent->flags &
(NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) {
++rtb->num_lost_ignore_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,
- pktns->id, ent->ts, ent->rst.lost,
- ent->rst.tx_in_flight,
- ent->rst.is_app_limited),
- ts);
+ } else if (ent->hd.pkt_num >= rtb->cc_pkt_num) {
+ rtb->rst->lost += ent->pktlen;
+
+ if (rtb->cc->on_pkt_lost) {
+ cc->on_pkt_lost(cc, cstat,
+ ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen,
+ pktns->id, ent->ts, ent->rst.lost,
+ ent->rst.tx_in_flight,
+ ent->rst.is_app_limited),
+ ts);
+ }
}
if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
ngtcp2_cc *cc = rtb->cc;
ngtcp2_cc_pkt pkt;
+ assert(ent->hd.pkt_num >= rtb->cc_pkt_num);
+
ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts);
if (cc->on_pkt_acked) {
}
rtb_remove(rtb, &it, &acked_ent, ent, cstat);
- ++num_acked;
}
for (i = 0; i < fr->rangecnt; ++i) {
}
rtb_remove(rtb, &it, &acked_ent, ent, cstat);
- ++num_acked;
}
}
ngtcp2_max_uint64(pkt_ts - 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) {
+ if (rv == 0 && cc->new_rtt_sample &&
+ rtb->largest_acked_tx_pkt_num >= rtb->cc_pkt_num) {
cc->new_rtt_sample(cc, cstat, ts);
}
}
cc_ack.bytes_delivered += ent->pktlen;
cc_ack.pkt_delivered = ent->rst.delivered;
+
+ rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts);
+
+ ++num_acked;
}
- rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts);
acked_ent = ent->next;
ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
rtb->frc_objalloc, rtb->mem);
for (ent = acked_ent; ent; ent = acked_ent) {
rtb_on_pkt_acked(rtb, ent, cstat, pktns, ts);
acked_ent = ent->next;
+ ++num_acked;
ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
rtb->frc_objalloc, rtb->mem);
}
}
}
- rtb->rst->lost += cc_ack.bytes_lost;
-
cc_ack.largest_pkt_sent_ts = largest_pkt_sent_ts;
if (num_acked && cc->on_ack_recv) {
cc->on_ack_recv(cc, cstat, &cc_ack, ts);
/* strm_rob_heavily_fragmented returns nonzero if the number of gaps
in |rob| exceeds the limit. */
static int strm_rob_heavily_fragmented(const ngtcp2_rob *rob) {
- return ngtcp2_ksl_len(&rob->gapksl) >= 5000;
+ return ngtcp2_ksl_len(&rob->gapksl) >= 1000;
}
int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data,
}
}
+ rv = ngtcp2_rob_push(strm->rx.rob, offset, data, datalen);
+ if (rv != 0) {
+ return rv;
+ }
+
if (strm_rob_heavily_fragmented(strm->rx.rob)) {
return NGTCP2_ERR_INTERNAL;
}
- return ngtcp2_rob_push(strm->rx.rob, offset, data, datalen);
+ return 0;
}
void ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset) {
if (rv != 0) {
return rv;
}
+ } else if (ngtcp2_ksl_len(strm->tx.streamfrq) >= 1000) {
+ return NGTCP2_ERR_INTERNAL;
}
return ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &frc->fr.stream.offset,
datalen = ngtcp2_vec_len(fr->data, fr->datacnt);
/* datalen could be zero if 0 length STREAM has been sent */
- if (left == 0 && datalen) {
+ /* We might see more data in the queue, then left < datalen could be
+ true. We only see the first one for now. */
+ if ((fr->type == NGTCP2_FRAME_STREAM &&
+ (left < datalen && left < NGTCP2_MIN_STREAM_DATALEN)) ||
+ (left == 0 && datalen)) {
rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc);
if (rv != 0) {
assert(ngtcp2_err_is_fatal(rv));
}
}
- return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len);
+ rv = ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len);
+ if (rv != 0) {
+ return rv;
+ }
+
+ if (ngtcp2_ksl_len(&strm->tx.acked_offset->gap) >= 1000) {
+ return NGTCP2_ERR_INTERNAL;
+ }
+
+ return 0;
}
void ngtcp2_strm_set_app_error_code(ngtcp2_strm *strm,
}
/* Set default values */
- memset(params, 0, sizeof(*params));
- params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE;
- params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT;
- params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY;
- params->active_connection_id_limit =
- NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT;
+ *params = (ngtcp2_transport_params){
+ .max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE,
+ .ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT,
+ .max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY,
+ .active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT,
+ };
p = end = data;
#include "ngtcp2_mem.h"
-/*
- * ngtcp2_vec_lit is a convenient macro to fill the object pointed by
- * |DEST| with the literal string |LIT|.
- */
-#define ngtcp2_vec_lit(DEST, LIT) \
- ((DEST)->base = (uint8_t *)(LIT), (DEST)->len = sizeof(LIT) - 1, (DEST))
-
/*
* ngtcp2_vec_init initializes |vec| with the given parameters. It
* returns |vec|.
*/
#define NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN 0x02u
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_begin_path_validation` is a callback function which
+ * is called when the path validation has started. |flags| is zero or
+ * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_*
+ * <NGTCP2_PATH_VALIDATION_FLAG_NONE>`. |path| is the path that is
+ * being validated. |fallback_path|, if not NULL, is the path that is
+ * used when this validation fails.
+ *
+ * Currently, the flags may only contain
+ * :macro:`NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR`.
+ *
+ * The callback function must return 0 if it succeeds. Returning
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return
+ * immediately.
+ */
+typedef int (*ngtcp2_begin_path_validation)(ngtcp2_conn *conn, uint32_t flags,
+ const ngtcp2_path *path,
+ const ngtcp2_path *fallback_path,
+ void *user_data);
+
/**
* @functypedef
*
void *user_data);
#define NGTCP2_CALLBACKS_V1 1
-#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V1
+#define NGTCP2_CALLBACKS_V2 2
+#define NGTCP2_CALLBACKS_VERSION NGTCP2_CALLBACKS_V2
/**
* @struct
* is only used by client.
*/
ngtcp2_tls_early_data_rejected tls_early_data_rejected;
+ /* The following fields have been added since NGTCP2_CALLBACKS_V2. */
+ /**
+ * :member:`begin_path_validation` is a callback function which is
+ * invoked when a path validation has started. This field is
+ * available since v1.14.0.
+ */
+ ngtcp2_begin_path_validation begin_path_validation;
} ngtcp2_callbacks;
/**
*
* Version number of the ngtcp2 library release.
*/
-#define NGTCP2_VERSION "1.13.0"
+#define NGTCP2_VERSION "1.14.0"
/**
* @macro
* number, 8 bits for minor and 8 bits for patch. Version 1.2.3
* becomes 0x010203.
*/
-#define NGTCP2_VERSION_NUM 0x010d00
+#define NGTCP2_VERSION_NUM 0x010e00
#endif /* !defined(NGTCP2_VERSION_H) */