diff --git a/src/event/quic/ngx_event_quic_output.c b/src/event/quic/ngx_event_quic_output.c index 1fc8fadd5..add69b1a2 100644 --- a/src/event/quic/ngx_event_quic_output.c +++ b/src/event/quic/ngx_event_quic_output.c @@ -10,10 +10,6 @@ #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 @@ -532,12 +528,12 @@ 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, ngx_quic_socket_t *qsock) { - size_t len, hlen, pad_len; + size_t len, pad, min_payload, max_payload; u_char *p; ssize_t flen; - ngx_str_t out, res; + ngx_str_t res; ngx_int_t rc; - ngx_uint_t nframes, has_pr; + ngx_uint_t nframes, expand; ngx_msec_t now; ngx_queue_t *q; ngx_quic_frame_t *f; @@ -555,18 +551,22 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, ngx_quic_init_packet(c, ctx, qsock, &pkt); - hlen = (ctx->level == ssl_encryption_application) - ? NGX_QUIC_MAX_SHORT_HEADER - : NGX_QUIC_MAX_LONG_HEADER; + min_payload = ngx_quic_payload_size(&pkt, min); + max_payload = ngx_quic_payload_size(&pkt, max); - hlen += EVP_GCM_TLS_TAG_LEN; - hlen -= NGX_QUIC_MAX_CID_LEN - qsock->cid->len; + /* RFC 9001, 5.4.2. Header Protection Sample */ + pad = 4 - pkt.num_len; + min_payload = ngx_max(min_payload, pad); + + if (min_payload > max_payload) { + return 0; + } now = ngx_current_msec; nframes = 0; p = src; len = 0; - has_pr = 0; + expand = 0; for (q = ngx_queue_head(&ctx->frames); q != ngx_queue_sentinel(&ctx->frames); @@ -574,18 +574,39 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, { f = ngx_queue_data(q, ngx_quic_frame_t, queue); - if (f->type == NGX_QUIC_FT_PATH_RESPONSE - || f->type == NGX_QUIC_FT_PATH_CHALLENGE) + if (!expand && (f->type == NGX_QUIC_FT_PATH_RESPONSE + || f->type == NGX_QUIC_FT_PATH_CHALLENGE)) { - has_pr = 1; + /* + * RFC 9000, 8.2.1. Initiating Path Validation + * + * An endpoint MUST expand datagrams that contain a + * PATH_CHALLENGE frame to at least the smallest allowed + * maximum datagram size of 1200 bytes... + * + * (same applies to PATH_RESPONSE frames) + */ + + if (max < 1200) { + /* expanded packet will not fit */ + break; + } + + if (min < 1200) { + min = 1200; + + min_payload = ngx_quic_payload_size(&pkt, min); + } + + expand = 1; } - if (hlen + len >= max) { + if (len >= max_payload) { break; } - if (hlen + len + f->len > max) { - rc = ngx_quic_split_frame(c, f, max - hlen - len); + if (len + f->len > max_payload) { + rc = ngx_quic_split_frame(c, f, max_payload - len); if (rc == NGX_ERROR) { return NGX_ERROR; @@ -626,53 +647,21 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, return 0; } - out.data = src; - out.len = len; + if (len < min_payload) { + ngx_memset(p, NGX_QUIC_FT_PADDING, min_payload - len); + len = min_payload; + } - pad_len = 4; - - if (min || has_pr) { - hlen = EVP_GCM_TLS_TAG_LEN - + ngx_quic_create_header(&pkt, NULL, out.len, NULL); - - /* - * RFC 9000, 8.2.1. Initiating Path Validation - * - * An endpoint MUST expand datagrams that contain a - * PATH_CHALLENGE frame to at least the smallest allowed - * maximum datagram size of 1200 bytes, unless the - * anti-amplification limit for the path does not permit - * sending a datagram of this size. - * - * (same applies to PATH_RESPONSE frames) - */ - - if (has_pr) { - min = ngx_max(1200, min); - } - - if (min > hlen + pad_len) { - pad_len = min - hlen; - } - } - - if (out.len < pad_len) { - /* compensate for potentially enlarged header in Length bytes */ - pad_len -= ngx_quic_create_header(&pkt, NULL, pad_len, NULL) - - ngx_quic_create_header(&pkt, NULL, out.len, NULL); - ngx_memset(p, NGX_QUIC_FT_PADDING, pad_len - out.len); - out.len = pad_len; - } - - pkt.payload = out; + pkt.payload.data = src; + pkt.payload.len = len; 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); + ngx_quic_level_name(ctx->level), pkt.payload.len, + pkt.need_ack, pkt.number, pkt.num_len, pkt.trunc); if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) { return NGX_ERROR; @@ -1253,6 +1242,7 @@ ssize_t ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame, size_t min, struct sockaddr *sockaddr, socklen_t socklen) { + size_t min_payload, pad; ssize_t len; ngx_str_t res; ngx_quic_header_t pkt; @@ -1267,6 +1257,11 @@ ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame, ngx_quic_init_packet(c, ctx, qc->socket, &pkt); + min_payload = min ? ngx_quic_payload_size(&pkt, min) : 0; + + pad = 4 - pkt.num_len; + min_payload = ngx_max(min_payload, pad); + len = ngx_quic_create_frame(NULL, frame); if (len > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) { return -1; @@ -1279,10 +1274,10 @@ ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame, return -1; } - if (len < (ssize_t) min) { - ngx_memset(src + len, NGX_QUIC_FT_PADDING, min - len); - len = min; - } + if (len < (ssize_t) min_payload) { + ngx_memset(src + len, NGX_QUIC_FT_PADDING, min_payload - len); + len = min_payload; + } pkt.payload.data = src; pkt.payload.len = len; diff --git a/src/event/quic/ngx_event_quic_protection.c b/src/event/quic/ngx_event_quic_protection.c index 43336b41e..d193a7738 100644 --- a/src/event/quic/ngx_event_quic_protection.c +++ b/src/event/quic/ngx_event_quic_protection.c @@ -836,11 +836,10 @@ ngx_quic_create_packet(ngx_quic_header_t *pkt, ngx_str_t *res) ngx_quic_ciphers_t ciphers; u_char nonce[NGX_QUIC_IV_LEN], mask[NGX_QUIC_HP_LEN]; - out.len = pkt->payload.len + EVP_GCM_TLS_TAG_LEN; - ad.data = res->data; - ad.len = ngx_quic_create_header(pkt, ad.data, out.len, &pnp); + ad.len = ngx_quic_create_header(pkt, ad.data, &pnp); + out.len = pkt->payload.len + EVP_GCM_TLS_TAG_LEN; out.data = res->data + ad.len; #ifdef NGX_QUIC_DEBUG_CRYPTO diff --git a/src/event/quic/ngx_event_quic_transport.c b/src/event/quic/ngx_event_quic_transport.c index 16ae9c37c..6bc188b59 100644 --- a/src/event/quic/ngx_event_quic_transport.c +++ b/src/event/quic/ngx_event_quic_transport.c @@ -94,7 +94,7 @@ static ngx_int_t ngx_quic_supported_version(uint32_t version); static ngx_int_t ngx_quic_parse_long_header_v1(ngx_quic_header_t *pkt); static size_t ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out, - size_t pkt_len, u_char **pnp); + u_char **pnp); static size_t ngx_quic_create_short_header(ngx_quic_header_t *pkt, u_char *out, u_char **pnp); @@ -613,25 +613,63 @@ ngx_quic_create_version_negotiation(ngx_quic_header_t *pkt, u_char *out) } +/* returns the amount of payload quic packet of "pkt_len" size may fit or 0 */ size_t -ngx_quic_create_header(ngx_quic_header_t *pkt, u_char *out, size_t pkt_len, - u_char **pnp) +ngx_quic_payload_size(ngx_quic_header_t *pkt, size_t pkt_len) +{ + size_t len; + + if ngx_quic_short_pkt(pkt->flags) { + + len = 1 + pkt->dcid.len + pkt->num_len + EVP_GCM_TLS_TAG_LEN; + if (len > pkt_len) { + return 0; + } + + return pkt_len - len; + } + + /* flags, version, dcid and scid with lengths and zero-length token */ + len = 5 + 2 + pkt->dcid.len + pkt->scid.len + + (pkt->level == ssl_encryption_initial ? 1 : 0); + + if (len > pkt_len) { + return 0; + } + + /* (pkt_len - len) is 'remainder' packet length (see RFC 9000, 17.2) */ + len += ngx_quic_varint_len(pkt_len - len) + + pkt->num_len + EVP_GCM_TLS_TAG_LEN; + + if (len > pkt_len) { + return 0; + } + + return pkt_len - len; +} + + +size_t +ngx_quic_create_header(ngx_quic_header_t *pkt, u_char *out, u_char **pnp) { return ngx_quic_short_pkt(pkt->flags) ? ngx_quic_create_short_header(pkt, out, pnp) - : ngx_quic_create_long_header(pkt, out, pkt_len, pnp); + : ngx_quic_create_long_header(pkt, out, pnp); } static size_t ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out, - size_t pkt_len, u_char **pnp) + u_char **pnp) { + size_t rem_len; u_char *p, *start; + rem_len = pkt->num_len + pkt->payload.len + EVP_GCM_TLS_TAG_LEN; + if (out == NULL) { return 5 + 2 + pkt->dcid.len + pkt->scid.len - + ngx_quic_varint_len(pkt_len + pkt->num_len) + pkt->num_len + + ngx_quic_varint_len(rem_len) + pkt->num_len + (pkt->level == ssl_encryption_initial ? 1 : 0); } @@ -651,7 +689,7 @@ ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out, ngx_quic_build_int(&p, 0); } - ngx_quic_build_int(&p, pkt_len + pkt->num_len); + ngx_quic_build_int(&p, rem_len); *pnp = p; diff --git a/src/event/quic/ngx_event_quic_transport.h b/src/event/quic/ngx_event_quic_transport.h index 493882308..ed5777b16 100644 --- a/src/event/quic/ngx_event_quic_transport.h +++ b/src/event/quic/ngx_event_quic_transport.h @@ -345,8 +345,10 @@ ngx_int_t ngx_quic_parse_packet(ngx_quic_header_t *pkt); size_t ngx_quic_create_version_negotiation(ngx_quic_header_t *pkt, u_char *out); +size_t ngx_quic_payload_size(ngx_quic_header_t *pkt, size_t pkt_len); + size_t ngx_quic_create_header(ngx_quic_header_t *pkt, u_char *out, - size_t pkt_len, u_char **pnp); + u_char **pnp); size_t ngx_quic_create_retry_itag(ngx_quic_header_t *pkt, u_char *out, u_char **start);