# HG changeset patch # User Vladimir Homutov # Date 1587410722 -10800 # Node ID 6481427ca3fc766f8ee4a9e8e4c95f1715fa47cb # Parent e0abe17a2878b1fae369b848673a2fe226ee487f Respecting maximum packet size. The header size macros for long and short packets were fixed to provide correct values in bytes. Currently the sending code limits frames so they don't exceed max_packet_size. But it does not account the case when a single frame can exceed the limit. As a result of this patch, big payload (CRYPTO and STREAM) will be split into a number of smaller frames that fit into advertised max_packet_size (which specifies final packet size, after encryption). diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c --- a/src/event/ngx_event_quic.c +++ b/src/event/ngx_event_quic.c @@ -320,7 +320,7 @@ ngx_quic_add_handshake_data(ngx_ssl_conn enum ssl_encryption_level_t level, const uint8_t *data, size_t len) { u_char *p, *end; - size_t client_params_len; + size_t client_params_len, fsize, limit; const uint8_t *client_params; ngx_quic_frame_t *frame; ngx_connection_t *c; @@ -359,31 +359,56 @@ ngx_quic_add_handshake_data(ngx_ssl_conn qc->tp.max_idle_timeout = qc->ctp.max_idle_timeout; } + if (qc->ctp.max_packet_size < NGX_QUIC_MIN_INITIAL_SIZE + || qc->ctp.max_packet_size > NGX_QUIC_DEFAULT_MAX_PACKET_SIZE) + { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "maximum packet size is invalid"); + return NGX_ERROR; + } + qc->client_tp_done = 1; } } + /* + * we need to fit at least 1 frame into a packet, thus account head/tail; + * 17 = 1 + 8x2 is max header for CRYPTO frame, with 1 byte for frame type + */ + limit = qc->ctp.max_packet_size - NGX_QUIC_MAX_LONG_HEADER - 17 + - EVP_GCM_TLS_TAG_LEN; + fs = &qc->crypto[level]; - frame = ngx_quic_alloc_frame(c, len); - if (frame == NULL) { - return 0; + p = (u_char *) data; + end = (u_char *) data + len; + + while (p < end) { + + fsize = ngx_min(limit, (size_t) (end - p)); + + frame = ngx_quic_alloc_frame(c, fsize); + if (frame == NULL) { + return 0; + } + + ngx_memcpy(frame->data, p, fsize); + + frame->level = level; + frame->type = NGX_QUIC_FT_CRYPTO; + frame->u.crypto.offset = fs->sent; + frame->u.crypto.length = fsize; + frame->u.crypto.data = frame->data; + + fs->sent += fsize; + p += fsize; + + ngx_sprintf(frame->info, "crypto, generated by SSL len=%ui level=%d", + fsize, level); + + ngx_quic_queue_frame(qc, frame); } - ngx_memcpy(frame->data, data, len); - - frame->level = level; - frame->type = NGX_QUIC_FT_CRYPTO; - frame->u.crypto.offset += fs->sent; - frame->u.crypto.length = len; - frame->u.crypto.data = frame->data; - - fs->sent += len; - - ngx_sprintf(frame->info, "crypto, generated by SSL len=%ui level=%d", len, level); - - ngx_quic_queue_frame(qc, frame); - return 1; } @@ -478,7 +503,7 @@ ngx_quic_new_connection(ngx_connection_t ngx_quic_connection_t *qc; static u_char buf[NGX_QUIC_DEFAULT_MAX_PACKET_SIZE]; - if (ngx_buf_size(pkt->raw) < 1200) { + if (ngx_buf_size(pkt->raw) < NGX_QUIC_MIN_INITIAL_SIZE) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "too small UDP datagram"); return NGX_ERROR; } @@ -2671,6 +2696,8 @@ ngx_quic_stream_recv(ngx_connection_t *c static ssize_t ngx_quic_stream_send(ngx_connection_t *c, u_char *buf, size_t size) { + u_char *p, *end; + size_t fsize, limit; ngx_connection_t *pc; ngx_quic_frame_t *frame; ngx_quic_stream_t *qs; @@ -2686,32 +2713,48 @@ ngx_quic_stream_send(ngx_connection_t *c ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic send: %uz", size); - frame = ngx_quic_alloc_frame(pc, size); - if (frame == NULL) { - return 0; + /* + * we need to fit at least 1 frame into a packet, thus account head/tail; + * 25 = 1 + 8x3 is max header for STREAM frame, with 1 byte for frame type + */ + limit = qc->ctp.max_packet_size - NGX_QUIC_MAX_SHORT_HEADER - 25 + - EVP_GCM_TLS_TAG_LEN; + + p = (u_char *) buf; + end = (u_char *) buf + size; + + while (p < end) { + + fsize = ngx_min(limit, (size_t) (end - p)); + + frame = ngx_quic_alloc_frame(pc, fsize); + if (frame == NULL) { + return 0; + } + + ngx_memcpy(frame->data, p, fsize); + + frame->level = ssl_encryption_application; + frame->type = NGX_QUIC_FT_STREAM6; /* OFF=1 LEN=1 FIN=0 */ + frame->u.stream.off = 1; + frame->u.stream.len = 1; + frame->u.stream.fin = 0; + + frame->u.stream.type = frame->type; + frame->u.stream.stream_id = qs->id; + frame->u.stream.offset = c->sent; + frame->u.stream.length = fsize; + frame->u.stream.data = frame->data; + + c->sent += fsize; + p += fsize; + + ngx_sprintf(frame->info, "stream %xi len=%ui level=%d", + qs->id, fsize, frame->level); + + ngx_quic_queue_frame(qc, frame); } - ngx_memcpy(frame->data, buf, size); - - frame->level = ssl_encryption_application; - frame->type = NGX_QUIC_FT_STREAM6; /* OFF=1 LEN=1 FIN=0 */ - frame->u.stream.off = 1; - frame->u.stream.len = 1; - frame->u.stream.fin = 0; - - frame->u.stream.type = frame->type; - frame->u.stream.stream_id = qs->id; - frame->u.stream.offset = c->sent; - frame->u.stream.length = size; - frame->u.stream.data = frame->data; - - c->sent += size; - - ngx_sprintf(frame->info, "stream %xi len=%ui level=%d", - qs->id, size, frame->level); - - ngx_quic_queue_frame(qc, frame); - return size; } diff --git a/src/event/ngx_event_quic.h b/src/event/ngx_event_quic.h --- a/src/event/ngx_event_quic.h +++ b/src/event/ngx_event_quic.h @@ -14,13 +14,16 @@ #define NGX_QUIC_DRAFT_VERSION 27 #define NGX_QUIC_VERSION (0xff000000 + NGX_QUIC_DRAFT_VERSION) -#define NGX_QUIC_MAX_SHORT_HEADER 25 -#define NGX_QUIC_MAX_LONG_HEADER 346 +#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_DEFAULT_MAX_PACKET_SIZE 65527 #define NGX_QUIC_DEFAULT_ACK_DELAY_EXPONENT 3 #define NGX_QUIC_DEFAULT_MAX_ACK_DELAY 25 +#define NGX_QUIC_MIN_INITIAL_SIZE 1200 + #define NGX_QUIC_STREAM_SERVER_INITIATED 0x01 #define NGX_QUIC_STREAM_UNIDIRECTIONAL 0x02