aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVladimir Homutov <vl@nginx.com>2021-04-13 14:41:20 +0300
committerVladimir Homutov <vl@nginx.com>2021-04-13 14:41:20 +0300
commit8df0b6bb2c3477cd0666b796b4e1a7373c4a600c (patch)
tree370c2e03da266b975df79b5ca56a34fa97e28915 /src
parent9326156b8b2599a56632a3afd5f66a0923d595fe (diff)
downloadnginx-8df0b6bb2c3477cd0666b796b4e1a7373c4a600c.tar.gz
nginx-8df0b6bb2c3477cd0666b796b4e1a7373c4a600c.zip
QUIC: separate files for output and ack related processing.
Diffstat (limited to 'src')
-rw-r--r--src/event/quic/ngx_event_quic.c1930
-rw-r--r--src/event/quic/ngx_event_quic_ack.c1081
-rw-r--r--src/event/quic/ngx_event_quic_ack.h31
-rw-r--r--src/event/quic/ngx_event_quic_connection.h41
-rw-r--r--src/event/quic/ngx_event_quic_output.c851
-rw-r--r--src/event/quic/ngx_event_quic_output.h40
6 files changed, 2021 insertions, 1953 deletions
diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c
index 703425085..365488701 100644
--- a/src/event/quic/ngx_event_quic.c
+++ b/src/event/quic/ngx_event_quic.c
@@ -11,28 +11,12 @@
#include <ngx_event_quic_connection.h>
-#define ngx_quic_lost_threshold(qc) \
- ngx_max(NGX_QUIC_TIME_THR * ngx_max((qc)->latest_rtt, (qc)->avg_rtt), \
- NGX_QUIC_TIME_GRANULARITY)
-
/*
* 7.4. Cryptographic Message Buffering
* Implementations MUST support buffering at least 4096 bytes of data
*/
#define NGX_QUIC_MAX_BUFFERED 65535
-/*
- * Endpoints MUST discard packets that are too small to be valid QUIC
- * packets. With the set of AEAD functions defined in [QUIC-TLS],
- * packets that are smaller than 21 bytes are never valid.
- */
-#define NGX_QUIC_MIN_PKT_LEN 21
-
-#define NGX_QUIC_MIN_SR_PACKET 43 /* 5 random + 16 srt + 22 padding */
-#define NGX_QUIC_MAX_SR_PACKET 1200
-
-#define NGX_QUIC_MAX_ACK_GAP 2
-
#if BORINGSSL_API_VERSION >= 10
static int ngx_quic_set_read_secret(ngx_ssl_conn_t *ssl_conn,
@@ -50,30 +34,19 @@ static int ngx_quic_set_encryption_secrets(ngx_ssl_conn_t *ssl_conn,
static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
enum ssl_encryption_level_t level, const uint8_t *data, size_t len);
static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn);
-static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
- enum ssl_encryption_level_t level, uint8_t alert);
static ngx_int_t ngx_quic_apply_transport_params(ngx_connection_t *c,
ngx_quic_tp_t *ctp);
static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_send_stateless_reset(ngx_connection_t *c,
- ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_process_stateless_reset(ngx_connection_t *c,
ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c,
- ngx_quic_header_t *inpkt);
-static ngx_int_t ngx_quic_send_retry(ngx_connection_t *c,
- ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_new_token(ngx_connection_t *c, u_char *key,
- ngx_str_t *token, ngx_str_t *odcid, time_t expires, ngx_uint_t is_retry);
static void ngx_quic_address_hash(ngx_connection_t *c, ngx_uint_t no_port,
u_char buf[20]);
static ngx_int_t ngx_quic_validate_token(ngx_connection_t *c,
u_char *key, ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
-static ngx_inline size_t ngx_quic_max_udp_payload(ngx_connection_t *c);
static void ngx_quic_input_handler(ngx_event_t *rev);
static ngx_int_t ngx_quic_close_quic(ngx_connection_t *c, ngx_int_t rc);
@@ -85,60 +58,21 @@ static ngx_int_t ngx_quic_process_packet(ngx_connection_t *c,
ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_process_payload(ngx_connection_t *c,
ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_send_early_cc(ngx_connection_t *c,
- ngx_quic_header_t *inpkt, ngx_uint_t err, const char *reason);
static void ngx_quic_discard_ctx(ngx_connection_t *c,
enum ssl_encryption_level_t level);
static ngx_int_t ngx_quic_check_csid(ngx_quic_connection_t *qc,
ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_handle_frames(ngx_connection_t *c,
ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_ack_packet(ngx_connection_t *c,
- ngx_quic_header_t *pkt);
-static ngx_int_t ngx_quic_send_ack_range(ngx_connection_t *c,
- ngx_quic_send_ctx_t *ctx, uint64_t smallest, uint64_t largest);
-static void ngx_quic_drop_ack_ranges(ngx_connection_t *c,
- ngx_quic_send_ctx_t *ctx, uint64_t pn);
-static ngx_int_t ngx_quic_send_ack(ngx_connection_t *c,
- ngx_quic_send_ctx_t *ctx);
-static ngx_int_t ngx_quic_send_cc(ngx_connection_t *c);
-static ngx_int_t ngx_quic_send_new_token(ngx_connection_t *c);
-
-static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
- ngx_quic_header_t *pkt, ngx_quic_frame_t *f);
-static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,
- ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max,
- ngx_msec_t *send_time);
-static void ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
- enum ssl_encryption_level_t level, ngx_msec_t send_time);
+
static ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c,
ngx_quic_header_t *pkt, ngx_quic_frame_t *frame);
ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c,
ngx_quic_frame_t *frame, void *data);
-static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);
-static ngx_int_t ngx_quic_generate_ack(ngx_connection_t *c,
- ngx_quic_send_ctx_t *ctx);
-static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
- ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
-static ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len);
-
-static void ngx_quic_set_packet_number(ngx_quic_header_t *pkt,
- ngx_quic_send_ctx_t *ctx);
-static void ngx_quic_pto_handler(ngx_event_t *ev);
-static void ngx_quic_lost_handler(ngx_event_t *ev);
-static ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c);
-static void ngx_quic_set_lost_timer(ngx_connection_t *c);
-static void ngx_quic_resend_frames(ngx_connection_t *c,
- ngx_quic_send_ctx_t *ctx);
static void ngx_quic_push_handler(ngx_event_t *ev);
-static void ngx_quic_congestion_ack(ngx_connection_t *c,
- ngx_quic_frame_t *frame);
-static void ngx_quic_congestion_lost(ngx_connection_t *c,
- ngx_quic_frame_t *frame);
-
static ngx_core_module_t ngx_quic_module_ctx = {
ngx_string("quic"),
@@ -178,7 +112,7 @@ static SSL_QUIC_METHOD quic_method = {
#if (NGX_DEBUG)
-static void
+void
ngx_quic_connstate_dbg(ngx_connection_t *c)
{
u_char *p, *last;
@@ -241,10 +175,6 @@ ngx_quic_connstate_dbg(ngx_connection_t *c)
"quic %*s", p - buf, buf);
}
-#else
-
-#define ngx_quic_connstate_dbg(c)
-
#endif
@@ -468,38 +398,6 @@ ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn)
}
-static int
-ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
- uint8_t alert)
-{
- ngx_connection_t *c;
- ngx_quic_connection_t *qc;
-
- c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
-
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic ngx_quic_send_alert() lvl:%d alert:%d",
- (int) level, (int) alert);
-
- qc = ngx_quic_get_connection(c);
- if (qc == NULL) {
- return 1;
- }
-
- qc->error_level = level;
- qc->error = NGX_QUIC_ERR_CRYPTO(alert);
- qc->error_reason = "TLS alert";
- qc->error_app = 0;
- qc->error_ftype = 0;
-
- if (ngx_quic_send_cc(c) != NGX_OK) {
- return 0;
- }
-
- return 1;
-}
-
-
static ngx_int_t
ngx_quic_apply_transport_params(ngx_connection_t *c, ngx_quic_tp_t *ctp)
{
@@ -722,57 +620,6 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf,
}
-static ngx_int_t
-ngx_quic_send_stateless_reset(ngx_connection_t *c, ngx_quic_conf_t *conf,
- ngx_quic_header_t *pkt)
-{
- u_char *token;
- size_t len, max;
- uint16_t rndbytes;
- u_char buf[NGX_QUIC_MAX_SR_PACKET];
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic handle stateless reset output");
-
- if (pkt->len <= NGX_QUIC_MIN_PKT_LEN) {
- return NGX_DECLINED;
- }
-
- if (pkt->len <= NGX_QUIC_MIN_SR_PACKET) {
- len = pkt->len - 1;
-
- } else {
- max = ngx_min(NGX_QUIC_MAX_SR_PACKET, pkt->len * 3);
-
- if (RAND_bytes((u_char *) &rndbytes, sizeof(rndbytes)) != 1) {
- return NGX_ERROR;
- }
-
- len = (rndbytes % (max - NGX_QUIC_MIN_SR_PACKET + 1))
- + NGX_QUIC_MIN_SR_PACKET;
- }
-
- if (RAND_bytes(buf, len - NGX_QUIC_SR_TOKEN_LEN) != 1) {
- return NGX_ERROR;
- }
-
- buf[0] &= ~NGX_QUIC_PKT_LONG;
- buf[0] |= NGX_QUIC_PKT_FIXED_BIT;
-
- token = &buf[len - NGX_QUIC_SR_TOKEN_LEN];
-
- if (ngx_quic_new_sr_token(c, &pkt->dcid, conf->sr_token_key, token)
- != NGX_OK)
- {
- return NGX_ERROR;
- }
-
- (void) ngx_quic_send(c, buf, len);
-
- return NGX_DECLINED;
-}
-
-
ngx_int_t
ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid, u_char *secret,
u_char *token)
@@ -843,102 +690,7 @@ ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)
}
-static ngx_int_t
-ngx_quic_negotiate_version(ngx_connection_t *c, ngx_quic_header_t *inpkt)
-{
- size_t len;
- ngx_quic_header_t pkt;
- static u_char buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "sending version negotiation packet");
-
- pkt.log = c->log;
- pkt.flags = NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_FIXED_BIT;
- pkt.dcid = inpkt->scid;
- pkt.scid = inpkt->dcid;
-
- len = ngx_quic_create_version_negotiation(&pkt, buf);
-
-#ifdef NGX_QUIC_DEBUG_PACKETS
- ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic vnego packet to send len:%uz %*xs", len, len, buf);
-#endif
-
- (void) ngx_quic_send(c, buf, len);
-
- return NGX_ERROR;
-}
-
-
-static ngx_int_t
-ngx_quic_send_retry(ngx_connection_t *c, ngx_quic_conf_t *conf,
- ngx_quic_header_t *inpkt)
-{
- time_t expires;
- ssize_t len;
- ngx_str_t res, token;
- ngx_quic_header_t pkt;
-
- u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE];
- u_char dcid[NGX_QUIC_SERVER_CID_LEN];
-
- expires = ngx_time() + NGX_QUIC_RETRY_TOKEN_LIFETIME;
-
- if (ngx_quic_new_token(c, conf->av_token_key, &token, &inpkt->dcid,
- expires, 1)
- != NGX_OK)
- {
- return NGX_ERROR;
- }
-
- ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
- pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_RETRY;
- pkt.version = inpkt->version;
- pkt.log = c->log;
-
- pkt.odcid = inpkt->dcid;
- pkt.dcid = inpkt->scid;
-
- /* TODO: generate routable dcid */
- if (RAND_bytes(dcid, NGX_QUIC_SERVER_CID_LEN) != 1) {
- return NGX_ERROR;
- }
-
- pkt.scid.len = NGX_QUIC_SERVER_CID_LEN;
- pkt.scid.data = dcid;
-
- pkt.token = token;
-
- res.data = buf;
-
- if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
- return NGX_ERROR;
- }
-
-#ifdef NGX_QUIC_DEBUG_PACKETS
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic packet to send len:%uz %xV", res.len, &res);
-#endif
-
- len = ngx_quic_send(c, res.data, res.len);
- if (len == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic retry packet sent to %xV", &pkt.dcid);
-
- /*
- * quic-transport 17.2.5.1: A server MUST NOT send more than one Retry
- * packet in response to a single UDP datagram.
- * NGX_DONE will stop quic_input() from processing further
- */
- return NGX_DONE;
-}
-
-
-static ngx_int_t
+ngx_int_t
ngx_quic_new_token(ngx_connection_t *c, u_char *key, ngx_str_t *token,
ngx_str_t *odcid, time_t exp, ngx_uint_t is_retry)
{
@@ -1267,21 +1019,6 @@ ngx_quic_init_connection(ngx_connection_t *c)
}
-static ngx_inline size_t
-ngx_quic_max_udp_payload(ngx_connection_t *c)
-{
- /* TODO: path MTU discovery */
-
-#if (NGX_HAVE_INET6)
- if (c->sockaddr->sa_family == AF_INET6) {
- return NGX_QUIC_MAX_UDP_PAYLOAD_OUT6;
- }
-#endif
-
- return NGX_QUIC_MAX_UDP_PAYLOAD_OUT;
-}
-
-
static void
ngx_quic_input_handler(ngx_event_t *rev)
{
@@ -1902,83 +1639,6 @@ ngx_quic_process_payload(ngx_connection_t *c, ngx_quic_header_t *pkt)
}
-static ngx_int_t
-ngx_quic_send_early_cc(ngx_connection_t *c, ngx_quic_header_t *inpkt,
- ngx_uint_t err, const char *reason)
-{
- ssize_t len;
- ngx_str_t res;
- ngx_quic_frame_t frame;
- ngx_quic_header_t pkt;
-
- static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
- static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
-
- ngx_memzero(&frame, sizeof(ngx_quic_frame_t));
- ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
-
- frame.level = inpkt->level;
- frame.type = NGX_QUIC_FT_CONNECTION_CLOSE;
- frame.u.close.error_code = err;
-
- frame.u.close.reason.data = (u_char *) reason;
- frame.u.close.reason.len = ngx_strlen(reason);
-
- len = ngx_quic_create_frame(NULL, &frame);
- if (len > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) {
- return NGX_ERROR;
- }
-
- ngx_quic_log_frame(c->log, &frame, 1);
-
- len = ngx_quic_create_frame(src, &frame);
- if (len == -1) {
- return NGX_ERROR;
- }
-
- pkt.keys = ngx_quic_keys_new(c->pool);
- if (pkt.keys == NULL) {
- return NGX_ERROR;
- }
-
- if (ngx_quic_keys_set_initial_secret(c->pool, pkt.keys, &inpkt->dcid,
- inpkt->version)
- != NGX_OK)
- {
- return NGX_ERROR;
- }
-
- pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG
- | NGX_QUIC_PKT_INITIAL;
-
- pkt.num_len = 1;
- /*
- * pkt.num = 0;
- * pkt.trunc = 0;
- */
-
- pkt.version = inpkt->version;
- pkt.log = c->log;
- pkt.level = inpkt->level;
- pkt.dcid = inpkt->scid;
- pkt.scid = inpkt->dcid;
- pkt.payload.data = src;
- pkt.payload.len = len;
-
- res.data = dst;
-
- if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
- return NGX_ERROR;
- }
-
- if (ngx_quic_send(c, res.data, res.len) == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- return NGX_OK;
-}
-
-
static void
ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level)
{
@@ -2291,736 +1951,6 @@ ngx_quic_handle_frames(ngx_connection_t *c, ngx_quic_header_t *pkt)
static ngx_int_t
-ngx_quic_ack_packet(ngx_connection_t *c, ngx_quic_header_t *pkt)
-{
- uint64_t base, largest, smallest, gs, ge, gap, range, pn;
- uint64_t prev_pending;
- ngx_uint_t i, nr;
- ngx_quic_send_ctx_t *ctx;
- ngx_quic_ack_range_t *r;
- ngx_quic_connection_t *qc;
-
- c->log->action = "preparing ack";
-
- qc = ngx_quic_get_connection(c);
-
- ctx = ngx_quic_get_send_ctx(qc, pkt->level);
-
- ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic ngx_quic_ack_packet pn:%uL largest %L fr:%uL"
- " nranges:%ui", pkt->pn, (int64_t) ctx->largest_range,
- ctx->first_range, ctx->nranges);
-
- prev_pending = ctx->pending_ack;
-
- if (pkt->need_ack) {
-
- ngx_post_event(&qc->push, &ngx_posted_events);
-
- if (ctx->send_ack == 0) {
- ctx->ack_delay_start = ngx_current_msec;
- }
-
- ctx->send_ack++;
-
- if (ctx->pending_ack == NGX_QUIC_UNSET_PN
- || ctx->pending_ack < pkt->pn)
- {
- ctx->pending_ack = pkt->pn;
- }
- }
-
- base = ctx->largest_range;
- pn = pkt->pn;
-
- if (base == NGX_QUIC_UNSET_PN) {
- ctx->largest_range = pn;
- ctx->largest_received = pkt->received;
- return NGX_OK;
- }
-
- if (base == pn) {
- return NGX_OK;
- }
-
- largest = base;
- smallest = largest - ctx->first_range;
-
- if (pn > base) {
-
- if (pn - base == 1) {
- ctx->first_range++;
- ctx->largest_range = pn;
- ctx->largest_received = pkt->received;
-
- return NGX_OK;
-
- } else {
- /* new gap in front of current largest */
-
- /* no place for new range, send current range as is */
- if (ctx->nranges == NGX_QUIC_MAX_RANGES) {
-
- if (prev_pending != NGX_QUIC_UNSET_PN) {
- if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
- return NGX_ERROR;
- }
- }
-
- if (prev_pending == ctx->pending_ack || !pkt->need_ack) {
- ctx->pending_ack = NGX_QUIC_UNSET_PN;
- }
- }
-
- gap = pn - base - 2;
- range = ctx->first_range;
-
- ctx->first_range = 0;
- ctx->largest_range = pn;
- ctx->largest_received = pkt->received;
-
- /* packet is out of order, force send */
- if (pkt->need_ack) {
- ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
- }
-
- i = 0;
-
- goto insert;
- }
- }
-
- /* pn < base, perform lookup in existing ranges */
-
- /* packet is out of order */
- if (pkt->need_ack) {
- ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
- }
-
- if (pn >= smallest && pn <= largest) {
- return NGX_OK;
- }
-
-#if (NGX_SUPPRESS_WARN)
- r = NULL;
-#endif
-
- for (i = 0; i < ctx->nranges; i++) {
- r = &ctx->ranges[i];
-
- ge = smallest - 1;
- gs = ge - r->gap;
-
- if (pn >= gs && pn <= ge) {
-
- if (gs == ge) {
- /* gap size is exactly one packet, now filled */
-
- /* data moves to previous range, current is removed */
-
- if (i == 0) {
- ctx->first_range += r->range + 2;
-
- } else {
- ctx->ranges[i - 1].range += r->range + 2;
- }
-
- nr = ctx->nranges - i - 1;
- if (nr) {
- ngx_memmove(&ctx->ranges[i], &ctx->ranges[i + 1],
- sizeof(ngx_quic_ack_range_t) * nr);
- }
-
- ctx->nranges--;
-
- } else if (pn == gs) {
- /* current gap shrinks from tail (current range grows) */
- r->gap--;
- r->range++;
-
- } else if (pn == ge) {
- /* current gap shrinks from head (previous range grows) */
- r->gap--;
-
- if (i == 0) {
- ctx->first_range++;
-
- } else {
- ctx->ranges[i - 1].range++;
- }
-
- } else {
- /* current gap is split into two parts */
-
- gap = ge - pn - 1;
- range = 0;
-
- if (ctx->nranges == NGX_QUIC_MAX_RANGES) {
- if (prev_pending != NGX_QUIC_UNSET_PN) {
- if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
- return NGX_ERROR;
- }
- }
-
- if (prev_pending == ctx->pending_ack || !pkt->need_ack) {
- ctx->pending_ack = NGX_QUIC_UNSET_PN;
- }
- }
-
- r->gap = pn - gs - 1;
- goto insert;
- }
-
- return NGX_OK;
- }
-
- largest = smallest - r->gap - 2;
- smallest = largest - r->range;
-
- if (pn >= smallest && pn <= largest) {
- /* this packet number is already known */
- return NGX_OK;
- }
-
- }
-
- if (pn == smallest - 1) {
- /* extend first or last range */
-
- if (i == 0) {
- ctx->first_range++;
-
- } else {
- r->range++;
- }
-
- return NGX_OK;
- }
-
- /* nothing found, add new range at the tail */
-
- if (ctx->nranges == NGX_QUIC_MAX_RANGES) {
- /* packet is too old to keep it */
-
- if (pkt->need_ack) {
- return ngx_quic_send_ack_range(c, ctx, pn, pn);
- }
-
- return NGX_OK;
- }
-
- gap = smallest - 2 - pn;
- range = 0;
-
-insert:
-
- if (ctx->nranges < NGX_QUIC_MAX_RANGES) {
- ctx->nranges++;
- }
-
- ngx_memmove(&ctx->ranges[i + 1], &ctx->ranges[i],
- sizeof(ngx_quic_ack_range_t) * (ctx->nranges - i - 1));
-
- ctx->ranges[i].gap = gap;
- ctx->ranges[i].range = range;
-
- return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_quic_send_ack_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
- uint64_t smallest, uint64_t largest)
-{
- ngx_quic_frame_t *frame;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
-
- frame = ngx_quic_alloc_frame(c);
- if (frame == NULL) {
- return NGX_ERROR;
- }
-
- frame->level = ctx->level;
- frame->type = NGX_QUIC_FT_ACK;
- frame->u.ack.largest = largest;
- frame->u.ack.delay = 0;
- frame->u.ack.range_count = 0;
- frame->u.ack.first_range = largest - smallest;
-
- ngx_quic_queue_frame(qc, frame);
-
- return NGX_OK;
-}
-
-
-static void
-ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
- uint64_t pn)
-{
- uint64_t base;
- ngx_uint_t i, smallest, largest;
- ngx_quic_ack_range_t *r;
-
- ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic ngx_quic_drop_ack_ranges pn:%uL largest:%uL"
- " fr:%uL nranges:%ui", pn, ctx->largest_range,
- ctx->first_range, ctx->nranges);
-
- base = ctx->largest_range;
-
- if (base == NGX_QUIC_UNSET_PN) {
- return;
- }
-
- if (ctx->pending_ack != NGX_QUIC_UNSET_PN && pn >= ctx->pending_ack) {
- ctx->pending_ack = NGX_QUIC_UNSET_PN;
- }
-
- largest = base;
- smallest = largest - ctx->first_range;
-
- if (pn >= largest) {
- ctx->largest_range = NGX_QUIC_UNSET_PN;
- ctx->first_range = 0;
- ctx->nranges = 0;
- return;
- }
-
- if (pn >= smallest) {
- ctx->first_range = largest - pn - 1;
- ctx->nranges = 0;
- return;
- }
-
- for (i = 0; i < ctx->nranges; i++) {
- r = &ctx->ranges[i];
-
- largest = smallest - r->gap - 2;
- smallest = largest - r->range;
-
- if (pn >= largest) {
- ctx->nranges = i;
- return;
- }
- if (pn >= smallest) {
- r->range = largest - pn - 1;
- ctx->nranges = i + 1;
- return;
- }
- }
-}
-
-
-static ngx_int_t
-ngx_quic_send_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
-{
- size_t len, left;
- uint64_t ack_delay;
- ngx_buf_t *b;
- ngx_uint_t i;
- ngx_chain_t *cl, **ll;
- ngx_quic_frame_t *frame;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
-
- ack_delay = ngx_current_msec - ctx->largest_received;
- ack_delay *= 1000;
- ack_delay >>= qc->tp.ack_delay_exponent;
-
- frame = ngx_quic_alloc_frame(c);
- if (frame == NULL) {
- return NGX_ERROR;
- }
-
- ll = &frame->data;
- b = NULL;
-
- for (i = 0; i < ctx->nranges; i++) {
- len = ngx_quic_create_ack_range(NULL, ctx->ranges[i].gap,
- ctx->ranges[i].range);
-
- left = b ? b->end - b->last : 0;
-
- if (left < len) {
- cl = ngx_quic_alloc_buf(c);
- if (cl == NULL) {
- return NGX_ERROR;
- }
-
- *ll = cl;
- ll = &cl->next;
-
- b = cl->buf;
- left = b->end - b->last;
-
- if (left < len) {
- return NGX_ERROR;
- }
- }
-
- b->last += ngx_quic_create_ack_range(b->last, ctx->ranges[i].gap,
- ctx->ranges[i].range);
-
- frame->u.ack.ranges_length += len;
- }
-
- *ll = NULL;
-
- frame->level = ctx->level;
- frame->type = NGX_QUIC_FT_ACK;
- frame->u.ack.largest = ctx->largest_range;
- frame->u.ack.delay = ack_delay;
- frame->u.ack.range_count = ctx->nranges;
- frame->u.ack.first_range = ctx->first_range;
-
- ngx_quic_queue_frame(qc, frame);
-
- return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_quic_send_cc(ngx_connection_t *c)
-{
- ngx_quic_frame_t *frame;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
-
- if (qc->draining) {
- return NGX_OK;
- }
-
- if (qc->closing
- && ngx_current_msec - qc->last_cc < NGX_QUIC_CC_MIN_INTERVAL)
- {
- /* dot not send CC too often */
- return NGX_OK;
- }
-
- frame = ngx_quic_alloc_frame(c);
- if (frame == NULL) {
- return NGX_ERROR;
- }
-
- frame->level = qc->error_level;
- frame->type = qc->error_app ? NGX_QUIC_FT_CONNECTION_CLOSE_APP
- : NGX_QUIC_FT_CONNECTION_CLOSE;
- frame->u.close.error_code = qc->error;
- frame->u.close.frame_type = qc->error_ftype;
-
- if (qc->error_reason) {
- frame->u.close.reason.len = ngx_strlen(qc->error_reason);
- frame->u.close.reason.data = (u_char *) qc->error_reason;
- }
-
- ngx_quic_queue_frame(qc, frame);
-
- qc->last_cc = ngx_current_msec;
-
- return ngx_quic_output(c);
-}
-
-
-static ngx_int_t
-ngx_quic_send_new_token(ngx_connection_t *c)
-{
- time_t expires;
- ngx_str_t token;
- ngx_quic_frame_t *frame;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
-
- if (!qc->conf->retry) {
- return NGX_OK;
- }
-
- expires = ngx_time() + NGX_QUIC_NEW_TOKEN_LIFETIME;
-
- if (ngx_quic_new_token(c, qc->conf->av_token_key, &token, NULL, expires, 0)
- != NGX_OK)
- {
- return NGX_ERROR;
- }
-
- frame = ngx_quic_alloc_frame(c);
- if (frame == NULL) {
- return NGX_ERROR;
- }
-
- frame->level = ssl_encryption_application;
- frame->type = NGX_QUIC_FT_NEW_TOKEN;
- frame->u.token.length = token.len;
- frame->u.token.data = token.data;
-
- ngx_quic_queue_frame(qc, frame);
-
- return NGX_OK;
-}
-
-
-static ngx_int_t
-ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
- ngx_quic_frame_t *f)
-{
- ssize_t n;
- u_char *pos, *end;
- uint64_t min, max, gap, range;
- ngx_msec_t send_time;
- ngx_uint_t i;
- ngx_quic_send_ctx_t *ctx;
- ngx_quic_ack_frame_t *ack;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
-
- ctx = ngx_quic_get_send_ctx(qc, pkt->level);
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic ngx_quic_handle_ack_frame level:%d", pkt->level);
-
- ack = &f->u.ack;
-
- /*
- * If any computed packet number is negative, an endpoint MUST
- * generate a connection error of type FRAME_ENCODING_ERROR.
- * (19.3.1)
- */
-
- if (ack->first_range > ack->largest) {
- qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "quic invalid first range in ack frame");
- return NGX_ERROR;
- }
-
- min = ack->largest - ack->first_range;
- max = ack->largest;
-
- if (ngx_quic_handle_ack_frame_range(c, ctx, min, max, &send_time)
- != NGX_OK)
- {
- return NGX_ERROR;
- }
-
- /* 13.2.3. Receiver Tracking of ACK Frames */
- if (ctx->largest_ack < max || ctx->largest_ack == NGX_QUIC_UNSET_PN) {
- ctx->largest_ack = max;
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic updated largest received ack:%uL", max);
-
- /*
- * An endpoint generates an RTT sample on receiving an
- * ACK frame that meets the following two conditions:
- *
- * - the largest acknowledged packet number is newly acknowledged
- * - at least one of the newly acknowledged packets was ack-eliciting.
- */
-
- if (send_time != NGX_TIMER_INFINITE) {
- ngx_quic_rtt_sample(c, ack, pkt->level, send_time);
- }
- }
-
- if (f->data) {
- pos = f->data->buf->pos;
- end = f->data->buf->last;
-
- } else {
- pos = NULL;
- end = NULL;
- }
-
- for (i = 0; i < ack->range_count; i++) {
-
- n = ngx_quic_parse_ack_range(pkt->log, pos, end, &gap, &range);
- if (n == NGX_ERROR) {
- return NGX_ERROR;
- }
- pos += n;
-
- if (gap + 2 > min) {
- qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "quic invalid range:%ui in ack frame", i);
- return NGX_ERROR;
- }
-
- max = min - gap - 2;
-
- if (range > max) {
- qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "quic invalid range:%ui in ack frame", i);
- return NGX_ERROR;
- }
-
- min = max - range;
-
- if (ngx_quic_handle_ack_frame_range(c, ctx, min, max, &send_time)
- != NGX_OK)
- {
- return NGX_ERROR;
- }
- }
-
- return ngx_quic_detect_lost(c);
-}
-
-
-static ngx_int_t
-ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
- uint64_t min, uint64_t max, ngx_msec_t *send_time)
-{
- ngx_uint_t found;
- ngx_queue_t *q;
- ngx_quic_frame_t *f;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
-
- *send_time = NGX_TIMER_INFINITE;
- found = 0;
-
- q = ngx_queue_last(&ctx->sent);
-
- while (q != ngx_queue_sentinel(&ctx->sent)) {
-
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
- q = ngx_queue_prev(q);
-
- if (f->pnum >= min && f->pnum <= max) {
- ngx_quic_congestion_ack(c, f);
-
- switch (f->type) {
- case NGX_QUIC_FT_ACK:
- case NGX_QUIC_FT_ACK_ECN:
- ngx_quic_drop_ack_ranges(c, ctx, f->u.ack.largest);
- break;
-
- case NGX_QUIC_FT_STREAM0:
- case NGX_QUIC_FT_STREAM1:
- case NGX_QUIC_FT_STREAM2:
- case NGX_QUIC_FT_STREAM3:
- case NGX_QUIC_FT_STREAM4:
- case NGX_QUIC_FT_STREAM5:
- case NGX_QUIC_FT_STREAM6:
- case NGX_QUIC_FT_STREAM7:
- ngx_quic_handle_stream_ack(c, f);
- break;
- }
-
- if (f->pnum == max) {
- *send_time = f->last;
- }
-
- ngx_queue_remove(&f->queue);
- ngx_quic_free_frame(c, f);
- found = 1;
- }
- }
-
- if (!found) {
-
- if (max < ctx->pnum) {
- /* duplicate ACK or ACK for non-ack-eliciting frame */
- return NGX_OK;
- }
-
- ngx_log_error(NGX_LOG_INFO, c->log, 0,
- "quic ACK for the packet not sent");
-
- qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;
- qc->error_ftype = NGX_QUIC_FT_ACK;
- qc->error_reason = "unknown packet number";
-
- return NGX_ERROR;
- }
-
- if (!qc->push.timer_set) {
- ngx_post_event(&qc->push, &ngx_posted_events);
- }
-
- qc->pto_count = 0;
-
- return NGX_OK;
-}
-
-
-static void
-ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
- enum ssl_encryption_level_t level, ngx_msec_t send_time)
-{
- ngx_msec_t latest_rtt, ack_delay, adjusted_rtt, rttvar_sample;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
-
- latest_rtt = ngx_current_msec - send_time;
- qc->latest_rtt = latest_rtt;
-
- if (qc->min_rtt == NGX_TIMER_INFINITE) {
- qc->min_rtt = latest_rtt;
- qc->avg_rtt = latest_rtt;
- qc->rttvar = latest_rtt / 2;
-
- } else {
- qc->min_rtt = ngx_min(qc->min_rtt, latest_rtt);
-
- ack_delay = ack->delay * (1 << qc->ctp.ack_delay_exponent) / 1000;
-
- if (c->ssl->handshaked) {
- ack_delay = ngx_min(ack_delay, qc->ctp.max_ack_delay);
- }
-
- adjusted_rtt = latest_rtt;
-
- if (qc->min_rtt + ack_delay < latest_rtt) {
- adjusted_rtt -= ack_delay;
- }
-
- qc->avg_rtt = 0.875 * qc->avg_rtt + 0.125 * adjusted_rtt;
- rttvar_sample = ngx_abs((ngx_msec_int_t) (qc->avg_rtt - adjusted_rtt));
- qc->rttvar = 0.75 * qc->rttvar + 0.25 * rttvar_sample;
- }
-
- ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic rtt sample latest:%M min:%M avg:%M var:%M",
- latest_rtt, qc->min_rtt, qc->avg_rtt, qc->rttvar);
-}
-
-
-ngx_msec_t
-ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
-{
- ngx_msec_t duration;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
-
- /* PTO calculation: quic-recovery, Appendix 8 */
- duration = qc->avg_rtt;
-
- duration += ngx_max(4 * qc->rttvar, NGX_QUIC_TIME_GRANULARITY);
- duration <<= qc->pto_count;
-
- if (qc->congestion.in_flight == 0) { /* no in-flight packets */
- return duration;
- }
-
- if (ctx->level == ssl_encryption_application && c->ssl->handshaked) {
- duration += qc->ctp.max_ack_delay << qc->pto_count;
- }
-
- return duration;
-}
-
-
-static ngx_int_t
ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
ngx_quic_frame_t *frame)
{
@@ -3170,507 +2100,6 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_quic_frame_t *frame, void *data)
}
-ngx_int_t
-ngx_quic_output(ngx_connection_t *c)
-{
- off_t max;
- size_t len, min, in_flight;
- ssize_t n;
- u_char *p;
- ngx_uint_t i, pad;
- ngx_quic_send_ctx_t *ctx;
- ngx_quic_congestion_t *cg;
- ngx_quic_connection_t *qc;
- static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
-
- c->log->action = "sending frames";
-
- qc = ngx_quic_get_connection(c);
- cg = &qc->congestion;
-
- in_flight = cg->in_flight;
-
- for ( ;; ) {
- p = dst;
-
- len = ngx_min(qc->ctp.max_udp_payload_size,
- NGX_QUIC_MAX_UDP_PAYLOAD_SIZE);
-
- if (!qc->validated) {
- max = qc->received * 3;
- max = (c->sent >= max) ? 0 : max - c->sent;
- len = ngx_min(len, (size_t) max);
- }
-
- pad = ngx_quic_get_padding_level(c);
-
- for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
-
- ctx = &qc->send_ctx[i];
-
- if (ngx_quic_generate_ack(c, ctx) != NGX_OK) {
- return NGX_ERROR;
- }
-
- min = (i == pad && p - dst < NGX_QUIC_MIN_INITIAL_SIZE)
- ? NGX_QUIC_MIN_INITIAL_SIZE - (p - dst) : 0;
-
- n = ngx_quic_output_packet(c, ctx, p, len, min);
- if (n == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- p += n;
- len -= n;
- }
-
- len = p - dst;
- if (len == 0) {
- break;
- }
-
- n = ngx_quic_send(c, dst, len);
- if (n == NGX_ERROR) {
- return NGX_ERROR;
- }
- }
-
- if (in_flight != cg->in_flight && !qc->send_timer_set && !qc->closing) {
- qc->send_timer_set = 1;
- ngx_add_timer(c->read, qc->tp.max_idle_timeout);
- }
-
- ngx_quic_set_lost_timer(c);
-
- return NGX_OK;
-}
-
-
-static ngx_uint_t
-ngx_quic_get_padding_level(ngx_connection_t *c)
-{
- ngx_queue_t *q;
- ngx_quic_frame_t *f;
- ngx_quic_send_ctx_t *ctx;
- ngx_quic_connection_t *qc;
-
- /*
- * 14.1. Initial Datagram Size
- *
- * Similarly, a server MUST expand the payload of all UDP datagrams
- * carrying ack-eliciting Initial packets to at least the smallest
- * allowed maximum datagram size of 1200 bytes
- */
-
- qc = ngx_quic_get_connection(c);
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
-
- for (q = ngx_queue_head(&ctx->frames);
- q != ngx_queue_sentinel(&ctx->frames);
- q = ngx_queue_next(q))
- {
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
- if (f->need_ack) {
- ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
-
- if (ngx_queue_empty(&ctx->frames)) {
- return 0;
- }
-
- return 1;
- }
- }
-
- return NGX_QUIC_SEND_CTX_LAST;
-}
-
-
-static ngx_int_t
-ngx_quic_generate_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
-{
- ngx_msec_t delay;
- ngx_quic_connection_t *qc;
-
- if (!ctx->send_ack) {
- return NGX_OK;
- }
-
- if (ctx->level == ssl_encryption_application) {
-
- delay = ngx_current_msec - ctx->ack_delay_start;
- qc = ngx_quic_get_connection(c);
-
- if (ctx->send_ack < NGX_QUIC_MAX_ACK_GAP
- && delay < qc->tp.max_ack_delay)
- {
- if (!qc->push.timer_set && !qc->closing) {
- ngx_add_timer(&qc->push,
- qc->tp.max_ack_delay - delay);
- }
-
- return NGX_OK;
- }
- }
-
- if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
- return NGX_ERROR;
- }
-
- ctx->send_ack = 0;
-
- return NGX_OK;
-}
-
-
-static ssize_t
-ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
- u_char *data, size_t max, size_t min)
-{
- size_t len, hlen, pad_len;
- u_char *p;
- ssize_t flen;
- ngx_str_t out, res;
- ngx_int_t rc;
- ngx_uint_t nframes;
- ngx_msec_t now;
- ngx_queue_t *q;
- ngx_quic_frame_t *f;
- ngx_quic_header_t pkt;
- ngx_quic_congestion_t *cg;
- ngx_quic_connection_t *qc;
- static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
-
- if (ngx_queue_empty(&ctx->frames)) {
- return 0;
- }
-
- ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic output %s packet max:%uz min:%uz",
- ngx_quic_level_name(ctx->level), max, min);
-
- qc = ngx_quic_get_connection(c);
- cg = &qc->congestion;
-
- hlen = (ctx->level == ssl_encryption_application)
- ? NGX_QUIC_MAX_SHORT_HEADER
- : NGX_QUIC_MAX_LONG_HEADER;
-
- hlen += EVP_GCM_TLS_TAG_LEN;
- hlen -= NGX_QUIC_MAX_CID_LEN - qc->scid.len;
-
- ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
-
- now = ngx_current_msec;
- nframes = 0;
- p = src;
- len = 0;
-
- for (q = ngx_queue_head(&ctx->frames);
- q != ngx_queue_sentinel(&ctx->frames);
- q = ngx_queue_next(q))
- {
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
- if (!pkt.need_ack && f->need_ack && max > cg->window) {
- max = cg->window;
- }
-
- if (hlen + len >= max) {
- break;
- }
-
- if (hlen + len + f->len > max) {
- rc = ngx_quic_split_frame(c, f, max - hlen - len);
-
- if (rc == NGX_ERROR) {
- return NGX_ERROR;
- }
-
- if (rc == NGX_DECLINED) {
- break;
- }
- }
-
- if (f->need_ack) {
- pkt.need_ack = 1;
- }
-
- ngx_quic_log_frame(c->log, f, 1);
-
- flen = ngx_quic_create_frame(p, f);
- if (flen == -1) {
- return NGX_ERROR;
- }
-
- len += flen;
- p += flen;
-
- f->pnum = ctx->pnum;
- f->first = now;
- f->last = now;
- f->plen = 0;
-
- nframes++;
-
- if (f->flush) {
- break;
- }
- }
-
- if (nframes == 0) {
- return 0;
- }
-
- out.data = src;
- out.len = len;
-
- pkt.keys = qc->keys;
- pkt.flags = NGX_QUIC_PKT_FIXED_BIT;
-
- if (ctx->level == ssl_encryption_initial) {
- pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;
-
- } else if (ctx->level == ssl_encryption_handshake) {
- pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;
-
- } else {
- if (qc->key_phase) {
- pkt.flags |= NGX_QUIC_PKT_KPHASE;
- }
- }
-
- ngx_quic_set_packet_number(&pkt, ctx);
-
- pkt.version = qc->version;
- pkt.log = c->log;
- pkt.level = ctx->level;
- pkt.dcid = qc->scid;
- pkt.scid = qc->dcid;
-
- pad_len = 4;
-
- if (min) {
- hlen = EVP_GCM_TLS_TAG_LEN
- + ngx_quic_create_header(&pkt, NULL, out.len, NULL);
-
- if (min > hlen + pad_len) {
- pad_len = min - hlen;
- }
- }
-
- if (out.len < pad_len) {
- ngx_memset(p, NGX_QUIC_FT_PADDING, pad_len - out.len);
- out.len = pad_len;
- }
-
- pkt.payload = out;
-
- res.data = data;
-
- ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic packet tx %s bytes:%ui"
- " need_ack:%d number:%L encoded nl:%d trunc:0x%xD",
- ngx_quic_level_name(ctx->level), out.len, pkt.need_ack,
- pkt.number, pkt.num_len, pkt.trunc);
-
- if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
- return NGX_ERROR;
- }
-
- ctx->pnum++;
-
- if (pkt.need_ack) {
- /* move frames into the sent queue to wait for ack */
-
- if (!qc->closing) {
- q = ngx_queue_head(&ctx->frames);
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
- f->plen = res.len;
-
- do {
- q = ngx_queue_head(&ctx->frames);
- ngx_queue_remove(q);
- ngx_queue_insert_tail(&ctx->sent, q);
- } while (--nframes);
- }
-
- cg->in_flight += res.len;
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic congestion send if:%uz", cg->in_flight);
- }
-
- while (nframes--) {
- q = ngx_queue_head(&ctx->frames);
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
- ngx_queue_remove(q);
- ngx_quic_free_frame(c, f);
- }
-
- return res.len;
-}
-
-
-static ssize_t
-ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len)
-{
- ngx_buf_t b;
- ngx_chain_t cl, *res;
-
- ngx_memzero(&b, sizeof(ngx_buf_t));
-
- b.pos = b.start = buf;
- b.last = b.end = buf + len;
- b.last_buf = 1;
- b.temporary = 1;
-
- cl.buf = &b;
- cl.next= NULL;
-
- res = c->send_chain(c, &cl, 0);
- if (res == NGX_CHAIN_ERROR) {
- return NGX_ERROR;
- }
-
- return len;
-}
-
-
-static void
-ngx_quic_set_packet_number(ngx_quic_header_t *pkt, ngx_quic_send_ctx_t *ctx)
-{
- uint64_t delta;
-
- delta = ctx->pnum - ctx->largest_ack;
- pkt->number = ctx->pnum;
-
- if (delta <= 0x7F) {
- pkt->num_len = 1;
- pkt->trunc = ctx->pnum & 0xff;
-
- } else if (delta <= 0x7FFF) {
- pkt->num_len = 2;
- pkt->flags |= 0x1;
- pkt->trunc = ctx->pnum & 0xffff;
-
- } else if (delta <= 0x7FFFFF) {
- pkt->num_len = 3;
- pkt->flags |= 0x2;
- pkt->trunc = ctx->pnum & 0xffffff;
-
- } else {
- pkt->num_len = 4;
- pkt->flags |= 0x3;
- pkt->trunc = ctx->pnum & 0xffffffff;
- }
-}
-
-
-static void
-ngx_quic_pto_handler(ngx_event_t *ev)
-{
- ngx_uint_t i;
- ngx_msec_t now;
- ngx_queue_t *q, *next;
- ngx_connection_t *c;
- ngx_quic_frame_t *f;
- ngx_quic_send_ctx_t *ctx;
- ngx_quic_connection_t *qc;
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic pto timer");
-
- c = ev->data;
- qc = ngx_quic_get_connection(c);
- now = ngx_current_msec;
-
- for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
-
- ctx = &qc->send_ctx[i];
-
- if (ngx_queue_empty(&ctx->sent)) {
- continue;
- }
-
- q = ngx_queue_head(&ctx->sent);
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
- if (f->pnum <= ctx->largest_ack
- && ctx->largest_ack != NGX_QUIC_UNSET_PN)
- {
- continue;
- }
-
- if ((ngx_msec_int_t) (f->last + ngx_quic_pto(c, ctx) - now) > 0) {
- continue;
- }
-
- ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic pto %s pto_count:%ui",
- ngx_quic_level_name(ctx->level), qc->pto_count);
-
- for (q = ngx_queue_head(&ctx->frames);
- q != ngx_queue_sentinel(&ctx->frames);
- /* void */)
- {
- next = ngx_queue_next(q);
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
- if (f->type == NGX_QUIC_FT_PING) {
- ngx_queue_remove(q);
- ngx_quic_free_frame(c, f);
- }
-
- q = next;
- }
-
- for (q = ngx_queue_head(&ctx->sent);
- q != ngx_queue_sentinel(&ctx->sent);
- /* void */)
- {
- next = ngx_queue_next(q);
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
- if (f->type == NGX_QUIC_FT_PING) {
- ngx_quic_congestion_lost(c, f);
- ngx_queue_remove(q);
- ngx_quic_free_frame(c, f);
- }
-
- q = next;
- }
-
- /* enforce 2 udp datagrams */
-
- f = ngx_quic_alloc_frame(c);
- if (f == NULL) {
- break;
- }
-
- f->level = ctx->level;
- f->type = NGX_QUIC_FT_PING;
- f->flush = 1;
-
- ngx_quic_queue_frame(qc, f);
-
- f = ngx_quic_alloc_frame(c);
- if (f == NULL) {
- break;
- }
-
- f->level = ctx->level;
- f->type = NGX_QUIC_FT_PING;
-
- ngx_quic_queue_frame(qc, f);
- }
-
- qc->pto_count++;
-
- ngx_quic_connstate_dbg(c);
-}
-
-
static void
ngx_quic_push_handler(ngx_event_t *ev)
{
@@ -3689,265 +2118,6 @@ ngx_quic_push_handler(ngx_event_t *ev)
}
-static
-void ngx_quic_lost_handler(ngx_event_t *ev)
-{
- ngx_connection_t *c;
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic lost timer");
-
- c = ev->data;
-
- if (ngx_quic_detect_lost(c) != NGX_OK) {
- ngx_quic_close_connection(c, NGX_ERROR);
- }
-
- ngx_quic_connstate_dbg(c);
-}
-
-
-static ngx_int_t
-ngx_quic_detect_lost(ngx_connection_t *c)
-{
- ngx_uint_t i;
- ngx_msec_t now, wait, thr;
- ngx_queue_t *q;
- ngx_quic_frame_t *start;
- ngx_quic_send_ctx_t *ctx;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
- now = ngx_current_msec;
- thr = ngx_quic_lost_threshold(qc);
-
- for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
-
- ctx = &qc->send_ctx[i];
-
- if (ctx->largest_ack == NGX_QUIC_UNSET_PN) {
- continue;
- }
-
- while (!ngx_queue_empty(&ctx->sent)) {
-
- q = ngx_queue_head(&ctx->sent);
- start = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
- if (start->pnum > ctx->largest_ack) {
- break;
- }
-
- wait = start->last + thr - now;
-
- ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic detect_lost pnum:%uL thr:%M wait:%i level:%d",
- start->pnum, thr, (ngx_int_t) wait, start->level);
-
- if ((ngx_msec_int_t) wait > 0
- && ctx->largest_ack - start->pnum < NGX_QUIC_PKT_THR)
- {
- break;
- }
-
- ngx_quic_resend_frames(c, ctx);
- }
- }
-
- ngx_quic_set_lost_timer(c);
-
- return NGX_OK;
-}
-
-
-static void
-ngx_quic_set_lost_timer(ngx_connection_t *c)
-{
- ngx_uint_t i;
- ngx_msec_t now;
- ngx_queue_t *q;
- ngx_msec_int_t lost, pto, w;
- ngx_quic_frame_t *f;
- ngx_quic_send_ctx_t *ctx;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
- now = ngx_current_msec;
-
- lost = -1;
- pto = -1;
-
- for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
- ctx = &qc->send_ctx[i];
-
- if (ngx_queue_empty(&ctx->sent)) {
- continue;
- }
-
- if (ctx->largest_ack != NGX_QUIC_UNSET_PN) {
- q = ngx_queue_head(&ctx->sent);
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
- w = (ngx_msec_int_t) (f->last + ngx_quic_lost_threshold(qc) - now);
-
- if (f->pnum <= ctx->largest_ack) {
- if (w < 0 || ctx->largest_ack - f->pnum >= NGX_QUIC_PKT_THR) {
- w = 0;
- }
-
- if (lost == -1 || w < lost) {
- lost = w;
- }
- }
- }
-
- q = ngx_queue_last(&ctx->sent);
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
- w = (ngx_msec_int_t) (f->last + ngx_quic_pto(c, ctx) - now);
-
- if (w < 0) {
- w = 0;
- }
-
- if (pto == -1 || w < pto) {
- pto = w;
- }
- }
-
- if (qc->pto.timer_set) {
- ngx_del_timer(&qc->pto);
- }
-
- if (lost != -1) {
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic lost timer lost:%M", lost);
-
- qc->pto.handler = ngx_quic_lost_handler;
- ngx_add_timer(&qc->pto, lost);
- return;
- }
-
- if (pto != -1) {
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic lost timer pto:%M", pto);
-
- qc->pto.handler = ngx_quic_pto_handler;
- ngx_add_timer(&qc->pto, pto);
- return;
- }
-
- ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic lost timer unset");
-}
-
-
-static void
-ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
-{
- size_t n;
- ngx_buf_t *b;
- ngx_queue_t *q;
- ngx_quic_frame_t *f, *start;
- ngx_quic_stream_t *sn;
- ngx_quic_connection_t *qc;
-
- qc = ngx_quic_get_connection(c);
- q = ngx_queue_head(&ctx->sent);
- start = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
- ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic resend packet pnum:%uL", start->pnum);
-
- ngx_quic_congestion_lost(c, start);
-
- do {
- f = ngx_queue_data(q, ngx_quic_frame_t, queue);
-
- if (f->pnum != start->pnum) {
- break;
- }
-
- q = ngx_queue_next(q);
-
- ngx_queue_remove(&f->queue);
-
- switch (f->type) {
- case NGX_QUIC_FT_ACK:
- case NGX_QUIC_FT_ACK_ECN:
- if (ctx->level == ssl_encryption_application) {
- /* force generation of most recent acknowledgment */
- ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
- }
-
- ngx_quic_free_frame(c, f);
- break;
-
- case NGX_QUIC_FT_PING:
- case NGX_QUIC_FT_PATH_RESPONSE:
- case NGX_QUIC_FT_CONNECTION_CLOSE:
- ngx_quic_free_frame(c, f);
- break;
-
- case NGX_QUIC_FT_MAX_DATA:
- f->u.max_data.max_data = qc->streams.recv_max_data;
- ngx_quic_queue_frame(qc, f);
- break;
-
- case NGX_QUIC_FT_MAX_STREAMS:
- case NGX_QUIC_FT_MAX_STREAMS2:
- f->u.max_streams.limit = f->u.max_streams.bidi
- ? qc->streams.client_max_streams_bidi
- : qc->streams.client_max_streams_uni;
- ngx_quic_queue_frame(qc, f);
- break;
-
- case NGX_QUIC_FT_MAX_STREAM_DATA:
- sn = ngx_quic_find_stream(&qc->streams.tree,
- f->u.max_stream_data.id);
- if (sn == NULL) {
- ngx_quic_free_frame(c, f);
- break;
- }
-
- b = sn->b;
- n = sn->fs.received + (b->pos - b->start) + (b->end - b->last);
-
- if (f->u.max_stream_data.limit < n) {
- f->u.max_stream_data.limit = n;
- }
-
- ngx_quic_queue_frame(qc, f);
- break;
-
- case NGX_QUIC_FT_STREAM0:
- case NGX_QUIC_FT_STREAM1:
- case NGX_QUIC_FT_STREAM2:
- case NGX_QUIC_FT_STREAM3:
- case NGX_QUIC_FT_STREAM4:
- case NGX_QUIC_FT_STREAM5:
- case NGX_QUIC_FT_STREAM6:
- case NGX_QUIC_FT_STREAM7:
- sn = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id);
-
- if (sn && sn->c->write->error) {
- /* RESET_STREAM was sent */
- ngx_quic_free_frame(c, f);
- break;
- }
-
- /* fall through */
-
- default:
- ngx_queue_insert_tail(&ctx->frames, &f->queue);
- }
-
- } while (q != ngx_queue_sentinel(&ctx->sent));
-
- if (qc->closing) {
- return;
- }
-
- ngx_post_event(&qc->push, &ngx_posted_events);
-}
-
-
void
ngx_quic_shutdown_quic(ngx_connection_t *c)
{
@@ -3981,100 +2151,6 @@ ngx_quic_shutdown_quic(ngx_connection_t *c)
}
-
-static void
-ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
-{
- ngx_msec_t timer;
- ngx_quic_congestion_t *cg;
- ngx_quic_connection_t *qc;
-
- if (f->plen == 0) {
- return;
- }
-
- qc = ngx_quic_get_connection(c);
- cg = &qc->congestion;
-
- cg->in_flight -= f->plen;
-
- timer = f->last - cg->recovery_start;
-
- if ((ngx_msec_int_t) timer <= 0) {
- ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic congestion ack recovery win:%uz ss:%z if:%uz",
- cg->window, cg->ssthresh, cg->in_flight);
-
- return;
- }
-
- if (cg->window < cg->ssthresh) {
- cg->window += f->plen;
-
- ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic congestion slow start win:%uz ss:%z if:%uz",
- cg->window, cg->ssthresh, cg->in_flight);
-
- } else {
- cg->window += qc->tp.max_udp_payload_size * f->plen / cg->window;
-
- ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic congestion avoidance win:%uz ss:%z if:%uz",
- cg->window, cg->ssthresh, cg->in_flight);
- }
-
- /* prevent recovery_start from wrapping */
-
- timer = cg->recovery_start - ngx_current_msec + qc->tp.max_idle_timeout * 2;
-
- if ((ngx_msec_int_t) timer < 0) {
- cg->recovery_start = ngx_current_msec - qc->tp.max_idle_timeout * 2;
- }
-}
-
-
-static void
-ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
-{
- ngx_msec_t timer;
- ngx_quic_congestion_t *cg;
- ngx_quic_connection_t *qc;
-
- if (f->plen == 0) {
- return;
- }
-
- qc = ngx_quic_get_connection(c);
- cg = &qc->congestion;
-
- cg->in_flight -= f->plen;
- f->plen = 0;
-
- timer = f->last - cg->recovery_start;
-
- if ((ngx_msec_int_t) timer <= 0) {
- ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic congestion lost recovery win:%uz ss:%z if:%uz",
- cg->window, cg->ssthresh, cg->in_flight);
-
- return;
- }
-
- cg->recovery_start = ngx_current_msec;
- cg->window /= 2;
-
- if (cg->window < qc->tp.max_udp_payload_size * 2) {
- cg->window = qc->tp.max_udp_payload_size * 2;
- }
-
- cg->ssthresh = cg->window;
-
- ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
- "quic congestion lost win:%uz ss:%z if:%uz",
- cg->window, cg->ssthresh, cg->in_flight);
-}
-
-
uint32_t
ngx_quic_version(ngx_connection_t *c)
{
diff --git a/src/event/quic/ngx_event_quic_ack.c b/src/event/quic/ngx_event_quic_ack.c
new file mode 100644
index 000000000..8f78ac51e
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_ack.c
@@ -0,0 +1,1081 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_quic_connection.h>
+
+
+#define NGX_QUIC_MAX_ACK_GAP 2
+
+/* quic-recovery, section 6.1.1, Packet Threshold */
+#define NGX_QUIC_PKT_THR 3 /* packets */
+/* quic-recovery, section 6.1.2, Time Threshold */
+#define NGX_QUIC_TIME_THR 1.125
+#define NGX_QUIC_TIME_GRANULARITY 1 /* ms */
+
+#define ngx_quic_lost_threshold(qc) \
+ ngx_max(NGX_QUIC_TIME_THR * ngx_max((qc)->latest_rtt, (qc)->avg_rtt), \
+ NGX_QUIC_TIME_GRANULARITY)
+
+
+static void ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
+ enum ssl_encryption_level_t level, ngx_msec_t send_time);
+static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,
+ ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max,
+ ngx_msec_t *send_time);
+static void ngx_quic_drop_ack_ranges(ngx_connection_t *c,
+ ngx_quic_send_ctx_t *ctx, uint64_t pn);
+static ngx_int_t ngx_quic_detect_lost(ngx_connection_t *c);
+static void ngx_quic_congestion_lost(ngx_connection_t *c,
+ ngx_quic_frame_t *frame);
+static void ngx_quic_lost_handler(ngx_event_t *ev);
+
+
+ngx_int_t
+ngx_quic_handle_ack_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
+ ngx_quic_frame_t *f)
+{
+ ssize_t n;
+ u_char *pos, *end;
+ uint64_t min, max, gap, range;
+ ngx_msec_t send_time;
+ ngx_uint_t i;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_ack_frame_t *ack;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+
+ ctx = ngx_quic_get_send_ctx(qc, pkt->level);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic ngx_quic_handle_ack_frame level:%d", pkt->level);
+
+ ack = &f->u.ack;
+
+ /*
+ * If any computed packet number is negative, an endpoint MUST
+ * generate a connection error of type FRAME_ENCODING_ERROR.
+ * (19.3.1)
+ */
+
+ if (ack->first_range > ack->largest) {
+ qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "quic invalid first range in ack frame");
+ return NGX_ERROR;
+ }
+
+ min = ack->largest - ack->first_range;
+ max = ack->largest;
+
+ if (ngx_quic_handle_ack_frame_range(c, ctx, min, max, &send_time)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ /* 13.2.3. Receiver Tracking of ACK Frames */
+ if (ctx->largest_ack < max || ctx->largest_ack == NGX_QUIC_UNSET_PN) {
+ ctx->largest_ack = max;
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic updated largest received ack:%uL", max);
+
+ /*
+ * An endpoint generates an RTT sample on receiving an
+ * ACK frame that meets the following two conditions:
+ *
+ * - the largest acknowledged packet number is newly acknowledged
+ * - at least one of the newly acknowledged packets was ack-eliciting.
+ */
+
+ if (send_time != NGX_TIMER_INFINITE) {
+ ngx_quic_rtt_sample(c, ack, pkt->level, send_time);
+ }
+ }
+
+ if (f->data) {
+ pos = f->data->buf->pos;
+ end = f->data->buf->last;
+
+ } else {
+ pos = NULL;
+ end = NULL;
+ }
+
+ for (i = 0; i < ack->range_count; i++) {
+
+ n = ngx_quic_parse_ack_range(pkt->log, pos, end, &gap, &range);
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ pos += n;
+
+ if (gap + 2 > min) {
+ qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "quic invalid range:%ui in ack frame", i);
+ return NGX_ERROR;
+ }
+
+ max = min - gap - 2;
+
+ if (range > max) {
+ qc->error = NGX_QUIC_ERR_FRAME_ENCODING_ERROR;
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "quic invalid range:%ui in ack frame", i);
+ return NGX_ERROR;
+ }
+
+ min = max - range;
+
+ if (ngx_quic_handle_ack_frame_range(c, ctx, min, max, &send_time)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ return ngx_quic_detect_lost(c);
+}
+
+
+static void
+ngx_quic_rtt_sample(ngx_connection_t *c, ngx_quic_ack_frame_t *ack,
+ enum ssl_encryption_level_t level, ngx_msec_t send_time)
+{
+ ngx_msec_t latest_rtt, ack_delay, adjusted_rtt, rttvar_sample;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+
+ latest_rtt = ngx_current_msec - send_time;
+ qc->latest_rtt = latest_rtt;
+
+ if (qc->min_rtt == NGX_TIMER_INFINITE) {
+ qc->min_rtt = latest_rtt;
+ qc->avg_rtt = latest_rtt;
+ qc->rttvar = latest_rtt / 2;
+
+ } else {
+ qc->min_rtt = ngx_min(qc->min_rtt, latest_rtt);
+
+ ack_delay = ack->delay * (1 << qc->ctp.ack_delay_exponent) / 1000;
+
+ if (c->ssl->handshaked) {
+ ack_delay = ngx_min(ack_delay, qc->ctp.max_ack_delay);
+ }
+
+ adjusted_rtt = latest_rtt;
+
+ if (qc->min_rtt + ack_delay < latest_rtt) {
+ adjusted_rtt -= ack_delay;
+ }
+
+ qc->avg_rtt = 0.875 * qc->avg_rtt + 0.125 * adjusted_rtt;
+ rttvar_sample = ngx_abs((ngx_msec_int_t) (qc->avg_rtt - adjusted_rtt));
+ qc->rttvar = 0.75 * qc->rttvar + 0.25 * rttvar_sample;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic rtt sample latest:%M min:%M avg:%M var:%M",
+ latest_rtt, qc->min_rtt, qc->avg_rtt, qc->rttvar);
+}
+
+
+static ngx_int_t
+ngx_quic_handle_ack_frame_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
+ uint64_t min, uint64_t max, ngx_msec_t *send_time)
+{
+ ngx_uint_t found;
+ ngx_queue_t *q;
+ ngx_quic_frame_t *f;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+
+ *send_time = NGX_TIMER_INFINITE;
+ found = 0;
+
+ q = ngx_queue_last(&ctx->sent);
+
+ while (q != ngx_queue_sentinel(&ctx->sent)) {
+
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+ q = ngx_queue_prev(q);
+
+ if (f->pnum >= min && f->pnum <= max) {
+ ngx_quic_congestion_ack(c, f);
+
+ switch (f->type) {
+ case NGX_QUIC_FT_ACK:
+ case NGX_QUIC_FT_ACK_ECN:
+ ngx_quic_drop_ack_ranges(c, ctx, f->u.ack.largest);
+ break;
+
+ case NGX_QUIC_FT_STREAM0:
+ case NGX_QUIC_FT_STREAM1:
+ case NGX_QUIC_FT_STREAM2:
+ case NGX_QUIC_FT_STREAM3:
+ case NGX_QUIC_FT_STREAM4:
+ case NGX_QUIC_FT_STREAM5:
+ case NGX_QUIC_FT_STREAM6:
+ case NGX_QUIC_FT_STREAM7:
+ ngx_quic_handle_stream_ack(c, f);
+ break;
+ }
+
+ if (f->pnum == max) {
+ *send_time = f->last;
+ }
+
+ ngx_queue_remove(&f->queue);
+ ngx_quic_free_frame(c, f);
+ found = 1;
+ }
+ }
+
+ if (!found) {
+
+ if (max < ctx->pnum) {
+ /* duplicate ACK or ACK for non-ack-eliciting frame */
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_INFO, c->log, 0,
+ "quic ACK for the packet not sent");
+
+ qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION;
+ qc->error_ftype = NGX_QUIC_FT_ACK;
+ qc->error_reason = "unknown packet number";
+
+ return NGX_ERROR;
+ }
+
+ if (!qc->push.timer_set) {
+ ngx_post_event(&qc->push, &ngx_posted_events);
+ }
+
+ qc->pto_count = 0;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_quic_congestion_ack(ngx_connection_t *c, ngx_quic_frame_t *f)
+{
+ ngx_msec_t timer;
+ ngx_quic_congestion_t *cg;
+ ngx_quic_connection_t *qc;
+
+ if (f->plen == 0) {
+ return;
+ }
+
+ qc = ngx_quic_get_connection(c);
+ cg = &qc->congestion;
+
+ cg->in_flight -= f->plen;
+
+ timer = f->last - cg->recovery_start;
+
+ if ((ngx_msec_int_t) timer <= 0) {
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic congestion ack recovery win:%uz ss:%z if:%uz",
+ cg->window, cg->ssthresh, cg->in_flight);
+
+ return;
+ }
+
+ if (cg->window < cg->ssthresh) {
+ cg->window += f->plen;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic congestion slow start win:%uz ss:%z if:%uz",
+ cg->window, cg->ssthresh, cg->in_flight);
+
+ } else {
+ cg->window += qc->tp.max_udp_payload_size * f->plen / cg->window;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic congestion avoidance win:%uz ss:%z if:%uz",
+ cg->window, cg->ssthresh, cg->in_flight);
+ }
+
+ /* prevent recovery_start from wrapping */
+
+ timer = cg->recovery_start - ngx_current_msec + qc->tp.max_idle_timeout * 2;
+
+ if ((ngx_msec_int_t) timer < 0) {
+ cg->recovery_start = ngx_current_msec - qc->tp.max_idle_timeout * 2;
+ }
+}
+
+
+static void
+ngx_quic_drop_ack_ranges(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
+ uint64_t pn)
+{
+ uint64_t base;
+ ngx_uint_t i, smallest, largest;
+ ngx_quic_ack_range_t *r;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic ngx_quic_drop_ack_ranges pn:%uL largest:%uL"
+ " fr:%uL nranges:%ui", pn, ctx->largest_range,
+ ctx->first_range, ctx->nranges);
+
+ base = ctx->largest_range;
+
+ if (base == NGX_QUIC_UNSET_PN) {
+ return;
+ }
+
+ if (ctx->pending_ack != NGX_QUIC_UNSET_PN && pn >= ctx->pending_ack) {
+ ctx->pending_ack = NGX_QUIC_UNSET_PN;
+ }
+
+ largest = base;
+ smallest = largest - ctx->first_range;
+
+ if (pn >= largest) {
+ ctx->largest_range = NGX_QUIC_UNSET_PN;
+ ctx->first_range = 0;
+ ctx->nranges = 0;
+ return;
+ }
+
+ if (pn >= smallest) {
+ ctx->first_range = largest - pn - 1;
+ ctx->nranges = 0;
+ return;
+ }
+
+ for (i = 0; i < ctx->nranges; i++) {
+ r = &ctx->ranges[i];
+
+ largest = smallest - r->gap - 2;
+ smallest = largest - r->range;
+
+ if (pn >= largest) {
+ ctx->nranges = i;
+ return;
+ }
+ if (pn >= smallest) {
+ r->range = largest - pn - 1;
+ ctx->nranges = i + 1;
+ return;
+ }
+ }
+}
+
+
+static ngx_int_t
+ngx_quic_detect_lost(ngx_connection_t *c)
+{
+ ngx_uint_t i;
+ ngx_msec_t now, wait, thr;
+ ngx_queue_t *q;
+ ngx_quic_frame_t *start;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+ now = ngx_current_msec;
+ thr = ngx_quic_lost_threshold(qc);
+
+ for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
+
+ ctx = &qc->send_ctx[i];
+
+ if (ctx->largest_ack == NGX_QUIC_UNSET_PN) {
+ continue;
+ }
+
+ while (!ngx_queue_empty(&ctx->sent)) {
+
+ q = ngx_queue_head(&ctx->sent);
+ start = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+ if (start->pnum > ctx->largest_ack) {
+ break;
+ }
+
+ wait = start->last + thr - now;
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic detect_lost pnum:%uL thr:%M wait:%i level:%d",
+ start->pnum, thr, (ngx_int_t) wait, start->level);
+
+ if ((ngx_msec_int_t) wait > 0
+ && ctx->largest_ack - start->pnum < NGX_QUIC_PKT_THR)
+ {
+ break;
+ }
+
+ ngx_quic_resend_frames(c, ctx);
+ }
+ }
+
+ ngx_quic_set_lost_timer(c);
+
+ return NGX_OK;
+}
+
+
+void
+ngx_quic_resend_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
+{
+ size_t n;
+ ngx_buf_t *b;
+ ngx_queue_t *q;
+ ngx_quic_frame_t *f, *start;
+ ngx_quic_stream_t *sn;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+ q = ngx_queue_head(&ctx->sent);
+ start = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic resend packet pnum:%uL", start->pnum);
+
+ ngx_quic_congestion_lost(c, start);
+
+ do {
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+ if (f->pnum != start->pnum) {
+ break;
+ }
+
+ q = ngx_queue_next(q);
+
+ ngx_queue_remove(&f->queue);
+
+ switch (f->type) {
+ case NGX_QUIC_FT_ACK:
+ case NGX_QUIC_FT_ACK_ECN:
+ if (ctx->level == ssl_encryption_application) {
+ /* force generation of most recent acknowledgment */
+ ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
+ }
+
+ ngx_quic_free_frame(c, f);
+ break;
+
+ case NGX_QUIC_FT_PING:
+ case NGX_QUIC_FT_PATH_RESPONSE:
+ case NGX_QUIC_FT_CONNECTION_CLOSE:
+ ngx_quic_free_frame(c, f);
+ break;
+
+ case NGX_QUIC_FT_MAX_DATA:
+ f->u.max_data.max_data = qc->streams.recv_max_data;
+ ngx_quic_queue_frame(qc, f);
+ break;
+
+ case NGX_QUIC_FT_MAX_STREAMS:
+ case NGX_QUIC_FT_MAX_STREAMS2:
+ f->u.max_streams.limit = f->u.max_streams.bidi
+ ? qc->streams.client_max_streams_bidi
+ : qc->streams.client_max_streams_uni;
+ ngx_quic_queue_frame(qc, f);
+ break;
+
+ case NGX_QUIC_FT_MAX_STREAM_DATA:
+ sn = ngx_quic_find_stream(&qc->streams.tree,
+ f->u.max_stream_data.id);
+ if (sn == NULL) {
+ ngx_quic_free_frame(c, f);
+ break;
+ }
+
+ b = sn->b;
+ n = sn->fs.received + (b->pos - b->start) + (b->end - b->last);
+
+ if (f->u.max_stream_data.limit < n) {
+ f->u.max_stream_data.limit = n;
+ }
+
+ ngx_quic_queue_frame(qc, f);
+ break;
+
+ case NGX_QUIC_FT_STREAM0:
+ case NGX_QUIC_FT_STREAM1:
+ case NGX_QUIC_FT_STREAM2:
+ case NGX_QUIC_FT_STREAM3:
+ case NGX_QUIC_FT_STREAM4:
+ case NGX_QUIC_FT_STREAM5:
+ case NGX_QUIC_FT_STREAM6:
+ case NGX_QUIC_FT_STREAM7:
+ sn = ngx_quic_find_stream(&qc->streams.tree, f->u.stream.stream_id);
+
+ if (sn && sn->c->write->error) {
+ /* RESET_STREAM was sent */
+ ngx_quic_free_frame(c, f);
+ break;
+ }
+
+ /* fall through */
+
+ default:
+ ngx_queue_insert_tail(&ctx->frames, &f->queue);
+ }
+
+ } while (q != ngx_queue_sentinel(&ctx->sent));
+
+ if (qc->closing) {
+ return;
+ }
+
+ ngx_post_event(&qc->push, &ngx_posted_events);
+}
+
+
+static void
+ngx_quic_congestion_lost(ngx_connection_t *c, ngx_quic_frame_t *f)
+{
+ ngx_msec_t timer;
+ ngx_quic_congestion_t *cg;
+ ngx_quic_connection_t *qc;
+
+ if (f->plen == 0) {
+ return;
+ }
+
+ qc = ngx_quic_get_connection(c);
+ cg = &qc->congestion;
+
+ cg->in_flight -= f->plen;
+ f->plen = 0;
+
+ timer = f->last - cg->recovery_start;
+
+ if ((ngx_msec_int_t) timer <= 0) {
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic congestion lost recovery win:%uz ss:%z if:%uz",
+ cg->window, cg->ssthresh, cg->in_flight);
+
+ return;
+ }
+
+ cg->recovery_start = ngx_current_msec;
+ cg->window /= 2;
+
+ if (cg->window < qc->tp.max_udp_payload_size * 2) {
+ cg->window = qc->tp.max_udp_payload_size * 2;
+ }
+
+ cg->ssthresh = cg->window;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic congestion lost win:%uz ss:%z if:%uz",
+ cg->window, cg->ssthresh, cg->in_flight);
+}
+
+
+void
+ngx_quic_set_lost_timer(ngx_connection_t *c)
+{
+ ngx_uint_t i;
+ ngx_msec_t now;
+ ngx_queue_t *q;
+ ngx_msec_int_t lost, pto, w;
+ ngx_quic_frame_t *f;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+ now = ngx_current_msec;
+
+ lost = -1;
+ pto = -1;
+
+ for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
+ ctx = &qc->send_ctx[i];
+
+ if (ngx_queue_empty(&ctx->sent)) {
+ continue;
+ }
+
+ if (ctx->largest_ack != NGX_QUIC_UNSET_PN) {
+ q = ngx_queue_head(&ctx->sent);
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+ w = (ngx_msec_int_t) (f->last + ngx_quic_lost_threshold(qc) - now);
+
+ if (f->pnum <= ctx->largest_ack) {
+ if (w < 0 || ctx->largest_ack - f->pnum >= NGX_QUIC_PKT_THR) {
+ w = 0;
+ }
+
+ if (lost == -1 || w < lost) {
+ lost = w;
+ }
+ }
+ }
+
+ q = ngx_queue_last(&ctx->sent);
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+ w = (ngx_msec_int_t) (f->last + ngx_quic_pto(c, ctx) - now);
+
+ if (w < 0) {
+ w = 0;
+ }
+
+ if (pto == -1 || w < pto) {
+ pto = w;
+ }
+ }
+
+ if (qc->pto.timer_set) {
+ ngx_del_timer(&qc->pto);
+ }
+
+ if (lost != -1) {
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic lost timer lost:%M", lost);
+
+ qc->pto.handler = ngx_quic_lost_handler;
+ ngx_add_timer(&qc->pto, lost);
+ return;
+ }
+
+ if (pto != -1) {
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic lost timer pto:%M", pto);
+
+ qc->pto.handler = ngx_quic_pto_handler;
+ ngx_add_timer(&qc->pto, pto);
+ return;
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic lost timer unset");
+}
+
+
+ngx_msec_t
+ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
+{
+ ngx_msec_t duration;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+
+ /* PTO calculation: quic-recovery, Appendix 8 */
+ duration = qc->avg_rtt;
+
+ duration += ngx_max(4 * qc->rttvar, NGX_QUIC_TIME_GRANULARITY);
+ duration <<= qc->pto_count;
+
+ if (qc->congestion.in_flight == 0) { /* no in-flight packets */
+ return duration;
+ }
+
+ if (ctx->level == ssl_encryption_application && c->ssl->handshaked) {
+ duration += qc->ctp.max_ack_delay << qc->pto_count;
+ }
+
+ return duration;
+}
+
+
+static
+void ngx_quic_lost_handler(ngx_event_t *ev)
+{
+ ngx_connection_t *c;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic lost timer");
+
+ c = ev->data;
+
+ if (ngx_quic_detect_lost(c) != NGX_OK) {
+ ngx_quic_close_connection(c, NGX_ERROR);
+ }
+
+ ngx_quic_connstate_dbg(c);
+}
+
+
+void
+ngx_quic_pto_handler(ngx_event_t *ev)
+{
+ ngx_uint_t i;
+ ngx_msec_t now;
+ ngx_queue_t *q, *next;
+ ngx_connection_t *c;
+ ngx_quic_frame_t *f;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_connection_t *qc;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "quic pto timer");
+
+ c = ev->data;
+ qc = ngx_quic_get_connection(c);
+ now = ngx_current_msec;
+
+ for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
+
+ ctx = &qc->send_ctx[i];
+
+ if (ngx_queue_empty(&ctx->sent)) {
+ continue;
+ }
+
+ q = ngx_queue_head(&ctx->sent);
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+ if (f->pnum <= ctx->largest_ack
+ && ctx->largest_ack != NGX_QUIC_UNSET_PN)
+ {
+ continue;
+ }
+
+ if ((ngx_msec_int_t) (f->last + ngx_quic_pto(c, ctx) - now) > 0) {
+ continue;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic pto %s pto_count:%ui",
+ ngx_quic_level_name(ctx->level), qc->pto_count);
+
+ for (q = ngx_queue_head(&ctx->frames);
+ q != ngx_queue_sentinel(&ctx->frames);
+ /* void */)
+ {
+ next = ngx_queue_next(q);
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+ if (f->type == NGX_QUIC_FT_PING) {
+ ngx_queue_remove(q);
+ ngx_quic_free_frame(c, f);
+ }
+
+ q = next;
+ }
+
+ for (q = ngx_queue_head(&ctx->sent);
+ q != ngx_queue_sentinel(&ctx->sent);
+ /* void */)
+ {
+ next = ngx_queue_next(q);
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+ if (f->type == NGX_QUIC_FT_PING) {
+ ngx_quic_congestion_lost(c, f);
+ ngx_queue_remove(q);
+ ngx_quic_free_frame(c, f);
+ }
+
+ q = next;
+ }
+
+ /* enforce 2 udp datagrams */
+
+ f = ngx_quic_alloc_frame(c);
+ if (f == NULL) {
+ break;
+ }
+
+ f->level = ctx->level;
+ f->type = NGX_QUIC_FT_PING;
+ f->flush = 1;
+
+ ngx_quic_queue_frame(qc, f);
+
+ f = ngx_quic_alloc_frame(c);
+ if (f == NULL) {
+ break;
+ }
+
+ f->level = ctx->level;
+ f->type = NGX_QUIC_FT_PING;
+
+ ngx_quic_queue_frame(qc, f);
+ }
+
+ qc->pto_count++;
+
+ ngx_quic_connstate_dbg(c);
+}
+
+
+ngx_int_t
+ngx_quic_ack_packet(ngx_connection_t *c, ngx_quic_header_t *pkt)
+{
+ uint64_t base, largest, smallest, gs, ge, gap, range, pn;
+ uint64_t prev_pending;
+ ngx_uint_t i, nr;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_ack_range_t *r;
+ ngx_quic_connection_t *qc;
+
+ c->log->action = "preparing ack";
+
+ qc = ngx_quic_get_connection(c);
+
+ ctx = ngx_quic_get_send_ctx(qc, pkt->level);
+
+ ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic ngx_quic_ack_packet pn:%uL largest %L fr:%uL"
+ " nranges:%ui", pkt->pn, (int64_t) ctx->largest_range,
+ ctx->first_range, ctx->nranges);
+
+ prev_pending = ctx->pending_ack;
+
+ if (pkt->need_ack) {
+
+ ngx_post_event(&qc->push, &ngx_posted_events);
+
+ if (ctx->send_ack == 0) {
+ ctx->ack_delay_start = ngx_current_msec;
+ }
+
+ ctx->send_ack++;
+
+ if (ctx->pending_ack == NGX_QUIC_UNSET_PN
+ || ctx->pending_ack < pkt->pn)
+ {
+ ctx->pending_ack = pkt->pn;
+ }
+ }
+
+ base = ctx->largest_range;
+ pn = pkt->pn;
+
+ if (base == NGX_QUIC_UNSET_PN) {
+ ctx->largest_range = pn;
+ ctx->largest_received = pkt->received;
+ return NGX_OK;
+ }
+
+ if (base == pn) {
+ return NGX_OK;
+ }
+
+ largest = base;
+ smallest = largest - ctx->first_range;
+
+ if (pn > base) {
+
+ if (pn - base == 1) {
+ ctx->first_range++;
+ ctx->largest_range = pn;
+ ctx->largest_received = pkt->received;
+
+ return NGX_OK;
+
+ } else {
+ /* new gap in front of current largest */
+
+ /* no place for new range, send current range as is */
+ if (ctx->nranges == NGX_QUIC_MAX_RANGES) {
+
+ if (prev_pending != NGX_QUIC_UNSET_PN) {
+ if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (prev_pending == ctx->pending_ack || !pkt->need_ack) {
+ ctx->pending_ack = NGX_QUIC_UNSET_PN;
+ }
+ }
+
+ gap = pn - base - 2;
+ range = ctx->first_range;
+
+ ctx->first_range = 0;
+ ctx->largest_range = pn;
+ ctx->largest_received = pkt->received;
+
+ /* packet is out of order, force send */
+ if (pkt->need_ack) {
+ ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
+ }
+
+ i = 0;
+
+ goto insert;
+ }
+ }
+
+ /* pn < base, perform lookup in existing ranges */
+
+ /* packet is out of order */
+ if (pkt->need_ack) {
+ ctx->send_ack = NGX_QUIC_MAX_ACK_GAP;
+ }
+
+ if (pn >= smallest && pn <= largest) {
+ return NGX_OK;
+ }
+
+#if (NGX_SUPPRESS_WARN)
+ r = NULL;
+#endif
+
+ for (i = 0; i < ctx->nranges; i++) {
+ r = &ctx->ranges[i];
+
+ ge = smallest - 1;
+ gs = ge - r->gap;
+
+ if (pn >= gs && pn <= ge) {
+
+ if (gs == ge) {
+ /* gap size is exactly one packet, now filled */
+
+ /* data moves to previous range, current is removed */
+
+ if (i == 0) {
+ ctx->first_range += r->range + 2;
+
+ } else {
+ ctx->ranges[i - 1].range += r->range + 2;
+ }
+
+ nr = ctx->nranges - i - 1;
+ if (nr) {
+ ngx_memmove(&ctx->ranges[i], &ctx->ranges[i + 1],
+ sizeof(ngx_quic_ack_range_t) * nr);
+ }
+
+ ctx->nranges--;
+
+ } else if (pn == gs) {
+ /* current gap shrinks from tail (current range grows) */
+ r->gap--;
+ r->range++;
+
+ } else if (pn == ge) {
+ /* current gap shrinks from head (previous range grows) */
+ r->gap--;
+
+ if (i == 0) {
+ ctx->first_range++;
+
+ } else {
+ ctx->ranges[i - 1].range++;
+ }
+
+ } else {
+ /* current gap is split into two parts */
+
+ gap = ge - pn - 1;
+ range = 0;
+
+ if (ctx->nranges == NGX_QUIC_MAX_RANGES) {
+ if (prev_pending != NGX_QUIC_UNSET_PN) {
+ if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (prev_pending == ctx->pending_ack || !pkt->need_ack) {
+ ctx->pending_ack = NGX_QUIC_UNSET_PN;
+ }
+ }
+
+ r->gap = pn - gs - 1;
+ goto insert;
+ }
+
+ return NGX_OK;
+ }
+
+ largest = smallest - r->gap - 2;
+ smallest = largest - r->range;
+
+ if (pn >= smallest && pn <= largest) {
+ /* this packet number is already known */
+ return NGX_OK;
+ }
+
+ }
+
+ if (pn == smallest - 1) {
+ /* extend first or last range */
+
+ if (i == 0) {
+ ctx->first_range++;
+
+ } else {
+ r->range++;
+ }
+
+ return NGX_OK;
+ }
+
+ /* nothing found, add new range at the tail */
+
+ if (ctx->nranges == NGX_QUIC_MAX_RANGES) {
+ /* packet is too old to keep it */
+
+ if (pkt->need_ack) {
+ return ngx_quic_send_ack_range(c, ctx, pn, pn);
+ }
+
+ return NGX_OK;
+ }
+
+ gap = smallest - 2 - pn;
+ range = 0;
+
+insert:
+
+ if (ctx->nranges < NGX_QUIC_MAX_RANGES) {
+ ctx->nranges++;
+ }
+
+ ngx_memmove(&ctx->ranges[i + 1], &ctx->ranges[i],
+ sizeof(ngx_quic_ack_range_t) * (ctx->nranges - i - 1));
+
+ ctx->ranges[i].gap = gap;
+ ctx->ranges[i].range = range;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_quic_generate_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
+{
+ ngx_msec_t delay;
+ ngx_quic_connection_t *qc;
+
+ if (!ctx->send_ack) {
+ return NGX_OK;
+ }
+
+ if (ctx->level == ssl_encryption_application) {
+
+ delay = ngx_current_msec - ctx->ack_delay_start;
+ qc = ngx_quic_get_connection(c);
+
+ if (ctx->send_ack < NGX_QUIC_MAX_ACK_GAP
+ && delay < qc->tp.max_ack_delay)
+ {
+ if (!qc->push.timer_set && !qc->closing) {
+ ngx_add_timer(&qc->push,
+ qc->tp.max_ack_delay - delay);
+ }
+
+ return NGX_OK;
+ }
+ }
+
+ if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ctx->send_ack = 0;
+
+ return NGX_OK;
+}
diff --git a/src/event/quic/ngx_event_quic_ack.h b/src/event/quic/ngx_event_quic_ack.h
new file mode 100644
index 000000000..8e53446d7
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_ack.h
@@ -0,0 +1,31 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_QUIC_ACK_H_INCLUDED_
+#define _NGX_EVENT_QUIC_ACK_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
+ ngx_quic_header_t *pkt, ngx_quic_frame_t *f);
+
+void ngx_quic_congestion_ack(ngx_connection_t *c,
+ ngx_quic_frame_t *frame);
+void ngx_quic_resend_frames(ngx_connection_t *c,
+ ngx_quic_send_ctx_t *ctx);
+void ngx_quic_set_lost_timer(ngx_connection_t *c);
+void ngx_quic_pto_handler(ngx_event_t *ev);
+ngx_msec_t ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
+
+ngx_int_t ngx_quic_ack_packet(ngx_connection_t *c,
+ ngx_quic_header_t *pkt);
+ngx_int_t ngx_quic_generate_ack(ngx_connection_t *c,
+ ngx_quic_send_ctx_t *ctx);
+
+#endif /* _NGX_EVENT_QUIC_ACK_H_INCLUDED_ */
diff --git a/src/event/quic/ngx_event_quic_connection.h b/src/event/quic/ngx_event_quic_connection.h
index 17bc96435..6c89ab820 100644
--- a/src/event/quic/ngx_event_quic_connection.h
+++ b/src/event/quic/ngx_event_quic_connection.h
@@ -15,39 +15,22 @@
#include <ngx_event_quic_protection.h>
typedef struct ngx_quic_connection_s ngx_quic_connection_t;
+typedef struct ngx_quic_send_ctx_s ngx_quic_send_ctx_t;
#include <ngx_event_quic_frames.h>
#include <ngx_event_quic_migration.h>
#include <ngx_event_quic_connid.h>
#include <ngx_event_quic_streams.h>
+#include <ngx_event_quic_ack.h>
+#include <ngx_event_quic_output.h>
-#define NGX_QUIC_MAX_SHORT_HEADER 25 /* 1 flags + 20 dcid + 4 pn */
-#define NGX_QUIC_MAX_LONG_HEADER 56
- /* 1 flags + 4 version + 2 x (1 + 20) s/dcid + 4 pn + 4 len + token len */
-
-#define NGX_QUIC_MAX_UDP_PAYLOAD_OUT 1252
-#define NGX_QUIC_MAX_UDP_PAYLOAD_OUT6 1232
-
-#define NGX_QUIC_RETRY_TOKEN_LIFETIME 3 /* seconds */
-#define NGX_QUIC_NEW_TOKEN_LIFETIME 600 /* seconds */
-#define NGX_QUIC_RETRY_BUFFER_SIZE 256
- /* 1 flags + 4 version + 3 x (1 + 20) s/o/dcid + itag + token(64) */
#define NGX_QUIC_MAX_TOKEN_SIZE 64
/* SHA-1(addr)=20 + sizeof(time_t) + retry(1) + odcid.len(1) + odcid */
/* quic-recovery, section 6.2.2, kInitialRtt */
#define NGX_QUIC_INITIAL_RTT 333 /* ms */
-/* quic-recovery, section 6.1.1, Packet Threshold */
-#define NGX_QUIC_PKT_THR 3 /* packets */
-/* quic-recovery, section 6.1.2, Time Threshold */
-#define NGX_QUIC_TIME_THR 1.125
-#define NGX_QUIC_TIME_GRANULARITY 1 /* ms */
-
-#define NGX_QUIC_CC_MIN_INTERVAL 1000 /* 1s */
-
-
#define NGX_QUIC_UNSET_PN (uint64_t) -1
#define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
@@ -124,7 +107,7 @@ typedef struct {
* with Initial packet protection keys and acknowledged in packets which
* are also Initial packets.
*/
-typedef struct {
+struct ngx_quic_send_ctx_s {
enum ssl_encryption_level_t level;
uint64_t pnum; /* to be sent */
@@ -142,7 +125,7 @@ typedef struct {
ngx_uint_t nranges;
ngx_quic_ack_range_t ranges[NGX_QUIC_MAX_RANGES];
ngx_uint_t send_ack;
-} ngx_quic_send_ctx_t;
+};
struct ngx_quic_connection_s {
@@ -221,16 +204,22 @@ struct ngx_quic_connection_s {
void ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc);
-ngx_msec_t ngx_quic_pto(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx);
+void ngx_quic_shutdown_quic(ngx_connection_t *c);
ngx_int_t ngx_quic_new_sr_token(ngx_connection_t *c, ngx_str_t *cid,
u_char *secret, u_char *token);
-
-ngx_int_t ngx_quic_output(ngx_connection_t *c);
-void ngx_quic_shutdown_quic(ngx_connection_t *c);
+ngx_int_t ngx_quic_new_token(ngx_connection_t *c, u_char *key,
+ ngx_str_t *token, ngx_str_t *odcid, time_t expires, ngx_uint_t is_retry);
/********************************* DEBUG *************************************/
+#if (NGX_DEBUG)
+void ngx_quic_connstate_dbg(ngx_connection_t *c);
+#else
+#define ngx_quic_connstate_dbg(c)
+#endif
+
+
/* #define NGX_QUIC_DEBUG_PACKETS */ /* dump packet contents */
/* #define NGX_QUIC_DEBUG_FRAMES */ /* dump frames contents */
/* #define NGX_QUIC_DEBUG_ALLOC */ /* log frames and bufs alloc */
diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_quic_output.c
new file mode 100644
index 000000000..c8e483afb
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_output.c
@@ -0,0 +1,851 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_quic_connection.h>
+
+
+#define NGX_QUIC_MAX_SHORT_HEADER 25 /* 1 flags + 20 dcid + 4 pn */
+#define NGX_QUIC_MAX_LONG_HEADER 56
+ /* 1 flags + 4 version + 2 x (1 + 20) s/dcid + 4 pn + 4 len + token len */
+
+#define NGX_QUIC_MAX_UDP_PAYLOAD_OUT 1252
+#define NGX_QUIC_MAX_UDP_PAYLOAD_OUT6 1232
+
+#define NGX_QUIC_RETRY_TOKEN_LIFETIME 3 /* seconds */
+#define NGX_QUIC_NEW_TOKEN_LIFETIME 600 /* seconds */
+#define NGX_QUIC_RETRY_BUFFER_SIZE 256
+ /* 1 flags + 4 version + 3 x (1 + 20) s/o/dcid + itag + token(64) */
+
+/*
+ * Endpoints MUST discard packets that are too small to be valid QUIC
+ * packets. With the set of AEAD functions defined in [QUIC-TLS],
+ * packets that are smaller than 21 bytes are never valid.
+ */
+#define NGX_QUIC_MIN_PKT_LEN 21
+
+#define NGX_QUIC_MIN_SR_PACKET 43 /* 5 rand + 16 srt + 22 padding */
+#define NGX_QUIC_MAX_SR_PACKET 1200
+
+#define NGX_QUIC_CC_MIN_INTERVAL 1000 /* 1s */
+
+
+static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
+ ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
+static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);
+static ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len);
+static void ngx_quic_set_packet_number(ngx_quic_header_t *pkt,
+ ngx_quic_send_ctx_t *ctx);
+
+
+size_t
+ngx_quic_max_udp_payload(ngx_connection_t *c)
+{
+ /* TODO: path MTU discovery */
+
+#if (NGX_HAVE_INET6)
+ if (c->sockaddr->sa_family == AF_INET6) {
+ return NGX_QUIC_MAX_UDP_PAYLOAD_OUT6;
+ }
+#endif
+
+ return NGX_QUIC_MAX_UDP_PAYLOAD_OUT;
+}
+
+
+ngx_int_t
+ngx_quic_output(ngx_connection_t *c)
+{
+ off_t max;
+ size_t len, min, in_flight;
+ ssize_t n;
+ u_char *p;
+ ngx_uint_t i, pad;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_congestion_t *cg;
+ ngx_quic_connection_t *qc;
+ static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
+
+ c->log->action = "sending frames";
+
+ qc = ngx_quic_get_connection(c);
+ cg = &qc->congestion;
+
+ in_flight = cg->in_flight;
+
+ for ( ;; ) {
+ p = dst;
+
+ len = ngx_min(qc->ctp.max_udp_payload_size,
+ NGX_QUIC_MAX_UDP_PAYLOAD_SIZE);
+
+ if (!qc->validated) {
+ max = qc->received * 3;
+ max = (c->sent >= max) ? 0 : max - c->sent;
+ len = ngx_min(len, (size_t) max);
+ }
+
+ pad = ngx_quic_get_padding_level(c);
+
+ for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
+
+ ctx = &qc->send_ctx[i];
+
+ if (ngx_quic_generate_ack(c, ctx) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ min = (i == pad && p - dst < NGX_QUIC_MIN_INITIAL_SIZE)
+ ? NGX_QUIC_MIN_INITIAL_SIZE - (p - dst) : 0;
+
+ n = ngx_quic_output_packet(c, ctx, p, len, min);
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ p += n;
+ len -= n;
+ }
+
+ len = p - dst;
+ if (len == 0) {
+ break;
+ }
+
+ n = ngx_quic_send(c, dst, len);
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (in_flight != cg->in_flight && !qc->send_timer_set && !qc->closing) {
+ qc->send_timer_set = 1;
+ ngx_add_timer(c->read, qc->tp.max_idle_timeout);
+ }
+
+ ngx_quic_set_lost_timer(c);
+
+ return NGX_OK;
+}
+
+
+static ngx_uint_t
+ngx_quic_get_padding_level(ngx_connection_t *c)
+{
+ ngx_queue_t *q;
+ ngx_quic_frame_t *f;
+ ngx_quic_send_ctx_t *ctx;
+ ngx_quic_connection_t *qc;
+
+ /*
+ * 14.1. Initial Datagram Size
+ *
+ * Similarly, a server MUST expand the payload of all UDP datagrams
+ * carrying ack-eliciting Initial packets to at least the smallest
+ * allowed maximum datagram size of 1200 bytes
+ */
+
+ qc = ngx_quic_get_connection(c);
+ ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
+
+ for (q = ngx_queue_head(&ctx->frames);
+ q != ngx_queue_sentinel(&ctx->frames);
+ q = ngx_queue_next(q))
+ {
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+ if (f->need_ack) {
+ ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
+
+ if (ngx_queue_empty(&ctx->frames)) {
+ return 0;
+ }
+
+ return 1;
+ }
+ }
+
+ return NGX_QUIC_SEND_CTX_LAST;
+}
+
+
+static ssize_t
+ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
+ u_char *data, size_t max, size_t min)
+{
+ size_t len, hlen, pad_len;
+ u_char *p;
+ ssize_t flen;
+ ngx_str_t out, res;
+ ngx_int_t rc;
+ ngx_uint_t nframes;
+ ngx_msec_t now;
+ ngx_queue_t *q;
+ ngx_quic_frame_t *f;
+ ngx_quic_header_t pkt;
+ ngx_quic_congestion_t *cg;
+ ngx_quic_connection_t *qc;
+ static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
+
+ if (ngx_queue_empty(&ctx->frames)) {
+ return 0;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic output %s packet max:%uz min:%uz",
+ ngx_quic_level_name(ctx->level), max, min);
+
+ qc = ngx_quic_get_connection(c);
+ cg = &qc->congestion;
+
+ hlen = (ctx->level == ssl_encryption_application)
+ ? NGX_QUIC_MAX_SHORT_HEADER
+ : NGX_QUIC_MAX_LONG_HEADER;
+
+ hlen += EVP_GCM_TLS_TAG_LEN;
+ hlen -= NGX_QUIC_MAX_CID_LEN - qc->scid.len;
+
+ ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
+
+ now = ngx_current_msec;
+ nframes = 0;
+ p = src;
+ len = 0;
+
+ for (q = ngx_queue_head(&ctx->frames);
+ q != ngx_queue_sentinel(&ctx->frames);
+ q = ngx_queue_next(q))
+ {
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+ if (!pkt.need_ack && f->need_ack && max > cg->window) {
+ max = cg->window;
+ }
+
+ if (hlen + len >= max) {
+ break;
+ }
+
+ if (hlen + len + f->len > max) {
+ rc = ngx_quic_split_frame(c, f, max - hlen - len);
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc == NGX_DECLINED) {
+ break;
+ }
+ }
+
+ if (f->need_ack) {
+ pkt.need_ack = 1;
+ }
+
+ ngx_quic_log_frame(c->log, f, 1);
+
+ flen = ngx_quic_create_frame(p, f);
+ if (flen == -1) {
+ return NGX_ERROR;
+ }
+
+ len += flen;
+ p += flen;
+
+ f->pnum = ctx->pnum;
+ f->first = now;
+ f->last = now;
+ f->plen = 0;
+
+ nframes++;
+
+ if (f->flush) {
+ break;
+ }
+ }
+
+ if (nframes == 0) {
+ return 0;
+ }
+
+ out.data = src;
+ out.len = len;
+
+ pkt.keys = qc->keys;
+ pkt.flags = NGX_QUIC_PKT_FIXED_BIT;
+
+ if (ctx->level == ssl_encryption_initial) {
+ pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;
+
+ } else if (ctx->level == ssl_encryption_handshake) {
+ pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;
+
+ } else {
+ if (qc->key_phase) {
+ pkt.flags |= NGX_QUIC_PKT_KPHASE;
+ }
+ }
+
+ ngx_quic_set_packet_number(&pkt, ctx);
+
+ pkt.version = qc->version;
+ pkt.log = c->log;
+ pkt.level = ctx->level;
+ pkt.dcid = qc->scid;
+ pkt.scid = qc->dcid;
+
+ pad_len = 4;
+
+ if (min) {
+ hlen = EVP_GCM_TLS_TAG_LEN
+ + ngx_quic_create_header(&pkt, NULL, out.len, NULL);
+
+ if (min > hlen + pad_len) {
+ pad_len = min - hlen;
+ }
+ }
+
+ if (out.len < pad_len) {
+ ngx_memset(p, NGX_QUIC_FT_PADDING, pad_len - out.len);
+ out.len = pad_len;
+ }
+
+ pkt.payload = out;
+
+ res.data = data;
+
+ ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic packet tx %s bytes:%ui"
+ " need_ack:%d number:%L encoded nl:%d trunc:0x%xD",
+ ngx_quic_level_name(ctx->level), out.len, pkt.need_ack,
+ pkt.number, pkt.num_len, pkt.trunc);
+
+ if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ctx->pnum++;
+
+ if (pkt.need_ack) {
+ /* move frames into the sent queue to wait for ack */
+
+ if (!qc->closing) {
+ q = ngx_queue_head(&ctx->frames);
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+ f->plen = res.len;
+
+ do {
+ q = ngx_queue_head(&ctx->frames);
+ ngx_queue_remove(q);
+ ngx_queue_insert_tail(&ctx->sent, q);
+ } while (--nframes);
+ }
+
+ cg->in_flight += res.len;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic congestion send if:%uz", cg->in_flight);
+ }
+
+ while (nframes--) {
+ q = ngx_queue_head(&ctx->frames);
+ f = ngx_queue_data(q, ngx_quic_frame_t, queue);
+
+ ngx_queue_remove(q);
+ ngx_quic_free_frame(c, f);
+ }
+
+ return res.len;
+}
+
+
+ssize_t
+ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len)
+{
+ ngx_buf_t b;
+ ngx_chain_t cl, *res;
+
+ ngx_memzero(&b, sizeof(ngx_buf_t));
+
+ b.pos = b.start = buf;
+ b.last = b.end = buf + len;
+ b.last_buf = 1;
+ b.temporary = 1;
+
+ cl.buf = &b;
+ cl.next= NULL;
+
+ res = c->send_chain(c, &cl, 0);
+ if (res == NGX_CHAIN_ERROR) {
+ return NGX_ERROR;
+ }
+
+ return len;
+}
+
+
+static void
+ngx_quic_set_packet_number(ngx_quic_header_t *pkt, ngx_quic_send_ctx_t *ctx)
+{
+ uint64_t delta;
+
+ delta = ctx->pnum - ctx->largest_ack;
+ pkt->number = ctx->pnum;
+
+ if (delta <= 0x7F) {
+ pkt->num_len = 1;
+ pkt->trunc = ctx->pnum & 0xff;
+
+ } else if (delta <= 0x7FFF) {
+ pkt->num_len = 2;
+ pkt->flags |= 0x1;
+ pkt->trunc = ctx->pnum & 0xffff;
+
+ } else if (delta <= 0x7FFFFF) {
+ pkt->num_len = 3;
+ pkt->flags |= 0x2;
+ pkt->trunc = ctx->pnum & 0xffffff;
+
+ } else {
+ pkt->num_len = 4;
+ pkt->flags |= 0x3;
+ pkt->trunc = ctx->pnum & 0xffffffff;
+ }
+}
+
+
+ngx_int_t
+ngx_quic_negotiate_version(ngx_connection_t *c, ngx_quic_header_t *inpkt)
+{
+ size_t len;
+ ngx_quic_header_t pkt;
+ static u_char buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "sending version negotiation packet");
+
+ pkt.log = c->log;
+ pkt.flags = NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_FIXED_BIT;
+ pkt.dcid = inpkt->scid;
+ pkt.scid = inpkt->dcid;
+
+ len = ngx_quic_create_version_negotiation(&pkt, buf);
+
+#ifdef NGX_QUIC_DEBUG_PACKETS
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic vnego packet to send len:%uz %*xs", len, len, buf);
+#endif
+
+ (void) ngx_quic_send(c, buf, len);
+
+ return NGX_ERROR;
+}
+
+
+int
+ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
+ uint8_t alert)
+{
+ ngx_connection_t *c;
+ ngx_quic_connection_t *qc;
+
+ c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic ngx_quic_send_alert() lvl:%d alert:%d",
+ (int) level, (int) alert);
+
+ qc = ngx_quic_get_connection(c);
+ if (qc == NULL) {
+ return 1;
+ }
+
+ qc->error_level = level;
+ qc->error = NGX_QUIC_ERR_CRYPTO(alert);
+ qc->error_reason = "TLS alert";
+ qc->error_app = 0;
+ qc->error_ftype = 0;
+
+ if (ngx_quic_send_cc(c) != NGX_OK) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+ngx_int_t
+ngx_quic_send_stateless_reset(ngx_connection_t *c, ngx_quic_conf_t *conf,
+ ngx_quic_header_t *pkt)
+{
+ u_char *token;
+ size_t len, max;
+ uint16_t rndbytes;
+ u_char buf[NGX_QUIC_MAX_SR_PACKET];
+
+ ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic handle stateless reset output");
+
+ if (pkt->len <= NGX_QUIC_MIN_PKT_LEN) {
+ return NGX_DECLINED;
+ }
+
+ if (pkt->len <= NGX_QUIC_MIN_SR_PACKET) {
+ len = pkt->len - 1;
+
+ } else {
+ max = ngx_min(NGX_QUIC_MAX_SR_PACKET, pkt->len * 3);
+
+ if (RAND_bytes((u_char *) &rndbytes, sizeof(rndbytes)) != 1) {
+ return NGX_ERROR;
+ }
+
+ len = (rndbytes % (max - NGX_QUIC_MIN_SR_PACKET + 1))
+ + NGX_QUIC_MIN_SR_PACKET;
+ }
+
+ if (RAND_bytes(buf, len - NGX_QUIC_SR_TOKEN_LEN) != 1) {
+ return NGX_ERROR;
+ }
+
+ buf[0] &= ~NGX_QUIC_PKT_LONG;
+ buf[0] |= NGX_QUIC_PKT_FIXED_BIT;
+
+ token = &buf[len - NGX_QUIC_SR_TOKEN_LEN];
+
+ if (ngx_quic_new_sr_token(c, &pkt->dcid, conf->sr_token_key, token)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_quic_send(c, buf, len);
+
+ return NGX_DECLINED;
+}
+
+
+ngx_int_t
+ngx_quic_send_cc(ngx_connection_t *c)
+{
+ ngx_quic_frame_t *frame;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+
+ if (qc->draining) {
+ return NGX_OK;
+ }
+
+ if (qc->closing
+ && ngx_current_msec - qc->last_cc < NGX_QUIC_CC_MIN_INTERVAL)
+ {
+ /* dot not send CC too often */
+ return NGX_OK;
+ }
+
+ frame = ngx_quic_alloc_frame(c);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ frame->level = qc->error_level;
+ frame->type = qc->error_app ? NGX_QUIC_FT_CONNECTION_CLOSE_APP
+ : NGX_QUIC_FT_CONNECTION_CLOSE;
+ frame->u.close.error_code = qc->error;
+ frame->u.close.frame_type = qc->error_ftype;
+
+ if (qc->error_reason) {
+ frame->u.close.reason.len = ngx_strlen(qc->error_reason);
+ frame->u.close.reason.data = (u_char *) qc->error_reason;
+ }
+
+ ngx_quic_queue_frame(qc, frame);
+
+ qc->last_cc = ngx_current_msec;
+
+ return ngx_quic_output(c);
+}
+
+
+ngx_int_t
+ngx_quic_send_early_cc(ngx_connection_t *c, ngx_quic_header_t *inpkt,
+ ngx_uint_t err, const char *reason)
+{
+ ssize_t len;
+ ngx_str_t res;
+ ngx_quic_frame_t frame;
+ ngx_quic_header_t pkt;
+
+ static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
+ static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
+
+ ngx_memzero(&frame, sizeof(ngx_quic_frame_t));
+ ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
+
+ frame.level = inpkt->level;
+ frame.type = NGX_QUIC_FT_CONNECTION_CLOSE;
+ frame.u.close.error_code = err;
+
+ frame.u.close.reason.data = (u_char *) reason;
+ frame.u.close.reason.len = ngx_strlen(reason);
+
+ len = ngx_quic_create_frame(NULL, &frame);
+ if (len > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) {
+ return NGX_ERROR;
+ }
+
+ ngx_quic_log_frame(c->log, &frame, 1);
+
+ len = ngx_quic_create_frame(src, &frame);
+ if (len == -1) {
+ return NGX_ERROR;
+ }
+
+ pkt.keys = ngx_quic_keys_new(c->pool);
+ if (pkt.keys == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_quic_keys_set_initial_secret(c->pool, pkt.keys, &inpkt->dcid,
+ inpkt->version)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG
+ | NGX_QUIC_PKT_INITIAL;
+
+ pkt.num_len = 1;
+ /*
+ * pkt.num = 0;
+ * pkt.trunc = 0;
+ */
+
+ pkt.version = inpkt->version;
+ pkt.log = c->log;
+ pkt.level = inpkt->level;
+ pkt.dcid = inpkt->scid;
+ pkt.scid = inpkt->dcid;
+ pkt.payload.data = src;
+ pkt.payload.len = len;
+
+ res.data = dst;
+
+ if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_quic_send(c, res.data, res.len) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_quic_send_retry(ngx_connection_t *c, ngx_quic_conf_t *conf,
+ ngx_quic_header_t *inpkt)
+{
+ time_t expires;
+ ssize_t len;
+ ngx_str_t res, token;
+ ngx_quic_header_t pkt;
+
+ u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE];
+ u_char dcid[NGX_QUIC_SERVER_CID_LEN];
+
+ expires = ngx_time() + NGX_QUIC_RETRY_TOKEN_LIFETIME;
+
+ if (ngx_quic_new_token(c, conf->av_token_key, &token, &inpkt->dcid,
+ expires, 1)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
+ pkt.flags = NGX_QUIC_PKT_FIXED_BIT | NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_RETRY;
+ pkt.version = inpkt->version;
+ pkt.log = c->log;
+
+ pkt.odcid = inpkt->dcid;
+ pkt.dcid = inpkt->scid;
+
+ /* TODO: generate routable dcid */
+ if (RAND_bytes(dcid, NGX_QUIC_SERVER_CID_LEN) != 1) {
+ return NGX_ERROR;
+ }
+
+ pkt.scid.len = NGX_QUIC_SERVER_CID_LEN;
+ pkt.scid.data = dcid;
+
+ pkt.token = token;
+
+ res.data = buf;
+
+ if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+#ifdef NGX_QUIC_DEBUG_PACKETS
+ ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic packet to send len:%uz %xV", res.len, &res);
+#endif
+
+ len = ngx_quic_send(c, res.data, res.len);
+ if (len == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+ "quic retry packet sent to %xV", &pkt.dcid);
+
+ /*
+ * quic-transport 17.2.5.1: A server MUST NOT send more than one Retry
+ * packet in response to a single UDP datagram.
+ * NGX_DONE will stop quic_input() from processing further
+ */
+ return NGX_DONE;
+}
+
+
+ngx_int_t
+ngx_quic_send_new_token(ngx_connection_t *c)
+{
+ time_t expires;
+ ngx_str_t token;
+ ngx_quic_frame_t *frame;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+
+ if (!qc->conf->retry) {
+ return NGX_OK;
+ }
+
+ expires = ngx_time() + NGX_QUIC_NEW_TOKEN_LIFETIME;
+
+ if (ngx_quic_new_token(c, qc->conf->av_token_key, &token, NULL, expires, 0)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ frame = ngx_quic_alloc_frame(c);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ frame->level = ssl_encryption_application;
+ frame->type = NGX_QUIC_FT_NEW_TOKEN;
+ frame->u.token.length = token.len;
+ frame->u.token.data = token.data;
+
+ ngx_quic_queue_frame(qc, frame);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_quic_send_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
+{
+ size_t len, left;
+ uint64_t ack_delay;
+ ngx_buf_t *b;
+ ngx_uint_t i;
+ ngx_chain_t *cl, **ll;
+ ngx_quic_frame_t *frame;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+
+ ack_delay = ngx_current_msec - ctx->largest_received;
+ ack_delay *= 1000;
+ ack_delay >>= qc->tp.ack_delay_exponent;
+
+ frame = ngx_quic_alloc_frame(c);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ ll = &frame->data;
+ b = NULL;
+
+ for (i = 0; i < ctx->nranges; i++) {
+ len = ngx_quic_create_ack_range(NULL, ctx->ranges[i].gap,
+ ctx->ranges[i].range);
+
+ left = b ? b->end - b->last : 0;
+
+ if (left < len) {
+ cl = ngx_quic_alloc_buf(c);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ *ll = cl;
+ ll = &cl->next;
+
+ b = cl->buf;
+ left = b->end - b->last;
+
+ if (left < len) {
+ return NGX_ERROR;
+ }
+ }
+
+ b->last += ngx_quic_create_ack_range(b->last, ctx->ranges[i].gap,
+ ctx->ranges[i].range);
+
+ frame->u.ack.ranges_length += len;
+ }
+
+ *ll = NULL;
+
+ frame->level = ctx->level;
+ frame->type = NGX_QUIC_FT_ACK;
+ frame->u.ack.largest = ctx->largest_range;
+ frame->u.ack.delay = ack_delay;
+ frame->u.ack.range_count = ctx->nranges;
+ frame->u.ack.first_range = ctx->first_range;
+
+ ngx_quic_queue_frame(qc, frame);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_quic_send_ack_range(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
+ uint64_t smallest, uint64_t largest)
+{
+ ngx_quic_frame_t *frame;
+ ngx_quic_connection_t *qc;
+
+ qc = ngx_quic_get_connection(c);
+
+ frame = ngx_quic_alloc_frame(c);
+ if (frame == NULL) {
+ return NGX_ERROR;
+ }
+
+ frame->level = ctx->level;
+ frame->type = NGX_QUIC_FT_ACK;
+ frame->u.ack.largest = largest;
+ frame->u.ack.delay = 0;
+ frame->u.ack.range_count = 0;
+ frame->u.ack.first_range = largest - smallest;
+
+ ngx_quic_queue_frame(qc, frame);
+
+ return NGX_OK;
+}
diff --git a/src/event/quic/ngx_event_quic_output.h b/src/event/quic/ngx_event_quic_output.h
new file mode 100644
index 000000000..3277122cb
--- /dev/null
+++ b/src/event/quic/ngx_event_quic_output.h
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NGX_EVENT_QUIC_OUTPUT_H_INCLUDED_
+#define _NGX_EVENT_QUIC_OUTPUT_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+size_t ngx_quic_max_udp_payload(ngx_connection_t *c);
+
+ngx_int_t ngx_quic_output(ngx_connection_t *c);
+
+ngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c,
+ ngx_quic_header_t *inpkt);
+
+int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
+ enum ssl_encryption_level_t level, uint8_t alert);
+
+ngx_int_t ngx_quic_send_stateless_reset(ngx_connection_t *c,
+ ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
+ngx_int_t ngx_quic_send_cc(ngx_connection_t *c);
+ngx_int_t ngx_quic_send_early_cc(ngx_connection_t *c,
+ ngx_quic_header_t *inpkt, ngx_uint_t err, const char *reason);
+
+ngx_int_t ngx_quic_send_retry(ngx_connection_t *c,
+ ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
+ngx_int_t ngx_quic_send_new_token(ngx_connection_t *c);
+
+ngx_int_t ngx_quic_send_ack(ngx_connection_t *c,
+ ngx_quic_send_ctx_t *ctx);
+ngx_int_t ngx_quic_send_ack_range(ngx_connection_t *c,
+ ngx_quic_send_ctx_t *ctx, uint64_t smallest, uint64_t largest);
+
+#endif /* _NGX_EVENT_QUIC_OUTPUT_H_INCLUDED_ */