]> git.kaiwu.me - nginx.git/commitdiff
Respecting maximum packet size.
authorVladimir Homutov <vl@nginx.com>
Mon, 20 Apr 2020 19:25:22 +0000 (22:25 +0300)
committerVladimir Homutov <vl@nginx.com>
Mon, 20 Apr 2020 19:25:22 +0000 (22:25 +0300)
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).

src/event/ngx_event_quic.c
src/event/ngx_event_quic.h

index 990c1ec1443282e2fda3138caff38ff0c67aa5bf..2dd415f9967024acd98c0224d430d78d70f2fde3 100644 (file)
@@ -320,7 +320,7 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *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,30 +359,55 @@ ngx_quic_add_handshake_data(ngx_ssl_conn_t *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;
 
-    ngx_memcpy(frame->data, data, len);
+    while (p < end) {
 
-    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;
+        fsize = ngx_min(limit, (size_t) (end - p));
 
-    fs->sent += len;
+        frame = ngx_quic_alloc_frame(c, fsize);
+        if (frame == NULL) {
+            return 0;
+        }
 
-    ngx_sprintf(frame->info, "crypto, generated by SSL len=%ui level=%d", len, level);
+        ngx_memcpy(frame->data, p, fsize);
 
-    ngx_quic_queue_frame(qc, frame);
+        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);
+    }
 
     return 1;
 }
@@ -478,7 +503,7 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
     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, u_char *buf, size_t size)
 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,31 +2713,47 @@ ngx_quic_stream_send(ngx_connection_t *c, u_char *buf, size_t size)
 
     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;
 
-    ngx_memcpy(frame->data, buf, size);
+    p = (u_char *) buf;
+    end = (u_char *) 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;
+    while (p < end) {
 
-    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;
+        fsize = ngx_min(limit, (size_t) (end - p));
 
-    c->sent += size;
+        frame = ngx_quic_alloc_frame(pc, fsize);
+        if (frame == NULL) {
+            return 0;
+        }
 
-    ngx_sprintf(frame->info, "stream %xi len=%ui level=%d",
-                qs->id, size, frame->level);
+        ngx_memcpy(frame->data, p, fsize);
 
-    ngx_quic_queue_frame(qc, frame);
+        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);
+    }
 
     return size;
 }
index c843fbabe4dd793080217437fd7a26a0351ce9cd..0dfc9b2e76e8e2fabf250895cf76b5c7f34bccf0 100644 (file)
 #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