comparison src/event/ngx_event_quic.c @ 8333:167d32476737 quic

Crypto buffer frames reordering. If offset in CRYPTO frame doesn't match expected, following actions are taken: a) Duplicate frames or frames within [0...current offset] are ignored b) New data from intersecting ranges (starts before current_offset, ends after) is consumed c) "Future" frames are stored in a sorted queue (min offset .. max offset) Once a frame is consumed, current offset is updated and the queue is inspected: we iterate the queue until the gap is found and act as described above for each frame. The amount of data in buffered frames is limited by corresponding macro. The CRYPTO and STREAM frame structures are now compatible: they share the same set of initial fields. This allows to have code that deals with both of this frames. The ordering layer now processes the frame with offset and invokes the handler when it can organise an ordered stream of data.
author Vladimir Homutov <vl@nginx.com>
date Tue, 14 Apr 2020 12:16:25 +0300
parents 6ad871b63422
children 72d20158c814
comparison
equal deleted inserted replaced
8332:6ad871b63422 8333:167d32476737
24 #define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1) 24 #define NGX_QUIC_SEND_CTX_LAST (NGX_QUIC_ENCRYPTION_LAST - 1)
25 25
26 #define NGX_QUIC_STREAMS_INC 16 26 #define NGX_QUIC_STREAMS_INC 16
27 #define NGX_QUIC_STREAMS_LIMIT (1ULL < 60) 27 #define NGX_QUIC_STREAMS_LIMIT (1ULL < 60)
28 28
29 /*
30 * 7.4. Cryptographic Message Buffering
31 * Implementations MUST support buffering at least 4096 bytes of data
32 */
33 #define NGX_QUIC_MAX_BUFFERED 65535
34
29 35
30 typedef enum { 36 typedef enum {
31 NGX_QUIC_ST_INITIAL, /* connection just created */ 37 NGX_QUIC_ST_INITIAL, /* connection just created */
32 NGX_QUIC_ST_HANDSHAKE, /* handshake started */ 38 NGX_QUIC_ST_HANDSHAKE, /* handshake started */
33 NGX_QUIC_ST_EARLY_DATA, /* handshake in progress */ 39 NGX_QUIC_ST_EARLY_DATA, /* handshake in progress */
62 ngx_queue_t frames; 68 ngx_queue_t frames;
63 ngx_queue_t sent; 69 ngx_queue_t sent;
64 } ngx_quic_send_ctx_t; 70 } ngx_quic_send_ctx_t;
65 71
66 72
73 /* ordered frames stream context */
74 typedef struct {
75 uint64_t sent;
76 uint64_t received;
77 ngx_queue_t frames;
78 size_t total; /* size of buffered data */
79 } ngx_quic_frames_stream_t;
80
81
67 struct ngx_quic_connection_s { 82 struct ngx_quic_connection_s {
68 ngx_str_t scid; 83 ngx_str_t scid;
69 ngx_str_t dcid; 84 ngx_str_t dcid;
70 ngx_str_t token; 85 ngx_str_t token;
71 86
76 ngx_quic_state_t state; 91 ngx_quic_state_t state;
77 92
78 ngx_quic_send_ctx_t send_ctx[NGX_QUIC_SEND_CTX_LAST]; 93 ngx_quic_send_ctx_t send_ctx[NGX_QUIC_SEND_CTX_LAST];
79 ngx_quic_secrets_t keys[NGX_QUIC_ENCRYPTION_LAST]; 94 ngx_quic_secrets_t keys[NGX_QUIC_ENCRYPTION_LAST];
80 ngx_quic_secrets_t next_key; 95 ngx_quic_secrets_t next_key;
81 uint64_t crypto_offset_out[NGX_QUIC_ENCRYPTION_LAST]; 96 ngx_quic_frames_stream_t crypto[NGX_QUIC_ENCRYPTION_LAST];
82 uint64_t crypto_offset_in[NGX_QUIC_ENCRYPTION_LAST];
83 97
84 ngx_ssl_t *ssl; 98 ngx_ssl_t *ssl;
85 99
86 ngx_event_t push; 100 ngx_event_t push;
87 ngx_event_t retry; 101 ngx_event_t retry;
145 static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c, 159 static ngx_int_t ngx_quic_handle_ack_frame(ngx_connection_t *c,
146 ngx_quic_header_t *pkt, ngx_quic_ack_frame_t *f); 160 ngx_quic_header_t *pkt, ngx_quic_ack_frame_t *f);
147 static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c, 161 static ngx_int_t ngx_quic_handle_ack_frame_range(ngx_connection_t *c,
148 ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max); 162 ngx_quic_send_ctx_t *ctx, uint64_t min, uint64_t max);
149 static ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c, 163 static ngx_int_t ngx_quic_handle_crypto_frame(ngx_connection_t *c,
150 ngx_quic_header_t *pkt, ngx_quic_crypto_frame_t *frame); 164 ngx_quic_header_t *pkt, ngx_quic_frame_t *frame);
165 static ngx_int_t ngx_quic_adjust_frame_offset(ngx_connection_t *c,
166 ngx_quic_frame_t *f, uint64_t offset_in);
167 static ngx_int_t ngx_quic_buffer_frame(ngx_connection_t *c,
168 ngx_quic_frames_stream_t *stream, ngx_quic_frame_t *f);
169
170 typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c,
171 ngx_quic_frame_t *frame);
172
173 static ngx_int_t ngx_quic_handle_ordered_frame(ngx_connection_t *c,
174 ngx_quic_frames_stream_t *fs, ngx_quic_frame_t *frame,
175 ngx_quic_frame_handler_pt handler);
176
177 static ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c,
178 ngx_quic_frame_t *frame);
151 static ngx_int_t ngx_quic_handle_stream_frame(ngx_connection_t *c, 179 static ngx_int_t ngx_quic_handle_stream_frame(ngx_connection_t *c,
152 ngx_quic_header_t *pkt, ngx_quic_stream_frame_t *frame); 180 ngx_quic_header_t *pkt, ngx_quic_stream_frame_t *frame);
153 static ngx_int_t ngx_quic_handle_max_streams(ngx_connection_t *c); 181 static ngx_int_t ngx_quic_handle_max_streams(ngx_connection_t *c);
154 static ngx_int_t ngx_quic_handle_streams_blocked_frame(ngx_connection_t *c, 182 static ngx_int_t ngx_quic_handle_streams_blocked_frame(ngx_connection_t *c,
155 ngx_quic_header_t *pkt, ngx_quic_streams_blocked_frame_t *f); 183 ngx_quic_header_t *pkt, ngx_quic_streams_blocked_frame_t *f);
290 318
291 static int 319 static int
292 ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, 320 ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn,
293 enum ssl_encryption_level_t level, const uint8_t *data, size_t len) 321 enum ssl_encryption_level_t level, const uint8_t *data, size_t len)
294 { 322 {
295 u_char *p, *end; 323 u_char *p, *end;
296 size_t client_params_len; 324 size_t client_params_len;
297 const uint8_t *client_params; 325 const uint8_t *client_params;
298 ngx_quic_frame_t *frame; 326 ngx_quic_frame_t *frame;
299 ngx_connection_t *c; 327 ngx_connection_t *c;
300 ngx_quic_connection_t *qc; 328 ngx_quic_connection_t *qc;
329 ngx_quic_frames_stream_t *fs;
301 330
302 c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn); 331 c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
303 qc = c->quic; 332 qc = c->quic;
304 333
305 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, 334 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
333 362
334 qc->client_tp_done = 1; 363 qc->client_tp_done = 1;
335 } 364 }
336 } 365 }
337 366
367 fs = &qc->crypto[level];
368
338 frame = ngx_quic_alloc_frame(c, len); 369 frame = ngx_quic_alloc_frame(c, len);
339 if (frame == NULL) { 370 if (frame == NULL) {
340 return 0; 371 return 0;
341 } 372 }
342 373
343 ngx_memcpy(frame->data, data, len); 374 ngx_memcpy(frame->data, data, len);
344 375
345 frame->level = level; 376 frame->level = level;
346 frame->type = NGX_QUIC_FT_CRYPTO; 377 frame->type = NGX_QUIC_FT_CRYPTO;
347 frame->u.crypto.offset += qc->crypto_offset_out[level]; 378 frame->u.crypto.offset += fs->sent;
348 frame->u.crypto.len = len; 379 frame->u.crypto.length = len;
349 frame->u.crypto.data = frame->data; 380 frame->u.crypto.data = frame->data;
350 381
351 qc->crypto_offset_out[level] += len; 382 fs->sent += len;
352 383
353 ngx_sprintf(frame->info, "crypto, generated by SSL len=%ui level=%d", len, level); 384 ngx_sprintf(frame->info, "crypto, generated by SSL len=%ui level=%d", len, level);
354 385
355 ngx_quic_queue_frame(qc, frame); 386 ngx_quic_queue_frame(qc, frame);
356 387
476 qc->state = NGX_QUIC_ST_INITIAL; 507 qc->state = NGX_QUIC_ST_INITIAL;
477 508
478 ngx_rbtree_init(&qc->streams.tree, &qc->streams.sentinel, 509 ngx_rbtree_init(&qc->streams.tree, &qc->streams.sentinel,
479 ngx_quic_rbtree_insert_stream); 510 ngx_quic_rbtree_insert_stream);
480 511
481 for (i = 0; i < 3; i++) { 512 for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) {
482 ngx_queue_init(&qc->send_ctx[i].frames); 513 ngx_queue_init(&qc->send_ctx[i].frames);
483 ngx_queue_init(&qc->send_ctx[i].sent); 514 ngx_queue_init(&qc->send_ctx[i].sent);
484 } 515 }
516
517 for (i = 0; i < NGX_QUIC_ENCRYPTION_LAST; i++) {
518 ngx_queue_init(&qc->crypto[i].frames);
519 }
485 520
486 ngx_queue_init(&qc->free_frames); 521 ngx_queue_init(&qc->free_frames);
487 522
488 qc->retry.log = c->log; 523 qc->retry.log = c->log;
489 qc->retry.data = c; 524 qc->retry.data = c;
711 ngx_quic_stream_t *qs; 746 ngx_quic_stream_t *qs;
712 ngx_quic_connection_t *qc; 747 ngx_quic_connection_t *qc;
713 748
714 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "close quic connection"); 749 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "close quic connection");
715 750
751 // TODO: free frames from reorder queue if any
752
716 qc = c->quic; 753 qc = c->quic;
717 754
718 if (qc) { 755 if (qc) {
719 qc->closing = 1; 756 qc->closing = 1;
720 tree = &qc->streams.tree; 757 tree = &qc->streams.tree;
827 864
828 } else { 865 } else {
829 rc = ngx_quic_app_input(c, &pkt); 866 rc = ngx_quic_app_input(c, &pkt);
830 } 867 }
831 868
832 if (rc != NGX_OK) { 869 if (rc == NGX_ERROR) {
833 return rc; 870 return NGX_ERROR;
834 } 871 }
872
873 /* NGX_OK || NGX_DECLINED */
874
875 /*
876 * we get NGX_DECLINED when there are no keys [yet] available
877 * to decrypt packet.
878 * Instead of queueing it, we ignore it and rely on the sender's
879 * retransmission:
880 *
881 * 12.2. Coalescing Packets:
882 *
883 * For example, if decryption fails (because the keys are
884 * not available or any other reason), the receiver MAY either
885 * discard or buffer the packet for later processing and MUST
886 * attempt to process the remaining packets.
887 */
835 888
836 /* b->pos is at header end, adjust by actual packet length */ 889 /* b->pos is at header end, adjust by actual packet length */
837 p = b->pos + pkt.len; 890 p = b->pos + pkt.len;
838 b->pos = p; /* reset b->pos to the next packet start */ 891 b->pos = p; /* reset b->pos to the next packet start */
839 } 892 }
1117 1170
1118 break; 1171 break;
1119 1172
1120 case NGX_QUIC_FT_CRYPTO: 1173 case NGX_QUIC_FT_CRYPTO:
1121 1174
1122 if (ngx_quic_handle_crypto_frame(c, pkt, &frame.u.crypto) 1175 if (ngx_quic_handle_crypto_frame(c, pkt, &frame) != NGX_OK) {
1123 != NGX_OK)
1124 {
1125 return NGX_ERROR; 1176 return NGX_ERROR;
1126 } 1177 }
1127 1178
1128 ack_this = 1; 1179 ack_this = 1;
1129 break; 1180 break;
1371 } 1422 }
1372 1423
1373 1424
1374 static ngx_int_t 1425 static ngx_int_t
1375 ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, 1426 ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt,
1376 ngx_quic_crypto_frame_t *f) 1427 ngx_quic_frame_t *frame)
1377 { 1428 {
1378 int sslerr; 1429 ngx_quic_connection_t *qc;
1379 ssize_t n; 1430 ngx_quic_frames_stream_t *fs;
1380 uint64_t *curr_offset;
1381 ngx_ssl_conn_t *ssl_conn;
1382 ngx_quic_connection_t *qc;
1383 1431
1384 qc = c->quic; 1432 qc = c->quic;
1385 1433 fs = &qc->crypto[pkt->level];
1386 curr_offset = &qc->crypto_offset_in[pkt->level]; 1434
1387 1435 return ngx_quic_handle_ordered_frame(c, fs, frame, ngx_quic_crypto_input);
1388 if (f->offset != *curr_offset) { 1436 }
1437
1438
1439 static ngx_int_t
1440 ngx_quic_handle_ordered_frame(ngx_connection_t *c, ngx_quic_frames_stream_t *fs,
1441 ngx_quic_frame_t *frame, ngx_quic_frame_handler_pt handler)
1442 {
1443 size_t full_len;
1444 ngx_queue_t *q;
1445 ngx_quic_ordered_frame_t *f;
1446
1447 f = &frame->u.ord;
1448
1449 if (f->offset > fs->received) {
1450 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
1451 "out-of-order frame: expecting %ui got %ui",
1452 fs->received, f->offset);
1453
1454 return ngx_quic_buffer_frame(c, fs, frame);
1455 }
1456
1457 if (f->offset < fs->received) {
1458
1459 if (ngx_quic_adjust_frame_offset(c, frame, fs->received)
1460 == NGX_DONE)
1461 {
1462 /* old/duplicate data range */
1463 return NGX_OK;
1464 }
1465
1466 /* intersecting data range, frame modified */
1467 }
1468
1469 /* f->offset == fs->received */
1470
1471 if (handler(c, frame) != NGX_OK) {
1472 return NGX_ERROR;
1473 }
1474
1475 fs->received += f->length;
1476
1477 /* now check the queue if we can continue with buffered frames */
1478
1479 do {
1480 q = ngx_queue_head(&fs->frames);
1481 if (q == ngx_queue_sentinel(&fs->frames)) {
1482 break;
1483 }
1484
1485 frame = ngx_queue_data(q, ngx_quic_frame_t, queue);
1486 f = &frame->u.ord;
1487
1488 if (f->offset > fs->received) {
1489 /* gap found, nothing more to do */
1490 break;
1491 }
1492
1493 full_len = f->length;
1494
1495 if (f->offset < fs->received) {
1496
1497 if (ngx_quic_adjust_frame_offset(c, frame, fs->received)
1498 == NGX_DONE)
1499 {
1500 /* old/duplicate data range */
1501 ngx_queue_remove(q);
1502 fs->total -= f->length;
1503
1504 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
1505 "skipped buffered frame, total %ui", fs->total);
1506 ngx_quic_free_frame(c, frame);
1507 continue;
1508 }
1509
1510 /* frame was adjusted, proceed to input */
1511 }
1512
1513 /* f->offset == fs->received */
1514
1515 if (handler(c, frame) != NGX_OK) {
1516 return NGX_ERROR;
1517 }
1518
1519 fs->received += f->length;
1520 fs->total -= full_len;
1521
1522 ngx_queue_remove(q);
1523
1524 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
1525 "consumed buffered frame, total %ui", fs->total);
1526
1527 ngx_quic_free_frame(c, frame);
1528
1529 } while (1);
1530
1531 return NGX_OK;
1532 }
1533
1534
1535 static ngx_int_t
1536 ngx_quic_adjust_frame_offset(ngx_connection_t *c, ngx_quic_frame_t *frame,
1537 uint64_t offset_in)
1538 {
1539 size_t tail;
1540 ngx_quic_ordered_frame_t *f;
1541
1542 f = &frame->u.ord;
1543
1544 tail = offset_in - f->offset;
1545
1546 if (tail >= f->length) {
1547 /* range preceeding already received data or duplicate, ignore */
1548
1549 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
1550 "old or duplicate data in ordered frame, ignored");
1551 return NGX_DONE;
1552 }
1553
1554 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
1555 "adjusted ordered frame data start to expected offset");
1556
1557 /* intersecting range: adjust data size */
1558
1559 f->offset += tail;
1560 f->data += tail;
1561 f->length -= tail;
1562
1563 return NGX_OK;
1564 }
1565
1566
1567 static ngx_int_t
1568 ngx_quic_buffer_frame(ngx_connection_t *c, ngx_quic_frames_stream_t *fs,
1569 ngx_quic_frame_t *frame)
1570 {
1571 u_char *data;
1572 ngx_queue_t *q;
1573 ngx_quic_frame_t *dst, *item;
1574 ngx_quic_ordered_frame_t *f, *df;
1575
1576 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "ngx_quic_buffer_frame");
1577
1578 f = &frame->u.ord;
1579
1580 /* frame start offset is in the future, buffer it */
1581
1582 /* check limit on total size used by all buffered frames, not actual data */
1583 if (NGX_QUIC_MAX_BUFFERED - fs->total < f->length) {
1389 ngx_log_error(NGX_LOG_INFO, c->log, 0, 1584 ngx_log_error(NGX_LOG_INFO, c->log, 0,
1390 "crypto frame with unexpected offset"); 1585 "ordered input buffer limit exceeded");
1391 1586 return NGX_ERROR;
1392 /* TODO: support reordering/buffering of data */ 1587 }
1393 return NGX_ERROR; 1588
1394 } 1589 dst = ngx_quic_alloc_frame(c, f->length);
1590 if (dst == NULL) {
1591 return NGX_ERROR;
1592 }
1593
1594 data = dst->data;
1595 ngx_memcpy(dst, frame, sizeof(ngx_quic_frame_t));
1596 dst->data = data;
1597
1598 ngx_memcpy(dst->data, f->data, f->length);
1599
1600 df = &dst->u.ord;
1601 df->data = dst->data;
1602
1603 fs->total += f->length;
1604
1605 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
1606 "ordered frame with unexpected offset: buffered, total %ui",
1607 fs->total);
1608
1609 /* TODO: do we need some timeout for this queue ? */
1610
1611 if (ngx_queue_empty(&fs->frames)) {
1612 ngx_queue_insert_after(&fs->frames, &dst->queue);
1613 return NGX_OK;
1614 }
1615
1616 for (q = ngx_queue_last(&fs->frames);
1617 q != ngx_queue_sentinel(&fs->frames);
1618 q = ngx_queue_prev(q))
1619 {
1620 item = ngx_queue_data(q, ngx_quic_frame_t, queue);
1621 f = &item->u.ord;
1622
1623 if (f->offset < df->offset) {
1624 ngx_queue_insert_after(q, &dst->queue);
1625 return NGX_OK;
1626 }
1627 }
1628
1629 ngx_queue_insert_after(&fs->frames, &dst->queue);
1630
1631 return NGX_OK;
1632 }
1633
1634
1635 static ngx_int_t
1636 ngx_quic_crypto_input(ngx_connection_t *c, ngx_quic_frame_t *frame)
1637 {
1638 int sslerr;
1639 ssize_t n;
1640 ngx_ssl_conn_t *ssl_conn;
1641 ngx_quic_crypto_frame_t *f;
1642
1643 f = &frame->u.crypto;
1395 1644
1396 ssl_conn = c->ssl->connection; 1645 ssl_conn = c->ssl->connection;
1397 1646
1398 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, 1647 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
1399 "SSL_quic_read_level: %d, SSL_quic_write_level: %d", 1648 "SSL_quic_read_level: %d, SSL_quic_write_level: %d",
1400 (int) SSL_quic_read_level(ssl_conn), 1649 (int) SSL_quic_read_level(ssl_conn),
1401 (int) SSL_quic_write_level(ssl_conn)); 1650 (int) SSL_quic_write_level(ssl_conn));
1402 1651
1403 if (!SSL_provide_quic_data(ssl_conn, SSL_quic_read_level(ssl_conn), 1652 if (!SSL_provide_quic_data(ssl_conn, SSL_quic_read_level(ssl_conn),
1404 f->data, f->len)) 1653 f->data, f->length))
1405 { 1654 {
1406 ngx_ssl_error(NGX_LOG_INFO, c->log, 0, 1655 ngx_ssl_error(NGX_LOG_INFO, c->log, 0,
1407 "SSL_provide_quic_data() failed"); 1656 "SSL_provide_quic_data() failed");
1408 return NGX_ERROR; 1657 return NGX_ERROR;
1409 } 1658 }
1410
1411 *curr_offset += f->len;
1412 1659
1413 n = SSL_do_handshake(ssl_conn); 1660 n = SSL_do_handshake(ssl_conn);
1414 1661
1415 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); 1662 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
1416 1663