diff options
| author | robot-contrib <[email protected]> | 2025-10-09 23:34:05 +0300 | 
|---|---|---|
| committer | robot-contrib <[email protected]> | 2025-10-09 23:46:51 +0300 | 
| commit | be51fa9be2f4e296199004581c360e275a0d5fe6 (patch) | |
| tree | 3e97ee0738b17720f2f9f7cac54275f42df6f194 /contrib/libs/ngtcp2/lib | |
| parent | 531cb793128d1050afd3c08c8de92cd631f14564 (diff) | |
Update contrib/libs/ngtcp2 to 1.15.1
commit_hash:b2f5dc7047d3d3a77ccd864c0e4bf1d99c87247c
Diffstat (limited to 'contrib/libs/ngtcp2/lib')
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) */ | 
