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;
}
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;
}
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;
}
return NGTCP2_ERR_NOMEM;
}
- hd = (ngtcp2_memblock_hd *)(void *)p;
+ hd = (void *)p;
hd->next = balloc->head;
balloc->head = hd;
ngtcp2_buf_init(
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);
bbr->max_inflight = 0;
- bbr->congestion_recovery_start_ts = UINT64_MAX;
-
bbr->bdp = 0;
}
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;
}
}
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,
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);
}
}
- if (!bbr_is_inflight_too_high(bbr)) {
+ if (!bbr_is_inflight_too_high(bbr, &bbr->rst->rs)) {
if (bbr->inflight_longterm == UINT64_MAX) {
return;
}
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) {
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);
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;
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);
}
}
}
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,
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) {
(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;
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);
}
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;
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,
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 =
{
},
};
- reno_cc_reset(reno);
+ reno_cc_reset(reno, cstat);
}
static int in_congestion_recovery(const 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);
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;
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);
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) {
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;
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 =
{
.rst = rst,
};
- cubic_cc_reset(cubic);
+ cubic_cc_reset(cubic, cstat);
}
uint64_t ngtcp2_cbrt(uint64_t n) {
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;
}
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);
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,
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;
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;
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));
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);
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,
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);
}
* 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.
*/
* @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);
/**
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;
/**
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,
/* 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. */
} 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,
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 >=
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) {
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:
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);
/*
(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 {
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;
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) {
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);
}
(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;
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;
}
reasonlen);
return;
- };
+ }
ngtcp2_ccerr_set_transport_error(
ccerr, ngtcp2_err_infer_quic_transport_error_code(liberr), reason,
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);
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;
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);
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;
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;
}
}
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;
}
conn->flags &= ~NGTCP2_CONN_FLAG_AGGREGATE_PKTS;
- ngtcp2_conn_update_pkt_tx_time(conn, ts);
-
return nwrite;
}
/* 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
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;
#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,
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;
}
rs->delivered = rst->delivered - rs->prior_delivered;
if (rs->interval < cstat->min_rtt) {
- rs->interval = UINT64_MAX;
return;
}
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);
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) {
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;
}
}
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;
}
}
-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);
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;
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;
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;
}
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) {
}
}
- 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 &&
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) {
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);
}
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;
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;
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) {
* @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
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;
/**
/**
* :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;
/**
* `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);
* @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);
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
*
* 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|.
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
*
(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
*
* Version number of the ngtcp2 library release.
*/
-#define NGTCP2_VERSION "1.16.0"
+#define NGTCP2_VERSION "1.17.0"
/**
* @macro
* 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) */