Configurable transport parameters.

- integer parameters can be configured using the following directives:

    quic_max_idle_timeout
    quic_max_ack_delay
    quic_max_packet_size
    quic_initial_max_data
    quic_initial_max_stream_data_bidi_local
    quic_initial_max_stream_data_bidi_remote
    quic_initial_max_stream_data_uni
    quic_initial_max_streams_bidi
    quic_initial_max_streams_uni
    quic_ack_delay_exponent
    quic_active_migration
    quic_active_connection_id_limit

 - only following parameters are actually sent:

    active_connection_id_limit
    initial_max_streams_uni
    initial_max_streams_bidi
    initial_max_stream_data_bidi_local
    initial_max_stream_data_bidi_remote
    initial_max_stream_data_uni

 (other parameters are to be added into ngx_quic_create_transport_params()
  function as needed, should be easy now)

 - draft 24 and draft 27 are now supported
   (at compile-time using quic_version macro)
This commit is contained in:
Vladimir Homutov
2020-03-20 13:47:44 +03:00
parent 5f9b188c21
commit 30de0ca52d
7 changed files with 361 additions and 23 deletions

View File

@@ -32,6 +32,8 @@ struct ngx_quic_connection_s {
ngx_str_t dcid;
ngx_str_t token;
ngx_quic_tp_t tp;
/* current packet numbers for each namespace */
ngx_uint_t initial_pn;
ngx_uint_t handshake_pn;
@@ -67,7 +69,7 @@ static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn,
static ngx_int_t ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
ngx_quic_header_t *pkt);
ngx_quic_tp_t *tp, ngx_quic_header_t *pkt);
static ngx_int_t ngx_quic_init_connection(ngx_connection_t *c);
static void ngx_quic_handshake_handler(ngx_event_t *rev);
static void ngx_quic_close_connection(ngx_connection_t *c);
@@ -283,8 +285,8 @@ ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level,
void
ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout,
ngx_connection_handler_pt handler)
ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
ngx_msec_t timeout, ngx_connection_handler_pt handler)
{
ngx_buf_t *b;
ngx_quic_header_t pkt;
@@ -302,7 +304,7 @@ ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout,
pkt.data = b->start;
pkt.len = b->last - b->start;
if (ngx_quic_new_connection(c, ssl, &pkt) != NGX_OK) {
if (ngx_quic_new_connection(c, ssl, tp, &pkt) != NGX_OK) {
ngx_quic_close_connection(c);
return;
}
@@ -320,7 +322,7 @@ ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout,
static ngx_int_t
ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
ngx_quic_header_t *pkt)
{
ngx_quic_connection_t *qc;
@@ -354,6 +356,7 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_ssl_t *ssl,
c->quic = qc;
qc->ssl = ssl;
qc->tp = *tp;
qc->dcid.len = pkt->dcid.len;
qc->dcid.data = ngx_pnalloc(c->pool, pkt->dcid.len);
@@ -402,19 +405,11 @@ static ngx_int_t
ngx_quic_init_connection(ngx_connection_t *c)
{
int n, sslerr;
u_char *p;
ssize_t len;
ngx_ssl_conn_t *ssl_conn;
ngx_quic_connection_t *qc;
static const uint8_t params[] =
"\x00\x29" /* parameters length: 41 bytes */
"\x00\x0e\x00\x01\x05" /* active connection id limit: 5 */
"\x00\x04\x00\x04\x80\x98\x96\x80" /* initial max data = 10000000 */
"\x00\x09\x00\x01\x03" /* initial max streams uni: 3 */
"\x00\x08\x00\x01\x10" /* initial max streams bidi: 16 */
"\x00\x05\x00\x02\x40\xff" /* initial max stream bidi local: 255 */
"\x00\x06\x00\x02\x40\xff" /* initial max stream bidi remote: 255 */
"\x00\x07\x00\x02\x40\xff"; /* initial max stream data uni: 255 */
qc = c->quic;
if (ngx_ssl_create_connection(qc->ssl, c, NGX_SSL_BUFFER) != NGX_OK) {
@@ -429,7 +424,20 @@ ngx_quic_init_connection(ngx_connection_t *c)
return NGX_ERROR;
}
if (SSL_set_quic_transport_params(ssl_conn, params, sizeof(params) - 1) == 0) {
len = ngx_quic_create_transport_params(NULL, NULL, &qc->tp);
/* always succeeds */
p = ngx_pnalloc(c->pool, len);
if (p == NULL) {
return NGX_ERROR;
}
len = ngx_quic_create_transport_params(p, p + len, &qc->tp);
if (len < 0) {
return NGX_ERROR;
}
if (SSL_set_quic_transport_params(ssl_conn, p, len) == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"SSL_set_quic_transport_params() failed");
return NGX_ERROR;

View File

@@ -15,6 +15,29 @@
//#define quic_version 0xff00001b /* draft-27 (FFN 76) */
typedef struct {
/* configurable */
ngx_msec_t max_idle_timeout;
ngx_msec_t max_ack_delay;
ngx_uint_t max_packet_size;
ngx_uint_t initial_max_data;
ngx_uint_t initial_max_stream_data_bidi_local;
ngx_uint_t initial_max_stream_data_bidi_remote;
ngx_uint_t initial_max_stream_data_uni;
ngx_uint_t initial_max_streams_bidi;
ngx_uint_t initial_max_streams_uni;
ngx_uint_t ack_delay_exponent;
ngx_uint_t disable_active_migration;
ngx_uint_t active_connection_id_limit;
/* TODO */
ngx_uint_t original_connection_id;
u_char stateless_reset_token[16];
void *preferred_address;
} ngx_quic_tp_t;
struct ngx_quic_stream_s {
uint64_t id;
ngx_uint_t unidirectional:1;
@@ -25,8 +48,8 @@ struct ngx_quic_stream_s {
void ngx_quic_init_ssl_methods(SSL_CTX* ctx);
void ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_msec_t timeout,
ngx_connection_handler_pt handler);
void ngx_quic_run(ngx_connection_t *c, ngx_ssl_t *ssl, ngx_quic_tp_t *tp,
ngx_msec_t timeout, ngx_connection_handler_pt handler);
ngx_connection_t *ngx_quic_create_uni_stream(ngx_connection_t *c);

View File

@@ -87,6 +87,7 @@ static char *ngx_quic_errors[] = {
"INVALID_TOKEN",
"",
"CRYPTO_BUFFER_EXCEEDED",
"",
"CRYPTO_ERROR",
};
@@ -639,11 +640,11 @@ ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end,
return NGX_ERROR;
}
if (f->u.close.error_code > NGX_QUIC_ERR_LAST) {
if (f->u.close.error_code >= NGX_QUIC_ERR_LAST) {
ngx_log_error(NGX_LOG_ERR, pkt->log, 0,
"unkown error code: %ui, truncated",
f->u.close.error_code);
f->u.close.error_code = NGX_QUIC_ERR_LAST;
f->u.close.error_code = NGX_QUIC_ERR_LAST - 1;
}
ngx_log_debug4(NGX_LOG_DEBUG_EVENT, pkt->log, 0,
@@ -963,6 +964,105 @@ ngx_quic_create_max_streams(u_char *p, ngx_quic_max_streams_frame_t *ms)
}
ssize_t
ngx_quic_create_transport_params(u_char *pos, u_char *end, ngx_quic_tp_t *tp)
{
u_char *p;
size_t len;
#if (quic_version < 0xff00001b)
/* older drafts with static transport parameters encoding */
#define ngx_quic_tp_len(id, value) \
4 + ngx_quic_varint_len(value)
#define ngx_quic_tp_vint(id, value) \
do { \
p = ngx_quic_write_uint16(p, id); \
p = ngx_quic_write_uint16(p, ngx_quic_varint_len(value)); \
ngx_quic_build_int(&p, value); \
} while (0)
#else
/* recent drafts with variable integer transport parameters encoding */
#define ngx_quic_tp_len(id, value) \
ngx_quic_varint_len(id) \
+ ngx_quic_varint_len(value) \
+ ngx_quic_varint_len(ngx_quic_varint_len(value))
#define ngx_quic_tp_vint(id, value) \
do { \
ngx_quic_build_int(&p, id); \
ngx_quic_build_int(&p, ngx_quic_varint_len(value)); \
ngx_quic_build_int(&p, value); \
} while (0)
#endif
p = pos;
len = ngx_quic_tp_len(NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT,
tp->active_connection_id_limit);
len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_DATA,tp->initial_max_data);
len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI,
tp->initial_max_streams_uni);
len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI,
tp->initial_max_streams_bidi);
len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
tp->initial_max_stream_data_bidi_local);
len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
tp->initial_max_stream_data_bidi_remote);
len += ngx_quic_tp_len(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI,
tp->initial_max_stream_data_uni);
if (pos == NULL) {
#if (quic_version < 0xff00001b)
len += ngx_quic_varint_len(len);
#endif
return len;
}
#if (quic_version < 0xff00001b)
/* TLS extension length */
p = ngx_quic_write_uint16(p, len);
#endif
ngx_quic_tp_vint(NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT,
tp->active_connection_id_limit);
ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_DATA,
tp->initial_max_data);
ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI,
tp->initial_max_streams_uni);
ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI,
tp->initial_max_streams_bidi);
ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL,
tp->initial_max_stream_data_bidi_local);
ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE,
tp->initial_max_stream_data_bidi_remote);
ngx_quic_tp_vint(NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI,
tp->initial_max_stream_data_uni);
ngx_quic_hexdump0(ngx_cycle->log, "transport parameters", pos, p - pos);
return p - pos;
}
static size_t
ngx_quic_create_close(u_char *p, ngx_quic_close_frame_t *cl)
{

View File

@@ -65,10 +65,28 @@
#define NGX_QUIC_ERR_INVALID_TOKEN 0x0B
/* 0xC is not defined */
#define NGX_QUIC_ERR_CRYPTO_BUFFER_EXCEEDED 0x0D
/* 0xE is not defined */
#define NGX_QUIC_ERR_CRYPTO_ERROR 0x10
#define NGX_QUIC_ERR_LAST NGX_QUIC_ERR_CRYPTO_ERROR
/* Transport parameters */
#define NGX_QUIC_TP_ORIGINAL_CONNECTION_ID 0x00
#define NGX_QUIC_TP_MAX_IDLE_TIMEOUT 0x01
#define NGX_QUIC_TP_STATELESS_RESET_TOKEN 0x02
#define NGX_QUIC_TP_MAX_PACKET_SIZE 0x03
#define NGX_QUIC_TP_INITIAL_MAX_DATA 0x04
#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL 0x05
#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE 0x06
#define NGX_QUIC_TP_INITIAL_MAX_STREAM_DATA_UNI 0x07
#define NGX_QUIC_TP_INITIAL_MAX_STREAMS_BIDI 0x08
#define NGX_QUIC_TP_INITIAL_MAX_STREAMS_UNI 0x09
#define NGX_QUIC_TP_ACK_DELAY_EXPONENT 0x0a
#define NGX_QUIC_TP_MAX_ACK_DELAY 0x0b
#define NGX_QUIC_TP_DISABLE_ACTIVE_MIGRATION 0x0c
#define NGX_QUIC_TP_PREFERRED_ADDRESS 0x0d
#define NGX_QUIC_TP_ACTIVE_CONNECTION_ID_LIMIT 0x0e
typedef struct {
ngx_uint_t pn;
@@ -208,4 +226,7 @@ ssize_t ngx_quic_parse_frame(ngx_quic_header_t *pkt, u_char *start, u_char *end,
ngx_quic_frame_t *frame);
ssize_t ngx_quic_create_frame(u_char *p, u_char *end, ngx_quic_frame_t *f);
ssize_t ngx_quic_create_transport_params(u_char *p, u_char *end,
ngx_quic_tp_t *tp);
#endif /* _NGX_EVENT_QUIC_WIRE_H_INCLUDED_ */

View File

@@ -341,11 +341,14 @@ ngx_http_init_connection(ngx_connection_t *c)
#if (NGX_HTTP_V3)
if (hc->quic) {
ngx_http_v3_srv_conf_t *v3cf;
ngx_http_ssl_srv_conf_t *sscf;
v3cf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module);
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
ngx_quic_run(c, &sscf->ssl, c->listening->post_accept_timeout,
ngx_quic_run(c, &sscf->ssl, &v3cf->quic,
c->listening->post_accept_timeout,
ngx_http_quic_stream_handler);
return;
}

View File

@@ -39,6 +39,11 @@
#define NGX_HTTP_V3_PARAM_BLOCKED_STREAMS 0x07
typedef struct {
ngx_quic_tp_t quic;
} ngx_http_v3_srv_conf_t;
typedef struct {
ngx_http_connection_t hc;

View File

@@ -11,10 +11,100 @@
static ngx_command_t ngx_http_v3_commands[] = {
{ ngx_string("quic_max_idle_timeout"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.max_idle_timeout),
NULL },
{ ngx_string("quic_max_ack_delay"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.max_ack_delay),
NULL },
{ ngx_string("quic_max_packet_size"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.max_packet_size),
NULL },
{ ngx_string("quic_initial_max_data"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_data),
NULL },
{ ngx_string("quic_initial_max_stream_data_bidi_local"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_stream_data_bidi_local),
NULL },
{ ngx_string("quic_initial_max_stream_data_bidi_remote"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_stream_data_bidi_remote),
NULL },
{ ngx_string("quic_initial_max_stream_data_uni"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_stream_data_uni),
NULL },
{ ngx_string("quic_initial_max_streams_bidi"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_streams_bidi),
NULL },
{ ngx_string("quic_initial_max_streams_uni"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.initial_max_streams_uni),
NULL },
{ ngx_string("quic_ack_delay_exponent"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.ack_delay_exponent),
NULL },
{ ngx_string("quic_active_migration"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.disable_active_migration),
NULL },
{ ngx_string("quic_active_connection_id_limit"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_SRV_CONF_OFFSET,
offsetof(ngx_http_v3_srv_conf_t, quic.active_connection_id_limit),
NULL },
ngx_null_command
};
static void *ngx_http_v3_create_srv_conf(ngx_conf_t *cf);
static char *ngx_http_v3_merge_srv_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_http_module_t ngx_http_v3_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
@@ -22,8 +112,8 @@ static ngx_http_module_t ngx_http_v3_module_ctx = {
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_v3_create_srv_conf, /* create server configuration */
ngx_http_v3_merge_srv_conf, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
@@ -44,3 +134,91 @@ ngx_module_t ngx_http_v3_module = {
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_http_v3_create_srv_conf(ngx_conf_t *cf)
{
ngx_http_v3_srv_conf_t *v3cf;
v3cf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v3_srv_conf_t));
if (v3cf == NULL) {
return NULL;
}
/*
* set by ngx_pcalloc():
* v3cf->quic.original_connection_id = 0;
* v3cf->quic.stateless_reset_token = { 0 }
* conf->quic.preferred_address = NULL
*/
v3cf->quic.max_idle_timeout = NGX_CONF_UNSET_MSEC;
v3cf->quic.max_ack_delay = NGX_CONF_UNSET_MSEC;
v3cf->quic.max_packet_size = NGX_CONF_UNSET_UINT;
v3cf->quic.initial_max_data = NGX_CONF_UNSET_UINT;
v3cf->quic.initial_max_stream_data_bidi_local = NGX_CONF_UNSET_UINT;
v3cf->quic.initial_max_stream_data_bidi_remote = NGX_CONF_UNSET_UINT;
v3cf->quic.initial_max_stream_data_uni = NGX_CONF_UNSET_UINT;
v3cf->quic.initial_max_streams_bidi = NGX_CONF_UNSET_UINT;
v3cf->quic.initial_max_streams_uni = NGX_CONF_UNSET_UINT;
v3cf->quic.ack_delay_exponent = NGX_CONF_UNSET_UINT;
v3cf->quic.disable_active_migration = NGX_CONF_UNSET_UINT;
v3cf->quic.active_connection_id_limit = NGX_CONF_UNSET_UINT;
return v3cf;
}
static char *
ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_v3_srv_conf_t *prev = parent;
ngx_http_v3_srv_conf_t *conf = child;
ngx_conf_merge_msec_value(conf->quic.max_idle_timeout,
prev->quic.max_idle_timeout, 10000);
// > 2 ^ 14 is invalid
ngx_conf_merge_msec_value(conf->quic.max_ack_delay,
prev->quic.max_ack_delay, 25);
// < 1200 is invalid
ngx_conf_merge_uint_value(conf->quic.max_packet_size,
prev->quic.max_packet_size, 65527);
ngx_conf_merge_uint_value(conf->quic.initial_max_data,
prev->quic.initial_max_data, 10000000);
ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_bidi_local,
prev->quic.initial_max_stream_data_bidi_local,
255);
ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_bidi_remote,
prev->quic.initial_max_stream_data_bidi_remote,
255);
ngx_conf_merge_uint_value(conf->quic.initial_max_stream_data_uni,
prev->quic.initial_max_stream_data_uni, 255);
ngx_conf_merge_uint_value(conf->quic.initial_max_streams_bidi,
prev->quic.initial_max_streams_bidi, 16);
ngx_conf_merge_uint_value(conf->quic.initial_max_streams_uni,
prev->quic.initial_max_streams_uni, 16);
// > 20 is invalid
ngx_conf_merge_uint_value(conf->quic.ack_delay_exponent,
prev->quic.ack_delay_exponent, 3);
ngx_conf_merge_uint_value(conf->quic.disable_active_migration,
prev->quic.disable_active_migration, 1);
// < 2 is invalid
ngx_conf_merge_uint_value(conf->quic.active_connection_id_limit,
prev->quic.active_connection_id_limit, 2);
return NGX_CONF_OK;
}