Mercurial > hg > nginx
comparison src/event/quic/ngx_event_quic_output.c @ 8763:4117aa7fa38e quic
QUIC: connection migration.
The patch adds proper transitions between multiple networking addresses that
can be used by a single quic connection. New networking paths are validated
using PATH_CHALLENGE/PATH_RESPONSE frames.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Thu, 29 Apr 2021 15:35:02 +0300 |
parents | bc910a5ec737 |
children | 4715f3e669f1 |
comparison
equal
deleted
inserted
replaced
8762:12f18e0bca09 | 8763:4117aa7fa38e |
---|---|
33 #define NGX_QUIC_MAX_SR_PACKET 1200 | 33 #define NGX_QUIC_MAX_SR_PACKET 1200 |
34 | 34 |
35 #define NGX_QUIC_CC_MIN_INTERVAL 1000 /* 1s */ | 35 #define NGX_QUIC_CC_MIN_INTERVAL 1000 /* 1s */ |
36 | 36 |
37 | 37 |
38 static ngx_int_t ngx_quic_socket_output(ngx_connection_t *c, | |
39 ngx_quic_socket_t *qsock); | |
38 static ssize_t ngx_quic_output_packet(ngx_connection_t *c, | 40 static ssize_t ngx_quic_output_packet(ngx_connection_t *c, |
39 ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min); | 41 ngx_quic_send_ctx_t *ctx, u_char *data, size_t max, size_t min, |
42 ngx_quic_socket_t *qsock); | |
40 static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c); | 43 static ngx_uint_t ngx_quic_get_padding_level(ngx_connection_t *c); |
41 static ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len); | 44 static ssize_t ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len, |
45 struct sockaddr *sockaddr, socklen_t socklen); | |
42 static void ngx_quic_set_packet_number(ngx_quic_header_t *pkt, | 46 static void ngx_quic_set_packet_number(ngx_quic_header_t *pkt, |
43 ngx_quic_send_ctx_t *ctx); | 47 ngx_quic_send_ctx_t *ctx); |
44 | 48 |
45 | 49 |
46 size_t | 50 size_t |
58 } | 62 } |
59 | 63 |
60 | 64 |
61 ngx_int_t | 65 ngx_int_t |
62 ngx_quic_output(ngx_connection_t *c) | 66 ngx_quic_output(ngx_connection_t *c) |
67 { | |
68 ngx_quic_connection_t *qc; | |
69 | |
70 qc = ngx_quic_get_connection(c); | |
71 | |
72 if (ngx_quic_socket_output(c, qc->socket) != NGX_OK) { | |
73 return NGX_ERROR; | |
74 } | |
75 | |
76 ngx_quic_set_lost_timer(c); | |
77 | |
78 return NGX_OK; | |
79 } | |
80 | |
81 | |
82 static ngx_int_t | |
83 ngx_quic_socket_output(ngx_connection_t *c, ngx_quic_socket_t *qsock) | |
63 { | 84 { |
64 off_t max; | 85 off_t max; |
65 size_t len, min, in_flight; | 86 size_t len, min, in_flight; |
66 ssize_t n; | 87 ssize_t n; |
67 u_char *p; | 88 u_char *p; |
68 ngx_uint_t i, pad; | 89 ngx_uint_t i, pad; |
90 ngx_quic_path_t *path; | |
69 ngx_quic_send_ctx_t *ctx; | 91 ngx_quic_send_ctx_t *ctx; |
70 ngx_quic_congestion_t *cg; | 92 ngx_quic_congestion_t *cg; |
71 ngx_quic_connection_t *qc; | 93 ngx_quic_connection_t *qc; |
72 static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; | 94 static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; |
73 | 95 |
76 qc = ngx_quic_get_connection(c); | 98 qc = ngx_quic_get_connection(c); |
77 cg = &qc->congestion; | 99 cg = &qc->congestion; |
78 | 100 |
79 in_flight = cg->in_flight; | 101 in_flight = cg->in_flight; |
80 | 102 |
103 path = qsock->path; | |
104 | |
81 for ( ;; ) { | 105 for ( ;; ) { |
82 p = dst; | 106 p = dst; |
83 | 107 |
84 len = ngx_min(qc->ctp.max_udp_payload_size, | 108 len = ngx_min(qc->ctp.max_udp_payload_size, |
85 NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); | 109 NGX_QUIC_MAX_UDP_PAYLOAD_SIZE); |
86 | 110 |
87 if (!qc->validated) { | 111 if (path->state != NGX_QUIC_PATH_VALIDATED) { |
88 max = qc->received * 3; | 112 max = path->received * 3; |
89 max = (c->sent >= max) ? 0 : max - c->sent; | 113 max = (path->sent >= max) ? 0 : max - path->sent; |
114 | |
90 len = ngx_min(len, (size_t) max); | 115 len = ngx_min(len, (size_t) max); |
91 } | 116 } |
92 | 117 |
93 pad = ngx_quic_get_padding_level(c); | 118 pad = ngx_quic_get_padding_level(c); |
94 | 119 |
101 } | 126 } |
102 | 127 |
103 min = (i == pad && p - dst < NGX_QUIC_MIN_INITIAL_SIZE) | 128 min = (i == pad && p - dst < NGX_QUIC_MIN_INITIAL_SIZE) |
104 ? NGX_QUIC_MIN_INITIAL_SIZE - (p - dst) : 0; | 129 ? NGX_QUIC_MIN_INITIAL_SIZE - (p - dst) : 0; |
105 | 130 |
106 n = ngx_quic_output_packet(c, ctx, p, len, min); | 131 n = ngx_quic_output_packet(c, ctx, p, len, min, qsock); |
107 if (n == NGX_ERROR) { | 132 if (n == NGX_ERROR) { |
108 return NGX_ERROR; | 133 return NGX_ERROR; |
109 } | 134 } |
110 | 135 |
111 p += n; | 136 p += n; |
115 len = p - dst; | 140 len = p - dst; |
116 if (len == 0) { | 141 if (len == 0) { |
117 break; | 142 break; |
118 } | 143 } |
119 | 144 |
120 n = ngx_quic_send(c, dst, len); | 145 n = ngx_quic_send(c, dst, len, path->sockaddr, path->socklen); |
146 | |
121 if (n == NGX_ERROR) { | 147 if (n == NGX_ERROR) { |
122 return NGX_ERROR; | 148 return NGX_ERROR; |
123 } | 149 } |
150 | |
151 path->sent += len; | |
124 } | 152 } |
125 | 153 |
126 if (in_flight != cg->in_flight && !qc->send_timer_set && !qc->closing) { | 154 if (in_flight != cg->in_flight && !qc->send_timer_set && !qc->closing) { |
127 qc->send_timer_set = 1; | 155 qc->send_timer_set = 1; |
128 ngx_add_timer(c->read, qc->tp.max_idle_timeout); | 156 ngx_add_timer(c->read, qc->tp.max_idle_timeout); |
129 } | 157 } |
130 | 158 |
131 ngx_quic_set_lost_timer(c); | |
132 | 159 |
133 return NGX_OK; | 160 return NGX_OK; |
134 } | 161 } |
135 | 162 |
136 | 163 |
174 } | 201 } |
175 | 202 |
176 | 203 |
177 static ssize_t | 204 static ssize_t |
178 ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, | 205 ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, |
179 u_char *data, size_t max, size_t min) | 206 u_char *data, size_t max, size_t min, ngx_quic_socket_t *qsock) |
180 { | 207 { |
181 size_t len, hlen, pad_len; | 208 size_t len, hlen, pad_len; |
182 u_char *p; | 209 u_char *p; |
183 ssize_t flen; | 210 ssize_t flen; |
184 ngx_str_t out, res; | 211 ngx_str_t out, res; |
185 ngx_int_t rc; | 212 ngx_int_t rc; |
186 ngx_uint_t nframes; | 213 ngx_uint_t nframes, has_pr; |
187 ngx_msec_t now; | 214 ngx_msec_t now; |
188 ngx_queue_t *q; | 215 ngx_queue_t *q; |
189 ngx_quic_frame_t *f; | 216 ngx_quic_frame_t *f; |
190 ngx_quic_header_t pkt; | 217 ngx_quic_header_t pkt; |
191 ngx_quic_congestion_t *cg; | 218 ngx_quic_congestion_t *cg; |
194 | 221 |
195 if (ngx_queue_empty(&ctx->frames)) { | 222 if (ngx_queue_empty(&ctx->frames)) { |
196 return 0; | 223 return 0; |
197 } | 224 } |
198 | 225 |
199 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, | 226 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, |
200 "quic output %s packet max:%uz min:%uz", | 227 "quic output sock #%uL %s packet max:%uz min:%uz", |
201 ngx_quic_level_name(ctx->level), max, min); | 228 qsock->sid.seqnum, ngx_quic_level_name(ctx->level), |
229 max, min); | |
202 | 230 |
203 qc = ngx_quic_get_connection(c); | 231 qc = ngx_quic_get_connection(c); |
204 cg = &qc->congestion; | 232 cg = &qc->congestion; |
205 | 233 |
206 hlen = (ctx->level == ssl_encryption_application) | 234 hlen = (ctx->level == ssl_encryption_application) |
207 ? NGX_QUIC_MAX_SHORT_HEADER | 235 ? NGX_QUIC_MAX_SHORT_HEADER |
208 : NGX_QUIC_MAX_LONG_HEADER; | 236 : NGX_QUIC_MAX_LONG_HEADER; |
209 | 237 |
210 hlen += EVP_GCM_TLS_TAG_LEN; | 238 hlen += EVP_GCM_TLS_TAG_LEN; |
211 hlen -= NGX_QUIC_MAX_CID_LEN - qc->scid.len; | 239 hlen -= NGX_QUIC_MAX_CID_LEN - qsock->cid->len; |
212 | 240 |
213 ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); | 241 ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); |
214 | 242 |
215 now = ngx_current_msec; | 243 now = ngx_current_msec; |
216 nframes = 0; | 244 nframes = 0; |
217 p = src; | 245 p = src; |
218 len = 0; | 246 len = 0; |
247 has_pr = 0; | |
219 | 248 |
220 for (q = ngx_queue_head(&ctx->frames); | 249 for (q = ngx_queue_head(&ctx->frames); |
221 q != ngx_queue_sentinel(&ctx->frames); | 250 q != ngx_queue_sentinel(&ctx->frames); |
222 q = ngx_queue_next(q)) | 251 q = ngx_queue_next(q)) |
223 { | 252 { |
225 | 254 |
226 if (!pkt.need_ack && f->need_ack && max > cg->window) { | 255 if (!pkt.need_ack && f->need_ack && max > cg->window) { |
227 max = cg->window; | 256 max = cg->window; |
228 } | 257 } |
229 | 258 |
259 if (f->type == NGX_QUIC_FT_PATH_RESPONSE | |
260 || f->type == NGX_QUIC_FT_PATH_CHALLENGE) | |
261 { | |
262 has_pr = 1; | |
263 } | |
264 | |
230 if (hlen + len >= max) { | 265 if (hlen + len >= max) { |
231 break; | 266 break; |
232 } | 267 } |
233 | 268 |
234 if (hlen + len + f->len > max) { | 269 if (hlen + len + f->len > max) { |
294 ngx_quic_set_packet_number(&pkt, ctx); | 329 ngx_quic_set_packet_number(&pkt, ctx); |
295 | 330 |
296 pkt.version = qc->version; | 331 pkt.version = qc->version; |
297 pkt.log = c->log; | 332 pkt.log = c->log; |
298 pkt.level = ctx->level; | 333 pkt.level = ctx->level; |
299 pkt.dcid = qc->scid; | 334 |
300 pkt.scid = qc->dcid; | 335 pkt.dcid.data = qsock->cid->id; |
336 pkt.dcid.len = qsock->cid->len; | |
337 | |
338 pkt.scid.data = qsock->sid.id; | |
339 pkt.scid.len = qsock->sid.len; | |
301 | 340 |
302 pad_len = 4; | 341 pad_len = 4; |
303 | 342 |
304 if (min) { | 343 if (min || has_pr) { |
305 hlen = EVP_GCM_TLS_TAG_LEN | 344 hlen = EVP_GCM_TLS_TAG_LEN |
306 + ngx_quic_create_header(&pkt, NULL, out.len, NULL); | 345 + ngx_quic_create_header(&pkt, NULL, out.len, NULL); |
346 | |
347 /* | |
348 * An endpoint MUST expand datagrams that contain a | |
349 * PATH_CHALLENGE frame to at least the smallest allowed | |
350 * maximum datagram size of 1200 bytes, unless the | |
351 * anti-amplification limit for the path does not permit | |
352 * sending a datagram of this size. | |
353 * | |
354 * (same applies to PATH_RESPONSE frames) | |
355 */ | |
356 | |
357 if (has_pr) { | |
358 min = ngx_max(1200, min); | |
359 } | |
307 | 360 |
308 if (min > hlen + pad_len) { | 361 if (min > hlen + pad_len) { |
309 pad_len = min - hlen; | 362 pad_len = min - hlen; |
310 } | 363 } |
311 } | 364 } |
362 | 415 |
363 return res.len; | 416 return res.len; |
364 } | 417 } |
365 | 418 |
366 | 419 |
367 ssize_t | 420 static ssize_t |
368 ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len) | 421 ngx_quic_send(ngx_connection_t *c, u_char *buf, size_t len, |
369 { | 422 struct sockaddr *sockaddr, socklen_t socklen) |
370 ngx_buf_t b; | 423 { |
371 ngx_chain_t cl, *res; | 424 ngx_buf_t b; |
425 socklen_t orig_socklen; | |
426 ngx_chain_t cl, *res; | |
427 struct sockaddr *orig_sockaddr; | |
372 | 428 |
373 ngx_memzero(&b, sizeof(ngx_buf_t)); | 429 ngx_memzero(&b, sizeof(ngx_buf_t)); |
374 | 430 |
375 b.pos = b.start = buf; | 431 b.pos = b.start = buf; |
376 b.last = b.end = buf + len; | 432 b.last = b.end = buf + len; |
378 b.temporary = 1; | 434 b.temporary = 1; |
379 | 435 |
380 cl.buf = &b; | 436 cl.buf = &b; |
381 cl.next= NULL; | 437 cl.next= NULL; |
382 | 438 |
439 orig_socklen = c->socklen; | |
440 orig_sockaddr = c->sockaddr; | |
441 | |
442 c->sockaddr = sockaddr; | |
443 c->socklen = socklen; | |
444 | |
383 res = c->send_chain(c, &cl, 0); | 445 res = c->send_chain(c, &cl, 0); |
446 | |
447 c->sockaddr = orig_sockaddr; | |
448 c->socklen = orig_socklen; | |
449 | |
384 if (res == NGX_CHAIN_ERROR) { | 450 if (res == NGX_CHAIN_ERROR) { |
385 return NGX_ERROR; | 451 return NGX_ERROR; |
386 } | 452 } |
387 | 453 |
388 return len; | 454 return len; |
439 #ifdef NGX_QUIC_DEBUG_PACKETS | 505 #ifdef NGX_QUIC_DEBUG_PACKETS |
440 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, | 506 ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, |
441 "quic vnego packet to send len:%uz %*xs", len, len, buf); | 507 "quic vnego packet to send len:%uz %*xs", len, len, buf); |
442 #endif | 508 #endif |
443 | 509 |
444 (void) ngx_quic_send(c, buf, len); | 510 (void) ngx_quic_send(c, buf, len, c->sockaddr, c->socklen); |
445 | 511 |
446 return NGX_ERROR; | 512 return NGX_ERROR; |
447 } | 513 } |
448 | 514 |
449 | 515 |
522 != NGX_OK) | 588 != NGX_OK) |
523 { | 589 { |
524 return NGX_ERROR; | 590 return NGX_ERROR; |
525 } | 591 } |
526 | 592 |
527 (void) ngx_quic_send(c, buf, len); | 593 (void) ngx_quic_send(c, buf, len, c->sockaddr, c->socklen); |
528 | 594 |
529 return NGX_DECLINED; | 595 return NGX_DECLINED; |
530 } | 596 } |
531 | 597 |
532 | 598 |
640 | 706 |
641 if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) { | 707 if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) { |
642 return NGX_ERROR; | 708 return NGX_ERROR; |
643 } | 709 } |
644 | 710 |
645 if (ngx_quic_send(c, res.data, res.len) == NGX_ERROR) { | 711 if (ngx_quic_send(c, res.data, res.len, c->sockaddr, c->socklen) |
712 == NGX_ERROR) | |
713 { | |
646 return NGX_ERROR; | 714 return NGX_ERROR; |
647 } | 715 } |
648 | 716 |
649 return NGX_OK; | 717 return NGX_OK; |
650 } | 718 } |
662 u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE]; | 730 u_char buf[NGX_QUIC_RETRY_BUFFER_SIZE]; |
663 u_char dcid[NGX_QUIC_SERVER_CID_LEN]; | 731 u_char dcid[NGX_QUIC_SERVER_CID_LEN]; |
664 | 732 |
665 expires = ngx_time() + NGX_QUIC_RETRY_TOKEN_LIFETIME; | 733 expires = ngx_time() + NGX_QUIC_RETRY_TOKEN_LIFETIME; |
666 | 734 |
667 if (ngx_quic_new_token(c, conf->av_token_key, &token, &inpkt->dcid, | 735 if (ngx_quic_new_token(c, c->sockaddr, c->socklen, conf->av_token_key, |
668 expires, 1) | 736 &token, &inpkt->dcid, expires, 1) |
669 != NGX_OK) | 737 != NGX_OK) |
670 { | 738 { |
671 return NGX_ERROR; | 739 return NGX_ERROR; |
672 } | 740 } |
673 | 741 |
698 #ifdef NGX_QUIC_DEBUG_PACKETS | 766 #ifdef NGX_QUIC_DEBUG_PACKETS |
699 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | 767 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, |
700 "quic packet to send len:%uz %xV", res.len, &res); | 768 "quic packet to send len:%uz %xV", res.len, &res); |
701 #endif | 769 #endif |
702 | 770 |
703 len = ngx_quic_send(c, res.data, res.len); | 771 len = ngx_quic_send(c, res.data, res.len, c->sockaddr, c->socklen); |
704 if (len == NGX_ERROR) { | 772 if (len == NGX_ERROR) { |
705 return NGX_ERROR; | 773 return NGX_ERROR; |
706 } | 774 } |
707 | 775 |
708 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, | 776 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, |
716 return NGX_DONE; | 784 return NGX_DONE; |
717 } | 785 } |
718 | 786 |
719 | 787 |
720 ngx_int_t | 788 ngx_int_t |
721 ngx_quic_send_new_token(ngx_connection_t *c) | 789 ngx_quic_send_new_token(ngx_connection_t *c, ngx_quic_path_t *path) |
722 { | 790 { |
723 time_t expires; | 791 time_t expires; |
724 ngx_str_t token; | 792 ngx_str_t token; |
725 ngx_quic_frame_t *frame; | 793 ngx_quic_frame_t *frame; |
726 ngx_quic_connection_t *qc; | 794 ngx_quic_connection_t *qc; |
727 | 795 |
728 qc = ngx_quic_get_connection(c); | 796 qc = ngx_quic_get_connection(c); |
729 | 797 |
730 if (!qc->conf->retry) { | |
731 return NGX_OK; | |
732 } | |
733 | |
734 expires = ngx_time() + NGX_QUIC_NEW_TOKEN_LIFETIME; | 798 expires = ngx_time() + NGX_QUIC_NEW_TOKEN_LIFETIME; |
735 | 799 |
736 if (ngx_quic_new_token(c, qc->conf->av_token_key, &token, NULL, expires, 0) | 800 if (ngx_quic_new_token(c, path->sockaddr, path->socklen, |
801 qc->conf->av_token_key, &token, NULL, expires, 0) | |
737 != NGX_OK) | 802 != NGX_OK) |
738 { | 803 { |
739 return NGX_ERROR; | 804 return NGX_ERROR; |
740 } | 805 } |
741 | 806 |
847 | 912 |
848 ngx_quic_queue_frame(qc, frame); | 913 ngx_quic_queue_frame(qc, frame); |
849 | 914 |
850 return NGX_OK; | 915 return NGX_OK; |
851 } | 916 } |
917 | |
918 | |
919 ssize_t | |
920 ngx_quic_frame_sendto(ngx_connection_t *c, ngx_quic_frame_t *frame, | |
921 size_t min, struct sockaddr *sockaddr, socklen_t socklen) | |
922 { | |
923 ssize_t len; | |
924 ngx_str_t res; | |
925 ngx_quic_header_t pkt; | |
926 ngx_quic_send_ctx_t *ctx; | |
927 ngx_quic_connection_t *qc; | |
928 | |
929 static u_char src[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; | |
930 static u_char dst[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; | |
931 | |
932 qc = ngx_quic_get_connection(c); | |
933 | |
934 ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); | |
935 | |
936 len = ngx_quic_create_frame(NULL, frame); | |
937 if (len > NGX_QUIC_MAX_UDP_PAYLOAD_SIZE) { | |
938 return -1; | |
939 } | |
940 | |
941 ngx_quic_log_frame(c->log, frame, 1); | |
942 | |
943 len = ngx_quic_create_frame(src, frame); | |
944 if (len == -1) { | |
945 return -1; | |
946 } | |
947 | |
948 if (len < (ssize_t) min) { | |
949 ngx_memset(src + len, NGX_QUIC_FT_PADDING, min - len); | |
950 len = min; | |
951 } | |
952 | |
953 pkt.keys = qc->keys; | |
954 pkt.flags = NGX_QUIC_PKT_FIXED_BIT; | |
955 | |
956 if (qc->key_phase) { | |
957 pkt.flags |= NGX_QUIC_PKT_KPHASE; | |
958 } | |
959 | |
960 ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); | |
961 | |
962 ngx_quic_set_packet_number(&pkt, ctx); | |
963 | |
964 pkt.version = qc->version; | |
965 pkt.log = c->log; | |
966 pkt.level = ctx->level; | |
967 | |
968 pkt.dcid.data = qc->socket->cid->id; | |
969 pkt.dcid.len = qc->socket->cid->len; | |
970 | |
971 pkt.scid.data = qc->socket->sid.id; | |
972 pkt.scid.len = qc->socket->sid.len; | |
973 | |
974 pkt.payload.data = src; | |
975 pkt.payload.len = len; | |
976 | |
977 res.data = dst; | |
978 | |
979 if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) { | |
980 return -1; | |
981 } | |
982 | |
983 ctx->pnum++; | |
984 | |
985 len = ngx_quic_send(c, res.data, res.len, sockaddr, socklen); | |
986 | |
987 return len; | |
988 } |