summaryrefslogtreecommitdiffstats
path: root/contrib/libs/ngtcp2/lib
diff options
context:
space:
mode:
authorrobot-contrib <[email protected]>2025-10-09 23:34:05 +0300
committerrobot-contrib <[email protected]>2025-10-09 23:46:51 +0300
commitbe51fa9be2f4e296199004581c360e275a0d5fe6 (patch)
tree3e97ee0738b17720f2f9f7cac54275f42df6f194 /contrib/libs/ngtcp2/lib
parent531cb793128d1050afd3c08c8de92cd631f14564 (diff)
Update contrib/libs/ngtcp2 to 1.15.1
commit_hash:b2f5dc7047d3d3a77ccd864c0e4bf1d99c87247c
Diffstat (limited to 'contrib/libs/ngtcp2/lib')
-rw-r--r--contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2.h131
-rw-r--r--contrib/libs/ngtcp2/lib/includes/ngtcp2/version.h4
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_acktr.c6
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_bbr.c19
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_bbr.h7
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_buf.c6
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_buf.h11
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_conn.c647
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_conn.h15
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_frame_chain.h4
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_pcg.c88
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_pcg.h54
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_pkt.c293
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_pkt.h83
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_ratelim.c77
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_ratelim.h59
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_rob.c22
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_rob.h8
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_rtb.c4
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_settings.c7
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_settings.h7
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_strm.c13
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_strm.h8
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_vec.c11
-rw-r--r--contrib/libs/ngtcp2/lib/ngtcp2_vec.h14
25 files changed, 1460 insertions, 138 deletions
diff --git a/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
index aa6b8ea6378..2201fcae0b2 100644
--- a/contrib/libs/ngtcp2/lib/includes/ngtcp2/ngtcp2.h
+++ b/contrib/libs/ngtcp2/lib/includes/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_*
* <NGTCP2_PATH_VALIDATION_FLAG_NONE>`. |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);
@@ -5599,6 +5616,77 @@ 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)
+ * <ngtcp2_conn_get_path_max_tx_udp_payload_size>` bytes long. It is
+ * recommended to pass the buffer at least
+ * `ngtcp2_conn_get_max_tx_udp_payload_size(conn)
+ * <ngtcp2_conn_get_max_tx_udp_payload_size>` 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)
+ * <ngtcp2_conn_get_path_max_tx_udp_payload_size>` bytes long. The
+ * application can adjust the length of the buffer to limit the number
+ * of packets to aggregate. If this function returns positive
+ * integer, all packets share the same :type:`ngtcp2_path` and
+ * :type:`ngtcp2_pkt_info` values, and they are assigned to the
+ * objects pointed by |path| and |pi| respectively. The length of all
+ * packets other than the last packet is assigned to |*pgsolen|. The
+ * length of last packet is equal to or less than |*pgsolen|.
+ * |write_pkt| must write a single packet. After all packets are
+ * written, this function calls `ngtcp2_conn_update_pkt_tx_time`.
+ *
+ * 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
*
* `ngtcp2_strerror` returns the text representation of |liberr|.
@@ -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 <ngtcp2_settings.cc_algo>` =
+ * * :member:`cc_algo <ngtcp2_settings.cc_algo>` =
* :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUBIC`
- * * :type:`initial_rtt <ngtcp2_settings.initial_rtt>` =
+ * * :member:`initial_rtt <ngtcp2_settings.initial_rtt>` =
* :macro:`NGTCP2_DEFAULT_INITIAL_RTT`
- * * :type:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2
- * * :type:`max_tx_udp_payload_size
+ * * :member:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2
+ * * :member:`max_tx_udp_payload_size
* <ngtcp2_settings.max_tx_udp_payload_size>` = 1452
- * * :type:`handshake_timeout <ngtcp2_settings.handshake_timeout>` =
+ * * :member:`handshake_timeout <ngtcp2_settings.handshake_timeout>` =
* ``UINT64_MAX``
+ * * :member:`glitch_ratelim_burst
+ * <ngtcp2_settings.glitch_ratelim_burst>` = 1000
+ * * :member:`glitch_ratelim_rate
+ * <ngtcp2_settings.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
* <ngtcp2_transport_params.max_udp_payload_size>` =
* :macro:`NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE`
- * * :type:`ack_delay_exponent
+ * * :member:`ack_delay_exponent
* <ngtcp2_transport_params.ack_delay_exponent>` =
* :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT`
- * * :type:`max_ack_delay <ngtcp2_transport_params.max_ack_delay>` =
+ * * :member:`max_ack_delay <ngtcp2_transport_params.max_ack_delay>` =
* :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY`
- * * :type:`active_connection_id_limit
+ * * :member:`active_connection_id_limit
* <ngtcp2_transport_params.active_connection_id_limit>` =
* :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT`
*/
@@ -5981,6 +6073,17 @@ NGTCP2_EXTERN uint32_t ngtcp2_select_version(const uint32_t *preferred_versions,
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
* version.
diff --git a/contrib/libs/ngtcp2/lib/includes/ngtcp2/version.h b/contrib/libs/ngtcp2/lib/includes/ngtcp2/version.h
index 533f6d5ef6c..e70a095ceb1 100644
--- a/contrib/libs/ngtcp2/lib/includes/ngtcp2/version.h
+++ b/contrib/libs/ngtcp2/lib/includes/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.1"
/**
* @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 0x010f01
#endif /* !defined(NGTCP2_VERSION_H) */
diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_acktr.c b/contrib/libs/ngtcp2/lib/ngtcp2_acktr.c
index 776dc0c2c3e..59bc621ef46 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_acktr.c
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_bbr.c b/contrib/libs/ngtcp2/lib/ngtcp2_bbr.c
index a2ffeb6188a..44be1e189b8 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_bbr.c
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_bbr.h b/contrib/libs/ngtcp2/lib/ngtcp2_bbr.h
index e823711a500..0499924f582 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_bbr.h
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_buf.c b/contrib/libs/ngtcp2/lib/ngtcp2_buf.c
index 75326d6b76b..bf4273f816a 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_buf.c
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_buf.h b/contrib/libs/ngtcp2/lib/ngtcp2_buf.h
index b2bdafc3875..b59ac9a54b3 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_buf.h
+++ b/contrib/libs/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,
@@ -81,6 +79,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.
*/
typedef struct ngtcp2_buf_chain ngtcp2_buf_chain;
diff --git a/contrib/libs/ngtcp2/lib/ngtcp2_conn.c b/contrib/libs/ngtcp2/lib/ngtcp2_conn.c
index 393c2281f1a..d97ba6a71b2 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_conn.c
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_conn.h b/contrib/libs/ngtcp2/lib/ngtcp2_conn.h
index 5979d39654b..2d607d379fc 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_conn.h
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_frame_chain.h b/contrib/libs/ngtcp2/lib/ngtcp2_frame_chain.h
index e7b33632529..01f07cfa4d9 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_frame_chain.h
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_pcg.c b/contrib/libs/ngtcp2/lib/ngtcp2_pcg.c
new file mode 100644
index 00000000000..9d0eb57e0db
--- /dev/null
+++ b/contrib/libs/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 <assert.h>
+
+/*
+ * 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 <[email protected]>,
+ * 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/contrib/libs/ngtcp2/lib/ngtcp2_pcg.h b/contrib/libs/ngtcp2/lib/ngtcp2_pcg.h
new file mode 100644
index 00000000000..a637183efc1
--- /dev/null
+++ b/contrib/libs/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 <config.h>
+#endif /* defined(HAVE_CONFIG_H) */
+
+#include <ngtcp2/ngtcp2.h>
+
+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/contrib/libs/ngtcp2/lib/ngtcp2_pkt.c b/contrib/libs/ngtcp2/lib/ngtcp2_pkt.c
index af8a059d08f..d63dc932e1b 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_pkt.c
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_pkt.h b/contrib/libs/ngtcp2/lib/ngtcp2_pkt.h
index ec72708e042..ed358dc48d5 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_pkt.h
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_ratelim.c b/contrib/libs/ngtcp2/lib/ngtcp2_ratelim.c
new file mode 100644
index 00000000000..efedc3daa76
--- /dev/null
+++ b/contrib/libs/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 <assert.h>
+
+#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/contrib/libs/ngtcp2/lib/ngtcp2_ratelim.h b/contrib/libs/ngtcp2/lib/ngtcp2_ratelim.h
new file mode 100644
index 00000000000..14485c562d4
--- /dev/null
+++ b/contrib/libs/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 <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2.h>
+
+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/contrib/libs/ngtcp2/lib/ngtcp2_rob.c b/contrib/libs/ngtcp2/lib/ngtcp2_rob.c
index 853f1d650ea..ef1938ea632 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_rob.c
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_rob.h b/contrib/libs/ngtcp2/lib/ngtcp2_rob.h
index d53b5160b10..60a1c5b46a0 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_rob.h
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_rtb.c b/contrib/libs/ngtcp2/lib/ngtcp2_rtb.c
index b50f482bc7e..7df1c197db7 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_rtb.c
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_settings.c b/contrib/libs/ngtcp2/lib/ngtcp2_settings.c
index 77a68bd112e..f774504282e 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_settings.c
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_settings.h b/contrib/libs/ngtcp2/lib/ngtcp2_settings.h
index 80466d43e47..caa0fb58c58 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_settings.h
+++ b/contrib/libs/ngtcp2/lib/ngtcp2_settings.h
@@ -31,6 +31,13 @@
#include <ngtcp2/ngtcp2.h>
+/* 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/contrib/libs/ngtcp2/lib/ngtcp2_strm.c b/contrib/libs/ngtcp2/lib/ngtcp2_strm.c
index 70ec6ee2fe9..faa41771322 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_strm.c
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_strm.h b/contrib/libs/ngtcp2/lib/ngtcp2_strm.h
index c72f8b9dc89..1a1e8fd3b7d 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_strm.h
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_vec.c b/contrib/libs/ngtcp2/lib/ngtcp2_vec.c
index dbca8691d64..ada027b9095 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_vec.c
+++ b/contrib/libs/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/contrib/libs/ngtcp2/lib/ngtcp2_vec.h b/contrib/libs/ngtcp2/lib/ngtcp2_vec.h
index d90a3204a9f..af9b4d64453 100644
--- a/contrib/libs/ngtcp2/lib/ngtcp2_vec.h
+++ b/contrib/libs/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) */