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)