]> git.kaiwu.me - nginx.git/commitdiff
QUIC: refactored packet creation.
authorVladimir Homutov <vl@nginx.com>
Thu, 7 Oct 2021 10:48:29 +0000 (13:48 +0300)
committerVladimir Homutov <vl@nginx.com>
Thu, 7 Oct 2021 10:48:29 +0000 (13:48 +0300)
The "min" and "max" arguments refer to UDP datagram size.  Generating payload
requires to account properly for header size, which is variable and depends on
payload size and packet number.

src/event/quic/ngx_event_quic_output.c
src/event/quic/ngx_event_quic_protection.c
src/event/quic/ngx_event_quic_transport.c
src/event/quic/ngx_event_quic_transport.h

index 1fc8fadd5e23974921eeff07a1693e7faefc5919..add69b1a21e7b2c7922d4aeb27d70d00de9adf11 100644 (file)
 #include <ngx_event_quic_connection.h>
 
 
-#define NGX_QUIC_MAX_SHORT_HEADER        25 /* 1 flags + 20 dcid + 4 pn */
-#define NGX_QUIC_MAX_LONG_HEADER         56
-    /* 1 flags + 4 version + 2 x (1 + 20) s/dcid + 4 pn + 4 len + token len */
-
 #define NGX_QUIC_MAX_UDP_PAYLOAD_OUT   1252
 #define NGX_QUIC_MAX_UDP_PAYLOAD_OUT6  1232
 
@@ -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);
+
+    /* RFC 9001, 5.4.2.  Header Protection Sample */
+    pad = 4 - pkt.num_len;
+    min_payload = ngx_max(min_payload, pad);
 
-    hlen += EVP_GCM_TLS_TAG_LEN;
-    hlen -= NGX_QUIC_MAX_CID_LEN - qsock->cid->len;
+    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;
-
-    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 (len < min_payload) {
+        ngx_memset(p, NGX_QUIC_FT_PADDING, min_payload - len);
+        len = min_payload;
+     }
 
-        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;
index 43336b41e995a9c0d0c53a139ec554302b7eecff..d193a7738c85aa0ad6fa7a1f961a4e0c6a7af328 100644 (file)
@@ -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
index 16ae9c37cc7731d28facb37e42af3f2d1f8ccb23..6bc188b59c8801dd94eb910f5319c3343ccc87ed 100644 (file)
@@ -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;
 
index 4938823085c7ff045ec05b03297ce3c76a813c06..ed5777b163aec9ddfda49c96d0979553d3a18802 100644 (file)
@@ -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);