static uint64_t bbr_inflight_with_headroom(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat);
-static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat);
+static void bbr_raise_inflight_longterm_slope(ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat);
-static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_ack *ack);
+static void bbr_probe_inflight_longterm_upward(ngtcp2_cc_bbr *bbr,
+ 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, ngtcp2_tstamp ts);
+ const ngtcp2_cc_ack *ack);
static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
ngtcp2_tstamp ts);
static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat);
-static int bbr_check_inflight_too_high(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts);
-
-static int is_inflight_too_high(const ngtcp2_rs *rs);
+static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr);
static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat,
- const ngtcp2_rs *rs, ngtcp2_tstamp ts);
+ 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_hi_from_lost_packet(ngtcp2_cc_bbr *bbr,
- const ngtcp2_rs *rs,
- const ngtcp2_cc_pkt *pkt);
+static uint64_t
+bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_cc_pkt *pkt);
static void bbr_update_min_rtt(ngtcp2_cc_bbr *bbr, const ngtcp2_cc_ack *ack,
ngtcp2_tstamp ts);
bbr->bw_probe_up_rounds = 0;
bbr->bw_probe_up_acks = 0;
- bbr->inflight_hi = UINT64_MAX;
+ bbr->inflight_longterm = UINT64_MAX;
bbr->probe_rtt_expired = 0;
bbr->probe_rtt_min_delay = UINT64_MAX;
}
static void bbr_reset_lower_bounds(ngtcp2_cc_bbr *bbr) {
- bbr->bw_lo = UINT64_MAX;
- bbr->inflight_lo = UINT64_MAX;
+ bbr->bw_shortterm = UINT64_MAX;
+ bbr->inflight_shortterm = UINT64_MAX;
}
static void bbr_init_round_counting(ngtcp2_cc_bbr *bbr) {
static void bbr_check_full_bw_reached(ngtcp2_cc_bbr *bbr,
ngtcp2_conn_stat *cstat) {
- if (bbr->full_bw_now || bbr->rst->rs.is_app_limited) {
+ if (bbr->full_bw_now || !bbr->round_start || bbr->rst->rs.is_app_limited) {
return;
}
return;
}
- if (!bbr->round_start) {
- return;
- }
-
++bbr->full_bw_count;
bbr->full_bw_now = bbr->full_bw_count >= 3;
if (bbr->full_bw_reached || bbr->loss_events_in_round <= 6 ||
(bbr->in_loss_recovery &&
bbr->round_count <= bbr->round_count_at_recovery) ||
- !is_inflight_too_high(&bbr->rst->rs)) {
+ !bbr_is_inflight_too_high(bbr)) {
return;
}
bbr->full_bw_reached = 1;
- bbr->inflight_hi = ngtcp2_max_uint64(bbr_bdp_multiple(bbr, bbr->cwnd_gain_h),
- bbr->inflight_latest);
+ bbr->inflight_longterm = ngtcp2_max_uint64(
+ bbr_bdp_multiple(bbr, bbr->cwnd_gain_h), bbr->inflight_latest);
}
static void bbr_init_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
}
static void bbr_init_lower_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) {
- if (bbr->bw_lo == UINT64_MAX) {
- bbr->bw_lo = bbr->max_bw;
+ if (bbr->bw_shortterm == UINT64_MAX) {
+ bbr->bw_shortterm = bbr->max_bw;
}
- if (bbr->inflight_lo == UINT64_MAX) {
- bbr->inflight_lo = cstat->cwnd;
+ if (bbr->inflight_shortterm == UINT64_MAX) {
+ bbr->inflight_shortterm = cstat->cwnd;
}
}
static void bbr_loss_lower_bounds(ngtcp2_cc_bbr *bbr) {
- bbr->bw_lo = ngtcp2_max_uint64(
- bbr->bw_latest, bbr->bw_lo * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM);
- bbr->inflight_lo = ngtcp2_max_uint64(
+ bbr->bw_shortterm = ngtcp2_max_uint64(
+ bbr->bw_latest,
+ bbr->bw_shortterm * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM);
+ bbr->inflight_shortterm = ngtcp2_max_uint64(
bbr->inflight_latest,
- bbr->inflight_lo * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM);
+ bbr->inflight_shortterm * NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM);
}
static void bbr_bound_bw_for_model(ngtcp2_cc_bbr *bbr) {
- bbr->bw = ngtcp2_min_uint64(bbr->max_bw, bbr->bw_lo);
+ bbr->bw = ngtcp2_min_uint64(bbr->max_bw, bbr->bw_shortterm);
}
static void bbr_update_max_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
bbr->pacing_gain_h = 125;
bbr->cwnd_gain_h = 225;
- bbr_raise_inflight_hi_slope(bbr, cstat);
+ bbr_raise_inflight_longterm_slope(bbr, cstat);
}
static void bbr_update_probe_bw_cycle_phase(ngtcp2_cc_bbr *bbr,
return;
}
- bbr_adapt_upper_bounds(bbr, cstat, ack, ts);
+ bbr_adapt_upper_bounds(bbr, cstat, ack);
if (!bbr_is_in_probe_bw_state(bbr)) {
return;
}
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_hi) {
+ 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) {
ngtcp2_conn_stat *cstat) {
uint64_t headroom;
uint64_t mpcwnd;
- if (bbr->inflight_hi == UINT64_MAX) {
+ if (bbr->inflight_longterm == UINT64_MAX) {
return UINT64_MAX;
}
- headroom = ngtcp2_max_uint64(cstat->max_tx_udp_payload_size,
- bbr->inflight_hi * NGTCP2_BBR_HEADROOM_NUMER /
- NGTCP2_BBR_HEADROOM_DENOM);
+ headroom =
+ ngtcp2_max_uint64(cstat->max_tx_udp_payload_size,
+ bbr->inflight_longterm * NGTCP2_BBR_HEADROOM_NUMER /
+ NGTCP2_BBR_HEADROOM_DENOM);
mpcwnd = min_pipe_cwnd(cstat->max_tx_udp_payload_size);
- if (bbr->inflight_hi > headroom) {
- return ngtcp2_max_uint64(bbr->inflight_hi - headroom, mpcwnd);
+ if (bbr->inflight_longterm > headroom) {
+ return ngtcp2_max_uint64(bbr->inflight_longterm - headroom, mpcwnd);
}
return mpcwnd;
}
-static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat) {
+static void bbr_raise_inflight_longterm_slope(ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat) {
uint64_t growth_this_round = cstat->max_tx_udp_payload_size
<< bbr->bw_probe_up_rounds;
bbr->probe_up_cnt = ngtcp2_max_uint64(cstat->cwnd / growth_this_round, 1);
}
-static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_ack *ack) {
+static void bbr_probe_inflight_longterm_upward(ngtcp2_cc_bbr *bbr,
+ ngtcp2_conn_stat *cstat,
+ const ngtcp2_cc_ack *ack) {
uint64_t delta;
- if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_hi) {
+ if (!bbr->rst->is_cwnd_limited || cstat->cwnd < bbr->inflight_longterm) {
return;
}
bbr->probe_up_cnt * cstat->max_tx_udp_payload_size) {
delta = bbr->bw_probe_up_acks / bbr->probe_up_cnt;
bbr->bw_probe_up_acks -= delta * bbr->probe_up_cnt;
- bbr->inflight_hi += delta;
+ bbr->inflight_longterm += delta;
}
if (bbr->round_start) {
- bbr_raise_inflight_hi_slope(bbr, cstat);
+ bbr_raise_inflight_longterm_slope(bbr, cstat);
}
}
static void bbr_adapt_upper_bounds(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat,
- const ngtcp2_cc_ack *ack, ngtcp2_tstamp ts) {
+ 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;
}
}
- if (!bbr_check_inflight_too_high(bbr, cstat, ts)) {
- if (bbr->inflight_hi == UINT64_MAX) {
+ if (!bbr_is_inflight_too_high(bbr)) {
+ if (bbr->inflight_longterm == UINT64_MAX) {
return;
}
- if (bbr->rst->rs.tx_in_flight > bbr->inflight_hi) {
- bbr->inflight_hi = bbr->rst->rs.tx_in_flight;
+ if (bbr->rst->rs.tx_in_flight > bbr->inflight_longterm) {
+ bbr->inflight_longterm = bbr->rst->rs.tx_in_flight;
}
if (bbr->state == NGTCP2_BBR_STATE_PROBE_BW_UP) {
- bbr_probe_inflight_hi_upward(bbr, cstat, ack);
+ bbr_probe_inflight_longterm_upward(bbr, cstat, ack);
}
}
}
return ngtcp2_min_uint64(bbr->bdp, cstat->cwnd);
}
-static int bbr_check_inflight_too_high(ngtcp2_cc_bbr *bbr,
- ngtcp2_conn_stat *cstat,
- ngtcp2_tstamp ts) {
- if (is_inflight_too_high(&bbr->rst->rs)) {
- if (bbr->bw_probe_samples) {
- bbr_handle_inflight_too_high(bbr, cstat, &bbr->rst->rs, ts);
- }
-
- return 1;
- }
-
- return 0;
-}
-
-static int is_inflight_too_high(const ngtcp2_rs *rs) {
+static int bbr_is_inflight_too_high(ngtcp2_cc_bbr *bbr) {
+ const ngtcp2_rs *rs = &bbr->rst->rs;
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) {
- bbr->inflight_hi = ngtcp2_max_uint64(
+ bbr->inflight_longterm = ngtcp2_max_uint64(
rs->tx_in_flight, bbr_target_inflight(bbr, cstat) *
NGTCP2_BBR_BETA_NUMER / NGTCP2_BBR_BETA_DENOM);
}
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 = {0};
+ ngtcp2_rs *rs = &bbr->rst->rs;
bbr_note_loss(bbr);
return;
}
- rs.tx_in_flight = pkt->tx_in_flight;
+ 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;
- rs.is_app_limited = pkt->is_app_limited;
+ rs->lost = bbr->rst->lost + pkt->pktlen - pkt->lost;
+ rs->is_app_limited = pkt->is_app_limited;
- if (is_inflight_too_high(&rs)) {
- rs.tx_in_flight = bbr_inflight_hi_from_lost_packet(bbr, &rs, pkt);
+ if (bbr_is_inflight_too_high(bbr)) {
+ rs->tx_in_flight = bbr_inflight_longterm_from_lost_packet(bbr, pkt);
- bbr_handle_inflight_too_high(bbr, cstat, &rs, ts);
+ bbr_handle_inflight_too_high(bbr, cstat, ts);
}
}
-static uint64_t bbr_inflight_hi_from_lost_packet(ngtcp2_cc_bbr *bbr,
- const ngtcp2_rs *rs,
- const ngtcp2_cc_pkt *pkt) {
+static uint64_t
+bbr_inflight_longterm_from_lost_packet(ngtcp2_cc_bbr *bbr,
+ const ngtcp2_cc_pkt *pkt) {
+ ngtcp2_rs *rs = &bbr->rst->rs;
uint64_t inflight_prev, lost_prev, lost_prefix;
(void)bbr;
ngtcp2_conn_stat *cstat) {
uint64_t inflight;
- /* Not documented */
- /* bbr_update_aggregation_budget(bbr); */
-
inflight = bbr_bdp_multiple(bbr, bbr->cwnd_gain_h) + bbr->extra_acked;
bbr->max_inflight = bbr_quantization_budget(bbr, cstat, inflight);
}
if (bbr_is_in_probe_bw_state(bbr) &&
bbr->state != NGTCP2_BBR_STATE_PROBE_BW_CRUISE) {
- cap = bbr->inflight_hi;
+ cap = bbr->inflight_longterm;
} else if (bbr->state == NGTCP2_BBR_STATE_PROBE_RTT ||
bbr->state == NGTCP2_BBR_STATE_PROBE_BW_CRUISE) {
cap = bbr_inflight_with_headroom(bbr, cstat);
}
- cap = ngtcp2_min_uint64(cap, bbr->inflight_lo);
+ cap = ngtcp2_min_uint64(cap, bbr->inflight_shortterm);
cap = ngtcp2_max_uint64(cap, mpcwnd);
cstat->cwnd = ngtcp2_min_uint64(cstat->cwnd, cap);
uint64_t inflight_latest;
/* Lower bounds */
- uint64_t bw_lo;
- uint64_t inflight_lo;
+ uint64_t bw_shortterm;
+ uint64_t inflight_shortterm;
/* Round counting */
uint64_t next_round_delivered;
int bw_probe_samples;
size_t bw_probe_up_rounds;
uint64_t bw_probe_up_acks;
- uint64_t inflight_hi;
+ uint64_t inflight_longterm;
int probe_rtt_expired;
ngtcp2_duration probe_rtt_min_delay;
ngtcp2_tstamp probe_rtt_min_stamp;
? 0
: NGTCP2_ERR_INVALID_ARGUMENT;
}
+
+void ngtcp2_dcid_apply_validated_path(ngtcp2_dcid *dcid,
+ const ngtcp2_path_history_entry *ent) {
+ dcid->flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED;
+ dcid->max_udp_payload_size = ent->max_udp_payload_size;
+}
const ngtcp2_path *path,
const uint8_t *token);
+/* TODO It might be performance win if we store congestion state in
+ this entry, and restore it when migrate back to this path. */
+typedef struct ngtcp2_path_history_entry {
+ /* ps contains path. */
+ ngtcp2_path_storage ps;
+ /* max_udp_payload_size is the maximum size of UDP datagram payload
+ that is allowed to be sent to this path. */
+ size_t max_udp_payload_size;
+ /* ts is the timestamp when this entry is added to the path history.
+ It happens when a local endpoint migrates to the another path. */
+ ngtcp2_tstamp ts;
+} ngtcp2_path_history_entry;
+
+/*
+ * ngtcp2_dcid_apply_validated_path applies the defaults from |ent|
+ * which contains the validated path and its stored configurations.
+ */
+void ngtcp2_dcid_apply_validated_path(ngtcp2_dcid *dcid,
+ const ngtcp2_path_history_entry *ent);
+
#endif /* !defined(NGTCP2_CID_H) */
p->version_info_present = 1;
}
+static void conn_update_skip_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns) {
+ uint8_t gap;
+
+ conn->callbacks.rand(&gap, 1, &conn->local.settings.rand_ctx);
+
+ pktns->tx.skip_pkt.next_pkt_num =
+ pktns->tx.last_pkt_num + 3 +
+ (int64_t)gap * (1ll << pktns->tx.skip_pkt.exponent++);
+
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "next skip pkn=%" PRId64,
+ pktns->tx.skip_pkt.next_pkt_num);
+}
+
+static int conn_handle_skip_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns,
+ ngtcp2_tstamp ts) {
+ ngtcp2_rtb_entry *rtbent;
+ ngtcp2_pkt_hd hd;
+ int rv;
+
+ assert(NGTCP2_PKTNS_ID_APPLICATION == pktns->id);
+
+ if (pktns->tx.last_pkt_num + 1 != pktns->tx.skip_pkt.next_pkt_num) {
+ return 0;
+ }
+
+ ngtcp2_pkt_hd_init(&hd, 0, NGTCP2_PKT_1RTT, NULL, NULL,
+ pktns->tx.skip_pkt.next_pkt_num, 0, 0);
+
+ rv = ngtcp2_rtb_entry_objalloc_new(&rtbent, &hd, NULL, ts, 0,
+ NGTCP2_RTB_ENTRY_FLAG_SKIP,
+ &conn->rtb_entry_objalloc);
+ if (rv != 0) {
+ assert(ngtcp2_err_is_fatal(rv));
+ return rv;
+ }
+
+ rv = ngtcp2_rtb_add(&pktns->rtb, rtbent, &conn->cstat);
+ if (rv != 0) {
+ ngtcp2_rtb_entry_objalloc_del(rtbent, &conn->rtb_entry_objalloc,
+ &conn->frc_objalloc, conn->mem);
+ return rv;
+ }
+
+ ++pktns->tx.last_pkt_num;
+
+ conn_update_skip_pkt(conn, pktns);
+
+ return 0;
+}
+
static size_t buflen_align(size_t buflen) {
return (buflen + 0x7) & (size_t)~0x7;
}
ngtcp2_unreachable();
}
+ ngtcp2_static_ringbuf_path_history_init(&(*pconn)->path_history);
+
+ (*pconn)->callbacks = *callbacks;
+
rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst,
&(*pconn)->cc, settings->initial_pkt_num, &(*pconn)->log,
&(*pconn)->qlog, &(*pconn)->rtb_entry_objalloc,
&(*pconn)->qlog, &(*pconn)->rtb_entry_objalloc,
&(*pconn)->frc_objalloc, mem);
+ conn_update_skip_pkt(*pconn, &(*pconn)->pktns);
+
scident = ngtcp2_mem_malloc(mem, sizeof(*scident));
if (scident == NULL) {
rv = NGTCP2_ERR_NOMEM;
(*pconn)->keep_alive.timeout = UINT64_MAX;
(*pconn)->oscid = *scid;
- (*pconn)->callbacks = *callbacks;
(*pconn)->mem = mem;
(*pconn)->user_data = user_data;
(*pconn)->idle_ts = settings->initial_ts;
if (num_reclaimed < 0) {
ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc,
conn->mem);
- return rv;
+ return num_reclaimed;
}
if (num_reclaimed) {
goto build_pkt;
those packets have been acknowledged (i.e., retransmission in
another packet). For server, in this case, we don't have to
send any probe packet. Client needs to send probe packets
- until it knows that server has completed address validation or
- handshake has been confirmed. */
+ until it knows that server has completed address
+ validation. */
if (pktns->rtb.num_pto_eliciting == 0 &&
(conn->server ||
- (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED |
- NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) {
+ (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) {
pktns->rtb.probe_pkt_left = 0;
ngtcp2_conn_set_loss_detection_timer(conn, ts);
/* TODO If packet is empty, we should return now if cwnd is
nfrc->fr.max_data.max_data;
}
+ rv = conn_handle_skip_pkt(conn, pktns, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid,
pktns->tx.last_pkt_num + 1,
pktns_select_pkt_numlen(pktns), version);
pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) {
num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, 1);
if (num_reclaimed < 0) {
- return rv;
+ return num_reclaimed;
}
if (num_reclaimed) {
goto build_pkt;
/* TODO Push STREAM frame back to ngtcp2_strm if there is an error
before ngtcp2_rtb_entry is safely created and added. */
- if (require_padding) {
+ if ((flags & (NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY)) &&
+ (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);
} else {
lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen);
version = conn->negotiated_version;
cc.ckm = pktns->crypto.tx.ckm;
cc.hp_ctx = pktns->crypto.tx.hp_ctx;
+
+ rv = conn_handle_skip_pkt(conn, pktns, ts);
+ if (rv != 0) {
+ return rv;
+ }
+
break;
default:
/* We don't support 0-RTT packet in this function. */
/* Just freeing memory is dangerous because we might free twice. */
- rv = ngtcp2_rtb_remove_all(rtb, conn, &conn->pktns, &conn->cstat);
+ rv = ngtcp2_rtb_reclaim_on_retry(rtb, conn, &conn->pktns, &conn->cstat);
if (rv != 0) {
return rv;
}
- rv = ngtcp2_rtb_remove_all(in_rtb, conn, in_pktns, &conn->cstat);
+ rv = ngtcp2_rtb_reclaim_on_retry(in_rtb, conn, in_pktns, &conn->cstat);
if (rv != 0) {
return rv;
}
num_acked =
ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pktns, pkt_ts, ts);
if (num_acked < 0) {
- assert(ngtcp2_err_is_fatal((int)num_acked));
return (int)num_acked;
}
hd->pkt_numlen = (size_t)((dest[0] & NGTCP2_PKT_NUMLEN_MASK) + 1);
- for (i = 0; i < hd->pkt_numlen; ++i) {
+ for (i = 0; i < 4; ++i) {
*p++ = *(pkt + pkt_num_offset + i) ^ mask[i + 1];
}
- hd->pkt_num = ngtcp2_get_pkt_num(p - hd->pkt_numlen, hd->pkt_numlen);
+ p -= 4;
+ hd->pkt_num = ngtcp2_get_pkt_num(p, hd->pkt_numlen);
+ p += hd->pkt_numlen;
return p - dest;
}
}
}
+ if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) {
+ ngtcp2_conn_add_path_history(conn, &conn->dcid.current, ts);
+ }
+
ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid);
conn_reset_congestion_state(conn, ts);
handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams,
size_t n) {
if (
-#if SIZE_MAX > UINT32_MAX
+#if SIZE_MAX == UINT64_MAX
NGTCP2_MAX_STREAMS < n ||
-#endif /* SIZE_MAX > UINT32_MAX */
+#endif /* SIZE_MAX == UINT64_MAX */
*punsent_max_remote_streams > (uint64_t)(NGTCP2_MAX_STREAMS - n)) {
*punsent_max_remote_streams = NGTCP2_MAX_STREAMS;
} else {
int new_cid_used,
ngtcp2_tstamp ts) {
ngtcp2_dcid dcid;
- ngtcp2_pv *pv;
+ ngtcp2_pv *pv = NULL;
int rv;
ngtcp2_duration pto;
int require_new_cid;
int local_addr_eq;
int pref_addr_migration;
uint32_t remote_addr_cmp;
+ const ngtcp2_path_history_entry *validated_path;
assert(conn->server);
/* Run PMTUD just in case if it is prematurely aborted */
assert(!conn->pmtud);
- return conn_start_pmtud(conn);
+ if (!conn->local.settings.no_pmtud) {
+ return conn_start_pmtud(conn);
+ }
+
+ return 0;
}
remote_addr_cmp =
dcid.bytes_recv += dgramlen;
- pto = conn_compute_pto(conn, &conn->pktns);
+ validated_path = ngtcp2_conn_find_path_history(conn, path, ts);
+ if (!validated_path) {
+ pto = conn_compute_pto(conn, &conn->pktns);
- rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout_pto(conn, pto),
- NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem);
- if (rv != 0) {
- return rv;
- }
+ rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout_pto(conn, pto),
+ NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
- if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT)) {
- ngtcp2_pv_set_fallback(pv, &conn->pv->fallback_dcid,
- conn->pv->fallback_pto);
- /* Unset the flag bit so that conn_stop_pv does not retire
- DCID. */
- conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_PRESENT;
- } else if (!pref_addr_migration) {
- ngtcp2_pv_set_fallback(pv, &conn->dcid.current, pto);
+ if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT)) {
+ ngtcp2_pv_set_fallback(pv, &conn->pv->fallback_dcid,
+ conn->pv->fallback_pto);
+ /* Unset the flag bit so that conn_stop_pv does not retire
+ DCID. */
+ conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_PRESENT;
+ } else if (!pref_addr_migration) {
+ ngtcp2_pv_set_fallback(pv, &conn->dcid.current, pto);
+ }
}
- if (!pref_addr_migration) {
+ if (!pref_addr_migration || validated_path) {
+ if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) {
+ ngtcp2_conn_add_path_history(conn, &conn->dcid.current, ts);
+ }
+
ngtcp2_dcid_copy(&conn->dcid.current, &dcid);
if (!local_addr_eq || (remote_addr_cmp & (NGTCP2_ADDR_CMP_FLAG_ADDR |
conn_reset_ecn_validation_state(conn);
ngtcp2_conn_stop_pmtud(conn);
+
+ if (validated_path) {
+ ngtcp2_dcid_apply_validated_path(&conn->dcid.current, validated_path);
+
+ if (!conn->local.settings.no_pmtud) {
+ rv = conn_start_pmtud(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ }
}
if (conn->pv) {
return 0;
}
-/*
- * conn_allow_path_change_under_disable_active_migration returns
- * nonzero if a packet from |path| is acceptable under
- * disable_active_migration is on.
- */
-static int
-conn_allow_path_change_under_disable_active_migration(ngtcp2_conn *conn,
- const ngtcp2_path *path) {
- uint32_t remote_addr_cmp;
-
- assert(conn->server);
- assert(conn->local.transport_params.disable_active_migration);
-
- /* If local address does not change, it must be passive migration
- (NAT rebinding). */
- if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local)) {
- remote_addr_cmp =
- ngtcp2_addr_cmp(&conn->dcid.current.ps.path.remote, &path->remote);
-
- return (remote_addr_cmp | NGTCP2_ADDR_CMP_FLAG_PORT) ==
- NGTCP2_ADDR_CMP_FLAG_PORT;
- }
-
- /* If local address changes, it must be one of the preferred
- addresses. */
- return conn_server_preferred_addr_migration(conn, &path->local);
-}
-
/*
* conn_recv_pkt processes a packet contained in the buffer pointed by
* |pkt| of length |pktlen|. |pkt| may contain multiple QUIC packets.
int path_challenge_recved = 0;
size_t num_ack_processed = 0;
- if (conn->server) {
- if (conn->local.transport_params.disable_active_migration &&
- !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) &&
- !conn_allow_path_change_under_disable_active_migration(conn, path)) {
- ngtcp2_log_info(
- &conn->log, NGTCP2_LOG_EVENT_PKT,
- "packet is discarded because local active migration is disabled");
-
- return NGTCP2_ERR_DISCARD_PKT;
- }
-
- assert(conn->remote.transport_params);
-
- if (conn->remote.transport_params->disable_active_migration &&
- !ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local) &&
- !conn_server_preferred_addr_migration(conn, &path->local)) {
- ngtcp2_log_info(
- &conn->log, NGTCP2_LOG_EVENT_PKT,
- "packet is discarded because remote active migration is disabled");
-
- return NGTCP2_ERR_DISCARD_PKT;
- }
- }
-
if (pkt[0] & NGTCP2_HEADER_FORM_BIT) {
nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen);
if (nread < 0) {
conn->dcid.current.bytes_recv += dgramlen;
}
+ if (conn->server && conn->local.transport_params.disable_active_migration &&
+ !ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local) &&
+ !conn_server_preferred_addr_migration(conn, &path->local)) {
+ ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT,
+ "packet is discarded because active migration is disabled");
+
+ return 0;
+ }
+
while (pktlen) {
nread = conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts);
if (nread < 0) {
*/
static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen,
- uint64_t write_datalen,
+ uint8_t wflags, uint64_t write_datalen,
ngtcp2_tstamp ts) {
int rv;
ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0;
if (pending_early_datalen) {
early_spktlen = conn_retransmit_retry_early(
conn, pi, dest + nwrite, destlen - (size_t)nwrite, (size_t)nwrite,
- nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING
- : NGTCP2_WRITE_PKT_FLAG_NONE,
+ wflags | (nwrite ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING
+ : NGTCP2_WRITE_PKT_FLAG_NONE),
ts);
if (early_spktlen < 0) {
if (pending_early_datalen &&
!(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) {
nwrite = conn_retransmit_retry_early(
- conn, pi, dest, destlen, (size_t)res, NGTCP2_WRITE_PKT_FLAG_NONE, ts);
+ conn, pi, dest, destlen, (size_t)res,
+ wflags | ((nwrite &&
+ ngtcp2_pkt_get_type_long(
+ conn->negotiated_version ? conn->negotiated_version
+ : conn->client_chosen_version,
+ *(dest - nwrite)) == NGTCP2_PKT_INITIAL)
+ ? NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING
+ : NGTCP2_WRITE_PKT_FLAG_NONE),
+ ts);
if (nwrite < 0) {
return nwrite;
}
* pointed by |dest| if it succeeds, or one of the following negative
* error codes: (TBD).
*/
-static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn,
- ngtcp2_pkt_info *pi,
- uint8_t *dest, size_t destlen,
- ngtcp2_vmsg *vmsg,
- ngtcp2_tstamp ts) {
+static ngtcp2_ssize
+conn_client_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen, uint8_t wflags,
+ ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) {
int send_stream = 0;
int send_datagram = 0;
ngtcp2_ssize spktlen, early_spktlen;
uint64_t datalen;
uint64_t write_datalen = 0;
- uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
uint32_t version;
}
if (!ppe_pending) {
- spktlen = conn_write_handshake(conn, pi, dest, destlen, write_datalen, ts);
+ spktlen =
+ conn_write_handshake(conn, pi, dest, destlen, wflags, write_datalen, ts);
if (spktlen < 0) {
return spktlen;
if (conn->in_pktns) {
ts = ngtcp2_rtb_lost_pkt_ts(&conn->in_pktns->rtb);
if (ts != UINT64_MAX) {
- ts += conn_compute_pto(conn, conn->in_pktns);
+ ts += conn_compute_pto(conn, conn->in_pktns) * 3;
res = ngtcp2_min_uint64(res, ts);
}
}
if (conn->hs_pktns) {
ts = ngtcp2_rtb_lost_pkt_ts(&conn->hs_pktns->rtb);
if (ts != UINT64_MAX) {
- ts += conn_compute_pto(conn, conn->hs_pktns);
+ ts += conn_compute_pto(conn, conn->hs_pktns) * 3;
res = ngtcp2_min_uint64(res, ts);
}
}
ts = ngtcp2_rtb_lost_pkt_ts(&conn->pktns.rtb);
if (ts != UINT64_MAX) {
- ts += conn_compute_pto(conn, &conn->pktns);
+ ts += conn_compute_pto(conn, &conn->pktns) * 3;
res = ngtcp2_min_uint64(res, ts);
}
}
void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) {
- ngtcp2_duration pto;
+ ngtcp2_duration timeout;
if (conn->in_pktns) {
- pto = conn_compute_pto(conn, conn->in_pktns);
- ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, pto, ts);
+ timeout = conn_compute_pto(conn, conn->in_pktns) * 3;
+ ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, timeout, ts);
}
if (conn->hs_pktns) {
- pto = conn_compute_pto(conn, conn->hs_pktns);
- ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, pto, ts);
+ timeout = conn_compute_pto(conn, conn->hs_pktns) * 3;
+ ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, timeout, ts);
}
- pto = conn_compute_pto(conn, &conn->pktns);
- ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts);
+ timeout = conn_compute_pto(conn, &conn->pktns) * 3;
+ ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, timeout, ts);
}
/*
ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, ngtcp2_ssize *pdatalen,
uint32_t flags, int64_t stream_id, const uint8_t *data, size_t datalen,
ngtcp2_tstamp ts) {
- ngtcp2_vec datav;
+ ngtcp2_vec datav, *v;
+ size_t datacnt;
- datav.len = datalen;
- datav.base = (uint8_t *)data;
+ if (datalen == 0) {
+ v = NULL;
+ datacnt = 0;
+ } else {
+ datav.len = datalen;
+ datav.base = (uint8_t *)data;
+ v = &datav;
+ datacnt = 1;
+ }
return ngtcp2_conn_writev_stream_versioned(conn, path, pkt_info_version, pi,
dest, destlen, pdatalen, flags,
- stream_id, &datav, 1, ts);
+ stream_id, v, datacnt, ts);
}
-static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn,
- ngtcp2_path *path,
- int pkt_info_version,
- ngtcp2_pkt_info *pi, uint8_t *dest,
- size_t destlen, ngtcp2_vmsg *vmsg,
- ngtcp2_tstamp ts) {
+static ngtcp2_ssize
+conn_write_vmsg_wrapper(ngtcp2_conn *conn, ngtcp2_path *path,
+ int pkt_info_version, ngtcp2_pkt_info *pi,
+ uint8_t *dest, size_t destlen, uint8_t wflags,
+ ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) {
ngtcp2_conn_stat *cstat = &conn->cstat;
ngtcp2_ssize nwrite;
nwrite = ngtcp2_conn_write_vmsg(conn, path, pkt_info_version, pi, dest,
- destlen, vmsg, ts);
+ destlen, wflags, vmsg, ts);
if (nwrite < 0) {
return nwrite;
}
ngtcp2_vmsg vmsg, *pvmsg;
ngtcp2_strm *strm;
int64_t datalen;
+ uint8_t wflags;
if (pdatalen) {
*pdatalen = -1;
pvmsg = NULL;
}
+ if (flags & NGTCP2_WRITE_STREAM_FLAG_PADDING) {
+ wflags = NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY;
+ } else {
+ wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
+ }
+
return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest,
- destlen, pvmsg, ts);
+ destlen, wflags, pvmsg, ts);
}
ngtcp2_ssize ngtcp2_conn_write_datagram_versioned(
ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, int *paccepted,
uint32_t flags, uint64_t dgram_id, const uint8_t *data, size_t datalen,
ngtcp2_tstamp ts) {
- ngtcp2_vec datav;
+ ngtcp2_vec datav, *v;
+ size_t datacnt;
- datav.len = datalen;
- datav.base = (uint8_t *)data;
+ if (datalen == 0) {
+ v = NULL;
+ datacnt = 0;
+ } else {
+ datav.len = datalen;
+ datav.base = (uint8_t *)data;
+ v = &datav;
+ datacnt = 1;
+ }
return ngtcp2_conn_writev_datagram_versioned(conn, path, pkt_info_version, pi,
dest, destlen, paccepted, flags,
- dgram_id, &datav, 1, ts);
+ dgram_id, v, datacnt, ts);
}
ngtcp2_ssize ngtcp2_conn_writev_datagram_versioned(
ngtcp2_tstamp ts) {
ngtcp2_vmsg vmsg;
int64_t datalen;
+ uint8_t wflags;
if (paccepted) {
*paccepted = 0;
datalen = ngtcp2_vec_len_varint(datav, datavcnt);
if (datalen == -1
-#if UINT64_MAX > SIZE_MAX
+#if SIZE_MAX < UINT64_MAX
|| (uint64_t)datalen > SIZE_MAX
-#endif /* UINT64_MAX > SIZE_MAX */
+#endif /* SIZE_MAX < UINT64_MAX */
) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
vmsg.datagram.datacnt = datavcnt;
vmsg.datagram.paccepted = paccepted;
+ if (flags & NGTCP2_WRITE_DATAGRAM_FLAG_PADDING) {
+ wflags = NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY;
+ } else {
+ wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
+ }
+
return conn_write_vmsg_wrapper(conn, path, pkt_info_version, pi, dest,
- destlen, &vmsg, ts);
+ destlen, wflags, &vmsg, ts);
}
ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
int pkt_info_version, ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen,
- ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts) {
+ uint8_t wflags, ngtcp2_vmsg *vmsg,
+ ngtcp2_tstamp ts) {
ngtcp2_ssize nwrite;
size_t origlen;
size_t origdestlen = destlen;
int rv;
- uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE;
int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0;
ngtcp2_conn_stat *cstat = &conn->cstat;
ngtcp2_ssize res = 0;
return conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts);
}
- nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts);
+ nwrite =
+ conn_client_write_handshake(conn, pi, dest, destlen, wflags, vmsg, ts);
/* We might be unable to write a packet because of depletion of
congestion window budget, perhaps due to packet loss that
shrinks the window drastically. */
}
}
- nwrite = conn_write_handshake(conn, pi, dest, destlen,
+ nwrite = conn_write_handshake(conn, pi, dest, destlen, wflags,
/* write_datalen = */ 0, ts);
if (nwrite < 0) {
return nwrite;
int rv;
ngtcp2_dcid dcid;
ngtcp2_pv *pv;
+ const ngtcp2_path_history_entry *validated_path;
assert(!conn->server);
ngtcp2_dcidtr_pop_unused(&conn->dcid.dtr, &dcid);
ngtcp2_dcid_set_path(&dcid, path);
+ if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) {
+ ngtcp2_conn_add_path_history(conn, &conn->dcid.current, ts);
+ }
+
ngtcp2_dcid_copy(&conn->dcid.current, &dcid);
conn_reset_congestion_state(conn, ts);
conn_reset_ecn_validation_state(conn);
- /* TODO It might be better to add a new flag which indicates that a
- connection should be closed if this path validation failed. The
- current design allows an application to continue, by migrating
- into yet another path. */
- rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout(conn),
- NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem);
- if (rv != 0) {
- return rv;
- }
+ validated_path = ngtcp2_conn_find_path_history(conn, path, ts);
+ if (validated_path) {
+ ngtcp2_dcid_apply_validated_path(&conn->dcid.current, validated_path);
- conn->pv = pv;
+ if (!conn->local.settings.no_pmtud) {
+ rv = conn_start_pmtud(conn);
+ if (rv != 0) {
+ return rv;
+ }
+ }
+ } else {
+ /* TODO It might be better to add a new flag which indicates that
+ a connection should be closed if this path validation failed.
+ The current design allows an application to continue, by
+ migrating into yet another path. */
+ rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout(conn),
+ NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem);
+ if (rv != 0) {
+ return rv;
+ }
+
+ conn->pv = pv;
+ }
return conn_call_activate_dcid(conn, &conn->dcid.current);
}
assert(!conn->server);
+ if (ngtcp2_conn_find_path_history(conn, path, ts)) {
+ return ngtcp2_conn_initiate_immediate_migration(conn, path, ts);
+ }
+
conn_update_timestamp(conn, ts);
rv = conn_initiate_migration_precheck(conn, &path->local);
return strm->tx.loss_count;
}
+void ngtcp2_conn_add_path_history(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
+ ngtcp2_tstamp ts) {
+ ngtcp2_path_history_entry *ent;
+
+ ent = ngtcp2_ringbuf_push_front(&conn->path_history.rb);
+ ngtcp2_path_storage_init2(&ent->ps, &dcid->ps.path);
+ ent->max_udp_payload_size = dcid->max_udp_payload_size;
+ ent->ts = ts;
+}
+
+const ngtcp2_path_history_entry *
+ngtcp2_conn_find_path_history(ngtcp2_conn *conn, const ngtcp2_path *path,
+ ngtcp2_tstamp ts) {
+ ngtcp2_ringbuf *rb = &conn->path_history.rb;
+ size_t i, len = ngtcp2_ringbuf_len(rb);
+ ngtcp2_path_history_entry *ent;
+
+ for (i = 0; i < len; ++i) {
+ ent = ngtcp2_ringbuf_get(rb, i);
+ if (ngtcp2_tstamp_elapsed(ent->ts, 10 * NGTCP2_MINUTES, ts)) {
+ return NULL;
+ }
+
+ if (ngtcp2_path_eq(path, &ent->ps.path)) {
+ return ent;
+ }
+ }
+
+ return NULL;
+}
+
void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent,
const ngtcp2_path *path,
const uint8_t *data) {
NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING, but it requests to add
padding to the full UDP datagram payload size. */
#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING_FULL 0x04u
+/* NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY adds padding to the QUIC
+ packet as much as possible if the packet is not empty. */
+#define NGTCP2_WRITE_PKT_FLAG_PADDING_IF_NOT_EMPTY 0x08u
/*
* ngtcp2_max_frame is defined so that it covers the largest ACK
/* last_pkt_num is the packet number which the local endpoint sent
last time.*/
int64_t last_pkt_num;
+ struct {
+ /* next_pkt_num is the next packet number to skip. */
+ int64_t next_pkt_num;
+ /* exponent makes gap of skipping packets spread
+ exponentially. */
+ int64_t exponent;
+ } skip_pkt;
ngtcp2_frame_chain *frq;
/* non_ack_pkt_start_ts is the timestamp since the local endpoint
starts sending continuous non ACK-eliciting packets. */
ngtcp2_static_ringbuf_def(path_challenge, 4,
sizeof(ngtcp2_path_challenge_entry))
+ngtcp2_static_ringbuf_def(path_history, 4, sizeof(ngtcp2_path_history_entry))
+
ngtcp2_objalloc_decl(strm, ngtcp2_strm, oplent)
struct ngtcp2_conn {
ngtcp2_cc_cubic cubic;
ngtcp2_cc_bbr bbr;
};
+ /* path_history remembers the paths that have been validated
+ successfully. The path is added to this history when a local
+ endpoint migrates to the another path. */
+ ngtcp2_static_ringbuf_path_history path_history;
const ngtcp2_mem *mem;
/* idle_ts is the time instant when idle timer started. */
ngtcp2_tstamp idle_ts;
ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path,
int pkt_info_version, ngtcp2_pkt_info *pi,
uint8_t *dest, size_t destlen,
- ngtcp2_vmsg *vmsg, ngtcp2_tstamp ts);
+ uint8_t wflags, ngtcp2_vmsg *vmsg,
+ ngtcp2_tstamp ts);
/*
* ngtcp2_conn_write_single_frame_pkt writes a packet which contains
*/
void ngtcp2_conn_discard_handshake_state(ngtcp2_conn *conn, ngtcp2_tstamp ts);
+void ngtcp2_conn_add_path_history(ngtcp2_conn *conn, const ngtcp2_dcid *dcid,
+ ngtcp2_tstamp ts);
+
+const ngtcp2_path_history_entry *
+ngtcp2_conn_find_path_history(ngtcp2_conn *conn, const ngtcp2_path *path,
+ ngtcp2_tstamp ts);
+
#endif /* !defined(NGTCP2_CONN_H) */
return 0;
}
-static int verify_stateless_reset(const ngtcp2_ringbuf *rb,
- const ngtcp2_path *path,
- const uint8_t *token) {
+int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr,
+ const ngtcp2_path *path,
+ const uint8_t *token) {
const ngtcp2_dcid *dcid;
+ const ngtcp2_ringbuf *rb = &dtr->bound.rb;
size_t i, len = ngtcp2_ringbuf_len(rb);
for (i = 0; i < len; ++i) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
-int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr,
- const ngtcp2_path *path,
- const uint8_t *token) {
- int rv;
-
- rv = verify_stateless_reset(&dtr->retired.rb, path, token);
- if (rv == 0) {
- return 0;
- }
-
- return verify_stateless_reset(&dtr->bound.rb, path, token);
-}
-
static int verify_token_uniqueness(const ngtcp2_ringbuf *rb, int *pfound,
uint64_t seq, const ngtcp2_cid *cid,
const uint8_t *token) {
*/
int ngtcp2_dcidtr_bound_full(const ngtcp2_dcidtr *dtr);
-#endif /* NGTCP2_DCIDTR_H */
+#endif /* !defined(NGTCP2_DCIDTR_H) */
}
ngtcp2_get_uvarint(&vi, p);
-#if SIZE_MAX > UINT32_MAX
+#if SIZE_MAX < UINT64_MAX
if (vi > SIZE_MAX) {
return NGTCP2_ERR_INVALID_ARGUMENT;
}
-#endif /* SIZE_MAX > UINT32_MAX */
+#endif /* SIZE_MAX < UINT64_MAX */
longlen = (size_t)vi;
}
}
for (i = 0; i < fr->datacnt; ++i) {
- assert(fr->data[i].len);
- assert(fr->data[i].base);
+ if (fr->data[i].len == 0) {
+ continue;
+ }
+
p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len);
}
left -= n;
if (left > 8 + 1073741823 && len > 1073741823) {
-#if SIZE_MAX > UINT32_MAX
len = ngtcp2_min_uint64(len, 4611686018427387903lu);
-#endif /* SIZE_MAX > UINT32_MAX */
return (size_t)ngtcp2_min_uint64(len, (uint64_t)(left - 8));
}
left -= n;
if (left > 8 + 1073741823 && len > 1073741823) {
-#if SIZE_MAX > UINT32_MAX
+#if SIZE_MAX == UINT64_MAX
len = ngtcp2_min_size(len, 4611686018427387903lu);
-#endif /* SIZE_MAX > UINT32_MAX */
+#endif /* SIZE_MAX == UINT64_MAX */
return ngtcp2_min_size(len, left - 8);
}
int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) {
ngtcp2_ssize rv;
ngtcp2_buf *buf = &ppe->buf;
+ size_t buf_left = ngtcp2_buf_left(buf);
ngtcp2_crypto_cc *cc = ppe->cc;
- if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) {
+ if (buf_left <= cc->aead.max_overhead) {
return NGTCP2_ERR_NOBUF;
}
ppe->pkt_num_offset = ppe->len_offset + NGTCP2_PKT_LENGTHLEN;
- rv = ngtcp2_pkt_encode_hd_long(
- buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd);
+ rv = ngtcp2_pkt_encode_hd_long(buf->last, buf_left - cc->aead.max_overhead,
+ hd);
} else {
ppe->pkt_num_offset = 1 + hd->dcid.datalen;
- rv = ngtcp2_pkt_encode_hd_short(
- buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd);
+ rv = ngtcp2_pkt_encode_hd_short(buf->last, buf_left - cc->aead.max_overhead,
+ hd);
}
if (rv < 0) {
int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr) {
ngtcp2_ssize rv;
ngtcp2_buf *buf = &ppe->buf;
+ size_t buf_left = ngtcp2_buf_left(buf);
ngtcp2_crypto_cc *cc = ppe->cc;
- if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) {
+ if (buf_left <= cc->aead.max_overhead) {
return NGTCP2_ERR_NOBUF;
}
- rv = ngtcp2_pkt_encode_frame(
- buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, fr);
+ rv = ngtcp2_pkt_encode_frame(buf->last, buf_left - cc->aead.max_overhead, fr);
if (rv < 0) {
return (int)rv;
}
size_t ngtcp2_ppe_left(const ngtcp2_ppe *ppe) {
ngtcp2_crypto_cc *cc = ppe->cc;
+ size_t buf_left = ngtcp2_buf_left(&ppe->buf);
- if (ngtcp2_buf_left(&ppe->buf) < cc->aead.max_overhead) {
+ if (buf_left <= cc->aead.max_overhead) {
return 0;
}
- return ngtcp2_buf_left(&ppe->buf) - cc->aead.max_overhead;
-}
-
-size_t ngtcp2_ppe_pktlen(const ngtcp2_ppe *ppe) {
- ngtcp2_crypto_cc *cc = ppe->cc;
-
- return ngtcp2_buf_len(&ppe->buf) + cc->aead.max_overhead;
+ return buf_left - cc->aead.max_overhead;
}
size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n) {
assert(ngtcp2_buf_left(buf) >= len + cc->aead.max_overhead);
+ if (len == 0) {
+ return 0;
+ }
+
buf->last = ngtcp2_setmem(buf->last, 0, len);
return len;
*/
size_t ngtcp2_ppe_left(const ngtcp2_ppe *ppe);
-/*
- * ngtcp2_ppe_pktlen returns the provisional packet length. It
- * includes AEAD overhead.
- */
-size_t ngtcp2_ppe_pktlen(const ngtcp2_ppe *ppe);
-
/*
* ngtcp2_ppe_dgram_padding is equivalent to call
* ngtcp2_ppe_dgram_padding_size(ppe, NGTCP2_MAX_UDP_PAYLOAD_SIZE).
rtb->cc_pkt_num = cc_pkt_num;
rtb->cc_bytes_in_flight = 0;
rtb->num_lost_pkts = 0;
- rtb->num_lost_pmtud_pkts = 0;
+ rtb->num_lost_ignore_pkts = 0;
}
void ngtcp2_rtb_free(ngtcp2_rtb *rtb) {
assert(rtb->num_lost_pkts);
--rtb->num_lost_pkts;
- if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
- assert(rtb->num_lost_pmtud_pkts);
- --rtb->num_lost_pmtud_pkts;
+ if (ent->flags &
+ (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) {
+ assert(rtb->num_lost_ignore_pkts);
+ --rtb->num_lost_ignore_pkts;
}
return 0;
fr->stream.offset + ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt);
range = ngtcp2_range_intersect(&range, &gap);
- if (ngtcp2_range_len(&range) == 0) {
- if (!fr->stream.fin) {
+ if (ngtcp2_range_len(&range) == 0 && !fr->stream.fin &&
/* 0 length STREAM frame with offset == 0 must be
retransmitted if no non-empty data are sent to this
stream, fin flag is not set, and no data in this stream
are acknowledged. */
- if (fr->stream.offset != 0 || fr->stream.datacnt != 0 ||
- strm->tx.offset ||
- (strm->flags &
- (NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_ANY_ACKED))) {
- continue;
- }
- } else if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) {
- continue;
- }
+ (fr->stream.offset != 0 || fr->stream.datacnt != 0 ||
+ strm->tx.offset ||
+ (strm->flags &
+ (NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_ANY_ACKED)))) {
+ continue;
}
if ((flags & NGTCP2_RECLAIM_FLAG_ON_LOSS) &&
ngtcp2_cc *cc = rtb->cc;
ngtcp2_cc_pkt pkt;
- ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
- ent->ts);
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP)) {
+ ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
+ ent->ts);
- if (rtb->qlog) {
- ngtcp2_qlog_pkt_lost(rtb->qlog, ent);
+ if (rtb->qlog) {
+ ngtcp2_qlog_pkt_lost(rtb->qlog, ent);
+ }
}
- if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
- ++rtb->num_lost_pmtud_pkts;
+ 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,
size_t ecn_acked = 0;
int verify_ecn = 0;
ngtcp2_cc_ack cc_ack = {0};
- size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_pmtud_pkts;
+ size_t num_lost_pkts = rtb->num_lost_pkts - rtb->num_lost_ignore_pkts;
cc_ack.prior_bytes_in_flight = cstat->bytes_in_flight;
cc_ack.rtt = UINT64_MAX;
ent = ngtcp2_ksl_it_get(&it);
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP) {
+ rv = NGTCP2_ERR_PROTO;
+ goto fail;
+ }
+
if (largest_ack == pkt_num) {
largest_pkt_sent_ts = ent->ts;
}
ent = ngtcp2_ksl_it_get(&it);
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP) {
+ rv = NGTCP2_ERR_PROTO;
+ goto fail;
+ }
+
if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) {
ack_eliciting_pkt_acked = 1;
}
}
if (rtb->cc->on_spurious_congestion && num_lost_pkts &&
- rtb->num_lost_pkts == rtb->num_lost_pmtud_pkts) {
+ rtb->num_lost_pkts == rtb->num_lost_ignore_pkts) {
rtb->cc->on_spurious_congestion(cc, cstat, ts);
}
--rtb->num_lost_pkts;
- if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
- --rtb->num_lost_pmtud_pkts;
+ if (ent->flags &
+ (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) {
+ --rtb->num_lost_ignore_pkts;
}
rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
}
}
-void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
+void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb,
+ ngtcp2_duration timeout,
ngtcp2_tstamp ts) {
ngtcp2_ksl_it it;
ngtcp2_rtb_entry *ent;
int rv;
(void)rv;
- if (ngtcp2_ksl_len(&rtb->ents) == 0) {
+ if (rtb->num_lost_pkts == 0) {
return;
}
it = ngtcp2_ksl_end(&rtb->ents);
- for (;;) {
+ for (; rtb->num_lost_pkts;) {
assert(ngtcp2_ksl_it_end(&it));
ngtcp2_ksl_it_prev(&it);
ent = ngtcp2_ksl_it_get(&it);
if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) ||
- ts - ent->lost_ts < pto) {
+ ts - ent->lost_ts < timeout) {
return;
}
--rtb->num_lost_pkts;
- if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
- --rtb->num_lost_pmtud_pkts;
+ if (ent->flags &
+ (NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE | NGTCP2_RTB_ENTRY_FLAG_SKIP)) {
+ --rtb->num_lost_ignore_pkts;
}
rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
assert(0 == rv);
ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
rtb->frc_objalloc, rtb->mem);
-
- if (ngtcp2_ksl_len(&rtb->ents) == 0) {
- return;
- }
}
}
return ent->lost_ts;
}
-static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
- ngtcp2_pktns *pktns,
- ngtcp2_rtb_entry *ent) {
- ngtcp2_frame_chain **pfrc, *frc;
+static int rtb_reclaim_frame_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns,
+ ngtcp2_rtb_entry *ent) {
+ ngtcp2_frame_chain **pfrc = &ent->frc, *frc;
ngtcp2_stream *sfr;
ngtcp2_strm *strm;
int rv;
- ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags,
- ent->ts);
-
- if (rtb->qlog) {
- ngtcp2_qlog_pkt_lost(rtb->qlog, ent);
- }
-
- if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) {
- ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC,
- "pkn=%" PRId64
- " is a probe packet, no retransmission is necessary",
- ent->hd.pkt_num);
- return 0;
- }
-
- if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) {
- --rtb->num_lost_pkts;
-
- if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE) {
- --rtb->num_lost_pmtud_pkts;
- }
-
- ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC,
- "pkn=%" PRId64
- " was declared lost and has already been retransmitted",
- ent->hd.pkt_num);
- return 0;
- }
-
- if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
- ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC,
- "pkn=%" PRId64 " has already been reclaimed on PTO",
- ent->hd.pkt_num);
- return 0;
- }
-
- if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) &&
- (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) ||
- !conn->callbacks.lost_datagram)) {
- /* PADDING only (or PADDING + ACK ) packets will have NULL
- ent->frc. */
- return 0;
- }
-
- pfrc = &ent->frc;
-
for (; *pfrc;) {
switch ((*pfrc)->fr.type) {
case NGTCP2_FRAME_STREAM:
return 0;
}
-int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
- ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) {
+int ngtcp2_rtb_reclaim_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) {
ngtcp2_rtb_entry *ent;
- ngtcp2_ksl_it it;
+ ngtcp2_ksl_it it = ngtcp2_ksl_begin(&rtb->ents);
int rv;
- it = ngtcp2_ksl_begin(&rtb->ents);
-
for (; !ngtcp2_ksl_it_end(&it);) {
ent = ngtcp2_ksl_it_get(&it);
rv = ngtcp2_ksl_remove_hint(&rtb->ents, &it, &it, &ent->hd.pkt_num);
assert(0 == rv);
- rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent);
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_SKIP)) {
+ ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type,
+ ent->hd.flags, ent->ts);
+
+ if (rtb->qlog) {
+ ngtcp2_qlog_pkt_lost(rtb->qlog, ent);
+ }
+ }
+
+ /* We never send PING only probe packet because we should have
+ CRYPTO data or just nothing. If we have nothing, then we do
+ not send probe packet. */
+ assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE));
+
+ /* We never get ACK before Retry packet. */
+ assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED));
+ assert(0 == rtb->num_lost_pkts);
+ assert(0 == rtb->num_lost_ignore_pkts);
+
+ /* PMTUD probe must not be sent before handshake completion. */
+ assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE));
+
+ if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) {
+ ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_LDC,
+ "pkn=%" PRId64 " has already been reclaimed on PTO",
+ ent->hd.pkt_num);
+ continue;
+ }
+
+ if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) &&
+ (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_DATAGRAM) ||
+ !conn->callbacks.lost_datagram)) {
+ continue;
+ }
+
+ rv = rtb_reclaim_frame_on_retry(rtb, conn, pktns, ent);
ngtcp2_rtb_entry_objalloc_del(ent, rtb->rtb_entry_objalloc,
rtb->frc_objalloc, rtb->mem);
/* NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING indicates that the entry
includes a packet which elicits PTO probe packets. */
#define NGTCP2_RTB_ENTRY_FLAG_PTO_ELICITING 0x100u
+/* NGTCP2_RTB_ENTRY_FLAG_SKIP indicates that the entry has the skipped
+ packet number. */
+#define NGTCP2_RTB_ENTRY_FLAG_SKIP 0x200u
typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry;
/* num_lost_pkts is the number entries in ents which has
NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */
size_t num_lost_pkts;
- /* num_lost_pmtud_pkts is the number of entries in ents which have
- both NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED and
- NGTCP2_RTB_ENTRY_FLAG_PMTUD_PROBE flags set. */
- size_t num_lost_pmtud_pkts;
+ /* num_lost_ignore_pkts is the number of entries in ents which have
+ NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set, and should be
+ excluded from lost byte count. If only those packets are lost,
+ congestion event is not triggered. */
+ size_t num_lost_ignore_pkts;
} ngtcp2_rtb;
/*
/*
* ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet.
*/
-void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto,
+void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb,
+ ngtcp2_duration timeout,
ngtcp2_tstamp ts);
/*
ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(const ngtcp2_rtb *rtb);
/*
- * ngtcp2_rtb_remove_all removes all packets from |rtb|, and prepends
- * all frames to |*pfrc|. Even when this function fails, some frames
- * might be prepended to |*pfrc|, and the caller should handle them.
+ * ngtcp2_rtb_reclaim_on_retry is called when Retry packet is
+ * received. It removes all packets from |rtb|, and retransmittable
+ * frames are reclaimed for retransmission.
*/
-int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
- ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat);
+int ngtcp2_rtb_reclaim_on_retry(ngtcp2_rtb *rtb, ngtcp2_conn *conn,
+ ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat);
/*
* ngtcp2_rtb_remove_early_data removes all entries for 0RTT packets.
/**
* @macro
*
- * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1
- * second.
+ * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to
+ * 1 nanosecond.
+ */
+#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds
+ * to 1 microsecond.
*/
-#define NGTCP2_SECONDS ((ngtcp2_duration)1000000000ULL)
+#define NGTCP2_MICROSECONDS ((ngtcp2_duration)(1000ULL * NGTCP2_NANOSECONDS))
/**
* @macro
* :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds
* to 1 millisecond.
*/
-#define NGTCP2_MILLISECONDS ((ngtcp2_duration)1000000ULL)
+#define NGTCP2_MILLISECONDS ((ngtcp2_duration)(1000ULL * NGTCP2_MICROSECONDS))
/**
* @macro
*
- * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds
- * to 1 microsecond.
+ * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1
+ * second.
*/
-#define NGTCP2_MICROSECONDS ((ngtcp2_duration)1000ULL)
+#define NGTCP2_SECONDS ((ngtcp2_duration)(1000ULL * NGTCP2_MILLISECONDS))
/**
* @macro
*
- * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to
- * 1 nanosecond.
+ * :macro:`NGTCP2_MINUTES` is a count of tick which corresponds to 1
+ * minute.
*/
-#define NGTCP2_NANOSECONDS ((ngtcp2_duration)1ULL)
+#define NGTCP2_MINUTES ((ngtcp2_duration)(60ULL * NGTCP2_SECONDS))
/**
* @macrosection
* :type:`ngtcp2_rand` is a callback function to get random data of
* length |destlen|. Application must fill random |destlen| bytes to
* the buffer pointed by |dest|. The generated data is used only in
- * non-cryptographic context.
+ * non-cryptographic context. But it is strongly recommended to use a
+ * secure random number generator.
*/
typedef void (*ngtcp2_rand)(uint8_t *dest, size_t destlen,
const ngtcp2_rand_ctx *rand_ctx);
*/
#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02u
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_WRITE_STREAM_FLAG_PADDING` indicates that a
+ * non-empty 0 RTT or 1 RTT packet is padded to the minimum length of
+ * a sending path MTU or a given packet buffer when finalizing it.
+ * ACK, PATH_CHALLENGE, PATH_RESPONSE, CONNECTION_CLOSE only packets
+ * and PMTUD packets are excluded.
+ */
+#define NGTCP2_WRITE_STREAM_FLAG_PADDING 0x04u
+
/**
* @function
*
* include, call this function with |stream_id| as -1 to stop
* coalescing and write a packet.
*
+ * If :macro:`NGTCP2_WRITE_STREAM_FLAG_PADDING` is set in |flags| when
+ * finalizing a non-empty 0 RTT or 1 RTT packet, the packet is padded
+ * to the minimum length of a sending path MTU or a given packet
+ * buffer.
+ *
* This function returns 0 if it cannot write any frame because buffer
* is too small, or packet is congestion limited. Application should
* keep reading and wait for congestion window to grow.
*/
#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01u
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_PADDING` indicates that a
+ * non-empty 0 RTT or 1 RTT packet is padded to the minimum length of
+ * a sending path MTU or a given packet buffer when finalizing it.
+ * ACK, PATH_CHALLENGE, PATH_RESPONSE, CONNECTION_CLOSE only packets
+ * and PMTUD packets are excluded.
+ */
+#define NGTCP2_WRITE_DATAGRAM_FLAG_PADDING 0x02u
+
/**
* @function
*
* (or `ngtcp2_conn_writev_stream`) until it returns a positive number
* (which indicates a complete packet is ready).
*
+ * If :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_PADDING` is set in |flags|
+ * when finalizing a non-empty 0 RTT or 1 RTT packet, the packet is
+ * padded to the minimum length of a sending path MTU or a given
+ * packet buffer.
+ *
* This function returns the number of bytes written in |dest| if it
* succeeds, or one of the following negative error codes:
*
*/
#define NGTCP2_CRYPTO_ERR_VERIFY_TOKEN -203
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_ERR_NOMEM` indicates out of memory.
+ */
+#define NGTCP2_CRYPTO_ERR_NOMEM -501
+
/**
* @function
*
*
* Version number of the ngtcp2 library release.
*/
-#define NGTCP2_VERSION "1.11.0"
+#define NGTCP2_VERSION "1.12.0"
/**
* @macro
* number, 8 bits for minor and 8 bits for patch. Version 1.2.3
* becomes 0x010203.
*/
-#define NGTCP2_VERSION_NUM 0x010b00
+#define NGTCP2_VERSION_NUM 0x010c00
#endif /* !defined(NGTCP2_VERSION_H) */