comparison src/event/ngx_event_quic.c @ 8562:b31c02454539 quic

QUIC: added stateless reset support. The new "quic_stateless_reset_token_key" directive is added. It sets the endpoint key used to generate stateless reset tokens and enables feature. If the endpoint receives short-header packet that can't be matched to existing connection, a stateless reset packet is generated with a proper token. If a valid stateless reset token is found in the incoming packet, the connection is closed. Example configuration: http { quic_stateless_reset_token_key "foo"; ... }
author Vladimir Homutov <vl@nginx.com>
date Wed, 30 Sep 2020 20:54:46 +0300
parents b4ef79ef1c23
children bed310672f39
comparison
equal deleted inserted replaced
8561:b4ef79ef1c23 8562:b31c02454539
34 */ 34 */
35 #define NGX_QUIC_MAX_BUFFERED 65535 35 #define NGX_QUIC_MAX_BUFFERED 65535
36 36
37 #define NGX_QUIC_STREAM_GONE (void *) -1 37 #define NGX_QUIC_STREAM_GONE (void *) -1
38 38
39 /*
40 * Endpoints MUST discard packets that are too small to be valid QUIC
41 * packets. With the set of AEAD functions defined in [QUIC-TLS],
42 * packets that are smaller than 21 bytes are never valid.
43 */
44 #define NGX_QUIC_MIN_PKT_LEN 21
45
46 #define NGX_QUIC_MIN_SR_PACKET 43 /* 5 random + 16 srt + 22 padding */
47 #define NGX_QUIC_MAX_SR_PACKET 1200
48
39 49
40 typedef struct { 50 typedef struct {
41 ngx_rbtree_t tree; 51 ngx_rbtree_t tree;
42 ngx_rbtree_node_t sentinel; 52 ngx_rbtree_node_t sentinel;
43 53
152 typedef struct { 162 typedef struct {
153 ngx_queue_t queue; 163 ngx_queue_t queue;
154 uint64_t seqnum; 164 uint64_t seqnum;
155 size_t len; 165 size_t len;
156 u_char id[NGX_QUIC_CID_LEN_MAX]; 166 u_char id[NGX_QUIC_CID_LEN_MAX];
157 u_char sr_token[NGX_QUIC_SRT_LEN]; 167 u_char sr_token[NGX_QUIC_SR_TOKEN_LEN];
158 } ngx_quic_client_id_t; 168 } ngx_quic_client_id_t;
159 169
160 170
161 typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c, 171 typedef ngx_int_t (*ngx_quic_frame_handler_pt)(ngx_connection_t *c,
162 ngx_quic_frame_t *frame, void *data); 172 ngx_quic_frame_t *frame, void *data);
182 enum ssl_encryption_level_t level, uint8_t alert); 192 enum ssl_encryption_level_t level, uint8_t alert);
183 193
184 194
185 static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c, 195 static ngx_quic_connection_t *ngx_quic_new_connection(ngx_connection_t *c,
186 ngx_ssl_t *ssl, ngx_quic_conf_t *conf, ngx_quic_header_t *pkt); 196 ngx_ssl_t *ssl, ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
197 static ngx_int_t ngx_quic_send_stateless_reset(ngx_connection_t *c,
198 ngx_quic_conf_t *conf, ngx_quic_header_t *pkt);
199 static ngx_int_t ngx_quic_process_stateless_reset(ngx_connection_t *c,
200 ngx_quic_header_t *pkt);
187 static ngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c, 201 static ngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c,
188 ngx_quic_header_t *inpkt); 202 ngx_quic_header_t *inpkt);
189 static ngx_int_t ngx_quic_new_dcid(ngx_connection_t *c, 203 static ngx_int_t ngx_quic_new_dcid(ngx_connection_t *c,
190 ngx_quic_connection_t *qc, ngx_str_t *odcid); 204 ngx_quic_connection_t *qc, ngx_str_t *odcid);
191 static ngx_int_t ngx_quic_send_retry(ngx_connection_t *c); 205 static ngx_int_t ngx_quic_send_retry(ngx_connection_t *c);
756 return qc; 770 return qc;
757 } 771 }
758 772
759 773
760 static ngx_int_t 774 static ngx_int_t
775 ngx_quic_send_stateless_reset(ngx_connection_t *c, ngx_quic_conf_t *conf,
776 ngx_quic_header_t *pkt)
777 {
778 u_char *token;
779 size_t len, max;
780 uint16_t rndbytes;
781 u_char buf[NGX_QUIC_MAX_SR_PACKET];
782
783 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
784 "quic handle stateless reset output");
785
786 if (conf->sr_token_key.len == 0) {
787 return NGX_DECLINED;
788 }
789
790 if (pkt->len <= NGX_QUIC_MIN_PKT_LEN) {
791 return NGX_DECLINED;
792 }
793
794 if (pkt->len <= NGX_QUIC_MIN_SR_PACKET) {
795 len = pkt->len - 1;
796
797 } else {
798 max = ngx_min(NGX_QUIC_MAX_SR_PACKET, pkt->len * 3);
799
800 if (RAND_bytes((u_char *) &rndbytes, sizeof(rndbytes)) != 1) {
801 return NGX_ERROR;
802 }
803
804 len = (rndbytes % (max - NGX_QUIC_MIN_SR_PACKET + 1))
805 + NGX_QUIC_MIN_SR_PACKET;
806 }
807
808 if (RAND_bytes(buf, len - NGX_QUIC_SR_TOKEN_LEN) != 1) {
809 return NGX_ERROR;
810 }
811
812 buf[0] &= ~NGX_QUIC_PKT_LONG;
813 buf[0] |= NGX_QUIC_PKT_FIXED_BIT;
814
815 token = &buf[len - NGX_QUIC_SR_TOKEN_LEN];
816
817 if (ngx_quic_new_sr_token(c, &pkt->dcid, &conf->sr_token_key, token)
818 != NGX_OK)
819 {
820 return NGX_ERROR;
821 }
822
823 (void) c->send(c, buf, len);
824
825 return NGX_DECLINED;
826 }
827
828
829 static ngx_int_t
830 ngx_quic_process_stateless_reset(ngx_connection_t *c, ngx_quic_header_t *pkt)
831 {
832 u_char *tail, ch;
833 ngx_uint_t i;
834 ngx_queue_t *q;
835 ngx_quic_client_id_t *cid;
836 ngx_quic_connection_t *qc;
837
838 qc = c->quic;
839
840 /* A stateless reset uses an entire UDP datagram */
841 if (pkt->raw->start != pkt->data) {
842 return NGX_DECLINED;
843 }
844
845 tail = pkt->raw->last - NGX_QUIC_SR_TOKEN_LEN;
846
847 for (q = ngx_queue_head(&qc->client_ids);
848 q != ngx_queue_sentinel(&qc->client_ids);
849 q = ngx_queue_next(q))
850 {
851 cid = ngx_queue_data(q, ngx_quic_client_id_t, queue);
852
853 if (cid->seqnum == 0) {
854 /* no stateless reset token in initial connection id */
855 continue;
856 }
857
858 /* constant time comparison */
859
860 for (ch = 0, i = 0; i < NGX_QUIC_SR_TOKEN_LEN; i++) {
861 ch |= tail[i] ^ cid->sr_token[i];
862 }
863
864 if (ch == 0) {
865 return NGX_OK;
866 }
867 }
868
869 return NGX_DECLINED;
870 }
871
872
873 static ngx_int_t
761 ngx_quic_negotiate_version(ngx_connection_t *c, ngx_quic_header_t *inpkt) 874 ngx_quic_negotiate_version(ngx_connection_t *c, ngx_quic_header_t *inpkt)
762 { 875 {
763 size_t len; 876 size_t len;
764 ngx_quic_header_t pkt; 877 ngx_quic_header_t pkt;
765 878
1113 #ifdef SSL_READ_EARLY_DATA_SUCCESS 1226 #ifdef SSL_READ_EARLY_DATA_SUCCESS
1114 if (SSL_CTX_get_max_early_data(qc->ssl->ctx)) { 1227 if (SSL_CTX_get_max_early_data(qc->ssl->ctx)) {
1115 SSL_set_quic_early_data_enabled(ssl_conn, 1); 1228 SSL_set_quic_early_data_enabled(ssl_conn, 1);
1116 } 1229 }
1117 #endif 1230 #endif
1231
1232 if (qc->conf->sr_token_key.len) {
1233 qc->tp.sr_enabled = 1;
1234
1235 if (ngx_quic_new_sr_token(c, &qc->dcid, &qc->conf->sr_token_key,
1236 qc->tp.sr_token)
1237 != NGX_OK)
1238 {
1239 return NGX_ERROR;
1240 }
1241
1242 ngx_quic_hexdump(c->log, "quic stateless reset token",
1243 qc->tp.sr_token, NGX_QUIC_SR_TOKEN_LEN);
1244 }
1118 1245
1119 len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp, &clen); 1246 len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp, &clen);
1120 /* always succeeds */ 1247 /* always succeeds */
1121 1248
1122 p = ngx_pnalloc(c->pool, len); 1249 p = ngx_pnalloc(c->pool, len);
1576 "quic unsupported version: 0x%xD", pkt->version); 1703 "quic unsupported version: 0x%xD", pkt->version);
1577 return NGX_DECLINED; 1704 return NGX_DECLINED;
1578 } 1705 }
1579 1706
1580 if (ngx_quic_check_peer(qc, pkt) != NGX_OK) { 1707 if (ngx_quic_check_peer(qc, pkt) != NGX_OK) {
1708
1709 if (pkt->level == ssl_encryption_application) {
1710 if (ngx_quic_process_stateless_reset(c, pkt) == NGX_OK) {
1711 ngx_log_error(NGX_LOG_INFO, c->log, 0,
1712 "quic stateless reset packet detected");
1713
1714 qc->draining = 1;
1715 ngx_quic_close_connection(c, NGX_OK);
1716
1717 return NGX_OK;
1718 }
1719
1720 return ngx_quic_send_stateless_reset(c, qc->conf, pkt);
1721 }
1722
1581 return NGX_DECLINED; 1723 return NGX_DECLINED;
1582 } 1724 }
1583 1725
1584 if (qc->in_retry) { 1726 if (qc->in_retry) {
1585 1727
1653 if (ngx_quic_init_secrets(c) != NGX_OK) { 1795 if (ngx_quic_init_secrets(c) != NGX_OK) {
1654 return NGX_ERROR; 1796 return NGX_ERROR;
1655 } 1797 }
1656 1798
1657 } else if (pkt->level == ssl_encryption_application) { 1799 } else if (pkt->level == ssl_encryption_application) {
1658 return NGX_DECLINED; 1800 return ngx_quic_send_stateless_reset(c, conf, pkt);
1659 1801
1660 } else { 1802 } else {
1661 return NGX_ERROR; 1803 return NGX_ERROR;
1662 } 1804 }
1663 } 1805 }
3335 * same NEW_CONNECTION_ID frame to be received multiple times 3477 * same NEW_CONNECTION_ID frame to be received multiple times
3336 */ 3478 */
3337 3479
3338 if (cid->len != f->len 3480 if (cid->len != f->len
3339 || ngx_strncmp(cid->id, f->cid, f->len) != 0 3481 || ngx_strncmp(cid->id, f->cid, f->len) != 0
3340 || ngx_strncmp(cid->sr_token, f->srt, NGX_QUIC_SRT_LEN) != 0) 3482 || ngx_strncmp(cid->sr_token, f->srt, NGX_QUIC_SR_TOKEN_LEN) != 0)
3341 { 3483 {
3342 /* 3484 /*
3343 * ..a sequence number is used for different connection IDs, 3485 * ..a sequence number is used for different connection IDs,
3344 * the endpoint MAY treat that receipt as a connection error 3486 * the endpoint MAY treat that receipt as a connection error
3345 * of type PROTOCOL_VIOLATION. 3487 * of type PROTOCOL_VIOLATION.
3358 3500
3359 cid->seqnum = f->seqnum; 3501 cid->seqnum = f->seqnum;
3360 cid->len = f->len; 3502 cid->len = f->len;
3361 ngx_memcpy(cid->id, f->cid, f->len); 3503 ngx_memcpy(cid->id, f->cid, f->len);
3362 3504
3363 ngx_memcpy(cid->sr_token, f->srt, NGX_QUIC_SRT_LEN); 3505 ngx_memcpy(cid->sr_token, f->srt, NGX_QUIC_SR_TOKEN_LEN);
3364 3506
3365 ngx_queue_insert_tail(&qc->client_ids, &cid->queue); 3507 ngx_queue_insert_tail(&qc->client_ids, &cid->queue);
3366 qc->nclient_ids++; 3508 qc->nclient_ids++;
3367 3509
3368 /* always use latest available connection id */ 3510 /* always use latest available connection id */