comparison src/event/ngx_event_quic.c @ 8658:0af4ec6d1f92 quic

QUIC: coalesce output packets into a single UDP datagram. Now initial output packet is not padded anymore if followed by a handshake packet. If the datagram is still not big enough to satisfy minimum size requirements, handshake packet is padded.
author Roman Arutyunyan <arut@nginx.com>
date Mon, 07 Dec 2020 15:09:08 +0000
parents 2dfc5ef29973
children d9f673d18e9b
comparison
equal deleted inserted replaced
8657:2dfc5ef29973 8658:0af4ec6d1f92
152 152
153 ngx_uint_t pto_count; 153 ngx_uint_t pto_count;
154 154
155 ngx_queue_t free_frames; 155 ngx_queue_t free_frames;
156 ngx_chain_t *free_bufs; 156 ngx_chain_t *free_bufs;
157 ngx_buf_t *free_shadow_bufs;
157 158
158 #ifdef NGX_QUIC_DEBUG_ALLOC 159 #ifdef NGX_QUIC_DEBUG_ALLOC
159 ngx_uint_t nframes; 160 ngx_uint_t nframes;
160 ngx_uint_t nbufs; 161 ngx_uint_t nbufs;
161 #endif 162 #endif
162 163
163 ngx_quic_streams_t streams; 164 ngx_quic_streams_t streams;
164 ngx_quic_congestion_t congestion; 165 ngx_quic_congestion_t congestion;
165 size_t received; 166 off_t received;
166 167
167 ngx_uint_t error; 168 ngx_uint_t error;
168 enum ssl_encryption_level_t error_level; 169 enum ssl_encryption_level_t error_level;
169 ngx_uint_t error_ftype; 170 ngx_uint_t error_ftype;
170 const char *error_reason; 171 const char *error_reason;
329 330
330 static void ngx_quic_queue_frame(ngx_quic_connection_t *qc, 331 static void ngx_quic_queue_frame(ngx_quic_connection_t *qc,
331 ngx_quic_frame_t *frame); 332 ngx_quic_frame_t *frame);
332 333
333 static ngx_int_t ngx_quic_output(ngx_connection_t *c); 334 static ngx_int_t ngx_quic_output(ngx_connection_t *c);
334 static ngx_int_t ngx_quic_output_frames(ngx_connection_t *c, 335 static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c);
336 static ngx_int_t ngx_quic_generate_ack(ngx_connection_t *c,
335 ngx_quic_send_ctx_t *ctx); 337 ngx_quic_send_ctx_t *ctx);
338 static ssize_t ngx_quic_output_packet(ngx_connection_t *c,
339 ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min);
340 static ngx_int_t ngx_quic_split_frame(ngx_connection_t *c, ngx_quic_frame_t *f,
341 size_t len);
336 static void ngx_quic_free_frames(ngx_connection_t *c, ngx_queue_t *frames); 342 static void ngx_quic_free_frames(ngx_connection_t *c, ngx_queue_t *frames);
337 static ngx_int_t ngx_quic_send_frames(ngx_connection_t *c,
338 ngx_quic_send_ctx_t *ctx, ngx_queue_t *frames);
339 static ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len); 343 static ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len);
340 344
341 static void ngx_quic_set_packet_number(ngx_quic_header_t *pkt, 345 static void ngx_quic_set_packet_number(ngx_quic_header_t *pkt,
342 ngx_quic_send_ctx_t *ctx); 346 ngx_quic_send_ctx_t *ctx);
343 static void ngx_quic_pto_handler(ngx_event_t *ev); 347 static void ngx_quic_pto_handler(ngx_event_t *ev);
359 size_t size); 363 size_t size);
360 static ssize_t ngx_quic_stream_send(ngx_connection_t *c, u_char *buf, 364 static ssize_t ngx_quic_stream_send(ngx_connection_t *c, u_char *buf,
361 size_t size); 365 size_t size);
362 static ngx_chain_t *ngx_quic_stream_send_chain(ngx_connection_t *c, 366 static ngx_chain_t *ngx_quic_stream_send_chain(ngx_connection_t *c,
363 ngx_chain_t *in, off_t limit); 367 ngx_chain_t *in, off_t limit);
364 static size_t ngx_quic_max_stream_frame(ngx_quic_connection_t *qc);
365 static size_t ngx_quic_max_stream_flow(ngx_connection_t *c); 368 static size_t ngx_quic_max_stream_flow(ngx_connection_t *c);
366 static void ngx_quic_stream_cleanup_handler(void *data); 369 static void ngx_quic_stream_cleanup_handler(void *data);
367 static ngx_quic_frame_t *ngx_quic_alloc_frame(ngx_connection_t *c); 370 static ngx_quic_frame_t *ngx_quic_alloc_frame(ngx_connection_t *c);
368 static void ngx_quic_free_frame(ngx_connection_t *c, ngx_quic_frame_t *frame); 371 static void ngx_quic_free_frame(ngx_connection_t *c, ngx_quic_frame_t *frame);
369 372
376 static void ngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in); 379 static void ngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in);
377 static ngx_chain_t *ngx_quic_copy_buf(ngx_connection_t *c, u_char *data, 380 static ngx_chain_t *ngx_quic_copy_buf(ngx_connection_t *c, u_char *data,
378 size_t len); 381 size_t len);
379 static ngx_chain_t *ngx_quic_copy_chain(ngx_connection_t *c, ngx_chain_t *in, 382 static ngx_chain_t *ngx_quic_copy_chain(ngx_connection_t *c, ngx_chain_t *in,
380 size_t limit); 383 size_t limit);
384 static ngx_chain_t *ngx_quic_split_bufs(ngx_connection_t *c, ngx_chain_t *in,
385 size_t len);
381 386
382 387
383 static SSL_QUIC_METHOD quic_method = { 388 static SSL_QUIC_METHOD quic_method = {
384 #if BORINGSSL_API_VERSION >= 10 389 #if BORINGSSL_API_VERSION >= 10
385 ngx_quic_set_read_secret, 390 ngx_quic_set_read_secret,
783 static int 788 static int
784 ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, 789 ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
785 enum ssl_encryption_level_t level, const uint8_t *data, size_t len) 790 enum ssl_encryption_level_t level, const uint8_t *data, size_t len)
786 { 791 {
787 u_char *p, *end; 792 u_char *p, *end;
788 size_t client_params_len, fsize, limit; 793 size_t client_params_len;
789 const uint8_t *client_params; 794 const uint8_t *client_params;
790 ngx_quic_frame_t *frame; 795 ngx_quic_frame_t *frame;
791 ngx_connection_t *c; 796 ngx_connection_t *c;
792 ngx_quic_connection_t *qc; 797 ngx_quic_connection_t *qc;
793 ngx_quic_frames_stream_t *fs; 798 ngx_quic_frames_stream_t *fs;
891 qc->streams.server_max_streams_uni = qc->ctp.initial_max_streams_uni; 896 qc->streams.server_max_streams_uni = qc->ctp.initial_max_streams_uni;
892 897
893 qc->client_tp_done = 1; 898 qc->client_tp_done = 1;
894 } 899 }
895 900
896 /*
897 * we need to fit at least 1 frame into a packet, thus account head/tail;
898 * 17 = 1 + 8x2 is max header for CRYPTO frame, with 1 byte for frame type
899 */
900 limit = qc->ctp.max_udp_payload_size - NGX_QUIC_MAX_LONG_HEADER - 17
901 - EVP_GCM_TLS_TAG_LEN;
902
903 fs = &qc->crypto[level]; 901 fs = &qc->crypto[level];
904 902
905 p = (u_char *) data; 903 frame = ngx_quic_alloc_frame(c);
906 end = (u_char *) data + len; 904 if (frame == NULL) {
907 905 return 0;
908 while (p < end) { 906 }
909 907
910 fsize = ngx_min(limit, (size_t) (end - p)); 908 frame->data = ngx_quic_copy_buf(c, (u_char *) data, len);
911 909 if (frame->data == NGX_CHAIN_ERROR) {
912 frame = ngx_quic_alloc_frame(c); 910 return 0;
913 if (frame == NULL) { 911 }
914 return 0; 912
915 } 913 frame->level = level;
916 914 frame->type = NGX_QUIC_FT_CRYPTO;
917 frame->data = ngx_quic_copy_buf(c, p, fsize); 915 frame->u.crypto.offset = fs->sent;
918 if (frame->data == NGX_CHAIN_ERROR) { 916 frame->u.crypto.length = len;
919 return 0; 917
920 } 918 fs->sent += len;
921 919
922 frame->level = level; 920 ngx_quic_queue_frame(qc, frame);
923 frame->type = NGX_QUIC_FT_CRYPTO;
924 frame->u.crypto.offset = fs->sent;
925 frame->u.crypto.length = fsize;
926
927 fs->sent += fsize;
928 p += fsize;
929
930 ngx_quic_queue_frame(qc, frame);
931 }
932 921
933 return 1; 922 return 1;
934 } 923 }
935 924
936 925
4699 4688
4700 4689
4701 static ngx_int_t 4690 static ngx_int_t
4702 ngx_quic_output(ngx_connection_t *c) 4691 ngx_quic_output(ngx_connection_t *c)
4703 { 4692 {
4704 ngx_uint_t i; 4693 off_t max;
4705 ngx_msec_t delay; 4694 size_t len, min;
4695 ssize_t n;
4696 u_char *p;
4697 ngx_uint_t i, pad;
4706 ngx_quic_send_ctx_t *ctx; 4698 ngx_quic_send_ctx_t *ctx;
4707 ngx_quic_connection_t *qc; 4699 ngx_quic_connection_t *qc;
4700 static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
4708 4701
4709 c->log->action = "sending frames"; 4702 c->log->action = "sending frames";
4710 4703
4711 qc = ngx_quic_get_connection(c); 4704 qc = ngx_quic_get_connection(c);
4712 4705
4713 for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) { 4706 for ( ;; ) {
4714 4707 p = dst;
4715 ctx = &qc->send_ctx[i]; 4708
4716 4709 len = ngx_min(qc->ctp.max_udp_payload_size,
4717 if (ctx->send_ack) { 4710 NGX_QUIC_MAX_UDP_PAYLOAD_SIZE);
4718 4711
4719 if (ctx->level == ssl_encryption_application) { 4712 if (!qc->validated) {
4720 4713 max = qc->received * 3;
4721 delay = ngx_current_msec - ctx->ack_delay_start; 4714 max = (c->sent >= max) ? 0 : max - c->sent;
4722 4715 len = ngx_min(len, (size_t) max);
4723 if (ctx->send_ack < NGX_QUIC_MAX_ACK_GAP 4716 }
4724 && delay < qc->tp.max_ack_delay) 4717
4725 { 4718 pad = ngx_quic_get_padding_level(c);
4726 if (!qc->push.timer_set && !qc->closing) { 4719
4727 ngx_add_timer(&qc->push, qc->tp.max_ack_delay - delay); 4720 for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
4728 } 4721
4729 4722 ctx = &qc->send_ctx[i];
4730 goto output; 4723
4731 } 4724 if (ngx_quic_generate_ack(c, ctx) != NGX_OK) {
4732 }
4733
4734 if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
4735 return NGX_ERROR; 4725 return NGX_ERROR;
4736 } 4726 }
4737 ctx->send_ack = 0; 4727
4738 } 4728 min = (i == pad && p - dst < NGX_QUIC_MIN_INITIAL_SIZE)
4739 4729 ? NGX_QUIC_MIN_INITIAL_SIZE - (p - dst) : 0;
4740 output: 4730
4741 4731 n = ngx_quic_output_packet(c, ctx, p, len, min);
4742 if (ngx_quic_output_frames(c, ctx) != NGX_OK) { 4732 if (n == NGX_ERROR) {
4733 return NGX_ERROR;
4734 }
4735
4736 p += n;
4737 len -= n;
4738 }
4739
4740 len = p - dst;
4741 if (len == 0) {
4742 break;
4743 }
4744
4745 n = ngx_quic_send(c, dst, len);
4746 if (n == NGX_ERROR) {
4743 return NGX_ERROR; 4747 return NGX_ERROR;
4744 } 4748 }
4745 } 4749
4746 4750 if (!qc->send_timer_set && !qc->closing) {
4747 if (!qc->send_timer_set && !qc->closing) { 4751 qc->send_timer_set = 1;
4748 qc->send_timer_set = 1; 4752 ngx_add_timer(c->read, qc->tp.max_idle_timeout);
4749 ngx_add_timer(c->read, qc->tp.max_idle_timeout); 4753 }
4750 } 4754 }
4751 4755
4752 return NGX_OK; 4756 return NGX_OK;
4753 } 4757 }
4754 4758
4755 4759
4756 static ngx_int_t 4760 static ngx_uint_t
4757 ngx_quic_output_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx) 4761 ngx_quic_get_padding_level(ngx_connection_t *c)
4758 { 4762 {
4759 size_t len, hlen, cutoff; 4763 ngx_queue_t *q;
4760 ngx_uint_t need_ack;
4761 ngx_queue_t *q, range;
4762 ngx_quic_frame_t *f; 4764 ngx_quic_frame_t *f;
4763 ngx_quic_congestion_t *cg; 4765 ngx_quic_send_ctx_t *ctx;
4764 ngx_quic_connection_t *qc; 4766 ngx_quic_connection_t *qc;
4765 4767
4768 /*
4769 * 14.1. Initial Datagram Size
4770 *
4771 * Similarly, a server MUST expand the payload of all UDP datagrams
4772 * carrying ack-eliciting Initial packets to at least the smallest
4773 * allowed maximum datagram size of 1200 bytes
4774 */
4775
4766 qc = ngx_quic_get_connection(c); 4776 qc = ngx_quic_get_connection(c);
4767 cg = &qc->congestion; 4777 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_initial);
4768 4778
4769 if (ngx_queue_empty(&ctx->frames)) { 4779 for (q = ngx_queue_head(&ctx->frames);
4770 return NGX_OK; 4780 q != ngx_queue_sentinel(&ctx->frames);
4771 }
4772
4773 q = ngx_queue_head(&ctx->frames);
4774 f = ngx_queue_data(q, ngx_quic_frame_t, queue);
4775
4776 /* all frames in same send_ctx share same level */
4777 hlen = (f->level == ssl_encryption_application) ? NGX_QUIC_MAX_SHORT_HEADER
4778 : NGX_QUIC_MAX_LONG_HEADER;
4779 hlen += EVP_GCM_TLS_TAG_LEN;
4780 hlen -= NGX_QUIC_MAX_CID_LEN - qc->scid.len;
4781
4782 do {
4783 len = 0;
4784 need_ack = 0;
4785 ngx_queue_init(&range);
4786
4787 do {
4788 /* process group of frames that fits into packet */
4789 f = ngx_queue_data(q, ngx_quic_frame_t, queue);
4790
4791 if (len && hlen + len + f->len > qc->ctp.max_udp_payload_size) {
4792 break;
4793 }
4794
4795 if (f->need_ack) {
4796 need_ack = 1;
4797 }
4798
4799 if (need_ack && cg->in_flight + len + f->len > cg->window) {
4800 break;
4801 }
4802
4803 if (!qc->validated) {
4804 /*
4805 * Prior to validation, endpoints are limited in what they
4806 * are able to send. During the handshake, a server cannot
4807 * send more than three times the data it receives;
4808 */
4809
4810 if (f->level == ssl_encryption_initial) {
4811 cutoff = (c->sent + NGX_QUIC_MIN_INITIAL_SIZE) / 3;
4812
4813 } else {
4814 cutoff = (c->sent + hlen + len + f->len) / 3;
4815 }
4816
4817 if (cutoff > qc->received) {
4818 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
4819 "quic hit amplification limit"
4820 " received:%uz sent:%O",
4821 qc->received, c->sent);
4822 break;
4823 }
4824 }
4825
4826 q = ngx_queue_next(q);
4827
4828 f->first = ngx_current_msec;
4829
4830 ngx_queue_remove(&f->queue);
4831 ngx_queue_insert_tail(&range, &f->queue);
4832
4833 len += f->len;
4834
4835 } while (q != ngx_queue_sentinel(&ctx->frames));
4836
4837 if (ngx_queue_empty(&range)) {
4838 break;
4839 }
4840
4841 if (ngx_quic_send_frames(c, ctx, &range) != NGX_OK) {
4842 return NGX_ERROR;
4843 }
4844
4845 } while (q != ngx_queue_sentinel(&ctx->frames));
4846
4847 return NGX_OK;
4848 }
4849
4850
4851 static void
4852 ngx_quic_free_frames(ngx_connection_t *c, ngx_queue_t *frames)
4853 {
4854 ngx_queue_t *q;
4855 ngx_quic_frame_t *f;
4856
4857 do {
4858 q = ngx_queue_head(frames);
4859
4860 if (q == ngx_queue_sentinel(frames)) {
4861 break;
4862 }
4863
4864 ngx_queue_remove(q);
4865
4866 f = ngx_queue_data(q, ngx_quic_frame_t, queue);
4867
4868 ngx_quic_free_frame(c, f);
4869 } while (1);
4870 }
4871
4872
4873 static ngx_int_t
4874 ngx_quic_send_frames(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
4875 ngx_queue_t *frames)
4876 {
4877 u_char *p;
4878 size_t pad_len;
4879 ssize_t len;
4880 ngx_str_t out, res;
4881 ngx_msec_t now;
4882 ngx_queue_t *q;
4883 ngx_quic_frame_t *f, *start;
4884 ngx_quic_header_t pkt;
4885 ngx_quic_connection_t *qc;
4886 static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
4887 static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
4888
4889 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
4890 "quic ngx_quic_send_frames");
4891
4892 q = ngx_queue_head(frames);
4893 start = ngx_queue_data(q, ngx_quic_frame_t, queue);
4894
4895 ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
4896
4897 now = ngx_current_msec;
4898
4899 p = src;
4900 out.data = src;
4901
4902 for (q = ngx_queue_head(frames);
4903 q != ngx_queue_sentinel(frames);
4904 q = ngx_queue_next(q)) 4781 q = ngx_queue_next(q))
4905 { 4782 {
4906 f = ngx_queue_data(q, ngx_quic_frame_t, queue); 4783 f = ngx_queue_data(q, ngx_quic_frame_t, queue);
4907 4784
4908 ngx_quic_log_frame(c->log, f, 1); 4785 if (f->need_ack) {
4909 4786 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_handshake);
4910 len = ngx_quic_create_frame(p, f); 4787
4911 if (len == -1) { 4788 if (ngx_queue_empty(&ctx->frames)) {
4912 ngx_quic_free_frames(c, frames); 4789 return 0;
4913 return NGX_ERROR; 4790 }
4791
4792 return 1;
4793 }
4794 }
4795
4796 return NGX_QUIC_SEND_CTX_LAST;
4797 }
4798
4799
4800 static ngx_int_t
4801 ngx_quic_generate_ack(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx)
4802 {
4803 ngx_msec_t delay;
4804 ngx_quic_connection_t *qc;
4805
4806 if (!ctx->send_ack) {
4807 return NGX_OK;
4808 }
4809
4810 if (ctx->level == ssl_encryption_application) {
4811
4812 delay = ngx_current_msec - ctx->ack_delay_start;
4813 qc = ngx_quic_get_connection(c);
4814
4815 if (ctx->send_ack < NGX_QUIC_MAX_ACK_GAP
4816 && delay < qc->tp.max_ack_delay)
4817 {
4818 if (!qc->push.timer_set && !qc->closing) {
4819 ngx_add_timer(&qc->push,
4820 qc->tp.max_ack_delay - delay);
4821 }
4822
4823 return NGX_OK;
4824 }
4825 }
4826
4827 if (ngx_quic_send_ack(c, ctx) != NGX_OK) {
4828 return NGX_ERROR;
4829 }
4830
4831 ctx->send_ack = 0;
4832
4833 return NGX_OK;
4834 }
4835
4836
4837 static ssize_t
4838 ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx,
4839 u_char *data, size_t max, size_t min)
4840 {
4841 size_t len, hlen, pad_len;
4842 u_char *p;
4843 ssize_t flen;
4844 ngx_str_t out, res;
4845 ngx_int_t rc;
4846 ngx_uint_t nframes;
4847 ngx_msec_t now;
4848 ngx_queue_t *q;
4849 ngx_quic_frame_t *f;
4850 ngx_quic_header_t pkt;
4851 ngx_quic_congestion_t *cg;
4852 ngx_quic_connection_t *qc;
4853 static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE];
4854
4855 if (ngx_queue_empty(&ctx->frames)) {
4856 return 0;
4857 }
4858
4859 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
4860 "quic output %s packet max:%uz min:%uz",
4861 ngx_quic_level_name(ctx->level), max, min);
4862
4863 qc = ngx_quic_get_connection(c);
4864 cg = &qc->congestion;
4865
4866 hlen = (ctx->level == ssl_encryption_application)
4867 ? NGX_QUIC_MAX_SHORT_HEADER
4868 : NGX_QUIC_MAX_LONG_HEADER;
4869
4870 hlen += EVP_GCM_TLS_TAG_LEN;
4871 hlen -= NGX_QUIC_MAX_CID_LEN - qc->scid.len;
4872
4873 ngx_memzero(&pkt, sizeof(ngx_quic_header_t));
4874
4875 now = ngx_current_msec;
4876 nframes = 0;
4877 p = src;
4878 len = 0;
4879
4880 for (q = ngx_queue_head(&ctx->frames);
4881 q != ngx_queue_sentinel(&ctx->frames);
4882 q = ngx_queue_next(q))
4883 {
4884 f = ngx_queue_data(q, ngx_quic_frame_t, queue);
4885
4886 if (!pkt.need_ack && f->need_ack && max > cg->window) {
4887 max = cg->window;
4888 }
4889
4890 if (hlen + len >= max) {
4891 break;
4892 }
4893
4894 if (hlen + len + f->len > max) {
4895 rc = ngx_quic_split_frame(c, f, max - hlen - len);
4896
4897 if (rc == NGX_ERROR) {
4898 return NGX_ERROR;
4899 }
4900
4901 if (rc == NGX_DECLINED) {
4902 break;
4903 }
4914 } 4904 }
4915 4905
4916 if (f->need_ack) { 4906 if (f->need_ack) {
4917 pkt.need_ack = 1; 4907 pkt.need_ack = 1;
4918 } 4908 }
4919 4909
4920 p += len; 4910 ngx_quic_log_frame(c->log, f, 1);
4911
4912 flen = ngx_quic_create_frame(p, f);
4913 if (flen == -1) {
4914 return NGX_ERROR;
4915 }
4916
4917 len += flen;
4918 p += flen;
4919
4921 f->pnum = ctx->pnum; 4920 f->pnum = ctx->pnum;
4921 f->first = now;
4922 f->last = now; 4922 f->last = now;
4923 f->plen = 0; 4923 f->plen = 0;
4924 } 4924
4925 4925 nframes++;
4926 out.len = p - out.data; 4926 }
4927 4927
4928 qc = ngx_quic_get_connection(c); 4928 if (nframes == 0) {
4929 return 0;
4930 }
4931
4932 out.data = src;
4933 out.len = len;
4929 4934
4930 pkt.keys = qc->keys; 4935 pkt.keys = qc->keys;
4931
4932 pkt.flags = NGX_QUIC_PKT_FIXED_BIT; 4936 pkt.flags = NGX_QUIC_PKT_FIXED_BIT;
4933 4937
4934 if (start->level == ssl_encryption_initial) { 4938 if (ctx->level == ssl_encryption_initial) {
4935 pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL; 4939 pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_INITIAL;
4936 4940
4937 } else if (start->level == ssl_encryption_handshake) { 4941 } else if (ctx->level == ssl_encryption_handshake) {
4938 pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE; 4942 pkt.flags |= NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_HANDSHAKE;
4939 4943
4940 } else { 4944 } else {
4941 if (qc->key_phase) { 4945 if (qc->key_phase) {
4942 pkt.flags |= NGX_QUIC_PKT_KPHASE; 4946 pkt.flags |= NGX_QUIC_PKT_KPHASE;
4945 4949
4946 ngx_quic_set_packet_number(&pkt, ctx); 4950 ngx_quic_set_packet_number(&pkt, ctx);
4947 4951
4948 pkt.version = qc->version; 4952 pkt.version = qc->version;
4949 pkt.log = c->log; 4953 pkt.log = c->log;
4950 pkt.level = start->level; 4954 pkt.level = ctx->level;
4951 pkt.dcid = qc->scid; 4955 pkt.dcid = qc->scid;
4952 pkt.scid = qc->dcid; 4956 pkt.scid = qc->dcid;
4953 4957
4954 if (start->level == ssl_encryption_initial && pkt.need_ack) { 4958 pad_len = 4;
4955 pad_len = NGX_QUIC_MIN_INITIAL_SIZE - EVP_GCM_TLS_TAG_LEN 4959
4956 - ngx_quic_create_header(&pkt, NULL, out.len, NULL); 4960 if (min) {
4957 pad_len = ngx_min(pad_len, NGX_QUIC_MIN_INITIAL_SIZE); 4961 hlen = EVP_GCM_TLS_TAG_LEN
4958 4962 + ngx_quic_create_header(&pkt, NULL, out.len, NULL);
4959 } else { 4963
4960 pad_len = 4; 4964 if (min > hlen + pad_len) {
4965 pad_len = min - hlen;
4966 }
4961 } 4967 }
4962 4968
4963 if (out.len < pad_len) { 4969 if (out.len < pad_len) {
4964 ngx_memset(p, NGX_QUIC_FT_PADDING, pad_len - out.len); 4970 ngx_memset(p, NGX_QUIC_FT_PADDING, pad_len - out.len);
4965 out.len = pad_len; 4971 out.len = pad_len;
4966 } 4972 }
4967 4973
4968 pkt.payload = out; 4974 pkt.payload = out;
4969 4975
4970 res.data = dst; 4976 res.data = data;
4971 4977
4972 ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0, 4978 ngx_log_debug6(NGX_LOG_DEBUG_EVENT, c->log, 0,
4973 "quic packet tx %s bytes:%ui" 4979 "quic packet tx %s bytes:%ui"
4974 " need_ack:%d number:%L encoded nl:%d trunc:0x%xD", 4980 " need_ack:%d number:%L encoded nl:%d trunc:0x%xD",
4975 ngx_quic_level_name(start->level), out.len, pkt.need_ack, 4981 ngx_quic_level_name(ctx->level), out.len, pkt.need_ack,
4976 pkt.number, pkt.num_len, pkt.trunc); 4982 pkt.number, pkt.num_len, pkt.trunc);
4977 4983
4978 if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) { 4984 if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) {
4979 ngx_quic_free_frames(c, frames);
4980 return NGX_ERROR; 4985 return NGX_ERROR;
4981 } 4986 }
4982 4987
4983 len = ngx_quic_send(c, res.data, res.len);
4984 if (len == NGX_ERROR) {
4985 ngx_quic_free_frames(c, frames);
4986 return NGX_ERROR;
4987 }
4988
4989 /* len == NGX_OK || NGX_AGAIN */
4990 ctx->pnum++; 4988 ctx->pnum++;
4991 4989
4992 if (pkt.need_ack) { 4990 if (pkt.need_ack) {
4993 /* move frames into the sent queue to wait for ack */ 4991 /* move frames into the sent queue to wait for ack */
4994 4992
4995 if (qc->closing) { 4993 if (!qc->closing) {
4996 /* if we are closing, any ack will be discarded */ 4994 q = ngx_queue_head(&ctx->frames);
4997 ngx_quic_free_frames(c, frames); 4995 f = ngx_queue_data(q, ngx_quic_frame_t, queue);
4998 4996 f->plen = res.len;
4999 } else { 4997
5000 ngx_queue_add(&ctx->sent, frames); 4998 do {
4999 q = ngx_queue_head(&ctx->frames);
5000 ngx_queue_remove(q);
5001 ngx_queue_insert_tail(&ctx->sent, q);
5002 } while (--nframes);
5003
5001 if (qc->pto.timer_set) { 5004 if (qc->pto.timer_set) {
5002 ngx_del_timer(&qc->pto); 5005 ngx_del_timer(&qc->pto);
5003 } 5006 }
5007
5004 ngx_add_timer(&qc->pto, ngx_quic_pto(c, ctx)); 5008 ngx_add_timer(&qc->pto, ngx_quic_pto(c, ctx));
5005 5009 }
5006 start->plen = len; 5010
5007 } 5011 cg->in_flight += res.len;
5008
5009 qc->congestion.in_flight += len;
5010 5012
5011 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, 5013 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
5012 "quic congestion send if:%uz", 5014 "quic congestion send if:%uz", cg->in_flight);
5013 qc->congestion.in_flight); 5015 }
5014 } else { 5016
5015 /* no ack is expected for this frames, so we can free them */ 5017 while (nframes--) {
5016 ngx_quic_free_frames(c, frames); 5018 q = ngx_queue_head(&ctx->frames);
5017 } 5019 f = ngx_queue_data(q, ngx_quic_frame_t, queue);
5020
5021 ngx_queue_remove(q);
5022 ngx_quic_free_frame(c, f);
5023 }
5024
5025 return res.len;
5026 }
5027
5028
5029 static ngx_int_t
5030 ngx_quic_split_frame(ngx_connection_t *c, ngx_quic_frame_t *f, size_t len)
5031 {
5032 size_t shrink;
5033 ngx_quic_frame_t *nf;
5034 ngx_quic_ordered_frame_t *of, *onf;
5035
5036 switch (f->type) {
5037 case NGX_QUIC_FT_CRYPTO:
5038 case NGX_QUIC_FT_STREAM0:
5039 case NGX_QUIC_FT_STREAM1:
5040 case NGX_QUIC_FT_STREAM2:
5041 case NGX_QUIC_FT_STREAM3:
5042 case NGX_QUIC_FT_STREAM4:
5043 case NGX_QUIC_FT_STREAM5:
5044 case NGX_QUIC_FT_STREAM6:
5045 case NGX_QUIC_FT_STREAM7:
5046 break;
5047
5048 default:
5049 return NGX_DECLINED;
5050 }
5051
5052 if ((size_t) f->len <= len) {
5053 return NGX_OK;
5054 }
5055
5056 shrink = f->len - len;
5057
5058 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
5059 "quic split frame now:%uz need:%uz shrink:%uz",
5060 f->len, len, shrink);
5061
5062 of = &f->u.ord;
5063
5064 if (of->length <= shrink) {
5065 return NGX_DECLINED;
5066 }
5067
5068 of->length -= shrink;
5069 f->len = ngx_quic_create_frame(NULL, f);
5070
5071 if ((size_t) f->len > len) {
5072 ngx_log_error(NGX_LOG_ERR, c->log, 0, "could not split QUIC frame");
5073 return NGX_ERROR;
5074 }
5075
5076 nf = ngx_quic_alloc_frame(c);
5077 if (nf == NULL) {
5078 return NGX_ERROR;
5079 }
5080
5081 *nf = *f;
5082 onf = &nf->u.ord;
5083 onf->offset += of->length;
5084 onf->length = shrink;
5085 nf->len = ngx_quic_create_frame(NULL, nf);
5086
5087 nf->data = ngx_quic_split_bufs(c, f->data, of->length);
5088 if (nf->data == NGX_CHAIN_ERROR) {
5089 return NGX_ERROR;
5090 }
5091
5092 ngx_queue_insert_after(&f->queue, &nf->queue);
5018 5093
5019 return NGX_OK; 5094 return NGX_OK;
5095 }
5096
5097
5098 static void
5099 ngx_quic_free_frames(ngx_connection_t *c, ngx_queue_t *frames)
5100 {
5101 ngx_queue_t *q;
5102 ngx_quic_frame_t *f;
5103
5104 do {
5105 q = ngx_queue_head(frames);
5106
5107 if (q == ngx_queue_sentinel(frames)) {
5108 break;
5109 }
5110
5111 ngx_queue_remove(q);
5112
5113 f = ngx_queue_data(q, ngx_quic_frame_t, queue);
5114
5115 ngx_quic_free_frame(c, f);
5116 } while (1);
5020 } 5117 }
5021 5118
5022 5119
5023 static ssize_t 5120 static ssize_t
5024 ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len) 5121 ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len)
5774 5871
5775 5872
5776 static ngx_chain_t * 5873 static ngx_chain_t *
5777 ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) 5874 ngx_quic_stream_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
5778 { 5875 {
5779 size_t n, max, max_frame, max_flow, max_limit; 5876 size_t n, flow;
5780 #if (NGX_DEBUG)
5781 size_t sent;
5782 ngx_uint_t nframes;
5783 #endif
5784 ngx_event_t *wev; 5877 ngx_event_t *wev;
5785 ngx_chain_t *cl; 5878 ngx_chain_t *cl;
5786 ngx_connection_t *pc; 5879 ngx_connection_t *pc;
5787 ngx_quic_frame_t *frame; 5880 ngx_quic_frame_t *frame;
5788 ngx_quic_stream_t *qs; 5881 ngx_quic_stream_t *qs;
5795 5888
5796 if (wev->error) { 5889 if (wev->error) {
5797 return NGX_CHAIN_ERROR; 5890 return NGX_CHAIN_ERROR;
5798 } 5891 }
5799 5892
5800 max_frame = ngx_quic_max_stream_frame(qc); 5893 flow = ngx_quic_max_stream_flow(c);
5801 max_flow = ngx_quic_max_stream_flow(c); 5894 if (flow == 0) {
5802 max_limit = limit; 5895 wev->ready = 0;
5803 5896 return in;
5804 #if (NGX_DEBUG) 5897 }
5805 sent = 0; 5898
5806 nframes = 0; 5899 n = (limit && (size_t) limit < flow) ? (size_t) limit : flow;
5807 #endif 5900
5808 5901 frame = ngx_quic_alloc_frame(pc);
5809 for ( ;; ) { 5902 if (frame == NULL) {
5810 max = ngx_min(max_frame, max_flow); 5903 return NGX_CHAIN_ERROR;
5811 5904 }
5812 if (limit) { 5905
5813 max = ngx_min(max, max_limit); 5906 frame->data = ngx_quic_copy_chain(pc, in, n);
5814 } 5907 if (frame->data == NGX_CHAIN_ERROR) {
5815 5908 return NGX_CHAIN_ERROR;
5816 for (cl = in, n = 0; in; in = in->next) { 5909 }
5817 5910
5818 if (!ngx_buf_in_memory(in->buf)) { 5911 for (n = 0, cl = frame->data; cl; cl = cl->next) {
5819 continue; 5912 n += ngx_buf_size(cl->buf);
5820 } 5913 }
5821 5914
5822 n += ngx_buf_size(in->buf); 5915 while (in && ngx_buf_size(in->buf) == 0) {
5823 5916 in = in->next;
5824 if (n > max) { 5917 }
5825 n = max; 5918
5826 break; 5919 frame->level = ssl_encryption_application;
5827 } 5920 frame->type = NGX_QUIC_FT_STREAM6; /* OFF=1 LEN=1 FIN=0 */
5828 } 5921 frame->u.stream.off = 1;
5829 5922 frame->u.stream.len = 1;
5830 if (n == 0) { 5923 frame->u.stream.fin = 0;
5831 wev->ready = (max_flow ? 1 : 0); 5924
5832 break; 5925 frame->u.stream.type = frame->type;
5833 } 5926 frame->u.stream.stream_id = qs->id;
5834 5927 frame->u.stream.offset = c->sent;
5835 frame = ngx_quic_alloc_frame(pc); 5928 frame->u.stream.length = n;
5836 if (frame == NULL) { 5929
5837 return NGX_CHAIN_ERROR; 5930 c->sent += n;
5838 } 5931 qc->streams.sent += n;
5839 5932
5840 frame->level = ssl_encryption_application; 5933 ngx_quic_queue_frame(qc, frame);
5841 frame->type = NGX_QUIC_FT_STREAM6; /* OFF=1 LEN=1 FIN=0 */ 5934
5842 frame->u.stream.off = 1; 5935 wev->ready = (n < flow) ? 1 : 0;
5843 frame->u.stream.len = 1; 5936
5844 frame->u.stream.fin = 0; 5937 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
5845 5938 "quic send_chain sent:%uz", n);
5846 frame->u.stream.type = frame->type;
5847 frame->u.stream.stream_id = qs->id;
5848 frame->u.stream.offset = c->sent;
5849 frame->u.stream.length = n;
5850
5851 c->sent += n;
5852 qc->streams.sent += n;
5853 max_flow -= n;
5854
5855 if (limit) {
5856 max_limit -= n;
5857 }
5858
5859 #if (NGX_DEBUG)
5860 sent += n;
5861 nframes++;
5862 #endif
5863
5864 frame->data = ngx_quic_copy_chain(pc, cl, n);
5865 if (frame->data == NGX_CHAIN_ERROR) {
5866 return NGX_CHAIN_ERROR;
5867 }
5868
5869 ngx_quic_queue_frame(qc, frame);
5870 }
5871
5872 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
5873 "quic send_chain sent:%uz nframes:%ui", sent, nframes);
5874 5939
5875 return in; 5940 return in;
5876 }
5877
5878
5879 static size_t
5880 ngx_quic_max_stream_frame(ngx_quic_connection_t *qc)
5881 {
5882 /*
5883 * we need to fit at least 1 frame into a packet, thus account head/tail;
5884 * 25 = 1 + 8x3 is max header for STREAM frame, with 1 byte for frame type
5885 */
5886
5887 return qc->ctp.max_udp_payload_size - NGX_QUIC_MAX_SHORT_HEADER - 25
5888 - EVP_GCM_TLS_TAG_LEN;
5889 } 5941 }
5890 5942
5891 5943
5892 static size_t 5944 static size_t
5893 ngx_quic_max_stream_flow(ngx_connection_t *c) 5945 ngx_quic_max_stream_flow(ngx_connection_t *c)
6259 6311
6260 6312
6261 static void 6313 static void
6262 ngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in) 6314 ngx_quic_free_bufs(ngx_connection_t *c, ngx_chain_t *in)
6263 { 6315 {
6316 ngx_buf_t *b, *shadow;
6264 ngx_chain_t *cl; 6317 ngx_chain_t *cl;
6265 ngx_quic_connection_t *qc; 6318 ngx_quic_connection_t *qc;
6266 6319
6267 qc = ngx_quic_get_connection(c); 6320 qc = ngx_quic_get_connection(c);
6268 6321
6272 "quic free buffer n:%ui", qc->nbufs); 6325 "quic free buffer n:%ui", qc->nbufs);
6273 #endif 6326 #endif
6274 6327
6275 cl = in; 6328 cl = in;
6276 in = in->next; 6329 in = in->next;
6330 b = cl->buf;
6331
6332 if (b->shadow) {
6333 if (!b->last_shadow) {
6334 b->recycled = 1;
6335 ngx_free_chain(c->pool, cl);
6336 continue;
6337 }
6338
6339 do {
6340 shadow = b->shadow;
6341 b->shadow = qc->free_shadow_bufs;
6342 qc->free_shadow_bufs = b;
6343 b = shadow;
6344 } while (b->recycled);
6345
6346 if (b->shadow) {
6347 b->last_shadow = 1;
6348 ngx_free_chain(c->pool, cl);
6349 continue;
6350 }
6351
6352 cl->buf = b;
6353 }
6277 6354
6278 cl->next = qc->free_bufs; 6355 cl->next = qc->free_bufs;
6279 qc->free_bufs = cl; 6356 qc->free_bufs = cl;
6280 } 6357 }
6281 } 6358 }
6371 6448
6372 *ll = NULL; 6449 *ll = NULL;
6373 6450
6374 return out; 6451 return out;
6375 } 6452 }
6453
6454
6455 static ngx_chain_t *
6456 ngx_quic_split_bufs(ngx_connection_t *c, ngx_chain_t *in, size_t len)
6457 {
6458 size_t n;
6459 ngx_buf_t *b;
6460 ngx_chain_t *out;
6461 ngx_quic_connection_t *qc;
6462
6463 qc = ngx_quic_get_connection(c);
6464
6465 while (in) {
6466 n = ngx_buf_size(in->buf);
6467
6468 if (n == len) {
6469 out = in->next;
6470 in->next = NULL;
6471 return out;
6472 }
6473
6474 if (n > len) {
6475 break;
6476 }
6477
6478 len -= n;
6479 in = in->next;
6480 }
6481
6482 if (in == NULL) {
6483 return NULL;
6484 }
6485
6486 /* split in->buf by creating shadow bufs which reference it */
6487
6488 if (in->buf->shadow == NULL) {
6489 if (qc->free_shadow_bufs) {
6490 b = qc->free_shadow_bufs;
6491 qc->free_shadow_bufs = b->shadow;
6492
6493 } else {
6494 b = ngx_alloc_buf(c->pool);
6495 if (b == NULL) {
6496 return NGX_CHAIN_ERROR;
6497 }
6498 }
6499
6500 *b = *in->buf;
6501 b->shadow = in->buf;
6502 b->last_shadow = 1;
6503 in->buf = b;
6504 }
6505
6506 out = ngx_alloc_chain_link(c->pool);
6507 if (out == NULL) {
6508 return NGX_CHAIN_ERROR;
6509 }
6510
6511 if (qc->free_shadow_bufs) {
6512 b = qc->free_shadow_bufs;
6513 qc->free_shadow_bufs = b->shadow;
6514
6515 } else {
6516 b = ngx_alloc_buf(c->pool);
6517 if (b == NULL) {
6518 ngx_free_chain(c->pool, out);
6519 return NGX_CHAIN_ERROR;
6520 }
6521 }
6522
6523 out->buf = b;
6524 out->next = in->next;
6525 in->next = NULL;
6526
6527 *b = *in->buf;
6528 b->last_shadow = 0;
6529 b->pos = b->pos + len;
6530
6531 in->buf->shadow = b;
6532 in->buf->last = in->buf->pos + len;
6533
6534 return out;
6535 }