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