Mercurial > hg > nginx
comparison src/event/ngx_event_quic.c @ 8186:0a2683df5f11 quic
Implemented improved version of quic_output().
Now handshake generates frames, and they are queued in c->quic->frames.
The ngx_quic_output() is called from ngx_quic_flush_flight() or manually,
processes the queue and encrypts all frames according to required encryption
level.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Wed, 04 Mar 2020 15:52:12 +0300 |
parents | 6a76d9657772 |
children | de5917df2c30 |
comparison
equal
deleted
inserted
replaced
8185:6a76d9657772 | 8186:0a2683df5f11 |
---|---|
122 ngx_str_t iv; | 122 ngx_str_t iv; |
123 ngx_str_t hp; | 123 ngx_str_t hp; |
124 } ngx_quic_secret_t; | 124 } ngx_quic_secret_t; |
125 | 125 |
126 | 126 |
127 typedef enum ssl_encryption_level_t ngx_quic_level_t; | |
128 | |
129 typedef struct ngx_quic_frame_s ngx_quic_frame_t; | |
130 | |
131 typedef struct { | |
132 ngx_uint_t pn; | |
133 // ngx_uint_t nranges; | |
134 // ... | |
135 } ngx_quic_ack_frame_t; | |
136 | |
137 typedef ngx_str_t ngx_quic_crypto_frame_t; | |
138 | |
139 struct ngx_quic_frame_s { | |
140 ngx_uint_t type; | |
141 ngx_quic_level_t level; | |
142 ngx_quic_frame_t *next; | |
143 union { | |
144 ngx_quic_crypto_frame_t crypto; | |
145 ngx_quic_ack_frame_t ack; | |
146 // more frames | |
147 } u; | |
148 | |
149 u_char info[128]; // for debug purposes | |
150 }; | |
151 | |
152 | |
127 struct ngx_quic_connection_s { | 153 struct ngx_quic_connection_s { |
128 | 154 |
129 ngx_quic_state_t state; | 155 ngx_quic_state_t state; |
130 ngx_ssl_t *ssl; | 156 ngx_ssl_t *ssl; |
131 | 157 |
132 ngx_str_t out; // stub for some kind of output queue | 158 ngx_quic_frame_t *frames; |
133 | 159 |
134 ngx_str_t scid; | 160 ngx_str_t scid; |
135 ngx_str_t dcid; | 161 ngx_str_t dcid; |
136 ngx_str_t token; | 162 ngx_str_t token; |
137 | 163 |
146 ngx_quic_secret_t server_in; | 172 ngx_quic_secret_t server_in; |
147 ngx_quic_secret_t server_hs; | 173 ngx_quic_secret_t server_hs; |
148 ngx_quic_secret_t server_ad; | 174 ngx_quic_secret_t server_ad; |
149 }; | 175 }; |
150 | 176 |
151 | |
152 typedef enum ssl_encryption_level_t ngx_quic_level_t; | |
153 | 177 |
154 typedef struct { | 178 typedef struct { |
155 ngx_quic_secret_t *secret; | 179 ngx_quic_secret_t *secret; |
156 ngx_uint_t type; | 180 ngx_uint_t type; |
157 ngx_uint_t *number; | 181 ngx_uint_t *number; |
238 } | 262 } |
239 | 263 |
240 return NGX_OK; | 264 return NGX_OK; |
241 } | 265 } |
242 | 266 |
267 static ngx_int_t | |
268 ngx_quic_send_packet(ngx_connection_t *c, ngx_quic_connection_t *qc, | |
269 ngx_quic_level_t level, ngx_str_t *payload) | |
270 { | |
271 ngx_str_t res; | |
272 ngx_quic_header_t pkt; | |
273 | |
274 static ngx_str_t initial_token = ngx_null_string; | |
275 | |
276 ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); | |
277 ngx_quic_hexdump0(c->log, "payload", payload->data, payload->len); | |
278 | |
279 pkt.level = level; | |
280 | |
281 if (level == ssl_encryption_initial) { | |
282 pkt.number = &qc->initial_pn; | |
283 pkt.flags = NGX_QUIC_PKT_INITIAL; | |
284 pkt.secret = &qc->server_in; | |
285 pkt.token = &initial_token; | |
286 | |
287 if (ngx_quic_create_long_packet(c, c->ssl->connection, | |
288 &pkt, payload, &res) | |
289 != NGX_OK) | |
290 { | |
291 return NGX_ERROR; | |
292 } | |
293 | |
294 } else if (level == ssl_encryption_handshake) { | |
295 pkt.number = &qc->handshake_pn; | |
296 pkt.flags = NGX_QUIC_PKT_HANDSHAKE; | |
297 pkt.secret = &qc->server_hs; | |
298 | |
299 if (ngx_quic_create_long_packet(c, c->ssl->connection, | |
300 &pkt, payload, &res) | |
301 != NGX_OK) | |
302 { | |
303 return NGX_ERROR; | |
304 } | |
305 | |
306 } else { | |
307 pkt.number = &qc->appdata_pn; | |
308 pkt.secret = &qc->server_ad; | |
309 | |
310 if (ngx_quic_create_short_packet(c, c->ssl->connection, | |
311 &pkt, payload, &res) | |
312 != NGX_OK) | |
313 { | |
314 return NGX_ERROR; | |
315 } | |
316 } | |
317 | |
318 ngx_quic_hexdump0(c->log, "packet to send", res.data, res.len); | |
319 | |
320 c->send(c, res.data, res.len); // TODO: err handling | |
321 | |
322 return NGX_OK; | |
323 } | |
324 | |
325 | |
326 static size_t | |
327 ngx_quic_create_ack(u_char *p, ngx_quic_ack_frame_t *ack) | |
328 { | |
329 if (p == NULL) { | |
330 return 5; /* minimal ACK */ | |
331 } | |
332 | |
333 ngx_quic_build_int(&p, NGX_QUIC_FT_ACK); | |
334 ngx_quic_build_int(&p, ack->pn); | |
335 ngx_quic_build_int(&p, 0); | |
336 ngx_quic_build_int(&p, 0); | |
337 ngx_quic_build_int(&p, ack->pn); | |
338 | |
339 return 5; | |
340 } | |
341 | |
342 | |
343 static size_t | |
344 ngx_quic_create_crypto(u_char *p, ngx_quic_crypto_frame_t *crypto) | |
345 { | |
346 u_char *start; | |
347 | |
348 if (p == NULL) { | |
349 if (crypto->len >= 64) { | |
350 return crypto->len + 4; | |
351 | |
352 } else { | |
353 return crypto->len + 3; | |
354 } // TODO: proper calculation of varint | |
355 } | |
356 | |
357 start = p; | |
358 | |
359 ngx_quic_build_int(&p, NGX_QUIC_FT_CRYPTO); | |
360 ngx_quic_build_int(&p, 0); | |
361 ngx_quic_build_int(&p, crypto->len); | |
362 p = ngx_cpymem(p, crypto->data, crypto->len); | |
363 | |
364 return p - start; | |
365 } | |
366 | |
367 size_t | |
368 ngx_quic_frame_len(ngx_quic_frame_t *frame) | |
369 { | |
370 switch (frame->type) { | |
371 case NGX_QUIC_FT_ACK: | |
372 return ngx_quic_create_ack(NULL, &frame->u.ack); | |
373 case NGX_QUIC_FT_CRYPTO: | |
374 return ngx_quic_create_crypto(NULL, &frame->u.crypto); | |
375 default: | |
376 /* BUG: unsupported frame type generated */ | |
377 return 0; | |
378 } | |
379 } | |
380 | |
381 | |
382 /* pack a group of frames [start; end) into memory p and send as single packet */ | |
383 ngx_int_t | |
384 ngx_quic_frames_send(ngx_connection_t *c, ngx_quic_frame_t *start, | |
385 ngx_quic_frame_t *end, size_t total) | |
386 { | |
387 u_char *p; | |
388 ngx_str_t out; | |
389 ngx_quic_frame_t *f; | |
390 | |
391 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
392 "sending frames %p...%p", start, end); | |
393 | |
394 p = ngx_pnalloc(c->pool, total); | |
395 if (p == NULL) { | |
396 return NGX_ERROR; | |
397 } | |
398 | |
399 out.data = p; | |
400 | |
401 for (f = start; f != end; f = f->next) { | |
402 | |
403 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "frame: %s", f->info); | |
404 | |
405 switch (f->type) { | |
406 case NGX_QUIC_FT_ACK: | |
407 p += ngx_quic_create_ack(p, &f->u.ack); | |
408 break; | |
409 | |
410 case NGX_QUIC_FT_CRYPTO: | |
411 p += ngx_quic_create_crypto(p, &f->u.crypto); | |
412 break; | |
413 | |
414 default: | |
415 /* BUG: unsupported frame type generated */ | |
416 return NGX_ERROR; | |
417 } | |
418 } | |
419 | |
420 out.len = p - out.data; | |
421 | |
422 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
423 "packet ready: %ui bytes at level %d", | |
424 out.len, start->level); | |
425 | |
426 // IOVEC/sendmsg_chain ? | |
427 if (ngx_quic_send_packet(c, c->quic, start->level, &out) != NGX_OK) { | |
428 return NGX_ERROR; | |
429 } | |
430 | |
431 return NGX_OK; | |
432 } | |
243 | 433 |
244 ngx_int_t | 434 ngx_int_t |
245 ngx_quic_output(ngx_connection_t *c) | 435 ngx_quic_output(ngx_connection_t *c) |
246 { | 436 { |
247 /* stub for processing output queue */ | 437 size_t len; |
248 | 438 ngx_uint_t lvl; |
249 if (c->quic->out.data) { | 439 ngx_quic_frame_t *f, *start; |
250 c->send(c, c->quic->out.data, c->quic->out.len); | 440 ngx_quic_connection_t *qc; |
251 c->quic->out.data = NULL; | 441 |
252 } | 442 qc = c->quic; |
443 | |
444 if (qc->frames == NULL) { | |
445 return NGX_OK; | |
446 } | |
447 | |
448 lvl = qc->frames->level; | |
449 start = qc->frames; | |
450 f = start; | |
451 | |
452 do { | |
453 len = 0; | |
454 | |
455 do { | |
456 /* process same-level group of frames */ | |
457 | |
458 len += ngx_quic_frame_len(f);// TODO: handle overflow, max size | |
459 | |
460 f = f->next; | |
461 } while (f && f->level == lvl); | |
462 | |
463 | |
464 if (ngx_quic_frames_send(c, start, f, len) != NGX_OK) { | |
465 return NGX_ERROR; | |
466 } | |
467 | |
468 if (f == NULL) { | |
469 break; | |
470 } | |
471 | |
472 lvl = f->level; // TODO: must not decrease (ever, also between calls) | |
473 start = f; | |
474 | |
475 } while (1); | |
476 | |
477 qc->frames = NULL; | |
253 | 478 |
254 return NGX_OK; | 479 return NGX_OK; |
255 } | 480 } |
256 | 481 |
257 | 482 |
539 return NGX_OK; | 764 return NGX_OK; |
540 } | 765 } |
541 | 766 |
542 | 767 |
543 static void | 768 static void |
544 ngx_quic_create_ack(u_char **p, uint64_t num) | 769 ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame) |
545 { | 770 { |
546 ngx_quic_build_int(p, NGX_QUIC_FT_ACK); | 771 ngx_quic_frame_t *f; |
547 ngx_quic_build_int(p, num); | 772 |
548 ngx_quic_build_int(p, 0); | 773 if (qc->frames == NULL) { |
549 ngx_quic_build_int(p, 0); | 774 qc->frames = frame; |
550 ngx_quic_build_int(p, num); | 775 return; |
551 } | 776 } |
552 | 777 |
553 | 778 for (f = qc->frames; f->next; f = f->next) { /* void */ } |
554 static void | 779 |
555 ngx_quic_create_crypto(u_char **p, u_char *data, size_t len) | 780 f->next = frame; |
556 { | 781 } |
557 ngx_quic_build_int(p, NGX_QUIC_FT_CRYPTO); | |
558 ngx_quic_build_int(p, 0); | |
559 ngx_quic_build_int(p, len); | |
560 *p = ngx_cpymem(*p, data, len); | |
561 } | |
562 | |
563 | 782 |
564 | 783 |
565 static int | 784 static int |
566 ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, | 785 ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, |
567 enum ssl_encryption_level_t level, const uint8_t *data, size_t len) | 786 enum ssl_encryption_level_t level, const uint8_t *data, size_t len) |
568 { | 787 { |
569 u_char *p; | 788 u_char *p; |
570 ngx_str_t payload, res; | 789 ngx_quic_frame_t *frame; |
571 ngx_connection_t *c; | 790 ngx_connection_t *c; |
572 ngx_quic_header_t pkt; | |
573 ngx_quic_connection_t *qc; | 791 ngx_quic_connection_t *qc; |
574 | |
575 ngx_str_t initial_token = ngx_null_string; | |
576 | 792 |
577 c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); | 793 c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); |
578 qc = c->quic; | 794 qc = c->quic; |
579 | 795 |
580 //ngx_ssl_handshake_log(c); // TODO: enable again | 796 //ngx_ssl_handshake_log(c); // TODO: enable again |
581 | 797 |
582 ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); | 798 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, |
583 | 799 "ngx_quic_add_handshake_data"); |
584 pkt.level = level; | 800 |
585 | 801 frame = ngx_pcalloc(c->pool, sizeof(ngx_quic_frame_t)); |
586 payload.data = ngx_alloc(4 + len + 5 /*minimal ACK*/, c->log); | 802 if (frame == NULL) { |
587 if (payload.data == 0) { | |
588 return 0; | 803 return 0; |
589 } | 804 } |
590 p = payload.data; | 805 |
591 | 806 p = ngx_pnalloc(c->pool, len); |
592 ngx_quic_create_crypto(&p, (u_char *) data, len); | 807 if (p == NULL) { |
808 return 0; | |
809 } | |
810 | |
811 ngx_memcpy(p, data, len); | |
812 | |
813 frame->level = level; | |
814 frame->type = NGX_QUIC_FT_CRYPTO; | |
815 frame->u.crypto.len = len; | |
816 frame->u.crypto.data = p; | |
817 | |
818 ngx_sprintf(frame->info, "crypto, generated by SSL len=%ui level=%d", len, level); | |
819 | |
820 ngx_quic_queue_frame(qc, frame); | |
593 | 821 |
594 if (level == ssl_encryption_initial) { | 822 if (level == ssl_encryption_initial) { |
595 ngx_quic_create_ack(&p, 0); | 823 frame = ngx_pcalloc(c->pool, sizeof(ngx_quic_frame_t)); |
596 | 824 if (frame == NULL) { |
597 pkt.number = &qc->initial_pn; | |
598 pkt.flags = NGX_QUIC_PKT_INITIAL; | |
599 pkt.secret = &qc->server_in; | |
600 | |
601 pkt.token = &initial_token; | |
602 | |
603 } else if (level == ssl_encryption_handshake) { | |
604 pkt.number = &qc->handshake_pn; | |
605 pkt.flags = NGX_QUIC_PKT_HANDSHAKE; | |
606 pkt.secret = &qc->server_hs; | |
607 | |
608 pkt.token = NULL; | |
609 | |
610 } else { | |
611 pkt.number = &qc->appdata_pn; | |
612 pkt.secret = &qc->server_ad; | |
613 } | |
614 | |
615 payload.len = p - payload.data; | |
616 | |
617 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, | |
618 "ngx_quic_add_handshake_data: clear_len:%uz", | |
619 payload.len); | |
620 | |
621 if (level == ssl_encryption_application) { | |
622 | |
623 if (ngx_quic_create_short_packet(c, ssl_conn, &pkt, &payload, &res) | |
624 != NGX_OK) | |
625 { | |
626 return 0; | 825 return 0; |
627 } | 826 } |
628 | 827 frame->level = level; |
629 } else { | 828 frame->type = NGX_QUIC_FT_ACK; |
630 | 829 frame->u.ack.pn = 0; |
631 if (ngx_quic_create_long_packet(c, ssl_conn, &pkt, &payload, &res) | 830 ngx_sprintf(frame->info, "ACK for PN=0 at initial, added manually from add_handshake_data"); |
632 != NGX_OK) | 831 |
633 { | 832 ngx_quic_queue_frame(qc, frame); |
634 return 0; | 833 } |
635 } | 834 |
636 } | 835 return 1; |
637 | 836 } |
638 // TODO: save state of data to send into qc (push into queue) | 837 |
639 qc->out = res; | 838 |
839 static int | |
840 ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn) | |
841 { | |
842 ngx_connection_t *c; | |
843 | |
844 c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); | |
845 | |
846 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_quic_flush_flight()"); | |
640 | 847 |
641 if (ngx_quic_output(c) != NGX_OK) { | 848 if (ngx_quic_output(c) != NGX_OK) { |
642 return 0; | 849 return 0; |
643 } | 850 } |
644 | |
645 return 1; | |
646 } | |
647 | |
648 | |
649 static int | |
650 ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn) | |
651 { | |
652 ngx_connection_t *c; | |
653 | |
654 c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); | |
655 | |
656 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_quic_flush_flight()"); | |
657 | 851 |
658 return 1; | 852 return 1; |
659 } | 853 } |
660 | 854 |
661 | 855 |
1185 (int) SSL_quic_read_level(c->ssl->connection), | 1379 (int) SSL_quic_read_level(c->ssl->connection), |
1186 (int) SSL_quic_write_level(c->ssl->connection)); | 1380 (int) SSL_quic_write_level(c->ssl->connection)); |
1187 | 1381 |
1188 // ACK Client Finished | 1382 // ACK Client Finished |
1189 | 1383 |
1190 ngx_str_t payload, res; | 1384 ngx_quic_frame_t *frame; |
1191 ngx_quic_header_t pkt; | 1385 |
1192 ngx_memzero(&pkt, sizeof(ngx_quic_header_t)); | 1386 frame = ngx_pcalloc(c->pool, sizeof(ngx_quic_frame_t)); |
1193 | 1387 if (frame == NULL) { |
1194 pkt.level = ssl_encryption_handshake; | |
1195 pkt.number = &qc->handshake_pn; | |
1196 pkt.flags = NGX_QUIC_PKT_HANDSHAKE; | |
1197 pkt.secret = &qc->server_hs; | |
1198 | |
1199 payload.data = ngx_alloc(5 /*minimal ACK*/, c->log); | |
1200 if (payload.data == 0) { | |
1201 return 0; | 1388 return 0; |
1202 } | 1389 } |
1203 | 1390 |
1204 p = payload.data; | 1391 frame->level = ssl_encryption_handshake; |
1205 ngx_quic_create_ack(&p, pn); | 1392 frame->type = NGX_QUIC_FT_ACK; |
1206 | 1393 frame->u.ack.pn = pn; |
1207 payload.len = p - payload.data; | 1394 |
1208 | 1395 ngx_sprintf(frame->info, "ACK for PN=%d at handshake level, in respond to client finished", pn); |
1209 if (ngx_quic_create_long_packet(c, c->ssl->connection, &pkt, &payload, &res) | 1396 ngx_quic_queue_frame(qc, frame); |
1210 != NGX_OK) | |
1211 { | |
1212 return 0; | |
1213 } | |
1214 | |
1215 qc->out = res; | |
1216 | 1397 |
1217 if (ngx_quic_output(c) != NGX_OK) { | 1398 if (ngx_quic_output(c) != NGX_OK) { |
1218 return 0; | 1399 return 0; |
1219 } | 1400 } |
1220 | 1401 |