aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVladimir Homutov <vl@nginx.com>2021-10-07 13:48:29 +0300
committerVladimir Homutov <vl@nginx.com>2021-10-07 13:48:29 +0300
commit5f9c4e15a398bc10e23c7d366d181380135e2503 (patch)
tree546ce66ba5e2111124cdeacdfcff10ca1a57af15 /src
parent151985c931b4c96ca95e78b60f28d537e41352cc (diff)
downloadnginx-5f9c4e15a398bc10e23c7d366d181380135e2503.tar.gz
nginx-5f9c4e15a398bc10e23c7d366d181380135e2503.zip
QUIC: refactored packet creation.
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.
Diffstat (limited to 'src')
-rw-r--r--src/event/quic/ngx_event_quic_output.c121
-rw-r--r--src/event/quic/ngx_event_quic_protection.c5
-rw-r--r--src/event/quic/ngx_event_quic_transport.c52
-rw-r--r--src/event/quic/ngx_event_quic_transport.h4
4 files changed, 108 insertions, 74 deletions
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 <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;
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);