Release 2.23.0

- [FEATURE] IETF Client 0-RTT support.
- [BUGFIX] Do not schedule MTU probe on first tick.
- [BUGFIX] Parsing DATAGRAM frame.
- [BUGFIX] If push promise fails, do not invoke hset destructor.
- [BUGFIX] Client: When connections are IDed by port number, check DCID.
  Fixes issue #176.
- Revert the 2.22.1 lsquic_is_valid_hs_packet change.  All that was
  necessary is a change to the way we call it in lsquic_engine.  No
  change to the function itself is required.
This commit is contained in:
Dmitri Tikhonov 2020-10-13 08:20:25 -04:00
parent f3d781aa59
commit 04f8f447b2
24 changed files with 808 additions and 309 deletions

View File

@ -1,3 +1,15 @@
2020-10-13
- 2.23.0
- [FEATURE] IETF Client 0-RTT support.
- [BUGFIX] Do not schedule MTU probe on first tick.
- [BUGFIX] Parsing DATAGRAM frame.
- [BUGFIX] If push promise fails, do not invoke hset destructor.
- [BUGFIX] Client: When connections are IDed by port number, check DCID.
Fixes issue #176.
- Revert the 2.22.1 lsquic_is_valid_hs_packet change. All that was
necessary is a change to the way we call it in lsquic_engine. No
change to the function itself is required.
2020-10-08
- 2.22.1
- [BUGFIX] Function that checks validity of handshake packets.

View File

@ -1506,8 +1506,14 @@ idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
headers.count = sizeof(header_arr) / sizeof(header_arr[0]);
req = new_req(GET, push_path->path, st_h->req->authority_str);
if (req)
(void) lsquic_conn_push_stream(lsquic_stream_conn(stream),
req, stream, &headers);
{
if (0 != lsquic_conn_push_stream(lsquic_stream_conn(stream),
req, stream, &headers))
{
LSQ_WARN("stream push failed");
interop_server_hset_destroy(req);
}
}
else
LSQ_WARN("cannot allocate req for push");
free(push_path);

View File

@ -93,7 +93,11 @@ load_cert (struct lsquic_hash *certs, const char *optarg)
SSL_CTX_set_max_proto_version(cert->ce_ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_default_verify_paths(cert->ce_ssl_ctx);
SSL_CTX_set_alpn_select_cb(cert->ce_ssl_ctx, select_alpn, NULL);
SSL_CTX_set_early_data_enabled(cert->ce_ssl_ctx, 1); /* XXX */
{
const char *const s = getenv("LSQUIC_ENABLE_EARLY_DATA");
if (!s || atoi(s))
SSL_CTX_set_early_data_enabled(cert->ce_ssl_ctx, 1); /* XXX */
}
if (1 != SSL_CTX_use_certificate_chain_file(cert->ce_ssl_ctx, cert_file))
{
LSQ_ERROR("SSL_CTX_use_certificate_chain_file failed: %s", cert_file);

View File

@ -1993,6 +1993,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
settings->es_qpack_dec_max_blocked = atoi(val);
return 0;
}
if (0 == strncmp(name, "init_max_streams_bidi", 21))
{
settings->es_init_max_streams_bidi = atoi(val);
return 0;
}
break;
case 23:
if (0 == strncmp(name, "max_udp_payload_size_rx", 23))

View File

@ -58,9 +58,9 @@ developed by the IETF. Both types are included in a single enum:
IETF QUIC version ID 29
.. member:: LSQVER_ID30; this version is deprecated.
.. member:: LSQVER_ID30
IETF QUIC version ID 30
IETF QUIC version ID 30; this version is deprecated.
.. member:: LSQVER_ID31

View File

@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies'
author = u'LiteSpeed Technologies'
# The short X.Y version
version = u'2.22'
version = u'2.23'
# The full version, including alpha/beta/rc tags
release = u'2.22.1'
release = u'2.23.0'
# -- General configuration ---------------------------------------------------

View File

@ -18,7 +18,7 @@ connections.
To aid development, there is a :macro:`LSQUIC_FORCED_TCID0_VERSIONS` that
specifies the list of versions with 0-sized connections. (If you, for
example, want to turn them.)
example, want to turn them off.)
Once gQUIC becomes deprecated in the future, there will remain no technical
reason why a single engine instance could not be used both for client and

View File

@ -24,8 +24,8 @@ extern "C" {
#endif
#define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 22
#define LSQUIC_PATCH_VERSION 1
#define LSQUIC_MINOR_VERSION 23
#define LSQUIC_PATCH_VERSION 0
/**
* Engine flags:
@ -1923,8 +1923,7 @@ lsquic_get_h3_alpns (unsigned versions);
* been established: it will return incorrect result.
*/
int
lsquic_is_valid_hs_packet (lsquic_engine_t *, const unsigned char *,
size_t bufsz, size_t packet_in_sz);
lsquic_is_valid_hs_packet (lsquic_engine_t *, const unsigned char *, size_t);
/**
* Parse cid from packet stored in `buf' and store it to `cid'. Returns 0

View File

@ -282,6 +282,10 @@ struct conn_iface
/* Optional method */
size_t
(*ci_get_min_datagram_size) (struct lsquic_conn *);
/* Optional method */
void
(*ci_early_data_failed) (struct lsquic_conn *);
};
#define LSCONN_CCE_BITS 3

View File

@ -15,6 +15,8 @@ struct ssl_stream_method_st;
struct ssl_st;
struct sockaddr;
struct conn_cid_elem;
struct lsquic_engine_settings;
enum lsquic_version;
#define DNONC_LENGTH 32
#define SRST_LENGTH 16
@ -242,12 +244,6 @@ struct enc_session_funcs_gquic
void (*esf_reset_cid) (enc_session_t *, const lsquic_cid_t *);
};
enum iquic_handshake_status {
IHS_WANT_READ,
IHS_WANT_WRITE,
IHS_STOP,
};
struct crypto_stream_if
{
ssize_t (*csi_write) (void *stream, const void *buf, size_t len);
@ -371,4 +367,10 @@ lsquic_sess_resume_version (const unsigned char *, size_t);
*/
#define IQUIC_TAG_LEN 16
/* Return number of bytes written to `buf' or -1 on error */
int
lsquic_enc_sess_ietf_gen_quic_ctx (
const struct lsquic_engine_settings *settings,
enum lsquic_version version, unsigned char *buf, size_t bufsz);
#endif

View File

@ -107,6 +107,20 @@ static void
no_sess_ticket (enum alarm_id alarm_id, void *ctx,
lsquic_time_t expiry, lsquic_time_t now);
static int
iquic_new_session_cb (SSL *, SSL_SESSION *);
static void
keylog_callback (const SSL *, const char *);
static enum ssl_verify_result_t
verify_server_cert_callback (SSL *, uint8_t *out_alert);
static void
maybe_setup_key_logging (struct enc_sess_iquic *);
static void
iquic_esfi_destroy (enc_session_t *);
#define SAMPLE_SZ 16
@ -223,7 +237,7 @@ struct enc_sess_iquic
lsquic_cid_t esi_iscid; /* Initial SCID */
unsigned esi_key_phase;
enum {
ESI_INITIALIZED = 1 << 0,
ESI_UNUSED0 = 1 << 0,
ESI_LOG_SECRETS = 1 << 1,
ESI_HANDSHAKE_OK = 1 << 2,
ESI_ODCID = 1 << 3,
@ -243,17 +257,15 @@ struct enc_sess_iquic
ESI_MAX_PACKNO_INIT = 1 << 17,
ESI_MAX_PACKNO_HSK = ESI_MAX_PACKNO_INIT << PNS_HSK,
ESI_MAX_PACKNO_APP = ESI_MAX_PACKNO_INIT << PNS_APP,
ESI_HAVE_0RTT_TP = 1 << 20,
} esi_flags;
enum enc_level esi_last_w;
unsigned esi_trasec_sz;
char *esi_hostname;
void *esi_keylog_handle;
#ifndef NDEBUG
char *esi_sni_bypass;
#endif
const unsigned char *esi_alpn;
unsigned char *esi_sess_resume_buf;
size_t esi_sess_resume_sz;
/* Need MD and AEAD for key rotation */
const EVP_MD *esi_md;
const EVP_AEAD *esi_aead;
@ -279,6 +291,7 @@ struct enc_sess_iquic
esi_hp_batch_packets[HP_BATCH_SIZE];
unsigned char esi_hp_batch_samples[HP_BATCH_SIZE][SAMPLE_SZ];
unsigned char esi_grease;
signed char esi_have_forw;
};
@ -696,24 +709,23 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
static SSL_SESSION *
maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess,
const SSL_CTX *ssl_ctx)
const SSL_CTX *ssl_ctx, const unsigned char *sess_resume,
size_t sess_resume_sz)
{
SSL_SESSION *ssl_session;
lsquic_ver_tag_t ver_tag;
enum lsquic_version quic_ver;
uint32_t rtt_ver, ticket_sz, trapa_sz;
const unsigned char *ticket_buf, *trapa_buf, *p;
const unsigned char *const end
= enc_sess->esi_sess_resume_buf + enc_sess->esi_sess_resume_sz;
const unsigned char *const end = sess_resume + sess_resume_sz;
if (enc_sess->esi_sess_resume_sz
< sizeof(ver_tag) + sizeof(rtt_ver) + sizeof(ticket_sz))
if (sess_resume_sz < sizeof(ver_tag) + sizeof(rtt_ver) + sizeof(ticket_sz))
{
LSQ_DEBUG("rtt buf too short");
return NULL;
}
p = enc_sess->esi_sess_resume_buf;
p = sess_resume;
memcpy(&ver_tag, p, sizeof(ver_tag));
p += sizeof(ver_tag);
quic_ver = lsquic_tag2ver(ver_tag);
@ -760,8 +772,6 @@ maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess,
p += trapa_sz;
assert(p == end);
(void) /* TODO */ trapa_buf;
ssl_session = SSL_SESSION_from_bytes(ticket_buf, ticket_sz, ssl_ctx);
if (!ssl_session)
{
@ -769,6 +779,22 @@ maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess,
return NULL;
}
if (SSL_SESSION_early_data_capable(ssl_session))
{
if (0 > (quic_ver == LSQVER_ID27 ? lsquic_tp_decode_27
: lsquic_tp_decode)(trapa_buf, trapa_sz, 1,
&enc_sess->esi_peer_tp))
{
SSL_SESSION_free(ssl_session);
LSQ_WARN("cannot parse stored transport parameters");
return NULL;
}
LSQ_DEBUG("early data capable, will try 0-RTT");
enc_sess->esi_flags |= ESI_HAVE_0RTT_TP;
}
else
LSQ_DEBUG("early data not capable -- not trying 0-RTT");
LSQ_INFO("instantiated SSL_SESSION from serialized buffer");
return ssl_session;
}
@ -795,6 +821,16 @@ iquic_esfi_create_client (const char *hostname,
struct lsquic_alarmset *alset, unsigned max_streams_uni)
{
struct enc_sess_iquic *enc_sess;
SSL_CTX *ssl_ctx;
SSL_SESSION *ssl_session;
const struct alpn_map *am;
int transpa_len;
char errbuf[ERR_ERROR_STRING_BUF_LEN];
unsigned char trans_params[0x80
#if LSQUIC_TEST_QUANTUM_READINESS
+ 4 + lsquic_tp_get_quantum_sz()
#endif
];
fiu_return_on("enc_sess_ietf/create_client", NULL);
@ -802,18 +838,6 @@ iquic_esfi_create_client (const char *hostname,
if (!enc_sess)
return NULL;
if (hostname)
{
enc_sess->esi_hostname = strdup(hostname);
if (!enc_sess->esi_hostname)
{
free(enc_sess);
return NULL;
}
}
else
enc_sess->esi_hostname = NULL;
enc_sess->esi_enpub = enpub;
enc_sess->esi_streams = crypto_streams;
enc_sess->esi_cryst_if = cryst_if;
@ -844,35 +868,123 @@ iquic_esfi_create_client (const char *hostname,
return NULL;
}
/* Have to wait until the call to init_client() -- this is when the
* result of version negotiation is known.
*/
enc_sess->esi_max_streams_uni = max_streams_uni;
if (enc_sess->esi_enpub->enp_alpn)
enc_sess->esi_alpn = enc_sess->esi_enpub->enp_alpn;
else if (enc_sess->esi_enpub->enp_flags & ENPUB_HTTP)
{
for (am = s_h3_alpns; am < s_h3_alpns + sizeof(s_h3_alpns)
/ sizeof(s_h3_alpns[0]); ++am)
if (am->version == enc_sess->esi_ver_neg->vn_ver)
goto alpn_selected;
LSQ_ERROR("version %s has no matching ALPN",
lsquic_ver2str[enc_sess->esi_ver_neg->vn_ver]);
goto err;
alpn_selected:
enc_sess->esi_alpn = am->alpn;
}
LSQ_DEBUG("Create new SSL_CTX");
ssl_ctx = SSL_CTX_new(TLS_method());
if (!ssl_ctx)
{
LSQ_ERROR("cannot create SSL context: %s",
ERR_error_string(ERR_get_error(), errbuf));
goto err;
}
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_default_verify_paths(ssl_ctx);
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT);
if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info)
SSL_CTX_sess_set_new_cb(ssl_ctx, iquic_new_session_cb);
if (enc_sess->esi_enpub->enp_kli)
SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
if (enc_sess->esi_enpub->enp_verify_cert
|| LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)
|| LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_QLOG))
SSL_CTX_set_custom_verify(ssl_ctx, SSL_VERIFY_PEER,
verify_server_cert_callback);
SSL_CTX_set_early_data_enabled(ssl_ctx, 1);
enc_sess->esi_ssl = SSL_new(ssl_ctx);
if (!enc_sess->esi_ssl)
{
LSQ_ERROR("cannot create SSL object: %s",
ERR_error_string(ERR_get_error(), errbuf));
goto err;
}
transpa_len = gen_trans_params(enc_sess, trans_params,
sizeof(trans_params));
if (transpa_len < 0)
{
goto err;
}
if (1 != SSL_set_quic_transport_params(enc_sess->esi_ssl, trans_params,
transpa_len))
{
LSQ_ERROR("cannot set QUIC transport params: %s",
ERR_error_string(ERR_get_error(), errbuf));
goto err;
}
if (!(SSL_set_quic_method(enc_sess->esi_ssl, &cry_quic_method)))
{
LSQ_INFO("could not set stream method");
goto err;
}
maybe_setup_key_logging(enc_sess);
if (enc_sess->esi_alpn &&
0 != SSL_set_alpn_protos(enc_sess->esi_ssl, enc_sess->esi_alpn,
enc_sess->esi_alpn[0] + 1))
{
LSQ_ERROR("cannot set ALPN: %s",
ERR_error_string(ERR_get_error(), errbuf));
goto err;
}
if (1 != SSL_set_tlsext_host_name(enc_sess->esi_ssl, hostname))
{
LSQ_ERROR("cannot set hostname: %s",
ERR_error_string(ERR_get_error(), errbuf));
goto err;
}
if (sess_resume && sess_resume_sz)
{
enc_sess->esi_sess_resume_buf = malloc(sess_resume_sz);
if (enc_sess->esi_sess_resume_buf)
ssl_session = maybe_create_SSL_SESSION(enc_sess, ssl_ctx,
sess_resume, sess_resume_sz);
if (ssl_session)
{
memcpy(enc_sess->esi_sess_resume_buf, sess_resume, sess_resume_sz);
enc_sess->esi_sess_resume_sz = sess_resume_sz;
(void) /* This only ever returns 1: */
SSL_set_session(enc_sess->esi_ssl, ssl_session);
SSL_SESSION_free(ssl_session);
ssl_session = NULL;
enc_sess->esi_flags |= ESI_USE_SSL_TICKET;
}
else
enc_sess->esi_sess_resume_sz = 0;
}
else
{
enc_sess->esi_sess_resume_buf = NULL;
enc_sess->esi_sess_resume_sz = 0;
}
SSL_set_ex_data(enc_sess->esi_ssl, s_idx, enc_sess);
SSL_set_connect_state(enc_sess->esi_ssl);
if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info)
enc_sess->esi_flags |= ESI_WANT_TICKET;
enc_sess->esi_alset = alset;
lsquic_alarmset_init_alarm(enc_sess->esi_alset, AL_SESS_TICKET,
no_sess_ticket, enc_sess);
enc_sess->esi_max_streams_uni = max_streams_uni;
SSL_CTX_free(ssl_ctx);
return enc_sess;
err:
if (enc_sess)
iquic_esfi_destroy(enc_sess);
if (ssl_ctx)
SSL_CTX_free(ssl_ctx);
return NULL;
}
@ -1242,6 +1354,7 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
{
struct enc_sess_iquic *const enc_sess = enc_session_p;
const struct alpn_map *am;
unsigned quic_ctx_idx;
int transpa_len;
SSL_CTX *ssl_ctx = NULL;
union {
@ -1287,9 +1400,10 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
LSQ_INFO("could not set stream method");
return -1;
}
/* TODO: set to transport parameter string instead of the constant string */
quic_ctx_idx = enc_sess->esi_conn->cn_version == LSQVER_ID27 ? 0 : 1;
if (!SSL_set_quic_early_data_context(enc_sess->esi_ssl,
(unsigned char *) "lsquic", 6))
enc_sess->esi_enpub->enp_quic_ctx_buf[quic_ctx_idx],
enc_sess->esi_enpub->enp_quic_ctx_sz[quic_ctx_idx]))
{
LSQ_INFO("could not set early data context");
return -1;
@ -1317,7 +1431,6 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
SSL_set_ex_data(enc_sess->esi_ssl, s_idx, enc_sess);
SSL_set_accept_state(enc_sess->esi_ssl);
LSQ_DEBUG("initialized server enc session");
enc_sess->esi_flags |= ESI_INITIALIZED;
return 0;
}
@ -1415,129 +1528,6 @@ iquic_new_session_cb (SSL *ssl, SSL_SESSION *session)
}
static int
init_client (struct enc_sess_iquic *const enc_sess)
{
SSL_CTX *ssl_ctx;
SSL_SESSION *ssl_session;
const struct alpn_map *am;
int transpa_len;
char errbuf[ERR_ERROR_STRING_BUF_LEN];
#define hexbuf errbuf /* This is a dual-purpose buffer */
unsigned char trans_params[0x80
#if LSQUIC_TEST_QUANTUM_READINESS
+ 4 + lsquic_tp_get_quantum_sz()
#endif
];
if (enc_sess->esi_enpub->enp_alpn)
enc_sess->esi_alpn = enc_sess->esi_enpub->enp_alpn;
else if (enc_sess->esi_enpub->enp_flags & ENPUB_HTTP)
{
for (am = s_h3_alpns; am < s_h3_alpns + sizeof(s_h3_alpns)
/ sizeof(s_h3_alpns[0]); ++am)
if (am->version == enc_sess->esi_ver_neg->vn_ver)
goto ok;
LSQ_ERROR("version %s has no matching ALPN",
lsquic_ver2str[enc_sess->esi_ver_neg->vn_ver]);
return -1;
ok: enc_sess->esi_alpn = am->alpn;
}
LSQ_DEBUG("Create new SSL_CTX");
ssl_ctx = SSL_CTX_new(TLS_method());
if (!ssl_ctx)
{
LSQ_ERROR("cannot create SSL context: %s",
ERR_error_string(ERR_get_error(), errbuf));
goto err;
}
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_default_verify_paths(ssl_ctx);
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT);
if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info)
SSL_CTX_sess_set_new_cb(ssl_ctx, iquic_new_session_cb);
if (enc_sess->esi_enpub->enp_kli)
SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
if (enc_sess->esi_enpub->enp_verify_cert
|| LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)
|| LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_QLOG))
SSL_CTX_set_custom_verify(ssl_ctx, SSL_VERIFY_PEER,
verify_server_cert_callback);
SSL_CTX_set_early_data_enabled(ssl_ctx, 1);
transpa_len = gen_trans_params(enc_sess, trans_params,
sizeof(trans_params));
if (transpa_len < 0)
{
goto err;
}
enc_sess->esi_ssl = SSL_new(ssl_ctx);
if (!enc_sess->esi_ssl)
{
LSQ_ERROR("cannot create SSL object: %s",
ERR_error_string(ERR_get_error(), errbuf));
goto err;
}
if (!(SSL_set_quic_method(enc_sess->esi_ssl, &cry_quic_method)))
{
LSQ_INFO("could not set stream method");
goto err;
}
maybe_setup_key_logging(enc_sess);
if (1 != SSL_set_quic_transport_params(enc_sess->esi_ssl, trans_params,
transpa_len))
{
LSQ_ERROR("cannot set QUIC transport params: %s",
ERR_error_string(ERR_get_error(), errbuf));
goto err;
}
if (enc_sess->esi_alpn &&
0 != SSL_set_alpn_protos(enc_sess->esi_ssl, enc_sess->esi_alpn,
enc_sess->esi_alpn[0] + 1))
{
LSQ_ERROR("cannot set ALPN: %s",
ERR_error_string(ERR_get_error(), errbuf));
goto err;
}
if (1 != SSL_set_tlsext_host_name(enc_sess->esi_ssl,
enc_sess->esi_hostname))
{
LSQ_ERROR("cannot set hostname: %s",
ERR_error_string(ERR_get_error(), errbuf));
goto err;
}
free(enc_sess->esi_hostname);
enc_sess->esi_hostname = NULL;
if (enc_sess->esi_sess_resume_buf)
{
ssl_session = maybe_create_SSL_SESSION(enc_sess, ssl_ctx);
if (ssl_session)
{
if (SSL_set_session(enc_sess->esi_ssl, ssl_session))
enc_sess->esi_flags |= ESI_USE_SSL_TICKET;
else
LSQ_WARN("cannot set session");
}
}
SSL_set_ex_data(enc_sess->esi_ssl, s_idx, enc_sess);
SSL_set_connect_state(enc_sess->esi_ssl);
SSL_CTX_free(ssl_ctx);
LSQ_DEBUG("initialized client enc session");
enc_sess->esi_flags |= ESI_INITIALIZED;
return 0;
err:
if (ssl_ctx)
SSL_CTX_free(ssl_ctx);
return -1;
#undef hexbuf
}
struct crypto_params
{
const EVP_AEAD *aead;
@ -1607,14 +1597,72 @@ get_crypto_params (const struct enc_sess_iquic *enc_sess,
}
/* [draft-ietf-quic-transport-31] Section 7.4.1:
" If 0-RTT data is accepted by the server, the server MUST NOT reduce
" any limits or alter any values that might be violated by the client
" with its 0-RTT data. In particular, a server that accepts 0-RTT data
" MUST NOT set values for the following parameters (Section 18.2) that
" are smaller than the remembered value of the parameters.
"
" * active_connection_id_limit
"
" * initial_max_data
"
" * initial_max_stream_data_bidi_local
"
" * initial_max_stream_data_bidi_remote
"
" * initial_max_stream_data_uni
"
" * initial_max_streams_bidi
"
" * initial_max_streams_uni
*/
#define REDUCTION_PROHIBITED_TPS (0 \
| (1 << TPI_ACTIVE_CONNECTION_ID_LIMIT) \
| (1 << TPI_INIT_MAX_DATA) \
| (1 << TPI_INIT_MAX_STREAMS_UNI) \
| (1 << TPI_INIT_MAX_STREAMS_BIDI) \
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL) \
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE) \
| (1 << TPI_INIT_MAX_STREAM_DATA_UNI) \
)
static int
check_server_tps_for_violations (const struct enc_sess_iquic *enc_sess,
const struct transport_params *params_0rtt,
const struct transport_params *new_params)
{
enum transport_param_id tpi;
for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi)
if ((1 << tpi) & REDUCTION_PROHIBITED_TPS)
if (new_params->tp_numerics[tpi] > params_0rtt->tp_numerics[tpi])
{
LSQ_INFO("server's new TP %s increased in value from %"PRIu64
" to %"PRIu64, lsquic_tpi2str[tpi],
params_0rtt->tp_numerics[tpi],
new_params->tp_numerics[tpi]);
return -1;
}
LSQ_DEBUG("server's new transport parameters do not violate save 0-RTT "
"parameters");
return 0;
}
static int
get_peer_transport_params (struct enc_sess_iquic *enc_sess)
{
struct transport_params *const trans_params = &enc_sess->esi_peer_tp;
struct transport_params params_0rtt;
const uint8_t *params_buf;
size_t bufsz;
char *params_str;
const enum lsquic_version version = enc_sess->esi_conn->cn_version;
int have_0rtt_tp;
SSL_get_peer_quic_transport_params(enc_sess->esi_ssl, &params_buf, &bufsz);
if (!params_buf)
@ -1623,6 +1671,13 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess)
return -1;
}
have_0rtt_tp = !!(enc_sess->esi_flags & ESI_HAVE_0RTT_TP);
if (have_0rtt_tp)
{
params_0rtt = enc_sess->esi_peer_tp;
enc_sess->esi_flags &= ~ESI_HAVE_0RTT_TP;
}
LSQ_DEBUG("have peer transport parameters (%zu bytes)", bufsz);
if (0 > (version == LSQVER_ID27 ? lsquic_tp_decode_27
: lsquic_tp_decode)(params_buf, bufsz,
@ -1646,6 +1701,10 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess)
return -1;
}
if (have_0rtt_tp && 0 != check_server_tps_for_violations(enc_sess,
&params_0rtt, trans_params))
return -1;
const lsquic_cid_t *const cids[LAST_TPI + 1] = {
[TP_CID_IDX(TPI_ORIGINAL_DEST_CID)] = enc_sess->esi_flags & ESI_ODCID ? &enc_sess->esi_odcid : NULL,
[TP_CID_IDX(TPI_RETRY_SOURCE_CID)] = enc_sess->esi_flags & ESI_RSCID ? &enc_sess->esi_rscid : NULL,
@ -1771,6 +1830,14 @@ maybe_get_peer_transport_params (struct enc_sess_iquic *enc_sess)
}
enum iquic_handshake_status {
IHS_WANT_READ,
IHS_WANT_WRITE,
IHS_WANT_RW,
IHS_STOP,
};
static enum iquic_handshake_status
iquic_esfi_handshake (struct enc_sess_iquic *enc_sess)
{
@ -1791,9 +1858,12 @@ iquic_esfi_handshake (struct enc_sess_iquic *enc_sess)
LSQ_DEBUG("retry write");
return IHS_WANT_WRITE;
case SSL_ERROR_EARLY_DATA_REJECTED:
LSQ_DEBUG("early data rejected");
hsk_status = LSQ_HSK_RESUMED_FAIL;
goto err;
LSQ_DEBUG("early data rejected: reset");
SSL_reset_early_data_reject(enc_sess->esi_ssl);
if (enc_sess->esi_conn->cn_if->ci_early_data_failed)
enc_sess->esi_conn->cn_if->ci_early_data_failed(
enc_sess->esi_conn);
return IHS_WANT_RW;
/* fall through */
default:
LSQ_DEBUG("handshake: %s", ERR_error_string(err, errbuf));
@ -1866,7 +1936,9 @@ iquic_esfi_get_peer_transport_params (enc_session_t *enc_session_p)
{
struct enc_sess_iquic *const enc_sess = enc_session_p;
if (0 == maybe_get_peer_transport_params(enc_sess))
if (enc_sess->esi_flags & ESI_HAVE_0RTT_TP)
return &enc_sess->esi_peer_tp;
else if (0 == maybe_get_peer_transport_params(enc_sess))
return &enc_sess->esi_peer_tp;
else
return NULL;
@ -1892,8 +1964,6 @@ iquic_esfi_destroy (enc_session_t *enc_session_p)
free_handshake_keys(enc_sess);
cleanup_hp(&enc_sess->esi_hp);
free(enc_sess->esi_sess_resume_buf);
free(enc_sess->esi_hostname);
free(enc_sess);
}
@ -1910,11 +1980,18 @@ static const enum enc_level hety2el[] =
};
static const enum enc_level pns2enc_level[] =
static const enum enc_level pns2enc_level[2][N_PNS] =
{
[PNS_INIT] = ENC_LEV_CLEAR,
[PNS_HSK] = ENC_LEV_INIT,
[PNS_APP] = ENC_LEV_FORW,
[0] = {
[PNS_INIT] = ENC_LEV_CLEAR,
[PNS_HSK] = ENC_LEV_INIT,
[PNS_APP] = ENC_LEV_EARLY,
},
[1] = {
[PNS_INIT] = ENC_LEV_CLEAR,
[PNS_HSK] = ENC_LEV_INIT,
[PNS_APP] = ENC_LEV_FORW,
},
};
@ -1941,8 +2018,7 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p,
char errbuf[ERR_ERROR_STRING_BUF_LEN];
pns = lsquic_packet_out_pns(packet_out);
/* TODO Obviously, will need more logic for 0-RTT */
enc_level = pns2enc_level[ pns ];
enc_level = pns2enc_level[ enc_sess->esi_have_forw ][ pns ];
if (enc_level == ENC_LEV_FORW)
{
@ -2458,7 +2534,7 @@ static int
iquic_esf_sess_resume_enabled (enc_session_t *enc_session_p)
{
struct enc_sess_iquic *const enc_sess = enc_session_p;
return enc_sess->esi_sess_resume_buf != NULL;
return !!(enc_sess->esi_flags & ESI_USE_SSL_TICKET);
}
@ -2817,6 +2893,9 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level,
key_len, hexbuf));
}
if (rw && enc_level == ENC_LEV_FORW)
enc_sess->esi_have_forw = 1;
return 1;
err:
@ -2955,25 +3034,11 @@ chsk_ietf_on_new_stream (void *stream_if_ctx, struct lsquic_stream *stream)
enum enc_level enc_level;
enc_level = enc_sess->esi_cryst_if->csi_enc_level(stream);
if (enc_level != ENC_LEV_CLEAR)
{
LSQ_DEBUG("skip initialization of stream at level %u", enc_level);
goto end;
}
if (
(enc_sess->esi_flags & ESI_SERVER) == 0 &&
0 != init_client(enc_sess))
{
LSQ_WARN("enc session could not be initialized");
return NULL;
}
enc_sess->esi_cryst_if->csi_wantwrite(stream, 1);
if (enc_level == ENC_LEV_CLEAR)
enc_sess->esi_cryst_if->csi_wantwrite(stream, 1);
LSQ_DEBUG("handshake stream created successfully");
end:
return stream_if_ctx;
}
@ -3006,6 +3071,7 @@ chsk_ietf_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
static const char *const ihs2str[] = {
[IHS_WANT_READ] = "want read",
[IHS_WANT_WRITE] = "want write",
[IHS_WANT_RW] = "want rw",
[IHS_STOP] = "stop",
};
@ -3036,6 +3102,10 @@ iquic_esfi_shake_stream (enc_session_t *sess,
enc_sess->esi_cryst_if->csi_wantwrite(stream, 1);
enc_sess->esi_cryst_if->csi_wantread(stream, 0);
break;
case IHS_WANT_RW:
enc_sess->esi_cryst_if->csi_wantwrite(stream, 1);
enc_sess->esi_cryst_if->csi_wantread(stream, 1);
break;
default:
assert(st == IHS_STOP);
write = !lsquic_frab_list_empty(&enc_sess->esi_frals[enc_level]);
@ -3209,3 +3279,96 @@ const unsigned char *const lsquic_retry_nonce_buf[N_IETF_RETRY_VERSIONS] =
/* [draft-ietf-quic-tls-29] Section 5.8 */
(unsigned char *) "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c",
};
int
lsquic_enc_sess_ietf_gen_quic_ctx (
const struct lsquic_engine_settings *settings,
enum lsquic_version version, unsigned char *buf, size_t bufsz)
{
struct transport_params params;
int len;
/* This code is pretty much copied from gen_trans_params(), with
* small (but important) exceptions.
*/
memset(&params, 0, sizeof(params));
params.tp_init_max_data = settings->es_init_max_data;
params.tp_init_max_stream_data_bidi_local
= settings->es_init_max_stream_data_bidi_local;
params.tp_init_max_stream_data_bidi_remote
= settings->es_init_max_stream_data_bidi_remote;
params.tp_init_max_stream_data_uni
= settings->es_init_max_stream_data_uni;
params.tp_init_max_streams_uni
= settings->es_init_max_streams_uni;
params.tp_init_max_streams_bidi
= settings->es_init_max_streams_bidi;
params.tp_ack_delay_exponent
= TP_DEF_ACK_DELAY_EXP;
params.tp_max_idle_timeout = settings->es_idle_timeout * 1000;
params.tp_max_ack_delay = TP_DEF_MAX_ACK_DELAY;
params.tp_active_connection_id_limit = MAX_IETF_CONN_DCIDS;
params.tp_set |= (1 << TPI_INIT_MAX_DATA)
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL)
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE)
| (1 << TPI_INIT_MAX_STREAM_DATA_UNI)
| (1 << TPI_INIT_MAX_STREAMS_UNI)
| (1 << TPI_INIT_MAX_STREAMS_BIDI)
| (1 << TPI_ACK_DELAY_EXPONENT)
| (1 << TPI_MAX_IDLE_TIMEOUT)
| (1 << TPI_MAX_ACK_DELAY)
| (1 << TPI_ACTIVE_CONNECTION_ID_LIMIT)
;
if (settings->es_max_udp_payload_size_rx)
{
params.tp_max_udp_payload_size = settings->es_max_udp_payload_size_rx;
params.tp_set |= 1 << TPI_MAX_UDP_PAYLOAD_SIZE;
}
if (!settings->es_allow_migration)
params.tp_set |= 1 << TPI_DISABLE_ACTIVE_MIGRATION;
if (settings->es_ql_bits)
{
params.tp_loss_bits = settings->es_ql_bits - 1;
params.tp_set |= 1 << TPI_LOSS_BITS;
}
if (settings->es_delayed_acks)
{
params.tp_numerics[TPI_MIN_ACK_DELAY] = 10000; /* TODO: make into a constant? make configurable? */
params.tp_set |= 1 << TPI_MIN_ACK_DELAY;
}
if (settings->es_timestamps)
{
params.tp_numerics[TPI_TIMESTAMPS] = TS_GENERATE_THEM;
params.tp_set |= 1 << TPI_TIMESTAMPS;
}
if (settings->es_datagrams)
{
if (params.tp_set & (1 << TPI_MAX_UDP_PAYLOAD_SIZE))
params.tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE]
= params.tp_max_udp_payload_size;
else
params.tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE]
= TP_DEF_MAX_UDP_PAYLOAD_SIZE;
params.tp_set |= 1 << TPI_MAX_DATAGRAM_FRAME_SIZE;
}
params.tp_set &= SERVER_0RTT_TPS;
len = (version == LSQVER_ID27 ? lsquic_tp_encode_27 : lsquic_tp_encode)(
&params, 1, buf, bufsz);
if (len >= 0)
{
char str[MAX_TP_STR_SZ];
LSQ_LOG1(LSQ_LOG_DEBUG, "generated QUIC server context of %d bytes "
"for version %s", len, lsquic_ver2str[version]);
LSQ_LOG1(LSQ_LOG_DEBUG, "%s", ((version == LSQVER_ID27
? lsquic_tp_to_str_27 : lsquic_tp_to_str)(&params, str,
sizeof(str)), str));
}
else
LSQ_LOG1(LSQ_LOG_WARN, "cannot generate QUIC server context: %d",
errno);
return len;
}

View File

@ -631,6 +631,22 @@ lsquic_engine_new (unsigned flags,
engine->pub.enp_tokgen = lsquic_tg_new(&engine->pub);
if (!engine->pub.enp_tokgen)
return NULL;
if (engine->flags & ENG_SERVER)
for (i = 0; i < sizeof(engine->pub.enp_quic_ctx_sz)
/ sizeof(engine->pub.enp_quic_ctx_sz[0]); ++i)
{
int sz = lsquic_enc_sess_ietf_gen_quic_ctx(
&engine->pub.enp_settings,
i == 0 ? LSQVER_ID27 : LSQVER_ID28,
engine->pub.enp_quic_ctx_buf[i],
sizeof(engine->pub.enp_quic_ctx_buf));
if (sz < 0)
{
free(engine);
return NULL;
}
engine->pub.enp_quic_ctx_sz[i] = (unsigned) sz;
}
engine->pub.enp_crand = &engine->crand;
if (engine->pub.enp_settings.es_noprogress_timeout)
engine->pub.enp_noprog_timeout
@ -1106,6 +1122,34 @@ find_conn_by_addr (struct lsquic_hash *hash, const struct sockaddr *sa)
}
/* When connections are identified by the local address, we need to drop
* packets that use DCIDs that do not correspond to any of SCIDs. This
* can happen when peer retires a SCID. This mimics the normal behavior,
* when connections are looked up in engine->conns_hash by ID: when there
* is no match, the packet is dropped.
*/
static int
dcid_checks_out (const struct lsquic_conn *conn, const lsquic_cid_t *dcid)
{
const struct conn_cid_elem *cce;
if (LSQUIC_CIDS_EQ(CN_SCID(conn), dcid))
return 1;
/* Slow check for those rare cases */
for (cce = conn->cn_cces; cce < END_OF_CCES(conn); ++cce)
if ((conn->cn_cces_mask & (1 << (cce - conn->cn_cces)))
&& !(cce->cce_flags & CCE_PORT)
&& LSQUIC_CIDS_EQ(&cce->cce_cid, dcid))
{
LSQ_DEBUG("connection checks out");
return 1;
}
return 0;
}
static lsquic_conn_t *
find_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
struct packin_parse_state *ppstate, const struct sockaddr *sa_local)
@ -1114,7 +1158,17 @@ find_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
lsquic_conn_t *conn;
if (engine->flags & ENG_CONNS_BY_ADDR)
{
el = find_conn_by_addr(engine->conns_hash, sa_local);
if ((packet_in->pi_flags & PI_CONN_ID)
&& !dcid_checks_out(lsquic_hashelem_getdata(el),
&packet_in->pi_conn_id))
{
LSQ_DEBUGC("DCID matches no SCID in connection %"CID_FMT": drop it",
CID_BITS(&packet_in->pi_conn_id));
return NULL;
}
}
else if (packet_in->pi_flags & PI_CONN_ID)
el = lsquic_hash_find(engine->conns_hash,
packet_in->pi_conn_id.idbuf, packet_in->pi_conn_id.len);

View File

@ -78,6 +78,11 @@ struct lsquic_engine_public {
struct lsquic_hash *enp_server_certs;
/* gQUIC server configuration: */
struct lsquic_server_config *enp_server_config;
/* Serialized subset of server engine transport parameters that is used
* as SSL QUIC context. 0 is for version <= LSQVER_ID27, 1 is for others.
*/
unsigned char enp_quic_ctx_buf[2][200];
unsigned enp_quic_ctx_sz[2];
};
/* Put connection onto the Tickable Queue if it is not already on it. If

View File

@ -30,6 +30,8 @@
#include "lsquic_frab_list.h"
#include "lsquic_ev_log.h"
#include "fiu-local.h"
#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_WRITER
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(\
lsquic_stream_conn(fw->fw_stream))
@ -497,6 +499,8 @@ lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw,
unsigned char *buf;
int s;
fiu_return_on("frame_writer/writer_promise", -1);
if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers))
return -1;

View File

@ -4156,10 +4156,15 @@ full_conn_ci_push_stream (struct lsquic_conn *lconn, void *hset,
if (0 != lsquic_headers_stream_push_promise(conn->fc_pub.u.gquic.hs, dep_stream->id,
pushed_stream->id, headers))
{
/* Since the failure to write to HEADERS stream results in aborting
* the connection, we do not bother rolling back.
*/
LSQ_ERROR("could not send push promise");
/* If forget we ever had the hset pointer: */
lsquic_stream_drop_hset_ref(pushed_stream);
/* Now roll back stream creation and return stream ID: */
if (pushed_stream->sm_hash_el.qhe_flags & QHE_HASHED)
lsquic_hash_erase(conn->fc_pub.all_streams,
&pushed_stream->sm_hash_el);
lsquic_stream_destroy(pushed_stream);
conn->fc_last_stream_id -= 2;
LSQ_INFO("could not send push promise");
return -1;
}

View File

@ -137,7 +137,7 @@ enum ifull_conn_flags
IFC_IGNORE_HSK = 1 << 25,
IFC_PROC_CRYPTO = 1 << 26,
IFC_MIGRA = 1 << 27,
IFC_UNUSED28 = 1 << 28, /* Unused */
IFC_HTTP_INITED = 1 << 28, /* HTTP initialized */
IFC_DELAYED_ACKS = 1 << 29, /* Delayed ACKs are enabled */
IFC_TIMESTAMPS = 1 << 30, /* Timestamps are enabled */
IFC_DATAGRAMS = 1u<< 31, /* Datagrams are enabled */
@ -153,6 +153,7 @@ enum more_flags
MF_CONN_CLOSE_PACK = 1 << 4, /* CONNECTION_CLOSE has been packetized */
MF_SEND_WRONG_COUNTS= 1 << 5, /* Send wrong ECN counts to peer */
MF_WANT_DATAGRAM_WRITE = 1 << 6,
MF_DOING_0RTT = 1 << 7,
};
@ -568,6 +569,12 @@ find_cce_by_cid (struct ietf_full_conn *, const lsquic_cid_t *);
static void
mtu_probe_too_large (struct ietf_full_conn *, const struct lsquic_packet_out *);
static int
apply_trans_params (struct ietf_full_conn *, const struct transport_params *);
static int
init_http (struct ietf_full_conn *);
static unsigned
highest_bit_set (unsigned sz)
{
@ -1252,7 +1259,7 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
conn->ifc_peer_hq_settings.header_table_size = HQ_DF_QPACK_MAX_TABLE_CAPACITY;
conn->ifc_peer_hq_settings.qpack_blocked_streams = HQ_DF_QPACK_BLOCKED_STREAMS;
conn->ifc_flags = flags | IFC_CREATED_OK | IFC_FIRST_TICK;
conn->ifc_flags = flags | IFC_FIRST_TICK;
conn->ifc_max_ack_packno[PNS_INIT] = IQUIC_INVALID_PACKNO;
conn->ifc_max_ack_packno[PNS_HSK] = IQUIC_INVALID_PACKNO;
conn->ifc_max_ack_packno[PNS_APP] = IQUIC_INVALID_PACKNO;
@ -1282,6 +1289,7 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
const unsigned char *sess_resume, size_t sess_resume_sz,
const unsigned char *token, size_t token_sz)
{
const struct transport_params *params;
const struct enc_session_funcs_iquic *esfi;
struct ietf_full_conn *conn;
enum lsquic_version ver, sess_resume_version;
@ -1397,6 +1405,18 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
conn->ifc_created = now;
LSQ_DEBUG("logging using %s SCID",
LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "client" : "server");
if (sess_resume && (params
= conn->ifc_conn.cn_esf.i->esfi_get_peer_transport_params(
conn->ifc_conn.cn_enc_session), params != NULL))
{
LSQ_DEBUG("initializing transport parameters for 0RTT");
if (0 != apply_trans_params(conn, params))
goto full_err;
if ((conn->ifc_flags & IFC_HTTP) && 0 != init_http(conn))
goto full_err;
conn->ifc_mflags |= MF_DOING_0RTT;
}
conn->ifc_flags |= IFC_CREATED_OK;
return &conn->ifc_conn;
err4:
@ -1411,6 +1431,10 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
free(conn);
err0:
return NULL;
full_err:
ietf_full_conn_ci_destroy(&conn->ifc_conn);
return NULL;
}
@ -1635,6 +1659,7 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
LSQ_DEBUG("logging using %s SCID",
LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "server" : "client");
conn->ifc_flags |= IFC_CREATED_OK;
return &conn->ifc_conn;
err3:
@ -3317,35 +3342,13 @@ maybe_start_migration (struct ietf_full_conn *conn)
static int
handshake_ok (struct lsquic_conn *lconn)
apply_trans_params (struct ietf_full_conn *conn,
const struct transport_params *params)
{
struct ietf_full_conn *const conn = (struct ietf_full_conn *) lconn;
struct lsquic_stream *stream;
struct lsquic_hash_elem *el;
struct dcid_elem *dce;
const struct transport_params *params;
enum stream_id_type sit;
uint64_t limit;
char buf[MAX_TP_STR_SZ];
fiu_return_on("full_conn_ietf/handshake_ok", -1);
/* Need to set this flag even we hit an error in the rest of this funciton.
* This is because this flag is used to calculate packet out header size
*/
lconn->cn_flags |= LSCONN_HANDSHAKE_DONE;
params = lconn->cn_esf.i->esfi_get_peer_transport_params(
lconn->cn_enc_session);
if (!params)
{
ABORT_WARN("could not get transport parameters");
return -1;
}
LSQ_DEBUG("peer transport parameters: %s",
((lconn->cn_version == LSQVER_ID27 ? lsquic_tp_to_str_27
: lsquic_tp_to_str)(params, buf, sizeof(buf)), buf));
if ((params->tp_set & (1 << TPI_LOSS_BITS))
&& conn->ifc_settings->es_ql_bits == 2)
@ -3487,6 +3490,104 @@ handshake_ok (struct lsquic_conn *lconn)
CUR_NPATH(conn)->np_pack_size);
}
if (params->tp_active_connection_id_limit > conn->ifc_conn.cn_n_cces)
conn->ifc_active_cids_limit = conn->ifc_conn.cn_n_cces;
else
conn->ifc_active_cids_limit = params->tp_active_connection_id_limit;
conn->ifc_first_active_cid_seqno = conn->ifc_scid_seqno;
return 0;
}
static int
init_http (struct ietf_full_conn *conn)
{
fiu_return_on("full_conn_ietf/init_http", -1);
lsquic_qeh_init(&conn->ifc_qeh, &conn->ifc_conn);
if (0 == avail_streams_count(conn, conn->ifc_flags & IFC_SERVER,
SD_UNI))
{
ABORT_QUIETLY(1, HEC_GENERAL_PROTOCOL_ERROR, "cannot create "
"control stream due to peer-imposed limit");
conn->ifc_error = CONN_ERR(1, HEC_GENERAL_PROTOCOL_ERROR);
return -1;
}
if (0 != create_ctl_stream_out(conn))
{
ABORT_WARN("cannot create outgoing control stream");
return -1;
}
if (0 != lsquic_hcso_write_settings(&conn->ifc_hcso,
&conn->ifc_enpub->enp_settings, conn->ifc_flags & IFC_SERVER))
{
ABORT_WARN("cannot write SETTINGS");
return -1;
}
if (!(conn->ifc_flags & IFC_SERVER)
&& (conn->ifc_u.cli.ifcli_flags & IFCLI_PUSH_ENABLED)
&& 0 != lsquic_hcso_write_max_push_id(&conn->ifc_hcso,
conn->ifc_u.cli.ifcli_max_push_id))
{
ABORT_WARN("cannot write MAX_PUSH_ID");
return -1;
}
if (0 != lsquic_qdh_init(&conn->ifc_qdh, &conn->ifc_conn,
conn->ifc_flags & IFC_SERVER, conn->ifc_enpub,
conn->ifc_settings->es_qpack_dec_max_size,
conn->ifc_settings->es_qpack_dec_max_blocked))
{
ABORT_WARN("cannot initialize QPACK decoder");
return -1;
}
if (avail_streams_count(conn, conn->ifc_flags & IFC_SERVER, SD_UNI) > 0)
{
if (0 != create_qdec_stream_out(conn))
{
ABORT_WARN("cannot create outgoing QPACK decoder stream");
return -1;
}
}
else
{
queue_streams_blocked_frame(conn, SD_UNI);
LSQ_DEBUG("cannot create outgoing QPACK decoder stream due to "
"unidir limits");
}
conn->ifc_flags |= IFC_HTTP_INITED;
return 0;
}
static int
handshake_ok (struct lsquic_conn *lconn)
{
struct ietf_full_conn *const conn = (struct ietf_full_conn *) lconn;
struct dcid_elem *dce;
const struct transport_params *params;
char buf[MAX_TP_STR_SZ];
fiu_return_on("full_conn_ietf/handshake_ok", -1);
/* Need to set this flag even we hit an error in the rest of this funciton.
* This is because this flag is used to calculate packet out header size
*/
lconn->cn_flags |= LSCONN_HANDSHAKE_DONE;
params = lconn->cn_esf.i->esfi_get_peer_transport_params(
lconn->cn_enc_session);
if (!params)
{
ABORT_WARN("could not get transport parameters");
return -1;
}
LSQ_DEBUG("peer transport parameters: %s",
((lconn->cn_version == LSQVER_ID27 ? lsquic_tp_to_str_27
: lsquic_tp_to_str)(params, buf, sizeof(buf)), buf));
if (0 != apply_trans_params(conn, params))
return -1;
dce = get_new_dce(conn);
if (!dce)
{
@ -3518,65 +3619,9 @@ handshake_ok (struct lsquic_conn *lconn)
LSQ_INFO("applied peer transport parameters");
if (conn->ifc_flags & IFC_HTTP)
{
lsquic_qeh_init(&conn->ifc_qeh, &conn->ifc_conn);
if (0 == avail_streams_count(conn, conn->ifc_flags & IFC_SERVER,
SD_UNI))
{
ABORT_QUIETLY(1, HEC_GENERAL_PROTOCOL_ERROR, "cannot create "
"control stream due to peer-imposed limit");
conn->ifc_error = CONN_ERR(1, HEC_GENERAL_PROTOCOL_ERROR);
if ((conn->ifc_flags & (IFC_HTTP|IFC_HTTP_INITED)) == IFC_HTTP)
if (0 != init_http(conn))
return -1;
}
if (0 != create_ctl_stream_out(conn))
{
ABORT_WARN("cannot create outgoing control stream");
return -1;
}
if (0 != lsquic_hcso_write_settings(&conn->ifc_hcso,
&conn->ifc_enpub->enp_settings, conn->ifc_flags & IFC_SERVER))
{
ABORT_WARN("cannot write SETTINGS");
return -1;
}
if (!(conn->ifc_flags & IFC_SERVER)
&& (conn->ifc_u.cli.ifcli_flags & IFCLI_PUSH_ENABLED)
&& 0 != lsquic_hcso_write_max_push_id(&conn->ifc_hcso,
conn->ifc_u.cli.ifcli_max_push_id))
{
ABORT_WARN("cannot write MAX_PUSH_ID");
return -1;
}
if (0 != lsquic_qdh_init(&conn->ifc_qdh, &conn->ifc_conn,
conn->ifc_flags & IFC_SERVER, conn->ifc_enpub,
conn->ifc_settings->es_qpack_dec_max_size,
conn->ifc_settings->es_qpack_dec_max_blocked))
{
ABORT_WARN("cannot initialize QPACK decoder");
return -1;
}
if (avail_streams_count(conn, conn->ifc_flags & IFC_SERVER, SD_UNI) > 0)
{
if (0 != create_qdec_stream_out(conn))
{
ABORT_WARN("cannot create outgoing QPACK decoder stream");
return -1;
}
}
else
{
queue_streams_blocked_frame(conn, SD_UNI);
LSQ_DEBUG("cannot create outgoing QPACK decoder stream due to "
"unidir limits");
}
}
if (params->tp_active_connection_id_limit > conn->ifc_conn.cn_n_cces)
conn->ifc_active_cids_limit = conn->ifc_conn.cn_n_cces;
else
conn->ifc_active_cids_limit = params->tp_active_connection_id_limit;
conn->ifc_first_active_cid_seqno = conn->ifc_scid_seqno;
if (conn->ifc_settings->es_dplpmtud)
conn->ifc_mflags |= MF_CHECK_MTU_PROBE;
@ -3585,6 +3630,9 @@ handshake_ok (struct lsquic_conn *lconn)
conn->ifc_send_flags |= SF_SEND_NEW_CID;
maybe_create_delayed_streams(conn);
if (!(conn->ifc_flags & IFC_SERVER))
lsquic_send_ctl_0rtt_to_1rtt(&conn->ifc_send_ctl);
return 0;
}
@ -3615,10 +3663,10 @@ ietf_full_conn_ci_hsk_done (struct lsquic_conn *lconn,
}
break;
default:
case LSQ_HSK_RESUMED_FAIL: /* IETF crypto never returns this */
assert(0);
/* fall-through */
case LSQ_HSK_FAIL:
case LSQ_HSK_RESUMED_FAIL:
handshake_failed(lconn);
break;
}
@ -7406,6 +7454,7 @@ check_or_schedule_mtu_probe (struct ietf_full_conn *conn, lsquic_time_t now)
if (!(conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|| (conn->ifc_flags & IFC_CLOSING)
|| ~0ull == lsquic_senhist_largest(&conn->ifc_send_ctl.sc_senhist)
|| lsquic_senhist_largest(&conn->ifc_send_ctl.sc_senhist) < 30
|| lsquic_send_ctl_in_recovery(&conn->ifc_send_ctl)
|| !lsquic_send_ctl_can_send_probe(&conn->ifc_send_ctl,
@ -7594,6 +7643,16 @@ ietf_full_conn_ci_retx_timeout (struct lsquic_conn *lconn)
}
static void
ietf_full_conn_ci_early_data_failed (struct lsquic_conn *lconn)
{
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
LSQ_DEBUG("early data failed");
lsquic_send_ctl_stash_0rtt_packets(&conn->ifc_send_ctl);
}
static size_t
ietf_full_conn_ci_get_min_datagram_size (struct lsquic_conn *lconn)
{
@ -7830,7 +7889,8 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
conn->ifc_flags |= (s < 0) << IFC_BIT_ERROR;
if (0 == s)
process_crypto_stream_write_events(conn);
goto end_write;
if (!(conn->ifc_mflags & MF_DOING_0RTT))
goto end_write;
}
maybe_conn_flush_special_streams(conn);
@ -8279,6 +8339,7 @@ ietf_full_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz)
.ci_destroy = ietf_full_conn_ci_destroy, \
.ci_drain_time = ietf_full_conn_ci_drain_time, \
.ci_drop_crypto_streams = ietf_full_conn_ci_drop_crypto_streams, \
.ci_early_data_failed = ietf_full_conn_ci_early_data_failed, \
.ci_get_engine = ietf_full_conn_ci_get_engine, \
.ci_get_log_cid = ietf_full_conn_ci_get_log_cid, \
.ci_get_min_datagram_size= ietf_full_conn_ci_get_min_datagram_size, \

View File

@ -88,7 +88,7 @@ is_valid_gquic_hs_packet (const unsigned char *buf, size_t bufsz,
int
lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
const unsigned char *buf, size_t bufsz, size_t packet_in_sz)
const unsigned char *buf, size_t bufsz)
{
lsquic_ver_tag_t tag;
int is_valid;
@ -104,7 +104,7 @@ lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
case 0x80|0x00|0x20|0x10|0x08:
case 0x80|0x40|0x20|0x10|0x00:
case 0x80|0x00|0x20|0x10|0x00:
is_valid = packet_in_sz >= IQUIC_MIN_INIT_PACKET_SZ
is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ
&& lsquic_is_valid_iquic_hs_packet(buf, bufsz, &tag);
break;
/* 1X00 XGGG: ID-22 long header */
@ -122,7 +122,7 @@ lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
case 0x80|0x00|0x20|0x00|0x08:
case 0x80|0x40|0x20|0x00|0x00:
case 0x80|0x00|0x20|0x00|0x00:
is_valid = packet_in_sz >= IQUIC_MIN_INIT_PACKET_SZ
is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ
&& lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet(buf, bufsz, &tag);
break;
/* 01XX XGGG: ID-22 short header */

View File

@ -6,6 +6,7 @@
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
@ -560,6 +561,16 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
TAILQ_INIT(&conn->imc_crypto_frames);
if (odcid)
imico_peer_addr_validated(conn, "odcid");
#if LSQUIC_DEVEL
{
const char *const s = getenv("LSQUIC_LOSE_0RTT");
if (s && atoi(s))
{
LSQ_DEBUG("will lose 0-RTT packets (via env variable)");
conn->imc_delayed_packets_count = UCHAR_MAX;
}
}
#endif
LSQ_DEBUG("created mini connection object %p; max packet size=%hu",
conn, conn->imc_path.np_pack_size);

View File

@ -2163,11 +2163,11 @@ ietf_v1_parse_datagram_frame (const unsigned char *buf, size_t buf_len,
if (buf[0] & 1)
{
s = vint_read(buf + 1, buf + buf_len, &len);
if (s > 0 && 1 + s + len >= buf_len)
if (s > 0 && 1 + s + len <= buf_len)
{
*data = buf + 1 + s;
*data_len = len;
return 1 + buf_len + len;
return 1 + s + len;
}
else
return -1;

View File

@ -338,6 +338,7 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
TAILQ_INIT(&ctl->sc_unacked_packets[PNS_HSK]);
TAILQ_INIT(&ctl->sc_unacked_packets[PNS_APP]);
TAILQ_INIT(&ctl->sc_lost_packets);
TAILQ_INIT(&ctl->sc_0rtt_stash);
ctl->sc_enpub = enpub;
ctl->sc_alset = alset;
ctl->sc_ver_neg = ver_neg;
@ -1463,6 +1464,11 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
packet_out->po_flags &= ~PO_LOST;
send_ctl_destroy_packet(ctl, packet_out);
}
while ((packet_out = TAILQ_FIRST(&ctl->sc_0rtt_stash)))
{
TAILQ_REMOVE(&ctl->sc_0rtt_stash, packet_out, po_next);
send_ctl_destroy_packet(ctl, packet_out);
}
for (n = 0; n < sizeof(ctl->sc_buffered_packets) /
sizeof(ctl->sc_buffered_packets[0]); ++n)
{
@ -3795,3 +3801,72 @@ lsquic_send_ctl_rollback (struct lsquic_send_ctl *ctl,
if (buffered && (lost_types & QUIC_FTBIT_ACK))
lconn->cn_if->ci_ack_rollback(lconn, &ctl_state->ack_state);
}
/* Find 0-RTT packets and change them to 1-RTT packets */
void
lsquic_send_ctl_0rtt_to_1rtt (struct lsquic_send_ctl *ctl)
{
struct lsquic_packet_out *packet_out;
unsigned count;
struct lsquic_packets_tailq *const *q;
struct lsquic_packets_tailq *const queues[] = {
&ctl->sc_scheduled_packets,
&ctl->sc_unacked_packets[PNS_APP],
&ctl->sc_lost_packets,
&ctl->sc_buffered_packets[0].bpq_packets,
&ctl->sc_buffered_packets[1].bpq_packets,
};
assert(ctl->sc_flags & SC_IETF);
while (packet_out = TAILQ_FIRST(&ctl->sc_0rtt_stash), packet_out != NULL)
{
TAILQ_REMOVE(&ctl->sc_0rtt_stash, packet_out, po_next);
TAILQ_INSERT_TAIL(&ctl->sc_lost_packets, packet_out, po_next);
packet_out->po_flags |= PO_LOST;
}
count = 0;
for (q = queues; q < queues + sizeof(queues) / sizeof(queues[0]); ++q)
TAILQ_FOREACH(packet_out, *q, po_next)
if (packet_out->po_header_type == HETY_0RTT)
{
++count;
packet_out->po_header_type = HETY_NOT_SET;
if (packet_out->po_flags & PO_ENCRYPTED)
send_ctl_return_enc_data(ctl, packet_out);
}
LSQ_DEBUG("handshake ok: changed %u packet%.*s from 0-RTT to 1-RTT",
count, count != 1, "s");
}
/* Remove 0-RTT packets from the unacked queue and wait to retransmit them
* after handshake succeeds. This is the most common case. There could
* (theoretically) be some corner cases where 0-RTT packets are in the
* scheduled queue, but we let those be lost naturally if that occurs.
*/
void
lsquic_send_ctl_stash_0rtt_packets (struct lsquic_send_ctl *ctl)
{
struct lsquic_packet_out *packet_out, *next;
unsigned count, packet_sz;
count = 0;
for (packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets[PNS_APP]);
packet_out; packet_out = next)
{
next = TAILQ_NEXT(packet_out, po_next);
if (packet_out->po_header_type == HETY_0RTT)
{
packet_sz = packet_out_sent_sz(packet_out);
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
TAILQ_INSERT_TAIL(&ctl->sc_0rtt_stash, packet_out, po_next);
++count;
}
}
LSQ_DEBUG("stashed %u 0-RTT packet%.*s", count, count != 1, "s");
}

View File

@ -87,6 +87,7 @@ typedef struct lsquic_send_ctl {
/* Second section: everything else. */
struct lsquic_packets_tailq sc_scheduled_packets,
sc_0rtt_stash,
sc_lost_packets;
struct buf_packet_q sc_buffered_packets[BPT_OTHER_PRIO + 1];
const struct ver_neg *sc_ver_neg;
@ -431,4 +432,10 @@ void
lsquic_send_ctl_rollback (struct lsquic_send_ctl *, struct send_ctl_state *,
const struct iovec *, size_t);
void
lsquic_send_ctl_0rtt_to_1rtt (struct lsquic_send_ctl *);
void
lsquic_send_ctl_stash_0rtt_packets (struct lsquic_send_ctl *);
#endif

View File

@ -373,6 +373,39 @@ stream_is_hsk (const struct lsquic_stream *stream)
}
/* This function's only job is to change the allocated packet's header
* type to HETY_0RTT when stream frames are written before handshake
* is complete.
*/
static struct lsquic_packet_out *
stream_get_packet_for_stream_0rtt (struct lsquic_send_ctl *ctl,
unsigned need_at_least, const struct network_path *path,
const struct lsquic_stream *stream)
{
struct lsquic_packet_out *packet_out;
if (stream->conn_pub->lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
{
LSQ_DEBUG("switch to regular \"get packet for stream\" function");
/* Here we drop the "const" because this is a static function.
* Otherwise, we would not condone such sorcery.
*/
((struct lsquic_stream *) stream)->sm_get_packet_for_stream
= lsquic_send_ctl_get_packet_for_stream;
return lsquic_send_ctl_get_packet_for_stream(ctl, need_at_least,
path, stream);
}
else
{
packet_out = lsquic_send_ctl_get_packet_for_stream(ctl, need_at_least,
path, stream);
if (packet_out)
packet_out->po_header_type = HETY_0RTT;
return packet_out;
}
}
static struct lsquic_stream *
stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub,
const struct lsquic_stream_if *stream_if, void *stream_if_ctx,
@ -405,6 +438,7 @@ stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub,
stream->sm_bflags |= ctor_flags & ((1 << N_SMBF_FLAGS) - 1);
if (conn_pub->lconn->cn_flags & LSCONN_SERVER)
stream->sm_bflags |= SMBF_SERVER;
stream->sm_get_packet_for_stream = lsquic_send_ctl_get_packet_for_stream;
return stream;
}
@ -479,6 +513,13 @@ lsquic_stream_new (lsquic_stream_id_t id,
}
}
if ((stream->sm_bflags & (SMBF_SERVER|SMBF_IETF)) == SMBF_IETF
&& !(conn_pub->lconn->cn_flags & LSCONN_HANDSHAKE_DONE))
{
LSQ_DEBUG("use wrapper \"get packet for stream\" function");
stream->sm_get_packet_for_stream = stream_get_packet_for_stream_0rtt;
}
lsquic_sfcw_init(&stream->fc, initial_window, cfcw, conn_pub, id);
stream->max_send_off = initial_send_off;
LSQ_DEBUG("created stream");
@ -584,6 +625,14 @@ drop_buffered_data (struct lsquic_stream *stream)
}
void
lsquic_stream_drop_hset_ref (struct lsquic_stream *stream)
{
if (stream->uh)
stream->uh->uh_hset = NULL;
}
static void
destroy_uh (struct lsquic_stream *stream)
{
@ -3087,7 +3136,7 @@ stream_write_to_packet_std (struct frame_gen_ctx *fg_ctx, const size_t size)
else
need_at_least += size > 0;
get_packet:
packet_out = lsquic_send_ctl_get_packet_for_stream(send_ctl,
packet_out = stream->sm_get_packet_for_stream(send_ctl,
need_at_least, stream->conn_pub->path, stream);
if (packet_out)
{

View File

@ -17,6 +17,9 @@ struct data_frame;
enum quic_frame_type;
struct push_promise;
union hblock_ctx;
struct lsquic_packet_out;
struct lsquic_send_ctl;
struct network_path;
TAILQ_HEAD(lsquic_streams_tailq, lsquic_stream);
@ -311,6 +314,11 @@ struct lsquic_stream
size_t (*sm_write_avail)(struct lsquic_stream *);
int (*sm_readable)(struct lsquic_stream *);
struct lsquic_packet_out * (*sm_get_packet_for_stream)(
struct lsquic_send_ctl *,
unsigned, const struct network_path *,
const struct lsquic_stream *);
/* This element is optional */
const struct stream_filter_if *sm_sfi;
@ -622,4 +630,7 @@ void
lsquic_stream_set_pwritev_params (unsigned iovecs, unsigned frames);
#endif
void
lsquic_stream_drop_hset_ref (struct lsquic_stream *);
#endif

View File

@ -184,4 +184,26 @@ size_t
lsquic_tp_get_quantum_sz (void);
#endif
#define SERVER_0RTT_TPS (0 \
/* [draft-ietf-quic-transport-31] Section 7.4.1: */ \
| (1 << TPI_ACTIVE_CONNECTION_ID_LIMIT) \
| (1 << TPI_INIT_MAX_DATA) \
| (1 << TPI_INIT_MAX_STREAMS_UNI) \
| (1 << TPI_INIT_MAX_STREAMS_BIDI) \
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL) \
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE) \
| (1 << TPI_INIT_MAX_STREAM_DATA_UNI) \
| (1 << TPI_MAX_IDLE_TIMEOUT) \
| (1 << TPI_MAX_UDP_PAYLOAD_SIZE) \
| (1 << TPI_DISABLE_ACTIVE_MIGRATION) \
/* Not including TPI_LOSS_BITS, see */ \
/* draft-ferrieuxhamchaoui-quic-lossbits-03, Section 5.1 */ \
/* [draft-ietf-quic-datagram-01] Section 3: */ \
| (1 << TPI_MAX_DATAGRAM_FRAME_SIZE) \
/* [draft-iyengar-quic-delayed-ack-01] does not specfiy, store: */ \
| (1 << TPI_MIN_ACK_DELAY) \
/* [draft-huitema-quic-ts-03] does not specfiy, store: */ \
| (1 << TPI_TIMESTAMPS) \
)
#endif