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 2020-10-08
- 2.22.1 - 2.22.1
- [BUGFIX] Function that checks validity of handshake packets. - [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]); headers.count = sizeof(header_arr) / sizeof(header_arr[0]);
req = new_req(GET, push_path->path, st_h->req->authority_str); req = new_req(GET, push_path->path, st_h->req->authority_str);
if (req) 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 else
LSQ_WARN("cannot allocate req for push"); LSQ_WARN("cannot allocate req for push");
free(push_path); 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_max_proto_version(cert->ce_ssl_ctx, TLS1_3_VERSION);
SSL_CTX_set_default_verify_paths(cert->ce_ssl_ctx); 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_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)) 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); 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); settings->es_qpack_dec_max_blocked = atoi(val);
return 0; return 0;
} }
if (0 == strncmp(name, "init_max_streams_bidi", 21))
{
settings->es_init_max_streams_bidi = atoi(val);
return 0;
}
break; break;
case 23: case 23:
if (0 == strncmp(name, "max_udp_payload_size_rx", 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 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 .. member:: LSQVER_ID31

View file

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

View file

@ -18,7 +18,7 @@ connections.
To aid development, there is a :macro:`LSQUIC_FORCED_TCID0_VERSIONS` that To aid development, there is a :macro:`LSQUIC_FORCED_TCID0_VERSIONS` that
specifies the list of versions with 0-sized connections. (If you, for 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 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 reason why a single engine instance could not be used both for client and

View file

@ -24,8 +24,8 @@ extern "C" {
#endif #endif
#define LSQUIC_MAJOR_VERSION 2 #define LSQUIC_MAJOR_VERSION 2
#define LSQUIC_MINOR_VERSION 22 #define LSQUIC_MINOR_VERSION 23
#define LSQUIC_PATCH_VERSION 1 #define LSQUIC_PATCH_VERSION 0
/** /**
* Engine flags: * Engine flags:
@ -1923,8 +1923,7 @@ lsquic_get_h3_alpns (unsigned versions);
* been established: it will return incorrect result. * been established: it will return incorrect result.
*/ */
int int
lsquic_is_valid_hs_packet (lsquic_engine_t *, const unsigned char *, lsquic_is_valid_hs_packet (lsquic_engine_t *, const unsigned char *, size_t);
size_t bufsz, size_t packet_in_sz);
/** /**
* Parse cid from packet stored in `buf' and store it to `cid'. Returns 0 * 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 */ /* Optional method */
size_t size_t
(*ci_get_min_datagram_size) (struct lsquic_conn *); (*ci_get_min_datagram_size) (struct lsquic_conn *);
/* Optional method */
void
(*ci_early_data_failed) (struct lsquic_conn *);
}; };
#define LSCONN_CCE_BITS 3 #define LSCONN_CCE_BITS 3

View file

@ -15,6 +15,8 @@ struct ssl_stream_method_st;
struct ssl_st; struct ssl_st;
struct sockaddr; struct sockaddr;
struct conn_cid_elem; struct conn_cid_elem;
struct lsquic_engine_settings;
enum lsquic_version;
#define DNONC_LENGTH 32 #define DNONC_LENGTH 32
#define SRST_LENGTH 16 #define SRST_LENGTH 16
@ -242,12 +244,6 @@ struct enc_session_funcs_gquic
void (*esf_reset_cid) (enc_session_t *, const lsquic_cid_t *); 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 struct crypto_stream_if
{ {
ssize_t (*csi_write) (void *stream, const void *buf, size_t len); 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 #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 #endif

View file

@ -107,6 +107,20 @@ static void
no_sess_ticket (enum alarm_id alarm_id, void *ctx, no_sess_ticket (enum alarm_id alarm_id, void *ctx,
lsquic_time_t expiry, lsquic_time_t now); 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 #define SAMPLE_SZ 16
@ -223,7 +237,7 @@ struct enc_sess_iquic
lsquic_cid_t esi_iscid; /* Initial SCID */ lsquic_cid_t esi_iscid; /* Initial SCID */
unsigned esi_key_phase; unsigned esi_key_phase;
enum { enum {
ESI_INITIALIZED = 1 << 0, ESI_UNUSED0 = 1 << 0,
ESI_LOG_SECRETS = 1 << 1, ESI_LOG_SECRETS = 1 << 1,
ESI_HANDSHAKE_OK = 1 << 2, ESI_HANDSHAKE_OK = 1 << 2,
ESI_ODCID = 1 << 3, ESI_ODCID = 1 << 3,
@ -243,17 +257,15 @@ struct enc_sess_iquic
ESI_MAX_PACKNO_INIT = 1 << 17, ESI_MAX_PACKNO_INIT = 1 << 17,
ESI_MAX_PACKNO_HSK = ESI_MAX_PACKNO_INIT << PNS_HSK, ESI_MAX_PACKNO_HSK = ESI_MAX_PACKNO_INIT << PNS_HSK,
ESI_MAX_PACKNO_APP = ESI_MAX_PACKNO_INIT << PNS_APP, ESI_MAX_PACKNO_APP = ESI_MAX_PACKNO_INIT << PNS_APP,
ESI_HAVE_0RTT_TP = 1 << 20,
} esi_flags; } esi_flags;
enum enc_level esi_last_w; enum enc_level esi_last_w;
unsigned esi_trasec_sz; unsigned esi_trasec_sz;
char *esi_hostname;
void *esi_keylog_handle; void *esi_keylog_handle;
#ifndef NDEBUG #ifndef NDEBUG
char *esi_sni_bypass; char *esi_sni_bypass;
#endif #endif
const unsigned char *esi_alpn; const unsigned char *esi_alpn;
unsigned char *esi_sess_resume_buf;
size_t esi_sess_resume_sz;
/* Need MD and AEAD for key rotation */ /* Need MD and AEAD for key rotation */
const EVP_MD *esi_md; const EVP_MD *esi_md;
const EVP_AEAD *esi_aead; const EVP_AEAD *esi_aead;
@ -279,6 +291,7 @@ struct enc_sess_iquic
esi_hp_batch_packets[HP_BATCH_SIZE]; esi_hp_batch_packets[HP_BATCH_SIZE];
unsigned char esi_hp_batch_samples[HP_BATCH_SIZE][SAMPLE_SZ]; unsigned char esi_hp_batch_samples[HP_BATCH_SIZE][SAMPLE_SZ];
unsigned char esi_grease; 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 * static SSL_SESSION *
maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess, 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; SSL_SESSION *ssl_session;
lsquic_ver_tag_t ver_tag; lsquic_ver_tag_t ver_tag;
enum lsquic_version quic_ver; enum lsquic_version quic_ver;
uint32_t rtt_ver, ticket_sz, trapa_sz; uint32_t rtt_ver, ticket_sz, trapa_sz;
const unsigned char *ticket_buf, *trapa_buf, *p; const unsigned char *ticket_buf, *trapa_buf, *p;
const unsigned char *const end const unsigned char *const end = sess_resume + sess_resume_sz;
= enc_sess->esi_sess_resume_buf + enc_sess->esi_sess_resume_sz;
if (enc_sess->esi_sess_resume_sz if (sess_resume_sz < sizeof(ver_tag) + sizeof(rtt_ver) + sizeof(ticket_sz))
< sizeof(ver_tag) + sizeof(rtt_ver) + sizeof(ticket_sz))
{ {
LSQ_DEBUG("rtt buf too short"); LSQ_DEBUG("rtt buf too short");
return NULL; return NULL;
} }
p = enc_sess->esi_sess_resume_buf; p = sess_resume;
memcpy(&ver_tag, p, sizeof(ver_tag)); memcpy(&ver_tag, p, sizeof(ver_tag));
p += sizeof(ver_tag); p += sizeof(ver_tag);
quic_ver = lsquic_tag2ver(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; p += trapa_sz;
assert(p == end); assert(p == end);
(void) /* TODO */ trapa_buf;
ssl_session = SSL_SESSION_from_bytes(ticket_buf, ticket_sz, ssl_ctx); ssl_session = SSL_SESSION_from_bytes(ticket_buf, ticket_sz, ssl_ctx);
if (!ssl_session) if (!ssl_session)
{ {
@ -769,6 +779,22 @@ maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess,
return NULL; 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"); LSQ_INFO("instantiated SSL_SESSION from serialized buffer");
return ssl_session; return ssl_session;
} }
@ -795,6 +821,16 @@ iquic_esfi_create_client (const char *hostname,
struct lsquic_alarmset *alset, unsigned max_streams_uni) struct lsquic_alarmset *alset, unsigned max_streams_uni)
{ {
struct enc_sess_iquic *enc_sess; 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); fiu_return_on("enc_sess_ietf/create_client", NULL);
@ -802,18 +838,6 @@ iquic_esfi_create_client (const char *hostname,
if (!enc_sess) if (!enc_sess)
return NULL; 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_enpub = enpub;
enc_sess->esi_streams = crypto_streams; enc_sess->esi_streams = crypto_streams;
enc_sess->esi_cryst_if = cryst_if; enc_sess->esi_cryst_if = cryst_if;
@ -844,35 +868,123 @@ iquic_esfi_create_client (const char *hostname,
return NULL; return NULL;
} }
/* Have to wait until the call to init_client() -- this is when the enc_sess->esi_max_streams_uni = max_streams_uni;
* result of version negotiation is known.
*/ 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) if (sess_resume && sess_resume_sz)
{ {
enc_sess->esi_sess_resume_buf = malloc(sess_resume_sz); ssl_session = maybe_create_SSL_SESSION(enc_sess, ssl_ctx,
if (enc_sess->esi_sess_resume_buf) sess_resume, sess_resume_sz);
if (ssl_session)
{ {
memcpy(enc_sess->esi_sess_resume_buf, sess_resume, sess_resume_sz); (void) /* This only ever returns 1: */
enc_sess->esi_sess_resume_sz = sess_resume_sz; 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) if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info)
enc_sess->esi_flags |= ESI_WANT_TICKET; enc_sess->esi_flags |= ESI_WANT_TICKET;
enc_sess->esi_alset = alset; enc_sess->esi_alset = alset;
lsquic_alarmset_init_alarm(enc_sess->esi_alset, AL_SESS_TICKET, lsquic_alarmset_init_alarm(enc_sess->esi_alset, AL_SESS_TICKET,
no_sess_ticket, enc_sess); no_sess_ticket, enc_sess);
enc_sess->esi_max_streams_uni = max_streams_uni; SSL_CTX_free(ssl_ctx);
return enc_sess; 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; struct enc_sess_iquic *const enc_sess = enc_session_p;
const struct alpn_map *am; const struct alpn_map *am;
unsigned quic_ctx_idx;
int transpa_len; int transpa_len;
SSL_CTX *ssl_ctx = NULL; SSL_CTX *ssl_ctx = NULL;
union { union {
@ -1287,9 +1400,10 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
LSQ_INFO("could not set stream method"); LSQ_INFO("could not set stream method");
return -1; 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, 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"); LSQ_INFO("could not set early data context");
return -1; 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_ex_data(enc_sess->esi_ssl, s_idx, enc_sess);
SSL_set_accept_state(enc_sess->esi_ssl); SSL_set_accept_state(enc_sess->esi_ssl);
LSQ_DEBUG("initialized server enc session"); LSQ_DEBUG("initialized server enc session");
enc_sess->esi_flags |= ESI_INITIALIZED;
return 0; 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 struct crypto_params
{ {
const EVP_AEAD *aead; 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 static int
get_peer_transport_params (struct enc_sess_iquic *enc_sess) get_peer_transport_params (struct enc_sess_iquic *enc_sess)
{ {
struct transport_params *const trans_params = &enc_sess->esi_peer_tp; struct transport_params *const trans_params = &enc_sess->esi_peer_tp;
struct transport_params params_0rtt;
const uint8_t *params_buf; const uint8_t *params_buf;
size_t bufsz; size_t bufsz;
char *params_str; char *params_str;
const enum lsquic_version version = enc_sess->esi_conn->cn_version; 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); SSL_get_peer_quic_transport_params(enc_sess->esi_ssl, &params_buf, &bufsz);
if (!params_buf) if (!params_buf)
@ -1623,6 +1671,13 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess)
return -1; 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); LSQ_DEBUG("have peer transport parameters (%zu bytes)", bufsz);
if (0 > (version == LSQVER_ID27 ? lsquic_tp_decode_27 if (0 > (version == LSQVER_ID27 ? lsquic_tp_decode_27
: lsquic_tp_decode)(params_buf, bufsz, : lsquic_tp_decode)(params_buf, bufsz,
@ -1646,6 +1701,10 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess)
return -1; 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] = { 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_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, [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 static enum iquic_handshake_status
iquic_esfi_handshake (struct enc_sess_iquic *enc_sess) 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"); LSQ_DEBUG("retry write");
return IHS_WANT_WRITE; return IHS_WANT_WRITE;
case SSL_ERROR_EARLY_DATA_REJECTED: case SSL_ERROR_EARLY_DATA_REJECTED:
LSQ_DEBUG("early data rejected"); LSQ_DEBUG("early data rejected: reset");
hsk_status = LSQ_HSK_RESUMED_FAIL; SSL_reset_early_data_reject(enc_sess->esi_ssl);
goto err; 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 */ /* fall through */
default: default:
LSQ_DEBUG("handshake: %s", ERR_error_string(err, errbuf)); 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; 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; return &enc_sess->esi_peer_tp;
else else
return NULL; return NULL;
@ -1892,8 +1964,6 @@ iquic_esfi_destroy (enc_session_t *enc_session_p)
free_handshake_keys(enc_sess); free_handshake_keys(enc_sess);
cleanup_hp(&enc_sess->esi_hp); cleanup_hp(&enc_sess->esi_hp);
free(enc_sess->esi_sess_resume_buf);
free(enc_sess->esi_hostname);
free(enc_sess); 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, [0] = {
[PNS_HSK] = ENC_LEV_INIT, [PNS_INIT] = ENC_LEV_CLEAR,
[PNS_APP] = ENC_LEV_FORW, [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]; char errbuf[ERR_ERROR_STRING_BUF_LEN];
pns = lsquic_packet_out_pns(packet_out); pns = lsquic_packet_out_pns(packet_out);
/* TODO Obviously, will need more logic for 0-RTT */ enc_level = pns2enc_level[ enc_sess->esi_have_forw ][ pns ];
enc_level = pns2enc_level[ pns ];
if (enc_level == ENC_LEV_FORW) if (enc_level == ENC_LEV_FORW)
{ {
@ -2458,7 +2534,7 @@ static int
iquic_esf_sess_resume_enabled (enc_session_t *enc_session_p) iquic_esf_sess_resume_enabled (enc_session_t *enc_session_p)
{ {
struct enc_sess_iquic *const enc_sess = 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)); key_len, hexbuf));
} }
if (rw && enc_level == ENC_LEV_FORW)
enc_sess->esi_have_forw = 1;
return 1; return 1;
err: err:
@ -2955,25 +3034,11 @@ chsk_ietf_on_new_stream (void *stream_if_ctx, struct lsquic_stream *stream)
enum enc_level enc_level; enum enc_level enc_level;
enc_level = enc_sess->esi_cryst_if->csi_enc_level(stream); enc_level = enc_sess->esi_cryst_if->csi_enc_level(stream);
if (enc_level != ENC_LEV_CLEAR) if (enc_level == ENC_LEV_CLEAR)
{ enc_sess->esi_cryst_if->csi_wantwrite(stream, 1);
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);
LSQ_DEBUG("handshake stream created successfully"); LSQ_DEBUG("handshake stream created successfully");
end:
return stream_if_ctx; 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[] = { static const char *const ihs2str[] = {
[IHS_WANT_READ] = "want read", [IHS_WANT_READ] = "want read",
[IHS_WANT_WRITE] = "want write", [IHS_WANT_WRITE] = "want write",
[IHS_WANT_RW] = "want rw",
[IHS_STOP] = "stop", [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_wantwrite(stream, 1);
enc_sess->esi_cryst_if->csi_wantread(stream, 0); enc_sess->esi_cryst_if->csi_wantread(stream, 0);
break; 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: default:
assert(st == IHS_STOP); assert(st == IHS_STOP);
write = !lsquic_frab_list_empty(&enc_sess->esi_frals[enc_level]); 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 */ /* [draft-ietf-quic-tls-29] Section 5.8 */
(unsigned char *) "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c", (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); engine->pub.enp_tokgen = lsquic_tg_new(&engine->pub);
if (!engine->pub.enp_tokgen) if (!engine->pub.enp_tokgen)
return NULL; 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; engine->pub.enp_crand = &engine->crand;
if (engine->pub.enp_settings.es_noprogress_timeout) if (engine->pub.enp_settings.es_noprogress_timeout)
engine->pub.enp_noprog_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 * static lsquic_conn_t *
find_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in, find_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
struct packin_parse_state *ppstate, const struct sockaddr *sa_local) 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; lsquic_conn_t *conn;
if (engine->flags & ENG_CONNS_BY_ADDR) if (engine->flags & ENG_CONNS_BY_ADDR)
{
el = find_conn_by_addr(engine->conns_hash, sa_local); 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) else if (packet_in->pi_flags & PI_CONN_ID)
el = lsquic_hash_find(engine->conns_hash, el = lsquic_hash_find(engine->conns_hash,
packet_in->pi_conn_id.idbuf, packet_in->pi_conn_id.len); 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; struct lsquic_hash *enp_server_certs;
/* gQUIC server configuration: */ /* gQUIC server configuration: */
struct lsquic_server_config *enp_server_config; 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 /* 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_frab_list.h"
#include "lsquic_ev_log.h" #include "lsquic_ev_log.h"
#include "fiu-local.h"
#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_WRITER #define LSQUIC_LOGGER_MODULE LSQLM_FRAME_WRITER
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(\ #define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(\
lsquic_stream_conn(fw->fw_stream)) lsquic_stream_conn(fw->fw_stream))
@ -497,6 +499,8 @@ lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw,
unsigned char *buf; unsigned char *buf;
int s; int s;
fiu_return_on("frame_writer/writer_promise", -1);
if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers)) if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers))
return -1; 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, if (0 != lsquic_headers_stream_push_promise(conn->fc_pub.u.gquic.hs, dep_stream->id,
pushed_stream->id, headers)) pushed_stream->id, headers))
{ {
/* Since the failure to write to HEADERS stream results in aborting /* If forget we ever had the hset pointer: */
* the connection, we do not bother rolling back. lsquic_stream_drop_hset_ref(pushed_stream);
*/ /* Now roll back stream creation and return stream ID: */
LSQ_ERROR("could not send push promise"); 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; return -1;
} }

View file

@ -137,7 +137,7 @@ enum ifull_conn_flags
IFC_IGNORE_HSK = 1 << 25, IFC_IGNORE_HSK = 1 << 25,
IFC_PROC_CRYPTO = 1 << 26, IFC_PROC_CRYPTO = 1 << 26,
IFC_MIGRA = 1 << 27, 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_DELAYED_ACKS = 1 << 29, /* Delayed ACKs are enabled */
IFC_TIMESTAMPS = 1 << 30, /* Timestamps are enabled */ IFC_TIMESTAMPS = 1 << 30, /* Timestamps are enabled */
IFC_DATAGRAMS = 1u<< 31, /* Datagrams 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_CONN_CLOSE_PACK = 1 << 4, /* CONNECTION_CLOSE has been packetized */
MF_SEND_WRONG_COUNTS= 1 << 5, /* Send wrong ECN counts to peer */ MF_SEND_WRONG_COUNTS= 1 << 5, /* Send wrong ECN counts to peer */
MF_WANT_DATAGRAM_WRITE = 1 << 6, 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 static void
mtu_probe_too_large (struct ietf_full_conn *, const struct lsquic_packet_out *); 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 static unsigned
highest_bit_set (unsigned sz) 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.header_table_size = HQ_DF_QPACK_MAX_TABLE_CAPACITY;
conn->ifc_peer_hq_settings.qpack_blocked_streams = HQ_DF_QPACK_BLOCKED_STREAMS; 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_INIT] = IQUIC_INVALID_PACKNO;
conn->ifc_max_ack_packno[PNS_HSK] = IQUIC_INVALID_PACKNO; conn->ifc_max_ack_packno[PNS_HSK] = IQUIC_INVALID_PACKNO;
conn->ifc_max_ack_packno[PNS_APP] = 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 *sess_resume, size_t sess_resume_sz,
const unsigned char *token, size_t token_sz) const unsigned char *token, size_t token_sz)
{ {
const struct transport_params *params;
const struct enc_session_funcs_iquic *esfi; const struct enc_session_funcs_iquic *esfi;
struct ietf_full_conn *conn; struct ietf_full_conn *conn;
enum lsquic_version ver, sess_resume_version; 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; conn->ifc_created = now;
LSQ_DEBUG("logging using %s SCID", LSQ_DEBUG("logging using %s SCID",
LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "client" : "server"); 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; return &conn->ifc_conn;
err4: err4:
@ -1411,6 +1431,10 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
free(conn); free(conn);
err0: err0:
return NULL; 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", LSQ_DEBUG("logging using %s SCID",
LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "server" : "client"); LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "server" : "client");
conn->ifc_flags |= IFC_CREATED_OK;
return &conn->ifc_conn; return &conn->ifc_conn;
err3: err3:
@ -3317,35 +3342,13 @@ maybe_start_migration (struct ietf_full_conn *conn)
static int 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_stream *stream;
struct lsquic_hash_elem *el; struct lsquic_hash_elem *el;
struct dcid_elem *dce;
const struct transport_params *params;
enum stream_id_type sit; enum stream_id_type sit;
uint64_t limit; 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)) if ((params->tp_set & (1 << TPI_LOSS_BITS))
&& conn->ifc_settings->es_ql_bits == 2) && conn->ifc_settings->es_ql_bits == 2)
@ -3487,6 +3490,104 @@ handshake_ok (struct lsquic_conn *lconn)
CUR_NPATH(conn)->np_pack_size); 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); dce = get_new_dce(conn);
if (!dce) if (!dce)
{ {
@ -3518,65 +3619,9 @@ handshake_ok (struct lsquic_conn *lconn)
LSQ_INFO("applied peer transport parameters"); LSQ_INFO("applied peer transport parameters");
if (conn->ifc_flags & IFC_HTTP) if ((conn->ifc_flags & (IFC_HTTP|IFC_HTTP_INITED)) == IFC_HTTP)
{ if (0 != init_http(conn))
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; 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) if (conn->ifc_settings->es_dplpmtud)
conn->ifc_mflags |= MF_CHECK_MTU_PROBE; 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; conn->ifc_send_flags |= SF_SEND_NEW_CID;
maybe_create_delayed_streams(conn); maybe_create_delayed_streams(conn);
if (!(conn->ifc_flags & IFC_SERVER))
lsquic_send_ctl_0rtt_to_1rtt(&conn->ifc_send_ctl);
return 0; return 0;
} }
@ -3615,10 +3663,10 @@ ietf_full_conn_ci_hsk_done (struct lsquic_conn *lconn,
} }
break; break;
default: default:
case LSQ_HSK_RESUMED_FAIL: /* IETF crypto never returns this */
assert(0); assert(0);
/* fall-through */ /* fall-through */
case LSQ_HSK_FAIL: case LSQ_HSK_FAIL:
case LSQ_HSK_RESUMED_FAIL:
handshake_failed(lconn); handshake_failed(lconn);
break; 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) if (!(conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|| (conn->ifc_flags & IFC_CLOSING) || (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_senhist_largest(&conn->ifc_send_ctl.sc_senhist) < 30
|| lsquic_send_ctl_in_recovery(&conn->ifc_send_ctl) || lsquic_send_ctl_in_recovery(&conn->ifc_send_ctl)
|| !lsquic_send_ctl_can_send_probe(&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 static size_t
ietf_full_conn_ci_get_min_datagram_size (struct lsquic_conn *lconn) 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; conn->ifc_flags |= (s < 0) << IFC_BIT_ERROR;
if (0 == s) if (0 == s)
process_crypto_stream_write_events(conn); 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); 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_destroy = ietf_full_conn_ci_destroy, \
.ci_drain_time = ietf_full_conn_ci_drain_time, \ .ci_drain_time = ietf_full_conn_ci_drain_time, \
.ci_drop_crypto_streams = ietf_full_conn_ci_drop_crypto_streams, \ .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_engine = ietf_full_conn_ci_get_engine, \
.ci_get_log_cid = ietf_full_conn_ci_get_log_cid, \ .ci_get_log_cid = ietf_full_conn_ci_get_log_cid, \
.ci_get_min_datagram_size= ietf_full_conn_ci_get_min_datagram_size, \ .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 int
lsquic_is_valid_hs_packet (struct lsquic_engine *engine, 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; lsquic_ver_tag_t tag;
int is_valid; int is_valid;
@ -104,7 +104,7 @@ lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
case 0x80|0x00|0x20|0x10|0x08: case 0x80|0x00|0x20|0x10|0x08:
case 0x80|0x40|0x20|0x10|0x00: case 0x80|0x40|0x20|0x10|0x00:
case 0x80|0x00|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); && lsquic_is_valid_iquic_hs_packet(buf, bufsz, &tag);
break; break;
/* 1X00 XGGG: ID-22 long header */ /* 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|0x00|0x20|0x00|0x08:
case 0x80|0x40|0x20|0x00|0x00: case 0x80|0x40|0x20|0x00|0x00:
case 0x80|0x00|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); && lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet(buf, bufsz, &tag);
break; break;
/* 01XX XGGG: ID-22 short header */ /* 01XX XGGG: ID-22 short header */

View file

@ -6,6 +6,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
@ -560,6 +561,16 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
TAILQ_INIT(&conn->imc_crypto_frames); TAILQ_INIT(&conn->imc_crypto_frames);
if (odcid) if (odcid)
imico_peer_addr_validated(conn, "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", LSQ_DEBUG("created mini connection object %p; max packet size=%hu",
conn, conn->imc_path.np_pack_size); 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) if (buf[0] & 1)
{ {
s = vint_read(buf + 1, buf + buf_len, &len); 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 = buf + 1 + s;
*data_len = len; *data_len = len;
return 1 + buf_len + len; return 1 + s + len;
} }
else else
return -1; 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_HSK]);
TAILQ_INIT(&ctl->sc_unacked_packets[PNS_APP]); TAILQ_INIT(&ctl->sc_unacked_packets[PNS_APP]);
TAILQ_INIT(&ctl->sc_lost_packets); TAILQ_INIT(&ctl->sc_lost_packets);
TAILQ_INIT(&ctl->sc_0rtt_stash);
ctl->sc_enpub = enpub; ctl->sc_enpub = enpub;
ctl->sc_alset = alset; ctl->sc_alset = alset;
ctl->sc_ver_neg = ver_neg; 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; packet_out->po_flags &= ~PO_LOST;
send_ctl_destroy_packet(ctl, packet_out); 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) / for (n = 0; n < sizeof(ctl->sc_buffered_packets) /
sizeof(ctl->sc_buffered_packets[0]); ++n) 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)) if (buffered && (lost_types & QUIC_FTBIT_ACK))
lconn->cn_if->ci_ack_rollback(lconn, &ctl_state->ack_state); 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. */ /* Second section: everything else. */
struct lsquic_packets_tailq sc_scheduled_packets, struct lsquic_packets_tailq sc_scheduled_packets,
sc_0rtt_stash,
sc_lost_packets; sc_lost_packets;
struct buf_packet_q sc_buffered_packets[BPT_OTHER_PRIO + 1]; struct buf_packet_q sc_buffered_packets[BPT_OTHER_PRIO + 1];
const struct ver_neg *sc_ver_neg; const struct ver_neg *sc_ver_neg;
@ -431,4 +432,10 @@ void
lsquic_send_ctl_rollback (struct lsquic_send_ctl *, struct send_ctl_state *, lsquic_send_ctl_rollback (struct lsquic_send_ctl *, struct send_ctl_state *,
const struct iovec *, size_t); 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 #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 * static struct lsquic_stream *
stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub, 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, 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); stream->sm_bflags |= ctor_flags & ((1 << N_SMBF_FLAGS) - 1);
if (conn_pub->lconn->cn_flags & LSCONN_SERVER) if (conn_pub->lconn->cn_flags & LSCONN_SERVER)
stream->sm_bflags |= SMBF_SERVER; stream->sm_bflags |= SMBF_SERVER;
stream->sm_get_packet_for_stream = lsquic_send_ctl_get_packet_for_stream;
return 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); lsquic_sfcw_init(&stream->fc, initial_window, cfcw, conn_pub, id);
stream->max_send_off = initial_send_off; stream->max_send_off = initial_send_off;
LSQ_DEBUG("created stream"); 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 static void
destroy_uh (struct lsquic_stream *stream) 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 else
need_at_least += size > 0; need_at_least += size > 0;
get_packet: 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); need_at_least, stream->conn_pub->path, stream);
if (packet_out) if (packet_out)
{ {

View file

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

View file

@ -184,4 +184,26 @@ size_t
lsquic_tp_get_quantum_sz (void); lsquic_tp_get_quantum_sz (void);
#endif #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 #endif