# HG changeset patch # User Vladimir Homutov # Date 1597932664 -10800 # Node ID fc89d02bdca2a678fa59bc945971c281e71a60c4 # Parent a748095bf94e9c98f4d17407b786d4a8330b25fe QUIC: added version negotiation support. If a client attemtps to start a new connection with unsupported version, a version negotiation packet is sent that contains a list of supported versions (currently this is a single version, selected at compile time). diff --git a/src/event/ngx_event_quic.c b/src/event/ngx_event_quic.c --- a/src/event/ngx_event_quic.c +++ b/src/event/ngx_event_quic.c @@ -169,6 +169,8 @@ static int ngx_quic_send_alert(ngx_ssl_c static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_conf_t *conf, ngx_quic_header_t *pkt); +static ngx_int_t ngx_quic_negotiate_version(ngx_connection_t *c, + ngx_quic_header_t *inpkt); static ngx_int_t ngx_quic_new_dcid(ngx_connection_t *c, ngx_str_t *odcid); static ngx_int_t ngx_quic_retry(ngx_connection_t *c); static ngx_int_t ngx_quic_new_token(ngx_connection_t *c, ngx_str_t *token); @@ -659,6 +661,10 @@ ngx_quic_new_connection(ngx_connection_t return rc; } + if (pkt->version != NGX_QUIC_VERSION) { + return ngx_quic_negotiate_version(c, pkt); + } + if (!ngx_quic_pkt_in(pkt->flags)) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic invalid initial packet: 0x%xd", pkt->flags); @@ -824,6 +830,35 @@ ngx_quic_new_connection(ngx_connection_t static ngx_int_t +ngx_quic_negotiate_version(ngx_connection_t *c, ngx_quic_header_t *inpkt) +{ + size_t len; + ngx_quic_header_t pkt; + + /* buffer size is calculated assuming a single supported version */ + static u_char buf[NGX_QUIC_MAX_LONG_HEADER + sizeof(uint32_t)]; + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sending version negotiation packet"); + + pkt.log = c->log; + pkt.flags = NGX_QUIC_PKT_LONG | NGX_QUIC_PKT_FIXED_BIT; + pkt.dcid = inpkt->scid; + pkt.scid = inpkt->dcid; + + len = ngx_quic_create_version_negotiation(&pkt, buf); + +#ifdef NGX_QUIC_DEBUG_PACKETS + ngx_quic_hexdump(c->log, "quic vnego packet to send", buf, len); +#endif + + (void) c->send(c, buf, len); + + return NGX_ERROR; +} + + +static ngx_int_t ngx_quic_new_dcid(ngx_connection_t *c, ngx_str_t *odcid) { uint8_t len; @@ -1628,6 +1663,12 @@ ngx_quic_retry_input(ngx_connection_t *c return rc; } + if (pkt->version != NGX_QUIC_VERSION) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "quic unsupported version: 0x%xD", pkt->version); + return NGX_ERROR; + } + if (ngx_quic_pkt_zrtt(pkt->flags)) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic discard inflight 0-RTT packet"); @@ -1715,6 +1756,12 @@ ngx_quic_initial_input(ngx_connection_t return rc; } + if (pkt->version != NGX_QUIC_VERSION) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "quic unsupported version: 0x%xD", pkt->version); + return NGX_ERROR; + } + if (ngx_quic_parse_initial_header(pkt) != NGX_OK) { return NGX_ERROR; } @@ -1765,6 +1812,12 @@ ngx_quic_handshake_input(ngx_connection_ return rc; } + if (pkt->version != NGX_QUIC_VERSION) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "quic unsupported version: 0x%xD", pkt->version); + return NGX_ERROR; + } + if (ngx_quic_check_peer(qc, pkt) != NGX_OK) { return NGX_ERROR; } @@ -1825,6 +1878,12 @@ ngx_quic_early_input(ngx_connection_t *c return rc; } + if (pkt->version != NGX_QUIC_VERSION) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "quic unsupported version: 0x%xD", pkt->version); + return NGX_ERROR; + } + if (ngx_quic_check_peer(qc, pkt) != NGX_OK) { return NGX_ERROR; } diff --git a/src/event/ngx_event_quic_transport.c b/src/event/ngx_event_quic_transport.c --- a/src/event/ngx_event_quic_transport.c +++ b/src/event/ngx_event_quic_transport.c @@ -88,6 +88,15 @@ static ngx_int_t ngx_quic_parse_transpor uint16_t id, ngx_quic_tp_t *dst); +/* currently only single version (selected at compile-time) is supported */ +uint32_t ngx_quic_versions[] = { + NGX_QUIC_VERSION +}; + +#define NGX_QUIC_NVERSIONS \ + (sizeof(ngx_quic_versions) / sizeof(ngx_quic_versions[0])) + + /* literal errors indexed by corresponding value */ static char *ngx_quic_errors[] = { "NO_ERROR", @@ -232,8 +241,8 @@ ngx_quic_error_text(uint64_t error_code) ngx_int_t ngx_quic_parse_long_header(ngx_quic_header_t *pkt) { - u_char *p, *end; - uint8_t idlen; + u_char *p, *end; + uint8_t idlen; p = pkt->data; end = pkt->data + pkt->len; @@ -270,12 +279,6 @@ ngx_quic_parse_long_header(ngx_quic_head return NGX_DECLINED; } - if (pkt->version != NGX_QUIC_VERSION) { - ngx_log_error(NGX_LOG_INFO, pkt->log, 0, - "quic unsupported version: 0x%xD", pkt->version); - return NGX_ERROR; - } - p = ngx_quic_read_uint8(p, end, &idlen); if (p == NULL) { ngx_log_error(NGX_LOG_INFO, pkt->log, 0, @@ -327,6 +330,36 @@ ngx_quic_parse_long_header(ngx_quic_head size_t +ngx_quic_create_version_negotiation(ngx_quic_header_t *pkt, u_char *out) +{ + u_char *p, *start; + ngx_uint_t i; + + p = start = out; + + *p++ = pkt->flags; + + /* + * The Version field of a Version Negotiation packet + * MUST be set to 0x00000000 + */ + p = ngx_quic_write_uint32(p, 0); + + *p++ = pkt->dcid.len; + p = ngx_cpymem(p, pkt->dcid.data, pkt->dcid.len); + + *p++ = pkt->scid.len; + p = ngx_cpymem(p, pkt->scid.data, pkt->scid.len); + + for (i = 0; i < NGX_QUIC_NVERSIONS; i++) { + p = ngx_quic_write_uint32(p, ngx_quic_versions[i]); + } + + return p - start; +} + + +size_t ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out, size_t pkt_len, u_char **pnp) { diff --git a/src/event/ngx_event_quic_transport.h b/src/event/ngx_event_quic_transport.h --- a/src/event/ngx_event_quic_transport.h +++ b/src/event/ngx_event_quic_transport.h @@ -317,6 +317,8 @@ typedef struct { u_char *ngx_quic_error_text(uint64_t error_code); +size_t ngx_quic_create_version_negotiation(ngx_quic_header_t *pkt, u_char *out); + ngx_int_t ngx_quic_parse_long_header(ngx_quic_header_t *pkt); size_t ngx_quic_create_long_header(ngx_quic_header_t *pkt, u_char *out, size_t pkt_len, u_char **pnp);