Mercurial > hg > nginx-quic
comparison src/event/ngx_event_quic.c @ 8100: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
8099:b4ef79ef1c23 | 8100: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 */ |