Mercurial > hg > nginx
comparison src/event/ngx_event_quic.c @ 8538:3afaaaa930ab quic
QUIC: added support for multiple connection IDs.
The peer may issue additional connection IDs up to the limit defined by
transport parameter "active_connection_id_limit", using NEW_CONNECTION_ID
frames, and retire such IDs using RETIRE_CONNECTION_ID frame.
author | Vladimir Homutov <vl@nginx.com> |
---|---|
date | Thu, 03 Sep 2020 13:11:27 +0300 |
parents | c6b963de0c00 |
children | 62db595a86b5 |
comparison
equal
deleted
inserted
replaced
8537:88676e7f4449 | 8538:3afaaaa930ab |
---|---|
86 ngx_queue_t sent; | 86 ngx_queue_t sent; |
87 } ngx_quic_send_ctx_t; | 87 } ngx_quic_send_ctx_t; |
88 | 88 |
89 | 89 |
90 struct ngx_quic_connection_s { | 90 struct ngx_quic_connection_s { |
91 ngx_str_t scid; | 91 ngx_str_t scid; /* initial client ID */ |
92 ngx_str_t dcid; | 92 ngx_str_t dcid; /* server (our own) ID */ |
93 ngx_str_t odcid; | 93 ngx_str_t odcid; /* original server ID */ |
94 ngx_str_t token; | 94 ngx_str_t token; |
95 | |
96 ngx_queue_t client_ids; | |
97 ngx_queue_t free_client_ids; | |
98 ngx_uint_t nclient_ids; | |
99 uint64_t max_retired_seqnum; | |
100 uint64_t curr_seqnum; | |
95 | 101 |
96 ngx_uint_t client_tp_done; | 102 ngx_uint_t client_tp_done; |
97 ngx_quic_tp_t tp; | 103 ngx_quic_tp_t tp; |
98 ngx_quic_tp_t ctp; | 104 ngx_quic_tp_t ctp; |
99 | 105 |
139 unsigned key_phase:1; | 145 unsigned key_phase:1; |
140 unsigned in_retry:1; | 146 unsigned in_retry:1; |
141 unsigned initialized:1; | 147 unsigned initialized:1; |
142 unsigned validated:1; | 148 unsigned validated:1; |
143 }; | 149 }; |
150 | |
151 | |
152 typedef struct { | |
153 ngx_queue_t queue; | |
154 uint64_t seqnum; | |
155 size_t len; | |
156 u_char id[NGX_QUIC_CID_LEN_MAX]; | |
157 u_char sr_token[NGX_QUIC_SRT_LEN]; | |
158 } ngx_quic_client_id_t; | |
144 | 159 |
145 | 160 |
146 typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c, | 161 typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c, |
147 ngx_quic_frame_t *frame, void *data); | 162 ngx_quic_frame_t *frame, void *data); |
148 | 163 |
251 ngx_quic_header_t *pkt, ngx_quic_stop_sending_frame_t *f); | 266 ngx_quic_header_t *pkt, ngx_quic_stop_sending_frame_t *f); |
252 static ngx_int_t ngx_quic_handle_max_streams_frame(ngx_connection_t *c, | 267 static ngx_int_t ngx_quic_handle_max_streams_frame(ngx_connection_t *c, |
253 ngx_quic_header_t *pkt, ngx_quic_max_streams_frame_t *f); | 268 ngx_quic_header_t *pkt, ngx_quic_max_streams_frame_t *f); |
254 static ngx_int_t ngx_quic_handle_path_challenge_frame(ngx_connection_t *c, | 269 static ngx_int_t ngx_quic_handle_path_challenge_frame(ngx_connection_t *c, |
255 ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f); | 270 ngx_quic_header_t *pkt, ngx_quic_path_challenge_frame_t *f); |
271 static ngx_int_t ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c, | |
272 ngx_quic_header_t *pkt, ngx_quic_new_conn_id_frame_t *f); | |
273 static ngx_int_t ngx_quic_retire_connection_id(ngx_connection_t *c, | |
274 enum ssl_encryption_level_t level, uint64_t seqnum); | |
275 static ngx_quic_client_id_t *ngx_quic_alloc_connection_id(ngx_connection_t *c); | |
256 | 276 |
257 static void ngx_quic_queue_frame(ngx_quic_connection_t *qc, | 277 static void ngx_quic_queue_frame(ngx_quic_connection_t *qc, |
258 ngx_quic_frame_t *frame); | 278 ngx_quic_frame_t *frame); |
259 | 279 |
260 static ngx_int_t ngx_quic_output(ngx_connection_t *c); | 280 static ngx_int_t ngx_quic_output(ngx_connection_t *c); |
649 ngx_int_t rc; | 669 ngx_int_t rc; |
650 ngx_uint_t i; | 670 ngx_uint_t i; |
651 ngx_quic_tp_t *ctp; | 671 ngx_quic_tp_t *ctp; |
652 ngx_quic_secrets_t *keys; | 672 ngx_quic_secrets_t *keys; |
653 ngx_quic_send_ctx_t *ctx; | 673 ngx_quic_send_ctx_t *ctx; |
674 ngx_quic_client_id_t *cid; | |
654 ngx_quic_connection_t *qc; | 675 ngx_quic_connection_t *qc; |
655 static u_char buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; | 676 static u_char buf[NGX_QUIC_MAX_UDP_PAYLOAD_SIZE]; |
656 | 677 |
657 if (ngx_buf_size(pkt->raw) < NGX_QUIC_MIN_INITIAL_SIZE) { | 678 if (ngx_buf_size(pkt->raw) < NGX_QUIC_MIN_INITIAL_SIZE) { |
658 ngx_log_error(NGX_LOG_INFO, c->log, 0, | 679 ngx_log_error(NGX_LOG_INFO, c->log, 0, |
706 for (i = 0; i < NGX_QUIC_ENCRYPTION_LAST; i++) { | 727 for (i = 0; i < NGX_QUIC_ENCRYPTION_LAST; i++) { |
707 ngx_queue_init(&qc->crypto[i].frames); | 728 ngx_queue_init(&qc->crypto[i].frames); |
708 } | 729 } |
709 | 730 |
710 ngx_queue_init(&qc->free_frames); | 731 ngx_queue_init(&qc->free_frames); |
732 ngx_queue_init(&qc->client_ids); | |
733 ngx_queue_init(&qc->free_client_ids); | |
711 | 734 |
712 qc->avg_rtt = NGX_QUIC_INITIAL_RTT; | 735 qc->avg_rtt = NGX_QUIC_INITIAL_RTT; |
713 qc->rttvar = NGX_QUIC_INITIAL_RTT / 2; | 736 qc->rttvar = NGX_QUIC_INITIAL_RTT / 2; |
714 qc->min_rtt = NGX_TIMER_INFINITE; | 737 qc->min_rtt = NGX_TIMER_INFINITE; |
715 | 738 |
716 /* | 739 /* |
717 * qc->latest_rtt = 0 | 740 * qc->latest_rtt = 0 |
741 * qc->nclient_ids = 0 | |
742 * qc->max_retired_seqnum = 0 | |
718 */ | 743 */ |
719 | 744 |
720 qc->received = pkt->raw->last - pkt->raw->start; | 745 qc->received = pkt->raw->last - pkt->raw->start; |
721 | 746 |
722 qc->pto.log = c->log; | 747 qc->pto.log = c->log; |
763 qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len); | 788 qc->scid.data = ngx_pnalloc(c->pool, qc->scid.len); |
764 if (qc->scid.data == NULL) { | 789 if (qc->scid.data == NULL) { |
765 return NGX_ERROR; | 790 return NGX_ERROR; |
766 } | 791 } |
767 ngx_memcpy(qc->scid.data, pkt->scid.data, qc->scid.len); | 792 ngx_memcpy(qc->scid.data, pkt->scid.data, qc->scid.len); |
793 | |
794 cid = ngx_quic_alloc_connection_id(c); | |
795 if (cid == NULL) { | |
796 return NGX_ERROR; | |
797 } | |
798 | |
799 cid->seqnum = 0; | |
800 cid->len = pkt->scid.len; | |
801 ngx_memcpy(cid->id, pkt->scid.data, pkt->scid.len); | |
802 | |
803 ngx_queue_insert_tail(&qc->client_ids, &cid->queue); | |
804 qc->nclient_ids++; | |
805 qc->curr_seqnum = 0; | |
768 | 806 |
769 keys = &c->quic->keys[ssl_encryption_initial]; | 807 keys = &c->quic->keys[ssl_encryption_initial]; |
770 | 808 |
771 if (ngx_quic_set_initial_secret(c->pool, &keys->client, &keys->server, | 809 if (ngx_quic_set_initial_secret(c->pool, &keys->client, &keys->server, |
772 &qc->odcid) | 810 &qc->odcid) |
1933 | 1971 |
1934 | 1972 |
1935 static ngx_int_t | 1973 static ngx_int_t |
1936 ngx_quic_check_peer(ngx_quic_connection_t *qc, ngx_quic_header_t *pkt) | 1974 ngx_quic_check_peer(ngx_quic_connection_t *qc, ngx_quic_header_t *pkt) |
1937 { | 1975 { |
1938 ngx_str_t *dcid; | 1976 ngx_str_t *dcid; |
1977 ngx_queue_t *q; | |
1978 ngx_quic_client_id_t *cid; | |
1939 | 1979 |
1940 dcid = ngx_quic_pkt_zrtt(pkt->flags) ? &qc->odcid : &qc->dcid; | 1980 dcid = ngx_quic_pkt_zrtt(pkt->flags) ? &qc->odcid : &qc->dcid; |
1941 | 1981 |
1942 if (pkt->dcid.len != dcid->len) { | 1982 if (pkt->dcid.len != dcid->len) { |
1943 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic dcidl"); | 1983 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic dcidl"); |
1947 if (ngx_memcmp(pkt->dcid.data, dcid->data, dcid->len) != 0) { | 1987 if (ngx_memcmp(pkt->dcid.data, dcid->data, dcid->len) != 0) { |
1948 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic dcid"); | 1988 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic dcid"); |
1949 return NGX_ERROR; | 1989 return NGX_ERROR; |
1950 } | 1990 } |
1951 | 1991 |
1952 if (pkt->scid.len != qc->scid.len) { | 1992 for (q = ngx_queue_head(&qc->client_ids); |
1953 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic scidl"); | 1993 q != ngx_queue_sentinel(&qc->client_ids); |
1954 return NGX_ERROR; | 1994 q = ngx_queue_next(q)) |
1955 } | 1995 { |
1956 | 1996 cid = ngx_queue_data(q, ngx_quic_client_id_t, queue); |
1957 if (ngx_memcmp(pkt->scid.data, qc->scid.data, qc->scid.len) != 0) { | 1997 |
1958 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic scid"); | 1998 if (pkt->scid.len == cid->len |
1959 return NGX_ERROR; | 1999 && ngx_memcmp(pkt->scid.data, cid->id, cid->len) == 0) |
1960 } | 2000 { |
1961 | 2001 return NGX_OK; |
1962 return NGX_OK; | 2002 } |
2003 } | |
2004 | |
2005 | |
2006 ngx_log_error(NGX_LOG_INFO, pkt->log, 0, "quic unexpected quic scid"); | |
2007 return NGX_ERROR; | |
1963 } | 2008 } |
1964 | 2009 |
1965 | 2010 |
1966 static ngx_int_t | 2011 static ngx_int_t |
1967 ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt) | 2012 ngx_quic_app_input(ngx_connection_t *c, ngx_quic_header_t *pkt) |
2228 } | 2273 } |
2229 | 2274 |
2230 break; | 2275 break; |
2231 | 2276 |
2232 case NGX_QUIC_FT_NEW_CONNECTION_ID: | 2277 case NGX_QUIC_FT_NEW_CONNECTION_ID: |
2278 | |
2279 if (ngx_quic_handle_new_connection_id_frame(c, pkt, &frame.u.ncid) | |
2280 != NGX_OK) | |
2281 { | |
2282 return NGX_ERROR; | |
2283 } | |
2284 | |
2285 break; | |
2286 | |
2233 case NGX_QUIC_FT_RETIRE_CONNECTION_ID: | 2287 case NGX_QUIC_FT_RETIRE_CONNECTION_ID: |
2234 case NGX_QUIC_FT_PATH_RESPONSE: | 2288 case NGX_QUIC_FT_PATH_RESPONSE: |
2235 | 2289 |
2236 /* TODO: handle */ | 2290 /* TODO: handle */ |
2237 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, | 2291 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, |
3467 *(uint64_t *) &f->data, frame->level); | 3521 *(uint64_t *) &f->data, frame->level); |
3468 | 3522 |
3469 ngx_quic_queue_frame(c->quic, frame); | 3523 ngx_quic_queue_frame(c->quic, frame); |
3470 | 3524 |
3471 return NGX_OK; | 3525 return NGX_OK; |
3526 } | |
3527 | |
3528 | |
3529 static ngx_int_t | |
3530 ngx_quic_handle_new_connection_id_frame(ngx_connection_t *c, | |
3531 ngx_quic_header_t *pkt, ngx_quic_new_conn_id_frame_t *f) | |
3532 { | |
3533 ngx_queue_t *q; | |
3534 ngx_quic_client_id_t *cid, *item; | |
3535 ngx_quic_connection_t *qc; | |
3536 | |
3537 qc = c->quic; | |
3538 | |
3539 if (f->seqnum < qc->max_retired_seqnum) { | |
3540 /* | |
3541 * An endpoint that receives a NEW_CONNECTION_ID frame with | |
3542 * a sequence number smaller than the Retire Prior To field | |
3543 * of a previously received NEW_CONNECTION_ID frame MUST send | |
3544 * a corresponding RETIRE_CONNECTION_ID frame that retires | |
3545 * the newly received connection ID, unless it has already | |
3546 * done so for that sequence number. | |
3547 */ | |
3548 | |
3549 if (ngx_quic_retire_connection_id(c, pkt->level, f->seqnum) != NGX_OK) { | |
3550 return NGX_ERROR; | |
3551 } | |
3552 | |
3553 goto retire; | |
3554 } | |
3555 | |
3556 cid = NULL; | |
3557 | |
3558 for (q = ngx_queue_head(&qc->client_ids); | |
3559 q != ngx_queue_sentinel(&qc->client_ids); | |
3560 q = ngx_queue_next(q)) | |
3561 { | |
3562 item = ngx_queue_data(q, ngx_quic_client_id_t, queue); | |
3563 | |
3564 if (item->seqnum == f->seqnum) { | |
3565 cid = item; | |
3566 break; | |
3567 } | |
3568 } | |
3569 | |
3570 if (cid) { | |
3571 /* | |
3572 * Transmission errors, timeouts and retransmissions might cause the | |
3573 * same NEW_CONNECTION_ID frame to be received multiple times | |
3574 */ | |
3575 | |
3576 if (cid->len != f->len | |
3577 || ngx_strncmp(cid->id, f->cid, f->len) != 0 | |
3578 || ngx_strncmp(cid->sr_token, f->srt, NGX_QUIC_SRT_LEN) != 0) | |
3579 { | |
3580 /* | |
3581 * ..a sequence number is used for different connection IDs, | |
3582 * the endpoint MAY treat that receipt as a connection error | |
3583 * of type PROTOCOL_VIOLATION. | |
3584 */ | |
3585 qc->error = NGX_QUIC_ERR_PROTOCOL_VIOLATION; | |
3586 qc->error_reason = "seqnum refers to different connection id/token"; | |
3587 return NGX_ERROR; | |
3588 } | |
3589 | |
3590 } else { | |
3591 | |
3592 cid = ngx_quic_alloc_connection_id(c); | |
3593 if (cid == NULL) { | |
3594 return NGX_ERROR; | |
3595 } | |
3596 | |
3597 cid->seqnum = f->seqnum; | |
3598 cid->len = f->len; | |
3599 ngx_memcpy(cid->id, f->cid, f->len); | |
3600 | |
3601 ngx_memcpy(cid->sr_token, f->srt, NGX_QUIC_SRT_LEN); | |
3602 | |
3603 ngx_queue_insert_tail(&qc->client_ids, &cid->queue); | |
3604 qc->nclient_ids++; | |
3605 | |
3606 /* always use latest available connection id */ | |
3607 if (f->seqnum > qc->curr_seqnum) { | |
3608 qc->scid.len = cid->len; | |
3609 qc->scid.data = cid->id; | |
3610 qc->curr_seqnum = f->seqnum; | |
3611 } | |
3612 } | |
3613 | |
3614 retire: | |
3615 | |
3616 if (qc->max_retired_seqnum && f->retire <= qc->max_retired_seqnum) { | |
3617 /* | |
3618 * Once a sender indicates a Retire Prior To value, smaller values sent | |
3619 * in subsequent NEW_CONNECTION_ID frames have no effect. A receiver | |
3620 * MUST ignore any Retire Prior To fields that do not increase the | |
3621 * largest received Retire Prior To value. | |
3622 */ | |
3623 goto done; | |
3624 } | |
3625 | |
3626 qc->max_retired_seqnum = f->retire; | |
3627 | |
3628 q = ngx_queue_head(&qc->client_ids); | |
3629 | |
3630 while (q != ngx_queue_sentinel(&qc->client_ids)) { | |
3631 | |
3632 cid = ngx_queue_data(q, ngx_quic_client_id_t, queue); | |
3633 q = ngx_queue_next(q); | |
3634 | |
3635 if (cid->seqnum >= f->retire) { | |
3636 continue; | |
3637 } | |
3638 | |
3639 /* this connection id must be retired */ | |
3640 | |
3641 if (ngx_quic_retire_connection_id(c, pkt->level, cid->seqnum) | |
3642 != NGX_OK) | |
3643 { | |
3644 return NGX_ERROR; | |
3645 } | |
3646 | |
3647 ngx_queue_remove(&cid->queue); | |
3648 ngx_queue_insert_head(&qc->free_client_ids, &cid->queue); | |
3649 qc->nclient_ids--; | |
3650 } | |
3651 | |
3652 done: | |
3653 | |
3654 if (qc->nclient_ids > qc->tp.active_connection_id_limit) { | |
3655 /* | |
3656 * After processing a NEW_CONNECTION_ID frame and | |
3657 * adding and retiring active connection IDs, if the number of active | |
3658 * connection IDs exceeds the value advertised in its | |
3659 * active_connection_id_limit transport parameter, an endpoint MUST | |
3660 * close the connection with an error of type CONNECTION_ID_LIMIT_ERROR. | |
3661 */ | |
3662 qc->error = NGX_QUIC_ERR_CONNECTION_ID_LIMIT_ERROR; | |
3663 qc->error_reason = "too many connection ids received"; | |
3664 return NGX_ERROR; | |
3665 } | |
3666 | |
3667 return NGX_OK; | |
3668 } | |
3669 | |
3670 | |
3671 static ngx_int_t | |
3672 ngx_quic_retire_connection_id(ngx_connection_t *c, | |
3673 enum ssl_encryption_level_t level, uint64_t seqnum) | |
3674 { | |
3675 ngx_quic_frame_t *frame; | |
3676 | |
3677 frame = ngx_quic_alloc_frame(c, 0); | |
3678 if (frame == NULL) { | |
3679 return NGX_ERROR; | |
3680 } | |
3681 | |
3682 frame->level = level; | |
3683 frame->type = NGX_QUIC_FT_RETIRE_CONNECTION_ID; | |
3684 frame->u.retire_cid.sequence_number = seqnum; | |
3685 | |
3686 ngx_sprintf(frame->info, "RETIRE_CONNECTION_ID seqnum=%uL level=%d", | |
3687 seqnum, frame->level); | |
3688 | |
3689 ngx_quic_queue_frame(c->quic, frame); | |
3690 | |
3691 return NGX_OK; | |
3692 } | |
3693 | |
3694 | |
3695 static ngx_quic_client_id_t * | |
3696 ngx_quic_alloc_connection_id(ngx_connection_t *c) | |
3697 { | |
3698 ngx_queue_t *q; | |
3699 ngx_quic_client_id_t *cid; | |
3700 ngx_quic_connection_t *qc; | |
3701 | |
3702 qc = c->quic; | |
3703 | |
3704 if (!ngx_queue_empty(&qc->free_client_ids)) { | |
3705 | |
3706 q = ngx_queue_head(&qc->free_client_ids); | |
3707 cid = ngx_queue_data(q, ngx_quic_client_id_t, queue); | |
3708 | |
3709 ngx_queue_remove(&cid->queue); | |
3710 | |
3711 ngx_memzero(cid, sizeof(ngx_quic_client_id_t)); | |
3712 | |
3713 } else { | |
3714 | |
3715 cid = ngx_pcalloc(c->pool, sizeof(ngx_quic_client_id_t)); | |
3716 if (cid == NULL) { | |
3717 return NULL; | |
3718 } | |
3719 } | |
3720 | |
3721 return cid; | |
3472 } | 3722 } |
3473 | 3723 |
3474 | 3724 |
3475 static void | 3725 static void |
3476 ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame) | 3726 ngx_quic_queue_frame(ngx_quic_connection_t *qc, ngx_quic_frame_t *frame) |