# HG changeset patch # User Vladimir Homutov # Date 1618314080 -10800 # Node ID bc910a5ec7372060045f4353723e8d1898081580 # Parent 41807e581de94cfcc18283af213acad1d6652546 QUIC: separate files for output and ack related processing. diff --git a/auto/modules b/auto/modules --- a/auto/modules +++ b/auto/modules @@ -1346,14 +1346,18 @@ if [ $USE_OPENSSL$USE_OPENSSL_QUIC = YES src/event/quic/ngx_event_quic_frames.h \ src/event/quic/ngx_event_quic_connid.h \ src/event/quic/ngx_event_quic_migration.h \ - src/event/quic/ngx_event_quic_streams.h" + src/event/quic/ngx_event_quic_streams.h \ + src/event/quic/ngx_event_quic_ack.h \ + src/event/quic/ngx_event_quic_output.h" ngx_module_srcs="src/event/quic/ngx_event_quic.c \ src/event/quic/ngx_event_quic_transport.c \ src/event/quic/ngx_event_quic_protection.c \ src/event/quic/ngx_event_quic_frames.c \ src/event/quic/ngx_event_quic_connid.c \ src/event/quic/ngx_event_quic_migration.c \ - src/event/quic/ngx_event_quic_streams.c" + src/event/quic/ngx_event_quic_streams.c \ + src/event/quic/ngx_event_quic_ack.c \ + src/event/quic/ngx_event_quic_output.c" ngx_module_libs= ngx_module_link=YES diff --git a/src/event/quic/ngx_event_quic.c b/src/event/quic/ngx_event_quic.c --- a/src/event/quic/ngx_event_quic.c +++ b/src/event/quic/ngx_event_quic.c @@ -11,28 +11,12 @@ #include -#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_secre 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_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 "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 *ss } -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 } -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_con } -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_ } -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_ } -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 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 * } -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 } - -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 --- /dev/null +++ b/src/event/quic/ngx_event_quic_ack.c @@ -0,0 +1,1081 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +#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 --- /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 +#include + + +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 --- a/src/event/quic/ngx_event_quic_connection.h +++ b/src/event/quic/ngx_event_quic_connection.h @@ -15,39 +15,22 @@ #include typedef struct ngx_quic_connection_s ngx_quic_connection_t; +typedef struct ngx_quic_send_ctx_s ngx_quic_send_ctx_t; #include #include #include #include +#include +#include -#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 --- /dev/null +++ b/src/event/quic/ngx_event_quic_output.c @@ -0,0 +1,851 @@ + +/* + * Copyright (C) Nginx, Inc. + */ + + +#include +#include +#include +#include + + +#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 --- /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 +#include + + +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_ */