From: Daniel Salzman Date: Sat, 23 Aug 2025 06:41:27 +0000 (+0200) Subject: libngtcp2: update embedded library to v1.15.0 X-Git-Tag: v3.5.0~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4c7fc5a19f763bebb6e3f9cda48707571d5c607f;p=thirdparty%2Fknot-dns.git libngtcp2: update embedded library to v1.15.0 --- diff --git a/Knot.files b/Knot.files index 307c066eb7..f16de73ffd 100644 --- a/Knot.files +++ b/Knot.files @@ -32,8 +32,6 @@ src/contrib/libngtcp2/ngtcp2/crypto/shared.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.c -src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c -src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_addr.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_balloc.h @@ -41,6 +39,8 @@ src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h +src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_callbacks.c +src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_callbacks.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cc.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_cid.c @@ -52,6 +52,8 @@ src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conv.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_crypto.h +src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c +src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_err.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_frame_chain.c @@ -76,6 +78,8 @@ src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h +src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pcg.c +src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pcg.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pktns_id.h @@ -91,6 +95,8 @@ src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h +src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ratelim.c +src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ratelim.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h diff --git a/src/contrib/Makefile.inc b/src/contrib/Makefile.inc index 36832c8ffa..0855764d21 100644 --- a/src/contrib/Makefile.inc +++ b/src/contrib/Makefile.inc @@ -180,6 +180,8 @@ libembngtcp2_la_SOURCES = \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_opl.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_path.h \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_pcg.c \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_pcg.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_pktns_id.h \ @@ -195,6 +197,8 @@ libembngtcp2_la_SOURCES = \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_qlog.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_range.h \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_ratelim.c \ + contrib/libngtcp2/ngtcp2/lib/ngtcp2_ratelim.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_rcvry.h \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c \ contrib/libngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h \ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c index 776dc0c2c3..59bc621ef4 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_acktr.c @@ -316,9 +316,9 @@ void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) { } void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) { - acktr->flags &= (uint16_t) ~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK | - NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK | - NGTCP2_ACKTR_FLAG_CANCEL_TIMER); + acktr->flags &= + (uint16_t)~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK | NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK | + NGTCP2_ACKTR_FLAG_CANCEL_TIMER); acktr->first_unacked_ts = UINT64_MAX; acktr->rx_npkt = 0; } diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c index a2ffeb6188..44be1e189b 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.c @@ -33,6 +33,7 @@ #include "ngtcp2_rcvry.h" #include "ngtcp2_rst.h" #include "ngtcp2_conn_stat.h" +#include "ngtcp2_pcg.h" #define NGTCP2_BBR_MAX_BW_FILTERLEN 2 @@ -906,15 +907,9 @@ static int bbr_is_time_to_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, } static void bbr_pick_probe_wait(ngtcp2_cc_bbr *bbr) { - uint8_t rand; - - bbr->rand(&rand, 1, &bbr->rand_ctx); - - bbr->rounds_since_bw_probe = (uint64_t)(rand / 128); - - bbr->rand(&rand, 1, &bbr->rand_ctx); - - bbr->bw_probe_wait = 2 * NGTCP2_SECONDS + NGTCP2_SECONDS * rand / 255; + bbr->rounds_since_bw_probe = ngtcp2_pcg32_rand_n(bbr->pcg, 2); + bbr->bw_probe_wait = + 2 * NGTCP2_SECONDS + ngtcp2_pcg32_rand_n(bbr->pcg, NGTCP2_SECONDS + 1); } static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr, @@ -1388,8 +1383,7 @@ static void bbr_cc_reset(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, void ngtcp2_cc_bbr_init(ngtcp2_cc_bbr *bbr, ngtcp2_log *log, ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, - ngtcp2_tstamp initial_ts, ngtcp2_rand rand, - const ngtcp2_rand_ctx *rand_ctx) { + ngtcp2_tstamp initial_ts, ngtcp2_pcg32 *pcg) { *bbr = (ngtcp2_cc_bbr){ .cc = { @@ -1403,8 +1397,7 @@ void ngtcp2_cc_bbr_init(ngtcp2_cc_bbr *bbr, ngtcp2_log *log, .reset = bbr_cc_reset, }, .rst = rst, - .rand = rand, - .rand_ctx = *rand_ctx, + .pcg = pcg, .initial_cwnd = cstat->cwnd, }; diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h index e823711a50..0499924f58 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_bbr.h @@ -35,6 +35,7 @@ #include "ngtcp2_window_filter.h" typedef struct ngtcp2_rst ngtcp2_rst; +typedef struct ngtcp2_pcg32 ngtcp2_pcg32; typedef enum ngtcp2_bbr_state { NGTCP2_BBR_STATE_STARTUP, @@ -62,8 +63,7 @@ typedef struct ngtcp2_cc_bbr { uint64_t initial_cwnd; ngtcp2_rst *rst; - ngtcp2_rand rand; - ngtcp2_rand_ctx rand_ctx; + ngtcp2_pcg32 *pcg; /* max_bw_filter for tracking the maximum recent delivery rate samples for estimating max_bw. */ @@ -136,7 +136,6 @@ typedef struct ngtcp2_cc_bbr { void ngtcp2_cc_bbr_init(ngtcp2_cc_bbr *bbr, ngtcp2_log *log, ngtcp2_conn_stat *cstat, ngtcp2_rst *rst, - ngtcp2_tstamp initial_ts, ngtcp2_rand rand, - const ngtcp2_rand_ctx *rand_ctx); + ngtcp2_tstamp initial_ts, ngtcp2_pcg32 *pcg); #endif /* !defined(NGTCP2_BBR_H) */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c index 75326d6b76..bf4273f816 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.c @@ -36,6 +36,12 @@ size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) { return (size_t)(buf->end - buf->begin); } +void ngtcp2_buf_trunc(ngtcp2_buf *buf, size_t len) { + if (ngtcp2_buf_len(buf) > len) { + buf->last = buf->pos + len; + } +} + int ngtcp2_buf_chain_new(ngtcp2_buf_chain **pbufchain, size_t len, const ngtcp2_mem *mem) { *pbufchain = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_buf_chain) + len); diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h index b2bdafc387..b59ac9a54b 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_buf.h @@ -70,9 +70,7 @@ static inline size_t ngtcp2_buf_left(const ngtcp2_buf *buf) { * ngtcp2_buf_len returns the number of bytes left to read. In other * words, it returns buf->last - buf->pos. */ -static inline size_t ngtcp2_buf_len(const ngtcp2_buf *buf) { - return (size_t)(buf->last - buf->pos); -} +#define ngtcp2_buf_len(BUF) (size_t)((BUF)->last - (BUF)->pos) /* * ngtcp2_buf_cap returns the capacity of the buffer. In other words, @@ -80,6 +78,13 @@ static inline size_t ngtcp2_buf_len(const ngtcp2_buf *buf) { */ size_t ngtcp2_buf_cap(const ngtcp2_buf *buf); +/* + * ngtcp2_buf_trunc truncates the number of bytes to read to at most + * |len|. In other words, it sets buf->last = buf->pos + len if + * ngtcp2_buf_len(buf) > len. + */ +void ngtcp2_buf_trunc(ngtcp2_buf *buf, size_t len); + /* * ngtcp2_buf_chain is a linked list of ngtcp2_buf. */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c index 393c2281f1..d97ba6a71b 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.c @@ -288,7 +288,7 @@ static int conn_call_begin_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv) { int rv; uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE; - const ngtcp2_path *old_path = NULL; + const ngtcp2_path *fallback_path = NULL; if (!pv || !conn->callbacks.begin_path_validation) { return 0; @@ -299,11 +299,11 @@ static int conn_call_begin_path_validation(ngtcp2_conn *conn, } if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) { - old_path = &pv->fallback_dcid.ps.path; + fallback_path = &pv->fallback_dcid.ps.path; } rv = conn->callbacks.begin_path_validation(conn, flags, &pv->dcid.ps.path, - old_path, conn->user_data); + fallback_path, conn->user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -315,7 +315,7 @@ static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv, ngtcp2_path_validation_result res) { int rv; uint32_t flags = NGTCP2_PATH_VALIDATION_FLAG_NONE; - const ngtcp2_path *old_path = NULL; + const ngtcp2_path *fallback_path = NULL; if (!conn->callbacks.path_validation) { return 0; @@ -326,17 +326,17 @@ static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv, } if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) { - old_path = &pv->fallback_dcid.ps.path; + fallback_path = &pv->fallback_dcid.ps.path; } - if (conn->server && old_path && - (ngtcp2_addr_cmp(&pv->dcid.ps.path.remote, &old_path->remote) & + if (conn->server && fallback_path && + (ngtcp2_addr_cmp(&pv->dcid.ps.path.remote, &fallback_path->remote) & (NGTCP2_ADDR_CMP_FLAG_ADDR | NGTCP2_ADDR_CMP_FLAG_FAMILY))) { flags |= NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN; } - rv = conn->callbacks.path_validation(conn, flags, &pv->dcid.ps.path, old_path, - res, conn->user_data); + rv = conn->callbacks.path_validation(conn, flags, &pv->dcid.ps.path, + fallback_path, res, conn->user_data); if (rv != 0) { return NGTCP2_ERR_CALLBACK_FAILURE; } @@ -1061,13 +1061,28 @@ conn_set_local_transport_params(ngtcp2_conn *conn, } static void conn_update_skip_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { - uint8_t gap; + const int64_t min_gap = 3; + uint8_t r; + int64_t gap; - conn->callbacks.rand(&gap, 1, &conn->local.settings.rand_ctx); + assert(INT64_MAX != pktns->tx.skip_pkt.next_pkt_num); - pktns->tx.skip_pkt.next_pkt_num = - pktns->tx.last_pkt_num + 3 + - (int64_t)gap * (1ll << pktns->tx.skip_pkt.exponent++); + conn->callbacks.rand(&r, 1, &conn->local.settings.rand_ctx); + + if (1ll << pktns->tx.skip_pkt.exponent > + (NGTCP2_MAX_PKT_NUM - min_gap) / ((int64_t)r + 1)) { + pktns->tx.skip_pkt.next_pkt_num = INT64_MAX; + return; + } + + gap = ((int64_t)r + 1) * (1ll << pktns->tx.skip_pkt.exponent++) + min_gap; + + if (pktns->tx.last_pkt_num > NGTCP2_MAX_PKT_NUM - gap) { + pktns->tx.skip_pkt.next_pkt_num = INT64_MAX; + return; + } + + pktns->tx.skip_pkt.next_pkt_num = pktns->tx.last_pkt_num + gap; ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "next skip pkn=%" PRId64, pktns->tx.skip_pkt.next_pkt_num); @@ -1138,7 +1153,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_settings settingsbuf; ngtcp2_transport_params paramsbuf; ngtcp2_callbacks callbacksbuf; - uint64_t map_seed; + uint64_t seed; (void)settings_version; settings = @@ -1248,8 +1263,11 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_pq_init(&(*pconn)->scid.used, retired_ts_less, mem); - callbacks->rand((uint8_t *)&map_seed, sizeof(map_seed), &settings->rand_ctx); - ngtcp2_map_init(&(*pconn)->strms, map_seed, mem); + callbacks->rand((uint8_t *)&seed, sizeof(seed), &settings->rand_ctx); + ngtcp2_map_init(&(*pconn)->strms, seed, mem); + + callbacks->rand((uint8_t *)&seed, sizeof(seed), &settings->rand_ctx); + ngtcp2_pcg32_init(&(*pconn)->pcg, seed); ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem); @@ -1325,8 +1343,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, break; case NGTCP2_CC_ALGO_BBR: ngtcp2_cc_bbr_init(&(*pconn)->bbr, &(*pconn)->log, &(*pconn)->cstat, - &(*pconn)->rst, settings->initial_ts, callbacks->rand, - &settings->rand_ctx); + &(*pconn)->rst, settings->initial_ts, &(*pconn)->pcg); break; default: @@ -1335,6 +1352,9 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_static_ringbuf_path_history_init(&(*pconn)->path_history); + ngtcp2_ratelim_init(&(*pconn)->glitch_rlim, settings->glitch_ratelim_burst, + settings->glitch_ratelim_rate, settings->initial_ts); + (*pconn)->callbacks = *callbacks; rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst, @@ -1524,6 +1544,7 @@ int ngtcp2_conn_client_new_versioned( (*pconn)->state = NGTCP2_CS_CLIENT_INITIAL; (*pconn)->local.bidi.next_stream_id = 0; (*pconn)->local.uni.next_stream_id = 2; + (*pconn)->flags |= NGTCP2_CONN_FLAG_CRUMBLE_INITIAL_CRYPTO; rv = ngtcp2_conn_commit_local_transport_params(*pconn); if (rv != 0) { @@ -2127,6 +2148,230 @@ static uint8_t conn_pkt_flags_short(ngtcp2_conn *conn) { : NGTCP2_PKT_FLAG_NONE)); } +/* + * conn_cut_crypto_frame splits (*pfrc)->fr.stream by removing + * |removed_data| from (*pfrc)->fr.stream.data[0]. + * (*pfrc)->fr.stream.data[0] must contain |removed_data|, and + * (*pfrc)->fr.stream.datacnt >= 1. New ngtcp2_frame_chain object + * that contains |removed_data| is created, and pushed to + * |crypto_strm| via ngtcp2_strm_streamfrq_push. Because + * (*pfrc)->fr.stream.datacnt cannot be changed, if it is not 1, new + * ngtcp2_frame_chain object is created to contain the data before + * |removed_data|. Then *pfrc is deleted, and the newly created + * object is assigned to *pfrc instead. If there are data following + * the removed part of data, new ngtcp2_frame_chain object is created + * for it, and (*pfrc)->next points to the object. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static int conn_cut_crypto_frame(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, + ngtcp2_strm *crypto_strm, + const ngtcp2_vec *removed_data) { + ngtcp2_vec *data = (*pfrc)->fr.stream.data; + size_t datacnt = (*pfrc)->fr.stream.datacnt; + size_t ndatacnt; + ngtcp2_frame_chain *left_frc, *right_frc = NULL, *removed_frc; + size_t offset; + int rv; + + assert(datacnt); + assert(data[0].base < removed_data->base); + assert(ngtcp2_vec_end(removed_data) <= ngtcp2_vec_end(&data[0])); + + offset = (size_t)(removed_data->base - data->base); + + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &removed_frc, 1, &conn->frc_objalloc, conn->mem); + if (rv != 0) { + return rv; + } + + /* ngtcp2_frame_chain for the removed data */ + removed_frc->fr.stream.type = NGTCP2_FRAME_CRYPTO; + removed_frc->fr.stream.offset = (*pfrc)->fr.stream.offset + offset; + removed_frc->fr.stream.datacnt = 1; + removed_frc->fr.stream.data[0] = (ngtcp2_vec){ + .base = data->base + offset, + .len = removed_data->len, + }; + + rv = ngtcp2_strm_streamfrq_push(crypto_strm, removed_frc); + if (rv != 0) { + ngtcp2_frame_chain_objalloc_del(removed_frc, &conn->frc_objalloc, + conn->mem); + return rv; + } + + if (data[0].len == offset + removed_data->len) { + ndatacnt = datacnt - 1; + } else { + ndatacnt = datacnt; + } + + if (ndatacnt) { + /* ngtcp2_frame_chain after the removed data */ + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &right_frc, ndatacnt, &conn->frc_objalloc, conn->mem); + if (rv != 0) { + return rv; + } + + right_frc->fr.stream.type = NGTCP2_FRAME_CRYPTO; + right_frc->fr.stream.offset = + removed_frc->fr.stream.offset + removed_frc->fr.stream.data->len; + right_frc->fr.stream.datacnt = 0; + ngtcp2_vec_split(right_frc->fr.stream.data, &right_frc->fr.stream.datacnt, + data, &datacnt, offset + removed_data->len, ndatacnt); + + assert(ndatacnt == right_frc->fr.stream.datacnt); + assert(1 == datacnt); + } + + /* We cannot change (*pfrc)->fr.stream.datacnt. If it changes, + create new ngtcp2_frame_chain. */ + if ((*pfrc)->fr.stream.datacnt == 1) { + (*pfrc)->fr.stream.data[0].len = offset; + (*pfrc)->next = right_frc; + return 0; + } + + rv = ngtcp2_frame_chain_stream_datacnt_objalloc_new( + &left_frc, 1, &conn->frc_objalloc, conn->mem); + if (rv != 0) { + ngtcp2_frame_chain_objalloc_del(right_frc, &conn->frc_objalloc, conn->mem); + return rv; + } + + left_frc->fr.stream.type = NGTCP2_FRAME_CRYPTO; + left_frc->fr.stream.offset = (*pfrc)->fr.stream.offset; + left_frc->fr.stream.datacnt = 1; + left_frc->fr.stream.data[0] = (ngtcp2_vec){ + .base = data[0].base, + .len = offset, + }; + left_frc->next = right_frc; + + ngtcp2_frame_chain_objalloc_del(*pfrc, &conn->frc_objalloc, conn->mem); + *pfrc = left_frc; + + return 0; +} + +/* + * conn_crumble_initial_crypto splits CRYPTO frame (*pfrc)->fr.stream + * into pieces and adds PADDING and PING frames, and reorder those + * frames. Those frames are encoded in the buffer pointed by |data| + * and |offsets|. |data| is the pointer to the array of ngtcp2_vec of + * at least NGTCP2_MAX_STREAM_DATACNT. |offsets| contains the CRYPTO + * offset of the corresponding ngtcp2_vec in |data|, and it also + * should have the capacity at least NGTCP2_MAX_STREAM_DATACNT + * uint64_t. |left| is the number of bytes available for the current + * packet. |crypto_offset| is the next smallest CRYPTO offset. + * |crypto_strm| is the CRYPTO stream. + * + * This function returns the number of objects written to |data| and + * |offsets|, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static ngtcp2_ssize +conn_crumble_initial_crypto(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, + ngtcp2_vec *data, uint64_t *offsets, + ngtcp2_strm *crypto_strm, size_t left, + uint64_t crypto_offset) { + ngtcp2_vec server_name; + ngtcp2_vec removed_data; + size_t max_add_frames = 10; + size_t single_crypto_overhead = + 1 + ngtcp2_put_uvarintlen(crypto_offset + left - 1) + + ngtcp2_put_uvarintlen(left); + size_t total_crypto_overhead = single_crypto_overhead * max_add_frames; + size_t datacnt; + size_t i; + int rv; + + if (left <= total_crypto_overhead) { + return 0; + } + + left -= total_crypto_overhead; + + left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left); + if (left == (size_t)-1) { + return 0; + } + + rv = ngtcp2_strm_streamfrq_pop(crypto_strm, pfrc, left); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + if (*pfrc == NULL) { + return 0; + } + + assert(crypto_offset == (*pfrc)->fr.stream.offset); + + ngtcp2_vec_copy(data, (*pfrc)->fr.stream.data, (*pfrc)->fr.stream.datacnt); + datacnt = (*pfrc)->fr.stream.datacnt; + + offsets[0] = (*pfrc)->fr.stream.offset; + + for (i = 1; i < datacnt; ++i) { + offsets[i] = offsets[i - 1] + data[i - 1].len; + } + + if (datacnt < NGTCP2_MAX_STREAM_DATACNT && + ngtcp2_pkt_find_server_name(&server_name, data) && server_name.len > 1) { + if (ngtcp2_strm_streamfrq_empty(crypto_strm) || + ngtcp2_strm_streamfrq_unacked_offset(crypto_strm) == (uint64_t)-1) { + datacnt = ngtcp2_pkt_split_vec_at( + data, datacnt, offsets, + (size_t)(server_name.base - data[0].base) + server_name.len / 2); + } else { + /* If we have another data to send (most likely in the another + packet), remove the part of SNI from this packet. */ + datacnt = ngtcp2_pkt_remove_vec_partial( + &removed_data, data, datacnt, offsets, &conn->pcg, &server_name); + + rv = conn_cut_crypto_frame(conn, pfrc, crypto_strm, &removed_data); + if (rv != 0) { + ngtcp2_frame_chain_objalloc_del(*pfrc, &conn->frc_objalloc, conn->mem); + return rv; + } + + /* Add the length of removed data to total_crypto_overhead so + that we can use them for inter CRYPTO frames padding. */ + total_crypto_overhead += removed_data.len; + } + } + + if (datacnt < max_add_frames + 1) { + max_add_frames -= datacnt - 1; + + datacnt = ngtcp2_pkt_split_vec_rand(data, datacnt, offsets, &conn->pcg, + max_add_frames); + } + + for (i = 1; i < datacnt; ++i) { + total_crypto_overhead -= 1 + ngtcp2_put_uvarintlen(offsets[i]) + + ngtcp2_put_uvarintlen(data[i].len); + } + + datacnt = ngtcp2_pkt_append_ping_and_padding(data, datacnt, &conn->pcg, + total_crypto_overhead); + + ngtcp2_pkt_permutate_vec(data, datacnt, offsets, &conn->pcg); + + return (ngtcp2_ssize)datacnt; +} + static size_t conn_min_pktlen(ngtcp2_conn *conn); /* @@ -2253,38 +2498,94 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, destlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE) { build_pkt: for (; !ngtcp2_strm_streamfrq_empty(&pktns->crypto.strm);) { - left = ngtcp2_ppe_left(&ppe); - crypto_offset = ngtcp2_strm_streamfrq_unacked_offset(&pktns->crypto.strm); if (crypto_offset == (uint64_t)-1) { ngtcp2_strm_streamfrq_clear(&pktns->crypto.strm); break; } - left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left); - if (left == (size_t)-1) { + left = ngtcp2_ppe_left(&ppe); + if (left == 0) { break; } - rv = ngtcp2_strm_streamfrq_pop(&pktns->crypto.strm, &nfrc, left); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, - conn->mem); - return rv; - } + if (type == NGTCP2_PKT_INITIAL && + (conn->flags & NGTCP2_CONN_FLAG_CRUMBLE_INITIAL_CRYPTO)) { + ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT]; + uint64_t offsets[NGTCP2_MAX_STREAM_DATACNT]; + ngtcp2_ssize datacnt; + size_t i; - if (nfrc == NULL) { - break; - } + datacnt = conn_crumble_initial_crypto( + conn, &nfrc, data, offsets, &pktns->crypto.strm, left, crypto_offset); + if (datacnt < 0) { + assert(ngtcp2_err_is_fatal((int)datacnt)); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, + conn->mem); - rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &nfrc->fr); - if (rv != 0) { - ngtcp2_unreachable(); + return datacnt; + } + + if (datacnt == 0) { + break; + } + + for (i = 0; i < (size_t)datacnt; ++i) { + if (data[i].base == NULL) { + if (data[i].len == 0) { + lfr.ping.type = NGTCP2_FRAME_PING; + } else { + lfr.padding = (ngtcp2_padding){ + .type = NGTCP2_FRAME_PADDING, + .len = data[i].len, + }; + } + } else { + lfr.stream = (ngtcp2_stream){ + .type = NGTCP2_FRAME_CRYPTO, + .offset = offsets[i], + .datacnt = 1, + .data[0] = data[i], + }; + } + + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); + if (rv != 0) { + ngtcp2_unreachable(); + } + } + } else { + left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left); + if (left == (size_t)-1) { + break; + } + + rv = ngtcp2_strm_streamfrq_pop(&pktns->crypto.strm, &nfrc, left); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, + conn->mem); + return rv; + } + + if (nfrc == NULL) { + break; + } + + rv = + conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &nfrc->fr); + if (rv != 0) { + ngtcp2_unreachable(); + } } *pfrc = nfrc; - pfrc = &(*pfrc)->next; + + for (; nfrc->next;) { + nfrc = nfrc->next; + } + + pfrc = &nfrc->next; pkt_empty = 0; rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | @@ -5270,9 +5571,12 @@ static void assign_recved_ack_delay_unscaled(ngtcp2_ack *fr, * Stream ID exceeds allowed limit. * NGTCP2_ERR_NOMEM * Out of memory. + * NGTCP2_ERR_INTERNAL + * Suspicious remote endpoint activity exceeded threshold. */ static int conn_recv_max_stream_data(ngtcp2_conn *conn, - const ngtcp2_max_stream_data *fr) { + const ngtcp2_max_stream_data *fr, + ngtcp2_tstamp ts) { ngtcp2_strm *strm; ngtcp2_idtr *idtr; int local_stream = conn_local_stream(conn, fr->stream_id); @@ -5302,6 +5606,10 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn, if (strm == NULL) { if (local_stream) { /* Stream has been closed. */ + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } @@ -5312,6 +5620,10 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn, } assert(rv == NGTCP2_ERR_STREAM_IN_USE); /* Stream has been closed. */ + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } @@ -5331,19 +5643,29 @@ static int conn_recv_max_stream_data(ngtcp2_conn *conn, } } - if (strm->tx.max_offset < fr->max_stream_data) { - strm->tx.max_offset = fr->max_stream_data; - - /* Don't call callback if stream is half-closed local */ - if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { - return 0; + if (strm->tx.max_offset >= fr->max_stream_data) { + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; } - rv = conn_call_extend_max_stream_data(conn, strm, fr->stream_id, - fr->max_stream_data); - if (rv != 0) { - return rv; + return 0; + } + + strm->tx.max_offset = fr->max_stream_data; + + /* Don't call callback if stream is half-closed local */ + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; } + + return 0; + } + + rv = conn_call_extend_max_stream_data(conn, strm, fr->stream_id, + fr->max_stream_data); + if (rv != 0) { + return rv; } return 0; @@ -5976,7 +6298,8 @@ static int conn_verify_fixed_bit(ngtcp2_conn *conn, ngtcp2_pkt_hd *hd) { static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_encryption_level encryption_level, - ngtcp2_strm *strm, const ngtcp2_stream *fr); + ngtcp2_strm *strm, const ngtcp2_stream *fr, + ngtcp2_tstamp ts); static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, const ngtcp2_pkt_info *pi, const uint8_t *pkt, @@ -6015,6 +6338,8 @@ static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, * TLS stack reported error. * NGTCP2_ERR_PROTO * Generic QUIC protocol error. + * NGTCP2_ERR_INTERNAL + * Suspicious remote endpoint activity exceeded threshold. * * In addition to the above error codes, error codes returned from * conn_recv_pkt are also returned. @@ -6525,7 +6850,7 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, conn->negotiated_version); } - rv = conn_recv_crypto(conn, encryption_level, crypto, &fr->stream); + rv = conn_recv_crypto(conn, encryption_level, crypto, &fr->stream, ts); if (rv != 0) { return rv; } @@ -6814,15 +7139,24 @@ static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, * The end offset exceeds the maximum value. * NGTCP2_ERR_CALLBACK_FAILURE * User-defined callback function failed. + * NGTCP2_ERR_INTERNAL + * Suspicious remote endpoint activity exceeded threshold. */ static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_encryption_level encryption_level, - ngtcp2_strm *crypto, const ngtcp2_stream *fr) { + ngtcp2_strm *crypto, const ngtcp2_stream *fr, + ngtcp2_tstamp ts) { uint64_t fr_end_offset; uint64_t rx_offset; int rv; + ngtcp2_ssize nwrite; if (fr->datacnt == 0) { + if (encryption_level != NGTCP2_ENCRYPTION_LEVEL_INITIAL && + ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } @@ -6835,6 +7169,11 @@ static int conn_recv_crypto(ngtcp2_conn *conn, rx_offset = ngtcp2_strm_rx_offset(crypto); if (fr_end_offset <= rx_offset) { + if (encryption_level != NGTCP2_ENCRYPTION_LEVEL_INITIAL && + ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + if (conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT) && encryption_level == NGTCP2_ENCRYPTION_LEVEL_INITIAL) { @@ -6892,8 +7231,18 @@ static int conn_recv_crypto(ngtcp2_conn *conn, return NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED; } - return ngtcp2_strm_recv_reordering(crypto, fr->data[0].base, fr->data[0].len, - fr->offset); + nwrite = ngtcp2_strm_recv_reordering(crypto, fr->data[0].base, + fr->data[0].len, fr->offset); + if (nwrite < 0) { + return (int)nwrite; + } + + if (encryption_level != NGTCP2_ENCRYPTION_LEVEL_INITIAL && nwrite == 0 && + ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + + return 0; } /* @@ -6927,8 +7276,11 @@ static int conn_max_data_violated(ngtcp2_conn *conn, uint64_t datalen) { * NGTCP2_ERR_FINAL_SIZE * STREAM frame has strictly larger end offset than it is * permitted. + * NGTCP2_ERR_INTERNAL + * Suspicious remote endpoint activity exceeded threshold. */ -static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { +static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr, + ngtcp2_tstamp ts) { int rv; ngtcp2_strm *strm; ngtcp2_idtr *idtr; @@ -6937,6 +7289,8 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { int bidi; uint64_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); uint32_t sdflags = NGTCP2_STREAM_DATA_FLAG_NONE; + ngtcp2_ssize nwrite; + int new_strm = 0; local_stream = conn_local_stream(conn, fr->stream_id); bidi = bidi_stream(fr->stream_id); @@ -6970,8 +7324,11 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { strm = ngtcp2_conn_find_stream(conn, fr->stream_id); if (strm == NULL) { if (local_stream) { - /* TODO The stream has been closed. This should be responded - with RESET_STREAM, or simply ignored. */ + /* The stream has been closed. */ + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } @@ -6981,8 +7338,11 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return rv; } assert(rv == NGTCP2_ERR_STREAM_IN_USE); - /* TODO The stream has been closed. This should be responded - with RESET_STREAM, or simply ignored. */ + /* The stream has been closed. */ + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } @@ -6997,6 +7357,8 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return rv; } + new_strm = 1; + if (!bidi) { ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR); strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED; @@ -7037,10 +7399,18 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { } if (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_RECVED) { + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } if (rx_offset == fr_end_offset) { + if (!new_strm && ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } } else if (strm->rx.last_offset > fr_end_offset) { @@ -7060,10 +7430,18 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { ngtcp2_max_uint64(strm->rx.last_offset, fr_end_offset); if (fr_end_offset <= rx_offset) { + if (!new_strm && ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } if (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_RECVED) { + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } } @@ -7111,10 +7489,14 @@ static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { return rv; } } else if (fr->datacnt && !(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { - rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len, - fr->offset); - if (rv != 0) { - return rv; + nwrite = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, + fr->data[0].len, fr->offset); + if (nwrite < 0) { + return (int)nwrite; + } + + if (nwrite == 0 && ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; } } return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm); @@ -7211,9 +7593,12 @@ handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams, * NGTCP2_MAX_VARINT. * NGTCP2_ERR_FINAL_SIZE * The final offset is strictly larger than it is permitted. + * NGTCP2_ERR_INTERNAL + * Suspicious remote endpoint activity exceeded threshold. */ static int conn_recv_reset_stream(ngtcp2_conn *conn, - const ngtcp2_reset_stream *fr) { + const ngtcp2_reset_stream *fr, + ngtcp2_tstamp ts) { ngtcp2_strm *strm; int local_stream = conn_local_stream(conn, fr->stream_id); int bidi = bidi_stream(fr->stream_id); @@ -7251,6 +7636,10 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn, strm = ngtcp2_conn_find_stream(conn, fr->stream_id); if (strm == NULL) { if (local_stream) { + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } @@ -7260,6 +7649,11 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn, return rv; } assert(rv == NGTCP2_ERR_STREAM_IN_USE); + + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } @@ -7294,6 +7688,10 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn, } if (strm->flags & NGTCP2_STRM_FLAG_RESET_STREAM_RECVED) { + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } @@ -7349,9 +7747,12 @@ static int conn_recv_reset_stream(ngtcp2_conn *conn, * Out of memory. * NGTCP2_ERR_CALLBACK_FAILURE * User-defined callback function failed. + * NGTCP2_ERR_INTERNAL + * Suspicious remote endpoint activity exceeded threshold. */ static int conn_recv_stop_sending(ngtcp2_conn *conn, - const ngtcp2_stop_sending *fr) { + const ngtcp2_stop_sending *fr, + ngtcp2_tstamp ts) { int rv; ngtcp2_strm *strm; ngtcp2_idtr *idtr; @@ -7380,6 +7781,10 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, strm = ngtcp2_conn_find_stream(conn, fr->stream_id); if (strm == NULL) { if (local_stream) { + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } rv = ngtcp2_idtr_open(idtr, fr->stream_id); @@ -7388,6 +7793,11 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, return rv; } assert(rv == NGTCP2_ERR_STREAM_IN_USE); + + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } @@ -7410,6 +7820,10 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, } if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING_RECVED) { + if (ngtcp2_ratelim_drain(&conn->glitch_rlim, 1, ts) != 0) { + return NGTCP2_ERR_INTERNAL; + } + return 0; } @@ -8671,6 +9085,8 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi, * Flow control limit is violated. * NGTCP2_ERR_FINAL_SIZE * Frame has strictly larger end offset than it is permitted. + * NGTCP2_ERR_INTERNAL + * Suspicious remote endpoint activity exceeded threshold. */ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, const ngtcp2_pkt_info *pi, const uint8_t *pkt, @@ -9074,7 +9490,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ++num_ack_processed; break; case NGTCP2_FRAME_STREAM: - rv = conn_recv_stream(conn, &fr->stream); + rv = conn_recv_stream(conn, &fr->stream, ts); if (rv != 0) { return rv; } @@ -9082,28 +9498,28 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, break; case NGTCP2_FRAME_CRYPTO: rv = conn_recv_crypto(conn, NGTCP2_ENCRYPTION_LEVEL_1RTT, - &pktns->crypto.strm, &fr->stream); + &pktns->crypto.strm, &fr->stream, ts); if (rv != 0) { return rv; } non_probing_pkt = 1; break; case NGTCP2_FRAME_RESET_STREAM: - rv = conn_recv_reset_stream(conn, &fr->reset_stream); + rv = conn_recv_reset_stream(conn, &fr->reset_stream, ts); if (rv != 0) { return rv; } non_probing_pkt = 1; break; case NGTCP2_FRAME_STOP_SENDING: - rv = conn_recv_stop_sending(conn, &fr->stop_sending); + rv = conn_recv_stop_sending(conn, &fr->stop_sending, ts); if (rv != 0) { return rv; } non_probing_pkt = 1; break; case NGTCP2_FRAME_MAX_STREAM_DATA: - rv = conn_recv_max_stream_data(conn, &fr->max_stream_data); + rv = conn_recv_max_stream_data(conn, &fr->max_stream_data, ts); if (rv != 0) { return rv; } @@ -11463,7 +11879,11 @@ conn_write_vmsg_wrapper(ngtcp2_conn *conn, ngtcp2_path *path, } else if ((cstat->cwnd >= cstat->ssthresh || cstat->bytes_in_flight * 2 < cstat->cwnd) && nwrite == 0 && conn_pacing_pkt_tx_allowed(conn, ts) && - (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + /* Because NGTCP2_CONN_FLAG_AGGREGATE_PKTS is set after a + packet is produced, if it is set, we are sure that we + are not app-limited. */ + !(conn->flags & NGTCP2_CONN_FLAG_AGGREGATE_PKTS)) { conn->rst.app_limited = conn->rst.delivered + cstat->bytes_in_flight; if (conn->rst.app_limited == 0) { @@ -11811,7 +12231,7 @@ ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, if (!conn->pktns.rtb.probe_pkt_left && conn_cwnd_is_zero(conn)) { destlen = 0; } else { - if (res == 0) { + if (res == 0 && !(conn->flags & NGTCP2_CONN_FLAG_AGGREGATE_PKTS)) { nwrite = conn_write_path_response(conn, path, pi, dest, origdestlen, ts); if (nwrite) { @@ -13462,6 +13882,91 @@ void ngtcp2_conn_add_path_history(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, ent->ts = ts; } +ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen, + ngtcp2_write_pkt write_pkt, ngtcp2_tstamp ts) { + 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); + ngtcp2_ssize nwrite; + uint8_t *wbuf = buf; + size_t wbuflen; + ngtcp2_ecn_state ecn_state; + int first_pkt; + ngtcp2_pkt_info pi_discard; + ngtcp2_path_storage path_discard; + (void)pkt_info_version; + + assert(buflen >= path_max_udp_payloadlen); + + buflen = + ngtcp2_min_size(buflen, ngtcp2_max_size(ngtcp2_conn_get_send_quantum(conn), + path_max_udp_payloadlen)); + + for (;;) { + ecn_state = conn->tx.ecn.state; + + wbuflen = buflen >= max_udp_payloadlen ? max_udp_payloadlen + : path_max_udp_payloadlen; + + nwrite = write_pkt(conn, path, pi, wbuf, wbuflen, ts, conn->user_data); + if (nwrite < 0) { + break; + } + + if (nwrite == 0) { + nwrite = wbuf - buf; + break; + } + + first_pkt = buf == wbuf; + wbuf += nwrite; + buflen -= (size_t)nwrite; + + 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) { + nwrite = wbuf - buf; + break; + } + + /* All aggregated packets should share the same path and pi. + Pass the placeholder values to the callback because they + might be overwritten by later calls, especially pi is set to + empty when no packet is produced. */ + if (path) { + ngtcp2_path_storage_zero(&path_discard); + path = &path_discard.path; + } + + if (pi) { + pi = &pi_discard; + } + + conn->flags |= NGTCP2_CONN_FLAG_AGGREGATE_PKTS; + + continue; + } + + if (buflen < path_max_udp_payloadlen || (size_t)nwrite < *pgsolen || + ecn_state != conn->tx.ecn.state) { + nwrite = wbuf - buf; + break; + } + } + + conn->flags &= ~NGTCP2_CONN_FLAG_AGGREGATE_PKTS; + + ngtcp2_conn_update_pkt_tx_time(conn, ts); + + return nwrite; +} + const ngtcp2_path_history_entry * ngtcp2_conn_find_path_history(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_tstamp ts) { diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h index 5979d39654..2d607d379f 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_conn.h @@ -52,6 +52,8 @@ #include "ngtcp2_rst.h" #include "ngtcp2_conn_stat.h" #include "ngtcp2_dcidtr.h" +#include "ngtcp2_pcg.h" +#include "ngtcp2_ratelim.h" typedef enum { /* Client specific handshake states */ @@ -200,6 +202,14 @@ void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, /* NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR is set when the local endpoint has initiated key update. */ #define NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR 0x10000u +/* NGTCP2_CONN_FLAG_AGGREGATE_PKTS is set when + ngtcp2_conn_writev_stream is called inside the callback invoked by + ngtcp2_conn_write_aggregate_pkt. */ +#define NGTCP2_CONN_FLAG_AGGREGATE_PKTS 0x20000u +/* NGTCP2_CONN_FLAG_CRUMBLE_INITIAL_CRYPTO, if set, crumbles an + Initial CRYPTO frame into pieces as a countermeasure against Deep + Packet Inspection. */ +#define NGTCP2_CONN_FLAG_CRUMBLE_INITIAL_CRYPTO 0x40000u typedef struct ngtcp2_pktns { struct { @@ -641,12 +651,17 @@ struct ngtcp2_conn { successfully. The path is added to this history when a local endpoint migrates to the another path. */ ngtcp2_static_ringbuf_path_history path_history; + /* glitch_rlim is the rate limit of glitches that can be tolerated. + If more than those glitches are detected, a connection is + closed. */ + ngtcp2_ratelim glitch_rlim; const ngtcp2_mem *mem; /* idle_ts is the time instant when idle timer started. */ ngtcp2_tstamp idle_ts; /* handshake_confirmed_ts is the time instant when handshake is confirmed. For server, it is confirmed when completed. */ ngtcp2_tstamp handshake_confirmed_ts; + ngtcp2_pcg32 pcg; void *user_data; uint32_t client_chosen_version; uint32_t negotiated_version; diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_frame_chain.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_frame_chain.h index e7b3363252..01f07cfa4d 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_frame_chain.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_frame_chain.h @@ -90,10 +90,6 @@ ngtcp2_objalloc_decl(frame_chain, ngtcp2_frame_chain, oplent) int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, const ngtcp2_mem *mem); -/* NGTCP2_MAX_STREAM_DATACNT is the maximum number of ngtcp2_vec that - a ngtcp2_stream can include. */ -#define NGTCP2_MAX_STREAM_DATACNT 256 - /* * ngtcp2_frame_chain_objalloc_new allocates ngtcp2_frame_chain using * |objalloc|. diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pcg.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pcg.c new file mode 100644 index 0000000000..9d0eb57e0d --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pcg.c @@ -0,0 +1,88 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pcg.h" + +#include + +/* + * PCG implementation from + * https://github.com/imneme/pcg-c/blob/83252d9c23df9c82ecb42210afed61a7b42402d7/include/pcg_variants.h + * + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +#define NGTCP2_PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL +#define NGTCP2_PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL + +static void pcg_oneseq_64_step_r(ngtcp2_pcg32 *pcg) { + pcg->state = pcg->state * NGTCP2_PCG_DEFAULT_MULTIPLIER_64 + + NGTCP2_PCG_DEFAULT_INCREMENT_64; +} + +void ngtcp2_pcg32_init(ngtcp2_pcg32 *pcg, uint64_t seed) { + pcg->state = 0; + pcg_oneseq_64_step_r(pcg); + pcg->state += seed; + pcg_oneseq_64_step_r(pcg); +} + +static uint32_t pcg_rotr_32(uint32_t value, unsigned int rot) { + return (value >> rot) | (value << ((-rot) & 31)); +} + +static uint32_t pcg_output_xsh_rr_64_32(uint64_t state) { + return pcg_rotr_32((uint32_t)(((state >> 18u) ^ state) >> 27u), + (unsigned int)(state >> 59u)); +} + +uint32_t ngtcp2_pcg32_rand(ngtcp2_pcg32 *pcg) { + uint64_t oldstate = pcg->state; + + pcg_oneseq_64_step_r(pcg); + + return pcg_output_xsh_rr_64_32(oldstate); +} + +uint32_t ngtcp2_pcg32_rand_n(ngtcp2_pcg32 *pcg, uint32_t n) { + assert(n); + return (uint32_t)(((uint64_t)ngtcp2_pcg32_rand(pcg) * n) >> 32); +} diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pcg.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pcg.h new file mode 100644 index 0000000000..a637183efc --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pcg.h @@ -0,0 +1,54 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PCG_H +#define NGTCP2_PCG_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* defined(HAVE_CONFIG_H) */ + +#include + +typedef struct ngtcp2_pcg32 { + uint64_t state; +} ngtcp2_pcg32; + +/* + * ngtcp2_pcg32_init initializes |pcg| with |seed|. + */ +void ngtcp2_pcg32_init(ngtcp2_pcg32 *pcg, uint64_t seed); + +/* + * ngtcp2_pcg32_rand returns a random value in [0, UINT32_MAX]. + */ +uint32_t ngtcp2_pcg32_rand(ngtcp2_pcg32 *pcg); + +/* + * ngtcp2_pcg32_rand_n returns a random value in [0, n). |n| must not + * be zero. + */ +uint32_t ngtcp2_pcg32_rand_n(ngtcp2_pcg32 *pcg, uint32_t n); + +#endif /* !defined(NGTCP2_PCG_H) */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c index af8a059d08..d63dc932e1 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.c @@ -33,7 +33,9 @@ #include "ngtcp2_cid.h" #include "ngtcp2_mem.h" #include "ngtcp2_vec.h" +#include "ngtcp2_buf.h" #include "ngtcp2_unreachable.h" +#include "ngtcp2_pcg.h" int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path, const ngtcp2_pkt_info *pi, const uint8_t *pkt, @@ -2571,3 +2573,294 @@ int ngtcp2_pkt_verify_reserved_bits(uint8_t c) { return (c & NGTCP2_SHORT_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO; } + +size_t ngtcp2_pkt_split_vec_rand(ngtcp2_vec *data, size_t datacnt, + uint64_t *offsets, ngtcp2_pcg32 *pcg, + size_t max_add) { + ngtcp2_vec *v; + size_t idx; + size_t len; + + for (; max_add; --max_add) { + idx = ngtcp2_pcg32_rand_n(pcg, (uint32_t)datacnt); + assert(idx < datacnt); + + v = &data[idx]; + + if (v->len <= 1) { + continue; + } + + len = v->len / 2; + + ngtcp2_vec_split_at(&data[datacnt], v, len); + + offsets[datacnt] = offsets[idx] + len; + + ++datacnt; + } + + return datacnt; +} + +size_t ngtcp2_pkt_split_vec_at(ngtcp2_vec *data, size_t datacnt, + uint64_t *offsets, size_t at) { + assert(at < data[0].len); + + ngtcp2_vec_split_at(&data[datacnt], &data[0], at); + + offsets[datacnt] = offsets[0] + at; + + return datacnt + 1; +} + +static int pkt_tls_skip8(ngtcp2_buf *buf) { + size_t len; + + if (ngtcp2_buf_len(buf) < 1) { + return -1; + } + + len = *buf->pos++; + + if (ngtcp2_buf_len(buf) < len) { + return -1; + } + + buf->pos += len; + + return 0; +} + +static int pkt_tls_skip16(ngtcp2_buf *buf) { + uint16_t len; + + if (ngtcp2_buf_len(buf) < sizeof(len)) { + return -1; + } + + buf->pos = (uint8_t *)ngtcp2_get_uint16be(&len, buf->pos); + + if (ngtcp2_buf_len(buf) < len) { + return -1; + } + + buf->pos += len; + + return 0; +} + +int ngtcp2_pkt_find_server_name(ngtcp2_vec *server_name, const ngtcp2_vec *v) { + ngtcp2_buf buf; + uint32_t msglen; + uint16_t len; + uint16_t legacy_ver; + uint16_t ext_type; + + assert(v->len); + + ngtcp2_buf_init(&buf, v->base, v->len); + buf.last += v->len; + + /* Handshake msg_type and length */ + if (ngtcp2_buf_len(&buf) < 1 + 3) { + return 0; + } + + /* Keep parsing only when msg_type is client_hello(1). */ + if (*buf.pos++ != 1) { + return 0; + } + + buf.pos = (uint8_t *)ngtcp2_get_uint24be(&msglen, buf.pos); + + /* Truncate the buffer to msglen */ + ngtcp2_buf_trunc(&buf, msglen); + + /* legacy_version(0x0303) */ + if (ngtcp2_buf_len(&buf) < sizeof(uint16_t)) { + return 0; + } + + buf.pos = (uint8_t *)ngtcp2_get_uint16be(&legacy_ver, buf.pos); + if (legacy_ver != 0x0303) { + return 0; + } + + /* random */ + if (ngtcp2_buf_len(&buf) < 32) { + return 0; + } + + buf.pos += 32; + + /* legacy_session_id */ + if (pkt_tls_skip8(&buf) != 0) { + return 0; + } + + /* cipher_suites */ + if (pkt_tls_skip16(&buf) != 0) { + return 0; + } + + /* legacy_compression_methods */ + if (pkt_tls_skip8(&buf) != 0) { + return 0; + } + + /* extensions */ + if (ngtcp2_buf_len(&buf) < sizeof(uint16_t)) { + return 0; + } + + buf.pos = (uint8_t *)ngtcp2_get_uint16be(&len, buf.pos); + + /* Truncate the buffer to extensions length */ + ngtcp2_buf_trunc(&buf, len); + + for (;;) { + /* Verify that extension_type and length of extension_data are + available */ + if (ngtcp2_buf_len(&buf) < sizeof(uint16_t) * 2) { + return 0; + } + + /* extension_type */ + buf.pos = (uint8_t *)ngtcp2_get_uint16be(&ext_type, buf.pos); + if (ext_type != 0) { + /* extension_data */ + if (pkt_tls_skip16(&buf) != 0) { + return 0; + } + + continue; + } + + /* Server Name Indication extension(0) */ + + /* extension_data */ + buf.pos = (uint8_t *)ngtcp2_get_uint16be(&len, buf.pos); + if (ngtcp2_buf_len(&buf) < len || len < 2) { + return 0; + } + + /* Truncate the buffer to extension_data length */ + ngtcp2_buf_trunc(&buf, len); + + /* server_name_list */ + buf.pos = (uint8_t *)ngtcp2_get_uint16be(&len, buf.pos); + if (ngtcp2_buf_len(&buf) < len || len < 1 + 2) { + return 0; + } + + /* We deliberately do not check server_name_list length + 2 == + extension_data length. They most likely match, and even if + not, no problem at all. */ + + /* Truncate the buffer to server_name_list length */ + ngtcp2_buf_trunc(&buf, len); + + /* name_type */ + if (*buf.pos++ != 0) { + return 0; + } + + /* name */ + buf.pos = (uint8_t *)ngtcp2_get_uint16be(&len, buf.pos); + if (ngtcp2_buf_len(&buf) < len) { + return 0; + } + + server_name->base = buf.pos; + server_name->len = len; + + return 1; + } +} + +size_t ngtcp2_pkt_append_ping_and_padding(ngtcp2_vec *data, size_t datacnt, + ngtcp2_pcg32 *pcg, size_t n) { + uint32_t k; + + for (; n && datacnt < NGTCP2_MAX_STREAM_DATACNT;) { + k = ngtcp2_pcg32_rand_n(pcg, (uint32_t)n + 1); + if (k == 0) { + /* PING */ + data[datacnt] = (ngtcp2_vec){ + .base = NULL, + .len = 0, + }; + + ++k; + } else { + /* PADDING of k length */ + data[datacnt] = (ngtcp2_vec){ + .base = NULL, + .len = k, + }; + } + + ++datacnt; + n -= k; + } + + return datacnt; +} + +void ngtcp2_pkt_permutate_vec(ngtcp2_vec *data, size_t datacnt, + uint64_t *offsets, ngtcp2_pcg32 *pcg) { + size_t i, j; + ngtcp2_vec v; + uint64_t o; + + if (datacnt < 2) { + return; + } + + for (i = datacnt - 1; i > 0; --i) { + j = ngtcp2_pcg32_rand_n(pcg, (uint32_t)i); + + if (i == j) { + continue; + } + + v = data[i]; + data[i] = data[j]; + data[j] = v; + + o = offsets[i]; + offsets[i] = offsets[j]; + offsets[j] = o; + } +} + +size_t ngtcp2_pkt_remove_vec_partial(ngtcp2_vec *removed_data, ngtcp2_vec *data, + size_t datacnt, uint64_t *offsets, + ngtcp2_pcg32 *pcg, + const ngtcp2_vec *part) { + ngtcp2_vec *v = &data[0]; + size_t len; + + assert(datacnt); + assert(v->base < part->base); + assert(ngtcp2_vec_end(part) <= ngtcp2_vec_end(v)); + + len = (size_t)(part->base - v->base) + part->len / 2; + + ngtcp2_vec_split_at(removed_data, v, len); + + if (removed_data->len == 1) { + return datacnt; + } + + len = 1 + ngtcp2_pcg32_rand_n( + pcg, (uint32_t)ngtcp2_min_size(30, removed_data->len - 1)); + assert(len < removed_data->len); + + ngtcp2_vec_split_at(&data[datacnt], removed_data, len); + + offsets[datacnt] = offsets[0] + v->len + removed_data->len; + + return datacnt + 1; +} diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h index ec72708e04..ed358dc48d 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_pkt.h @@ -144,6 +144,12 @@ length of data to send is larger than this limit. */ #define NGTCP2_MIN_STREAM_DATALEN 256 +/* NGTCP2_MAX_STREAM_DATACNT is the maximum number of ngtcp2_vec that + a ngtcp2_stream can include. */ +#define NGTCP2_MAX_STREAM_DATACNT 256 + +typedef struct ngtcp2_pcg32 ngtcp2_pcg32; + typedef struct ngtcp2_pkt_retry { ngtcp2_cid odcid; uint8_t *token; @@ -1234,4 +1240,81 @@ uint8_t ngtcp2_pkt_versioned_type(uint32_t version, uint32_t pkt_type); */ uint8_t ngtcp2_pkt_get_type_long(uint32_t version, uint8_t c); +/* + * ngtcp2_pkt_split_vec_rand appends ngtcp2_vec at most |max_add| + * times to the array pointed by |data| of length |datacnt| by + * splitting the existing ngtcp2_vec into two. Which ngtcp2_vec to + * split is chosen randomly. |offsets| contains the offset of each + * ngtcp2_vec pointed by |data|. |offsets| is also updated. The + * arrays must have the capacity at least |datacnt| + |max_add|. + * |pcg| is a random number generator. + * + * This function returns |datacnt| plus the number of ngtcp2_vec that + * are appended. + */ +size_t ngtcp2_pkt_split_vec_rand(ngtcp2_vec *data, size_t datacnt, + uint64_t *offsets, ngtcp2_pcg32 *pcg, + size_t max_add); + +/* + * ngtcp2_pkt_split_vec_at splits data[0] at offset |at|, and the + * right side of ngtcp2_vec is assigned to data[datacnt]. Similarly, + * offsets[0] + |at| is assigned to offsets[datacnt]. |data| must + * point to the array of ngtcp2_vec of length |datacnt|, and |datacnt| + * must be greater than 0. |at| must be strictly less than data->len. + * + * This function returns |datacnt| + 1. + */ +size_t ngtcp2_pkt_split_vec_at(ngtcp2_vec *data, size_t datacnt, + uint64_t *offsets, size_t at); + +/* + * ngtcp2_pkt_find_server_name searches TLS Server Name Indication + * extension in |v|. If it is found, assign the portion of server + * name to the object pointed by |server_name|, and returns nonzero. + * Otherwise, it returns 0. If |v| contains the extension partially, + * the function returns 0. |v| must not be empty. + */ +int ngtcp2_pkt_find_server_name(ngtcp2_vec *server_name, const ngtcp2_vec *v); + +/* + * ngtcp2_pkt_append_ping_and_padding appends PING and PADDING frames + * to the array pointed by |data| of length |datacnt|. The capacity + * of array must be at least NGTCP2_MAX_STREAM_DATACNT. |n| is the + * number of bytes available for serialized PING and PADDING frames. + * |pcg| is a random number generator. Which frames to add is + * determined randomly. + * + * The special encoding of PING and PADDING frames into ngtcp2_vec: + * + * - .base is NULL. + * - If .len is 0, it represents PING. Otherwise, PADDING of .len + * length. + * + * This function returns |datacnt| plus the number of frames added. + */ +size_t ngtcp2_pkt_append_ping_and_padding(ngtcp2_vec *data, size_t datacnt, + ngtcp2_pcg32 *pcg, size_t n); + +/* + * ngtcp2_pkt_permutate_vec permutates |data| and |offsets|, both have + * the |datacnt| elements. |pcg| is a random number generator. + */ +void ngtcp2_pkt_permutate_vec(ngtcp2_vec *data, size_t datacnt, + uint64_t *offsets, ngtcp2_pcg32 *pcg); + +/* + * ngtcp2_pkt_remove_vec_partial removes the portion of data that + * contains part of |part| from data[0]. This function does not + * remove whole range of |part|. The length of removed data is chosen + * randomly. The removed portion of data is assigned to the object + * pointed by |removed_data|. If there is data located after the + * removed data, it will be assigned to data[datacnt]. + * offsets[datacnt] is also updated, and the function returns + * |datacnt| + 1. Otherwise, this function returns |datacnt|. + */ +size_t ngtcp2_pkt_remove_vec_partial(ngtcp2_vec *removed_data, ngtcp2_vec *data, + size_t datacnt, uint64_t *offsets, + ngtcp2_pcg32 *pcg, const ngtcp2_vec *part); + #endif /* !defined(NGTCP2_PKT_H) */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ratelim.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ratelim.c new file mode 100644 index 0000000000..efedc3daa7 --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ratelim.c @@ -0,0 +1,77 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * Copyright (c) 2023 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_ratelim.h" + +#include + +#include "ngtcp2_macro.h" + +void ngtcp2_ratelim_init(ngtcp2_ratelim *rlim, uint64_t burst, uint64_t rate, + ngtcp2_tstamp ts) { + *rlim = (ngtcp2_ratelim){ + .burst = burst, + .rate = rate, + .tokens = burst, + .ts = ts, + }; +} + +/* ratelim_update updates rlim->tokens with the current |ts|. */ +static void ratelim_update(ngtcp2_ratelim *rlim, ngtcp2_tstamp ts) { + uint64_t d, gain; + + assert(ts >= rlim->ts); + + if (ts == rlim->ts) { + return; + } + + d = ts - rlim->ts; + rlim->ts = ts; + + gain = rlim->rate * d + rlim->carry; + + rlim->tokens += gain / NGTCP2_SECONDS; + + if (rlim->tokens < rlim->burst) { + rlim->carry = gain % NGTCP2_SECONDS; + } else { + rlim->tokens = rlim->burst; + rlim->carry = 0; + } +} + +int ngtcp2_ratelim_drain(ngtcp2_ratelim *rlim, uint64_t n, ngtcp2_tstamp ts) { + ratelim_update(rlim, ts); + + if (rlim->tokens < n) { + return -1; + } + + rlim->tokens -= n; + + return 0; +} diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ratelim.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ratelim.h new file mode 100644 index 0000000000..14485c562d --- /dev/null +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_ratelim.h @@ -0,0 +1,59 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * Copyright (c) 2023 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RATELIM_H +#define NGTCP2_RATELIM_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* HAVE_CONFIG_H */ + +#include + +typedef struct ngtcp2_ratelim { + /* burst is the maximum number of tokens. */ + uint64_t burst; + /* rate is the rate of token generation measured by token / + second. */ + uint64_t rate; + /* tokens is the amount of tokens available to drain. */ + uint64_t tokens; + /* carry is the partial token gained in sub-second period. It is + added to the computation in the next update round. */ + uint64_t carry; + /* ts is the last timestamp that is known to this object. */ + ngtcp2_tstamp ts; +} ngtcp2_ratelim; + +/* ngtcp2_ratelim_init initializes |rlim| with the given + parameters. */ +void ngtcp2_ratelim_init(ngtcp2_ratelim *rlim, uint64_t burst, uint64_t rate, + ngtcp2_tstamp ts); + +/* ngtcp2_ratelim_drain drains |n| from rlim->tokens. It returns 0 if + it succeeds, or -1. */ +int ngtcp2_ratelim_drain(ngtcp2_ratelim *rlim, uint64_t n, ngtcp2_tstamp ts); + +#endif /* !defined(NGTCP2_RATELIM_H) */ diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c index 853f1d650e..ef1938ea63 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.c @@ -162,8 +162,8 @@ static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, return 0; } -int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, - size_t datalen) { +ngtcp2_ssize ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, + const uint8_t *data, size_t datalen) { int rv; ngtcp2_rob_gap *g; ngtcp2_range m, l, r; @@ -172,6 +172,8 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, .end = offset + datalen, }; ngtcp2_ksl_it it; + ngtcp2_ssize nwrite = 0; + size_t mlen; it = ngtcp2_ksl_lower_bound_search(&rob->gapksl, &q, ngtcp2_ksl_range_exclusive_search); @@ -180,7 +182,9 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, g = ngtcp2_ksl_it_get(&it); m = ngtcp2_range_intersect(&q, &g->range); - if (!ngtcp2_range_len(&m)) { + + mlen = (size_t)ngtcp2_range_len(&m); + if (mlen == 0) { break; } @@ -188,12 +192,13 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, ngtcp2_ksl_remove_hint(&rob->gapksl, &it, &it, &g->range); ngtcp2_rob_gap_del(g, rob->mem); - rv = rob_write_data(rob, m.begin, data + (m.begin - offset), - (size_t)ngtcp2_range_len(&m)); + rv = rob_write_data(rob, m.begin, data + (m.begin - offset), mlen); if (rv != 0) { return rv; } + nwrite += (ngtcp2_ssize)mlen; + continue; } @@ -222,16 +227,17 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, g->range = r; } - rv = rob_write_data(rob, m.begin, data + (m.begin - offset), - (size_t)ngtcp2_range_len(&m)); + rv = rob_write_data(rob, m.begin, data + (m.begin - offset), mlen); if (rv != 0) { return rv; } + nwrite += (ngtcp2_ssize)mlen; + ngtcp2_ksl_it_next(&it); } - return 0; + return nwrite; } void ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) { diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h index d53b5160b1..60a1c5b46a 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rob.h @@ -138,14 +138,14 @@ void ngtcp2_rob_free(ngtcp2_rob *rob); * ngtcp2_rob_push adds new data pointed by |data| of length |datalen| * at the stream offset |offset|. * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: + * This function returns the number of data newly buffered if it + * succeeds, or one of the following negative error codes: * * NGTCP2_ERR_NOMEM * Out of memory */ -int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, - size_t datalen); +ngtcp2_ssize ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, + const uint8_t *data, size_t datalen); /* * ngtcp2_rob_remove_prefix removes gap up to |offset|, exclusive. It diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c index b50f482bc7..7df1c197db 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_rtb.c @@ -802,8 +802,8 @@ ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR) && largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) { - conn->flags &= (uint32_t) ~(NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED | - NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR); + conn->flags &= (uint32_t)~(NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED | + NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR); conn->crypto.key_update.confirmed_ts = ts; ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_settings.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_settings.c index 77a68bd112..f774504282 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_settings.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_settings.c @@ -37,6 +37,10 @@ void ngtcp2_settings_default_versioned(int settings_version, switch (settings_version) { case NGTCP2_SETTINGS_VERSION: + settings->glitch_ratelim_burst = NGTCP2_DEFAULT_GLITCH_RATELIM_BURST; + settings->glitch_ratelim_rate = NGTCP2_DEFAULT_GLITCH_RATELIM_RATE; + /* fall through */ + case NGTCP2_SETTINGS_V2: case NGTCP2_SETTINGS_V1: settings->cc_algo = NGTCP2_CC_ALGO_CUBIC; settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT; @@ -82,6 +86,9 @@ size_t ngtcp2_settingslen_version(int settings_version) { switch (settings_version) { case NGTCP2_SETTINGS_VERSION: return sizeof(settings); + case NGTCP2_SETTINGS_V2: + return offsetof(ngtcp2_settings, pmtud_probeslen) + + sizeof(settings.pmtud_probeslen); case NGTCP2_SETTINGS_V1: return offsetof(ngtcp2_settings, initial_pkt_num) + sizeof(settings.initial_pkt_num); diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_settings.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_settings.h index 80466d43e4..caa0fb58c5 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_settings.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_settings.h @@ -31,6 +31,13 @@ #include +/* NGTCP2_DEFAULT_GLITCH_RATELIM_BURST is the maximum number of tokens + in glitch rate limiter. It is also the initial value. */ +#define NGTCP2_DEFAULT_GLITCH_RATELIM_BURST 1000 +/* NGTCP2_DEFAULT_GLITCH_RATELIM_RATE is the rate of tokens generated + per second for glitch rate limiter. */ +#define NGTCP2_DEFAULT_GLITCH_RATELIM_RATE 33 + /* * ngtcp2_settings_convert_to_latest converts |src| of version * |settings_version| to the latest version NGTCP2_SETTINGS_VERSION. diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c index 70ec6ee2fe..faa4177132 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.c @@ -123,9 +123,10 @@ static int strm_rob_heavily_fragmented(const ngtcp2_rob *rob) { return ngtcp2_ksl_len(&rob->gapksl) >= 1000; } -int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, - size_t datalen, uint64_t offset) { +ngtcp2_ssize ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, + size_t datalen, uint64_t offset) { int rv; + ngtcp2_ssize nwrite; if (strm->rx.rob == NULL) { rv = strm_rob_init(strm); @@ -138,16 +139,16 @@ int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, } } - rv = ngtcp2_rob_push(strm->rx.rob, offset, data, datalen); - if (rv != 0) { - return rv; + nwrite = ngtcp2_rob_push(strm->rx.rob, offset, data, datalen); + if (nwrite < 0) { + return nwrite; } if (strm_rob_heavily_fragmented(strm->rx.rob)) { return NGTCP2_ERR_INTERNAL; } - return 0; + return nwrite; } void ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset) { diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h index c72f8b9dc8..1a1e8fd3b7 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_strm.h @@ -208,14 +208,14 @@ uint64_t ngtcp2_strm_rx_offset(const ngtcp2_strm *strm); /* * ngtcp2_strm_recv_reordering handles reordered data. * - * It returns 0 if it succeeds, or one of the following negative error - * codes: + * It returns the number of bytes newly buffered if it succeeds, or + * one of the following negative error codes: * * NGTCP2_ERR_NOMEM * Out of memory */ -int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, - size_t datalen, uint64_t offset); +ngtcp2_ssize ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, + size_t datalen, uint64_t offset); /* * ngtcp2_strm_update_rx_offset tells that data up to |offset| bytes diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c index dbca8691d6..ada027b909 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.c @@ -217,3 +217,14 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt, void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt) { memcpy(dst, src, sizeof(ngtcp2_vec) * cnt); } + +void ngtcp2_vec_split_at(ngtcp2_vec *dst, ngtcp2_vec *src, size_t offset) { + assert(offset < src->len); + + *dst = (ngtcp2_vec){ + .base = src->base + offset, + .len = src->len - offset, + }; + + src->len = offset; +} diff --git a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h index d90a3204a9..af9b4d6445 100644 --- a/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h +++ b/src/contrib/libngtcp2/ngtcp2/lib/ngtcp2_vec.h @@ -96,4 +96,18 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt, */ void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt); +/* + * ngtcp2_vec_split_at splits |src| at the |offset|. Caller must + * ensure that offset < src->len. This function assigns the right + * part of vector into |dst|. + */ +void ngtcp2_vec_split_at(ngtcp2_vec *dst, ngtcp2_vec *src, size_t offset); + +/* + * ngtcp2_vec_end returns the one beyond the last offset of |v|. + */ +static inline uint8_t *ngtcp2_vec_end(const ngtcp2_vec *v) { + return v->base + v->len; +} + #endif /* !defined(NGTCP2_VEC_H) */ diff --git a/src/contrib/libngtcp2/ngtcp2/ngtcp2.h b/src/contrib/libngtcp2/ngtcp2/ngtcp2.h index b0cc867cde..d58a6f1240 100644 --- a/src/contrib/libngtcp2/ngtcp2/ngtcp2.h +++ b/src/contrib/libngtcp2/ngtcp2/ngtcp2.h @@ -1704,7 +1704,8 @@ typedef enum ngtcp2_token_type { #define NGTCP2_SETTINGS_V1 1 #define NGTCP2_SETTINGS_V2 2 -#define NGTCP2_SETTINGS_VERSION NGTCP2_SETTINGS_V2 +#define NGTCP2_SETTINGS_V3 3 +#define NGTCP2_SETTINGS_VERSION NGTCP2_SETTINGS_V3 /** * @struct @@ -1917,6 +1918,23 @@ typedef struct ngtcp2_settings { * field has been available since v1.4.0. */ size_t pmtud_probeslen; + /* The following fields have been added since NGTCP2_SETTINGS_V3. */ + /** + * :member:`glitch_ratelim_burst` is the maximum number of tokens + * available to "glitch" rate limiter. "glitch" is a suspicious + * activity from a remote endpoint. If detected, certain amount of + * tokens are consumed. If no tokens are available to consume, the + * connection is closed. The rate of token generation is specified + * by :member:`glitch_ratelim_rate`. This field has been available + * since v1.15.0. + */ + uint64_t glitch_ratelim_burst; + /** + * :member:`glitch_ratelim_rate` is the number of tokens generated + * per second. See :member:`glitch_ratelim_burst` for "glitch" rate + * limiter. This field has been available since v1.15.0. + */ + uint64_t glitch_ratelim_rate; } ngtcp2_settings; /** @@ -3000,9 +3018,8 @@ typedef int (*ngtcp2_begin_path_validation)(ngtcp2_conn *conn, uint32_t flags, * an application the outcome of path validation. |flags| is zero or * more of :macro:`NGTCP2_PATH_VALIDATION_FLAG_* * `. |path| is the path that was - * validated. |old_path| is the path that is previously used before a - * local endpoint has migrated to |path| if |old_path| is not NULL. - * If |res| is + * validated. |fallback_path|, if not NULL, is the path that is used + * if the path validation failed. If |res| is * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`, * the path validation succeeded. If |res| is * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_FAILURE`, @@ -3014,7 +3031,7 @@ typedef int (*ngtcp2_begin_path_validation)(ngtcp2_conn *conn, uint32_t flags, */ typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, uint32_t flags, const ngtcp2_path *path, - const ngtcp2_path *old_path, + const ngtcp2_path *fallback_path, ngtcp2_path_validation_result res, void *user_data); @@ -5598,6 +5615,77 @@ NGTCP2_EXTERN size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn); NGTCP2_EXTERN size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, int64_t stream_id); +/** + * @functypedef + * + * :type:`ngtcp2_write_pkt` is a callback function to write a single + * packet in the buffer pointed by |dest| of length |destlen|. The + * implementation should use `ngtcp2_conn_write_pkt`, + * `ngtcp2_conn_writev_stream`, `ngtcp2_conn_writev_datagram`, or + * their variants to write the packet. |path|, |pi|, |dest|, + * |destlen|, and |ts| should be directly passed to those functions. + * If the callback succeeds, it should return the number of bytes + * written to the buffer. In general, this callback function should + * return the value that the above mentioned functions returned except + * for the following error codes: + * + * - :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` + * - :macro:`NGTCP2_ERR_STREAM_SHUT_WR` + * - :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` + * + * Those error codes should be handled by an application. If any + * error occurred outside those functions, return + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. If no packet is produced, + * return 0. + * + * Because GSO requires that the aggregated packets have the same + * length, :macro:`NGTCP2_WRITE_STREAM_FLAG_PADDING` (or + * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_PADDING` if + * `ngtcp2_conn_writev_datagram` is used) is recommended. + * + * This callback function has been available since v1.15.0. + */ +typedef ngtcp2_ssize (*ngtcp2_write_pkt)(ngtcp2_conn *conn, ngtcp2_path *path, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts, + void *user_data); + +/** + * @function + * + * `ngtcp2_conn_write_aggregate_pkt` is a helper function to write + * multiple packets in the provided buffer, which is suitable to be + * sent at once in GSO. This function returns the number of bytes + * written to the buffer pointed by |buf| of length |buflen|. + * |buflen| must be at least + * `ngtcp2_conn_get_path_max_tx_udp_payload_size(conn) + * ` bytes long. It is + * recommended to pass the buffer at least + * `ngtcp2_conn_get_max_tx_udp_payload_size(conn) + * ` bytes in order to send a + * PMTUD packet. This function only writes multiple packets if the + * first packet is `ngtcp2_conn_get_path_max_tx_udp_payload_size(conn) + * ` 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`. + * + * This function returns the number of bytes written to the buffer, or + * a negative error code returned by |write_pkt|. + * + * This function has been available since v1.15.0. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_aggregate_pkt_versioned( + ngtcp2_conn *conn, ngtcp2_path *path, int pkt_info_version, + ngtcp2_pkt_info *pi, uint8_t *buf, size_t buflen, size_t *pgsolen, + ngtcp2_write_pkt write_pkt, ngtcp2_tstamp ts); + /** * @function * @@ -5680,15 +5768,19 @@ NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps); * values. First this function fills |settings| with 0, and set the * default value to the following fields: * - * * :type:`cc_algo ` = + * * :member:`cc_algo ` = * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUBIC` - * * :type:`initial_rtt ` = + * * :member:`initial_rtt ` = * :macro:`NGTCP2_DEFAULT_INITIAL_RTT` - * * :type:`ack_thresh ` = 2 - * * :type:`max_tx_udp_payload_size + * * :member:`ack_thresh ` = 2 + * * :member:`max_tx_udp_payload_size * ` = 1452 - * * :type:`handshake_timeout ` = + * * :member:`handshake_timeout ` = * ``UINT64_MAX`` + * * :member:`glitch_ratelim_burst + * ` = 1000 + * * :member:`glitch_ratelim_rate + * ` = 33 */ NGTCP2_EXTERN void ngtcp2_settings_default_versioned(int settings_version, ngtcp2_settings *settings); @@ -5700,15 +5792,15 @@ NGTCP2_EXTERN void ngtcp2_settings_default_versioned(int settings_version, * default values. First this function fills |params| with 0, and set * the default value to the following fields: * - * * :type:`max_udp_payload_size + * * :member:`max_udp_payload_size * ` = * :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE` - * * :type:`ack_delay_exponent + * * :member:`ack_delay_exponent * ` = * :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT` - * * :type:`max_ack_delay ` = + * * :member:`max_ack_delay ` = * :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY` - * * :type:`active_connection_id_limit + * * :member:`active_connection_id_limit * ` = * :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT` */ @@ -5980,6 +6072,17 @@ NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions, #define ngtcp2_conn_get_conn_info(CONN, CINFO) \ ngtcp2_conn_get_conn_info_versioned((CONN), NGTCP2_CONN_INFO_VERSION, (CINFO)) +/* + * `ngtcp2_conn_write_aggregate_pkt` is a wrapper around + * `ngtcp2_conn_write_aggregate_pkt_versioned` to set the correct + * struct version. + */ +#define ngtcp2_conn_write_aggregate_pkt(CONN, PATH, PI, BUF, BUFLEN, PGSOLEN, \ + WRITE_PKT, TS) \ + ngtcp2_conn_write_aggregate_pkt_versioned( \ + (CONN), (PATH), NGTCP2_PKT_INFO_VERSION, (PI), (BUF), (BUFLEN), (PGSOLEN), \ + (WRITE_PKT), (TS)) + /* * `ngtcp2_settings_default` is a wrapper around * `ngtcp2_settings_default_versioned` to set the correct struct diff --git a/src/contrib/libngtcp2/ngtcp2/version.h b/src/contrib/libngtcp2/ngtcp2/version.h index 533f6d5ef6..4bc5a6a299 100644 --- a/src/contrib/libngtcp2/ngtcp2/version.h +++ b/src/contrib/libngtcp2/ngtcp2/version.h @@ -36,7 +36,7 @@ * * Version number of the ngtcp2 library release. */ -#define NGTCP2_VERSION "1.14.0" +#define NGTCP2_VERSION "1.15.0" /** * @macro @@ -46,6 +46,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGTCP2_VERSION_NUM 0x010e00 +#define NGTCP2_VERSION_NUM 0x010f00 #endif /* !defined(NGTCP2_VERSION_H) */