comparison src/event/quic/ngx_event_quic_output.c @ 8894:de7b9af30fc6 quic

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.
author Vladimir Homutov <vl@nginx.com>
date Thu, 07 Oct 2021 13:48:29 +0300
parents d8ac4d3c24ac
children 25b87b392ce0
comparison
equal deleted inserted replaced
8893:126a15530136 8894:de7b9af30fc6
7 #include <ngx_config.h> 7 #include <ngx_config.h>
8 #include <ngx_core.h> 8 #include <ngx_core.h>
9 #include <ngx_event.h> 9 #include <ngx_event.h>
10 #include <ngx_event_quic_connection.h> 10 #include <ngx_event_quic_connection.h>
11 11
12
13 #define NGX_QUIC_MAX_SHORT_HEADER 25 /* 1 flags + 20 dcid + 4 pn */
14 #define NGX_QUIC_MAX_LONG_HEADER 56
15 /* 1 flags + 4 version + 2 x (1 + 20) s/dcid + 4 pn + 4 len + token len */
16 12
17 #define NGX_QUIC_MAX_UDP_PAYLOAD_OUT 1252 13 #define NGX_QUIC_MAX_UDP_PAYLOAD_OUT 1252
18 #define NGX_QUIC_MAX_UDP_PAYLOAD_OUT6 1232 14 #define NGX_QUIC_MAX_UDP_PAYLOAD_OUT6 1232
19 15
20 #define NGX_QUIC_MAX_UDP_SEGMENT_BUF 65487 /* 65K - IPv6 header */ 16 #define NGX_QUIC_MAX_UDP_SEGMENT_BUF 65487 /* 65K - IPv6 header */
530 526
531 static ssize_t 527 static ssize_t
532 ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, 528 ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
533 u_char *data, size_t max, size_t min, ngx_quic_socket_t *qsock) 529 u_char *data, size_t max, size_t min, ngx_quic_socket_t *qsock)
534 { 530 {
535 size_t len, hlen, pad_len; 531 size_t len, pad, min_payload, max_payload;
536 u_char *p; 532 u_char *p;
537 ssize_t flen; 533 ssize_t flen;
538 ngx_str_t out, res; 534 ngx_str_t res;
539 ngx_int_t rc; 535 ngx_int_t rc;
540 ngx_uint_t nframes, has_pr; 536 ngx_uint_t nframes, expand;
541 ngx_msec_t now; 537 ngx_msec_t now;
542 ngx_queue_t *q; 538 ngx_queue_t *q;
543 ngx_quic_frame_t *f; 539 ngx_quic_frame_t *f;
544 ngx_quic_header_t pkt; 540 ngx_quic_header_t pkt;
545 static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; 541 static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
553 qsock->sid.seqnum, ngx_quic_level_name(ctx->level), 549 qsock->sid.seqnum, ngx_quic_level_name(ctx->level),
554 max, min); 550 max, min);
555 551
556 ngx_quic_init_packet(c, ctx, qsock, &pkt); 552 ngx_quic_init_packet(c, ctx, qsock, &pkt);
557 553
558 hlen = (ctx->level == ssl_encryption_application) 554 min_payload = ngx_quic_payload_size(&pkt, min);
559 ? NGX_QUIC_MAX_SHORT_HEADER 555 max_payload = ngx_quic_payload_size(&pkt, max);
560 : NGX_QUIC_MAX_LONG_HEADER; 556
561 557 /* RFC 9001, 5.4.2. Header Protection Sample */
562 hlen += EVP_GCM_TLS_TAG_LEN; 558 pad = 4 - pkt.num_len;
563 hlen -= NGX_QUIC_MAX_CID_LEN - qsock->cid->len; 559 min_payload = ngx_max(min_payload, pad);
560
561 if (min_payload > max_payload) {
562 return 0;
563 }
564 564
565 now = ngx_current_msec; 565 now = ngx_current_msec;
566 nframes = 0; 566 nframes = 0;
567 p = src; 567 p = src;
568 len = 0; 568 len = 0;
569 has_pr = 0; 569 expand = 0;
570 570
571 for (q = ngx_queue_head(&ctx->frames); 571 for (q = ngx_queue_head(&ctx->frames);
572 q != ngx_queue_sentinel(&ctx->frames); 572 q != ngx_queue_sentinel(&ctx->frames);
573 q = ngx_queue_next(q)) 573 q = ngx_queue_next(q))
574 { 574 {
575 f = ngx_queue_data(q, ngx_quic_frame_t, queue); 575 f = ngx_queue_data(q, ngx_quic_frame_t, queue);
576 576
577 if (f->type == NGX_QUIC_FT_PATH_RESPONSE 577 if (!expand && (f->type == NGX_QUIC_FT_PATH_RESPONSE
578 || f->type == NGX_QUIC_FT_PATH_CHALLENGE) 578 || f->type == NGX_QUIC_FT_PATH_CHALLENGE))
579 { 579 {
580 has_pr = 1; 580 /*
581 } 581 * RFC 9000, 8.2.1. Initiating Path Validation
582 582 *
583 if (hlen + len >= max) { 583 * An endpoint MUST expand datagrams that contain a
584 * PATH_CHALLENGE frame to at least the smallest allowed
585 * maximum datagram size of 1200 bytes...
586 *
587 * (same applies to PATH_RESPONSE frames)
588 */
589
590 if (max < 1200) {
591 /* expanded packet will not fit */
592 break;
593 }
594
595 if (min < 1200) {
596 min = 1200;
597
598 min_payload = ngx_quic_payload_size(&pkt, min);
599 }
600
601 expand = 1;
602 }
603
604 if (len >= max_payload) {
584 break; 605 break;
585 } 606 }
586 607
587 if (hlen + len + f->len > max) { 608 if (len + f->len > max_payload) {
588 rc = ngx_quic_split_frame(c, f, max - hlen - len); 609 rc = ngx_quic_split_frame(c, f, max_payload - len);
589 610
590 if (rc == NGX_ERROR) { 611 if (rc == NGX_ERROR) {
591 return NGX_ERROR; 612 return NGX_ERROR;
592 } 613 }
593 614
624 645
625 if (nframes == 0) { 646 if (nframes == 0) {
626 return 0; 647 return 0;
627 } 648 }
628 649
629 out.data = src; 650 if (len < min_payload) {
630 out.len = len; 651 ngx_memset(p, NGX_QUIC_FT_PADDING, min_payload - len);
631 652 len = min_payload;
632 pad_len = 4; 653 }
633 654
634 if (min || has_pr) { 655 pkt.payload.data = src;
635 hlen = EVP_GCM_TLS_TAG_LEN 656 pkt.payload.len = len;
636 + ngx_quic_create_header(&pkt, NULL, out.len, NULL);
637
638 /*
639 * RFC 9000, 8.2.1. Initiating Path Validation
640 *
641 * An endpoint MUST expand datagrams that contain a
642 * PATH_CHALLENGE frame to at least the smallest allowed
643 * maximum datagram size of 1200 bytes, unless the
644 * anti-amplification limit for the path does not permit
645 * sending a datagram of this size.
646 *
647 * (same applies to PATH_RESPONSE frames)
648 */
649
650 if (has_pr) {
651 min = ngx_max(1200, min);
652 }
653
654 if (min > hlen + pad_len) {
655 pad_len = min - hlen;
656 }
657 }
658
659 if (out.len < pad_len) {
660 /* compensate for potentially enlarged header in Length bytes */
661 pad_len -= ngx_quic_create_header(&pkt, NULL, pad_len, NULL)
662 - ngx_quic_create_header(&pkt, NULL, out.len, NULL);
663 ngx_memset(p, NGX_QUIC_FT_PADDING, pad_len - out.len);
664 out.len = pad_len;
665 }
666
667 pkt.payload = out;
668 657
669 res.data = data; 658 res.data = data;
670 659
671 ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0, 660 ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0,
672 "quic packet tx %s bytes:%ui" 661 "quic packet tx %s bytes:%ui"
673 " need_ack:%d number:%L encoded nl:%d trunc:0x%xD", 662 " need_ack:%d number:%L encoded nl:%d trunc:0x%xD",
674 ngx_quic_level_name(ctx->level), out.len, pkt.need_ack, 663 ngx_quic_level_name(ctx->level), pkt.payload.len,
675 pkt.number, pkt.num_len, pkt.trunc); 664 pkt.need_ack, pkt.number, pkt.num_len, pkt.trunc);
676 665
677 if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) { 666 if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
678 return NGX_ERROR; 667 return NGX_ERROR;
679 } 668 }
680 669
1251 1240
1252 ssize_t 1241 ssize_t
1253 ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame, 1242 ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame,
1254 size_t min, struct sockaddr *sockaddr, socklen_t socklen) 1243 size_t min, struct sockaddr *sockaddr, socklen_t socklen)
1255 { 1244 {
1245 size_t min_payload, pad;
1256 ssize_t len; 1246 ssize_t len;
1257 ngx_str_t res; 1247 ngx_str_t res;
1258 ngx_quic_header_t pkt; 1248 ngx_quic_header_t pkt;
1259 ngx_quic_send_ctx_t *ctx; 1249 ngx_quic_send_ctx_t *ctx;
1260 ngx_quic_connection_t *qc; 1250 ngx_quic_connection_t *qc;
1265 qc = ngx_quic_get_connection(c); 1255 qc = ngx_quic_get_connection(c);
1266 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); 1256 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application);
1267 1257
1268 ngx_quic_init_packet(c, ctx, qc->socket, &pkt); 1258 ngx_quic_init_packet(c, ctx, qc->socket, &pkt);
1269 1259
1260 min_payload = min ? ngx_quic_payload_size(&pkt, min) : 0;
1261
1262 pad = 4 - pkt.num_len;
1263 min_payload = ngx_max(min_payload, pad);
1264
1270 len = ngx_quic_create_frame(NULL, frame); 1265 len = ngx_quic_create_frame(NULL, frame);
1271 if (len > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) { 1266 if (len > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) {
1272 return -1; 1267 return -1;
1273 } 1268 }
1274 1269
1277 len = ngx_quic_create_frame(src, frame); 1272 len = ngx_quic_create_frame(src, frame);
1278 if (len == -1) { 1273 if (len == -1) {
1279 return -1; 1274 return -1;
1280 } 1275 }
1281 1276
1282 if (len < (ssize_t) min) { 1277 if (len < (ssize_t) min_payload) {
1283 ngx_memset(src + len, NGX_QUIC_FT_PADDING, min - len); 1278 ngx_memset(src + len, NGX_QUIC_FT_PADDING, min_payload - len);
1284 len = min; 1279 len = min_payload;
1285 } 1280 }
1286 1281
1287 pkt.payload.data = src; 1282 pkt.payload.data = src;
1288 pkt.payload.len = len; 1283 pkt.payload.len = len;
1289 1284
1290 res.data = dst; 1285 res.data = dst;